]> git.saurik.com Git - apple/security.git/blob - OSX/Breadcrumb/SecBreadcrumb.c
Security-59306.120.7.tar.gz
[apple/security.git] / OSX / Breadcrumb / SecBreadcrumb.c
1 /*
2 * Copyright (c) 2014 - 2016 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 #include <Security/Security.h>
25 #include <Security/SecBreadcrumb.h>
26 #include <Security/SecRandom.h>
27
28 #include <corecrypto/ccaes.h>
29 #include <corecrypto/ccpbkdf2.h>
30 #include <corecrypto/ccmode.h>
31 #include <corecrypto/ccmode_factory.h>
32 #include <corecrypto/ccsha2.h>
33
34 #include <CommonCrypto/CommonRandomSPI.h>
35
36 #import "SecCFAllocator.h"
37
38 #define CFReleaseNull(CF) ({ __typeof__(CF) *const _pcf = &(CF), _cf = *_pcf; (_cf ? (*_pcf) = ((__typeof__(CF))0), (CFRelease(_cf), ((__typeof__(CF))0)) : _cf); })
39
40 #define kBCKeySize CCAES_KEY_SIZE_128
41 #define kBCSaltSize 20
42 #define kBCIterations 5000
43 #define BCTagLen 16
44 #define BCIVLen 16
45 #define BCversion1 1
46 #define BCversion2 2
47 #define BCPaddingSize 256
48 #define BCMaxSize 1024
49
50 Boolean
51 SecBreadcrumbCreateFromPassword(CFStringRef inPassword,
52 CFDataRef *outBreadcrumb,
53 CFDataRef *outEncryptedKey,
54 CFErrorRef *outError)
55 {
56 const struct ccmode_ecb *ecb = ccaes_ecb_encrypt_mode();
57 const struct ccmode_gcm *gcm = ccaes_gcm_encrypt_mode();
58 uint8_t iv[BCIVLen];
59 CFMutableDataRef key, npw;
60 CFDataRef pw;
61
62 *outBreadcrumb = NULL;
63 *outEncryptedKey = NULL;
64 if (outError)
65 *outError = NULL;
66
67 key = CFDataCreateMutable(SecCFAllocatorZeroize(), 0);
68 if (key == NULL)
69 return false;
70
71 CFDataSetLength(key, kBCKeySize + kBCSaltSize + 4);
72 if (SecRandomCopyBytes(kSecRandomDefault, CFDataGetLength(key) - 4, CFDataGetMutableBytePtr(key)) != 0) {
73 CFReleaseNull(key);
74 return false;
75 }
76 if (SecRandomCopyBytes(kSecRandomDefault, BCIVLen, iv) != 0) {
77 CFReleaseNull(key);
78 return false;
79 }
80
81 uint32_t size = htonl(kBCIterations);
82 memcpy(CFDataGetMutableBytePtr(key) + kBCKeySize + kBCSaltSize, &size, sizeof(size));
83
84 /*
85 * Create data for password
86 */
87
88 pw = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), inPassword, kCFStringEncodingUTF8, 0);
89 if (pw == NULL) {
90 CFReleaseNull(key);
91 return false;
92 }
93
94 const CFIndex passwordLength = CFDataGetLength(pw);
95
96 if (passwordLength > BCMaxSize) {
97 CFReleaseNull(pw);
98 CFReleaseNull(key);
99 return false;
100 }
101
102 CFIndex paddedSize = passwordLength + BCPaddingSize - (passwordLength % BCPaddingSize);
103 const CFIndex outLength = 1 + BCIVLen + 4 + paddedSize + BCTagLen;
104
105 npw = CFDataCreateMutable(NULL, outLength);
106 if (npw == NULL) {
107 CFReleaseNull(pw);
108 CFReleaseNull(key);
109 return false;
110 }
111 CFDataSetLength(npw, outLength);
112
113 cc_clear(outLength, CFDataGetMutableBytePtr(npw));
114 CFDataGetMutableBytePtr(npw)[0] = BCversion2;
115 memcpy(CFDataGetMutableBytePtr(npw) + 1, iv, BCIVLen);
116 size = htonl(passwordLength);
117 memcpy(CFDataGetMutableBytePtr(npw) + 1 + BCIVLen, &size, sizeof(size));
118 memcpy(CFDataGetMutableBytePtr(npw) + 1 + BCIVLen + 4, CFDataGetBytePtr(pw), passwordLength);
119
120 /*
121 * Now create a GCM encrypted password using the random key
122 */
123
124 ccgcm_ctx_decl(gcm->size, ctx);
125 ccgcm_init(gcm, ctx, kBCKeySize, CFDataGetMutableBytePtr(key));
126 ccgcm_set_iv(gcm, ctx, BCIVLen, iv);
127 ccgcm_gmac(gcm, ctx, 1, CFDataGetMutableBytePtr(npw));
128 ccgcm_update(gcm, ctx, outLength - BCTagLen - BCIVLen - 1, CFDataGetMutableBytePtr(npw) + 1 + BCIVLen, CFDataGetMutableBytePtr(npw) + 1 + BCIVLen);
129 ccgcm_finalize(gcm, ctx, BCTagLen, CFDataGetMutableBytePtr(npw) + outLength - BCTagLen);
130 ccgcm_ctx_clear(gcm->size, ctx);
131
132 /*
133 * Wrapping key is PBKDF2(sha256) over password
134 */
135
136 const struct ccdigest_info *di = ccsha256_di();
137 uint8_t rawkey[CCSHA256_OUTPUT_SIZE];
138 _Static_assert(sizeof(rawkey) >= kBCKeySize, "keysize changed w/o updating digest");
139 if (sizeof(rawkey) != di->output_size) abort();
140
141 if (ccpbkdf2_hmac(di, CFDataGetLength(pw), CFDataGetBytePtr(pw),
142 kBCSaltSize, CFDataGetMutableBytePtr(key) + kBCKeySize,
143 kBCIterations,
144 sizeof(rawkey), rawkey) != 0)
145 abort();
146
147 /*
148 * Wrap the random key with one round of ECB cryto
149 */
150
151 ccecb_ctx_decl(ccecb_context_size(ecb), ecbkey);
152 ccecb_init(ecb, ecbkey, kBCKeySize, rawkey);
153 ccecb_update(ecb, ecbkey, 1, CFDataGetMutableBytePtr(key), CFDataGetMutableBytePtr(key));
154 ccecb_ctx_clear(ccecb_context_size(ecb), ecbkey);
155
156 /*
157 *
158 */
159
160 cc_clear(sizeof(rawkey), rawkey);
161 CFReleaseNull(pw);
162
163 *outBreadcrumb = npw;
164 *outEncryptedKey = key;
165
166 return true;
167 }
168
169
170 Boolean
171 SecBreadcrumbCopyPassword(CFStringRef inPassword,
172 CFDataRef inBreadcrumb,
173 CFDataRef inEncryptedKey,
174 CFStringRef *outPassword,
175 CFErrorRef *outError)
176 {
177 const struct ccmode_ecb *ecb = ccaes_ecb_decrypt_mode();
178 CFMutableDataRef gcmkey, oldpw;
179 CFIndex outLength;
180 CFDataRef pw;
181 uint32_t size;
182
183 *outPassword = NULL;
184 if (outError)
185 *outError = NULL;
186
187 if (CFDataGetLength(inEncryptedKey) < kBCKeySize + kBCSaltSize + 4) {
188 return false;
189 }
190
191 if (CFDataGetBytePtr(inBreadcrumb)[0] == BCversion1) {
192 if (CFDataGetLength(inBreadcrumb) < 1 + 4 + BCPaddingSize + BCTagLen)
193 return false;
194
195 outLength = CFDataGetLength(inBreadcrumb) - 1 - BCTagLen;
196 } else if (CFDataGetBytePtr(inBreadcrumb)[0] == BCversion2) {
197 if (CFDataGetLength(inBreadcrumb) < 1 + BCIVLen + 4 + BCPaddingSize + BCTagLen)
198 return false;
199 outLength = CFDataGetLength(inBreadcrumb) - 1 - BCIVLen - BCTagLen;
200 } else {
201 return false;
202 }
203
204 gcmkey = CFDataCreateMutableCopy(SecCFAllocatorZeroize(), 0, inEncryptedKey);
205 if (gcmkey == NULL) {
206 return false;
207 }
208
209 if ((outLength % 16) != 0 && outLength < 4) {
210 CFReleaseNull(gcmkey);
211 return false;
212 }
213
214 oldpw = CFDataCreateMutable(SecCFAllocatorZeroize(), outLength);
215 if (oldpw == NULL) {
216 CFReleaseNull(gcmkey);
217 return false;
218 }
219 CFDataSetLength(oldpw, outLength);
220
221 /*
222 * Create data for password
223 */
224
225 pw = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), inPassword, kCFStringEncodingUTF8, 0);
226 if (pw == NULL) {
227 CFReleaseNull(oldpw);
228 CFReleaseNull(gcmkey);
229 return false;
230 }
231
232 /*
233 * Wrapping key is HMAC(sha256) over password
234 */
235
236 const struct ccdigest_info *di = ccsha256_di();
237 uint8_t rawkey[CCSHA256_OUTPUT_SIZE];
238 _Static_assert(sizeof(rawkey) >= kBCKeySize, "keysize changed w/o updating digest");
239 if (sizeof(rawkey) != di->output_size) abort();
240
241 memcpy(&size, CFDataGetMutableBytePtr(gcmkey) + kBCKeySize + kBCSaltSize, sizeof(size));
242 size = ntohl(size);
243
244 if (ccpbkdf2_hmac(di, CFDataGetLength(pw), CFDataGetBytePtr(pw),
245 kBCSaltSize, CFDataGetMutableBytePtr(gcmkey) + kBCKeySize,
246 size,
247 sizeof(rawkey), rawkey) != 0)
248 abort();
249
250 CFReleaseNull(pw);
251
252 /*
253 * Unwrap the random key with one round of ECB cryto
254 */
255
256 ccecb_ctx_decl(ccecb_context_size(ecb), ecbkey);
257 ccecb_init(ecb, ecbkey, kBCKeySize, rawkey);
258 ccecb_update(ecb, ecbkey, 1, CFDataGetMutableBytePtr(gcmkey), CFDataGetMutableBytePtr(gcmkey));
259 ccecb_ctx_clear(ccecb_context_size(ecb), ecbkey);
260 /*
261 * GCM unwrap
262 */
263
264 uint8_t tag[BCTagLen];
265
266 if (CFDataGetBytePtr(inBreadcrumb)[0] == BCversion1) {
267 memcpy(tag, CFDataGetBytePtr(inBreadcrumb) + 1 + outLength, BCTagLen);
268
269 ccgcm_one_shot_legacy(ccaes_gcm_decrypt_mode(), kBCKeySize, CFDataGetMutableBytePtr(gcmkey), 0, NULL, 1, CFDataGetBytePtr(inBreadcrumb),
270 outLength, CFDataGetBytePtr(inBreadcrumb) + 1, CFDataGetMutableBytePtr(oldpw), BCTagLen, tag);
271 if (memcmp(tag, CFDataGetBytePtr(inBreadcrumb) + 1 + outLength, BCTagLen) != 0) {
272 CFReleaseNull(oldpw);
273 CFReleaseNull(gcmkey);
274 return false;
275 }
276
277 } else {
278 const uint8_t *iv = CFDataGetBytePtr(inBreadcrumb) + 1;
279 int res;
280 memcpy(tag, CFDataGetBytePtr(inBreadcrumb) + 1 + BCIVLen + outLength, BCTagLen);
281
282 res = ccgcm_one_shot(ccaes_gcm_decrypt_mode(), kBCKeySize, CFDataGetMutableBytePtr(gcmkey),
283 BCIVLen, iv,
284 1, CFDataGetBytePtr(inBreadcrumb),
285 outLength, CFDataGetBytePtr(inBreadcrumb) + 1 + BCIVLen, CFDataGetMutableBytePtr(oldpw),
286 BCTagLen, tag);
287 if (res) {
288 CFReleaseNull(gcmkey);
289 CFReleaseNull(oldpw);
290 CFReleaseNull(gcmkey);
291 return false;
292 }
293 }
294
295 CFReleaseNull(gcmkey);
296
297
298 memcpy(&size, CFDataGetMutableBytePtr(oldpw), sizeof(size));
299 size = ntohl(size);
300 if ((ssize_t) size > outLength - 4) {
301 CFReleaseNull(oldpw);
302 return false;
303 }
304 memmove(CFDataGetMutableBytePtr(oldpw), CFDataGetMutableBytePtr(oldpw) + 4, size);
305 CFDataSetLength(oldpw, size);
306
307 *outPassword = CFStringCreateFromExternalRepresentation(SecCFAllocatorZeroize(), oldpw, kCFStringEncodingUTF8);
308 CFReleaseNull(oldpw);
309
310 return true;
311 }
312
313 CFDataRef
314 SecBreadcrumbCreateNewEncryptedKey(CFStringRef oldPassword,
315 CFStringRef newPassword,
316 CFDataRef encryptedKey,
317 CFErrorRef *outError)
318 {
319 const struct ccmode_ecb *enc = ccaes_ecb_encrypt_mode();
320 const struct ccmode_ecb *dec = ccaes_ecb_decrypt_mode();
321 const struct ccdigest_info *di = ccsha256_di();
322 uint8_t rawkey[CCSHA256_OUTPUT_SIZE];
323 CFDataRef newpw = NULL, oldpw = NULL;
324 CFMutableDataRef newEncryptedKey;
325
326 _Static_assert(sizeof(rawkey) >= kBCKeySize, "keysize changed w/o updating digest");
327 if (sizeof(rawkey) != di->output_size) abort();
328
329 if (CFDataGetLength(encryptedKey) < kBCKeySize + kBCSaltSize + 4) {
330 return NULL;
331 }
332
333 newEncryptedKey = CFDataCreateMutableCopy(SecCFAllocatorZeroize(), 0, encryptedKey);
334 if (newEncryptedKey == NULL) {
335 return NULL;
336 }
337
338 oldpw = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), oldPassword, kCFStringEncodingUTF8, 0);
339 if (oldpw == NULL) {
340 CFReleaseNull(newEncryptedKey);
341 return false;
342 }
343
344 newpw = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), newPassword, kCFStringEncodingUTF8, 0);
345 if (newpw == NULL) {
346 CFReleaseNull(newEncryptedKey);
347 CFReleaseNull(oldpw);
348 return false;
349 }
350
351 /*
352 * Unwrap with new key
353 */
354
355 uint32_t iter;
356
357 memcpy(&iter, CFDataGetMutableBytePtr(newEncryptedKey) + kBCKeySize + kBCSaltSize, sizeof(iter));
358 iter = ntohl(iter);
359
360 if (ccpbkdf2_hmac(di, CFDataGetLength(oldpw), CFDataGetBytePtr(oldpw),
361 kBCSaltSize, CFDataGetMutableBytePtr(newEncryptedKey) + kBCKeySize,
362 iter,
363 sizeof(rawkey), rawkey) != 0)
364 abort();
365
366 CFReleaseNull(oldpw);
367
368
369 ccecb_ctx_decl(dec->size, deckey);
370 ccecb_init(dec, deckey, kBCKeySize, rawkey);
371 ccecb_update(dec, deckey, 1, CFDataGetMutableBytePtr(newEncryptedKey), CFDataGetMutableBytePtr(newEncryptedKey));
372 ccecb_ctx_clear(ccecb_context_size(dec), deckey);
373
374 cc_clear(sizeof(rawkey), rawkey);
375
376 /*
377 * Re-wrap with new key
378 */
379
380 if (ccpbkdf2_hmac(di, CFDataGetLength(newpw), CFDataGetBytePtr(newpw),
381 kBCSaltSize, CFDataGetMutableBytePtr(newEncryptedKey) + kBCKeySize,
382 iter,
383 sizeof(rawkey), rawkey) != 0)
384 abort();
385
386 CFReleaseNull(newpw);
387
388
389 ccecb_ctx_decl(enc->size, enckey);
390 ccecb_init(enc, enckey, kBCKeySize, rawkey);
391 ccecb_update(enc, enckey, 1, CFDataGetMutableBytePtr(newEncryptedKey), CFDataGetMutableBytePtr(newEncryptedKey));
392 ccecb_ctx_clear(ccecb_context_size(enc), enckey);
393
394 cc_clear(sizeof(rawkey), rawkey);
395
396 return newEncryptedKey;
397 }