1/* 2 * si-40-seckey.c 3 * Security 4 * 5 * Created by Michael Brouwer on 1/29/07. 6 * Copyright (c) 2007-2009 Apple Inc. All Rights Reserved. 7 * 8 */ 9 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> 16 17#if 0 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> 26#include <stdlib.h> 27#include <unistd.h> 28#endif 29 30#include "keychain_regressions.h" 31#include "utilities/SecCFRelease.h" 32 33#if TARGET_OS_IPHONE 34static 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); 38 uint8_t sig[sigLen]; 39 40 DERItem oid; 41 oid.length = algId->algorithm.Length; 42 oid.data = algId->algorithm.Data; 43 44 /* Get the oid in decimal for display purposes. */ 45 CFStringRef oidStr = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, &oid); 46 char oidBuf[40]; 47 CFStringGetCString(oidStr, oidBuf, sizeof(oidBuf), kCFStringEncodingUTF8); 48 CFRelease(oidStr); 49 50 SKIP: { 51 OSStatus status; 52 53 /* Time to sign. */ 54 ok_status(status = SecKeyDigestAndSign(privKey, algId, dataToDigest, dataToDigestLen, 55 sig, &sigLen), "digest and sign %s with %ld bit RSA key", oidBuf, sigLen * 8); 56 57 skip("SecKeyDigestAndSign failed", 3, status == errSecSuccess); 58 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. */ 63 sig[0] ^= 0xff; 64 is_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen, 65 sig, sigLen), errSSLCrypto, "digest and verify bad sig"); 66 sig[0] ^= 0xff; 67 dataToDigest[0] ^= 0xff; 68 is_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen, 69 sig, sigLen), errSSLCrypto, "digest and verify bad digest"); 70 } 71} 72 73static void testdigestandsign(SecKeyRef privKey, SecKeyRef pubKey) { 74 static const SecAsn1Oid *oids[] = { 75 &CSSMOID_SHA1WithRSA, 76 &CSSMOID_SHA224WithRSA, 77 &CSSMOID_SHA256WithRSA, 78 &CSSMOID_SHA384WithRSA, 79 &CSSMOID_SHA512WithRSA, 80#if 0 81 &CSSMOID_SHA1WithRSA_OIW, 82 &CSSMOID_SHA1WithDSA, // BSAFE 83 &CSSMOID_SHA1WithDSA_CMS, // X509/CMS 84 &CSSMOID_SHA1WithDSA_JDK, // JDK 1.1 85#endif 86 }; 87 88 89 uint32_t ix; 90 SecAsn1AlgId algId = {}; 91 for (ix = 0; ix < sizeof(oids) / sizeof(*oids); ++ix) { 92 if (oids[ix]) { 93 algId.algorithm = *oids[ix]; 94 } else { 95 algId.algorithm.Length = 0; 96 algId.algorithm.Data = NULL; 97 } 98 99 testdigestandsignalg(privKey, pubKey, &algId); 100 } 101} 102#endif 103 104#if 0 105static void dump_bytes(uint8_t* bytes, size_t amount) 106{ 107 while (amount > 0) { 108 printf("0x%02x ", *bytes); 109 ++bytes; 110 --amount; 111 } 112} 113#endif 114 115 116#if !TARGET_OS_IPHONE 117#define kEncryptDecryptTestCount 0 118#else 119#define kEncryptDecryptTestCount 5 120static void test_encrypt_decrypt(SecKeyRef pubKey, SecKeyRef privKey, uint32_t padding, size_t keySizeInBytes) 121{ 122 SKIP: { 123 size_t max_len = keySizeInBytes; 124 switch (padding) { 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); 129 } 130 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); 138 139 // zero pad, no accidental second zero byte 140 if (padding == kSecPaddingNone) { 141 secret[0] = 0; 142 secret[1] = 128; 143 } 144 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, 149 secret, secret_len, 150 encrypted_secret, &encrypted_secret_len), "encrypt secret"); 151 152 ok_status(SecKeyDecrypt(privKey, padding, 153 encrypted_secret, encrypted_secret_len, 154 decrypted_secret, &decrypted_secret_len), "decrypt secret"); 155 156 // zero padding is removed on decode 157 if (padding == kSecPaddingNone) { 158 secret_len--; 159 secret_ptr++; 160 } 161 162 ok(decrypted_secret_len == secret_len, "correct length"); 163 ok_status(memcmp(secret_ptr, decrypted_secret, secret_len), "verify secret"); 164 } 165} 166#endif 167 168 169 170#if !TARGET_OS_IPHONE 171/* This is part of Security.framework on iOS */ 172 173enum { 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. 179}; 180 181static 182size_t SecKeyGetSize(SecKeyRef key, int whichSize) 183{ 184 size_t result = SecKeyGetBlockSize(key); 185 186 /* This is only RSA */ 187 if (whichSize == kSecKeyKeySizeInBits) 188 result *= 8; 189 190 return result; 191} 192#endif 193 194#define kKeyGenTestCount (11 + (3*kEncryptDecryptTestCount)) 195static void testkeygen(size_t keySizeInBits) { 196 SecKeyRef pubKey = NULL, privKey = NULL; 197 size_t keySizeInBytes = (keySizeInBits + 7) / 8; 198 CFNumberRef kzib; 199 200 kzib = CFNumberCreate(NULL, kCFNumberSInt32Type, &keySizeInBits); 201 CFMutableDictionaryRef kgp = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); 202 CFDictionaryAddValue(kgp, kSecAttrKeyType, kSecAttrKeyTypeRSA); 203 CFDictionaryAddValue(kgp, kSecAttrKeySizeInBits, kzib); 204 205 OSStatus status; 206 ok_status(status = SecKeyGeneratePair(kgp, &pubKey, &privKey), 207 "Generate %ld bit (%ld byte) RSA keypair", keySizeInBits, 208 keySizeInBytes); 209 CFRelease(kzib); 210 CFRelease(kgp); 211 212 SKIP: { 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"); 218 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); 225#if TARGET_OS_IPHONE 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"); 230#endif 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"); 235 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, 246 }; 247#if TARGET_OS_IPHONE 248 /* Thoses tests are making sure that MD2 and MD5 are NOT supported, 249 but they still are on OS X */ 250 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 258 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 267#endif 268 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), 273 "sign sha1 digest"); 274 ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA1, 275 digest, CC_SHA1_DIGEST_LENGTH, sig, sigLen), 276 "verify sig on sha1 digest"); 277 278#if TARGET_OS_IPHONE 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"); 286#endif 287 288#if TARGET_OS_IPHONE 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"); 305 } 306 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"); 316 } 317 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"); 327 } 328 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"); 338 } 339 340 test_encrypt_decrypt(pubKey, privKey, kSecPaddingNone, keySizeInBytes); 341 test_encrypt_decrypt(pubKey, privKey, kSecPaddingPKCS1, keySizeInBytes); 342 test_encrypt_decrypt(pubKey, privKey, kSecPaddingOAEP, keySizeInBytes); 343 344 testdigestandsign(privKey, pubKey); 345#endif 346 347 const void *privkeys[] = { 348 kSecValueRef 349 }; 350 const void *privvalues[] = { 351 privKey 352 }; 353 CFDictionaryRef privitem = CFDictionaryCreate(NULL, privkeys, privvalues, 354 sizeof(privkeys) / sizeof(*privkeys), NULL, NULL); 355#if TARGET_OS_IPHONE 356 /* OS X: keys are always added to the keychain when generated */ 357 ok_status(SecItemAdd(privitem, NULL), "add private key"); //FAIL 358#endif 359 ok_status(SecItemDelete(privitem), "delete private key"); 360 CFReleaseNull(privitem); 361 362 const void *pubkeys[] = { 363 kSecValueRef 364 }; 365 const void *pubvalues[] = { 366 pubKey 367 }; 368 CFDictionaryRef pubitem = CFDictionaryCreate(NULL, pubkeys, pubvalues, 369 sizeof(pubkeys) / sizeof(*pubkeys), NULL, NULL); 370#if TARGET_OS_IPHONE 371 /* OS X: keys are always added to the keychain when generated */ 372 ok_status(SecItemAdd(pubitem, NULL), "add public key"); //FAIL 373#endif 374 ok_status(SecItemDelete(pubitem), "delete public key"); 375 CFReleaseNull(pubitem); 376 377 /* Cleanup. */ 378 CFReleaseNull(pubKey); 379 CFReleaseNull(privKey); 380 } 381} 382 383#define kKeyGen2TestCount 11 384static void testkeygen2(size_t keySizeInBits) { 385 SecKeyRef pubKey = NULL, privKey = NULL; 386 size_t keySizeInBytes = (keySizeInBits + 7) / 8; 387 CFNumberRef kzib; 388 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); 393 394 CFReleaseNull(ourUUID); 395 CFReleaseNull(uuidString); 396 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); 401 402 CFDictionaryAddValue(pubd, kSecAttrLabel, publicName); 403 CFDictionaryAddValue(privd, kSecAttrLabel, privateName); 404 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); 412 413 OSStatus status; 414 ok_status(status = SecKeyGeneratePair(kgp, &pubKey, &privKey), 415 "Generate %ld bit (%ld byte) persistent RSA keypair", 416 keySizeInBits, keySizeInBytes); 417 CFRelease(kzib); 418 CFRelease(kgp); 419 420 SKIP: { 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"); 426 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"); 437 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"); 446 447#if TARGET_OS_IPHONE 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"); 453#endif 454 455 /* Cleanup. */ 456 CFReleaseNull(pubKey2); 457 CFReleaseNull(privKey2); 458 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"); 463 } 464 465 /* Cleanup. */ 466 CFReleaseNull(pubKey); 467 CFReleaseNull(privKey); 468 469 CFReleaseNull(publicName); 470 CFReleaseNull(privateName); 471 472 CFRelease(pubd); 473 CFRelease(privd); 474} 475 476/* Test basic add delete update copy matching stuff. */ 477#define kTestCount ((2 * kKeyGenTestCount) + kKeyGen2TestCount) 478static void tests(void) 479{ 480 /* Comment out lines below for testing generating all common key sizes, 481 disabled now for speed reasons. */ 482 //testkeygen(512); 483 //testkeygen(768); 484 testkeygen(1024); 485 testkeygen(2056); // Stranged sized for edge cases in padding. 486 //testkeygen(2048); 487 //testkeygen(4096); 488 489 testkeygen2(1024); // lots of FAIL! 490} 491 492int kc_40_seckey(int argc, char *const *argv) 493{ 494 plan_tests(kTestCount); 495 496 tests(); 497 498 return 0; 499} 500