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