5 * Created by Michael Brouwer on 1/29/07.
6 * Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
10 #include <TargetConditionals.h>
11 #include <CoreFoundation/CoreFoundation.h>
12 #include <Security/Security.h>
13 #include <Security/SecRandom.h>
14 #include <CommonCrypto/CommonDigest.h>
15 #include <Security/SecKeyPriv.h>
18 #include <Security/SecCertificate.h>
19 #include <Security/SecCertificateInternal.h>
20 #include <Security/SecKey.h>
21 #include <Security/SecKeyPriv.h>
22 #include <Security/SecItem.h>
23 #include <Security/SecAsn1Types.h>
24 #include <Security/oidsalg.h>
25 #include <libDER/libDER.h>
30 #include "keychain_regressions.h"
31 #include "utilities/SecCFRelease.h"
34 static void testdigestandsignalg(SecKeyRef privKey
, SecKeyRef pubKey
, const SecAsn1AlgId
*algId
) {
35 uint8_t dataToDigest
[256];
36 size_t dataToDigestLen
= sizeof(dataToDigest
);
37 size_t sigLen
= SecKeyGetSize(privKey
, kSecKeySignatureSize
);
41 oid
.length
= algId
->algorithm
.Length
;
42 oid
.data
= algId
->algorithm
.Data
;
44 /* Get the oid in decimal for display purposes. */
45 CFStringRef oidStr
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, &oid
);
47 CFStringGetCString(oidStr
, oidBuf
, sizeof(oidBuf
), kCFStringEncodingUTF8
);
54 ok_status(status
= SecKeyDigestAndSign(privKey
, algId
, dataToDigest
, dataToDigestLen
,
55 sig
, &sigLen
), "digest and sign %s with %ld bit RSA key", oidBuf
, sigLen
* 8);
57 skip("SecKeyDigestAndSign failed", 3, status
== errSecSuccess
);
59 /* Verify the signature we just made. */
60 ok_status(SecKeyDigestAndVerify(pubKey
, algId
, dataToDigest
, dataToDigestLen
,
61 sig
, sigLen
), "digest and verify");
62 /* Invalidate the signature. */
64 is_status(SecKeyDigestAndVerify(pubKey
, algId
, dataToDigest
, dataToDigestLen
,
65 sig
, sigLen
), errSSLCrypto
, "digest and verify bad sig");
67 dataToDigest
[0] ^= 0xff;
68 is_status(SecKeyDigestAndVerify(pubKey
, algId
, dataToDigest
, dataToDigestLen
,
69 sig
, sigLen
), errSSLCrypto
, "digest and verify bad digest");
73 static void testdigestandsign(SecKeyRef privKey
, SecKeyRef pubKey
) {
74 static const SecAsn1Oid
*oids
[] = {
76 &CSSMOID_SHA224WithRSA
,
77 &CSSMOID_SHA256WithRSA
,
78 &CSSMOID_SHA384WithRSA
,
79 &CSSMOID_SHA512WithRSA
,
81 &CSSMOID_SHA1WithRSA_OIW
,
82 &CSSMOID_SHA1WithDSA
, // BSAFE
83 &CSSMOID_SHA1WithDSA_CMS
, // X509/CMS
84 &CSSMOID_SHA1WithDSA_JDK
, // JDK 1.1
90 SecAsn1AlgId algId
= {};
91 for (ix
= 0; ix
< sizeof(oids
) / sizeof(*oids
); ++ix
) {
93 algId
.algorithm
= *oids
[ix
];
95 algId
.algorithm
.Length
= 0;
96 algId
.algorithm
.Data
= NULL
;
99 testdigestandsignalg(privKey
, pubKey
, &algId
);
105 static void dump_bytes(uint8_t* bytes
, size_t amount
)
108 printf("0x%02x ", *bytes
);
116 #if !TARGET_OS_IPHONE
117 #define kEncryptDecryptTestCount 0
119 #define kEncryptDecryptTestCount 5
120 static void test_encrypt_decrypt(SecKeyRef pubKey
, SecKeyRef privKey
, uint32_t padding
, size_t keySizeInBytes
)
123 size_t max_len
= keySizeInBytes
;
125 case kSecPaddingNone
: max_len
= keySizeInBytes
; break;
126 case kSecPaddingOAEP
: max_len
= keySizeInBytes
- 2 - 2 * CC_SHA1_DIGEST_LENGTH
; break;
127 case kSecPaddingPKCS1
: max_len
= keySizeInBytes
- 11; break;
128 default: skip("what is the max_len for this padding?", 5, false);
131 uint8_t secret
[max_len
+ 1], encrypted_secret
[keySizeInBytes
], decrypted_secret
[keySizeInBytes
];
132 uint8_t *secret_ptr
= secret
;
133 size_t secret_len
= max_len
;
134 size_t encrypted_secret_len
= sizeof(encrypted_secret
);
135 size_t decrypted_secret_len
= sizeof(decrypted_secret
);
136 memset(decrypted_secret
, 0xff, decrypted_secret_len
);
137 SecRandomCopyBytes(kSecRandomDefault
, sizeof(secret
), secret
);
139 // zero pad, no accidental second zero byte
140 if (padding
== kSecPaddingNone
) {
145 is_status(SecKeyEncrypt(pubKey
, padding
,
146 secret
, sizeof(secret
),
147 encrypted_secret
, &encrypted_secret_len
), errSecParam
, "encrypt secret (overflow)");
148 ok_status(SecKeyEncrypt(pubKey
, padding
,
150 encrypted_secret
, &encrypted_secret_len
), "encrypt secret");
152 ok_status(SecKeyDecrypt(privKey
, padding
,
153 encrypted_secret
, encrypted_secret_len
,
154 decrypted_secret
, &decrypted_secret_len
), "decrypt secret");
156 // zero padding is removed on decode
157 if (padding
== kSecPaddingNone
) {
162 ok(decrypted_secret_len
== secret_len
, "correct length");
163 ok_status(memcmp(secret_ptr
, decrypted_secret
, secret_len
), "verify secret");
170 #if !TARGET_OS_IPHONE
171 /* This is part of Security.framework on iOS */
174 // kSecKeyKeySizeInBits = 0, // already exists on osx
175 kSecKeySignatureSize
= 101,
176 kSecKeyEncryptedDataSize
= 102,
177 // More might belong here, but we aren't settled on how
178 // to take into account padding and/or digest types.
182 size_t SecKeyGetSize(SecKeyRef key
, int whichSize
)
184 size_t result
= SecKeyGetBlockSize(key
);
186 /* This is only RSA */
187 if (whichSize
== kSecKeyKeySizeInBits
)
194 #define kKeyGenTestCount (11 + (3*kEncryptDecryptTestCount))
195 static void testkeygen(size_t keySizeInBits
) {
196 SecKeyRef pubKey
= NULL
, privKey
= NULL
;
197 size_t keySizeInBytes
= (keySizeInBits
+ 7) / 8;
200 kzib
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &keySizeInBits
);
201 CFMutableDictionaryRef kgp
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
202 CFDictionaryAddValue(kgp
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
203 CFDictionaryAddValue(kgp
, kSecAttrKeySizeInBits
, kzib
);
206 ok_status(status
= SecKeyGeneratePair(kgp
, &pubKey
, &privKey
),
207 "Generate %ld bit (%ld byte) RSA keypair", keySizeInBits
,
213 skip("keygen failed", 8, status
== errSecSuccess
);
214 ok(pubKey
, "pubkey returned");
215 ok(privKey
, "privKey returned");
216 is(SecKeyGetSize(pubKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "public key size is ok");
217 is(SecKeyGetSize(privKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "private key size is ok");
219 /* Sign something. */
220 uint8_t something
[keySizeInBytes
];
221 size_t something_len
= keySizeInBytes
- 11;
222 SecRandomCopyBytes(kSecRandomDefault
, sizeof(something
), something
);
223 uint8_t sig
[keySizeInBytes
];
224 size_t sigLen
= sizeof(sig
);
226 /* TODO: This is returning another error on OS X */
227 is_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1
,
228 something
, something_len
+ 1, sig
, &sigLen
),
229 errSecParam
, "sign overflow");
231 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1
,
232 something
, something_len
, sig
, &sigLen
), "sign something");
233 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1
,
234 something
, something_len
, sig
, sigLen
), "verify sig on something");
236 // Torture test ASN.1 encoder by setting high bit to 1.
237 uint8_t digest
[CC_SHA512_DIGEST_LENGTH
] = {
238 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
239 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
240 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
241 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
242 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
243 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
244 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
245 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
248 /* Thoses tests are making sure that MD2 and MD5 are NOT supported,
249 but they still are on OS X */
251 //CC_MD2(something, sizeof(something), digest);
252 ok_status(!SecKeyRawSign(privKey
, kSecPaddingPKCS1MD2
,
253 digest
, CC_MD2_DIGEST_LENGTH
, sig
, &sigLen
),
254 "don't sign md2 digest"); //FAIL
255 ok_status(!SecKeyRawVerify(pubKey
, kSecPaddingPKCS1MD2
,
256 digest
, CC_MD2_DIGEST_LENGTH
, sig
, sigLen
),
257 "verify sig on md2 digest fails"); //FAIL
259 //CC_MD5(something, sizeof(something), digest);
260 sigLen
= sizeof(sig
);
261 ok_status(!SecKeyRawSign(privKey
, kSecPaddingPKCS1MD5
,
262 digest
, CC_MD5_DIGEST_LENGTH
, sig
, &sigLen
),
263 "don't sign md5 digest"); //FAIL
264 ok_status(!SecKeyRawVerify(pubKey
, kSecPaddingPKCS1MD5
,
265 digest
, CC_MD5_DIGEST_LENGTH
, sig
, sigLen
),
266 "verify sig on md5 digest fails"); //FAIL
269 //CCDigest(kCCDigestSHA1, something, sizeof(something), digest);
270 sigLen
= sizeof(sig
);
271 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA1
,
272 digest
, CC_SHA1_DIGEST_LENGTH
, sig
, &sigLen
),
274 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA1
,
275 digest
, CC_SHA1_DIGEST_LENGTH
, sig
, sigLen
),
276 "verify sig on sha1 digest");
279 /* The assumptions in these tests are just wrong on OS X */
280 uint8_t signature
[keySizeInBytes
], *ptr
= signature
;
281 size_t signature_len
= sizeof(signature
);
282 ok_status(SecKeyEncrypt(pubKey
, kSecPaddingNone
, sig
, sigLen
, signature
, &signature_len
), "inspect signature");
283 is(signature_len
, keySizeInBytes
- 1, "got signature"); // FAIL for 2056
284 while(*ptr
&& ((size_t)(ptr
- signature
) < signature_len
)) ptr
++;
285 is(signature
+ signature_len
- ptr
, 16 /* length(\0 || OID_SHA1) */ + CC_SHA1_DIGEST_LENGTH
, "successful decode");
289 /* Those are not supported on OS X */
290 /* PKCS1 padding is 00 01 PAD * 8 or more 00 data.
291 data is SEQ { SEQ { OID NULL } BIT STRING 00 DIGEST }
292 So min data + pad overhead is 11 + 9 + oidlen
293 oidlen = 11 for the sha2 family of oids, so we have 29 bytes; or
294 232 bits of minimum overhead. */
295 const size_t pkcs1Overhead
= 232;
296 if (keySizeInBits
> 224 + pkcs1Overhead
) {
297 //CC_SHA224(something, sizeof(something), digest);
298 sigLen
= sizeof(sig
);
299 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA224
,
300 digest
, CC_SHA224_DIGEST_LENGTH
, sig
, &sigLen
),
301 "sign sha224 digest");
302 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA224
,
303 digest
, CC_SHA224_DIGEST_LENGTH
, sig
, sigLen
),
304 "verify sig on sha224 digest");
307 if (keySizeInBits
> 256 + pkcs1Overhead
) {
308 //CC_SHA256(something, sizeof(something), digest);
309 sigLen
= sizeof(sig
);
310 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA256
,
311 digest
, CC_SHA256_DIGEST_LENGTH
, sig
, &sigLen
),
312 "sign sha256 digest");
313 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA256
,
314 digest
, CC_SHA256_DIGEST_LENGTH
, sig
, sigLen
),
315 "verify sig on sha256 digest");
318 if (keySizeInBits
> 384 + pkcs1Overhead
) {
319 //CC_SHA384(something, sizeof(something), digest);
320 sigLen
= sizeof(sig
);
321 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA384
,
322 digest
, CC_SHA384_DIGEST_LENGTH
, sig
, &sigLen
),
323 "sign sha384 digest");
324 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA384
,
325 digest
, CC_SHA384_DIGEST_LENGTH
, sig
, sigLen
),
326 "verify sig on sha384 digest");
329 if (keySizeInBits
> 512 + pkcs1Overhead
) {
330 //CC_SHA512(something, sizeof(something), digest);
331 sigLen
= sizeof(sig
);
332 ok_status(SecKeyRawSign(privKey
, kSecPaddingPKCS1SHA512
,
333 digest
, CC_SHA512_DIGEST_LENGTH
, sig
, &sigLen
),
334 "sign sha512 digest");
335 ok_status(SecKeyRawVerify(pubKey
, kSecPaddingPKCS1SHA512
,
336 digest
, CC_SHA512_DIGEST_LENGTH
, sig
, sigLen
),
337 "verify sig on sha512 digest");
340 test_encrypt_decrypt(pubKey
, privKey
, kSecPaddingNone
, keySizeInBytes
);
341 test_encrypt_decrypt(pubKey
, privKey
, kSecPaddingPKCS1
, keySizeInBytes
);
342 test_encrypt_decrypt(pubKey
, privKey
, kSecPaddingOAEP
, keySizeInBytes
);
344 testdigestandsign(privKey
, pubKey
);
347 const void *privkeys
[] = {
350 const void *privvalues
[] = {
353 CFDictionaryRef privitem
= CFDictionaryCreate(NULL
, privkeys
, privvalues
,
354 sizeof(privkeys
) / sizeof(*privkeys
), NULL
, NULL
);
356 /* OS X: keys are always added to the keychain when generated */
357 ok_status(SecItemAdd(privitem
, NULL
), "add private key"); //FAIL
359 ok_status(SecItemDelete(privitem
), "delete private key");
360 CFReleaseNull(privitem
);
362 const void *pubkeys
[] = {
365 const void *pubvalues
[] = {
368 CFDictionaryRef pubitem
= CFDictionaryCreate(NULL
, pubkeys
, pubvalues
,
369 sizeof(pubkeys
) / sizeof(*pubkeys
), NULL
, NULL
);
371 /* OS X: keys are always added to the keychain when generated */
372 ok_status(SecItemAdd(pubitem
, NULL
), "add public key"); //FAIL
374 ok_status(SecItemDelete(pubitem
), "delete public key");
375 CFReleaseNull(pubitem
);
378 CFReleaseNull(pubKey
);
379 CFReleaseNull(privKey
);
383 #define kKeyGen2TestCount 11
384 static void testkeygen2(size_t keySizeInBits
) {
385 SecKeyRef pubKey
= NULL
, privKey
= NULL
;
386 size_t keySizeInBytes
= (keySizeInBits
+ 7) / 8;
389 CFUUIDRef ourUUID
= CFUUIDCreate(kCFAllocatorDefault
);
390 CFStringRef uuidString
= CFUUIDCreateString(kCFAllocatorDefault
, ourUUID
);
391 CFMutableStringRef publicName
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, uuidString
);
392 CFMutableStringRef privateName
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, uuidString
);
394 CFReleaseNull(ourUUID
);
395 CFReleaseNull(uuidString
);
397 CFStringAppend(publicName
, CFSTR("-Public-40"));
398 CFStringAppend(privateName
, CFSTR("-Private-40"));
399 CFMutableDictionaryRef pubd
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
400 CFMutableDictionaryRef privd
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
402 CFDictionaryAddValue(pubd
, kSecAttrLabel
, publicName
);
403 CFDictionaryAddValue(privd
, kSecAttrLabel
, privateName
);
405 kzib
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &keySizeInBits
);
406 CFMutableDictionaryRef kgp
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
407 CFDictionaryAddValue(kgp
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
408 CFDictionaryAddValue(kgp
, kSecAttrKeySizeInBits
, kzib
);
409 CFDictionaryAddValue(kgp
, kSecAttrIsPermanent
, kCFBooleanTrue
);
410 CFDictionaryAddValue(kgp
, kSecPublicKeyAttrs
, pubd
);
411 CFDictionaryAddValue(kgp
, kSecPrivateKeyAttrs
, privd
);
414 ok_status(status
= SecKeyGeneratePair(kgp
, &pubKey
, &privKey
),
415 "Generate %ld bit (%ld byte) persistent RSA keypair",
416 keySizeInBits
, keySizeInBytes
);
421 skip("keygen failed", 8, status
== errSecSuccess
);
422 ok(pubKey
, "pubkey returned");
423 ok(privKey
, "privKey returned");
424 is(SecKeyGetSize(pubKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "public key size is ok");
425 is(SecKeyGetSize(privKey
, kSecKeyKeySizeInBits
), (size_t) keySizeInBits
, "private key size is ok");
427 SecKeyRef pubKey2
, privKey2
;
428 CFDictionaryAddValue(pubd
, kSecClass
, kSecClassKey
);
429 CFDictionaryAddValue(pubd
, kSecReturnRef
, kCFBooleanTrue
);
430 CFDictionaryAddValue(privd
, kSecClass
, kSecClassKey
);
431 CFDictionaryAddValue(privd
, kSecReturnRef
, kCFBooleanTrue
);
432 CFDictionaryAddValue(privd
, kSecAttrCanSign
, kCFBooleanTrue
);
433 ok_status(SecItemCopyMatching(pubd
, (CFTypeRef
*)&pubKey2
),
434 "retrieve pub key by label");
435 ok_status(SecItemCopyMatching(privd
, (CFTypeRef
*)&privKey2
),
436 "retrieve priv key by label and kSecAttrCanSign");
438 /* Sign something. */
439 uint8_t something
[50] = {0x80, 0xbe, 0xef, 0xba, 0xd0, };
440 uint8_t sig
[keySizeInBytes
];
441 size_t sigLen
= keySizeInBytes
;
442 ok_status(SecKeyRawSign(privKey2
, kSecPaddingPKCS1
,
443 something
, sizeof(something
), sig
, &sigLen
), "sign something");
444 ok_status(SecKeyRawVerify(pubKey2
, kSecPaddingPKCS1
,
445 something
, sizeof(something
), sig
, sigLen
), "verify sig on something");
448 /* SecKeyEncrypt does not return errSecParam on OS X in that case */
449 sigLen
= keySizeInBytes
;
450 is_status(SecKeyEncrypt(pubKey2
, kSecPaddingPKCS1SHA1
,
451 something
, sizeof(something
), sig
, &sigLen
), errSecParam
,
452 "encrypt something with invalid padding");
456 CFReleaseNull(pubKey2
);
457 CFReleaseNull(privKey2
);
459 /* delete from keychain - note: do it before releasing publicName and privateName
460 because pubd and privd have no retain/release callbacks */
461 ok_status(SecItemDelete(pubd
), "delete generated pub key");
462 ok_status(SecItemDelete(privd
), "delete generated priv key");
466 CFReleaseNull(pubKey
);
467 CFReleaseNull(privKey
);
469 CFReleaseNull(publicName
);
470 CFReleaseNull(privateName
);
476 /* Test basic add delete update copy matching stuff. */
477 #define kTestCount ((2 * kKeyGenTestCount) + kKeyGen2TestCount)
478 static void tests(void)
480 /* Comment out lines below for testing generating all common key sizes,
481 disabled now for speed reasons. */
485 testkeygen(2056); // Stranged sized for edge cases in padding.
489 testkeygen2(1024); // lots of FAIL!
492 int kc_40_seckey(int argc
, char *const *argv
)
494 plan_tests(kTestCount
);