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
16static const int kKeySize = CCAES_KEY_SIZE_128;
17static const int kSaltSize = 20;
18static const int kIterations = 5000;
19static const CFIndex tagLen = 16;
20static const uint8_t BCversion = 1;
21static const size_t paddingSize = 256;
22static const size_t maxSize = 1024;
23
24Boolean
25SecBreadcrumbCreateFromPassword(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
132Boolean
133SecBreadcrumbCopyPassword(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
254CFDataRef
255SecBreadcrumbCreateNewEncryptedKey(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}
336