5 // Created by Richard Murphy on 2/21/13.
9 #include <SecureObjectSync/SOSUserKeygen.h>
11 #include <corecrypto/ccrng.h>
12 #include <corecrypto/ccrng_pbkdf2_prng.h>
13 #include <corecrypto/ccec.h>
14 #include <corecrypto/ccdigest.h>
15 #include <corecrypto/ccsha2.h>
16 #include <CommonCrypto/CommonRandomSPI.h>
17 #include <Security/SecKey.h>
18 #include <Security/SecKeyPriv.h>
19 #include <Security/SecFramework.h>
20 #include <utilities/SecCFWrappers.h>
21 #include <utilities/SecCFRelease.h>
22 #include <utilities/debugging.h>
23 #include <SecureObjectSync/SOSCloudCircle.h>
24 #include <SecureObjectSync/SOSInternal.h>
25 #include <Security/SecFramework.h>
26 #include <Security/SecItem.h>
27 #include <utilities/der_plist.h>
28 #include <utilities/der_plist_internal.h>
30 #include <corecrypto/ccder.h>
31 #include <Security/oidsalg.h>
35 // The object identifier id-PBKDF2 identifies the PBKDF2 key derivation
36 // function (Section 5.2).
38 // id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12}
40 // The parameters field associated with this OID in an
41 // AlgorithmIdentifier shall have type PBKDF2-params:
43 // PBKDF2-params ::= SEQUENCE {
45 // specified OCTET STRING,
46 // otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
48 // iterationCount INTEGER (1..MAX),
49 // keyLength INTEGER (1..MAX) OPTIONAL,
50 // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT
51 // algid-hmacWithSHA1 }
53 // The fields of type PKDF2-params have the following meanings:
56 static size_t der_sizeof_SecAsn1Oid(const SecAsn1Oid
* secasn_oid
)
58 return ccder_sizeof(CCDER_OBJECT_IDENTIFIER
, secasn_oid
->Length
);
61 static uint8_t *der_encode_SecAsn1Oid(const SecAsn1Oid
* secasn_oid
, const uint8_t *der
, uint8_t *der_end
)
63 return ccder_encode_tl(CCDER_OBJECT_IDENTIFIER
, secasn_oid
->Length
, der
,
64 ccder_encode_body(secasn_oid
->Length
, secasn_oid
->Data
, der
, der_end
));
67 static const uint8_t *der_expect_SecAsn1Oid(const SecAsn1Oid
* secasn_oid
, const uint8_t *der
, const uint8_t *der_end
)
70 der
= ccder_decode_tl(CCDER_OBJECT_IDENTIFIER
, &len
,
73 if (secasn_oid
->Length
!= len
|| memcmp(secasn_oid
->Data
, der
, len
) != 0)
81 static size_t der_sizeof_pbkdf2_params(size_t saltLen
, const uint8_t *salt
,
82 unsigned long iterationCount
,
83 unsigned long keyLength
)
85 size_t body_size
= ccder_sizeof_raw_octet_string(saltLen
)
86 + ccder_sizeof_uint64(iterationCount
)
87 + ccder_sizeof_uint64(keyLength
)
88 + der_sizeof_SecAsn1Oid(&CSSMOID_PKCS5_HMAC_SHA1
);
90 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, body_size
);
93 static uint8_t *der_encode_pbkdf2_params(size_t saltLen
, const uint8_t *salt
,
94 unsigned long iterationCount
,
95 unsigned long keyLength
,
96 const uint8_t *der
, uint8_t *der_end
)
98 uint8_t* original_der_end
= der_end
;
100 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, original_der_end
, der
,
101 ccder_encode_raw_octet_string(saltLen
, salt
, der
,
102 ccder_encode_uint64(iterationCount
, der
,
103 ccder_encode_uint64(keyLength
, der
,
104 der_encode_SecAsn1Oid(&CSSMOID_PKCS5_HMAC_SHA1
, der
, der_end
)))));
107 static const uint8_t *der_decode_pbkdf2_params(size_t *saltLen
, const uint8_t **salt
,
108 unsigned long *iterationCount
,
109 unsigned long *keyLength
,
110 const uint8_t *der
, const uint8_t *der_end
)
112 const uint8_t * body_end
= NULL
;
113 der
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &body_end
, der
, der_end
);
115 if (body_end
!= der_end
)
118 size_t salt_size
= 0;
119 const uint8_t *salt_bytes
= NULL
;
121 der
= ccder_decode_tl(CCDER_OCTET_STRING
, &salt_size
, der
, body_end
);
125 uint64_t iteration_count
= 0;
126 uint64_t key_len
= 0;
127 der
= ccder_decode_uint64(&iteration_count
, der
, body_end
);
128 if (iteration_count
> UINT32_MAX
)
131 der
= ccder_decode_uint64(&key_len
, der
, body_end
);
132 if (key_len
> UINT32_MAX
)
135 der
= der_expect_SecAsn1Oid(&CSSMOID_PKCS5_HMAC_SHA1
, der
, body_end
);
141 *saltLen
= salt_size
;
143 *iterationCount
= (unsigned long)iteration_count
;
145 *keyLength
= (unsigned long) key_len
;
152 static SecKeyRef
ccec2SecKey(ccec_full_ctx_t fk
)
154 size_t export_size
= ccec_x963_export_size(1, fk
);
155 uint8_t export_keybytes
[export_size
];
156 ccec_x963_export(1, export_keybytes
, fk
);
157 CFDataRef exportedkey
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, export_keybytes
, export_size
, kCFAllocatorNull
);
159 CFDictionaryRef keyattributes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
160 kSecValueData
, exportedkey
,
161 kSecAttrKeyType
, kSecAttrKeyTypeEC
,
162 kSecAttrKeyClass
, kSecAttrKeyClassPrivate
,
165 SecKeyRef retval
= SecKeyCreateFromAttributeDictionary(keyattributes
);
167 CFRelease(keyattributes
);
168 CFRelease(exportedkey
);
169 bzero(export_keybytes
, 0);
174 #define ITERATIONMIN 50000
176 CFDataRef
SOSUserKeyCreateGenerateParameters(CFErrorRef
*error
) {
177 size_t saltlen
= SALTMAX
;
178 uint8_t salt
[saltlen
];
180 size_t iterations
= ITERATIONMIN
;
181 size_t keysize
= 256;
183 if(CCRandomCopyBytes(kCCRandomDefault
, salt
, sizeof(salt
)) != kCCSuccess
) {
184 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("CCRandomCopyBytes failed"), NULL
, error
);
188 CFMutableDataRef result
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
189 CFDataSetLength(result
, der_sizeof_pbkdf2_params(saltlen
, salt
, iterations
, keysize
));
191 uint8_t * encode
= der_encode_pbkdf2_params(saltlen
, salt
, iterations
, keysize
,
192 CFDataGetBytePtr(result
),
193 CFDataGetMutableBytePtr(result
) + CFDataGetLength(result
));
196 CFReleaseNull(result
);
199 secnotice("keygen", "Created new parameters: iterations %zd, keysize %zd: %@", iterations
, keysize
, result
);
205 SecKeyRef
SOSUserKeygen(CFDataRef password
, CFDataRef parameters
, CFErrorRef
*error
)
208 const uint8_t *salt
= NULL
;
210 size_t iterations
= 0;
213 const uint8_t *der
= CFDataGetBytePtr(parameters
);
214 const uint8_t *der_end
= der
+ CFDataGetLength(parameters
);
216 der
= der_decode_pbkdf2_params(&saltlen
, &salt
, &iterations
, &keysize
, der
, der_end
);
219 SOSCreateErrorWithFormat(kSOSErrorDecodeFailure
, NULL
, error
, NULL
,
220 CFSTR("Bad paramter encoding, got: %@"), parameters
);
223 if (keysize
!= 256) {
224 SOSCreateErrorWithFormat(kSOSErrorUnsupported
, NULL
, error
, NULL
,
225 CFSTR("Key size not supported, requested %zd."), keysize
);
229 SOSCreateErrorWithFormat(kSOSErrorUnsupported
, NULL
, error
, NULL
,
230 CFSTR("Salt length not supported, requested %zd."), saltlen
);
233 if (iterations
< ITERATIONMIN
) {
234 SOSCreateErrorWithFormat(kSOSErrorUnsupported
, NULL
, error
, NULL
,
235 CFSTR("Too few iterations, params suggested %zd."), iterations
);
239 const uint8_t *password_bytes
= CFDataGetBytePtr(password
);
240 size_t password_length
= CFDataGetLength(password
);
242 const size_t maxbytes
= 128;
244 ccec_const_cp_t cp
= ccec_get_cp(keysize
);
245 struct ccrng_pbkdf2_prng_state pbkdf2_prng
;
247 ccec_full_ctx_decl_cp(cp
, tmpkey
);
249 secnotice("keygen", "Generating key for: iterations %zd, keysize %zd: %@", iterations
, keysize
, parameters
);
251 if (ccrng_pbkdf2_prng_init(&pbkdf2_prng
, maxbytes
,
252 password_length
, password_bytes
,
255 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("prng init failed"), NULL
, error
);
259 if (ccec_generate_key(cp
, (struct ccrng_state
*)&pbkdf2_prng
, tmpkey
)) {
260 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("Keygen failed"), NULL
, error
);
265 return ccec2SecKey(tmpkey
);
268 void debugDumpUserParameters(CFStringRef message
, CFDataRef parameters
)
271 const uint8_t *salt
= NULL
;
273 size_t iterations
= 0;
276 const uint8_t *der
= CFDataGetBytePtr(parameters
);
277 const uint8_t *der_end
= der
+ CFDataGetLength(parameters
);
279 der
= der_decode_pbkdf2_params(&saltlen
, &salt
, &iterations
, &keysize
, der
, der_end
);
281 secnotice("keygen", "failed to decode pbkdf2 params");
285 BufferPerformWithHexString(salt
, saltlen
, ^(CFStringRef saltHex
) {
286 CFDataPerformWithHexString(parameters
, ^(CFStringRef parametersHex
) {
287 secnotice("keygen", "%@ <Params: count: %zd, keysize: %zd, salt: %@, raw: %@>]", message
, iterations
, keysize
, saltHex
, parametersHex
);