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 uint8_t BCversion
= 1;
21 static const size_t paddingSize
= 256;
22 static const size_t maxSize
= 1024;
25 SecBreadcrumbCreateFromPassword(CFStringRef inPassword
,
26 CFDataRef
*outBreadcrumb
,
27 CFDataRef
*outEncryptedKey
,
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
;
36 *outBreadcrumb
= NULL
;
37 *outEncryptedKey
= NULL
;
41 key
= CFDataCreateMutable(NULL
, 0);
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
));
51 * Create data for password
54 pw
= CFStringCreateExternalRepresentation(NULL
, inPassword
, kCFStringEncodingUTF8
, 0);
60 const CFIndex passwordLength
= CFDataGetLength(pw
);
62 if (passwordLength
> maxSize
) {
68 CFIndex paddedSize
= passwordLength
+ paddingSize
- (passwordLength
% paddingSize
);
69 const CFIndex outLength
= 1 + 4 + paddedSize
+ tagLen
;
71 npw
= CFDataCreateMutable(NULL
, outLength
);
77 CFDataSetLength(npw
, outLength
);
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
);
86 * Now create a GCM encrypted password using the random key
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
);
97 * Wrapping key is PBKDF2(sha256) over password
100 if (di
->output_size
< kKeySize
) abort();
102 uint8_t rawkey
[di
->output_size
];
104 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
105 kSaltSize
, CFDataGetMutableBytePtr(key
) + kKeySize
,
107 sizeof(rawkey
), rawkey
) != 0)
111 * Wrap the random key with one round of ECB cryto
114 ccecb_ctx_decl(ecb
->size
, ecbkey
);
115 ecb
->init(ecb
, ecbkey
, kKeySize
, rawkey
);
116 ecb
->ecb(ecbkey
, 1, CFDataGetMutableBytePtr(key
), CFDataGetMutableBytePtr(key
));
122 memset(rawkey
, 0, sizeof(rawkey
));
125 *outBreadcrumb
= npw
;
126 *outEncryptedKey
= key
;
133 SecBreadcrumbCopyPassword(CFStringRef inPassword
,
134 CFDataRef inBreadcrumb
,
135 CFDataRef inEncryptedKey
,
136 CFStringRef
*outPassword
,
137 CFErrorRef
*outError
)
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
;
150 if (CFDataGetLength(inEncryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
154 if (CFDataGetLength(inBreadcrumb
) < 1 + 4 + paddingSize
+ tagLen
) {
158 if (CFDataGetBytePtr(inBreadcrumb
)[0] != BCversion
) {
162 gcmkey
= CFDataCreateMutableCopy(NULL
, 0, inEncryptedKey
);
163 if (gcmkey
== NULL
) {
167 const CFIndex outLength
= CFDataGetLength(inBreadcrumb
) - 1 - tagLen
;
168 if ((outLength
% 16) != 0 && outLength
< 4) {
169 CFReleaseNull(gcmkey
);
173 oldpw
= CFDataCreateMutable(NULL
, outLength
);
175 CFReleaseNull(gcmkey
);
178 CFDataSetLength(oldpw
, outLength
);
182 * Create data for password
185 pw
= CFStringCreateExternalRepresentation(NULL
, inPassword
, kCFStringEncodingUTF8
, 0);
187 CFReleaseNull(oldpw
);
188 CFReleaseNull(gcmkey
);
193 * Wrapping key is HMAC(sha256) over password
196 if (di
->output_size
< kKeySize
) abort();
198 uint8_t rawkey
[di
->output_size
];
200 memcpy(&size
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
+ kSaltSize
, sizeof(size
));
203 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
204 kSaltSize
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
,
206 sizeof(rawkey
), rawkey
) != 0)
212 * Unwrap the random key with one round of ECB cryto
215 ccecb_ctx_decl(ecb
->size
, ecbkey
);
216 ecb
->init(ecb
, ecbkey
, kKeySize
, rawkey
);
217 ecb
->ecb(ecbkey
, 1, CFDataGetMutableBytePtr(gcmkey
), CFDataGetMutableBytePtr(gcmkey
));
224 ccgcm_ctx_decl(gcm
.size
, ctx
);
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
);
232 CFReleaseNull(gcmkey
);
234 if (memcmp(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + outLength
, tagLen
) != 0) {
235 CFReleaseNull(oldpw
);
239 memcpy(&size
, CFDataGetMutableBytePtr(oldpw
), sizeof(size
));
241 if (size
> outLength
- 4) {
242 CFReleaseNull(oldpw
);
245 memmove(CFDataGetMutableBytePtr(oldpw
), CFDataGetMutableBytePtr(oldpw
) + 4, size
);
246 CFDataSetLength(oldpw
, size
);
248 *outPassword
= CFStringCreateFromExternalRepresentation(NULL
, oldpw
, kCFStringEncodingUTF8
);
249 CFReleaseNull(oldpw
);
255 SecBreadcrumbCreateNewEncryptedKey(CFStringRef oldPassword
,
256 CFStringRef newPassword
,
257 CFDataRef encryptedKey
,
258 CFErrorRef
*outError
)
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
];
267 if (CFDataGetLength(encryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
271 newEncryptedKey
= CFDataCreateMutableCopy(NULL
, 0, encryptedKey
);
272 if (newEncryptedKey
== NULL
) {
276 oldpw
= CFStringCreateExternalRepresentation(NULL
, oldPassword
, kCFStringEncodingUTF8
, 0);
278 CFReleaseNull(newEncryptedKey
);
282 newpw
= CFStringCreateExternalRepresentation(NULL
, newPassword
, kCFStringEncodingUTF8
, 0);
284 CFReleaseNull(newEncryptedKey
);
285 CFReleaseNull(oldpw
);
289 if (di
->output_size
< kKeySize
) abort();
292 * Unwrap with new key
297 memcpy(&iter
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
+ kSaltSize
, sizeof(iter
));
300 if (ccpbkdf2_hmac(di
, CFDataGetLength(oldpw
), CFDataGetBytePtr(oldpw
),
301 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
303 sizeof(rawkey
), rawkey
) != 0)
306 CFReleaseNull(oldpw
);
309 ccecb_ctx_decl(dec
->size
, deckey
);
310 dec
->init(dec
, deckey
, kKeySize
, rawkey
);
311 dec
->ecb(deckey
, 1, CFDataGetMutableBytePtr(newEncryptedKey
), CFDataGetMutableBytePtr(newEncryptedKey
));
313 memset(rawkey
, 0, sizeof(rawkey
));
316 * Re-wrap with new key
319 if (ccpbkdf2_hmac(di
, CFDataGetLength(newpw
), CFDataGetBytePtr(newpw
),
320 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
322 sizeof(rawkey
), rawkey
) != 0)
325 CFReleaseNull(newpw
);
328 ccecb_ctx_decl(enc
->size
, enckey
);
329 enc
->init(enc
, enckey
, kKeySize
, rawkey
);
330 enc
->ecb(enckey
, 1, CFDataGetMutableBytePtr(newEncryptedKey
), CFDataGetMutableBytePtr(newEncryptedKey
));
332 memset(rawkey
, 0, sizeof(rawkey
));
334 return newEncryptedKey
;