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