2 * Copyright (c) 2014 - 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <Security/Security.h>
25 #include <Security/SecBreadcrumb.h>
26 #include <Security/SecRandom.h>
28 #include <corecrypto/ccaes.h>
29 #include <corecrypto/ccpbkdf2.h>
30 #include <corecrypto/ccmode.h>
31 #include <corecrypto/ccmode_factory.h>
32 #include <corecrypto/ccsha2.h>
34 #include <CommonCrypto/CommonRandomSPI.h>
36 #import "SecCFAllocator.h"
38 #define CFReleaseNull(CF) ({ __typeof__(CF) *const _pcf = &(CF), _cf = *_pcf; (_cf ? (*_pcf) = ((__typeof__(CF))0), (CFRelease(_cf), ((__typeof__(CF))0)) : _cf); })
40 static const int kKeySize
= CCAES_KEY_SIZE_128
;
41 static const int kSaltSize
= 20;
42 static const int kIterations
= 5000;
43 static const CFIndex tagLen
= 16;
44 static const CFIndex ivLen
= 16;
45 static const uint8_t BCversion1
= 1;
46 static const uint8_t BCversion2
= 2;
47 static const ssize_t paddingSize
= 256;
48 static const ssize_t maxSize
= 1024;
51 SecBreadcrumbCreateFromPassword(CFStringRef inPassword
,
52 CFDataRef
*outBreadcrumb
,
53 CFDataRef
*outEncryptedKey
,
56 const struct ccmode_ecb
*ecb
= ccaes_ecb_encrypt_mode();
57 const struct ccmode_gcm
*gcm
= ccaes_gcm_encrypt_mode();
58 const struct ccdigest_info
*di
= ccsha256_di();
60 CFMutableDataRef key
, npw
;
63 *outBreadcrumb
= NULL
;
64 *outEncryptedKey
= NULL
;
68 key
= CFDataCreateMutable(SecCFAllocatorZeroize(), 0);
72 CFDataSetLength(key
, kKeySize
+ kSaltSize
+ 4);
73 if (SecRandomCopyBytes(kSecRandomDefault
, CFDataGetLength(key
) - 4, CFDataGetMutableBytePtr(key
)) != 0) {
77 if (SecRandomCopyBytes(kSecRandomDefault
, ivLen
, iv
) != 0) {
82 uint32_t size
= htonl(kIterations
);
83 memcpy(CFDataGetMutableBytePtr(key
) + kKeySize
+ kSaltSize
, &size
, sizeof(size
));
86 * Create data for password
89 pw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), inPassword
, kCFStringEncodingUTF8
, 0);
95 const CFIndex passwordLength
= CFDataGetLength(pw
);
97 if (passwordLength
> maxSize
) {
103 CFIndex paddedSize
= passwordLength
+ paddingSize
- (passwordLength
% paddingSize
);
104 const CFIndex outLength
= 1 + ivLen
+ 4 + paddedSize
+ tagLen
;
106 npw
= CFDataCreateMutable(NULL
, outLength
);
112 CFDataSetLength(npw
, outLength
);
114 memset(CFDataGetMutableBytePtr(npw
), 0, outLength
);
115 CFDataGetMutableBytePtr(npw
)[0] = BCversion2
;
116 memcpy(CFDataGetMutableBytePtr(npw
) + 1, iv
, ivLen
);
117 size
= htonl(passwordLength
);
118 memcpy(CFDataGetMutableBytePtr(npw
) + 1 + ivLen
, &size
, sizeof(size
));
119 memcpy(CFDataGetMutableBytePtr(npw
) + 1 + ivLen
+ 4, CFDataGetBytePtr(pw
), passwordLength
);
122 * Now create a GCM encrypted password using the random key
125 ccgcm_ctx_decl(gcm
->size
, ctx
);
126 ccgcm_init(gcm
, ctx
, kKeySize
, CFDataGetMutableBytePtr(key
));
127 ccgcm_set_iv(gcm
, ctx
, ivLen
, iv
);
128 ccgcm_gmac(gcm
, ctx
, 1, CFDataGetMutableBytePtr(npw
));
129 ccgcm_update(gcm
, ctx
, outLength
- tagLen
- ivLen
- 1, CFDataGetMutableBytePtr(npw
) + 1 + ivLen
, CFDataGetMutableBytePtr(npw
) + 1 + ivLen
);
130 ccgcm_finalize(gcm
, ctx
, tagLen
, CFDataGetMutableBytePtr(npw
) + outLength
- tagLen
);
131 ccgcm_ctx_clear(gcm
->size
, ctx
);
134 * Wrapping key is PBKDF2(sha256) over password
137 if (di
->output_size
< kKeySize
) abort();
139 uint8_t rawkey
[di
->output_size
];
141 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
142 kSaltSize
, CFDataGetMutableBytePtr(key
) + kKeySize
,
144 sizeof(rawkey
), rawkey
) != 0)
148 * Wrap the random key with one round of ECB cryto
151 ccecb_ctx_decl(ccecb_context_size(ecb
), ecbkey
);
152 ccecb_init(ecb
, ecbkey
, kKeySize
, rawkey
);
153 ccecb_update(ecb
, ecbkey
, 1, CFDataGetMutableBytePtr(key
), CFDataGetMutableBytePtr(key
));
154 ccecb_ctx_clear(ccecb_context_size(ecb
), ecbkey
);
160 memset(rawkey
, 0, sizeof(rawkey
));
163 *outBreadcrumb
= npw
;
164 *outEncryptedKey
= key
;
171 SecBreadcrumbCopyPassword(CFStringRef inPassword
,
172 CFDataRef inBreadcrumb
,
173 CFDataRef inEncryptedKey
,
174 CFStringRef
*outPassword
,
175 CFErrorRef
*outError
)
177 const struct ccmode_ecb
*ecb
= ccaes_ecb_decrypt_mode();
178 const struct ccdigest_info
*di
= ccsha256_di();
179 CFMutableDataRef gcmkey
, oldpw
;
188 if (CFDataGetLength(inEncryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
192 if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion1
) {
193 if (CFDataGetLength(inBreadcrumb
) < 1 + 4 + paddingSize
+ tagLen
)
196 outLength
= CFDataGetLength(inBreadcrumb
) - 1 - tagLen
;
197 } else if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion2
) {
198 if (CFDataGetLength(inBreadcrumb
) < 1 + ivLen
+ 4 + paddingSize
+ tagLen
)
200 outLength
= CFDataGetLength(inBreadcrumb
) - 1 - ivLen
- tagLen
;
205 gcmkey
= CFDataCreateMutableCopy(SecCFAllocatorZeroize(), 0, inEncryptedKey
);
206 if (gcmkey
== NULL
) {
210 if ((outLength
% 16) != 0 && outLength
< 4) {
211 CFReleaseNull(gcmkey
);
215 oldpw
= CFDataCreateMutable(SecCFAllocatorZeroize(), outLength
);
217 CFReleaseNull(gcmkey
);
220 CFDataSetLength(oldpw
, outLength
);
223 * Create data for password
226 pw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), inPassword
, kCFStringEncodingUTF8
, 0);
228 CFReleaseNull(oldpw
);
229 CFReleaseNull(gcmkey
);
234 * Wrapping key is HMAC(sha256) over password
237 if (di
->output_size
< kKeySize
) abort();
239 uint8_t rawkey
[di
->output_size
];
241 memcpy(&size
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
+ kSaltSize
, sizeof(size
));
244 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
245 kSaltSize
, CFDataGetMutableBytePtr(gcmkey
) + kKeySize
,
247 sizeof(rawkey
), rawkey
) != 0)
253 * Unwrap the random key with one round of ECB cryto
256 ccecb_ctx_decl(ccecb_context_size(ecb
), ecbkey
);
257 ccecb_init(ecb
, ecbkey
, kKeySize
, rawkey
);
258 ccecb_update(ecb
, ecbkey
, 1, CFDataGetMutableBytePtr(gcmkey
), CFDataGetMutableBytePtr(gcmkey
));
259 ccecb_ctx_clear(ccecb_context_size(ecb
), ecbkey
);
266 if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion1
) {
267 memcpy(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + outLength
, tagLen
);
269 ccgcm_one_shot_legacy(ccaes_gcm_decrypt_mode(), kKeySize
, CFDataGetMutableBytePtr(gcmkey
), 0, NULL
, 1, CFDataGetBytePtr(inBreadcrumb
),
270 outLength
, CFDataGetBytePtr(inBreadcrumb
) + 1, CFDataGetMutableBytePtr(oldpw
), tagLen
, tag
);
271 if (memcmp(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + outLength
, tagLen
) != 0) {
272 CFReleaseNull(oldpw
);
273 CFReleaseNull(gcmkey
);
278 const uint8_t *iv
= CFDataGetBytePtr(inBreadcrumb
) + 1;
280 memcpy(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + ivLen
+ outLength
, tagLen
);
282 res
= ccgcm_one_shot(ccaes_gcm_decrypt_mode(), kKeySize
, CFDataGetMutableBytePtr(gcmkey
),
284 1, CFDataGetBytePtr(inBreadcrumb
),
285 outLength
, CFDataGetBytePtr(inBreadcrumb
) + 1 + ivLen
, CFDataGetMutableBytePtr(oldpw
),
288 CFReleaseNull(oldpw
);
289 CFReleaseNull(gcmkey
);
294 CFReleaseNull(gcmkey
);
297 memcpy(&size
, CFDataGetMutableBytePtr(oldpw
), sizeof(size
));
299 if ((ssize_t
) size
> outLength
- 4) {
300 CFReleaseNull(oldpw
);
303 memmove(CFDataGetMutableBytePtr(oldpw
), CFDataGetMutableBytePtr(oldpw
) + 4, size
);
304 CFDataSetLength(oldpw
, size
);
306 *outPassword
= CFStringCreateFromExternalRepresentation(SecCFAllocatorZeroize(), oldpw
, kCFStringEncodingUTF8
);
307 CFReleaseNull(oldpw
);
313 SecBreadcrumbCreateNewEncryptedKey(CFStringRef oldPassword
,
314 CFStringRef newPassword
,
315 CFDataRef encryptedKey
,
316 CFErrorRef
*outError
)
318 const struct ccmode_ecb
*enc
= ccaes_ecb_encrypt_mode();
319 const struct ccmode_ecb
*dec
= ccaes_ecb_decrypt_mode();
320 const struct ccdigest_info
*di
= ccsha256_di();
321 CFMutableDataRef newEncryptedKey
;
322 CFDataRef newpw
= NULL
, oldpw
= NULL
;
323 uint8_t rawkey
[di
->output_size
];
325 if (CFDataGetLength(encryptedKey
) < kKeySize
+ kSaltSize
+ 4) {
329 newEncryptedKey
= CFDataCreateMutableCopy(SecCFAllocatorZeroize(), 0, encryptedKey
);
330 if (newEncryptedKey
== NULL
) {
334 oldpw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), oldPassword
, kCFStringEncodingUTF8
, 0);
336 CFReleaseNull(newEncryptedKey
);
340 newpw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), newPassword
, kCFStringEncodingUTF8
, 0);
342 CFReleaseNull(newEncryptedKey
);
343 CFReleaseNull(oldpw
);
347 if (di
->output_size
< kKeySize
) abort();
350 * Unwrap with new key
355 memcpy(&iter
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
+ kSaltSize
, sizeof(iter
));
358 if (ccpbkdf2_hmac(di
, CFDataGetLength(oldpw
), CFDataGetBytePtr(oldpw
),
359 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
361 sizeof(rawkey
), rawkey
) != 0)
364 CFReleaseNull(oldpw
);
367 ccecb_ctx_decl(dec
->size
, deckey
);
368 ccecb_init(dec
, deckey
, kKeySize
, rawkey
);
369 ccecb_update(dec
, deckey
, 1, CFDataGetMutableBytePtr(newEncryptedKey
), CFDataGetMutableBytePtr(newEncryptedKey
));
370 ccecb_ctx_clear(ccecb_context_size(dec
), deckey
);
372 memset(rawkey
, 0, sizeof(rawkey
));
375 * Re-wrap with new key
378 if (ccpbkdf2_hmac(di
, CFDataGetLength(newpw
), CFDataGetBytePtr(newpw
),
379 kSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kKeySize
,
381 sizeof(rawkey
), rawkey
) != 0)
384 CFReleaseNull(newpw
);
387 ccecb_ctx_decl(enc
->size
, enckey
);
388 ccecb_init(enc
, enckey
, kKeySize
, rawkey
);
389 ccecb_update(enc
, enckey
, 1, CFDataGetMutableBytePtr(newEncryptedKey
), CFDataGetMutableBytePtr(newEncryptedKey
));
390 ccecb_ctx_clear(ccecb_context_size(enc
), enckey
);
392 memset(rawkey
, 0, sizeof(rawkey
));
394 return newEncryptedKey
;