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
= ccaes_gcm_encrypt_mode();
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 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
);
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(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
);
123 memset(rawkey
, 0, sizeof(rawkey
));
126 *outBreadcrumb
= npw
;
127 *outEncryptedKey
= key
;
134 SecBreadcrumbCopyPassword(CFStringRef inPassword
,
135 CFDataRef inBreadcrumb
,
136 CFDataRef inEncryptedKey
,
137 CFStringRef
*outPassword
,
138 CFErrorRef
*outError
)
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
;
151 if (CFDataGetLength(inEncryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
155 if (CFDataGetLength(inBreadcrumb
) < 1 + 4 + paddingSize
+ tagLen
) {
159 if (CFDataGetBytePtr(inBreadcrumb
)[0] != BCversion
) {
163 gcmkey
= CFDataCreateMutableCopy(NULL
, 0, inEncryptedKey
);
164 if (gcmkey
== NULL
) {
168 const CFIndex outLength
= CFDataGetLength(inBreadcrumb
) - 1 - tagLen
;
169 if ((outLength
% 16) != 0 && outLength
< 4) {
170 CFReleaseNull(gcmkey
);
174 oldpw
= CFDataCreateMutable(NULL
, outLength
);
176 CFReleaseNull(gcmkey
);
179 CFDataSetLength(oldpw
, outLength
);
183 * Create data for password
186 pw
= CFStringCreateExternalRepresentation(NULL
, inPassword
, kCFStringEncodingUTF8
, 0);
188 CFReleaseNull(oldpw
);
189 CFReleaseNull(gcmkey
);
194 * Wrapping key is HMAC(sha256) over password
197 if (di
->output_size
< kKeySize
) abort();
199 uint8_t rawkey
[di
->output_size
];
201 memcpy(&size
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
+ kSaltSize
, sizeof(size
));
204 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
205 kSaltSize
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
,
207 sizeof(rawkey
), rawkey
) != 0)
213 * Unwrap the random key with one round of ECB cryto
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
);
225 ccgcm_ctx_decl(gcm
->size
, ctx
);
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
);
233 CFReleaseNull(gcmkey
);
235 if (memcmp(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + outLength
, tagLen
) != 0) {
236 CFReleaseNull(oldpw
);
240 memcpy(&size
, CFDataGetMutableBytePtr(oldpw
), sizeof(size
));
242 if (size
> outLength
- 4) {
243 CFReleaseNull(oldpw
);
246 memmove(CFDataGetMutableBytePtr(oldpw
), CFDataGetMutableBytePtr(oldpw
) + 4, size
);
247 CFDataSetLength(oldpw
, size
);
249 *outPassword
= CFStringCreateFromExternalRepresentation(NULL
, oldpw
, kCFStringEncodingUTF8
);
250 CFReleaseNull(oldpw
);
256 SecBreadcrumbCreateNewEncryptedKey(CFStringRef oldPassword
,
257 CFStringRef newPassword
,
258 CFDataRef encryptedKey
,
259 CFErrorRef
*outError
)
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
];
268 if (CFDataGetLength(encryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
272 newEncryptedKey
= CFDataCreateMutableCopy(NULL
, 0, encryptedKey
);
273 if (newEncryptedKey
== NULL
) {
277 oldpw
= CFStringCreateExternalRepresentation(NULL
, oldPassword
, kCFStringEncodingUTF8
, 0);
279 CFReleaseNull(newEncryptedKey
);
283 newpw
= CFStringCreateExternalRepresentation(NULL
, newPassword
, kCFStringEncodingUTF8
, 0);
285 CFReleaseNull(newEncryptedKey
);
286 CFReleaseNull(oldpw
);
290 if (di
->output_size
< kKeySize
) abort();
293 * Unwrap with new key
298 memcpy(&iter
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
+ kSaltSize
, sizeof(iter
));
301 if (ccpbkdf2_hmac(di
, CFDataGetLength(oldpw
), CFDataGetBytePtr(oldpw
),
302 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
304 sizeof(rawkey
), rawkey
) != 0)
307 CFReleaseNull(oldpw
);
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
);
315 memset(rawkey
, 0, sizeof(rawkey
));
318 * Re-wrap with new key
321 if (ccpbkdf2_hmac(di
, CFDataGetLength(newpw
), CFDataGetBytePtr(newpw
),
322 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
324 sizeof(rawkey
), rawkey
) != 0)
327 CFReleaseNull(newpw
);
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
);
335 memset(rawkey
, 0, sizeof(rawkey
));
337 return newEncryptedKey
;