2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 // dbcrypto - cryptographic core for database and key blob cryptography
24 #include "server.h" // just for Server::csp()
25 #include <Security/genkey.h>
26 #include <Security/cryptoclient.h>
27 #include <Security/keyclient.h>
28 #include <Security/macclient.h>
29 #include <Security/wrapkey.h>
32 using namespace CssmClient
;
35 DatabaseCryptoCore::DatabaseCryptoCore() : mHaveMaster(false), mIsValid(false)
40 DatabaseCryptoCore::~DatabaseCryptoCore()
42 // key objects take care of themselves
49 void DatabaseCryptoCore::invalidate()
54 mEncryptionKey
.release();
55 mSigningKey
.release();
61 // Generate new secrets for this crypto core.
63 void DatabaseCryptoCore::generateNewSecrets()
65 // create a random DES3 key
66 GenerateKey
desGenerator(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
, 24 * 8);
67 mEncryptionKey
= desGenerator(KeySpec(CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
,
68 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
70 // create a random 20 byte HMAC/SHA1 signing "key"
71 GenerateKey
signGenerator(Server::csp(), CSSM_ALGID_SHA1HMAC
,
72 sizeof(DbBlob::PrivateBlob::SigningKey
) * 8);
73 mSigningKey
= signGenerator(KeySpec(CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_VERIFY
,
74 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
76 // secrets established
81 CssmClient::Key
DatabaseCryptoCore::masterKey()
89 // Establish the master secret as derived from a passphrase passed in.
90 // If a DbBlob is passed, take the salt from it and remember it.
91 // If a NULL DbBlob is passed, generate a new (random) salt.
92 // Note that the passphrase is NOT remembered; only the master key.
94 void DatabaseCryptoCore::setup(const DbBlob
*blob
, const CssmData
&passphrase
)
97 memcpy(mSalt
, blob
->salt
, sizeof(mSalt
));
99 Server::active().random(mSalt
);
100 mMasterKey
= deriveDbMasterKey(passphrase
);
106 // Establish the master secret directly from a master key passed in.
107 // We will copy the KeyData (caller still owns its copy).
108 // Blob/salt handling as above.
110 void DatabaseCryptoCore::setup(const DbBlob
*blob
, CssmClient::Key master
)
112 // pre-screen the key
113 CssmKey::Header header
= master
.header();
114 if (header
.keyClass() != CSSM_KEYCLASS_SESSION_KEY
)
115 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
116 if (header
.algorithm() != CSSM_ALGID_3DES_3KEY_EDE
)
117 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
121 memcpy(mSalt
, blob
->salt
, sizeof(mSalt
));
123 Server::active().random(mSalt
);
130 // Given a putative passphrase, determine whether that passphrase
131 // properly generates the database's master secret.
132 // Return a boolean accordingly. Do not change our state.
133 // The database must have a master secret (to compare with).
134 // Note that any errors thrown by the cryptography here will actually
135 // throw out of validatePassphrase, since they "should not happen" and
136 // thus indicate a problem *beyond* (just) a bad passphrase.
138 bool DatabaseCryptoCore::validatePassphrase(const CssmData
&passphrase
)
141 CssmClient::Key master
= deriveDbMasterKey(passphrase
);
143 // to compare master with mMaster, see if they encrypt alike
145 ("Now is the time for all good processes to come to the aid of their kernel.");
146 CssmData
noRemainder((void *)1, 0); // no cipher overflow
147 Encrypt
cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
148 cryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
149 cryptor
.padding(CSSM_PADDING_PKCS1
);
150 uint8 iv
[8]; // leave uninitialized; pseudo-random is cool
151 cryptor
.initVector(CssmData::wrap(iv
));
154 CssmAutoData
cipher1(Server::csp().allocator());
155 cryptor
.encrypt(probe
, cipher1
.get(), noRemainder
);
157 cryptor
.key(mMasterKey
);
158 CssmAutoData
cipher2(Server::csp().allocator());
159 cryptor
.encrypt(probe
, cipher2
.get(), noRemainder
);
161 return cipher1
== cipher2
;
166 // Encode a database blob from the core.
168 DbBlob
*DatabaseCryptoCore::encodeCore(const DbBlob
&blobTemplate
,
169 const CssmData
&publicAcl
, const CssmData
&privateAcl
) const
171 assert(isValid()); // must have secrets to work from
175 Server::active().random(iv
);
177 // build the encrypted section blob
178 CssmData
&encryptionBits
= *mEncryptionKey
;
179 CssmData
&signingBits
= *mSigningKey
;
181 incrypt
[0] = encryptionBits
;
182 incrypt
[1] = signingBits
;
183 incrypt
[2] = privateAcl
;
184 CssmData cryptoBlob
, remData
;
185 Encrypt
cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
186 cryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
187 cryptor
.padding(CSSM_PADDING_PKCS1
);
188 cryptor
.key(mMasterKey
);
189 CssmData
ivd(iv
, sizeof(iv
)); cryptor
.initVector(ivd
);
190 cryptor
.encrypt(incrypt
, 3, &cryptoBlob
, 1, remData
);
192 // allocate the final DbBlob, uh, blob
193 size_t length
= sizeof(DbBlob
) + publicAcl
.length() + cryptoBlob
.length();
194 DbBlob
*blob
= CssmAllocator::standard().malloc
<DbBlob
>(length
);
196 // assemble the DbBlob
197 memset(blob
, 0x7d, sizeof(DbBlob
)); // deterministically fill any alignment gaps
199 blob
->randomSignature
= blobTemplate
.randomSignature
;
200 blob
->sequence
= blobTemplate
.sequence
;
201 blob
->params
= blobTemplate
.params
;
202 memcpy(blob
->salt
, mSalt
, sizeof(blob
->salt
));
203 memcpy(blob
->iv
, iv
, sizeof(iv
));
204 memcpy(blob
->publicAclBlob(), publicAcl
, publicAcl
.length());
205 blob
->startCryptoBlob
= sizeof(DbBlob
) + publicAcl
.length();
206 memcpy(blob
->cryptoBlob(), cryptoBlob
, cryptoBlob
.length());
207 blob
->totalLength
= blob
->startCryptoBlob
+ cryptoBlob
.length();
210 CssmData signChunk
[] = {
211 CssmData(blob
->data(), offsetof(DbBlob
, blobSignature
)),
212 CssmData(blob
->publicAclBlob(), publicAcl
.length() + cryptoBlob
.length())
214 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
215 GenerateMac
signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY
); //@@@!!! CRUD
216 signer
.key(mSigningKey
);
217 signer
.sign(signChunk
, 2, signature
);
218 assert(signature
.length() == sizeof(blob
->blobSignature
));
220 // all done. Clean up
221 Server::csp()->allocator().free(cryptoBlob
);
227 // Decode a database blob into the core.
228 // Throws exceptions if decoding fails.
229 // Memory returned in privateAclBlob is allocated and becomes owned by caller.
231 void DatabaseCryptoCore::decodeCore(DbBlob
*blob
, void **privateAclBlob
)
233 assert(mHaveMaster
); // must have master key installed
235 // try to decrypt the cryptoblob section
236 Decrypt
decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
237 decryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
238 decryptor
.padding(CSSM_PADDING_PKCS1
);
239 decryptor
.key(mMasterKey
);
240 CssmData
ivd(blob
->iv
, sizeof(blob
->iv
)); decryptor
.initVector(ivd
);
241 CssmData
cryptoBlob(blob
->cryptoBlob(), blob
->cryptoBlobLength());
242 CssmData decryptedBlob
, remData
;
243 decryptor
.decrypt(cryptoBlob
, decryptedBlob
, remData
);
244 DbBlob::PrivateBlob
*privateBlob
= decryptedBlob
.interpretedAs
<DbBlob::PrivateBlob
>();
246 // tentatively establish keys
247 mEncryptionKey
= makeRawKey(privateBlob
->encryptionKey
,
248 sizeof(privateBlob
->encryptionKey
), CSSM_ALGID_3DES_3KEY_EDE
,
249 CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
);
250 mSigningKey
= makeRawKey(privateBlob
->signingKey
,
251 sizeof(privateBlob
->signingKey
), CSSM_ALGID_SHA1HMAC
,
252 CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_VERIFY
);
254 // verify signature on the whole blob
255 CssmData signChunk
[] = {
256 CssmData(blob
->data(), offsetof(DbBlob
, blobSignature
)),
257 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
259 CSSM_ALGORITHMS verifyAlgorithm
= CSSM_ALGID_SHA1HMAC
;
260 #if defined(COMPAT_OSX_10_0)
261 if (blob
->version() == blob
->version_MacOS_10_0
)
262 verifyAlgorithm
= CSSM_ALGID_SHA1HMAC_LEGACY
; // BSafe bug compatibility
264 VerifyMac
verifier(Server::csp(), verifyAlgorithm
);
265 verifier
.key(mSigningKey
);
266 verifier
.verify(signChunk
, 2, CssmData(blob
->blobSignature
, sizeof(blob
->blobSignature
)));
268 // all checks out; start extracting fields
269 this->mEncryptionKey
= mEncryptionKey
;
270 this->mSigningKey
= mSigningKey
;
271 if (privateAclBlob
) {
272 // extract private ACL blob as a separately allocated area
273 uint32 blobLength
= decryptedBlob
.length() - sizeof(DbBlob::PrivateBlob
);
274 *privateAclBlob
= CssmAllocator::standard().malloc(blobLength
);
275 memcpy(*privateAclBlob
, privateBlob
->privateAclBlob(), blobLength
);
278 // secrets have been established
280 CssmAllocator::standard().free(privateBlob
);
287 KeyBlob
*DatabaseCryptoCore::encodeKeyCore(const CssmKey
&inKey
,
288 const CssmData
&publicAcl
, const CssmData
&privateAcl
) const
290 assert(isValid()); // need our database secrets
294 Server::active().random(iv
);
296 // extract and hold some header bits the CSP does not want to see
298 uint32 heldAttributes
= key
.attributes() & managedAttributes
;
299 key
.clearAttribute(managedAttributes
);
300 key
.setAttribute(forcedAttributes
);
302 // use a CMS wrap to encrypt the key
303 WrapKey
wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
304 wrap
.key(mEncryptionKey
);
305 wrap
.mode(CSSM_ALGMODE_CBCPadIV8
);
306 wrap
.padding(CSSM_PADDING_PKCS1
);
307 CssmData
ivd(iv
, sizeof(iv
)); wrap
.initVector(ivd
);
308 wrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
,
309 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
));
311 wrap(key
, wrappedKey
, &privateAcl
);
313 // stick the held attribute bits back in
314 key
.clearAttribute(forcedAttributes
);
315 key
.setAttribute(heldAttributes
);
317 // allocate the final KeyBlob, uh, blob
318 size_t length
= sizeof(KeyBlob
) + publicAcl
.length() + wrappedKey
.length();
319 KeyBlob
*blob
= CssmAllocator::standard().malloc
<KeyBlob
>(length
);
321 // assemble the KeyBlob
322 memset(blob
, 0, sizeof(KeyBlob
)); // fill alignment gaps
324 memcpy(blob
->iv
, iv
, sizeof(iv
));
325 blob
->header
= key
.header();
326 h2ni(blob
->header
); // endian-correct the header
327 blob
->wrappedHeader
.blobType
= wrappedKey
.blobType();
328 blob
->wrappedHeader
.blobFormat
= wrappedKey
.blobFormat();
329 blob
->wrappedHeader
.wrapAlgorithm
= wrappedKey
.wrapAlgorithm();
330 blob
->wrappedHeader
.wrapMode
= wrappedKey
.wrapMode();
331 memcpy(blob
->publicAclBlob(), publicAcl
, publicAcl
.length());
332 blob
->startCryptoBlob
= sizeof(KeyBlob
) + publicAcl
.length();
333 memcpy(blob
->cryptoBlob(), wrappedKey
.data(), wrappedKey
.length());
334 blob
->totalLength
= blob
->startCryptoBlob
+ wrappedKey
.length();
337 CssmData signChunk
[] = {
338 CssmData(blob
->data(), offsetof(KeyBlob
, blobSignature
)),
339 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
341 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
342 GenerateMac
signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY
); //@@@!!! CRUD
343 signer
.key(mSigningKey
);
344 signer
.sign(signChunk
, 2, signature
);
345 assert(signature
.length() == sizeof(blob
->blobSignature
));
347 // all done. Clean up
348 Server::csp()->allocator().free(wrappedKey
);
356 void DatabaseCryptoCore::decodeKeyCore(KeyBlob
*blob
,
357 CssmKey
&key
, void * &pubAcl
, void * &privAcl
) const
359 assert(isValid()); // need our database secrets
361 // Assemble the encrypted blob as a CSSM "wrapped key"
363 wrappedKey
.KeyHeader
= blob
->header
;
364 h2ni(wrappedKey
.KeyHeader
);
365 wrappedKey
.blobType(blob
->wrappedHeader
.blobType
);
366 wrappedKey
.blobFormat(blob
->wrappedHeader
.blobFormat
);
367 wrappedKey
.wrapAlgorithm(blob
->wrappedHeader
.wrapAlgorithm
);
368 wrappedKey
.wrapMode(blob
->wrappedHeader
.wrapMode
);
369 wrappedKey
.KeyData
= CssmData(blob
->cryptoBlob(), blob
->cryptoBlobLength());
371 // verify signature (check against corruption)
372 CssmData signChunk
[] = {
373 CssmData::wrap(blob
, offsetof(KeyBlob
, blobSignature
)),
374 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
376 CSSM_ALGORITHMS verifyAlgorithm
= CSSM_ALGID_SHA1HMAC
;
377 #if defined(COMPAT_OSX_10_0)
378 if (blob
->version() == blob
->version_MacOS_10_0
)
379 verifyAlgorithm
= CSSM_ALGID_SHA1HMAC_LEGACY
; // BSafe bug compatibility
381 VerifyMac
verifier(Server::csp(), verifyAlgorithm
);
382 verifier
.key(mSigningKey
);
383 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
384 verifier
.verify(signChunk
, 2, signature
);
386 // extract and hold some header bits the CSP does not want to see
387 uint32 heldAttributes
= n2h(blob
->header
.attributes()) & managedAttributes
;
389 // decrypt the key using an unwrapping operation
390 UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
391 unwrap
.key(mEncryptionKey
);
392 unwrap
.mode(CSSM_ALGMODE_CBCPadIV8
);
393 unwrap
.padding(CSSM_PADDING_PKCS1
);
394 CssmData
ivd(blob
->iv
, sizeof(blob
->iv
)); unwrap
.initVector(ivd
);
395 unwrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
,
396 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
));
397 CssmData privAclData
;
398 wrappedKey
.clearAttribute(managedAttributes
); //@@@ shouldn't be needed(?)
400 KeySpec(n2h(blob
->header
.usage()),
401 (n2h(blob
->header
.attributes()) & ~managedAttributes
) | forcedAttributes
),
404 // compare retrieved key headers with blob headers (sanity check)
405 // @@@ this should probably be checked over carefully
406 CssmKey::Header
&real
= key
.header();
407 CssmKey::Header
&incoming
= blob
->header
;
410 if (real
.HeaderVersion
!= incoming
.HeaderVersion
||
411 real
.cspGuid() != incoming
.cspGuid())
412 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
413 if (real
.algorithm() != incoming
.algorithm())
414 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
416 // re-insert held bits
417 key
.header().KeyAttr
|= heldAttributes
;
419 // got a valid key: return the pieces
420 pubAcl
= blob
->publicAclBlob(); // points into blob (shared)
421 privAcl
= privAclData
; // was allocated by CSP decrypt
422 // key was set by unwrap operation
427 // Derive the blob-specific database blob encryption key from the passphrase and the salt.
429 CssmClient::Key
DatabaseCryptoCore::deriveDbMasterKey(const CssmData
&passphrase
) const
431 // derive an encryption key and IV from passphrase and salt
432 CssmClient::DeriveKey
makeKey(Server::csp(),
433 CSSM_ALGID_PKCS5_PBKDF2
, CSSM_ALGID_3DES_3KEY_EDE
, 24 * 8);
434 makeKey
.iterationCount(1000);
435 makeKey
.salt(CssmData::wrap(mSalt
));
436 CSSM_PKCS5_PBKDF2_PARAMS params
;
437 params
.Passphrase
= passphrase
;
438 params
.PseudoRandomFunction
= CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
;
439 CssmData paramData
= CssmData::wrap(params
);
440 return makeKey(¶mData
, KeySpec(CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
441 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
446 // Turn raw keybits into a symmetric key in the CSP
448 CssmClient::Key
DatabaseCryptoCore::makeRawKey(void *data
, size_t length
,
449 CSSM_ALGORITHMS algid
, CSSM_KEYUSE usage
)
453 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
454 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
455 key
.header().AlgorithmId
= algid
;
456 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
457 key
.header().KeyUsage
= usage
;
458 key
.header().KeyAttr
= 0;
459 key
.KeyData
= CssmData(data
, length
);
461 // unwrap it into the CSP (but keep it raw)
462 UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
);
463 CssmKey unwrappedKey
;
464 CssmData descriptiveData
;
466 KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
),
467 unwrappedKey
, &descriptiveData
, NULL
);
468 return CssmClient::Key(Server::csp(), unwrappedKey
);