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() : mIsValid(false)
40 DatabaseCryptoCore::~DatabaseCryptoCore()
42 // key objects take care of themselves
47 // Generate new secrets for this crypto core.
49 void DatabaseCryptoCore::generateNewSecrets()
51 // create a random DES3 key
52 GenerateKey
desGenerator(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
, 24 * 8);
53 encryptionKey
= desGenerator(KeySpec(CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
,
54 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
56 // create a random 20 byte HMAC1/SHA1 signing "key"
57 GenerateKey
signGenerator(Server::csp(), CSSM_ALGID_SHA1HMAC
,
58 sizeof(DbBlob::PrivateBlob::signingKey
) * 8);
59 signingKey
= signGenerator(KeySpec(CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_VERIFY
,
60 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
62 // secrets established
68 // Encode a database blob from the core.
70 DbBlob
*DatabaseCryptoCore::encodeCore(const DbBlob
&blobTemplate
,
71 const CssmData
&passphrase
,
72 const CssmData
&publicAcl
, const CssmData
&privateAcl
) const
74 assert(isValid()); // must have secrets to work from
76 // make a new salt and IV
78 Server::active().random(salt
);
80 Server::active().random(iv
);
82 // derive blob encryption key
83 CssmClient::Key blobCryptKey
= deriveDbCryptoKey(passphrase
,
84 CssmData(salt
, sizeof(salt
)));
86 // build the encrypted section blob
87 CssmData
&encryptionBits
= *encryptionKey
;
88 CssmData
&signingBits
= *signingKey
;
90 incrypt
[0] = encryptionBits
;
91 incrypt
[1] = signingBits
;
92 incrypt
[2] = privateAcl
;
93 CssmData cryptoBlob
, remData
;
94 Encrypt
cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
95 cryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
96 cryptor
.padding(CSSM_PADDING_PKCS1
);
97 cryptor
.key(blobCryptKey
);
98 CssmData
ivd(iv
, sizeof(iv
)); cryptor
.initVector(ivd
);
99 cryptor
.encrypt(incrypt
, 3, &cryptoBlob
, 1, remData
);
101 // allocate the final DbBlob, uh, blob
102 size_t length
= sizeof(DbBlob
) + publicAcl
.length() + cryptoBlob
.length();
103 DbBlob
*blob
= CssmAllocator::standard().malloc
<DbBlob
>(length
);
105 // assemble the DbBlob
106 memset(blob
, 0x7d, sizeof(DbBlob
)); // deterministically fill any alignment gaps
108 blob
->randomSignature
= blobTemplate
.randomSignature
;
109 blob
->sequence
= blobTemplate
.sequence
;
110 blob
->params
= blobTemplate
.params
;
111 memcpy(blob
->salt
, salt
, sizeof(salt
));
112 memcpy(blob
->iv
, iv
, sizeof(iv
));
113 memcpy(blob
->publicAclBlob(), publicAcl
, publicAcl
.length());
114 blob
->startCryptoBlob
= sizeof(DbBlob
) + publicAcl
.length();
115 memcpy(blob
->cryptoBlob(), cryptoBlob
, cryptoBlob
.length());
116 blob
->totalLength
= blob
->startCryptoBlob
+ cryptoBlob
.length();
119 CssmData signChunk
[] = {
120 CssmData(blob
->data(), offsetof(DbBlob
, blobSignature
)),
121 CssmData(blob
->publicAclBlob(), publicAcl
.length() + cryptoBlob
.length())
123 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
124 GenerateMac
signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY
); //@@@!!! CRUD
125 signer
.key(signingKey
);
126 signer
.sign(signChunk
, 2, signature
);
127 assert(signature
.length() == sizeof(blob
->blobSignature
));
129 // all done. Clean up
130 Server::csp()->allocator().free(cryptoBlob
);
136 // Decode a database blob into the core.
137 // Returns false if the decoding fails.
139 void DatabaseCryptoCore::decodeCore(DbBlob
*blob
, const CssmData
&passphrase
,
140 void **privateAclBlob
)
142 // derive blob encryption key
143 CssmClient::Key blobCryptKey
= deriveDbCryptoKey(passphrase
,
144 CssmData(blob
->salt
, sizeof(blob
->salt
)));
146 // try to decrypt the cryptoblob section
147 Decrypt
decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
148 decryptor
.mode(CSSM_ALGMODE_CBCPadIV8
);
149 decryptor
.padding(CSSM_PADDING_PKCS1
);
150 decryptor
.key(blobCryptKey
);
151 CssmData
ivd(blob
->iv
, sizeof(blob
->iv
)); decryptor
.initVector(ivd
);
152 CssmData
cryptoBlob(blob
->cryptoBlob(), blob
->cryptoBlobLength());
153 CssmData decryptedBlob
, remData
;
154 decryptor
.decrypt(cryptoBlob
, decryptedBlob
, remData
);
155 DbBlob::PrivateBlob
*privateBlob
= decryptedBlob
.interpretedAs
<DbBlob::PrivateBlob
>();
157 // tentatively establish keys
158 CssmClient::Key encryptionKey
= makeRawKey(privateBlob
->encryptionKey
,
159 sizeof(privateBlob
->encryptionKey
), CSSM_ALGID_3DES_3KEY_EDE
,
160 CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
);
161 CssmClient::Key signingKey
= makeRawKey(privateBlob
->signingKey
,
162 sizeof(privateBlob
->signingKey
), CSSM_ALGID_SHA1HMAC
,
163 CSSM_KEYUSE_SIGN
| CSSM_KEYUSE_VERIFY
);
165 // verify signature on the whole blob
166 CssmData signChunk
[] = {
167 CssmData(blob
->data(), offsetof(DbBlob
, blobSignature
)),
168 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
170 CSSM_ALGORITHMS verifyAlgorithm
= CSSM_ALGID_SHA1HMAC
;
171 #if defined(COMPAT_OSX_10_0)
172 if (blob
->version
== blob
->version_MacOS_10_0
)
173 verifyAlgorithm
= CSSM_ALGID_SHA1HMAC_LEGACY
; // BSafe bug compatibility
175 VerifyMac
verifier(Server::csp(), verifyAlgorithm
);
176 verifier
.key(signingKey
);
177 verifier
.verify(signChunk
, 2, CssmData(blob
->blobSignature
, sizeof(blob
->blobSignature
)));
179 // all checks out; start extracting fields
180 this->encryptionKey
= encryptionKey
;
181 this->signingKey
= signingKey
;
182 if (privateAclBlob
) {
183 // extract private ACL blob as a separately allocated area
184 uint32 blobLength
= decryptedBlob
.length() - sizeof(DbBlob::PrivateBlob
);
185 *privateAclBlob
= CssmAllocator::standard().malloc(blobLength
);
186 memcpy(*privateAclBlob
, privateBlob
->privateAclBlob(), blobLength
);
189 // secrets have been established
191 CssmAllocator::standard().free(privateBlob
);
198 KeyBlob
*DatabaseCryptoCore::encodeKeyCore(const CssmKey
&inKey
,
199 const CssmData
&publicAcl
, const CssmData
&privateAcl
) const
201 assert(isValid()); // need our database secrets
205 Server::active().random(iv
);
207 // extract and hold some header bits the CSP does not want to see
209 uint32 heldAttributes
= key
.attributes() & managedAttributes
;
210 key
.clearAttribute(managedAttributes
);
212 // use a CMS wrap to encrypt the key
213 WrapKey
wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
214 wrap
.key(encryptionKey
);
215 wrap
.mode(CSSM_ALGMODE_CBCPadIV8
);
216 wrap
.padding(CSSM_PADDING_PKCS1
);
217 CssmData
ivd(iv
, sizeof(iv
)); wrap
.initVector(ivd
);
218 wrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
,
219 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
));
221 wrap(key
, wrappedKey
, &privateAcl
);
223 // stick the held attribute bits back in
224 key
.setAttribute(heldAttributes
);
226 // allocate the final KeyBlob, uh, blob
227 size_t length
= sizeof(KeyBlob
) + publicAcl
.length() + wrappedKey
.length();
228 KeyBlob
*blob
= CssmAllocator::standard().malloc
<KeyBlob
>(length
);
230 // assemble the KeyBlob
231 memset(blob
, 0, sizeof(KeyBlob
)); // fill alignment gaps
233 memcpy(blob
->iv
, iv
, sizeof(iv
));
234 blob
->header
= key
.header();
235 blob
->wrappedHeader
.blobType
= wrappedKey
.blobType();
236 blob
->wrappedHeader
.blobFormat
= wrappedKey
.blobFormat();
237 blob
->wrappedHeader
.wrapAlgorithm
= wrappedKey
.wrapAlgorithm();
238 blob
->wrappedHeader
.wrapMode
= wrappedKey
.wrapMode();
239 memcpy(blob
->publicAclBlob(), publicAcl
, publicAcl
.length());
240 blob
->startCryptoBlob
= sizeof(KeyBlob
) + publicAcl
.length();
241 memcpy(blob
->cryptoBlob(), wrappedKey
.data(), wrappedKey
.length());
242 blob
->totalLength
= blob
->startCryptoBlob
+ wrappedKey
.length();
245 CssmData signChunk
[] = {
246 CssmData(blob
->data(), offsetof(KeyBlob
, blobSignature
)),
247 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
249 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
250 GenerateMac
signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY
); //@@@!!! CRUD
251 signer
.key(signingKey
);
252 signer
.sign(signChunk
, 2, signature
);
253 assert(signature
.length() == sizeof(blob
->blobSignature
));
255 // all done. Clean up
256 Server::csp()->allocator().free(wrappedKey
);
264 void DatabaseCryptoCore::decodeKeyCore(KeyBlob
*blob
,
265 CssmKey
&key
, void * &pubAcl
, void * &privAcl
) const
267 assert(isValid()); // need our database secrets
269 // Assemble the encrypted blob as a CSSM "wrapped key"
271 wrappedKey
.KeyHeader
= blob
->header
;
272 wrappedKey
.blobType(blob
->wrappedHeader
.blobType
);
273 wrappedKey
.blobFormat(blob
->wrappedHeader
.blobFormat
);
274 wrappedKey
.wrapAlgorithm(blob
->wrappedHeader
.wrapAlgorithm
);
275 wrappedKey
.wrapMode(blob
->wrappedHeader
.wrapMode
);
276 wrappedKey
.KeyData
= CssmData(blob
->cryptoBlob(), blob
->cryptoBlobLength());
278 // verify signature (check against corruption)
279 CssmData signChunk
[] = {
280 CssmData::wrap(blob
, offsetof(KeyBlob
, blobSignature
)),
281 CssmData(blob
->publicAclBlob(), blob
->publicAclBlobLength() + blob
->cryptoBlobLength())
283 CSSM_ALGORITHMS verifyAlgorithm
= CSSM_ALGID_SHA1HMAC
;
284 #if defined(COMPAT_OSX_10_0)
285 if (blob
->version
== blob
->version_MacOS_10_0
)
286 verifyAlgorithm
= CSSM_ALGID_SHA1HMAC_LEGACY
; // BSafe bug compatibility
288 VerifyMac
verifier(Server::csp(), verifyAlgorithm
);
289 verifier
.key(signingKey
);
290 CssmData
signature(blob
->blobSignature
, sizeof(blob
->blobSignature
));
291 verifier
.verify(signChunk
, 2, signature
);
293 // extract and hold some header bits the CSP does not want to see
294 uint32 heldAttributes
= blob
->header
.attributes() & managedAttributes
;
296 // decrypt the key using an unwrapping operation
297 UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE
);
298 unwrap
.key(encryptionKey
);
299 unwrap
.mode(CSSM_ALGMODE_CBCPadIV8
);
300 unwrap
.padding(CSSM_PADDING_PKCS1
);
301 CssmData
ivd(blob
->iv
, sizeof(blob
->iv
)); unwrap
.initVector(ivd
);
302 unwrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
,
303 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
));
304 CssmData privAclData
;
305 wrappedKey
.clearAttribute(managedAttributes
); //@@@ shouldn't be needed(?)
307 KeySpec(blob
->header
.usage(), blob
->header
.attributes() & ~managedAttributes
),
310 // compare retrieved key headers with blob headers (sanity check)
311 // @@@ this should probably be checked over carefully
312 CssmKey::Header
&real
= key
.header();
313 CssmKey::Header
&incoming
= blob
->header
;
314 if (real
.HeaderVersion
!= incoming
.HeaderVersion
||
315 real
.cspGuid() != incoming
.cspGuid() ||
316 real
.blobFormat() != incoming
.blobFormat())
317 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
318 if (real
.algorithm() != incoming
.algorithm())
319 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
321 // re-insert held bits
322 key
.header().KeyAttr
|= heldAttributes
;
324 // got a valid key: return the pieces
325 pubAcl
= blob
->publicAclBlob(); // points into blob (shared)
326 privAcl
= privAclData
; // was allocated by CSP decrypt
327 // key was set by unwrap operation
332 // Derive the blob-specific database blob encryption key from the passphrase and the salt.
334 CssmClient::Key
DatabaseCryptoCore::deriveDbCryptoKey(const CssmData
&passphrase
,
335 const CssmData
&salt
) const
337 // derive an encryption key and IV from passphrase and salt
338 CssmClient::DeriveKey
makeKey(Server::csp(),
339 CSSM_ALGID_PKCS5_PBKDF2
, CSSM_ALGID_3DES_3KEY_EDE
, 24 * 8);
340 makeKey
.iterationCount(1000);
342 CSSM_PKCS5_PBKDF2_PARAMS params
;
343 params
.Passphrase
= passphrase
;
344 params
.PseudoRandomFunction
= CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
;
345 CssmData paramData
= CssmData::wrap(params
);
346 return makeKey(¶mData
, KeySpec(CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
347 CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
));
352 // Turn raw keybits into a symmetric key in the CSP
354 CssmClient::Key
DatabaseCryptoCore::makeRawKey(void *data
, size_t length
,
355 CSSM_ALGORITHMS algid
, CSSM_KEYUSE usage
)
359 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
360 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
361 key
.header().AlgorithmId
= algid
;
362 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
363 key
.header().KeyUsage
= usage
;
364 key
.header().KeyAttr
= 0;
365 key
.KeyData
= CssmData(data
, length
);
367 // unwrap it into the CSP (but keep it raw)
368 UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
);
369 CssmKey unwrappedKey
;
370 CssmData descriptiveData
;
372 KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
),
373 unwrappedKey
, &descriptiveData
, NULL
);
374 return CssmClient::Key(Server::csp(), unwrappedKey
);