#include "SecECKey.h"
#include <Security/SecKeyInternal.h>
#include <Security/SecItem.h>
#include <Security/SecBasePriv.h>
#include <AssertMacros.h>
#include <Security/SecureTransport.h>
#include <CoreFoundation/CFData.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <CoreFoundation/CFNumber.h>
#include <Security/SecFramework.h>
#include <Security/SecRandom.h>
#include <utilities/debugging.h>
#include "SecItemPriv.h"
#include <Security/SecInternal.h>
#include <corecrypto/ccec.h>
#include <corecrypto/ccsha1.h>
#include <corecrypto/ccsha2.h>
#include <corecrypto/ccrng.h>
#include <corecrypto/ccder_decode_eckey.h>
#define kMaximumECKeySize 521
static CFIndex SecECKeyGetAlgorithmID(SecKeyRef key) {
return kSecECDSAAlgorithmID;
}
static void SecECPublicKeyDestroy(SecKeyRef key) {
ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
if (ccec_ctx_cp(pubkey).zp)
cc_zero(ccec_pub_ctx_size(ccn_sizeof_n(ccec_ctx_n(pubkey))), pubkey.pub);
}
static ccec_const_cp_t getCPForPublicSize(CFIndex encoded_length)
{
size_t keysize = ccec_x963_import_pub_size(encoded_length);
if(ccec_keysize_is_supported(keysize)) {
return ccec_get_cp(keysize);
}
ccec_const_cp_t nullCP = { .zp = NULL };
return nullCP;
}
static ccec_const_cp_t getCPForPrivateSize(CFIndex encoded_length)
{
size_t keysize = ccec_x963_import_priv_size(encoded_length);
if(ccec_keysize_is_supported(keysize)) {
return ccec_get_cp(keysize);
}
ccec_const_cp_t nullCP = { .zp = NULL };
return nullCP;
}
static ccoid_t ccoid_secp192r1 = CC_EC_OID_SECP192R1;
static ccoid_t ccoid_secp256r1 = CC_EC_OID_SECP256R1;
static ccoid_t ccoid_secp224r1 = CC_EC_OID_SECP224R1;
static ccoid_t ccoid_secp384r1 = CC_EC_OID_SECP384R1;
static ccoid_t ccoid_secp521r1 = CC_EC_OID_SECP521R1;
static ccec_const_cp_t ccec_cp_for_oid(ccoid_t oid)
{
if (oid.oid) {
if (ccoid_equal(oid, ccoid_secp192r1)) {
return ccec_cp_192();
} else if (ccoid_equal(oid, ccoid_secp256r1)) {
return ccec_cp_256();
} else if (ccoid_equal(oid, ccoid_secp224r1)) {
return ccec_cp_224();
} else if (ccoid_equal(oid, ccoid_secp384r1)) {
return ccec_cp_384();
} else if (ccoid_equal(oid, ccoid_secp521r1)) {
return ccec_cp_521();
}
}
return (ccec_const_cp_t){NULL};
}
static OSStatus SecECPublicKeyInit(SecKeyRef key,
const uint8_t *keyData, CFIndex keyDataLength, SecKeyEncoding encoding) {
ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
OSStatus err = errSecParam;
switch (encoding) {
case kSecDERKeyEncoding:
{
const SecDERKey *derKey = (const SecDERKey *)keyData;
if (keyDataLength != sizeof(SecDERKey)) {
err = errSecDecode;
break;
}
ccec_const_cp_t cp = getCPForPublicSize(derKey->keyLength);
err = (ccec_import_pub(cp, derKey->keyLength, derKey->key, pubkey)
? errSecDecode : errSecSuccess);
break;
}
case kSecKeyEncodingBytes:
{
ccec_const_cp_t cp = getCPForPublicSize(keyDataLength);
err = (ccec_import_pub(cp, keyDataLength, keyData, pubkey)
? errSecDecode : errSecSuccess);
break;
}
case kSecExtractPublicFromPrivate:
{
ccec_full_ctx_t fullKey;
fullKey._full = (ccec_full_ctx *) keyData;
cc_size fullKeyN = ccec_ctx_n(fullKey);
require(fullKeyN <= ccn_nof(kMaximumECKeySize), errOut);
memcpy(pubkey._pub, fullKey.pub, ccec_pub_ctx_size(ccn_sizeof_n(fullKeyN)));
err = errSecSuccess;
break;
}
case kSecKeyEncodingApplePkcs1:
default:
err = errSecParam;
break;
}
errOut:
return err;
}
static OSStatus SecECPublicKeyRawVerify(SecKeyRef key, SecPadding padding,
const uint8_t *signedData, size_t signedDataLen,
const uint8_t *sig, size_t sigLen) {
int err = errSSLCrypto; ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
bool valid = 0;
if (ccec_verify(pubkey, signedDataLen, signedData, sigLen, sig, &valid))
err = errSSLCrypto; if (valid)
err = errSecSuccess;
return err;
}
static OSStatus SecECPublicKeyRawEncrypt(SecKeyRef key, SecPadding padding,
const uint8_t *plainText, size_t plainTextLen,
uint8_t *cipherText, size_t *cipherTextLen) {
ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
int err = errSecUnimplemented;
#if 0
require_noerr(err = ccec_wrap_key(pubkey, &ccsha256_di,
plainTextLen, plainText, cipherText), errOut);
errOut:
#endif
return err;
}
static size_t SecECPublicKeyBlockSize(SecKeyRef key) {
ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
return ccec_ctx_size(pubkey);
}
static CFDataRef SecECPublicKeyExport(CFAllocatorRef allocator,
ccec_pub_ctx_t pubkey) {
size_t pub_size = ccec_export_pub_size(pubkey);
CFMutableDataRef blob = CFDataCreateMutable(allocator, pub_size);
if (blob) {
CFDataSetLength(blob, pub_size);
ccec_export_pub(pubkey, CFDataGetMutableBytePtr(blob));
}
return blob;
}
static OSStatus SecECPublicKeyCopyPublicOctets(SecKeyRef key, CFDataRef *serailziation)
{
ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
CFAllocatorRef allocator = CFGetAllocator(key);
*serailziation = SecECPublicKeyExport(allocator, pubkey);
if (NULL == *serailziation)
return errSecDecode;
else
return errSecSuccess;
}
static CFDictionaryRef SecECPublicKeyCopyAttributeDictionary(SecKeyRef key) {
return SecKeyGeneratePublicAttributeDictionary(key, kSecAttrKeyTypeEC);
}
static CFStringRef SecECPublicKeyCopyKeyDescription(SecKeyRef key)
{
ccec_pub_ctx_t ecPubkey;
CFStringRef keyDescription = NULL;
size_t xlen, ylen, ix;
CFMutableStringRef xString = NULL;
CFMutableStringRef yString = NULL;
ecPubkey.pub = key->key;
long curveType = (long)SecECKeyGetNamedCurve(key);
char* curve= NULL;
switch (curveType)
{
case 23:
curve = "kSecECCurveSecp256r1";
break;
case 24:
curve = "kSecECCurveSecp384r1";
break;
case 25:
curve = "kSecECCurveSecp521r1";
break;
case -1:
curve = "kSecECCurveNone";
break;
default:
curve = "kSecECCurveNone";
break;
}
uint8_t *xunit = (uint8_t*)ccec_ctx_x(ecPubkey);
require_quiet( NULL != xunit, fail);
xlen = (size_t)strlen((char*)xunit);
xString = CFStringCreateMutable(kCFAllocatorDefault, xlen * 2);
require_quiet( NULL != xString, fail);
for (ix = 0; ix < xlen; ++ix)
{
CFStringAppendFormat(xString, NULL, CFSTR("%02X"), xunit[ix]);
}
uint8_t *yunit = (uint8_t*)ccec_ctx_y(ecPubkey);
require_quiet( NULL != yunit, fail);
ylen = (size_t)strlen((char*)yunit);
yString = CFStringCreateMutable(kCFAllocatorDefault, ylen*2);
require_quiet( NULL != yString, fail);
for(ix = 0; ix < ylen; ++ix)
{
CFStringAppendFormat(yString, NULL, CFSTR("%02X"), yunit[ix]);
}
keyDescription = CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR( "<SecKeyRef curve type: %s, algorithm id: %lu, key type: %s, version: %d, block size: %zu bits, y: %@, x: %@, addr: %p>"), curve, (long)SecKeyGetAlgorithmID(key), key->key_class->name, key->key_class->version, (8*SecKeyGetBlockSize(key)), yString, xString, key);
fail:
CFReleaseSafe(xString);
CFReleaseSafe(yString);
if(!keyDescription)
keyDescription = CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecKeyRef curve type: %s, algorithm id: %lu, key type: %s, version: %d, block size: %zu bits, addr: %p>"), curve,(long)SecKeyGetAlgorithmID(key), key->key_class->name, key->key_class->version, (8*SecKeyGetBlockSize(key)), key);
return keyDescription;
}
SecKeyDescriptor kSecECPublicKeyDescriptor = {
kSecKeyDescriptorVersion,
"ECPublicKey",
ccec_pub_ctx_size(ccn_sizeof(kMaximumECKeySize)),
SecECPublicKeyInit,
SecECPublicKeyDestroy,
NULL,
SecECPublicKeyRawVerify,
SecECPublicKeyRawEncrypt,
NULL,
NULL,
SecECPublicKeyBlockSize,
SecECPublicKeyCopyAttributeDictionary,
SecECPublicKeyCopyKeyDescription,
SecECKeyGetAlgorithmID,
SecECPublicKeyCopyPublicOctets,
};
SecKeyRef SecKeyCreateECPublicKey(CFAllocatorRef allocator,
const uint8_t *keyData, CFIndex keyDataLength,
SecKeyEncoding encoding) {
return SecKeyCreate(allocator, &kSecECPublicKeyDescriptor, keyData,
keyDataLength, encoding);
}
static void SecECPrivateKeyDestroy(SecKeyRef key) {
ccec_full_ctx_t fullkey;
fullkey.hdr = key->key;
if (ccec_ctx_cp(fullkey).zp)
cc_zero(ccec_full_ctx_size(ccn_sizeof_n(ccec_ctx_n(fullkey))), fullkey.hdr);
}
static OSStatus SecECPrivateKeyInit(SecKeyRef key,
const uint8_t *keyData, CFIndex keyDataLength, SecKeyEncoding encoding) {
ccec_full_ctx_t fullkey;
fullkey.hdr = key->key;
OSStatus err = errSecParam;
switch (encoding) {
case kSecKeyEncodingPkcs1:
{
ccoid_t oid;
size_t n;
ccec_const_cp_t cp;
require_noerr(ccec_der_import_priv_keytype(keyDataLength, keyData, &oid, &n), abort);
cp = ccec_cp_for_oid(oid);
if (cp.zp == NULL) {
cp = ccec_curve_for_length_lookup(n * 8 ,
ccec_cp_192(), ccec_cp_224(), ccec_cp_256(), ccec_cp_384(), ccec_cp_521(), NULL);
}
require_action(cp.zp != NULL, abort, err = errSecDecode);
ccec_ctx_init(cp, fullkey);
require_noerr(ccec_der_import_priv(cp, keyDataLength, keyData, fullkey), abort);
err = errSecSuccess;
break;
}
case kSecKeyEncodingBytes:
{
ccec_const_cp_t cp = getCPForPrivateSize(keyDataLength);
require(cp.zp != NULL, abort);
ccec_ctx_init(cp, fullkey);
size_t pubSize = ccec_export_pub_size(fullkey);
require(pubSize < (size_t) keyDataLength, abort);
require_noerr_action(ccec_import_pub(cp, pubSize, keyData, fullkey),
abort,
err = errSecDecode);
keyData += pubSize;
keyDataLength -= pubSize;
cc_unit *k = ccec_ctx_k(fullkey);
require_noerr_action(ccn_read_uint(ccec_ctx_n(fullkey), k, keyDataLength, keyData),
abort,
err = errSecDecode);
err = errSecSuccess;
break;
}
case kSecGenerateKey:
{
CFDictionaryRef parameters = (CFDictionaryRef) keyData;
CFTypeRef ksize = CFDictionaryGetValue(parameters, kSecAttrKeySizeInBits);
CFIndex keyLengthInBits = getIntValue(ksize);
ccec_const_cp_t cp = ccec_get_cp(keyLengthInBits);
if (!cp.zp) {
secwarning("Invalid or missing key size in: %@", parameters);
return errSecKeySizeNotAllowed;
}
if (!ccec_generate_key(cp, ccrng_seckey, fullkey))
err = errSecSuccess;
break;
}
default:
break;
}
abort:
return err;
}
static OSStatus SecECPrivateKeyRawSign(SecKeyRef key, SecPadding padding,
const uint8_t *dataToSign, size_t dataToSignLen,
uint8_t *sig, size_t *sigLen) {
ccec_full_ctx_t fullkey;
fullkey.hdr = key->key;
int err;;
check(sigLen);
require_noerr(err = ccec_sign(fullkey, dataToSignLen, dataToSign,
sigLen, sig, ccrng_seckey), errOut);
errOut:
return err;
}
#if 0
static const struct ccdigest_info *
ccdigest_lookup_by_oid(unsigned long oid_size, const void *oid) {
static const struct ccdigest_info *dis[] = {
&ccsha1_di,
&ccsha224_di,
&ccsha256_di,
&ccsha384_di,
&ccsha512_di
};
size_t i;
for (i = 0; i < array_size(dis); ++i) {
if (oid_size == dis[i]->oid_size && !memcmp(dis[i]->oid, oid, oid_size))
return dis[i];
}
return NULL;
}
#endif
static OSStatus SecECPrivateKeyRawDecrypt(SecKeyRef key, SecPadding padding,
const uint8_t *cipherText, size_t cipherTextLen,
uint8_t *plainText, size_t *plainTextLen) {
ccec_full_ctx_t fullkey;
fullkey.hdr = key->key;
int err = errSecUnimplemented;
#if 0
err = ccec_unwrap_key(fullkey, ccrng_seckey, ccdigest_lookup_by_oid,
cipherTextLen, cipherText, plainTextLen, plainText);
#endif
return err;
}
static size_t SecECPrivateKeyBlockSize(SecKeyRef key) {
ccec_full_ctx_t fullkey;
fullkey.hdr = key->key;
return ccec_ctx_size(fullkey);
}
static OSStatus SecECPrivateKeyCopyPublicOctets(SecKeyRef key, CFDataRef *serailziation)
{
ccec_full_ctx_t fullkey;
fullkey.hdr = key->key;
CFAllocatorRef allocator = CFGetAllocator(key);
*serailziation = SecECPublicKeyExport(allocator, fullkey);
if (NULL == *serailziation)
return errSecDecode;
else
return errSecSuccess;
}
static CFDataRef SecECPPrivateKeyExport(CFAllocatorRef allocator,
ccec_full_ctx_t fullkey) {
size_t prime_size = ccec_cp_prime_size(ccec_ctx_cp(fullkey));
size_t key_size = ccec_export_pub_size(fullkey) + prime_size;
CFMutableDataRef blob = CFDataCreateMutable(allocator, key_size);
if (blob) {
CFDataSetLength(blob, key_size);
ccec_export_pub(fullkey, CFDataGetMutableBytePtr(blob));
UInt8 *dest = CFDataGetMutableBytePtr(blob) + ccec_export_pub_size(fullkey);
const cc_unit *k = ccec_ctx_k(fullkey);
ccn_write_uint_padded(ccec_ctx_n(fullkey), k, prime_size, dest);
}
return blob;
}
static CFDictionaryRef SecECPrivateKeyCopyAttributeDictionary(SecKeyRef key) {
CFDictionaryRef dict = NULL;
CFAllocatorRef allocator = CFGetAllocator(key);
ccec_full_ctx_t fullkey;
fullkey.hdr = key->key;
CFDataRef fullKeyBlob = NULL;
require(fullKeyBlob = SecECPPrivateKeyExport(allocator, fullkey), errOut);
dict = SecKeyGeneratePrivateAttributeDictionary(key, kSecAttrKeyTypeEC, fullKeyBlob);
errOut:
CFReleaseSafe(fullKeyBlob);
return dict;
}
static CFStringRef SecECPrivateKeyCopyKeyDescription(SecKeyRef key) {
long curveType = (long)SecECKeyGetNamedCurve(key);
char* curve= NULL;
switch (curveType)
{
case 23:
curve = "kSecECCurveSecp256r1";
break;
case 24:
curve = "kSecECCurveSecp384r1";
break;
case 25:
curve = "kSecECCurveSecp521r1";
break;
case -1:
curve = "kSecECCurveNone";
break;
default:
curve = "kSecECCurveNone";
break;
}
return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR( "<SecKeyRef curve type: %s, algorithm id: %lu, key type: %s, version: %d, block size: %zu bits, addr: %p>"), curve, (long)SecKeyGetAlgorithmID(key), key->key_class->name, key->key_class->version, (8*SecKeyGetBlockSize(key)), key);
}
SecKeyDescriptor kSecECPrivateKeyDescriptor = {
kSecKeyDescriptorVersion,
"ECPrivateKey",
ccec_full_ctx_size(ccn_sizeof(kMaximumECKeySize)),
SecECPrivateKeyInit,
SecECPrivateKeyDestroy,
SecECPrivateKeyRawSign,
NULL,
NULL,
SecECPrivateKeyRawDecrypt,
NULL,
SecECPrivateKeyBlockSize,
SecECPrivateKeyCopyAttributeDictionary,
SecECPrivateKeyCopyKeyDescription,
SecECKeyGetAlgorithmID,
SecECPrivateKeyCopyPublicOctets,
};
SecKeyRef SecKeyCreateECPrivateKey(CFAllocatorRef allocator,
const uint8_t *keyData, CFIndex keyDataLength,
SecKeyEncoding encoding) {
return SecKeyCreate(allocator, &kSecECPrivateKeyDescriptor, keyData,
keyDataLength, encoding);
}
OSStatus SecECKeyGeneratePair(CFDictionaryRef parameters,
SecKeyRef *publicKey, SecKeyRef *privateKey) {
OSStatus status = errSecParam;
CFAllocatorRef allocator = NULL;
SecKeyRef pubKey = NULL;
SecKeyRef privKey = SecKeyCreate(allocator, &kSecECPrivateKeyDescriptor,
(const void*) parameters, 0, kSecGenerateKey);
require(privKey, errOut);
pubKey = SecKeyCreate(allocator, &kSecECPublicKeyDescriptor,
privKey->key, 0, kSecExtractPublicFromPrivate);
require(pubKey, errOut);
if (publicKey) {
*publicKey = pubKey;
pubKey = NULL;
}
if (privateKey) {
*privateKey = privKey;
privKey = NULL;
}
status = errSecSuccess;
errOut:
CFReleaseSafe(pubKey);
CFReleaseSafe(privKey);
return status;
}
SecECNamedCurve SecECKeyGetNamedCurve(SecKeyRef key) {
if (key->key_class != &kSecECPublicKeyDescriptor &&
key->key_class != &kSecECPrivateKeyDescriptor)
return kSecECCurveNone;
ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
switch (ccec_ctx_size(pubkey)) {
#if 0
case 24:
return kSecECCurveSecp192r1;
case 28:
return kSecECCurveSecp224r1;
#endif
case 32:
return kSecECCurveSecp256r1;
case 48:
return kSecECCurveSecp384r1;
case 66:
return kSecECCurveSecp521r1;
}
return kSecECCurveNone;
}
CFDataRef SecECKeyCopyPublicBits(SecKeyRef key) {
if (key->key_class != &kSecECPublicKeyDescriptor &&
key->key_class != &kSecECPrivateKeyDescriptor)
return NULL;
ccec_pub_ctx_t pubkey;
pubkey.pub = key->key;
return SecECPublicKeyExport(CFGetAllocator(key), pubkey);
}