2 * Copyright (c) 2013-2014 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@
25 #include <Security/SecureObjectSync/SOSUserKeygen.h>
27 #include <corecrypto/ccrng.h>
28 #include <corecrypto/ccrng_pbkdf2_prng.h>
29 #include <corecrypto/ccec.h>
30 #include <corecrypto/ccdigest.h>
31 #include <corecrypto/ccsha2.h>
32 #include <CommonCrypto/CommonRandomSPI.h>
33 #include <Security/SecKey.h>
34 #include <Security/SecKeyPriv.h>
35 #include <Security/SecFramework.h>
36 #include <utilities/SecCFWrappers.h>
37 #include <utilities/SecCFRelease.h>
38 #include <utilities/debugging.h>
39 #include <Security/SecureObjectSync/SOSCloudCircle.h>
40 #include <Security/SecureObjectSync/SOSInternal.h>
41 #include <Security/SecureObjectSync/SOSAccount.h>
42 #include <Security/SecFramework.h>
43 #include <Security/SecItem.h>
44 #include <utilities/der_plist.h>
45 #include <utilities/der_plist_internal.h>
47 #include <corecrypto/ccder.h>
48 #include <Security/oidsalg.h>
52 // The object identifier id-PBKDF2 identifies the PBKDF2 key derivation
53 // function (Section 5.2).
55 // id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12}
57 // The parameters field associated with this OID in an
58 // AlgorithmIdentifier shall have type PBKDF2-params:
60 // PBKDF2-params ::= SEQUENCE {
62 // specified OCTET STRING,
63 // otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
65 // iterationCount INTEGER (1..MAX),
66 // keyLength INTEGER (1..MAX) OPTIONAL,
67 // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT
68 // algid-hmacWithSHA1 }
70 // The fields of type PKDF2-params have the following meanings:
73 static size_t der_sizeof_SecAsn1Oid(const SecAsn1Oid
* secasn_oid
)
75 return ccder_sizeof(CCDER_OBJECT_IDENTIFIER
, secasn_oid
->Length
);
78 static uint8_t *der_encode_SecAsn1Oid(const SecAsn1Oid
* secasn_oid
, const uint8_t *der
, uint8_t *der_end
)
80 return ccder_encode_tl(CCDER_OBJECT_IDENTIFIER
, secasn_oid
->Length
, der
,
81 ccder_encode_body(secasn_oid
->Length
, secasn_oid
->Data
, der
, der_end
));
84 static const uint8_t *der_expect_SecAsn1Oid(const SecAsn1Oid
* secasn_oid
, const uint8_t *der
, const uint8_t *der_end
)
87 der
= ccder_decode_tl(CCDER_OBJECT_IDENTIFIER
, &len
,
90 if (secasn_oid
->Length
!= len
|| memcmp(secasn_oid
->Data
, der
, len
) != 0)
98 static size_t der_sizeof_pbkdf2_params(size_t saltLen
, const uint8_t *salt
,
99 unsigned long iterationCount
,
100 unsigned long keyLength
)
102 size_t body_size
= ccder_sizeof_raw_octet_string(saltLen
)
103 + ccder_sizeof_uint64(iterationCount
)
104 + ccder_sizeof_uint64(keyLength
)
105 + der_sizeof_SecAsn1Oid(&CSSMOID_PKCS5_HMAC_SHA1
);
107 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, body_size
);
110 static uint8_t *der_encode_pbkdf2_params(size_t saltLen
, const uint8_t *salt
,
111 unsigned long iterationCount
,
112 unsigned long keyLength
,
113 const uint8_t *der
, uint8_t *der_end
)
115 uint8_t* original_der_end
= der_end
;
117 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, original_der_end
, der
,
118 ccder_encode_raw_octet_string(saltLen
, salt
, der
,
119 ccder_encode_uint64(iterationCount
, der
,
120 ccder_encode_uint64(keyLength
, der
,
121 der_encode_SecAsn1Oid(&CSSMOID_PKCS5_HMAC_SHA1
, der
, der_end
)))));
124 static const uint8_t *der_decode_pbkdf2_params(size_t *saltLen
, const uint8_t **salt
,
125 unsigned long *iterationCount
,
126 unsigned long *keyLength
,
127 const uint8_t *der
, const uint8_t *der_end
)
129 const uint8_t * body_end
= NULL
;
130 der
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &body_end
, der
, der_end
);
132 if (body_end
!= der_end
)
135 size_t salt_size
= 0;
136 const uint8_t *salt_bytes
= NULL
;
138 der
= ccder_decode_tl(CCDER_OCTET_STRING
, &salt_size
, der
, body_end
);
142 uint64_t iteration_count
= 0;
143 uint64_t key_len
= 0;
144 der
= ccder_decode_uint64(&iteration_count
, der
, body_end
);
145 if (iteration_count
> UINT32_MAX
)
148 der
= ccder_decode_uint64(&key_len
, der
, body_end
);
149 if (key_len
> UINT32_MAX
)
152 der
= der_expect_SecAsn1Oid(&CSSMOID_PKCS5_HMAC_SHA1
, der
, body_end
);
158 *saltLen
= salt_size
;
160 *iterationCount
= (unsigned long)iteration_count
;
162 *keyLength
= (unsigned long) key_len
;
169 static SecKeyRef
ccec2SecKey(ccec_full_ctx_t fk
)
171 size_t export_size
= ccec_x963_export_size(1, fk
);
172 uint8_t export_keybytes
[export_size
];
173 ccec_x963_export(1, export_keybytes
, fk
);
174 CFDataRef exportedkey
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, export_keybytes
, export_size
, kCFAllocatorNull
);
176 CFDictionaryRef keyattributes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
177 kSecValueData
, exportedkey
,
178 kSecAttrKeyType
, kSecAttrKeyTypeEC
,
179 kSecAttrKeyClass
, kSecAttrKeyClassPrivate
,
182 SecKeyRef retval
= SecKeyCreateFromAttributeDictionary(keyattributes
);
184 CFRelease(keyattributes
);
185 CFRelease(exportedkey
);
186 bzero(export_keybytes
, 0);
191 #define ITERATIONMIN 50000
193 CFDataRef
SOSUserKeyCreateGenerateParameters(CFErrorRef
*error
) {
194 size_t saltlen
= SALTMAX
;
195 uint8_t salt
[saltlen
];
197 size_t iterations
= ITERATIONMIN
;
198 size_t keysize
= 256;
200 if(CCRandomCopyBytes(kCCRandomDefault
, salt
, sizeof(salt
)) != kCCSuccess
) {
201 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("CCRandomCopyBytes failed"), NULL
, error
);
205 CFMutableDataRef result
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
206 CFDataSetLength(result
, der_sizeof_pbkdf2_params(saltlen
, salt
, iterations
, keysize
));
208 uint8_t * encode
= der_encode_pbkdf2_params(saltlen
, salt
, iterations
, keysize
,
209 CFDataGetBytePtr(result
),
210 CFDataGetMutableBytePtr(result
) + CFDataGetLength(result
));
213 CFReleaseNull(result
);
216 secnotice("keygen", "Created new parameters: iterations %zd, keysize %zd: %@", iterations
, keysize
, result
);
222 SecKeyRef
SOSUserKeygen(CFDataRef password
, CFDataRef parameters
, CFErrorRef
*error
)
225 const uint8_t *salt
= NULL
;
227 size_t iterations
= 0;
230 const uint8_t *der
= CFDataGetBytePtr(parameters
);
231 const uint8_t *der_end
= der
+ CFDataGetLength(parameters
);
233 der
= der_decode_pbkdf2_params(&saltlen
, &salt
, &iterations
, &keysize
, der
, der_end
);
236 SOSCreateErrorWithFormat(kSOSErrorDecodeFailure
, NULL
, error
, NULL
,
237 CFSTR("Bad paramter encoding, got: %@"), parameters
);
240 if (keysize
!= 256) {
241 SOSCreateErrorWithFormat(kSOSErrorUnsupported
, NULL
, error
, NULL
,
242 CFSTR("Key size not supported, requested %zd."), keysize
);
246 SOSCreateErrorWithFormat(kSOSErrorUnsupported
, NULL
, error
, NULL
,
247 CFSTR("Salt length not supported, requested %zd."), saltlen
);
250 if (iterations
< ITERATIONMIN
) {
251 SOSCreateErrorWithFormat(kSOSErrorUnsupported
, NULL
, error
, NULL
,
252 CFSTR("Too few iterations, params suggested %zd."), iterations
);
256 debugDumpUserParameters(CFSTR("params-keygen"), parameters
);
259 const uint8_t *password_bytes
= CFDataGetBytePtr(password
);
260 size_t password_length
= CFDataGetLength(password
);
262 const size_t maxbytes
= 128;
264 ccec_const_cp_t cp
= ccec_get_cp(keysize
);
265 struct ccrng_pbkdf2_prng_state pbkdf2_prng
;
267 ccec_full_ctx_decl_cp(cp
, tmpkey
);
269 secnotice("keygen", "Generating key for: iterations %zd, keysize %zd: %@", iterations
, keysize
, parameters
);
271 if (ccrng_pbkdf2_prng_init(&pbkdf2_prng
, maxbytes
,
272 password_length
, password_bytes
,
275 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("prng init failed"), NULL
, error
);
279 if (ccec_generate_key_legacy(cp
, (struct ccrng_state
*)&pbkdf2_prng
, tmpkey
)) {
280 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("Keygen failed"), NULL
, error
);
284 return ccec2SecKey(tmpkey
);
287 void debugDumpUserParameters(CFStringRef message
, CFDataRef parameters
)
290 const uint8_t *salt
= NULL
;
292 size_t iterations
= 0;
295 const uint8_t *der
= CFDataGetBytePtr(parameters
);
296 const uint8_t *der_end
= der
+ CFDataGetLength(parameters
);
298 der
= der_decode_pbkdf2_params(&saltlen
, &salt
, &iterations
, &keysize
, der
, der_end
);
300 secnotice("keygen", "failed to decode pbkdf2 params");
304 BufferPerformWithHexString(salt
, saltlen
, ^(CFStringRef saltHex
) {
305 CFDataPerformWithHexString(parameters
, ^(CFStringRef parametersHex
) {
306 secnotice("keygen", "%@ <Params: count: %zd, keysize: %zd, salt: %@, raw: %@>]", message
, iterations
, keysize
, saltHex
, parametersHex
);
311 CF_RETURNS_RETAINED CFStringRef
UserParametersDescription(CFDataRef parameters
){
313 __block CFStringRef description
= NULL
;
314 CFErrorRef error
= NULL
;
315 CFDataRef newParameters
= NULL
;
316 SecKeyRef newKey
= NULL
;
318 const uint8_t *parse_end
= der_decode_cloud_parameters(kCFAllocatorDefault
, kSecECDSAAlgorithmID
,
319 &newKey
, &newParameters
, &error
,
320 CFDataGetBytePtr(parameters
), CFDataGetPastEndPtr(parameters
));
322 if (parse_end
!= CFDataGetPastEndPtr(parameters
)){
323 secnotice("keygen", "failed to decode cloud parameters");
328 const uint8_t *salt
= NULL
;
330 size_t iterations
= 0;
333 const uint8_t *der
= CFDataGetBytePtr(newParameters
);
334 const uint8_t *der_end
= der
+ CFDataGetLength(newParameters
);
336 der
= der_decode_pbkdf2_params(&saltlen
, &salt
, &iterations
, &keysize
, der
, der_end
);
338 secnotice("keygen", "failed to decode pbkdf2 params");
342 BufferPerformWithHexString(salt
, saltlen
, ^(CFStringRef saltHex
) {
343 CFDataPerformWithHexString(newParameters
, ^(CFStringRef parametersHex
) {
344 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<Params: count: %zd, keysize: %zd, salt: %@, key: %@>"), iterations
, keysize
, saltHex
, newKey
);