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 #define kBCKeySize CCAES_KEY_SIZE_128
41 #define kBCSaltSize 20
42 #define kBCIterations 5000
47 #define BCPaddingSize 256
48 #define BCMaxSize 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();
59 CFMutableDataRef key
, npw
;
62 *outBreadcrumb
= NULL
;
63 *outEncryptedKey
= NULL
;
67 key
= CFDataCreateMutable(SecCFAllocatorZeroize(), 0);
71 CFDataSetLength(key
, kBCKeySize
+ kBCSaltSize
+ 4);
72 if (SecRandomCopyBytes(kSecRandomDefault
, CFDataGetLength(key
) - 4, CFDataGetMutableBytePtr(key
)) != 0) {
76 if (SecRandomCopyBytes(kSecRandomDefault
, BCIVLen
, iv
) != 0) {
81 uint32_t size
= htonl(kBCIterations
);
82 memcpy(CFDataGetMutableBytePtr(key
) + kBCKeySize
+ kBCSaltSize
, &size
, sizeof(size
));
85 * Create data for password
88 pw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), inPassword
, kCFStringEncodingUTF8
, 0);
94 const CFIndex passwordLength
= CFDataGetLength(pw
);
96 if (passwordLength
> BCMaxSize
) {
102 CFIndex paddedSize
= passwordLength
+ BCPaddingSize
- (passwordLength
% BCPaddingSize
);
103 const CFIndex outLength
= 1 + BCIVLen
+ 4 + paddedSize
+ BCTagLen
;
105 npw
= CFDataCreateMutable(NULL
, outLength
);
111 CFDataSetLength(npw
, outLength
);
113 cc_clear(outLength
, CFDataGetMutableBytePtr(npw
));
114 CFDataGetMutableBytePtr(npw
)[0] = BCversion2
;
115 memcpy(CFDataGetMutableBytePtr(npw
) + 1, iv
, BCIVLen
);
116 size
= htonl(passwordLength
);
117 memcpy(CFDataGetMutableBytePtr(npw
) + 1 + BCIVLen
, &size
, sizeof(size
));
118 memcpy(CFDataGetMutableBytePtr(npw
) + 1 + BCIVLen
+ 4, CFDataGetBytePtr(pw
), passwordLength
);
121 * Now create a GCM encrypted password using the random key
124 ccgcm_ctx_decl(gcm
->size
, ctx
);
125 ccgcm_init(gcm
, ctx
, kBCKeySize
, CFDataGetMutableBytePtr(key
));
126 ccgcm_set_iv(gcm
, ctx
, BCIVLen
, iv
);
127 ccgcm_gmac(gcm
, ctx
, 1, CFDataGetMutableBytePtr(npw
));
128 ccgcm_update(gcm
, ctx
, outLength
- BCTagLen
- BCIVLen
- 1, CFDataGetMutableBytePtr(npw
) + 1 + BCIVLen
, CFDataGetMutableBytePtr(npw
) + 1 + BCIVLen
);
129 ccgcm_finalize(gcm
, ctx
, BCTagLen
, CFDataGetMutableBytePtr(npw
) + outLength
- BCTagLen
);
130 ccgcm_ctx_clear(gcm
->size
, ctx
);
133 * Wrapping key is PBKDF2(sha256) over password
136 const struct ccdigest_info
*di
= ccsha256_di();
137 uint8_t rawkey
[CCSHA256_OUTPUT_SIZE
];
138 _Static_assert(sizeof(rawkey
) >= kBCKeySize
, "keysize changed w/o updating digest");
139 if (sizeof(rawkey
) != di
->output_size
) abort();
141 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
142 kBCSaltSize
, CFDataGetMutableBytePtr(key
) + kBCKeySize
,
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
, kBCKeySize
, rawkey
);
153 ccecb_update(ecb
, ecbkey
, 1, CFDataGetMutableBytePtr(key
), CFDataGetMutableBytePtr(key
));
154 ccecb_ctx_clear(ccecb_context_size(ecb
), ecbkey
);
160 cc_clear(sizeof(rawkey
), 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 CFMutableDataRef gcmkey
, oldpw
;
187 if (CFDataGetLength(inEncryptedKey
) < kBCKeySize
+ kBCSaltSize
+ 4) {
191 if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion1
) {
192 if (CFDataGetLength(inBreadcrumb
) < 1 + 4 + BCPaddingSize
+ BCTagLen
)
195 outLength
= CFDataGetLength(inBreadcrumb
) - 1 - BCTagLen
;
196 } else if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion2
) {
197 if (CFDataGetLength(inBreadcrumb
) < 1 + BCIVLen
+ 4 + BCPaddingSize
+ BCTagLen
)
199 outLength
= CFDataGetLength(inBreadcrumb
) - 1 - BCIVLen
- BCTagLen
;
204 gcmkey
= CFDataCreateMutableCopy(SecCFAllocatorZeroize(), 0, inEncryptedKey
);
205 if (gcmkey
== NULL
) {
209 if ((outLength
% 16) != 0 && outLength
< 4) {
210 CFReleaseNull(gcmkey
);
214 oldpw
= CFDataCreateMutable(SecCFAllocatorZeroize(), outLength
);
216 CFReleaseNull(gcmkey
);
219 CFDataSetLength(oldpw
, outLength
);
222 * Create data for password
225 pw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), inPassword
, kCFStringEncodingUTF8
, 0);
227 CFReleaseNull(oldpw
);
228 CFReleaseNull(gcmkey
);
233 * Wrapping key is HMAC(sha256) over password
236 const struct ccdigest_info
*di
= ccsha256_di();
237 uint8_t rawkey
[CCSHA256_OUTPUT_SIZE
];
238 _Static_assert(sizeof(rawkey
) >= kBCKeySize
, "keysize changed w/o updating digest");
239 if (sizeof(rawkey
) != di
->output_size
) abort();
241 memcpy(&size
, CFDataGetMutableBytePtr(gcmkey
) + kBCKeySize
+ kBCSaltSize
, sizeof(size
));
244 if (ccpbkdf2_hmac(di
, CFDataGetLength(pw
), CFDataGetBytePtr(pw
),
245 kBCSaltSize
, CFDataGetMutableBytePtr(gcmkey
) + kBCKeySize
,
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
, kBCKeySize
, rawkey
);
258 ccecb_update(ecb
, ecbkey
, 1, CFDataGetMutableBytePtr(gcmkey
), CFDataGetMutableBytePtr(gcmkey
));
259 ccecb_ctx_clear(ccecb_context_size(ecb
), ecbkey
);
264 uint8_t tag
[BCTagLen
];
266 if (CFDataGetBytePtr(inBreadcrumb
)[0] == BCversion1
) {
267 memcpy(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + outLength
, BCTagLen
);
269 ccgcm_one_shot_legacy(ccaes_gcm_decrypt_mode(), kBCKeySize
, CFDataGetMutableBytePtr(gcmkey
), 0, NULL
, 1, CFDataGetBytePtr(inBreadcrumb
),
270 outLength
, CFDataGetBytePtr(inBreadcrumb
) + 1, CFDataGetMutableBytePtr(oldpw
), BCTagLen
, tag
);
271 if (memcmp(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + outLength
, BCTagLen
) != 0) {
272 CFReleaseNull(oldpw
);
273 CFReleaseNull(gcmkey
);
278 const uint8_t *iv
= CFDataGetBytePtr(inBreadcrumb
) + 1;
280 memcpy(tag
, CFDataGetBytePtr(inBreadcrumb
) + 1 + BCIVLen
+ outLength
, BCTagLen
);
282 res
= ccgcm_one_shot(ccaes_gcm_decrypt_mode(), kBCKeySize
, CFDataGetMutableBytePtr(gcmkey
),
284 1, CFDataGetBytePtr(inBreadcrumb
),
285 outLength
, CFDataGetBytePtr(inBreadcrumb
) + 1 + BCIVLen
, CFDataGetMutableBytePtr(oldpw
),
288 CFReleaseNull(gcmkey
);
289 CFReleaseNull(oldpw
);
290 CFReleaseNull(gcmkey
);
295 CFReleaseNull(gcmkey
);
298 memcpy(&size
, CFDataGetMutableBytePtr(oldpw
), sizeof(size
));
300 if ((ssize_t
) size
> outLength
- 4) {
301 CFReleaseNull(oldpw
);
304 memmove(CFDataGetMutableBytePtr(oldpw
), CFDataGetMutableBytePtr(oldpw
) + 4, size
);
305 CFDataSetLength(oldpw
, size
);
307 *outPassword
= CFStringCreateFromExternalRepresentation(SecCFAllocatorZeroize(), oldpw
, kCFStringEncodingUTF8
);
308 CFReleaseNull(oldpw
);
314 SecBreadcrumbCreateNewEncryptedKey(CFStringRef oldPassword
,
315 CFStringRef newPassword
,
316 CFDataRef encryptedKey
,
317 CFErrorRef
*outError
)
319 const struct ccmode_ecb
*enc
= ccaes_ecb_encrypt_mode();
320 const struct ccmode_ecb
*dec
= ccaes_ecb_decrypt_mode();
321 const struct ccdigest_info
*di
= ccsha256_di();
322 uint8_t rawkey
[CCSHA256_OUTPUT_SIZE
];
323 CFDataRef newpw
= NULL
, oldpw
= NULL
;
324 CFMutableDataRef newEncryptedKey
;
326 _Static_assert(sizeof(rawkey
) >= kBCKeySize
, "keysize changed w/o updating digest");
327 if (sizeof(rawkey
) != di
->output_size
) abort();
329 if (CFDataGetLength(encryptedKey
) < kBCKeySize
+ kBCSaltSize
+ 4) {
333 newEncryptedKey
= CFDataCreateMutableCopy(SecCFAllocatorZeroize(), 0, encryptedKey
);
334 if (newEncryptedKey
== NULL
) {
338 oldpw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), oldPassword
, kCFStringEncodingUTF8
, 0);
340 CFReleaseNull(newEncryptedKey
);
344 newpw
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), newPassword
, kCFStringEncodingUTF8
, 0);
346 CFReleaseNull(newEncryptedKey
);
347 CFReleaseNull(oldpw
);
352 * Unwrap with new key
357 memcpy(&iter
, CFDataGetMutableBytePtr(newEncryptedKey
) + kBCKeySize
+ kBCSaltSize
, sizeof(iter
));
360 if (ccpbkdf2_hmac(di
, CFDataGetLength(oldpw
), CFDataGetBytePtr(oldpw
),
361 kBCSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kBCKeySize
,
363 sizeof(rawkey
), rawkey
) != 0)
366 CFReleaseNull(oldpw
);
369 ccecb_ctx_decl(dec
->size
, deckey
);
370 ccecb_init(dec
, deckey
, kBCKeySize
, rawkey
);
371 ccecb_update(dec
, deckey
, 1, CFDataGetMutableBytePtr(newEncryptedKey
), CFDataGetMutableBytePtr(newEncryptedKey
));
372 ccecb_ctx_clear(ccecb_context_size(dec
), deckey
);
374 cc_clear(sizeof(rawkey
), rawkey
);
377 * Re-wrap with new key
380 if (ccpbkdf2_hmac(di
, CFDataGetLength(newpw
), CFDataGetBytePtr(newpw
),
381 kBCSaltSize
, CFDataGetMutableBytePtr(newEncryptedKey
) + kBCKeySize
,
383 sizeof(rawkey
), rawkey
) != 0)
386 CFReleaseNull(newpw
);
389 ccecb_ctx_decl(enc
->size
, enckey
);
390 ccecb_init(enc
, enckey
, kBCKeySize
, rawkey
);
391 ccecb_update(enc
, enckey
, 1, CFDataGetMutableBytePtr(newEncryptedKey
), CFDataGetMutableBytePtr(newEncryptedKey
));
392 ccecb_ctx_clear(ccecb_context_size(enc
), enckey
);
394 cc_clear(sizeof(rawkey
), rawkey
);
396 return newEncryptedKey
;