#include <TargetConditionals.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include <Security/SecRandom.h>
#include <CommonCrypto/CommonDigest.h>
#include <Security/SecKeyPriv.h>
#if 0
#include <Security/SecCertificate.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecKey.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecItem.h>
#include <Security/SecAsn1Types.h>
#include <Security/oidsalg.h>
#include <libDER/libDER.h>
#include <stdlib.h>
#include <unistd.h>
#endif
#include "keychain_regressions.h"
#include "utilities/SecCFRelease.h"
#if TARGET_OS_IPHONE
static void testdigestandsignalg(SecKeyRef privKey, SecKeyRef pubKey, const SecAsn1AlgId *algId) {
uint8_t dataToDigest[256];
size_t dataToDigestLen = sizeof(dataToDigest);
size_t sigLen = SecKeyGetSize(privKey, kSecKeySignatureSize);
uint8_t sig[sigLen];
DERItem oid;
oid.length = algId->algorithm.Length;
oid.data = algId->algorithm.Data;
CFStringRef oidStr = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, &oid);
char oidBuf[40];
CFStringGetCString(oidStr, oidBuf, sizeof(oidBuf), kCFStringEncodingUTF8);
CFRelease(oidStr);
SKIP: {
OSStatus status;
ok_status(status = SecKeyDigestAndSign(privKey, algId, dataToDigest, dataToDigestLen,
sig, &sigLen), "digest and sign %s with %ld bit RSA key", oidBuf, sigLen * 8);
skip("SecKeyDigestAndSign failed", 3, status == errSecSuccess);
ok_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen,
sig, sigLen), "digest and verify");
sig[0] ^= 0xff;
is_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen,
sig, sigLen), errSSLCrypto, "digest and verify bad sig");
sig[0] ^= 0xff;
dataToDigest[0] ^= 0xff;
is_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen,
sig, sigLen), errSSLCrypto, "digest and verify bad digest");
}
}
static void testdigestandsign(SecKeyRef privKey, SecKeyRef pubKey) {
static const SecAsn1Oid *oids[] = {
&CSSMOID_SHA1WithRSA,
&CSSMOID_SHA224WithRSA,
&CSSMOID_SHA256WithRSA,
&CSSMOID_SHA384WithRSA,
&CSSMOID_SHA512WithRSA,
#if 0
&CSSMOID_SHA1WithRSA_OIW,
&CSSMOID_SHA1WithDSA, &CSSMOID_SHA1WithDSA_CMS, &CSSMOID_SHA1WithDSA_JDK, #endif
};
uint32_t ix;
SecAsn1AlgId algId = {};
for (ix = 0; ix < sizeof(oids) / sizeof(*oids); ++ix) {
if (oids[ix]) {
algId.algorithm = *oids[ix];
} else {
algId.algorithm.Length = 0;
algId.algorithm.Data = NULL;
}
testdigestandsignalg(privKey, pubKey, &algId);
}
}
#endif
#if 0
static void dump_bytes(uint8_t* bytes, size_t amount)
{
while (amount > 0) {
printf("0x%02x ", *bytes);
++bytes;
--amount;
}
}
#endif
#if !TARGET_OS_IPHONE
#define kEncryptDecryptTestCount 0
#else
#define kEncryptDecryptTestCount 5
static void test_encrypt_decrypt(SecKeyRef pubKey, SecKeyRef privKey, uint32_t padding, size_t keySizeInBytes)
{
SKIP: {
size_t max_len = keySizeInBytes;
switch (padding) {
case kSecPaddingNone: max_len = keySizeInBytes; break;
case kSecPaddingOAEP: max_len = keySizeInBytes - 2 - 2 * CC_SHA1_DIGEST_LENGTH; break;
case kSecPaddingPKCS1: max_len = keySizeInBytes - 11; break;
default: skip("what is the max_len for this padding?", 5, false);
}
uint8_t secret[max_len + 1], encrypted_secret[keySizeInBytes], decrypted_secret[keySizeInBytes];
uint8_t *secret_ptr = secret;
size_t secret_len = max_len;
size_t encrypted_secret_len = sizeof(encrypted_secret);
size_t decrypted_secret_len = sizeof(decrypted_secret);
memset(decrypted_secret, 0xff, decrypted_secret_len);
SecRandomCopyBytes(kSecRandomDefault, sizeof(secret), secret);
if (padding == kSecPaddingNone) {
secret[0] = 0;
secret[1] = 128;
}
is_status(SecKeyEncrypt(pubKey, padding,
secret, sizeof(secret),
encrypted_secret, &encrypted_secret_len), errSecParam, "encrypt secret (overflow)");
ok_status(SecKeyEncrypt(pubKey, padding,
secret, secret_len,
encrypted_secret, &encrypted_secret_len), "encrypt secret");
ok_status(SecKeyDecrypt(privKey, padding,
encrypted_secret, encrypted_secret_len,
decrypted_secret, &decrypted_secret_len), "decrypt secret");
if (padding == kSecPaddingNone) {
secret_len--;
secret_ptr++;
}
ok(decrypted_secret_len == secret_len, "correct length");
ok_status(memcmp(secret_ptr, decrypted_secret, secret_len), "verify secret");
}
}
#endif
#if !TARGET_OS_IPHONE
enum {
kSecKeySignatureSize = 101,
kSecKeyEncryptedDataSize = 102,
};
static
size_t SecKeyGetSize(SecKeyRef key, int whichSize)
{
size_t result = SecKeyGetBlockSize(key);
if (whichSize == kSecKeyKeySizeInBits)
result *= 8;
return result;
}
#endif
#define kKeyGenTestCount (11 + (3*kEncryptDecryptTestCount))
static void testkeygen(size_t keySizeInBits) {
SecKeyRef pubKey = NULL, privKey = NULL;
size_t keySizeInBytes = (keySizeInBits + 7) / 8;
CFNumberRef kzib;
kzib = CFNumberCreate(NULL, kCFNumberSInt32Type, &keySizeInBits);
CFMutableDictionaryRef kgp = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
CFDictionaryAddValue(kgp, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionaryAddValue(kgp, kSecAttrKeySizeInBits, kzib);
OSStatus status;
ok_status(status = SecKeyGeneratePair(kgp, &pubKey, &privKey),
"Generate %ld bit (%ld byte) RSA keypair", keySizeInBits,
keySizeInBytes);
CFRelease(kzib);
CFRelease(kgp);
SKIP: {
skip("keygen failed", 8, status == errSecSuccess);
ok(pubKey, "pubkey returned");
ok(privKey, "privKey returned");
is(SecKeyGetSize(pubKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "public key size is ok");
is(SecKeyGetSize(privKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "private key size is ok");
uint8_t something[keySizeInBytes];
size_t something_len = keySizeInBytes - 11;
SecRandomCopyBytes(kSecRandomDefault, sizeof(something), something);
uint8_t sig[keySizeInBytes];
size_t sigLen = sizeof(sig);
#if TARGET_OS_IPHONE
is_status(SecKeyRawSign(privKey, kSecPaddingPKCS1,
something, something_len + 1, sig, &sigLen),
errSecParam, "sign overflow");
#endif
ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1,
something, something_len, sig, &sigLen), "sign something");
ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1,
something, something_len, sig, sigLen), "verify sig on something");
uint8_t digest[CC_SHA512_DIGEST_LENGTH] = {
0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
};
#if TARGET_OS_IPHONE
ok_status(!SecKeyRawSign(privKey, kSecPaddingPKCS1MD2,
digest, CC_MD2_DIGEST_LENGTH, sig, &sigLen),
"don't sign md2 digest"); ok_status(!SecKeyRawVerify(pubKey, kSecPaddingPKCS1MD2,
digest, CC_MD2_DIGEST_LENGTH, sig, sigLen),
"verify sig on md2 digest fails");
sigLen = sizeof(sig);
ok_status(!SecKeyRawSign(privKey, kSecPaddingPKCS1MD5,
digest, CC_MD5_DIGEST_LENGTH, sig, &sigLen),
"don't sign md5 digest"); ok_status(!SecKeyRawVerify(pubKey, kSecPaddingPKCS1MD5,
digest, CC_MD5_DIGEST_LENGTH, sig, sigLen),
"verify sig on md5 digest fails"); #endif
sigLen = sizeof(sig);
ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA1,
digest, CC_SHA1_DIGEST_LENGTH, sig, &sigLen),
"sign sha1 digest");
ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA1,
digest, CC_SHA1_DIGEST_LENGTH, sig, sigLen),
"verify sig on sha1 digest");
#if TARGET_OS_IPHONE
uint8_t signature[keySizeInBytes], *ptr = signature;
size_t signature_len = sizeof(signature);
ok_status(SecKeyEncrypt(pubKey, kSecPaddingNone, sig, sigLen, signature, &signature_len), "inspect signature");
is(signature_len, keySizeInBytes - 1, "got signature"); while(*ptr && ((size_t)(ptr - signature) < signature_len)) ptr++;
is(signature + signature_len - ptr, 16 + CC_SHA1_DIGEST_LENGTH, "successful decode");
#endif
#if TARGET_OS_IPHONE
const size_t pkcs1Overhead = 232;
if (keySizeInBits > 224 + pkcs1Overhead) {
sigLen = sizeof(sig);
ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA224,
digest, CC_SHA224_DIGEST_LENGTH, sig, &sigLen),
"sign sha224 digest");
ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA224,
digest, CC_SHA224_DIGEST_LENGTH, sig, sigLen),
"verify sig on sha224 digest");
}
if (keySizeInBits > 256 + pkcs1Overhead) {
sigLen = sizeof(sig);
ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA256,
digest, CC_SHA256_DIGEST_LENGTH, sig, &sigLen),
"sign sha256 digest");
ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA256,
digest, CC_SHA256_DIGEST_LENGTH, sig, sigLen),
"verify sig on sha256 digest");
}
if (keySizeInBits > 384 + pkcs1Overhead) {
sigLen = sizeof(sig);
ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA384,
digest, CC_SHA384_DIGEST_LENGTH, sig, &sigLen),
"sign sha384 digest");
ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA384,
digest, CC_SHA384_DIGEST_LENGTH, sig, sigLen),
"verify sig on sha384 digest");
}
if (keySizeInBits > 512 + pkcs1Overhead) {
sigLen = sizeof(sig);
ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA512,
digest, CC_SHA512_DIGEST_LENGTH, sig, &sigLen),
"sign sha512 digest");
ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA512,
digest, CC_SHA512_DIGEST_LENGTH, sig, sigLen),
"verify sig on sha512 digest");
}
test_encrypt_decrypt(pubKey, privKey, kSecPaddingNone, keySizeInBytes);
test_encrypt_decrypt(pubKey, privKey, kSecPaddingPKCS1, keySizeInBytes);
test_encrypt_decrypt(pubKey, privKey, kSecPaddingOAEP, keySizeInBytes);
testdigestandsign(privKey, pubKey);
#endif
const void *privkeys[] = {
kSecValueRef
};
const void *privvalues[] = {
privKey
};
CFDictionaryRef privitem = CFDictionaryCreate(NULL, privkeys, privvalues,
sizeof(privkeys) / sizeof(*privkeys), NULL, NULL);
#if TARGET_OS_IPHONE
ok_status(SecItemAdd(privitem, NULL), "add private key"); #endif
ok_status(SecItemDelete(privitem), "delete private key");
CFReleaseNull(privitem);
const void *pubkeys[] = {
kSecValueRef
};
const void *pubvalues[] = {
pubKey
};
CFDictionaryRef pubitem = CFDictionaryCreate(NULL, pubkeys, pubvalues,
sizeof(pubkeys) / sizeof(*pubkeys), NULL, NULL);
#if TARGET_OS_IPHONE
ok_status(SecItemAdd(pubitem, NULL), "add public key"); #endif
ok_status(SecItemDelete(pubitem), "delete public key");
CFReleaseNull(pubitem);
CFReleaseNull(pubKey);
CFReleaseNull(privKey);
}
}
#define kKeyGen2TestCount 11
static void testkeygen2(size_t keySizeInBits) {
SecKeyRef pubKey = NULL, privKey = NULL;
size_t keySizeInBytes = (keySizeInBits + 7) / 8;
CFNumberRef kzib;
CFUUIDRef ourUUID = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, ourUUID);
CFMutableStringRef publicName = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, uuidString);
CFMutableStringRef privateName = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, uuidString);
CFReleaseNull(ourUUID);
CFReleaseNull(uuidString);
CFStringAppend(publicName, CFSTR("-Public-40"));
CFStringAppend(privateName, CFSTR("-Private-40"));
CFMutableDictionaryRef pubd = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
CFMutableDictionaryRef privd = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
CFDictionaryAddValue(pubd, kSecAttrLabel, publicName);
CFDictionaryAddValue(privd, kSecAttrLabel, privateName);
kzib = CFNumberCreate(NULL, kCFNumberSInt32Type, &keySizeInBits);
CFMutableDictionaryRef kgp = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
CFDictionaryAddValue(kgp, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionaryAddValue(kgp, kSecAttrKeySizeInBits, kzib);
CFDictionaryAddValue(kgp, kSecAttrIsPermanent, kCFBooleanTrue);
CFDictionaryAddValue(kgp, kSecPublicKeyAttrs, pubd);
CFDictionaryAddValue(kgp, kSecPrivateKeyAttrs, privd);
OSStatus status;
ok_status(status = SecKeyGeneratePair(kgp, &pubKey, &privKey),
"Generate %ld bit (%ld byte) persistent RSA keypair",
keySizeInBits, keySizeInBytes);
CFRelease(kzib);
CFRelease(kgp);
SKIP: {
skip("keygen failed", 8, status == errSecSuccess);
ok(pubKey, "pubkey returned");
ok(privKey, "privKey returned");
is(SecKeyGetSize(pubKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "public key size is ok");
is(SecKeyGetSize(privKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "private key size is ok");
SecKeyRef pubKey2, privKey2;
CFDictionaryAddValue(pubd, kSecClass, kSecClassKey);
CFDictionaryAddValue(pubd, kSecReturnRef, kCFBooleanTrue);
CFDictionaryAddValue(privd, kSecClass, kSecClassKey);
CFDictionaryAddValue(privd, kSecReturnRef, kCFBooleanTrue);
CFDictionaryAddValue(privd, kSecAttrCanSign, kCFBooleanTrue);
ok_status(SecItemCopyMatching(pubd, (CFTypeRef *)&pubKey2),
"retrieve pub key by label");
ok_status(SecItemCopyMatching(privd, (CFTypeRef *)&privKey2),
"retrieve priv key by label and kSecAttrCanSign");
uint8_t something[50] = {0x80, 0xbe, 0xef, 0xba, 0xd0, };
uint8_t sig[keySizeInBytes];
size_t sigLen = keySizeInBytes;
ok_status(SecKeyRawSign(privKey2, kSecPaddingPKCS1,
something, sizeof(something), sig, &sigLen), "sign something");
ok_status(SecKeyRawVerify(pubKey2, kSecPaddingPKCS1,
something, sizeof(something), sig, sigLen), "verify sig on something");
#if TARGET_OS_IPHONE
sigLen = keySizeInBytes;
is_status(SecKeyEncrypt(pubKey2, kSecPaddingPKCS1SHA1,
something, sizeof(something), sig, &sigLen), errSecParam,
"encrypt something with invalid padding");
#endif
CFReleaseNull(pubKey2);
CFReleaseNull(privKey2);
ok_status(SecItemDelete(pubd), "delete generated pub key");
ok_status(SecItemDelete(privd), "delete generated priv key");
}
CFReleaseNull(pubKey);
CFReleaseNull(privKey);
CFReleaseNull(publicName);
CFReleaseNull(privateName);
CFRelease(pubd);
CFRelease(privd);
}
#define kTestCount ((2 * kKeyGenTestCount) + kKeyGen2TestCount)
static void tests(void)
{
testkeygen(1024);
testkeygen(2056);
testkeygen2(1024); }
int kc_40_seckey(int argc, char *const *argv)
{
plan_tests(kTestCount);
tests();
return 0;
}