]> git.saurik.com Git - apple/security.git/blob - SecurityServer/dbcrypto.cpp
Security-28.tar.gz
[apple/security.git] / SecurityServer / dbcrypto.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 //
20 // dbcrypto - cryptographic core for database and key blob cryptography
21 //
22 #include "dbcrypto.h"
23 #include "ssblob.h"
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>
30
31
32 using namespace CssmClient;
33
34
35 DatabaseCryptoCore::DatabaseCryptoCore() : mIsValid(false)
36 {
37 }
38
39
40 DatabaseCryptoCore::~DatabaseCryptoCore()
41 {
42 // key objects take care of themselves
43 }
44
45
46 //
47 // Generate new secrets for this crypto core.
48 //
49 void DatabaseCryptoCore::generateNewSecrets()
50 {
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));
55
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));
61
62 // secrets established
63 mIsValid = true;
64 }
65
66
67 //
68 // Encode a database blob from the core.
69 //
70 DbBlob *DatabaseCryptoCore::encodeCore(const DbBlob &blobTemplate,
71 const CssmData &passphrase,
72 const CssmData &publicAcl, const CssmData &privateAcl) const
73 {
74 assert(isValid()); // must have secrets to work from
75
76 // make a new salt and IV
77 uint8 salt[20];
78 Server::active().random(salt);
79 uint8 iv[8];
80 Server::active().random(iv);
81
82 // derive blob encryption key
83 CssmClient::Key blobCryptKey = deriveDbCryptoKey(passphrase,
84 CssmData(salt, sizeof(salt)));
85
86 // build the encrypted section blob
87 CssmData &encryptionBits = *encryptionKey;
88 CssmData &signingBits = *signingKey;
89 CssmData incrypt[3];
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);
100
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);
104
105 // assemble the DbBlob
106 memset(blob, 0x7d, sizeof(DbBlob)); // deterministically fill any alignment gaps
107 blob->initialize();
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();
117
118 // sign the blob
119 CssmData signChunk[] = {
120 CssmData(blob->data(), offsetof(DbBlob, blobSignature)),
121 CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length())
122 };
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));
128
129 // all done. Clean up
130 Server::csp()->allocator().free(cryptoBlob);
131 return blob;
132 }
133
134
135 //
136 // Decode a database blob into the core.
137 // Returns false if the decoding fails.
138 //
139 void DatabaseCryptoCore::decodeCore(DbBlob *blob, const CssmData &passphrase,
140 void **privateAclBlob)
141 {
142 // derive blob encryption key
143 CssmClient::Key blobCryptKey = deriveDbCryptoKey(passphrase,
144 CssmData(blob->salt, sizeof(blob->salt)));
145
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>();
156
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);
164
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())
169 };
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
174 #endif
175 VerifyMac verifier(Server::csp(), verifyAlgorithm);
176 verifier.key(signingKey);
177 verifier.verify(signChunk, 2, CssmData(blob->blobSignature, sizeof(blob->blobSignature)));
178
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);
187 }
188
189 // secrets have been established
190 mIsValid = true;
191 CssmAllocator::standard().free(privateBlob);
192 }
193
194
195 //
196 // Encode a key blob
197 //
198 KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey,
199 const CssmData &publicAcl, const CssmData &privateAcl) const
200 {
201 assert(isValid()); // need our database secrets
202
203 // create new IV
204 uint8 iv[8];
205 Server::active().random(iv);
206
207 // extract and hold some header bits the CSP does not want to see
208 CssmKey key = inKey;
209 uint32 heldAttributes = key.attributes() & managedAttributes;
210 key.clearAttribute(managedAttributes);
211
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));
220 CssmKey wrappedKey;
221 wrap(key, wrappedKey, &privateAcl);
222
223 // stick the held attribute bits back in
224 key.setAttribute(heldAttributes);
225
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);
229
230 // assemble the KeyBlob
231 memset(blob, 0, sizeof(KeyBlob)); // fill alignment gaps
232 blob->initialize();
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();
243
244 // sign the blob
245 CssmData signChunk[] = {
246 CssmData(blob->data(), offsetof(KeyBlob, blobSignature)),
247 CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
248 };
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));
254
255 // all done. Clean up
256 Server::csp()->allocator().free(wrappedKey);
257 return blob;
258 }
259
260
261 //
262 // Decode a key blob
263 //
264 void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob,
265 CssmKey &key, void * &pubAcl, void * &privAcl) const
266 {
267 assert(isValid()); // need our database secrets
268
269 // Assemble the encrypted blob as a CSSM "wrapped key"
270 CssmKey wrappedKey;
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());
277
278 // verify signature (check against corruption)
279 CssmData signChunk[] = {
280 CssmData::wrap(blob, offsetof(KeyBlob, blobSignature)),
281 CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
282 };
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
287 #endif
288 VerifyMac verifier(Server::csp(), verifyAlgorithm);
289 verifier.key(signingKey);
290 CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
291 verifier.verify(signChunk, 2, signature);
292
293 // extract and hold some header bits the CSP does not want to see
294 uint32 heldAttributes = blob->header.attributes() & managedAttributes;
295
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(?)
306 unwrap(wrappedKey,
307 KeySpec(blob->header.usage(), blob->header.attributes() & ~managedAttributes),
308 key, &privAclData);
309
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);
320
321 // re-insert held bits
322 key.header().KeyAttr |= heldAttributes;
323
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
328 }
329
330
331 //
332 // Derive the blob-specific database blob encryption key from the passphrase and the salt.
333 //
334 CssmClient::Key DatabaseCryptoCore::deriveDbCryptoKey(const CssmData &passphrase,
335 const CssmData &salt) const
336 {
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);
341 makeKey.salt(salt);
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(&paramData, KeySpec(CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
347 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
348 }
349
350
351 //
352 // Turn raw keybits into a symmetric key in the CSP
353 //
354 CssmClient::Key DatabaseCryptoCore::makeRawKey(void *data, size_t length,
355 CSSM_ALGORITHMS algid, CSSM_KEYUSE usage)
356 {
357 // build a fake key
358 CssmKey key;
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);
366
367 // unwrap it into the CSP (but keep it raw)
368 UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
369 CssmKey unwrappedKey;
370 CssmData descriptiveData;
371 unwrap(key,
372 KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE),
373 unwrappedKey, &descriptiveData, NULL);
374 return CssmClient::Key(Server::csp(), unwrappedKey);
375 }