]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2000-2006,2013 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | // | |
26 | // dbcrypto - cryptographic core for database and key blob cryptography | |
27 | // | |
28 | #include "dbcrypto.h" | |
fa7225c8 | 29 | #include <security_utilities/casts.h> |
d8f41ccd A |
30 | #include <securityd_client/ssblob.h> |
31 | #include "server.h" // just for Server::csp() | |
32 | #include <security_cdsa_client/genkey.h> | |
33 | #include <security_cdsa_client/cryptoclient.h> | |
34 | #include <security_cdsa_client/keyclient.h> | |
35 | #include <security_cdsa_client/macclient.h> | |
36 | #include <security_cdsa_client/wrapkey.h> | |
37 | #include <security_cdsa_utilities/cssmendian.h> | |
38 | ||
39 | using namespace CssmClient; | |
40 | using LowLevelMemoryUtilities::fieldOffsetOf; | |
41 | ||
42 | ||
43 | // | |
44 | // The CryptoCore constructor doesn't do anything interesting. | |
45 | // It just initializes us to "empty". | |
46 | // | |
fa7225c8 | 47 | DatabaseCryptoCore::DatabaseCryptoCore(uint32 requestedVersion) : mBlobVersion(CommonBlob::version_MacOS_10_0), mHaveMaster(false), mIsValid(false) |
d8f41ccd | 48 | { |
fa7225c8 A |
49 | // If there's a specific version our callers want, give them that. Otherwise, ask CommonBlob what to do. |
50 | if(requestedVersion == CommonBlob::version_none) { | |
51 | mBlobVersion = CommonBlob::getCurrentVersion(); | |
52 | } else { | |
53 | mBlobVersion = requestedVersion; | |
54 | } | |
d8f41ccd A |
55 | } |
56 | ||
57 | DatabaseCryptoCore::~DatabaseCryptoCore() | |
58 | { | |
59 | // key objects take care of themselves | |
60 | } | |
61 | ||
62 | ||
63 | // | |
64 | // Forget the secrets | |
65 | // | |
66 | void DatabaseCryptoCore::invalidate() | |
67 | { | |
68 | mMasterKey.release(); | |
69 | mHaveMaster = false; | |
70 | ||
71 | mEncryptionKey.release(); | |
72 | mSigningKey.release(); | |
73 | mIsValid = false; | |
74 | } | |
75 | ||
fa7225c8 A |
76 | // |
77 | // Copy everything from another databasecryptocore | |
78 | // | |
79 | void DatabaseCryptoCore::initializeFrom(DatabaseCryptoCore& core, uint32 requestedVersion) { | |
80 | if(core.hasMaster()) { | |
81 | mMasterKey = core.mMasterKey; | |
82 | memcpy(mSalt, core.mSalt, sizeof(mSalt)); | |
83 | mHaveMaster = core.mHaveMaster; | |
84 | } else { | |
85 | mHaveMaster = false; | |
86 | } | |
87 | ||
88 | if(core.isValid()) { | |
89 | importSecrets(core); | |
90 | } else { | |
91 | mIsValid = false; | |
92 | } | |
93 | ||
94 | // As the last thing we do, check if we should be changing the version of this blob. | |
95 | if(requestedVersion == CommonBlob::version_none) { | |
96 | mBlobVersion = core.mBlobVersion; | |
97 | } else { | |
98 | mBlobVersion = requestedVersion; | |
99 | } | |
100 | } | |
d8f41ccd A |
101 | |
102 | // | |
103 | // Generate new secrets for this crypto core. | |
104 | // | |
105 | void DatabaseCryptoCore::generateNewSecrets() | |
106 | { | |
107 | // create a random DES3 key | |
108 | GenerateKey desGenerator(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE, 24 * 8); | |
109 | mEncryptionKey = desGenerator(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP, | |
110 | CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE)); | |
111 | ||
112 | // create a random 20 byte HMAC/SHA1 signing "key" | |
113 | GenerateKey signGenerator(Server::csp(), CSSM_ALGID_SHA1HMAC, | |
114 | sizeof(DbBlob::PrivateBlob::SigningKey) * 8); | |
115 | mSigningKey = signGenerator(KeySpec(CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY, | |
116 | CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE)); | |
117 | ||
118 | // secrets established | |
119 | mIsValid = true; | |
120 | } | |
121 | ||
122 | ||
123 | CssmClient::Key DatabaseCryptoCore::masterKey() | |
124 | { | |
125 | assert(mHaveMaster); | |
126 | return mMasterKey; | |
127 | } | |
128 | ||
129 | ||
130 | // | |
131 | // Establish the master secret as derived from a passphrase passed in. | |
132 | // If a DbBlob is passed, take the salt from it and remember it. | |
133 | // If a NULL DbBlob is passed, generate a new (random) salt. | |
134 | // Note that the passphrase is NOT remembered; only the master key. | |
135 | // | |
e3d460c9 | 136 | void DatabaseCryptoCore::setup(const DbBlob *blob, const CssmData &passphrase, bool copyVersion /* = true */) |
d8f41ccd | 137 | { |
e3d460c9 A |
138 | if (blob) { |
139 | if(copyVersion) { | |
140 | mBlobVersion = blob->version(); | |
141 | } | |
142 | memcpy(mSalt, blob->salt, sizeof(mSalt)); | |
79b9da22 | 143 | } else { |
d8f41ccd | 144 | Server::active().random(mSalt); |
79b9da22 | 145 | } |
d8f41ccd A |
146 | mMasterKey = deriveDbMasterKey(passphrase); |
147 | mHaveMaster = true; | |
148 | } | |
149 | ||
150 | ||
151 | // | |
152 | // Establish the master secret directly from a master key passed in. | |
153 | // We will copy the KeyData (caller still owns its copy). | |
154 | // Blob/salt handling as above. | |
155 | // | |
e3d460c9 | 156 | void DatabaseCryptoCore::setup(const DbBlob *blob, CssmClient::Key master, bool copyVersion /* = true */) |
d8f41ccd A |
157 | { |
158 | // pre-screen the key | |
159 | CssmKey::Header header = master.header(); | |
160 | if (header.keyClass() != CSSM_KEYCLASS_SESSION_KEY) | |
161 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); | |
162 | if (header.algorithm() != CSSM_ALGID_3DES_3KEY_EDE) | |
163 | CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); | |
164 | ||
165 | // accept it | |
e3d460c9 A |
166 | if (blob) { |
167 | if(copyVersion) { | |
168 | mBlobVersion = blob->version(); | |
169 | } | |
170 | memcpy(mSalt, blob->salt, sizeof(mSalt)); | |
79b9da22 | 171 | } else { |
d8f41ccd | 172 | Server::active().random(mSalt); |
79b9da22 | 173 | } |
d8f41ccd A |
174 | mMasterKey = master; |
175 | mHaveMaster = true; | |
176 | } | |
177 | ||
178 | bool DatabaseCryptoCore::get_encryption_key(CssmOwnedData &data) | |
179 | { | |
180 | bool result = false; | |
181 | if (isValid()) { | |
182 | data = mEncryptionKey->keyData(); | |
183 | result = true; | |
184 | } | |
185 | return result; | |
186 | } | |
187 | ||
188 | // | |
189 | // Given a putative passphrase, determine whether that passphrase | |
190 | // properly generates the database's master secret. | |
191 | // Return a boolean accordingly. Do not change our state. | |
192 | // The database must have a master secret (to compare with). | |
193 | // Note that any errors thrown by the cryptography here will actually | |
194 | // throw out of validatePassphrase, since they "should not happen" and | |
195 | // thus indicate a problem *beyond* (just) a bad passphrase. | |
196 | // | |
197 | bool DatabaseCryptoCore::validatePassphrase(const CssmData &passphrase) | |
198 | { | |
d8f41ccd | 199 | CssmClient::Key master = deriveDbMasterKey(passphrase); |
fa7225c8 A |
200 | return validateKey(master); |
201 | } | |
202 | ||
203 | bool DatabaseCryptoCore::validateKey(const CssmClient::Key& master) { | |
204 | assert(hasMaster()); | |
d8f41ccd A |
205 | // to compare master with mMaster, see if they encrypt alike |
206 | StringData probe | |
207 | ("Now is the time for all good processes to come to the aid of their kernel."); | |
208 | CssmData noRemainder((void *)1, 0); // no cipher overflow | |
209 | Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE); | |
210 | cryptor.mode(CSSM_ALGMODE_CBCPadIV8); | |
211 | cryptor.padding(CSSM_PADDING_PKCS1); | |
212 | uint8 iv[8]; // leave uninitialized; pseudo-random is cool | |
866f8763 A |
213 | CssmData ivData = CssmData::wrap(iv); |
214 | cryptor.initVector(ivData); | |
d8f41ccd A |
215 | |
216 | cryptor.key(master); | |
217 | CssmAutoData cipher1(Server::csp().allocator()); | |
218 | cryptor.encrypt(probe, cipher1.get(), noRemainder); | |
219 | ||
220 | cryptor.key(mMasterKey); | |
221 | CssmAutoData cipher2(Server::csp().allocator()); | |
222 | cryptor.encrypt(probe, cipher2.get(), noRemainder); | |
223 | ||
224 | return cipher1 == cipher2; | |
225 | } | |
226 | ||
227 | ||
228 | // | |
229 | // Encode a database blob from the core. | |
230 | // | |
231 | DbBlob *DatabaseCryptoCore::encodeCore(const DbBlob &blobTemplate, | |
232 | const CssmData &publicAcl, const CssmData &privateAcl) const | |
233 | { | |
234 | assert(isValid()); // must have secrets to work from | |
235 | ||
236 | // make a new IV | |
237 | uint8 iv[8]; | |
238 | Server::active().random(iv); | |
239 | ||
240 | // build the encrypted section blob | |
241 | CssmData &encryptionBits = *mEncryptionKey; | |
242 | CssmData &signingBits = *mSigningKey; | |
243 | CssmData incrypt[3]; | |
244 | incrypt[0] = encryptionBits; | |
245 | incrypt[1] = signingBits; | |
246 | incrypt[2] = privateAcl; | |
247 | CssmData cryptoBlob, remData; | |
248 | Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE); | |
249 | cryptor.mode(CSSM_ALGMODE_CBCPadIV8); | |
250 | cryptor.padding(CSSM_PADDING_PKCS1); | |
251 | cryptor.key(mMasterKey); | |
252 | CssmData ivd(iv, sizeof(iv)); cryptor.initVector(ivd); | |
253 | cryptor.encrypt(incrypt, 3, &cryptoBlob, 1, remData); | |
254 | ||
255 | // allocate the final DbBlob, uh, blob | |
256 | size_t length = sizeof(DbBlob) + publicAcl.length() + cryptoBlob.length(); | |
257 | DbBlob *blob = Allocator::standard().malloc<DbBlob>(length); | |
258 | ||
259 | // assemble the DbBlob | |
260 | memset(blob, 0x7d, sizeof(DbBlob)); // deterministically fill any alignment gaps | |
e3d460c9 | 261 | blob->initialize(mBlobVersion); |
d8f41ccd A |
262 | blob->randomSignature = blobTemplate.randomSignature; |
263 | blob->sequence = blobTemplate.sequence; | |
264 | blob->params = blobTemplate.params; | |
265 | memcpy(blob->salt, mSalt, sizeof(blob->salt)); | |
266 | memcpy(blob->iv, iv, sizeof(iv)); | |
267 | memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length()); | |
fa7225c8 | 268 | blob->startCryptoBlob = sizeof(DbBlob) + int_cast<size_t, uint32_t>(publicAcl.length()); |
d8f41ccd | 269 | memcpy(blob->cryptoBlob(), cryptoBlob, cryptoBlob.length()); |
fa7225c8 | 270 | blob->totalLength = blob->startCryptoBlob + int_cast<size_t, uint32_t>(cryptoBlob.length()); |
d8f41ccd A |
271 | |
272 | // sign the blob | |
273 | CssmData signChunk[] = { | |
274 | CssmData(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)), | |
275 | CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length()) | |
276 | }; | |
277 | CssmData signature(blob->blobSignature, sizeof(blob->blobSignature)); | |
e3d460c9 A |
278 | |
279 | CSSM_ALGORITHMS signingAlgorithm = CSSM_ALGID_SHA1HMAC; | |
280 | #if defined(COMPAT_OSX_10_0) | |
281 | if (blob->version() == blob->version_MacOS_10_0) | |
282 | signingAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; // BSafe bug compatibility | |
283 | #endif | |
284 | GenerateMac signer(Server::csp(), signingAlgorithm); | |
d8f41ccd A |
285 | signer.key(mSigningKey); |
286 | signer.sign(signChunk, 2, signature); | |
287 | assert(signature.length() == sizeof(blob->blobSignature)); | |
288 | ||
289 | // all done. Clean up | |
290 | Server::csp()->allocator().free(cryptoBlob); | |
291 | return blob; | |
292 | } | |
293 | ||
294 | ||
295 | // | |
296 | // Decode a database blob into the core. | |
297 | // Throws exceptions if decoding fails. | |
298 | // Memory returned in privateAclBlob is allocated and becomes owned by caller. | |
299 | // | |
300 | void DatabaseCryptoCore::decodeCore(const DbBlob *blob, void **privateAclBlob) | |
301 | { | |
302 | assert(mHaveMaster); // must have master key installed | |
303 | ||
304 | // try to decrypt the cryptoblob section | |
305 | Decrypt decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE); | |
306 | decryptor.mode(CSSM_ALGMODE_CBCPadIV8); | |
307 | decryptor.padding(CSSM_PADDING_PKCS1); | |
308 | decryptor.key(mMasterKey); | |
309 | CssmData ivd = CssmData::wrap(blob->iv); decryptor.initVector(ivd); | |
310 | CssmData cryptoBlob = CssmData::wrap(blob->cryptoBlob(), blob->cryptoBlobLength()); | |
311 | CssmData decryptedBlob, remData; | |
312 | decryptor.decrypt(cryptoBlob, decryptedBlob, remData); | |
313 | DbBlob::PrivateBlob *privateBlob = decryptedBlob.interpretedAs<DbBlob::PrivateBlob>(); | |
314 | ||
315 | // tentatively establish keys | |
316 | mEncryptionKey = makeRawKey(privateBlob->encryptionKey, | |
317 | sizeof(privateBlob->encryptionKey), CSSM_ALGID_3DES_3KEY_EDE, | |
318 | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP); | |
319 | mSigningKey = makeRawKey(privateBlob->signingKey, | |
320 | sizeof(privateBlob->signingKey), CSSM_ALGID_SHA1HMAC, | |
321 | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY); | |
322 | ||
323 | // verify signature on the whole blob | |
324 | CssmData signChunk[] = { | |
325 | CssmData::wrap(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)), | |
326 | CssmData::wrap(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength()) | |
327 | }; | |
328 | CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC; | |
329 | #if defined(COMPAT_OSX_10_0) | |
330 | if (blob->version() == blob->version_MacOS_10_0) | |
331 | verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; // BSafe bug compatibility | |
332 | #endif | |
333 | VerifyMac verifier(Server::csp(), verifyAlgorithm); | |
334 | verifier.key(mSigningKey); | |
335 | verifier.verify(signChunk, 2, CssmData::wrap(blob->blobSignature)); | |
336 | ||
337 | // all checks out; start extracting fields | |
338 | if (privateAclBlob) { | |
339 | // extract private ACL blob as a separately allocated area | |
fa7225c8 | 340 | uint32 blobLength = (uint32) decryptedBlob.length() - sizeof(DbBlob::PrivateBlob); |
d8f41ccd A |
341 | *privateAclBlob = Allocator::standard().malloc(blobLength); |
342 | memcpy(*privateAclBlob, privateBlob->privateAclBlob(), blobLength); | |
343 | } | |
344 | ||
345 | // secrets have been established | |
e3d460c9 | 346 | mBlobVersion = blob->version(); |
d8f41ccd A |
347 | mIsValid = true; |
348 | Allocator::standard().free(privateBlob); | |
349 | } | |
350 | ||
351 | ||
352 | // | |
353 | // Make another DatabaseCryptoCore's operational secrets our own. | |
354 | // Intended for keychain synchronization. | |
355 | // | |
356 | void DatabaseCryptoCore::importSecrets(const DatabaseCryptoCore &src) | |
357 | { | |
358 | assert(src.isValid()); // must have called src.decodeCore() first | |
359 | assert(hasMaster()); | |
360 | mEncryptionKey = src.mEncryptionKey; | |
361 | mSigningKey = src.mSigningKey; | |
e3d460c9 | 362 | mBlobVersion = src.mBlobVersion; // make sure we copy over all state |
d8f41ccd A |
363 | mIsValid = true; |
364 | } | |
365 | ||
366 | // | |
367 | // Encode a key blob | |
368 | // | |
369 | KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey, | |
370 | const CssmData &publicAcl, const CssmData &privateAcl, | |
371 | bool inTheClear) const | |
372 | { | |
373 | CssmKey key = inKey; | |
374 | uint8 iv[8]; | |
375 | CssmKey wrappedKey; | |
376 | ||
377 | if(inTheClear && (privateAcl.Length != 0)) { | |
378 | /* can't store private ACL component in the clear */ | |
379 | CssmError::throwMe(CSSMERR_DL_INVALID_ACCESS_CREDENTIALS); | |
380 | } | |
381 | ||
382 | // extract and hold some header bits the CSP does not want to see | |
383 | uint32 heldAttributes = key.attributes() & managedAttributes; | |
384 | key.clearAttribute(managedAttributes); | |
385 | key.setAttribute(forcedAttributes); | |
386 | ||
387 | if(inTheClear) { | |
388 | /* NULL wrap of public key */ | |
389 | WrapKey wrap(Server::csp(), CSSM_ALGID_NONE); | |
390 | wrap(key, wrappedKey, NULL); | |
391 | } | |
392 | else { | |
393 | assert(isValid()); // need our database secrets | |
394 | ||
395 | // create new IV | |
396 | Server::active().random(iv); | |
397 | ||
398 | // use a CMS wrap to encrypt the key | |
399 | WrapKey wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE); | |
400 | wrap.key(mEncryptionKey); | |
401 | wrap.mode(CSSM_ALGMODE_CBCPadIV8); | |
402 | wrap.padding(CSSM_PADDING_PKCS1); | |
403 | CssmData ivd(iv, sizeof(iv)); wrap.initVector(ivd); | |
404 | wrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, | |
405 | uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM)); | |
406 | wrap(key, wrappedKey, &privateAcl); | |
407 | } | |
408 | ||
409 | // stick the held attribute bits back in | |
410 | key.clearAttribute(forcedAttributes); | |
411 | key.setAttribute(heldAttributes); | |
412 | ||
413 | // allocate the final KeyBlob, uh, blob | |
414 | size_t length = sizeof(KeyBlob) + publicAcl.length() + wrappedKey.length(); | |
415 | KeyBlob *blob = Allocator::standard().malloc<KeyBlob>(length); | |
416 | ||
417 | // assemble the KeyBlob | |
418 | memset(blob, 0, sizeof(KeyBlob)); // fill alignment gaps | |
e3d460c9 | 419 | blob->initialize(mBlobVersion); |
d8f41ccd A |
420 | if(!inTheClear) { |
421 | memcpy(blob->iv, iv, sizeof(iv)); | |
422 | } | |
423 | blob->header = key.header(); | |
424 | h2ni(blob->header); // endian-correct the header | |
425 | blob->wrappedHeader.blobType = wrappedKey.blobType(); | |
426 | blob->wrappedHeader.blobFormat = wrappedKey.blobFormat(); | |
427 | blob->wrappedHeader.wrapAlgorithm = wrappedKey.wrapAlgorithm(); | |
428 | blob->wrappedHeader.wrapMode = wrappedKey.wrapMode(); | |
429 | memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length()); | |
fa7225c8 | 430 | blob->startCryptoBlob = sizeof(KeyBlob) + int_cast<size_t, uint32_t>(publicAcl.length()); |
d8f41ccd | 431 | memcpy(blob->cryptoBlob(), wrappedKey.data(), wrappedKey.length()); |
fa7225c8 | 432 | blob->totalLength = blob->startCryptoBlob + int_cast<size_t, uint32_t>(wrappedKey.length()); |
d8f41ccd A |
433 | |
434 | if(inTheClear) { | |
435 | /* indicate that this is cleartext for decoding */ | |
436 | blob->setClearTextSignature(); | |
437 | } | |
438 | else { | |
439 | // sign the blob | |
440 | CssmData signChunk[] = { | |
441 | CssmData(blob->data(), fieldOffsetOf(&KeyBlob::blobSignature)), | |
442 | CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength()) | |
443 | }; | |
444 | CssmData signature(blob->blobSignature, sizeof(blob->blobSignature)); | |
e3d460c9 A |
445 | |
446 | CSSM_ALGORITHMS signingAlgorithm = CSSM_ALGID_SHA1HMAC; | |
447 | #if defined(COMPAT_OSX_10_0) | |
448 | if (blob->version() == blob->version_MacOS_10_0) | |
449 | signingAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; // BSafe bug compatibility | |
450 | #endif | |
451 | GenerateMac signer(Server::csp(), signingAlgorithm); | |
d8f41ccd A |
452 | signer.key(mSigningKey); |
453 | signer.sign(signChunk, 2, signature); | |
454 | assert(signature.length() == sizeof(blob->blobSignature)); | |
455 | } | |
456 | ||
457 | // all done. Clean up | |
458 | Server::csp()->allocator().free(wrappedKey); | |
459 | return blob; | |
460 | } | |
461 | ||
462 | ||
463 | // | |
464 | // Decode a key blob | |
465 | // | |
466 | void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob, | |
467 | CssmKey &key, void * &pubAcl, void * &privAcl) const | |
468 | { | |
e3d460c9 A |
469 | // Note that we can't do anything with this key's version(). |
470 | ||
d8f41ccd A |
471 | // Assemble the encrypted blob as a CSSM "wrapped key" |
472 | CssmKey wrappedKey; | |
473 | wrappedKey.KeyHeader = blob->header; | |
474 | h2ni(wrappedKey.KeyHeader); | |
475 | wrappedKey.blobType(blob->wrappedHeader.blobType); | |
476 | wrappedKey.blobFormat(blob->wrappedHeader.blobFormat); | |
477 | wrappedKey.wrapAlgorithm(blob->wrappedHeader.wrapAlgorithm); | |
478 | wrappedKey.wrapMode(blob->wrappedHeader.wrapMode); | |
479 | wrappedKey.KeyData = CssmData(blob->cryptoBlob(), blob->cryptoBlobLength()); | |
480 | ||
481 | bool inTheClear = blob->isClearText(); | |
482 | if(!inTheClear) { | |
483 | // verify signature (check against corruption) | |
484 | assert(isValid()); // need our database secrets | |
485 | CssmData signChunk[] = { | |
486 | CssmData::wrap(blob, fieldOffsetOf(&KeyBlob::blobSignature)), | |
487 | CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength()) | |
488 | }; | |
489 | CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC; | |
490 | #if defined(COMPAT_OSX_10_0) | |
491 | if (blob->version() == blob->version_MacOS_10_0) | |
492 | verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; // BSafe bug compatibility | |
493 | #endif | |
494 | VerifyMac verifier(Server::csp(), verifyAlgorithm); | |
495 | verifier.key(mSigningKey); | |
496 | CssmData signature(blob->blobSignature, sizeof(blob->blobSignature)); | |
497 | verifier.verify(signChunk, 2, signature); | |
498 | } | |
499 | /* else signature indicates cleartext */ | |
500 | ||
501 | // extract and hold some header bits the CSP does not want to see | |
502 | uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes; | |
503 | ||
504 | CssmData privAclData; | |
505 | if(inTheClear) { | |
506 | /* NULL unwrap */ | |
507 | UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE); | |
508 | wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?) | |
509 | unwrap(wrappedKey, | |
510 | KeySpec(n2h(blob->header.usage()), | |
511 | (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes), | |
512 | key, &privAclData); | |
513 | } | |
514 | else { | |
515 | // decrypt the key using an unwrapping operation | |
516 | UnwrapKey unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE); | |
517 | unwrap.key(mEncryptionKey); | |
518 | unwrap.mode(CSSM_ALGMODE_CBCPadIV8); | |
519 | unwrap.padding(CSSM_PADDING_PKCS1); | |
520 | CssmData ivd(blob->iv, sizeof(blob->iv)); unwrap.initVector(ivd); | |
521 | unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, | |
522 | uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM)); | |
523 | wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?) | |
524 | unwrap(wrappedKey, | |
525 | KeySpec(n2h(blob->header.usage()), | |
526 | (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes), | |
527 | key, &privAclData); | |
528 | } | |
529 | ||
530 | // compare retrieved key headers with blob headers (sanity check) | |
531 | // @@@ this should probably be checked over carefully | |
532 | CssmKey::Header &real = key.header(); | |
533 | CssmKey::Header &incoming = blob->header; | |
534 | n2hi(incoming); | |
535 | ||
536 | if (real.HeaderVersion != incoming.HeaderVersion || | |
537 | real.cspGuid() != incoming.cspGuid()) | |
538 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); | |
539 | if (real.algorithm() != incoming.algorithm()) | |
540 | CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); | |
541 | ||
542 | // re-insert held bits | |
543 | key.header().KeyAttr |= heldAttributes; | |
544 | ||
545 | if(inTheClear && (real.keyClass() != CSSM_KEYCLASS_PUBLIC_KEY)) { | |
546 | /* Spoof - cleartext KeyBlob passed off as private key */ | |
547 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); | |
548 | } | |
549 | ||
550 | // got a valid key: return the pieces | |
551 | pubAcl = blob->publicAclBlob(); // points into blob (shared) | |
552 | privAcl = privAclData; // was allocated by CSP decrypt, else NULL for | |
553 | // cleatext keys | |
554 | // key was set by unwrap operation | |
555 | } | |
556 | ||
557 | ||
558 | // | |
559 | // Derive the blob-specific database blob encryption key from the passphrase and the salt. | |
560 | // | |
561 | CssmClient::Key DatabaseCryptoCore::deriveDbMasterKey(const CssmData &passphrase) const | |
562 | { | |
563 | // derive an encryption key and IV from passphrase and salt | |
564 | CssmClient::DeriveKey makeKey(Server::csp(), | |
565 | CSSM_ALGID_PKCS5_PBKDF2, CSSM_ALGID_3DES_3KEY_EDE, 24 * 8); | |
566 | makeKey.iterationCount(1000); | |
567 | CssmData salt = CssmData::wrap(mSalt); | |
568 | makeKey.salt(salt); | |
569 | CSSM_PKCS5_PBKDF2_PARAMS params; | |
570 | params.Passphrase = passphrase; | |
571 | params.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1; | |
572 | CssmData paramData = CssmData::wrap(params); | |
573 | return makeKey(¶mData, KeySpec(CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT, | |
574 | CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE)); | |
575 | } | |
576 | ||
577 | ||
578 | // | |
579 | // Turn raw keybits into a symmetric key in the CSP | |
580 | // | |
581 | CssmClient::Key DatabaseCryptoCore::makeRawKey(void *data, size_t length, | |
582 | CSSM_ALGORITHMS algid, CSSM_KEYUSE usage) | |
583 | { | |
584 | // build a fake key | |
585 | CssmKey key; | |
586 | key.header().BlobType = CSSM_KEYBLOB_RAW; | |
587 | key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; | |
588 | key.header().AlgorithmId = algid; | |
589 | key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY; | |
590 | key.header().KeyUsage = usage; | |
591 | key.header().KeyAttr = 0; | |
592 | key.KeyData = CssmData(data, length); | |
593 | ||
594 | // unwrap it into the CSP (but keep it raw) | |
595 | UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE); | |
596 | CssmKey unwrappedKey; | |
597 | CssmData descriptiveData; | |
598 | unwrap(key, | |
599 | KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE), | |
600 | unwrappedKey, &descriptiveData, NULL); | |
601 | return CssmClient::Key(Server::csp(), unwrappedKey); | |
602 | } |