2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
28 // dbcrypto - cryptographic core for database and key blob cryptography
31 #include <securityd_client/ssblob.h>
32 #include "server.h" // just for Server::csp()
33 #include <security_cdsa_client/genkey.h>
34 #include <security_cdsa_client/cryptoclient.h>
35 #include <security_cdsa_client/keyclient.h>
36 #include <security_cdsa_client/macclient.h>
37 #include <security_cdsa_client/wrapkey.h>
38 #include <security_cdsa_utilities/cssmendian.h>
41 using namespace CssmClient
;
44 DatabaseCryptoCore::DatabaseCryptoCore() : mHaveMaster(false), mIsValid(false)
49 DatabaseCryptoCore::~DatabaseCryptoCore()
51 // key objects take care of themselves
58 void DatabaseCryptoCore::invalidate()
63 mEncryptionKey
.release();
64 mSigningKey
.release();
70 // Generate new secrets for this crypto core.
72 void DatabaseCryptoCore::generateNewSecrets()
74 // create a random DES3 key
75 GenerateKey
desGenerator(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
, 24 * 8);
76 mEncryptionKey
= desGenerator(KeySpec(CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
,
77 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
79 // create a random 20 byte HMAC/SHA1 signing "key"
80 GenerateKey
signGenerator(Server::csp(), CSSM_ALGID_SHA1HMAC
,
81 sizeof(DbBlob::PrivateBlob::SigningKey
) * 8);
82 mSigningKey
= signGenerator(KeySpec(CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_VERIFY
,
83 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
85 // secrets established
90 CssmClient::Key
DatabaseCryptoCore::masterKey()
98 // Establish the master secret as derived from a passphrase passed in.
99 // If a DbBlob is passed, take the salt from it and remember it.
100 // If a NULL DbBlob is passed, generate a new (random) salt.
101 // Note that the passphrase is NOT remembered; only the master key.
103 void DatabaseCryptoCore::setup(const DbBlob
*blob
, const CssmData
&passphrase
)
106 memcpy(mSalt
, blob
->salt
, sizeof(mSalt
));
108 Server::active().random(mSalt
);
109 mMasterKey
= deriveDbMasterKey(passphrase
);
115 // Establish the master secret directly from a master key passed in.
116 // We will copy the KeyData (caller still owns its copy).
117 // Blob/salt handling as above.
119 void DatabaseCryptoCore::setup(const DbBlob
*blob
, CssmClient::Key master
)
121 // pre-screen the key
122 CssmKey::Header header
= master
.header();
123 if (header
.keyClass() != CSSM_KEYCLASS_SESSION_KEY
)
124 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
125 if (header
.algorithm() != CSSM_ALGID_3DES_3KEY_EDE
)
126 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
130 memcpy(mSalt
, blob
->salt
, sizeof(mSalt
));
132 Server::active().random(mSalt
);
139 // Given a putative passphrase, determine whether that passphrase
140 // properly generates the database's master secret.
141 // Return a boolean accordingly. Do not change our state.
142 // The database must have a master secret (to compare with).
143 // Note that any errors thrown by the cryptography here will actually
144 // throw out of validatePassphrase, since they "should not happen" and
145 // thus indicate a problem *beyond* (just) a bad passphrase.
147 bool DatabaseCryptoCore::validatePassphrase(const CssmData
&passphrase
)
150 CssmClient::Key master
= deriveDbMasterKey(passphrase
);
152 // to compare master with mMaster, see if they encrypt alike
154 ("Now is the time for all good processes to come to the aid of their kernel.");
155 CssmData
noRemainder((void *)1, 0); // no cipher overflow
156 Encrypt
cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
157 cryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
158 cryptor
.padding(CSSM_PADDING_PKCS1
);
159 uint8 iv
[8]; // leave uninitialized; pseudo-random is cool
160 cryptor
.initVector(CssmData::wrap(iv
));
163 CssmAutoData
cipher1(Server::csp().allocator());
164 cryptor
.encrypt(probe
, cipher1
.get(), noRemainder
);
166 cryptor
.key(mMasterKey
);
167 CssmAutoData
cipher2(Server::csp().allocator());
168 cryptor
.encrypt(probe
, cipher2
.get(), noRemainder
);
170 return cipher1
== cipher2
;
175 // Encode a database blob from the core.
177 DbBlob
*DatabaseCryptoCore::encodeCore(const DbBlob
&blobTemplate
,
178 const CssmData
&publicAcl
, const CssmData
&privateAcl
) const
180 assert(isValid()); // must have secrets to work from
184 Server::active().random(iv
);
186 // build the encrypted section blob
187 CssmData
&encryptionBits
= *mEncryptionKey
;
188 CssmData
&signingBits
= *mSigningKey
;
190 incrypt
[0] = encryptionBits
;
191 incrypt
[1] = signingBits
;
192 incrypt
[2] = privateAcl
;
193 CssmData cryptoBlob
, remData
;
194 Encrypt
cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
195 cryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
196 cryptor
.padding(CSSM_PADDING_PKCS1
);
197 cryptor
.key(mMasterKey
);
198 CssmData
ivd(iv
, sizeof(iv
)); cryptor
.initVector(ivd
);
199 cryptor
.encrypt(incrypt
, 3, &cryptoBlob
, 1, remData
);
201 // allocate the final DbBlob, uh, blob
202 size_t length
= sizeof(DbBlob
) + publicAcl
.length() + cryptoBlob
.length();
203 DbBlob
*blob
= Allocator::standard().malloc
<DbBlob
>(length
);
205 // assemble the DbBlob
206 memset(blob
, 0x7d, sizeof(DbBlob
)); // deterministically fill any alignment gaps
208 blob
->randomSignature
= blobTemplate
.randomSignature
;
209 blob
->sequence
= blobTemplate
.sequence
;
210 blob
->params
= blobTemplate
.params
;
211 memcpy(blob
->salt
, mSalt
, sizeof(blob
->salt
));
212 memcpy(blob
->iv
, iv
, sizeof(iv
));
213 memcpy(blob
->publicAclBlob(), publicAcl
, publicAcl
.length());
214 blob
->startCryptoBlob
= sizeof(DbBlob
) + publicAcl
.length();
215 memcpy(blob
->cryptoBlob(), cryptoBlob
, cryptoBlob
.length());
216 blob
->totalLength
= blob
->startCryptoBlob
+ cryptoBlob
.length();
219 CssmData signChunk
[] = {
220 CssmData(blob
->data(), offsetof(DbBlob
, blobSignature
)),
221 CssmData(blob
->publicAclBlob(), publicAcl
.length() + cryptoBlob
.length())
223 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
224 GenerateMac
signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY
); //@@@!!! CRUD
225 signer
.key(mSigningKey
);
226 signer
.sign(signChunk
, 2, signature
);
227 assert(signature
.length() == sizeof(blob
->blobSignature
));
229 // all done. Clean up
230 Server::csp()->allocator().free(cryptoBlob
);
236 // Decode a database blob into the core.
237 // Throws exceptions if decoding fails.
238 // Memory returned in privateAclBlob is allocated and becomes owned by caller.
240 void DatabaseCryptoCore::decodeCore(DbBlob
*blob
, void **privateAclBlob
)
242 assert(mHaveMaster
); // must have master key installed
244 // try to decrypt the cryptoblob section
245 Decrypt
decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
246 decryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
247 decryptor
.padding(CSSM_PADDING_PKCS1
);
248 decryptor
.key(mMasterKey
);
249 CssmData
ivd(blob
->iv
, sizeof(blob
->iv
)); decryptor
.initVector(ivd
);
250 CssmData
cryptoBlob(blob
->cryptoBlob(), blob
->cryptoBlobLength());
251 CssmData decryptedBlob
, remData
;
252 decryptor
.decrypt(cryptoBlob
, decryptedBlob
, remData
);
253 DbBlob::PrivateBlob
*privateBlob
= decryptedBlob
.interpretedAs
<DbBlob::PrivateBlob
>();
255 // tentatively establish keys
256 mEncryptionKey
= makeRawKey(privateBlob
->encryptionKey
,
257 sizeof(privateBlob
->encryptionKey
), CSSM_ALGID_3DES_3KEY_EDE
,
258 CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
);
259 mSigningKey
= makeRawKey(privateBlob
->signingKey
,
260 sizeof(privateBlob
->signingKey
), CSSM_ALGID_SHA1HMAC
,
261 CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_VERIFY
);
263 // verify signature on the whole blob
264 CssmData signChunk
[] = {
265 CssmData(blob
->data(), offsetof(DbBlob
, blobSignature
)),
266 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
268 CSSM_ALGORITHMS verifyAlgorithm
= CSSM_ALGID_SHA1HMAC
;
269 #if defined(COMPAT_OSX_10_0)
270 if (blob
->version() == blob
->version_MacOS_10_0
)
271 verifyAlgorithm
= CSSM_ALGID_SHA1HMAC_LEGACY
; // BSafe bug compatibility
273 VerifyMac
verifier(Server::csp(), verifyAlgorithm
);
274 verifier
.key(mSigningKey
);
275 verifier
.verify(signChunk
, 2, CssmData(blob
->blobSignature
, sizeof(blob
->blobSignature
)));
277 // all checks out; start extracting fields
278 this->mEncryptionKey
= mEncryptionKey
;
279 this->mSigningKey
= mSigningKey
;
280 if (privateAclBlob
) {
281 // extract private ACL blob as a separately allocated area
282 uint32 blobLength
= decryptedBlob
.length() - sizeof(DbBlob::PrivateBlob
);
283 *privateAclBlob
= Allocator::standard().malloc(blobLength
);
284 memcpy(*privateAclBlob
, privateBlob
->privateAclBlob(), blobLength
);
287 // secrets have been established
289 Allocator::standard().free(privateBlob
);
296 KeyBlob
*DatabaseCryptoCore::encodeKeyCore(const CssmKey
&inKey
,
297 const CssmData
&publicAcl
, const CssmData
&privateAcl
) const
299 assert(isValid()); // need our database secrets
303 Server::active().random(iv
);
305 // extract and hold some header bits the CSP does not want to see
307 uint32 heldAttributes
= key
.attributes() & managedAttributes
;
308 key
.clearAttribute(managedAttributes
);
309 key
.setAttribute(forcedAttributes
);
311 // use a CMS wrap to encrypt the key
312 WrapKey
wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
313 wrap
.key(mEncryptionKey
);
314 wrap
.mode(CSSM_ALGMODE_CBCPadIV8
);
315 wrap
.padding(CSSM_PADDING_PKCS1
);
316 CssmData
ivd(iv
, sizeof(iv
)); wrap
.initVector(ivd
);
317 wrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
,
318 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
));
320 wrap(key
, wrappedKey
, &privateAcl
);
322 // stick the held attribute bits back in
323 key
.clearAttribute(forcedAttributes
);
324 key
.setAttribute(heldAttributes
);
326 // allocate the final KeyBlob, uh, blob
327 size_t length
= sizeof(KeyBlob
) + publicAcl
.length() + wrappedKey
.length();
328 KeyBlob
*blob
= Allocator::standard().malloc
<KeyBlob
>(length
);
330 // assemble the KeyBlob
331 memset(blob
, 0, sizeof(KeyBlob
)); // fill alignment gaps
333 memcpy(blob
->iv
, iv
, sizeof(iv
));
334 blob
->header
= key
.header();
335 h2ni(blob
->header
); // endian-correct the header
336 blob
->wrappedHeader
.blobType
= wrappedKey
.blobType();
337 blob
->wrappedHeader
.blobFormat
= wrappedKey
.blobFormat();
338 blob
->wrappedHeader
.wrapAlgorithm
= wrappedKey
.wrapAlgorithm();
339 blob
->wrappedHeader
.wrapMode
= wrappedKey
.wrapMode();
340 memcpy(blob
->publicAclBlob(), publicAcl
, publicAcl
.length());
341 blob
->startCryptoBlob
= sizeof(KeyBlob
) + publicAcl
.length();
342 memcpy(blob
->cryptoBlob(), wrappedKey
.data(), wrappedKey
.length());
343 blob
->totalLength
= blob
->startCryptoBlob
+ wrappedKey
.length();
346 CssmData signChunk
[] = {
347 CssmData(blob
->data(), offsetof(KeyBlob
, blobSignature
)),
348 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
350 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
351 GenerateMac
signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY
); //@@@!!! CRUD
352 signer
.key(mSigningKey
);
353 signer
.sign(signChunk
, 2, signature
);
354 assert(signature
.length() == sizeof(blob
->blobSignature
));
356 // all done. Clean up
357 Server::csp()->allocator().free(wrappedKey
);
365 void DatabaseCryptoCore::decodeKeyCore(KeyBlob
*blob
,
366 CssmKey
&key
, void * &pubAcl
, void * &privAcl
) const
368 assert(isValid()); // need our database secrets
370 // Assemble the encrypted blob as a CSSM "wrapped key"
372 wrappedKey
.KeyHeader
= blob
->header
;
373 h2ni(wrappedKey
.KeyHeader
);
374 wrappedKey
.blobType(blob
->wrappedHeader
.blobType
);
375 wrappedKey
.blobFormat(blob
->wrappedHeader
.blobFormat
);
376 wrappedKey
.wrapAlgorithm(blob
->wrappedHeader
.wrapAlgorithm
);
377 wrappedKey
.wrapMode(blob
->wrappedHeader
.wrapMode
);
378 wrappedKey
.KeyData
= CssmData(blob
->cryptoBlob(), blob
->cryptoBlobLength());
380 // verify signature (check against corruption)
381 CssmData signChunk
[] = {
382 CssmData::wrap(blob
, offsetof(KeyBlob
, blobSignature
)),
383 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
385 CSSM_ALGORITHMS verifyAlgorithm
= CSSM_ALGID_SHA1HMAC
;
386 #if defined(COMPAT_OSX_10_0)
387 if (blob
->version() == blob
->version_MacOS_10_0
)
388 verifyAlgorithm
= CSSM_ALGID_SHA1HMAC_LEGACY
; // BSafe bug compatibility
390 VerifyMac
verifier(Server::csp(), verifyAlgorithm
);
391 verifier
.key(mSigningKey
);
392 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
393 verifier
.verify(signChunk
, 2, signature
);
395 // extract and hold some header bits the CSP does not want to see
396 uint32 heldAttributes
= n2h(blob
->header
.attributes()) & managedAttributes
;
398 // decrypt the key using an unwrapping operation
399 UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
400 unwrap
.key(mEncryptionKey
);
401 unwrap
.mode(CSSM_ALGMODE_CBCPadIV8
);
402 unwrap
.padding(CSSM_PADDING_PKCS1
);
403 CssmData
ivd(blob
->iv
, sizeof(blob
->iv
)); unwrap
.initVector(ivd
);
404 unwrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
,
405 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
));
406 CssmData privAclData
;
407 wrappedKey
.clearAttribute(managedAttributes
); //@@@ shouldn't be needed(?)
409 KeySpec(n2h(blob
->header
.usage()),
410 (n2h(blob
->header
.attributes()) & ~managedAttributes
) | forcedAttributes
),
413 // compare retrieved key headers with blob headers (sanity check)
414 // @@@ this should probably be checked over carefully
415 CssmKey::Header
&real
= key
.header();
416 CssmKey::Header
&incoming
= blob
->header
;
419 if (real
.HeaderVersion
!= incoming
.HeaderVersion
||
420 real
.cspGuid() != incoming
.cspGuid())
421 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
422 if (real
.algorithm() != incoming
.algorithm())
423 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
425 // re-insert held bits
426 key
.header().KeyAttr
|= heldAttributes
;
428 // got a valid key: return the pieces
429 pubAcl
= blob
->publicAclBlob(); // points into blob (shared)
430 privAcl
= privAclData
; // was allocated by CSP decrypt
431 // key was set by unwrap operation
436 // Derive the blob-specific database blob encryption key from the passphrase and the salt.
438 CssmClient::Key
DatabaseCryptoCore::deriveDbMasterKey(const CssmData
&passphrase
) const
440 // derive an encryption key and IV from passphrase and salt
441 CssmClient::DeriveKey
makeKey(Server::csp(),
442 CSSM_ALGID_PKCS5_PBKDF2
, CSSM_ALGID_3DES_3KEY_EDE
, 24 * 8);
443 makeKey
.iterationCount(1000);
444 makeKey
.salt(CssmData::wrap(mSalt
));
445 CSSM_PKCS5_PBKDF2_PARAMS params
;
446 params
.Passphrase
= passphrase
;
447 params
.PseudoRandomFunction
= CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
;
448 CssmData paramData
= CssmData::wrap(params
);
449 return makeKey(¶mData
, KeySpec(CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
450 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
455 // Turn raw keybits into a symmetric key in the CSP
457 CssmClient::Key
DatabaseCryptoCore::makeRawKey(void *data
, size_t length
,
458 CSSM_ALGORITHMS algid
, CSSM_KEYUSE usage
)
462 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
463 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
464 key
.header().AlgorithmId
= algid
;
465 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
466 key
.header().KeyUsage
= usage
;
467 key
.header().KeyAttr
= 0;
468 key
.KeyData
= CssmData(data
, length
);
470 // unwrap it into the CSP (but keep it raw)
471 UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
);
472 CssmKey unwrappedKey
;
473 CssmData descriptiveData
;
475 KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
),
476 unwrappedKey
, &descriptiveData
, NULL
);
477 return CssmClient::Key(Server::csp(), unwrappedKey
);