5 * Created by Michael Brouwer on 1/29/07.
6 * Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
10 #include <CoreFoundation/CoreFoundation.h>
11 #include <Security/SecCertificate.h>
12 #include <Security/SecCertificateInternal.h>
13 #include <Security/SecKey.h>
14 #include <Security/SecKeyPriv.h>
15 #include <Security/SecItem.h>
16 #include <Security/SecAsn1Types.h>
17 #include <Security/oidsalg.h>
18 #include <Security/SecureTransport.h>
19 #include <Security/SecRandom.h>
20 #include <utilities/array_size.h>
21 #include <CommonCrypto/CommonDigest.h>
22 #include <libDER/libDER.h>
26 #include "Security_regressions.h"
28 #include "utilities/SecCFRelease.h"
30 static void testdigestandsignalg(SecKeyRef privKey
, SecKeyRef pubKey
, const SecAsn1AlgId
*algId
) {
31 uint8_t dataToDigest
[256] = {0,};
32 size_t dataToDigestLen
= sizeof(dataToDigest
);
33 size_t sigLen
= SecKeyGetSize(privKey
, kSecKeySignatureSize
);
37 oid
.length
= algId
->algorithm
.Length
;
38 oid
.data
= algId
->algorithm
.Data
;
40 /* Get the oid in decimal for display purposes. */
41 CFStringRef oidStr
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, &oid
);
43 CFStringGetCString(oidStr
, oidBuf
, sizeof(oidBuf
), kCFStringEncodingUTF8
);
50 ok_status(status
= SecKeyDigestAndSign(privKey
, algId
, dataToDigest
, dataToDigestLen
,
51 sig
, &sigLen
), "digest and sign %s with %ld bit RSA key", oidBuf
, sigLen
* 8);
53 skip("SecKeyDigestAndSign failed", 3, status
== errSecSuccess
);
55 /* Verify the signature we just made. */
56 ok_status(SecKeyDigestAndVerify(pubKey
, algId
, dataToDigest
, dataToDigestLen
,
57 sig
, sigLen
), "digest and verify");
58 /* Invalidate the signature. */
60 is_status(SecKeyDigestAndVerify(pubKey
, algId
, dataToDigest
, dataToDigestLen
,
61 sig
, sigLen
), errSSLCrypto
, "digest and verify bad sig");
63 dataToDigest
[0] ^= 0xff;
64 is_status(SecKeyDigestAndVerify(pubKey
, algId
, dataToDigest
, dataToDigestLen
,
65 sig
, sigLen
), errSSLCrypto
, "digest and verify bad digest");
69 static void testdigestandsign(SecKeyRef privKey
, SecKeyRef pubKey
) {
70 static const SecAsn1Oid
*oids
[] = {
72 &CSSMOID_SHA224WithRSA
,
73 &CSSMOID_SHA256WithRSA
,
74 &CSSMOID_SHA384WithRSA
,
75 &CSSMOID_SHA512WithRSA
,
77 &CSSMOID_SHA1WithRSA_OIW
,
78 &CSSMOID_SHA1WithDSA
, // BSAFE
79 &CSSMOID_SHA1WithDSA_CMS
, // X509/CMS
80 &CSSMOID_SHA1WithDSA_JDK
, // JDK 1.1
86 SecAsn1AlgId algId
= {};
87 for (ix
= 0; ix
< array_size(oids
); ++ix
) {
89 algId
.algorithm
= *oids
[ix
];
91 algId
.algorithm
.Length
= 0;
92 algId
.algorithm
.Data
= NULL
;
95 testdigestandsignalg(privKey
, pubKey
, &algId
);
100 static void dump_bytes(uint8_t* bytes
, size_t amount
)
103 printf("0x%02x ", *bytes
);
110 #define kEncryptDecryptTestCount 5
111 static void test_encrypt_decrypt(SecKeyRef pubKey
, SecKeyRef privKey
, uint32_t padding
, size_t keySizeInBytes
)
114 size_t max_len
= keySizeInBytes
;
116 case kSecPaddingNone
: max_len
= keySizeInBytes
; break;
117 case kSecPaddingOAEP
: max_len
= keySizeInBytes
- 2 - 2 * CC_SHA1_DIGEST_LENGTH
; break;
118 case kSecPaddingPKCS1
: max_len
= keySizeInBytes
- 11; break;
119 default: skip("what is the max_len for this padding?", 5, false);
122 uint8_t secret
[max_len
+ 1], encrypted_secret
[keySizeInBytes
], decrypted_secret
[keySizeInBytes
];
123 uint8_t *secret_ptr
= secret
;
124 size_t secret_len
= max_len
;
125 size_t encrypted_secret_len
= sizeof(encrypted_secret
);
126 size_t decrypted_secret_len
= sizeof(decrypted_secret
);
127 memset(decrypted_secret
, 0xff, decrypted_secret_len
);
128 SecRandomCopyBytes(kSecRandomDefault
, sizeof(secret
), secret
);
130 // zero pad, no accidental second zero byte
131 if (padding
== kSecPaddingNone
) {
136 is_status(SecKeyEncrypt(pubKey
, padding
,
137 secret
, sizeof(secret
),
138 encrypted_secret
, &encrypted_secret_len
), errSecParam
, "encrypt secret (overflow)");
139 ok_status(SecKeyEncrypt(pubKey
, padding
,
141 encrypted_secret
, &encrypted_secret_len
), "encrypt secret");
143 ok_status(SecKeyDecrypt(privKey
, padding
,
144 encrypted_secret
, encrypted_secret_len
,
145 decrypted_secret
, &decrypted_secret_len
), "decrypt secret");
147 // zero padding is removed on decode
148 if (padding
== kSecPaddingNone
) {
153 ok(decrypted_secret_len
== secret_len
, "correct length");
154 ok_status(memcmp(secret_ptr
, decrypted_secret
, secret_len
), "verify secret");
158 #define kKeyGenTestCount (49 + (3*kEncryptDecryptTestCount))
159 static void testkeygen(size_t keySizeInBits
) {
160 SecKeyRef pubKey
= NULL
, privKey
= NULL
;
161 size_t keySizeInBytes
= (keySizeInBits
+ 7) / 8;
164 kzib
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &keySizeInBits
);
165 CFMutableDictionaryRef kgp
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
166 CFDictionaryAddValue(kgp
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
167 CFDictionaryAddValue(kgp
, kSecAttrKeySizeInBits
, kzib
);
170 ok_status(status
= SecKeyGeneratePair(kgp
, &pubKey
, &privKey
),
171 "Generate %ld bit (%ld byte) RSA keypair", keySizeInBits
,
177 skip("keygen failed", 8, status
== errSecSuccess
);
178 ok(pubKey
, "pubkey returned");
179 ok(privKey
, "privKey returned");
180 is(SecKeyGetSize(pubKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "public key size is ok");
181 is(SecKeyGetSize(privKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "private key size is ok");
183 /* Sign something. */
184 uint8_t something
[keySizeInBytes
];
185 size_t something_len
= keySizeInBytes
- 11;
186 SecRandomCopyBytes(kSecRandomDefault
, sizeof(something
), something
);
187 uint8_t sig
[keySizeInBytes
];
188 size_t sigLen
= sizeof(sig
);
189 is_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1
,
190 something
, something_len
+ 1, sig
, &sigLen
),
191 errSecParam
, "sign overflow");
192 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1
,
193 something
, something_len
, sig
, &sigLen
), "sign something");
194 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1
,
195 something
, something_len
, sig
, sigLen
), "verify sig on something");
197 // Torture test ASN.1 encoder by setting high bit to 1.
198 uint8_t digest
[CC_SHA512_DIGEST_LENGTH
] = {
199 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
200 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
201 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
202 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
203 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
204 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
205 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
206 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
208 //CC_MD2(something, sizeof(something), digest);
209 ok_status(!SecKeyRawSign(privKey
, kSecPaddingPKCS1MD2
,
210 digest
, CC_MD2_DIGEST_LENGTH
, sig
, &sigLen
),
211 "don't sign md2 digest");
212 ok_status(!SecKeyRawVerify(pubKey
, kSecPaddingPKCS1MD2
,
213 digest
, CC_MD2_DIGEST_LENGTH
, sig
, sigLen
),
214 "verify sig on md2 digest fails");
216 //CC_MD5(something, sizeof(something), digest);
217 sigLen
= sizeof(sig
);
218 ok_status(!SecKeyRawSign(privKey
, kSecPaddingPKCS1MD5
,
219 digest
, CC_MD5_DIGEST_LENGTH
, sig
, &sigLen
),
220 "don't sign md5 digest");
221 ok_status(!SecKeyRawVerify(pubKey
, kSecPaddingPKCS1MD5
,
222 digest
, CC_MD5_DIGEST_LENGTH
, sig
, sigLen
),
223 "verify sig on md5 digest fails");
225 //CCDigest(kCCDigestSHA1, something, sizeof(something), digest);
226 sigLen
= sizeof(sig
);
227 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA1
,
228 digest
, CC_SHA1_DIGEST_LENGTH
, sig
, &sigLen
),
230 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA1
,
231 digest
, CC_SHA1_DIGEST_LENGTH
, sig
, sigLen
),
232 "verify sig on sha1 digest");
234 uint8_t signature
[keySizeInBytes
], *ptr
= signature
;
235 size_t signature_len
= sizeof(signature
);
236 ok_status(SecKeyDecrypt(pubKey
, kSecPaddingNone
, sig
, sigLen
, signature
, &signature_len
), "inspect signature");
237 is(signature_len
, keySizeInBytes
- 1, "got signature");
238 while(*ptr
&& ((size_t)(ptr
- signature
) < signature_len
)) ptr
++;
239 is(signature
+ signature_len
- ptr
, 16 /* length(\0 || OID_SHA1) */ + CC_SHA1_DIGEST_LENGTH
, "successful decode");
241 /* PKCS1 padding is 00 01 PAD * 8 or more 00 data.
242 data is SEQ { SEQ { OID NULL } BIT STRING 00 DIGEST }
243 So min data + pad overhead is 11 + 9 + oidlen
244 oidlen = 11 for the sha2 family of oids, so we have 29 bytes; or
245 232 bits of minimum overhead. */
246 const size_t pkcs1Overhead
= 232;
247 if (keySizeInBits
> 224 + pkcs1Overhead
) {
248 //CC_SHA224(something, sizeof(something), digest);
249 sigLen
= sizeof(sig
);
250 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA224
,
251 digest
, CC_SHA224_DIGEST_LENGTH
, sig
, &sigLen
),
252 "sign sha224 digest");
253 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA224
,
254 digest
, CC_SHA224_DIGEST_LENGTH
, sig
, sigLen
),
255 "verify sig on sha224 digest");
258 if (keySizeInBits
> 256 + pkcs1Overhead
) {
259 //CC_SHA256(something, sizeof(something), digest);
260 sigLen
= sizeof(sig
);
261 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA256
,
262 digest
, CC_SHA256_DIGEST_LENGTH
, sig
, &sigLen
),
263 "sign sha256 digest");
264 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA256
,
265 digest
, CC_SHA256_DIGEST_LENGTH
, sig
, sigLen
),
266 "verify sig on sha256 digest");
269 if (keySizeInBits
> 384 + pkcs1Overhead
) {
270 //CC_SHA384(something, sizeof(something), digest);
271 sigLen
= sizeof(sig
);
272 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA384
,
273 digest
, CC_SHA384_DIGEST_LENGTH
, sig
, &sigLen
),
274 "sign sha384 digest");
275 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA384
,
276 digest
, CC_SHA384_DIGEST_LENGTH
, sig
, sigLen
),
277 "verify sig on sha384 digest");
280 if (keySizeInBits
> 512 + pkcs1Overhead
) {
281 //CC_SHA512(something, sizeof(something), digest);
282 sigLen
= sizeof(sig
);
283 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA512
,
284 digest
, CC_SHA512_DIGEST_LENGTH
, sig
, &sigLen
),
285 "sign sha512 digest");
286 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA512
,
287 digest
, CC_SHA512_DIGEST_LENGTH
, sig
, sigLen
),
288 "verify sig on sha512 digest");
291 test_encrypt_decrypt(pubKey
, privKey
, kSecPaddingNone
, keySizeInBytes
);
292 test_encrypt_decrypt(pubKey
, privKey
, kSecPaddingPKCS1
, keySizeInBytes
);
293 test_encrypt_decrypt(pubKey
, privKey
, kSecPaddingOAEP
, keySizeInBytes
);
295 testdigestandsign(privKey
, pubKey
);
297 const void *privkeys
[] = {
300 const void *privvalues
[] = {
303 CFDictionaryRef privitem
= CFDictionaryCreate(NULL
, privkeys
, privvalues
,
304 array_size(privkeys
), NULL
, NULL
);
305 ok_status(SecItemAdd(privitem
, NULL
), "add private key");
306 ok_status(SecItemDelete(privitem
), "delete private key");
307 CFReleaseNull(privitem
);
309 const void *pubkeys
[] = {
312 const void *pubvalues
[] = {
315 CFDictionaryRef pubitem
= CFDictionaryCreate(NULL
, pubkeys
, pubvalues
,
316 array_size(pubkeys
), NULL
, NULL
);
317 ok_status(SecItemAdd(pubitem
, NULL
), "add public key");
318 ok_status(SecItemDelete(pubitem
), "delete public key");
319 CFReleaseNull(pubitem
);
322 CFReleaseNull(pubKey
);
323 CFReleaseNull(privKey
);
327 #define kKeyGen2TestCount 12
328 static void testkeygen2(size_t keySizeInBits
) {
329 SecKeyRef pubKey
= NULL
, privKey
= NULL
;
330 size_t keySizeInBytes
= (keySizeInBits
+ 7) / 8;
333 CFUUIDRef ourUUID
= CFUUIDCreate(kCFAllocatorDefault
);
334 CFStringRef uuidString
= CFUUIDCreateString(kCFAllocatorDefault
, ourUUID
);
335 CFMutableStringRef publicName
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, uuidString
);
336 CFMutableStringRef privateName
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, uuidString
);
338 CFReleaseNull(ourUUID
);
339 CFReleaseNull(uuidString
);
341 CFStringAppend(publicName
, CFSTR("-Public-40"));
342 CFStringAppend(privateName
, CFSTR("-Private-40"));
343 CFMutableDictionaryRef pubd
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
344 CFMutableDictionaryRef privd
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
346 CFDictionaryAddValue(pubd
, kSecAttrLabel
, publicName
);
347 CFDictionaryAddValue(privd
, kSecAttrLabel
, privateName
);
349 kzib
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &keySizeInBits
);
350 CFMutableDictionaryRef kgp
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
351 CFDictionaryAddValue(kgp
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
352 CFDictionaryAddValue(kgp
, kSecAttrKeySizeInBits
, kzib
);
353 CFDictionaryAddValue(kgp
, kSecAttrIsPermanent
, kCFBooleanTrue
);
354 CFDictionaryAddValue(kgp
, kSecPublicKeyAttrs
, pubd
);
355 CFDictionaryAddValue(kgp
, kSecPrivateKeyAttrs
, privd
);
358 ok_status(status
= SecKeyGeneratePair(kgp
, &pubKey
, &privKey
),
359 "Generate %ld bit (%ld byte) persistent RSA keypair",
360 keySizeInBits
, keySizeInBytes
);
365 skip("keygen failed", 8, status
== errSecSuccess
);
366 ok(pubKey
, "pubkey returned");
367 ok(privKey
, "privKey returned");
368 is(SecKeyGetSize(pubKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "public key size is ok");
369 is(SecKeyGetSize(privKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "private key size is ok");
371 SecKeyRef pubKey2
, privKey2
;
372 CFDictionaryAddValue(pubd
, kSecClass
, kSecClassKey
);
373 CFDictionaryAddValue(pubd
, kSecReturnRef
, kCFBooleanTrue
);
374 CFDictionaryAddValue(privd
, kSecClass
, kSecClassKey
);
375 CFDictionaryAddValue(privd
, kSecReturnRef
, kCFBooleanTrue
);
376 CFDictionaryAddValue(privd
, kSecAttrCanSign
, kCFBooleanTrue
);
377 ok_status(SecItemCopyMatching(pubd
, (CFTypeRef
*)&pubKey2
),
378 "retrieve pub key by label");
379 ok_status(SecItemCopyMatching(privd
, (CFTypeRef
*)&privKey2
),
380 "retrieve priv key by label and kSecAttrCanSign");
382 /* Sign something. */
383 uint8_t something
[50] = {0x80, 0xbe, 0xef, 0xba, 0xd0, };
384 uint8_t sig
[keySizeInBytes
];
385 size_t sigLen
= keySizeInBytes
;
386 ok_status(SecKeyRawSign(privKey2
, kSecPaddingPKCS1
,
387 something
, sizeof(something
), sig
, &sigLen
), "sign something");
388 ok_status(SecKeyRawVerify(pubKey2
, kSecPaddingPKCS1
,
389 something
, sizeof(something
), sig
, sigLen
), "verify sig on something");
391 sigLen
= keySizeInBytes
;
392 is_status(SecKeyEncrypt(pubKey2
, kSecPaddingPKCS1SHA1
,
393 something
, sizeof(something
), sig
, &sigLen
), errSecParam
,
394 "encrypt something with invalid padding");
397 CFReleaseNull(pubKey2
);
398 CFReleaseNull(privKey2
);
400 /* delete from keychain - note: do it before releasing publicName and privateName
401 because pubd and privd have no retain/release callbacks */
402 ok_status(SecItemDelete(pubd
), "delete generated pub key");
403 ok_status(SecItemDelete(privd
), "delete generated priv key");
407 CFReleaseNull(pubKey
);
408 CFReleaseNull(privKey
);
410 CFReleaseNull(publicName
);
411 CFReleaseNull(privateName
);
417 /* Test basic add delete update copy matching stuff. */
418 #define kTestCount ((3 * kKeyGenTestCount) + kKeyGen2TestCount)
419 static void tests(void)
421 /* Comment out lines below for testing generating all common key sizes,
422 disabled now for speed reasons. */
426 testkeygen(2056); // Stranged sized for edge cases in padding.
433 int si_40_seckey(int argc
, char *const *argv
)
435 plan_tests(kTestCount
);