#include "SecKey.h"
#include "SecKeyPriv.h"
#include "SecItem.h"
#include "SecItemPriv.h"
#include <libDER/asn1Types.h>
#include <libDER/DER_Encode.h>
#include <libDER/DER_Decode.h>
#include <libDER/DER_Keys.h>
#include <Security/SecAsn1Types.h>
#include <Security/SecAsn1Coder.h>
#include <security_keychain/KeyItem.h>
#include <CommonCrypto/CommonKeyDerivation.h>
#include "SecBridge.h"
#include <security_keychain/Access.h>
#include <security_keychain/Keychains.h>
#include <security_keychain/KeyItem.h>
#include <string.h>
#include <syslog.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <security_cdsa_client/wrapkey.h>
#include "SecImportExportCrypto.h"
CFTypeID
SecKeyGetTypeID(void)
{
BEGIN_SECAPI
return gTypes().KeyItem.typeID;
END_SECAPI1(_kCFRuntimeNotATypeID)
}
static OSStatus SecKeyCreatePairInternal(
SecKeychainRef keychainRef,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE publicKeyUsage,
uint32 publicKeyAttr,
CSSM_KEYUSE privateKeyUsage,
uint32 privateKeyAttr,
SecAccessRef initialAccess,
SecKeyRef* publicKeyRef,
SecKeyRef* privateKeyRef)
{
BEGIN_SECAPI
Keychain keychain = Keychain::optional(keychainRef);
SecPointer<Access> theAccess(initialAccess ? Access::required(initialAccess) : new Access("<key>"));
SecPointer<KeyItem> pubItem, privItem;
Mutex *keychainMutex = keychain->getKeychainMutex();
StLock<Mutex> _(*keychainMutex);
KeyItem::createPair(keychain,
algorithm,
keySizeInBits,
contextHandle,
publicKeyUsage,
publicKeyAttr,
privateKeyUsage,
privateKeyAttr,
theAccess,
pubItem,
privItem);
if (publicKeyRef)
*publicKeyRef = pubItem->handle();
if (privateKeyRef)
*privateKeyRef = privItem->handle();
END_SECAPI
}
OSStatus
SecKeyCreatePair(
SecKeychainRef keychainRef,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE publicKeyUsage,
uint32 publicKeyAttr,
CSSM_KEYUSE privateKeyUsage,
uint32 privateKeyAttr,
SecAccessRef initialAccess,
SecKeyRef* publicKeyRef,
SecKeyRef* privateKeyRef)
{
OSStatus result = SecKeyCreatePairInternal(keychainRef, algorithm, keySizeInBits, contextHandle, publicKeyUsage,
publicKeyAttr, privateKeyUsage, privateKeyAttr, initialAccess, publicKeyRef, privateKeyRef);
return result;
}
OSStatus
SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey)
{
BEGIN_SECAPI
Required(cssmKey) = KeyItem::required(key)->key();
END_SECAPI
}
OSStatus
SecKeyGetCSPHandle(SecKeyRef keyRef, CSSM_CSP_HANDLE *cspHandle)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
Required(cspHandle) = keyItem->csp()->handle();
END_SECAPI
}
OSStatus
SecKeyGetAlgorithmID(SecKeyRef keyRef, const CSSM_X509_ALGORITHM_IDENTIFIER **algid)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
Required(algid) = &keyItem->algorithmIdentifier();
END_SECAPI
}
CFIndex
SecKeyGetAlgorithmId(SecKeyRef key)
{
const CSSM_KEY *cssmKey;
if (SecKeyGetCSSMKey(key, &cssmKey) != errSecSuccess)
return kSecNullAlgorithmID;
switch (cssmKey->KeyHeader.AlgorithmId) {
case CSSM_ALGID_RSA:
return kSecRSAAlgorithmID;
case CSSM_ALGID_DSA:
return kSecDSAAlgorithmID;
case CSSM_ALGID_ECDSA:
return kSecECDSAAlgorithmID;
default:
assert(0);
return kSecNullAlgorithmID;
}
}
OSStatus
SecKeyGetStrengthInBits(SecKeyRef keyRef, const CSSM_X509_ALGORITHM_IDENTIFIER *algid, unsigned int *strength)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
Required(strength) = keyItem->strengthInBits(algid);
END_SECAPI
}
OSStatus
SecKeyGetCredentials(
SecKeyRef keyRef,
CSSM_ACL_AUTHORIZATION_TAG operation,
SecCredentialType credentialType,
const CSSM_ACCESS_CREDENTIALS **outCredentials)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
Required(outCredentials) = keyItem->getCredentials(operation, credentialType);
END_SECAPI
}
OSStatus
SecKeyImportPair(
SecKeychainRef keychainRef,
const CSSM_KEY *publicCssmKey,
const CSSM_KEY *privateCssmKey,
SecAccessRef initialAccess,
SecKeyRef* publicKey,
SecKeyRef* privateKey)
{
BEGIN_SECAPI
Keychain keychain = Keychain::optional(keychainRef);
SecPointer<Access> theAccess(initialAccess ? Access::required(initialAccess) : new Access("<key>"));
SecPointer<KeyItem> pubItem, privItem;
KeyItem::importPair(keychain,
Required(publicCssmKey),
Required(privateCssmKey),
theAccess,
pubItem,
privItem);
if (publicKey)
*publicKey = pubItem->handle();
if (privateKey)
*privateKey = privItem->handle();
END_SECAPI
}
static OSStatus
SecKeyGenerateWithAttributes(
SecKeychainAttributeList* attrList,
SecKeychainRef keychainRef,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE keyUsage,
uint32 keyAttr,
SecAccessRef initialAccess,
SecKeyRef* keyRef)
{
BEGIN_SECAPI
Keychain keychain;
SecPointer<Access> theAccess;
if (keychainRef)
keychain = KeychainImpl::required(keychainRef);
if (initialAccess)
theAccess = Access::required(initialAccess);
SecPointer<KeyItem> item = KeyItem::generateWithAttributes(attrList,
keychain,
algorithm,
keySizeInBits,
contextHandle,
keyUsage,
keyAttr,
theAccess);
if (keyRef)
*keyRef = item->handle();
END_SECAPI
}
OSStatus
SecKeyGenerate(
SecKeychainRef keychainRef,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE keyUsage,
uint32 keyAttr,
SecAccessRef initialAccess,
SecKeyRef* keyRef)
{
return SecKeyGenerateWithAttributes(NULL,
keychainRef, algorithm, keySizeInBits,
contextHandle, keyUsage, keyAttr,
initialAccess, keyRef);
}
SecKeyRef
SecKeyCreate(CFAllocatorRef allocator,
const SecKeyDescriptor *keyClass,
const uint8_t *keyData,
CFIndex keyDataLength,
SecKeyEncoding encoding)
{
SecKeyRef keyRef = NULL;
OSStatus __secapiresult;
try {
__secapiresult=errSecSuccess;
}
catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
catch (...) { __secapiresult=errSecInternalComponent; }
return keyRef;
}
OSStatus
SecKeyCreateWithCSSMKey(const CSSM_KEY *cssmKey,
SecKeyRef *keyRef)
{
BEGIN_SECAPI
Required(cssmKey);
CssmClient::CSP csp(cssmKey->KeyHeader.CspId);
CssmClient::Key key(csp, *cssmKey);
KeyItem *item = new KeyItem(key);
if (keyRef)
*keyRef = item->handle();
END_SECAPI
}
static u_int32_t ConvertCFStringToInteger(CFStringRef ref)
{
if (ref == NULL)
{
return 0;
}
CFIndex numChars = CFStringGetMaximumSizeForEncoding(CFStringGetLength(ref), kCFStringEncodingUTF8);
char buffer[numChars];
if (!CFStringGetCString(ref, buffer, numChars, kCFStringEncodingUTF8))
{
MacOSError::throwMe(errSecParam);
}
return atoi(buffer);
}
static OSStatus CheckAlgorithmType(CFDictionaryRef parameters, CSSM_ALGORITHMS &algorithms)
{
CFStringRef ktype = (CFStringRef) CFDictionaryGetValue(parameters, kSecAttrKeyType);
if (ktype == NULL)
{
return errSecParam;
}
if (CFEqual(ktype, kSecAttrKeyTypeRSA)) {
algorithms = CSSM_ALGID_RSA;
return errSecSuccess;
} else if(CFEqual(ktype, kSecAttrKeyTypeECDSA) ||
CFEqual(ktype, kSecAttrKeyTypeEC)) {
algorithms = CSSM_ALGID_ECDSA;
return errSecSuccess;
} else if(CFEqual(ktype, kSecAttrKeyTypeAES)) {
algorithms = CSSM_ALGID_AES;
return errSecSuccess;
} else if(CFEqual(ktype, kSecAttrKeyType3DES)) {
algorithms = CSSM_ALGID_3DES;
return errSecSuccess;
} else {
return errSecUnsupportedAlgorithm;
}
}
static OSStatus GetKeySize(CFDictionaryRef parameters, CSSM_ALGORITHMS algorithms, uint32 &keySizeInBits)
{
CFTypeRef ref = CFDictionaryGetValue(parameters, kSecAttrKeySizeInBits);
keySizeInBits = kSecDefaultKeySize;
CFTypeID bitSizeType = CFGetTypeID(ref);
if (bitSizeType == CFStringGetTypeID())
keySizeInBits = ConvertCFStringToInteger((CFStringRef) ref);
else if (bitSizeType == CFNumberGetTypeID())
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &keySizeInBits);
else return errSecParam;
switch (algorithms) {
case CSSM_ALGID_ECDSA:
if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSecp256r1;
if(keySizeInBits == kSecp192r1 || keySizeInBits == kSecp256r1 || keySizeInBits == kSecp384r1 || keySizeInBits == kSecp521r1 ) return errSecSuccess;
break;
case CSSM_ALGID_RSA:
if(keySizeInBits % 8) return errSecParam;
if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = 2048;
if(keySizeInBits >= kSecRSAMin && keySizeInBits <= kSecRSAMax) return errSecSuccess;
break;
case CSSM_ALGID_AES:
if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSecAES128;
if(keySizeInBits == kSecAES128 || keySizeInBits == kSecAES192 || keySizeInBits == kSecAES256) return errSecSuccess;
break;
case CSSM_ALGID_3DES:
if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSec3DES192;
if(keySizeInBits == kSec3DES192) return errSecSuccess;
break;
default:
break;
}
return errSecParam;
}
enum AttributeType
{
kStringType,
kBooleanType,
kIntegerType
};
struct ParameterAttribute
{
const CFTypeRef *name;
AttributeType type;
};
static ParameterAttribute gAttributes[] =
{
{
&kSecAttrLabel,
kStringType
},
{
&kSecAttrIsPermanent,
kBooleanType
},
{
&kSecAttrApplicationTag,
kStringType
},
{
&kSecAttrEffectiveKeySize,
kBooleanType
},
{
&kSecAttrCanEncrypt,
kBooleanType
},
{
&kSecAttrCanDecrypt,
kBooleanType
},
{
&kSecAttrCanDerive,
kBooleanType
},
{
&kSecAttrCanSign,
kBooleanType
},
{
&kSecAttrCanVerify,
kBooleanType
},
{
&kSecAttrCanUnwrap,
kBooleanType
}
};
const int kNumberOfAttributes = sizeof(gAttributes) / sizeof(ParameterAttribute);
static OSStatus ScanDictionaryForParameters(CFDictionaryRef parameters, void* attributePointers[])
{
int i;
for (i = 0; i < kNumberOfAttributes; ++i)
{
CFTypeRef value = CFDictionaryGetValue(parameters, *(gAttributes[i].name));
if (value != NULL)
{
switch (gAttributes[i].type)
{
case kStringType:
*(CFTypeRef*) attributePointers[i] = value;
break;
case kBooleanType:
{
CFBooleanRef bRef = (CFBooleanRef) value;
*(bool*) attributePointers[i] = CFBooleanGetValue(bRef);
}
break;
case kIntegerType:
{
CFNumberRef nRef = (CFNumberRef) value;
CFNumberGetValue(nRef, kCFNumberSInt32Type, attributePointers[i]);
}
break;
}
}
}
return errSecSuccess;
}
static OSStatus GetKeyParameters(CFDictionaryRef parameters, int keySize, bool isPublic, CSSM_KEYUSE &keyUse, uint32 &attrs, CFTypeRef &labelRef, CFDataRef &applicationTagRef)
{
labelRef = NULL;
bool isPermanent = false;
applicationTagRef = NULL;
CFTypeRef effectiveKeySize = NULL;
bool canDecrypt = isPublic ? false : true;
bool canEncrypt = !canDecrypt;
bool canDerive = true;
bool canSign = isPublic ? false : true;
bool canVerify = !canSign;
bool canUnwrap = isPublic ? false : true;
attrs = CSSM_KEYATTR_EXTRACTABLE;
keyUse = 0;
void* attributePointers[] = {&labelRef, &isPermanent, &applicationTagRef, &effectiveKeySize, &canEncrypt, &canDecrypt,
&canDerive, &canSign, &canVerify, &canUnwrap};
OSStatus result = ScanDictionaryForParameters(parameters, attributePointers);
if (result != errSecSuccess)
{
return result;
}
CFTypeRef key;
if (isPublic)
{
key = kSecPublicKeyAttrs;
}
else
{
key = kSecPrivateKeyAttrs;
}
CFTypeRef dType = CFDictionaryGetValue(parameters, key);
if (dType != NULL)
{
if (CFGetTypeID(dType) != CFDictionaryGetTypeID())
{
return errSecParam;
}
result = ScanDictionaryForParameters((CFDictionaryRef)dType, attributePointers);
if (result != errSecSuccess)
{
return result;
}
}
keyUse = 0;
if (canDecrypt)
{
keyUse |= CSSM_KEYUSE_DECRYPT;
}
if (canEncrypt)
{
keyUse |= CSSM_KEYUSE_ENCRYPT;
}
if (canDerive)
{
keyUse |= CSSM_KEYUSE_DERIVE;
}
if (canSign)
{
keyUse |= CSSM_KEYUSE_SIGN;
}
if (canVerify)
{
keyUse |= CSSM_KEYUSE_VERIFY;
}
if (canUnwrap)
{
keyUse |= CSSM_KEYUSE_UNWRAP;
}
CFTypeRef value = NULL;
if (!isPublic && CFDictionaryGetValueIfPresent(parameters, kSecAttrIsExtractable, (const void **)&value) && value)
{
Boolean keyIsExtractable = CFEqual(kCFBooleanTrue, value);
if (!keyIsExtractable)
attrs = 0;
}
attrs |= CSSM_KEYATTR_PERMANENT;
return errSecSuccess;
}
static OSStatus MakeKeyGenParametersFromDictionary(CFDictionaryRef parameters,
CSSM_ALGORITHMS &algorithms,
uint32 &keySizeInBits,
CSSM_KEYUSE &publicKeyUse,
uint32 &publicKeyAttr,
CFTypeRef &publicKeyLabelRef,
CFDataRef &publicKeyAttributeTagRef,
CSSM_KEYUSE &privateKeyUse,
uint32 &privateKeyAttr,
CFTypeRef &privateKeyLabelRef,
CFDataRef &privateKeyAttributeTagRef,
SecAccessRef &initialAccess)
{
OSStatus result;
result = CheckAlgorithmType(parameters, algorithms);
if (result != errSecSuccess)
{
return result;
}
result = GetKeySize(parameters, algorithms, keySizeInBits);
if (result != errSecSuccess)
{
return result;
}
result = GetKeyParameters(parameters, keySizeInBits, false, privateKeyUse, privateKeyAttr, privateKeyLabelRef, privateKeyAttributeTagRef);
if (result != errSecSuccess)
{
return result;
}
result = GetKeyParameters(parameters, keySizeInBits, true, publicKeyUse, publicKeyAttr, publicKeyLabelRef, publicKeyAttributeTagRef);
if (result != errSecSuccess)
{
return result;
}
if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrAccess, (const void **)&initialAccess))
{
initialAccess = NULL;
}
else if (SecAccessGetTypeID() != CFGetTypeID(initialAccess))
{
return errSecParam;
}
return errSecSuccess;
}
static OSStatus SetKeyLabelAndTag(SecKeyRef keyRef, CFTypeRef label, CFDataRef tag)
{
int numToModify = 0;
if (label != NULL)
{
numToModify += 1;
}
if (tag != NULL)
{
numToModify += 1;
}
if (numToModify == 0)
{
return errSecSuccess;
}
SecKeychainAttributeList attrList;
SecKeychainAttribute attributes[numToModify];
int i = 0;
if (label != NULL)
{
if (CFStringGetTypeID() == CFGetTypeID(label)) {
CFStringRef label_string = static_cast<CFStringRef>(label);
attributes[i].tag = kSecKeyPrintName;
attributes[i].data = (void*) CFStringGetCStringPtr(label_string, kCFStringEncodingUTF8);
if (NULL == attributes[i].data) {
CFIndex buffer_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(label_string), kCFStringEncodingUTF8);
attributes[i].data = alloca((size_t)buffer_length);
if (NULL == attributes[i].data) {
UnixError::throwMe(ENOMEM);
}
if (!CFStringGetCString(label_string, static_cast<char *>(attributes[i].data), buffer_length, kCFStringEncodingUTF8)) {
MacOSError::throwMe(errSecParam);
}
}
attributes[i].length = (UInt32)strlen(static_cast<char *>(attributes[i].data));
} else if (CFDataGetTypeID() == CFGetTypeID(label)) {
CFDataRef label_data = static_cast<CFDataRef>(label);
attributes[i].tag = kSecKeyLabel;
attributes[i].data = (void*) CFDataGetBytePtr(label_data);
attributes[i].length = (UInt32)CFDataGetLength(label_data);
} else {
MacOSError::throwMe(errSecParam);
}
i++;
}
if (tag != NULL)
{
attributes[i].tag = kSecKeyApplicationTag;
attributes[i].data = (void*) CFDataGetBytePtr(tag);
attributes[i].length = (UInt32)CFDataGetLength(tag);
i++;
}
attrList.count = numToModify;
attrList.attr = attributes;
return SecKeychainItemModifyAttributesAndData((SecKeychainItemRef) keyRef, &attrList, 0, NULL);
}
OSStatus
SecKeyGeneratePair(
CFDictionaryRef parameters,
SecKeyRef *publicKey,
SecKeyRef *privateKey)
{
BEGIN_SECAPI
Required(parameters);
Required(publicKey);
Required(privateKey);
CSSM_ALGORITHMS algorithms;
uint32 keySizeInBits;
CSSM_KEYUSE publicKeyUse;
uint32 publicKeyAttr;
CFTypeRef publicKeyLabelRef;
CFDataRef publicKeyAttributeTagRef;
CSSM_KEYUSE privateKeyUse;
uint32 privateKeyAttr;
CFTypeRef privateKeyLabelRef;
CFDataRef privateKeyAttributeTagRef;
SecAccessRef initialAccess;
SecKeychainRef keychain;
OSStatus result = MakeKeyGenParametersFromDictionary(parameters, algorithms, keySizeInBits, publicKeyUse, publicKeyAttr, publicKeyLabelRef,
publicKeyAttributeTagRef, privateKeyUse, privateKeyAttr, privateKeyLabelRef, privateKeyAttributeTagRef,
initialAccess);
if (result != errSecSuccess)
{
return result;
}
keychain = NULL;
if (!CFDictionaryGetValueIfPresent(parameters, kSecUseKeychain, (const void **)&keychain))
keychain = NULL;
else if (SecKeychainGetTypeID() != CFGetTypeID(keychain))
keychain = NULL;
result = SecKeyCreatePair(keychain, algorithms, keySizeInBits, 0, publicKeyUse, publicKeyAttr, privateKeyUse, privateKeyAttr, initialAccess, publicKey, privateKey);
if (result != errSecSuccess)
{
return result;
}
SetKeyLabelAndTag(*publicKey, publicKeyLabelRef, publicKeyAttributeTagRef);
SetKeyLabelAndTag(*privateKey, privateKeyLabelRef, privateKeyAttributeTagRef);
return result;
END_SECAPI
}
OSStatus
SecKeyRawSign(
SecKeyRef key,
SecPadding padding,
const uint8_t *dataToSign,
size_t dataToSignLen,
uint8_t *sig,
size_t *sigLen)
{
BEGIN_SECAPI
Required(key);
SecPointer<KeyItem> keyItem(KeyItem::required(key));
CSSM_DATA dataInput;
dataInput.Data = (uint8_t*) dataToSign;
dataInput.Length = dataToSignLen;
CSSM_DATA output;
output.Data = sig;
output.Length = *sigLen;
const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault);
keyItem->RawSign(padding, dataInput, credentials, output);
*sigLen = output.Length;
END_SECAPI
}
OSStatus SecKeyRawVerifyOSX(
SecKeyRef key,
SecPadding padding,
const uint8_t *signedData,
size_t signedDataLen,
const uint8_t *sig,
size_t sigLen)
{
return SecKeyRawVerify(key,padding,signedData,signedDataLen,sig,sigLen);
}
OSStatus
SecKeyRawVerify(
SecKeyRef key,
SecPadding padding,
const uint8_t *signedData,
size_t signedDataLen,
const uint8_t *sig,
size_t sigLen)
{
BEGIN_SECAPI
Required(key);
SecPointer<KeyItem> keyItem(KeyItem::required(key));
CSSM_DATA dataInput;
dataInput.Data = (uint8_t*) signedData;
dataInput.Length = signedDataLen;
CSSM_DATA signature;
signature.Data = (uint8_t*) sig;
signature.Length = sigLen;
const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ANY, kSecCredentialTypeDefault);
keyItem->RawVerify(padding, dataInput, credentials, signature);
END_SECAPI
}
OSStatus
SecKeyEncrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *plainText,
size_t plainTextLen,
uint8_t *cipherText,
size_t *cipherTextLen)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItem(KeyItem::required(key));
CSSM_DATA inData, outData;
inData.Data = (uint8*) plainText;
inData.Length = plainTextLen;
outData.Data = cipherText;
outData.Length = *cipherTextLen;
const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ENCRYPT, kSecCredentialTypeDefault);
keyItem->Encrypt(padding, inData, credentials, outData);
*cipherTextLen = outData.Length;
END_SECAPI
}
OSStatus
SecKeyDecrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *cipherText,
size_t cipherTextLen,
uint8_t *plainText,
size_t *plainTextLen)
{
BEGIN_SECAPI
SecPointer<KeyItem> keyItem(KeyItem::required(key));
CSSM_DATA inData, outData;
inData.Data = (uint8*) cipherText;
inData.Length = cipherTextLen;
outData.Data = plainText;
outData.Length = *plainTextLen;
const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_DECRYPT, kSecCredentialTypeDefault);
keyItem->Decrypt(padding, inData, credentials, outData);
*plainTextLen = outData.Length;
END_SECAPI
}
size_t
SecKeyGetBlockSize(SecKeyRef key)
{
size_t blockSize = 0;
OSStatus __secapiresult;
try {
CSSM_KEY cssmKey = KeyItem::required(key)->key();
switch(cssmKey.KeyHeader.AlgorithmId)
{
case CSSM_ALGID_RSA:
case CSSM_ALGID_DSA:
blockSize = cssmKey.KeyHeader.LogicalKeySizeInBits / 8;
break;
case CSSM_ALGID_ECDSA:
{
#define ECDSA_KEY_SIZE_IN_BYTES(bits) (((bits) + 7) / 8)
#define ECDSA_MAX_COORD_SIZE_IN_BYTES(n) (ECDSA_KEY_SIZE_IN_BYTES(n) + 1)
size_t coordSize = ECDSA_MAX_COORD_SIZE_IN_BYTES(cssmKey.KeyHeader.LogicalKeySizeInBits);
assert(coordSize < 256);
size_t coordDERLen = (coordSize > 127) ? 2 : 1;
size_t coordLen = 1 + coordDERLen + coordSize;
size_t pointSize = 2 * coordLen;
assert(pointSize < 256);
size_t pointDERLen = (pointSize > 127) ? 2 : 1;
size_t pointLen = 1 + pointDERLen + pointSize;
blockSize = pointLen;
}
break;
case CSSM_ALGID_AES:
blockSize = 16;
break;
case CSSM_ALGID_DES:
case CSSM_ALGID_3DES_3KEY:
blockSize = 8;
break;
default:
assert(0);
blockSize = 16;
break;
}
__secapiresult=errSecSuccess;
}
catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
catch (...) { __secapiresult=errSecInternalComponent; }
return blockSize;
}
static CFTypeRef
utilGetStringFromCFDict(CFDictionaryRef parameters, CFTypeRef key, CFTypeRef defaultValue)
{
CFTypeRef value = CFDictionaryGetValue(parameters, key);
if (value != NULL) return value;
return defaultValue;
}
static uint32_t
utilGetNumberFromCFDict(CFDictionaryRef parameters, CFTypeRef key, uint32_t defaultValue)
{
uint32_t integerValue;
CFTypeRef value = CFDictionaryGetValue(parameters, key);
if (value != NULL) {
CFNumberRef nRef = (CFNumberRef) value;
CFNumberGetValue(nRef, kCFNumberSInt32Type, &integerValue);
return integerValue;
}
return defaultValue;
}
static uint32_t
utilGetMaskValFromCFDict(CFDictionaryRef parameters, CFTypeRef key, uint32_t maskValue)
{
CFTypeRef value = CFDictionaryGetValue(parameters, key);
if (value != NULL) {
CFBooleanRef bRef = (CFBooleanRef) value;
if(CFBooleanGetValue(bRef)) return maskValue;
}
return 0;
}
static void
utilGetKeyParametersFromCFDict(CFDictionaryRef parameters, CSSM_ALGORITHMS *algorithm, uint32 *keySizeInBits, CSSM_KEYUSE *keyUsage, CSSM_KEYCLASS *keyClass)
{
CFTypeRef algorithmDictValue = utilGetStringFromCFDict(parameters, kSecAttrKeyType, kSecAttrKeyTypeAES);
CFTypeRef keyClassDictValue = utilGetStringFromCFDict(parameters, kSecAttrKeyClass, kSecAttrKeyClassSymmetric);
if(CFEqual(algorithmDictValue, kSecAttrKeyTypeAES)) {
*algorithm = CSSM_ALGID_AES;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeDES)) {
*algorithm = CSSM_ALGID_DES;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyType3DES)) {
*algorithm = CSSM_ALGID_3DES_3KEY_EDE;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRC4)) {
*algorithm = CSSM_ALGID_RC4;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRC2)) {
*algorithm = CSSM_ALGID_RC2;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeCAST)) {
*algorithm = CSSM_ALGID_CAST;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRSA)) {
*algorithm = CSSM_ALGID_RSA;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeDSA)) {
*algorithm = CSSM_ALGID_DSA;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
} else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeECDSA) ||
CFEqual(algorithmDictValue, kSecAttrKeyTypeEC)) {
*algorithm = CSSM_ALGID_ECDSA;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
} else {
*algorithm = CSSM_ALGID_AES;
*keySizeInBits = 128;
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
}
if(CFEqual(keyClassDictValue, kSecAttrKeyClassPublic)) {
*keyClass = CSSM_KEYCLASS_PUBLIC_KEY;
} else if(CFEqual(keyClassDictValue, kSecAttrKeyClassPrivate)) {
*keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
} else if(CFEqual(keyClassDictValue, kSecAttrKeyClassSymmetric)) {
*keyClass = CSSM_KEYCLASS_SESSION_KEY;
}
*keySizeInBits = utilGetNumberFromCFDict(parameters, kSecAttrKeySizeInBits, *keySizeInBits);
*keyUsage = utilGetMaskValFromCFDict(parameters, kSecAttrCanEncrypt, CSSM_KEYUSE_ENCRYPT) |
utilGetMaskValFromCFDict(parameters, kSecAttrCanDecrypt, CSSM_KEYUSE_DECRYPT) |
utilGetMaskValFromCFDict(parameters, kSecAttrCanWrap, CSSM_KEYUSE_WRAP) |
utilGetMaskValFromCFDict(parameters, kSecAttrCanUnwrap, CSSM_KEYUSE_UNWRAP);
if(*keyClass == CSSM_KEYCLASS_PRIVATE_KEY || *keyClass == CSSM_KEYCLASS_PUBLIC_KEY) {
*keyUsage |= utilGetMaskValFromCFDict(parameters, kSecAttrCanSign, CSSM_KEYUSE_SIGN) |
utilGetMaskValFromCFDict(parameters, kSecAttrCanVerify, CSSM_KEYUSE_VERIFY);
}
if(*keyUsage == 0) {
switch (*keyClass) {
case CSSM_KEYCLASS_PRIVATE_KEY:
*keyUsage = CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_SIGN;
break;
case CSSM_KEYCLASS_PUBLIC_KEY:
*keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP;
break;
default:
*keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY;
break;
}
}
}
static CFStringRef
utilCopyDefaultKeyLabel(void)
{
CFDateRef dateNow = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
CFStringRef defaultLabel = CFCopyDescription(dateNow);
CFRelease(dateNow);
return defaultLabel;
}
SecKeyRef
SecKeyGenerateSymmetric(CFDictionaryRef parameters, CFErrorRef *error)
{
OSStatus result = errSecParam; SecKeyRef key = NULL;
SecKeychainRef keychain = NULL;
SecAccessRef access;
CFStringRef label;
CFStringRef appLabel;
CFStringRef appTag;
CFStringRef dateLabel = NULL;
CSSM_ALGORITHMS algorithm;
uint32 keySizeInBits;
CSSM_KEYUSE keyUsage;
uint32 keyAttr = CSSM_KEYATTR_RETURN_DEFAULT;
CSSM_KEYCLASS keyClass;
CFTypeRef value;
Boolean isPermanent;
Boolean isExtractable;
if (!CFDictionaryGetValueIfPresent(parameters, kSecUseKeychain, (const void **)&keychain))
keychain = NULL;
else if (SecKeychainGetTypeID() != CFGetTypeID(keychain)) {
keychain = NULL;
goto errorExit;
}
else
CFRetain(keychain);
if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrIsPermanent, (const void **)&value))
isPermanent = false;
else if (!value || (CFBooleanGetTypeID() != CFGetTypeID(value)))
goto errorExit;
else
isPermanent = CFEqual(kCFBooleanTrue, value);
if (isPermanent) {
if (keychain == NULL) {
result = SecKeychainCopyDefault(&keychain);
}
keyAttr |= CSSM_KEYATTR_PERMANENT;
}
if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrIsExtractable, (const void **)&value))
isExtractable = true; else if (!value || (CFBooleanGetTypeID() != CFGetTypeID(value)))
goto errorExit;
else
isExtractable = CFEqual(kCFBooleanTrue, value);
if (isExtractable)
keyAttr |= CSSM_KEYATTR_EXTRACTABLE;
if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrAccess, (const void **)&access))
access = NULL;
else if (SecAccessGetTypeID() != CFGetTypeID(access))
goto errorExit;
if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrLabel, (const void **)&label))
label = (dateLabel = utilCopyDefaultKeyLabel()); else if (CFStringGetTypeID() != CFGetTypeID(label))
goto errorExit;
if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrApplicationLabel, (const void **)&appLabel))
appLabel = (dateLabel) ? dateLabel : (dateLabel = utilCopyDefaultKeyLabel());
else if (CFStringGetTypeID() != CFGetTypeID(appLabel))
goto errorExit;
if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrApplicationTag, (const void **)&appTag))
appTag = NULL;
else if (CFStringGetTypeID() != CFGetTypeID(appTag))
goto errorExit;
utilGetKeyParametersFromCFDict(parameters, &algorithm, &keySizeInBits, &keyUsage, &keyClass);
if (!keychain) {
result = SecKeyGenerate(keychain, algorithm, keySizeInBits, 0, keyUsage, keyAttr, access, &key);
}
else {
size_t labelBufLen = (label) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(label), kCFStringEncodingUTF8) + 1 : 0;
char *labelBuf = (char *)malloc(labelBufLen);
size_t appLabelBufLen = (appLabel) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(appLabel), kCFStringEncodingUTF8) + 1 : 0;
char *appLabelBuf = (char *)malloc(appLabelBufLen);
size_t appTagBufLen = (appTag) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(appTag), kCFStringEncodingUTF8) + 1 : 0;
char *appTagBuf = (char *)malloc(appTagBufLen);
if (label && !CFStringGetCString(label, labelBuf, labelBufLen-1, kCFStringEncodingUTF8))
labelBuf[0]=0;
if (appLabel && !CFStringGetCString(appLabel, appLabelBuf, appLabelBufLen-1, kCFStringEncodingUTF8))
appLabelBuf[0]=0;
if (appTag && !CFStringGetCString(appTag, appTagBuf, appTagBufLen-1, kCFStringEncodingUTF8))
appTagBuf[0]=0;
SecKeychainAttribute attrs[] = {
{ kSecKeyPrintName, (UInt32)strlen(labelBuf), (char *)labelBuf },
{ kSecKeyLabel, (UInt32)strlen(appLabelBuf), (char *)appLabelBuf },
{ kSecKeyApplicationTag, (UInt32)strlen(appTagBuf), (char *)appTagBuf } };
SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
if (!appTag) --attributes.count;
result = SecKeyGenerateWithAttributes(&attributes,
keychain, algorithm, keySizeInBits, 0,
keyUsage, keyAttr, access, &key);
free(labelBuf);
free(appLabelBuf);
free(appTagBuf);
}
errorExit:
if (result && error) {
*error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, result, NULL);
}
if (dateLabel)
CFRelease(dateLabel);
if (keychain)
CFRelease(keychain);
return key;
}
SecKeyRef
SecKeyCreateFromData(CFDictionaryRef parameters, CFDataRef keyData, CFErrorRef *error)
{
CSSM_ALGORITHMS algorithm;
uint32 keySizeInBits;
CSSM_KEYUSE keyUsage;
CSSM_KEYCLASS keyClass;
CSSM_RETURN crtn;
utilGetKeyParametersFromCFDict(parameters, &algorithm, &keySizeInBits, &keyUsage, &keyClass);
CSSM_CSP_HANDLE cspHandle = cuCspStartup(CSSM_FALSE);
SecKeyImportExportParameters iparam;
memset(&iparam, 0, sizeof(iparam));
iparam.keyUsage = keyUsage;
SecExternalItemType itype;
switch (keyClass) {
case CSSM_KEYCLASS_PRIVATE_KEY:
itype = kSecItemTypePrivateKey;
break;
case CSSM_KEYCLASS_PUBLIC_KEY:
itype = kSecItemTypePublicKey;
break;
case CSSM_KEYCLASS_SESSION_KEY:
itype = kSecItemTypeSessionKey;
break;
default:
itype = kSecItemTypeUnknown;
break;
}
CFMutableArrayRef ka = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
crtn = impExpImportRawKey(keyData, kSecFormatUnknown, itype, algorithm, NULL, cspHandle, 0, NULL, NULL, ka);
if (crtn == CSSM_OK && CFArrayGetCount((CFArrayRef)ka)) {
SecKeyRef sk = (SecKeyRef)CFArrayGetValueAtIndex((CFArrayRef)ka, 0);
CFRetain(sk);
CFRelease(ka);
return sk;
} else {
if (error) {
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, crtn ? crtn : CSSM_ERRCODE_INTERNAL_ERROR, NULL);
}
return NULL;
}
}
void
SecKeyGeneratePairAsync(CFDictionaryRef parametersWhichMightBeMutiable, dispatch_queue_t deliveryQueue,
SecKeyGeneratePairBlock result)
{
CFDictionaryRef parameters = CFDictionaryCreateCopy(NULL, parametersWhichMightBeMutiable);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
SecKeyRef publicKey = NULL;
SecKeyRef privateKey = NULL;
OSStatus status = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
dispatch_async(deliveryQueue, ^{
CFErrorRef error = NULL;
if (errSecSuccess != status) {
error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
}
result(publicKey, privateKey, error);
if (error) {
CFRelease(error);
}
if (publicKey) {
CFRelease(publicKey);
}
if (privateKey) {
CFRelease(privateKey);
}
CFRelease(parameters);
});
});
}
SecKeyRef
SecKeyDeriveFromPassword(CFStringRef password, CFDictionaryRef parameters, CFErrorRef *error)
{
char *thePassword = NULL;
CFIndex passwordLen;
uint8_t *salt = NULL;
size_t saltLen;
CCPBKDFAlgorithm algorithm;
uint rounds;
uint8_t *derivedKey = NULL;
size_t derivedKeyLen;
CFDataRef saltDictValue, algorithmDictValue;
if((saltDictValue = (CFDataRef) CFDictionaryGetValue(parameters, kSecAttrSalt)) == NULL) {
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecMissingAlgorithmParms, NULL);
return NULL;
}
derivedKeyLen = utilGetNumberFromCFDict(parameters, kSecAttrKeySizeInBits, 128);
derivedKeyLen /= 8;
algorithmDictValue = (CFDataRef) utilGetStringFromCFDict(parameters, kSecAttrPRF, kSecAttrPRFHmacAlgSHA256);
rounds = utilGetNumberFromCFDict(parameters, kSecAttrRounds, 0);
saltLen = CFDataGetLength(saltDictValue);
if((salt = (uint8_t *) malloc(saltLen)) == NULL) {
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL);
return NULL;
}
CFDataGetBytes(saltDictValue, CFRangeMake(0, saltLen), (UInt8 *) salt);
passwordLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(password), kCFStringEncodingUTF8) + 1;
if((thePassword = (char *) malloc(passwordLen)) == NULL) {
free(salt);
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL);
return NULL;
}
CFStringGetBytes(password, CFRangeMake(0, CFStringGetLength(password)), kCFStringEncodingUTF8, '?', FALSE, (UInt8*)thePassword, passwordLen, &passwordLen);
if((derivedKey = (uint8_t *) malloc(derivedKeyLen)) == NULL) {
free(salt);
bzero(thePassword, strlen(thePassword));
free(thePassword);
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL);
return NULL;
}
if(algorithmDictValue == NULL) {
algorithm = kCCPRFHmacAlgSHA1;
} else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA1)) {
algorithm = kCCPRFHmacAlgSHA1;
} else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA224)) {
algorithm = kCCPRFHmacAlgSHA224;
} else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA256)) {
algorithm = kCCPRFHmacAlgSHA256;
} else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA384)) {
algorithm = kCCPRFHmacAlgSHA384;
} else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA512)) {
algorithm = kCCPRFHmacAlgSHA512;
} else {
#warning "This else clause is here to prevent the use of unitialized variable, but really, this should return an error, without leaking."
algorithm = kCCPRFHmacAlgSHA1;
}
if(rounds == 0) {
rounds = 33333; }
if(CCKeyDerivationPBKDF(kCCPBKDF2, thePassword, passwordLen, salt, saltLen, algorithm, rounds, derivedKey, derivedKeyLen)) {
#warning "Aren't we leaking salt and thePassword when this fail???"
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInternalError, NULL);
return NULL;
}
free(salt);
bzero(thePassword, strlen(thePassword));
free(thePassword);
CFDataRef keyData = CFDataCreate(NULL, derivedKey, derivedKeyLen);
bzero(derivedKey, derivedKeyLen);
free(derivedKey);
SecKeyRef retval = SecKeyCreateFromData(parameters, keyData, error);
return retval;
}
CFDataRef
SecKeyWrapSymmetric(SecKeyRef keyToWrap, SecKeyRef wrappingKey, CFDictionaryRef parameters, CFErrorRef *error)
{
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecUnimplemented, NULL);
return NULL;
}
SecKeyRef
SecKeyUnwrapSymmetric(CFDataRef *keyToUnwrap, SecKeyRef unwrappingKey, CFDictionaryRef parameters, CFErrorRef *error)
{
*error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecUnimplemented, NULL);
return NULL;
}
#define MAX_DIGEST_LEN (CC_SHA512_DIGEST_LENGTH)
#define MAX_OID_LEN (10)
#define DER_MAX_DIGEST_INFO_LEN (10 + MAX_DIGEST_LEN + MAX_OID_LEN)
static size_t DEREncodeDigestInfoPrefix(const SecAsn1Oid *oid,
size_t digestLength,
uint8_t *digestInfo,
size_t digestInfoLength)
{
size_t algIdLen = oid->Length + 4;
size_t topLen = algIdLen + digestLength + 4;
size_t totalLen = topLen + 2;
if (totalLen > digestInfoLength) {
return 0;
}
size_t ix = 0;
digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED);
digestInfo[ix++] = topLen;
digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED);
digestInfo[ix++] = algIdLen;
digestInfo[ix++] = SEC_ASN1_OBJECT_ID;
digestInfo[ix++] = oid->Length;
memcpy(&digestInfo[ix], oid->Data, oid->Length);
ix += oid->Length;
digestInfo[ix++] = SEC_ASN1_NULL;
digestInfo[ix++] = 0;
digestInfo[ix++] = SEC_ASN1_OCTET_STRING;
digestInfo[ix++] = digestLength;
return ix;
}
static OSStatus SecKeyGetDigestInfo(SecKeyRef key, const SecAsn1AlgId *algId,
const uint8_t *data, size_t dataLen, bool digestData,
uint8_t *digestInfo, size_t *digestInfoLen )
{
unsigned char *(*digestFcn)(const void *, CC_LONG, unsigned char *);
CFIndex keyAlgID = kSecNullAlgorithmID;
const SecAsn1Oid *digestOid;
size_t digestLen;
size_t offset = 0;
if ((algId->algorithm.Length == CSSMOID_RSA.Length) &&
!memcmp(algId->algorithm.Data, CSSMOID_RSA.Data,
algId->algorithm.Length - 1)) {
keyAlgID = kSecRSAAlgorithmID;
switch (algId->algorithm.Data[algId->algorithm.Length - 1]) {
#if 0
case 2:
digestFcn = CC_MD2;
digestLen = CC_MD2_DIGEST_LENGTH;
digestOid = &CSSMOID_MD2;
break;
case 3:
digestFcn = CC_MD4;
digestLen = CC_MD4_DIGEST_LENGTH;
digestOid = &CSSMOID_MD4;
break;
case 4:
digestFcn = CC_MD5;
digestLen = CC_MD5_DIGEST_LENGTH;
digestOid = &CSSMOID_MD5;
break;
#endif
case 5:
digestFcn = CC_SHA1;
digestLen = CC_SHA1_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA1;
break;
case 11:
digestFcn = CC_SHA256;
digestLen = CC_SHA256_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA256;
break;
case 12:
digestFcn = CC_SHA384;
digestLen = CC_SHA384_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA384;
break;
case 13:
digestFcn = CC_SHA512;
digestLen = CC_SHA512_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA512;
break;
case 14:
digestFcn = CC_SHA224;
digestLen = CC_SHA224_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA224;
break;
default:
secdebug("key", "unsupported rsa signature algorithm");
return errSecUnsupportedAlgorithm;
}
} else if ((algId->algorithm.Length == CSSMOID_ECDSA_WithSHA224.Length) &&
!memcmp(algId->algorithm.Data, CSSMOID_ECDSA_WithSHA224.Data,
algId->algorithm.Length - 1)) {
keyAlgID = kSecECDSAAlgorithmID;
switch (algId->algorithm.Data[algId->algorithm.Length - 1]) {
case 1:
digestFcn = CC_SHA224;
digestLen = CC_SHA224_DIGEST_LENGTH;
break;
case 2:
digestFcn = CC_SHA256;
digestLen = CC_SHA256_DIGEST_LENGTH;
break;
case 3:
digestFcn = CC_SHA384;
digestLen = CC_SHA384_DIGEST_LENGTH;
break;
case 4:
digestFcn = CC_SHA512;
digestLen = CC_SHA512_DIGEST_LENGTH;
break;
default:
secdebug("key", "unsupported ecdsa signature algorithm");
return errSecUnsupportedAlgorithm;
}
} else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_ECDSA_WithSHA1)) {
keyAlgID = kSecECDSAAlgorithmID;
digestFcn = CC_SHA1;
digestLen = CC_SHA1_DIGEST_LENGTH;
} else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_SHA1)) {
digestFcn = CC_SHA1;
digestLen = CC_SHA1_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA1;
} else if ((algId->algorithm.Length == CSSMOID_SHA224.Length) &&
!memcmp(algId->algorithm.Data, CSSMOID_SHA224.Data, algId->algorithm.Length - 1))
{
switch (algId->algorithm.Data[algId->algorithm.Length - 1]) {
case 4:
digestFcn = CC_SHA224;
digestLen = CC_SHA224_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA224;
break;
case 1:
digestFcn = CC_SHA256;
digestLen = CC_SHA256_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA256;
break;
case 2:
digestFcn = CC_SHA384;
digestLen = CC_SHA384_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA384;
break;
case 3:
digestFcn = CC_SHA512;
digestLen = CC_SHA512_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA512;
break;
default:
secdebug("key", "unsupported sha-2 signature algorithm");
return errSecUnsupportedAlgorithm;
}
} else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_MD5)) {
digestFcn = CC_MD5;
digestLen = CC_MD5_DIGEST_LENGTH;
digestOid = &CSSMOID_MD5;
} else {
secdebug("key", "unsupported digesting algorithm");
return errSecUnsupportedAlgorithm;
}
{
CFIndex supportedKeyAlgID = kSecNullAlgorithmID;
#if TARGET_OS_EMBEDDED
supportedKeyAlgID = SecKeyGetAlgorithmID(key);
#else
const CSSM_KEY* temporaryKey;
SecKeyGetCSSMKey(key, &temporaryKey);
CSSM_ALGORITHMS tempAlgorithm = temporaryKey->KeyHeader.AlgorithmId;
if (CSSM_ALGID_RSA == tempAlgorithm) {
supportedKeyAlgID = kSecRSAAlgorithmID;
} else if (CSSM_ALGID_ECDSA == tempAlgorithm) {
supportedKeyAlgID = kSecECDSAAlgorithmID;
}
#endif
if (keyAlgID == kSecNullAlgorithmID) {
keyAlgID = supportedKeyAlgID;
}
else if (keyAlgID != supportedKeyAlgID) {
return errSecUnsupportedAlgorithm;
}
}
switch(keyAlgID) {
case kSecRSAAlgorithmID:
offset = DEREncodeDigestInfoPrefix(digestOid, digestLen,
digestInfo, *digestInfoLen);
if (!offset)
return errSecBufferTooSmall;
break;
case kSecDSAAlgorithmID:
if (digestOid != &CSSMOID_SHA1)
return errSecUnsupportedAlgorithm;
break;
case kSecECDSAAlgorithmID:
break;
default:
secdebug("key", "unsupported signature algorithm");
return errSecUnsupportedAlgorithm;
}
if (digestData) {
if(dataLen>UINT32_MAX)
return errSecParam;
digestFcn(data, (CC_LONG)dataLen, &digestInfo[offset]);
*digestInfoLen = offset + digestLen;
} else {
if (dataLen != digestLen)
return errSecParam;
memcpy(&digestInfo[offset], data, dataLen);
*digestInfoLen = offset + dataLen;
}
return errSecSuccess;
}
OSStatus SecKeyVerifyDigest(
SecKeyRef key,
const SecAsn1AlgId *algId,
const uint8_t *digestData,
size_t digestDataLen,
const uint8_t *sig,
size_t sigLen)
{
size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN;
uint8_t digestInfo[digestInfoLength];
OSStatus status;
status = SecKeyGetDigestInfo(key, algId, digestData, digestDataLen, false ,
digestInfo, &digestInfoLength);
if (status)
return status;
return SecKeyRawVerify(key, kSecPaddingPKCS1,
digestInfo, digestInfoLength, sig, sigLen);
}
OSStatus SecKeySignDigest(
SecKeyRef key,
const SecAsn1AlgId *algId,
const uint8_t *digestData,
size_t digestDataLen,
uint8_t *sig,
size_t *sigLen)
{
size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN;
uint8_t digestInfo[digestInfoLength];
OSStatus status;
status = SecKeyGetDigestInfo(key, algId, digestData, digestDataLen, false,
digestInfo, &digestInfoLength);
if (status)
return status;
return SecKeyRawSign(key, kSecPaddingPKCS1,
digestInfo, digestInfoLength, sig, sigLen);
}
SecECNamedCurve SecECKeyGetNamedCurve(SecKeyRef key)
{
try {
SecPointer<KeyItem> keyItem(KeyItem::required(key));
switch (keyItem->key().header().LogicalKeySizeInBits) {
#if 0
case 192:
return kSecECCurveSecp192r1;
case 224:
return kSecECCurveSecp224r1;
#endif
case 256:
return kSecECCurveSecp256r1;
case 384:
return kSecECCurveSecp384r1;
case 521:
return kSecECCurveSecp521r1;
}
}
catch (...) {}
return kSecECCurveNone;
}
static inline CFDataRef _CFDataCreateReferenceFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range)
{
return CFDataCreateWithBytesNoCopy(allocator,
CFDataGetBytePtr(sourceData) + range.location, range.length,
kCFAllocatorNull);
}
static inline CFDataRef _CFDataCreateCopyFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range)
{
return CFDataCreate(allocator, CFDataGetBytePtr(sourceData) + range.location, range.length);
}
static inline bool _CFDataEquals(CFDataRef left, CFDataRef right)
{
return (left != NULL) &&
(right != NULL) &&
(CFDataGetLength(left) == CFDataGetLength(right)) &&
(0 == memcmp(CFDataGetBytePtr(left), CFDataGetBytePtr(right), (size_t)CFDataGetLength(left)));
}
#if ECDSA_DEBUG
void secdump(const unsigned char *data, unsigned long len)
{
unsigned long i;
char s[128];
char t[32];
s[0]=0;
for(i=0;i<len;i++)
{
if((i&0xf)==0) {
sprintf(t, "%04lx :", i);
strcat(s, t);
}
sprintf(t, " %02x", data[i]);
strcat(s, t);
if((i&0xf)==0xf) {
strcat(s, "\n");
syslog(LOG_NOTICE, s);
s[0]=0;
}
}
strcat(s, "\n");
syslog(LOG_NOTICE, s);
}
#endif
OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* publicBytes)
{
CFIndex keyAlgId;
#if TARGET_OS_EMBEDDED
keyAlgId = SecKeyGetAlgorithmID(key);
#else
keyAlgId = SecKeyGetAlgorithmId(key);
#endif
OSStatus ecStatus = errSecParam;
CFDataRef tempPublicData = NULL;
CFDataRef headerlessPublicData = NULL;
CFIndex headerLength = 0;
const UInt8* pData_Ptr = NULL;
if (kSecRSAAlgorithmID == keyAlgId)
{
return SecItemExport(key, kSecFormatBSAFE, 0, NULL, publicBytes);
}
if (kSecECDSAAlgorithmID == keyAlgId)
{
ecStatus = SecItemExport(key, kSecFormatOpenSSL, 0, NULL, &tempPublicData);
if(ecStatus != errSecSuccess)
{
secdebug("key", "SecKeyCopyPublicBytes: SecItemExport error (%d) for ECDSA public key %p",
ecStatus, (uintptr_t)key);
return ecStatus;
}
pData_Ptr = CFDataGetBytePtr(tempPublicData);
if (*pData_Ptr != 0x30)
{
secdebug("key", "SecKeyCopyPublicBytes: exported data is invalid");
if (NULL != tempPublicData)
CFRelease(tempPublicData);
ecStatus = errSecParam;
return ecStatus;
}
pData_Ptr++;
pData_Ptr += (*pData_Ptr & 0x80) ? ((*pData_Ptr & 0x7F) + 1) : 1;
if (*pData_Ptr != 0x30)
{
secdebug("key", "SecKeyCopyPublicBytes: Could not find the key sequence");
if (NULL != tempPublicData)
CFRelease(tempPublicData);
ecStatus = errSecParam;
return ecStatus;
}
pData_Ptr += 12;
pData_Ptr += (((int)*pData_Ptr) + 1);
if (*pData_Ptr != 0x03)
{
secdebug("key", "SecKeyCopyPublicBytes: Invalid key structure");
if (NULL != tempPublicData)
CFRelease(tempPublicData);
ecStatus = errSecParam;
return ecStatus;
}
pData_Ptr++;
pData_Ptr += (*pData_Ptr & 0x80) ? ((*pData_Ptr & 0x7F) + 1) : 1;
pData_Ptr++;
headerLength = (CFIndex)(((intptr_t)pData_Ptr) - ((intptr_t)CFDataGetBytePtr(tempPublicData)));
headerlessPublicData = _CFDataCreateCopyFromRange(kCFAllocatorDefault,
tempPublicData, CFRangeMake(headerLength, CFDataGetLength(tempPublicData) - headerLength));
if (!headerlessPublicData)
{
printf("SecKeyCopyPublicBytes: headerlessPublicData is nil (1)\n");
if (NULL != tempPublicData)
CFRelease(tempPublicData);
ecStatus = errSecParam;
return ecStatus;
}
if (publicBytes)
{
*publicBytes = headerlessPublicData;
}
ecStatus = errSecSuccess;
if (NULL != tempPublicData)
CFRelease(tempPublicData);
return ecStatus;
}
return errSecParam;
}
CFDataRef SecECKeyCopyPublicBits(SecKeyRef key)
{
CFDataRef exportedKey;
if(SecKeyCopyPublicBytes(key, &exportedKey) != errSecSuccess) {
exportedKey = NULL;
}
return exportedKey;
}
SecKeyRef SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef publicBytes)
{
SecExternalFormat externalFormat = kSecFormatOpenSSL;
SecExternalItemType externalItemType = kSecItemTypePublicKey;
CFDataRef workingData = NULL;
CFArrayRef outArray = NULL;
SecKeyRef retVal = NULL;
if (kSecRSAAlgorithmID == algorithmID) {
externalFormat = kSecFormatBSAFE;
workingData = _CFDataCreateReferenceFromRange(kCFAllocatorDefault, publicBytes, CFRangeMake(0, CFDataGetLength(publicBytes)));
} else if (kSecECDSAAlgorithmID == algorithmID) {
CFMutableDataRef tempData;
uint8 headerBytes[] = { 0x30,0x59,0x30,0x13,0x06,0x07,0x2a,0x86,
0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,
0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03,
0x42,0x00 };
tempData = CFDataCreateMutable(kCFAllocatorDefault, 0);
CFDataAppendBytes(tempData, headerBytes, sizeof(headerBytes));
CFDataAppendBytes(tempData, CFDataGetBytePtr(publicBytes), CFDataGetLength(publicBytes));
workingData = tempData;
}
if(SecItemImport(workingData, NULL, &externalFormat, &externalItemType, 0, NULL, NULL, &outArray) != errSecSuccess) {
goto cleanup;
}
if(!outArray || CFArrayGetCount(outArray) == 0) {
goto cleanup;
}
retVal = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
CFRetain(retVal);
cleanup:
if(workingData) CFRelease(workingData);
if(outArray) CFRelease(outArray);
return retVal;
}
SecKeyRef SecKeyCreateRSAPublicKey(CFAllocatorRef allocator,
const uint8_t *keyData, CFIndex keyDataLength,
SecKeyEncoding encoding)
{
CFDataRef pubKeyData = NULL;
if(kSecKeyEncodingPkcs1 == encoding) {
pubKeyData = CFDataCreate(allocator, keyData, keyDataLength);
} else if(kSecKeyEncodingApplePkcs1 == encoding) {
return NULL;
} else if(kSecKeyEncodingRSAPublicParams == encoding) {
SecRSAPublicKeyParams *params = (SecRSAPublicKeyParams *)keyData;
DERSize m_size = params->modulusLength;
DERSize e_size = params->exponentLength;
const DERSize seq_size = DERLengthOfItem(ASN1_INTEGER, m_size) +
DERLengthOfItem(ASN1_INTEGER, e_size);
const DERSize result_size = DERLengthOfItem(ASN1_SEQUENCE, seq_size);
DERSize r_size, remaining_size = result_size;
DERReturn drtn;
CFMutableDataRef pkcs1 = CFDataCreateMutable(allocator, result_size);
if (pkcs1 == NULL) {
return NULL;
}
CFDataSetLength(pkcs1, result_size);
uint8_t *bytes = CFDataGetMutableBytePtr(pkcs1);
*bytes++ = ASN1_CONSTR_SEQUENCE;
remaining_size--;
r_size = 4;
drtn = DEREncodeLength(seq_size, bytes, &r_size);
if (r_size <= remaining_size) {
bytes += r_size;
remaining_size -= r_size;
}
r_size = remaining_size;
drtn = DEREncodeItem(ASN1_INTEGER, m_size, (const DERByte *)params->modulus, (DERByte *)bytes, &r_size);
if (r_size <= remaining_size) {
bytes += r_size;
remaining_size -= r_size;
}
r_size = remaining_size;
drtn = DEREncodeItem(ASN1_INTEGER, e_size, (const DERByte *)params->exponent, (DERByte *)bytes, &r_size);
pubKeyData = pkcs1;
} else {
return NULL;
}
SecKeyRef publicKey = SecKeyCreateFromPublicData(allocator, kSecRSAAlgorithmID, pubKeyData);
CFRelease(pubKeyData);
return publicKey;
}
#if !TARGET_OS_EMBEDDED
static
OSStatus _SecKeyCopyRSAPublicModulusAndExponent(SecKeyRef key, CFDataRef *modulus, CFDataRef *exponent)
{
const CSSM_KEY *pubKey;
const CSSM_KEYHEADER *hdr;
CSSM_DATA pubKeyBlob;
OSStatus result;
result = SecKeyGetCSSMKey(key, &pubKey);
if(result != errSecSuccess) {
return result;
}
hdr = &pubKey->KeyHeader;
if(hdr->KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) {
return errSSLInternal;
}
if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
return errSSLInternal;
}
switch(hdr->BlobType) {
case CSSM_KEYBLOB_RAW:
pubKeyBlob.Length = pubKey->KeyData.Length;
pubKeyBlob.Data = pubKey->KeyData.Data;
break;
case CSSM_KEYBLOB_REFERENCE:
default:
return errSSLInternal;
}
assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
DERItem keyItem = {(DERByte *)pubKeyBlob.Data, pubKeyBlob.Length};
DERRSAPubKeyPKCS1 decodedKey;
if(DERParseSequence(&keyItem, DERNumRSAPubKeyPKCS1ItemSpecs,
DERRSAPubKeyPKCS1ItemSpecs,
&decodedKey, sizeof(decodedKey)) != DR_Success) {
return errSecDecode;
}
if(modulus) {
*modulus = CFDataCreate(kCFAllocatorDefault, decodedKey.modulus.data, decodedKey.modulus.length);
if(*modulus == NULL) {
return errSecDecode;
}
}
if(exponent) {
*exponent = CFDataCreate(kCFAllocatorDefault, decodedKey.pubExponent.data, decodedKey.pubExponent.length);
if(*exponent == NULL) {
return errSecDecode;
}
}
return errSecSuccess;
}
#endif
CFDataRef SecKeyCopyModulus(SecKeyRef key)
{
#if TARGET_OS_EMBEDDED
ccrsa_pub_ctx_t pubkey;
pubkey.pub = key->key;
size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey));
CFAllocatorRef allocator = CFGetAllocator(key);
CFMutableDataRef modulusData = CFDataCreateMutable(allocator, m_size);
if (modulusData == NULL)
return NULL;
CFDataSetLength(modulusData, m_size);
ccn_write_uint(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey), m_size, CFDataGetMutableBytePtr(modulusData));
#else
CFDataRef modulusData;
OSStatus status = _SecKeyCopyRSAPublicModulusAndExponent(key, &modulusData, NULL);
if(status != errSecSuccess) {
modulusData = NULL;
}
#endif
return modulusData;
}
CFDataRef SecKeyCopyExponent(SecKeyRef key)
{
#if TARGET_OS_EMBEDDED
ccrsa_pub_ctx_t pubkey;
pubkey.pub = key->key;
size_t e_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_e(pubkey));
CFAllocatorRef allocator = CFGetAllocator(key);
CFMutableDataRef exponentData = CFDataCreateMutable(allocator, e_size);
if (exponentData == NULL)
return NULL;
CFDataSetLength(exponentData, e_size);
ccn_write_uint(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey), e_size, CFDataGetMutableBytePtr(exponentData));
#else
CFDataRef exponentData;
OSStatus status = _SecKeyCopyRSAPublicModulusAndExponent(key, NULL, &exponentData);
if(status != errSecSuccess) {
exponentData = NULL;
}
#endif
return exponentData;
}
SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey) {
OSStatus status = errSecParam;
CFDataRef serializedPublic = NULL;
status = SecKeyCopyPublicBytes(privateKey, &serializedPublic);
if ((status == errSecSuccess) && (serializedPublic != NULL)) {
SecKeyRef publicKeyRef = SecKeyCreateFromPublicData(kCFAllocatorDefault, SecKeyGetAlgorithmId(privateKey), serializedPublic);
CFRelease(serializedPublic);
if (publicKeyRef != NULL) {
return publicKeyRef;
}
}
const void *keys[] = { kSecClass, kSecValueRef, kSecReturnAttributes };
const void *values[] = { kSecClassKey, privateKey, kCFBooleanTrue };
CFDictionaryRef query= CFDictionaryCreate(NULL, keys, values,
(sizeof(values) / sizeof(*values)),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFTypeRef foundItem = NULL;
status = SecItemCopyMatching(query, &foundItem);
if (status == errSecSuccess) {
if (CFGetTypeID(foundItem) == CFDictionaryGetTypeID()) {
CFMutableDictionaryRef query2 = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(query2, kSecClass, kSecClassKey);
CFDictionaryAddValue(query2, kSecAttrKeyClass, kSecAttrKeyClassPublic);
CFDictionaryAddValue(query2, kSecAttrApplicationLabel, CFDictionaryGetValue((CFDictionaryRef)foundItem, kSecAttrApplicationLabel));
CFDictionaryAddValue(query2, kSecReturnRef, kCFBooleanTrue);
CFTypeRef foundKey = NULL;
status = SecItemCopyMatching(query2, &foundKey);
if (status == errSecSuccess) {
if (CFGetTypeID(foundKey) == SecKeyGetTypeID()) {
CFRelease(query);
CFRelease(query2);
CFRelease(foundItem);
return (SecKeyRef)foundKey;
} else {
status = errSecItemNotFound;
}
}
CFRelease(query2);
} else {
status = errSecItemNotFound;
}
CFRelease(foundItem);
}
CFRelease(query);
return NULL;
}