2 #include <Security/Security.h>
3 #include <Security/SecBreadcrumb.h>
4 #include <Security/SecRandom.h>
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>
12 #include <CommonCrypto/CommonRandomSPI.h>
14 #define CFReleaseNull(CF) ({ __typeof__(CF) *const _pcf = &(CF), _cf = *_pcf; (_cf ? (*_pcf) = ((__typeof__(CF))0), (CFRelease(_cf), ((__typeof__(CF))0)) : _cf); })
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;
27 SecBreadcrumbCreateFromPassword(CFStringRef inPassword
,
28 CFDataRef
*outBreadcrumb
,
29 CFDataRef
*outEncryptedKey
,
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();
36 CFMutableDataRef key
, npw
;
39 *outBreadcrumb
= NULL
;
40 *outEncryptedKey
= NULL
;
44 key
= CFDataCreateMutable(NULL
, 0);
48 CFDataSetLength(key
, kKeySize
+ kSaltSize
+ 4);
49 if (SecRandomCopyBytes(kSecRandomDefault
, CFDataGetLength(key
) - 4, CFDataGetMutableBytePtr(key
)) != 0) {
53 if (SecRandomCopyBytes(kSecRandomDefault
, ivLen
, iv
) != 0) {
58 uint32_t size
= htonl(kIterations
);
59 memcpy(CFDataGetMutableBytePtr(key
) + kKeySize
+ kSaltSize
, &size
, sizeof(size
));
62 * Create data for password
65 pw
= CFStringCreateExternalRepresentation(NULL
, inPassword
, kCFStringEncodingUTF8
, 0);
71 const CFIndex passwordLength
= CFDataGetLength(pw
);
73 if (passwordLength
> maxSize
) {
79 CFIndex paddedSize
= passwordLength
+ paddingSize
- (passwordLength
% paddingSize
);
80 const CFIndex outLength
= 1 + ivLen
+ 4 + paddedSize
+ tagLen
;
82 npw
= CFDataCreateMutable(NULL
, outLength
);
88 CFDataSetLength(npw
, outLength
);
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
);
98 * Now create a GCM encrypted password using the random key
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
);
110 * Wrapping key is PBKDF2(sha256) over password
113 if (di
->output_size
< kKeySize
) abort();
115 uint8_t rawkey
[di
->output_size
];
117 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
118 kSaltSize
, CFDataGetMutableBytePtr(key
) + kKeySize
,
120 sizeof(rawkey
), rawkey
) != 0)
124 * Wrap the random key with one round of ECB cryto
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
);
136 memset(rawkey
, 0, sizeof(rawkey
));
139 *outBreadcrumb
= npw
;
140 *outEncryptedKey
= key
;
147 SecBreadcrumbCopyPassword(CFStringRef inPassword
,
148 CFDataRef inBreadcrumb
,
149 CFDataRef inEncryptedKey
,
150 CFStringRef
*outPassword
,
151 CFErrorRef
*outError
)
153 const struct ccmode_ecb
*ecb
= ccaes_ecb_decrypt_mode();
154 const struct ccdigest_info
*di
= ccsha256_di();
155 CFMutableDataRef gcmkey
, oldpw
;
164 if (CFDataGetLength(inEncryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
168 if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion1
) {
169 if (CFDataGetLength(inBreadcrumb
) < 1 + 4 + paddingSize
+ tagLen
)
172 outLength
= CFDataGetLength(inBreadcrumb
) - 1 - tagLen
;
173 } else if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion2
) {
174 if (CFDataGetLength(inBreadcrumb
) < 1 + ivLen
+ 4 + paddingSize
+ tagLen
)
176 outLength
= CFDataGetLength(inBreadcrumb
) - 1 - ivLen
- tagLen
;
181 gcmkey
= CFDataCreateMutableCopy(NULL
, 0, inEncryptedKey
);
182 if (gcmkey
== NULL
) {
186 if ((outLength
% 16) != 0 && outLength
< 4) {
187 CFReleaseNull(gcmkey
);
191 oldpw
= CFDataCreateMutable(NULL
, outLength
);
193 CFReleaseNull(gcmkey
);
196 CFDataSetLength(oldpw
, outLength
);
199 * Create data for password
202 pw
= CFStringCreateExternalRepresentation(NULL
, inPassword
, kCFStringEncodingUTF8
, 0);
204 CFReleaseNull(oldpw
);
205 CFReleaseNull(gcmkey
);
210 * Wrapping key is HMAC(sha256) over password
213 if (di
->output_size
< kKeySize
) abort();
215 uint8_t rawkey
[di
->output_size
];
217 memcpy(&size
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
+ kSaltSize
, sizeof(size
));
220 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
221 kSaltSize
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
,
223 sizeof(rawkey
), rawkey
) != 0)
229 * Unwrap the random key with one round of ECB cryto
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
);
242 if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion1
) {
243 memcpy(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + outLength
, tagLen
);
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
);
253 const uint8_t *iv
= CFDataGetBytePtr(inBreadcrumb
) + 1;
255 memcpy(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + ivLen
+ outLength
, tagLen
);
257 res
= ccgcm_one_shot(ccaes_gcm_decrypt_mode(), kKeySize
, CFDataGetMutableBytePtr(gcmkey
),
259 1, CFDataGetBytePtr(inBreadcrumb
),
260 outLength
, CFDataGetBytePtr(inBreadcrumb
) + 1 + ivLen
, CFDataGetMutableBytePtr(oldpw
),
263 CFReleaseNull(oldpw
);
268 CFReleaseNull(gcmkey
);
271 memcpy(&size
, CFDataGetMutableBytePtr(oldpw
), sizeof(size
));
273 if (size
> outLength
- 4) {
274 CFReleaseNull(oldpw
);
277 memmove(CFDataGetMutableBytePtr(oldpw
), CFDataGetMutableBytePtr(oldpw
) + 4, size
);
278 CFDataSetLength(oldpw
, size
);
280 *outPassword
= CFStringCreateFromExternalRepresentation(NULL
, oldpw
, kCFStringEncodingUTF8
);
281 CFReleaseNull(oldpw
);
287 SecBreadcrumbCreateNewEncryptedKey(CFStringRef oldPassword
,
288 CFStringRef newPassword
,
289 CFDataRef encryptedKey
,
290 CFErrorRef
*outError
)
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
];
299 if (CFDataGetLength(encryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
303 newEncryptedKey
= CFDataCreateMutableCopy(NULL
, 0, encryptedKey
);
304 if (newEncryptedKey
== NULL
) {
308 oldpw
= CFStringCreateExternalRepresentation(NULL
, oldPassword
, kCFStringEncodingUTF8
, 0);
310 CFReleaseNull(newEncryptedKey
);
314 newpw
= CFStringCreateExternalRepresentation(NULL
, newPassword
, kCFStringEncodingUTF8
, 0);
316 CFReleaseNull(newEncryptedKey
);
317 CFReleaseNull(oldpw
);
321 if (di
->output_size
< kKeySize
) abort();
324 * Unwrap with new key
329 memcpy(&iter
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
+ kSaltSize
, sizeof(iter
));
332 if (ccpbkdf2_hmac(di
, CFDataGetLength(oldpw
), CFDataGetBytePtr(oldpw
),
333 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
335 sizeof(rawkey
), rawkey
) != 0)
338 CFReleaseNull(oldpw
);
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
);
346 memset(rawkey
, 0, sizeof(rawkey
));
349 * Re-wrap with new key
352 if (ccpbkdf2_hmac(di
, CFDataGetLength(newpw
), CFDataGetBytePtr(newpw
),
353 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
355 sizeof(rawkey
), rawkey
) != 0)
358 CFReleaseNull(newpw
);
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
);
366 memset(rawkey
, 0, sizeof(rawkey
));
368 return newEncryptedKey
;