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