]> git.saurik.com Git - apple/security.git/blob - Security/Breadcrumb/SecBreadcrumb.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / 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 = CCMODE_FACTORY_GCM_ENCRYPT(ecb);
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 gcm.init(&gcm, ctx, kKeySize, CFDataGetMutableBytePtr(key));
91 gcm.gmac(ctx, 1, CFDataGetMutableBytePtr(npw));
92 gcm.gcm(ctx, outLength - tagLen - 1, CFDataGetMutableBytePtr(npw) + 1, CFDataGetMutableBytePtr(npw) + 1);
93 gcm.finalize(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(ecb->size, ecbkey);
115 ecb->init(ecb, ecbkey, kKeySize, rawkey);
116 ecb->ecb(ecbkey, 1, CFDataGetMutableBytePtr(key), CFDataGetMutableBytePtr(key));
117
118 /*
119 *
120 */
121
122 memset(rawkey, 0, sizeof(rawkey));
123 CFReleaseNull(pw);
124
125 *outBreadcrumb = npw;
126 *outEncryptedKey = key;
127
128 return true;
129 }
130
131
132 Boolean
133 SecBreadcrumbCopyPassword(CFStringRef inPassword,
134 CFDataRef inBreadcrumb,
135 CFDataRef inEncryptedKey,
136 CFStringRef *outPassword,
137 CFErrorRef *outError)
138 {
139 const struct ccmode_ecb *ecb = ccaes_ecb_decrypt_mode();
140 const struct ccmode_gcm gcm = CCMODE_FACTORY_GCM_DECRYPT(ccaes_ecb_encrypt_mode());
141 const struct ccdigest_info *di = ccsha256_di();
142 CFMutableDataRef gcmkey, oldpw;
143 CFDataRef pw;
144 uint32_t size;
145
146 *outPassword = NULL;
147 if (outError)
148 *outError = NULL;
149
150 if (CFDataGetLength(inEncryptedKey) < kKeySize + kSaltSize + 4) {
151 return false;
152 }
153
154 if (CFDataGetLength(inBreadcrumb) < 1 + 4 + paddingSize + tagLen) {
155 return false;
156 }
157
158 if (CFDataGetBytePtr(inBreadcrumb)[0] != BCversion) {
159 return false;
160 }
161
162 gcmkey = CFDataCreateMutableCopy(NULL, 0, inEncryptedKey);
163 if (gcmkey == NULL) {
164 return false;
165 }
166
167 const CFIndex outLength = CFDataGetLength(inBreadcrumb) - 1 - tagLen;
168 if ((outLength % 16) != 0 && outLength < 4) {
169 CFReleaseNull(gcmkey);
170 return false;
171 }
172
173 oldpw = CFDataCreateMutable(NULL, outLength);
174 if (oldpw == NULL) {
175 CFReleaseNull(gcmkey);
176 return false;
177 }
178 CFDataSetLength(oldpw, outLength);
179
180
181 /*
182 * Create data for password
183 */
184
185 pw = CFStringCreateExternalRepresentation(NULL, inPassword, kCFStringEncodingUTF8, 0);
186 if (pw == NULL) {
187 CFReleaseNull(oldpw);
188 CFReleaseNull(gcmkey);
189 return false;
190 }
191
192 /*
193 * Wrapping key is HMAC(sha256) over password
194 */
195
196 if (di->output_size < kKeySize) abort();
197
198 uint8_t rawkey[di->output_size];
199
200 memcpy(&size, CFDataGetMutableBytePtr(gcmkey) + kKeySize + kSaltSize, sizeof(size));
201 size = ntohl(size);
202
203 if (ccpbkdf2_hmac(di, CFDataGetLength(pw), CFDataGetBytePtr(pw),
204 kSaltSize, CFDataGetMutableBytePtr(gcmkey) + kKeySize,
205 size,
206 sizeof(rawkey), rawkey) != 0)
207 abort();
208
209 CFReleaseNull(pw);
210
211 /*
212 * Unwrap the random key with one round of ECB cryto
213 */
214
215 ccecb_ctx_decl(ecb->size, ecbkey);
216 ecb->init(ecb, ecbkey, kKeySize, rawkey);
217 ecb->ecb(ecbkey, 1, CFDataGetMutableBytePtr(gcmkey), CFDataGetMutableBytePtr(gcmkey));
218
219 /*
220 * GCM unwrap
221 */
222
223 uint8_t tag[tagLen];
224 ccgcm_ctx_decl(gcm.size, ctx);
225
226 gcm.init(&gcm, ctx, kKeySize, CFDataGetMutableBytePtr(gcmkey));
227 gcm.gmac(ctx, 1, CFDataGetBytePtr(inBreadcrumb));
228 gcm.gcm(ctx, outLength, CFDataGetBytePtr(inBreadcrumb) + 1, CFDataGetMutableBytePtr(oldpw));
229 gcm.finalize(ctx, tagLen, tag);
230 ccgcm_ctx_clear(gcm.size, ctx);
231
232 CFReleaseNull(gcmkey);
233
234 if (memcmp(tag, CFDataGetBytePtr(inBreadcrumb) + 1 + outLength, tagLen) != 0) {
235 CFReleaseNull(oldpw);
236 return false;
237 }
238
239 memcpy(&size, CFDataGetMutableBytePtr(oldpw), sizeof(size));
240 size = ntohl(size);
241 if (size > outLength - 4) {
242 CFReleaseNull(oldpw);
243 return false;
244 }
245 memmove(CFDataGetMutableBytePtr(oldpw), CFDataGetMutableBytePtr(oldpw) + 4, size);
246 CFDataSetLength(oldpw, size);
247
248 *outPassword = CFStringCreateFromExternalRepresentation(NULL, oldpw, kCFStringEncodingUTF8);
249 CFReleaseNull(oldpw);
250
251 return true;
252 }
253
254 CFDataRef
255 SecBreadcrumbCreateNewEncryptedKey(CFStringRef oldPassword,
256 CFStringRef newPassword,
257 CFDataRef encryptedKey,
258 CFErrorRef *outError)
259 {
260 const struct ccmode_ecb *enc = ccaes_ecb_encrypt_mode();
261 const struct ccmode_ecb *dec = ccaes_ecb_decrypt_mode();
262 const struct ccdigest_info *di = ccsha256_di();
263 CFMutableDataRef newEncryptedKey;
264 CFDataRef newpw = NULL, oldpw = NULL;
265 uint8_t rawkey[di->output_size];
266
267 if (CFDataGetLength(encryptedKey) < kKeySize + kSaltSize + 4) {
268 return NULL;
269 }
270
271 newEncryptedKey = CFDataCreateMutableCopy(NULL, 0, encryptedKey);
272 if (newEncryptedKey == NULL) {
273 return NULL;
274 }
275
276 oldpw = CFStringCreateExternalRepresentation(NULL, oldPassword, kCFStringEncodingUTF8, 0);
277 if (oldpw == NULL) {
278 CFReleaseNull(newEncryptedKey);
279 return false;
280 }
281
282 newpw = CFStringCreateExternalRepresentation(NULL, newPassword, kCFStringEncodingUTF8, 0);
283 if (newpw == NULL) {
284 CFReleaseNull(newEncryptedKey);
285 CFReleaseNull(oldpw);
286 return false;
287 }
288
289 if (di->output_size < kKeySize) abort();
290
291 /*
292 * Unwrap with new key
293 */
294
295 uint32_t iter;
296
297 memcpy(&iter, CFDataGetMutableBytePtr(newEncryptedKey) + kKeySize + kSaltSize, sizeof(iter));
298 iter = ntohl(iter);
299
300 if (ccpbkdf2_hmac(di, CFDataGetLength(oldpw), CFDataGetBytePtr(oldpw),
301 kSaltSize, CFDataGetMutableBytePtr(newEncryptedKey) + kKeySize,
302 iter,
303 sizeof(rawkey), rawkey) != 0)
304 abort();
305
306 CFReleaseNull(oldpw);
307
308
309 ccecb_ctx_decl(dec->size, deckey);
310 dec->init(dec, deckey, kKeySize, rawkey);
311 dec->ecb(deckey, 1, CFDataGetMutableBytePtr(newEncryptedKey), CFDataGetMutableBytePtr(newEncryptedKey));
312
313 memset(rawkey, 0, sizeof(rawkey));
314
315 /*
316 * Re-wrap with new key
317 */
318
319 if (ccpbkdf2_hmac(di, CFDataGetLength(newpw), CFDataGetBytePtr(newpw),
320 kSaltSize, CFDataGetMutableBytePtr(newEncryptedKey) + kKeySize,
321 iter,
322 sizeof(rawkey), rawkey) != 0)
323 abort();
324
325 CFReleaseNull(newpw);
326
327
328 ccecb_ctx_decl(enc->size, enckey);
329 enc->init(enc, enckey, kKeySize, rawkey);
330 enc->ecb(enckey, 1, CFDataGetMutableBytePtr(newEncryptedKey), CFDataGetMutableBytePtr(newEncryptedKey));
331
332 memset(rawkey, 0, sizeof(rawkey));
333
334 return newEncryptedKey;
335 }