]> git.saurik.com Git - apple/securityd.git/blob - src/dbcrypto.cpp
f885440eb13bf1d3e38ef013b56c37238a038323
[apple/securityd.git] / src / dbcrypto.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26
27 //
28 // dbcrypto - cryptographic core for database and key blob cryptography
29 //
30 #include "dbcrypto.h"
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
41 using namespace CssmClient;
42
43
44 DatabaseCryptoCore::DatabaseCryptoCore() : mHaveMaster(false), mIsValid(false)
45 {
46 }
47
48
49 DatabaseCryptoCore::~DatabaseCryptoCore()
50 {
51 // key objects take care of themselves
52 }
53
54
55 //
56 // Forget the secrets
57 //
58 void DatabaseCryptoCore::invalidate()
59 {
60 mMasterKey.release();
61 mHaveMaster = false;
62
63 mEncryptionKey.release();
64 mSigningKey.release();
65 mIsValid = false;
66 }
67
68
69 //
70 // Generate new secrets for this crypto core.
71 //
72 void DatabaseCryptoCore::generateNewSecrets()
73 {
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));
78
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));
84
85 // secrets established
86 mIsValid = true;
87 }
88
89
90 CssmClient::Key DatabaseCryptoCore::masterKey()
91 {
92 assert(mHaveMaster);
93 return mMasterKey;
94 }
95
96
97 //
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.
102 //
103 void DatabaseCryptoCore::setup(const DbBlob *blob, const CssmData &passphrase)
104 {
105 if (blob)
106 memcpy(mSalt, blob->salt, sizeof(mSalt));
107 else
108 Server::active().random(mSalt);
109 mMasterKey = deriveDbMasterKey(passphrase);
110 mHaveMaster = true;
111 }
112
113
114 //
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.
118 //
119 void DatabaseCryptoCore::setup(const DbBlob *blob, CssmClient::Key master)
120 {
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);
127
128 // accept it
129 if (blob)
130 memcpy(mSalt, blob->salt, sizeof(mSalt));
131 else
132 Server::active().random(mSalt);
133 mMasterKey = master;
134 mHaveMaster = true;
135 }
136
137
138 //
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.
146 //
147 bool DatabaseCryptoCore::validatePassphrase(const CssmData &passphrase)
148 {
149 assert(hasMaster());
150 CssmClient::Key master = deriveDbMasterKey(passphrase);
151
152 // to compare master with mMaster, see if they encrypt alike
153 StringData probe
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));
161
162 cryptor.key(master);
163 CssmAutoData cipher1(Server::csp().allocator());
164 cryptor.encrypt(probe, cipher1.get(), noRemainder);
165
166 cryptor.key(mMasterKey);
167 CssmAutoData cipher2(Server::csp().allocator());
168 cryptor.encrypt(probe, cipher2.get(), noRemainder);
169
170 return cipher1 == cipher2;
171 }
172
173
174 //
175 // Encode a database blob from the core.
176 //
177 DbBlob *DatabaseCryptoCore::encodeCore(const DbBlob &blobTemplate,
178 const CssmData &publicAcl, const CssmData &privateAcl) const
179 {
180 assert(isValid()); // must have secrets to work from
181
182 // make a new IV
183 uint8 iv[8];
184 Server::active().random(iv);
185
186 // build the encrypted section blob
187 CssmData &encryptionBits = *mEncryptionKey;
188 CssmData &signingBits = *mSigningKey;
189 CssmData incrypt[3];
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);
200
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);
204
205 // assemble the DbBlob
206 memset(blob, 0x7d, sizeof(DbBlob)); // deterministically fill any alignment gaps
207 blob->initialize();
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();
217
218 // sign the blob
219 CssmData signChunk[] = {
220 CssmData(blob->data(), offsetof(DbBlob, blobSignature)),
221 CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length())
222 };
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));
228
229 // all done. Clean up
230 Server::csp()->allocator().free(cryptoBlob);
231 return blob;
232 }
233
234
235 //
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.
239 //
240 void DatabaseCryptoCore::decodeCore(DbBlob *blob, void **privateAclBlob)
241 {
242 assert(mHaveMaster); // must have master key installed
243
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>();
254
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);
262
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())
267 };
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
272 #endif
273 VerifyMac verifier(Server::csp(), verifyAlgorithm);
274 verifier.key(mSigningKey);
275 verifier.verify(signChunk, 2, CssmData(blob->blobSignature, sizeof(blob->blobSignature)));
276
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);
285 }
286
287 // secrets have been established
288 mIsValid = true;
289 Allocator::standard().free(privateBlob);
290 }
291
292
293 //
294 // Encode a key blob
295 //
296 KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey,
297 const CssmData &publicAcl, const CssmData &privateAcl) const
298 {
299 assert(isValid()); // need our database secrets
300
301 // create new IV
302 uint8 iv[8];
303 Server::active().random(iv);
304
305 // extract and hold some header bits the CSP does not want to see
306 CssmKey key = inKey;
307 uint32 heldAttributes = key.attributes() & managedAttributes;
308 key.clearAttribute(managedAttributes);
309 key.setAttribute(forcedAttributes);
310
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));
319 CssmKey wrappedKey;
320 wrap(key, wrappedKey, &privateAcl);
321
322 // stick the held attribute bits back in
323 key.clearAttribute(forcedAttributes);
324 key.setAttribute(heldAttributes);
325
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);
329
330 // assemble the KeyBlob
331 memset(blob, 0, sizeof(KeyBlob)); // fill alignment gaps
332 blob->initialize();
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();
344
345 // sign the blob
346 CssmData signChunk[] = {
347 CssmData(blob->data(), offsetof(KeyBlob, blobSignature)),
348 CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
349 };
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));
355
356 // all done. Clean up
357 Server::csp()->allocator().free(wrappedKey);
358 return blob;
359 }
360
361
362 //
363 // Decode a key blob
364 //
365 void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob,
366 CssmKey &key, void * &pubAcl, void * &privAcl) const
367 {
368 assert(isValid()); // need our database secrets
369
370 // Assemble the encrypted blob as a CSSM "wrapped key"
371 CssmKey wrappedKey;
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());
379
380 // verify signature (check against corruption)
381 CssmData signChunk[] = {
382 CssmData::wrap(blob, offsetof(KeyBlob, blobSignature)),
383 CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
384 };
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
389 #endif
390 VerifyMac verifier(Server::csp(), verifyAlgorithm);
391 verifier.key(mSigningKey);
392 CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
393 verifier.verify(signChunk, 2, signature);
394
395 // extract and hold some header bits the CSP does not want to see
396 uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes;
397
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(?)
408 unwrap(wrappedKey,
409 KeySpec(n2h(blob->header.usage()),
410 (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
411 key, &privAclData);
412
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;
417 n2hi(incoming);
418
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);
424
425 // re-insert held bits
426 key.header().KeyAttr |= heldAttributes;
427
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
432 }
433
434
435 //
436 // Derive the blob-specific database blob encryption key from the passphrase and the salt.
437 //
438 CssmClient::Key DatabaseCryptoCore::deriveDbMasterKey(const CssmData &passphrase) const
439 {
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(&paramData, KeySpec(CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
450 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
451 }
452
453
454 //
455 // Turn raw keybits into a symmetric key in the CSP
456 //
457 CssmClient::Key DatabaseCryptoCore::makeRawKey(void *data, size_t length,
458 CSSM_ALGORITHMS algid, CSSM_KEYUSE usage)
459 {
460 // build a fake key
461 CssmKey key;
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);
469
470 // unwrap it into the CSP (but keep it raw)
471 UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
472 CssmKey unwrappedKey;
473 CssmData descriptiveData;
474 unwrap(key,
475 KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE),
476 unwrappedKey, &descriptiveData, NULL);
477 return CssmClient::Key(Server::csp(), unwrappedKey);
478 }