+ { DER_OFFSET(DERECPrivateKeyPublicKey, bitString),
+ ASN1_BIT_STRING,
+ DER_DEC_NO_OPTS | DER_ENC_NO_OPTS },
+};
+static const DERSize DERNumECPrivateKeyPublicKeyItemSpecs =
+sizeof(DERECPrivateKeyPublicKeyItemSpecs) / sizeof(DERItemSpec);
+
+static CFDataRef
+SecCDSAKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error) {
+
+ BEGIN_SECKEYAPI(CFDataRef, NULL)
+
+ result = NULL;
+ const CssmKey::Header header = key->key->unverifiedKeyHeader();
+ CFRef<CFDataRef> keyData;
+ switch (header.algorithm()) {
+ case CSSM_ALGID_RSA:
+ MacOSError::check(SecItemExport(key, kSecFormatOpenSSL, 0, NULL, keyData.take()));
+ break;
+ case CSSM_ALGID_ECDSA: {
+ MacOSError::check(SecItemExport(key, kSecFormatOpenSSL, 0, NULL, keyData.take()));
+ if (header.keyClass() == CSSM_KEYCLASS_PRIVATE_KEY) {
+ // Convert DER format into x9.63 format, which is expected for exported key.
+ DERItem keyItem = { (DERByte *)CFDataGetBytePtr(keyData), int_cast<CFIndex, DERSize>(CFDataGetLength(keyData)) };
+ DERECPrivateKey privateKey;
+ DERECPrivateKeyPublicKey privateKeyPublicKey;
+ DERByte numUnused;
+ DERItem pubKeyItem;
+ if (DERParseSequence(&keyItem, DERNumECPrivateKeyItemSpecs, DERECPrivateKeyItemSpecs,
+ &privateKey, sizeof(privateKey)) == DR_Success &&
+ DERParseSequenceContent(&privateKey.publicKey, DERNumECPrivateKeyPublicKeyItemSpecs,
+ DERECPrivateKeyPublicKeyItemSpecs,
+ &privateKeyPublicKey, sizeof(privateKeyPublicKey)) == DR_Success &&
+ DERParseBitString(&privateKeyPublicKey.bitString, &pubKeyItem, &numUnused) == DR_Success) {
+ CFRef<CFMutableDataRef> key = CFDataCreateMutable(kCFAllocatorDefault,
+ pubKeyItem.length + privateKey.privateKey.length);
+ CFDataSetLength(key, pubKeyItem.length + privateKey.privateKey.length);
+ CFDataReplaceBytes(key, CFRangeMake(0, pubKeyItem.length), pubKeyItem.data, pubKeyItem.length);
+ CFDataReplaceBytes(key, CFRangeMake(pubKeyItem.length, privateKey.privateKey.length),
+ privateKey.privateKey.data, privateKey.privateKey.length);
+ keyData = key.as<CFDataRef>();
+ }
+ }
+ break;
+ }
+ default:
+ MacOSError::throwMe(errSecUnimplemented);
+ }
+
+ if (header.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY) {
+ result = SecCDSAKeyCopyPublicKeyDataFromSubjectInfo(keyData);
+ } else {
+ result = keyData.yield();
+ }
+
+ END_SECKEYAPI
+}
+
+static CFDataRef SecCDSAKeyCopyLabel(SecKeyRef key) {
+ CFDataRef label = NULL;
+ if (key->key->isPersistent()) {
+ UInt32 tags[] = { kSecKeyLabel }, formats[] = { CSSM_DB_ATTRIBUTE_FORMAT_BLOB };
+ SecKeychainAttributeInfo info = { 1, tags, formats };
+ SecKeychainAttributeList *list = NULL;
+ key->key->getAttributesAndData(&info, NULL, &list, NULL, NULL);
+ if (list->count == 1) {
+ SecKeychainAttribute *attr = list->attr;
+ label = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)attr->data, (CFIndex)attr->length);
+ }
+ key->key->freeAttributesAndData(list, NULL);
+ }
+ return label;
+}
+
+static CFDictionaryRef
+SecCDSAKeyCopyAttributeDictionary(SecKeyRef key) {
+
+ CFErrorRef *error = NULL;
+ BEGIN_SECKEYAPI(CFDictionaryRef, NULL)
+
+ CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ CFDictionarySetValue(dict, kSecClass, kSecClassKey);
+
+ const CssmKey::Header header = key->key->unverifiedKeyHeader();
+ CFIndex sizeValue = header.LogicalKeySizeInBits;
+ CFRef<CFNumberRef> sizeInBits = CFNumberCreate(NULL, kCFNumberCFIndexType, &sizeValue);
+ CFDictionarySetValue(dict, kSecAttrKeySizeInBits, sizeInBits);
+ CFDictionarySetValue(dict, kSecAttrEffectiveKeySize, sizeInBits);
+
+ CFRef<CFDataRef> label = SecCDSAKeyCopyLabel(key);
+ if (!label) {
+ // For floating keys, calculate label as SHA1 of pubkey bytes.
+ CFRef<CFDataRef> pubKeyBlob;
+ if (SecCDSAKeyCopyPublicBytes(key, pubKeyBlob.take()) == errSecSuccess) {
+ uint8_t pubKeyHash[CC_SHA1_DIGEST_LENGTH];
+ CC_SHA1(CFDataGetBytePtr(pubKeyBlob), CC_LONG(CFDataGetLength(pubKeyBlob)), pubKeyHash);
+ label.take(CFDataCreate(kCFAllocatorDefault, pubKeyHash, sizeof(pubKeyHash)));
+ }
+ }
+
+ if (label) {
+ CFDictionarySetValue(dict, kSecAttrApplicationLabel, label);
+ }
+
+ CSSM_KEYATTR_FLAGS attrs = header.attributes();
+ CFDictionarySetValue(dict, kSecAttrIsPermanent, (attrs & CSSM_KEYATTR_PERMANENT) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrIsPrivate, (attrs & CSSM_KEYATTR_PRIVATE) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrIsModifiable, (attrs & CSSM_KEYATTR_MODIFIABLE) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrIsSensitive, (attrs & CSSM_KEYATTR_SENSITIVE) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrIsExtractable, (attrs & CSSM_KEYATTR_EXTRACTABLE) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrWasAlwaysSensitive, (attrs & CSSM_KEYATTR_ALWAYS_SENSITIVE) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrWasNeverExtractable, (attrs & CSSM_KEYATTR_NEVER_EXTRACTABLE) ? kCFBooleanTrue : kCFBooleanFalse);
+
+ CFDictionarySetValue(dict, kSecAttrCanEncrypt, (header.useFor(CSSM_KEYUSE_ENCRYPT)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanDecrypt, (header.useFor(CSSM_KEYUSE_DECRYPT)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanSign, (header.useFor(CSSM_KEYUSE_SIGN)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanVerify, (header.useFor(CSSM_KEYUSE_VERIFY)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanSignRecover, (header.useFor(CSSM_KEYUSE_SIGN_RECOVER)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanVerifyRecover, (header.useFor(CSSM_KEYUSE_VERIFY_RECOVER)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanWrap, (header.useFor(CSSM_KEYUSE_WRAP)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanUnwrap, (header.useFor(CSSM_KEYUSE_UNWRAP)) ? kCFBooleanTrue : kCFBooleanFalse);
+ CFDictionarySetValue(dict, kSecAttrCanDerive, (header.useFor(CSSM_KEYUSE_DERIVE)) ? kCFBooleanTrue : kCFBooleanFalse);
+
+ switch (header.keyClass()) {
+ case CSSM_KEYCLASS_PUBLIC_KEY:
+ CFDictionarySetValue(dict, kSecAttrKeyClass, kSecAttrKeyClassPublic);
+ break;
+ case CSSM_KEYCLASS_PRIVATE_KEY:
+ CFDictionarySetValue(dict, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
+ break;
+ }
+
+ switch (header.algorithm()) {
+ case CSSM_ALGID_RSA:
+ CFDictionarySetValue(dict, kSecAttrKeyType, kSecAttrKeyTypeRSA);
+ break;
+ case CSSM_ALGID_ECDSA:
+ CFDictionarySetValue(dict, kSecAttrKeyType, kSecAttrKeyTypeECDSA);
+ break;
+ }
+
+ CFRef<CFDataRef> keyData;
+ if (SecItemExport(key, kSecFormatOpenSSL, 0, NULL, keyData.take()) == errSecSuccess) {
+ CFDictionarySetValue(dict, kSecValueData, keyData);
+ }
+
+ if (header.algorithm() == CSSM_ALGID_RSA && header.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY &&
+ header.blobType() == CSSM_KEYBLOB_RAW) {
+ const CssmData &keyData = key->key->key()->keyData();
+ DERItem keyItem = { static_cast<DERByte *>(keyData.data()), keyData.length() };
+ DERRSAPubKeyPKCS1 decodedKey;
+ if (DERParseSequence(&keyItem, DERNumRSAPubKeyPKCS1ItemSpecs,
+ DERRSAPubKeyPKCS1ItemSpecs,
+ &decodedKey, sizeof(decodedKey)) == DR_Success) {
+ CFRef<CFDataRef> modulus = CFDataCreate(kCFAllocatorDefault, decodedKey.modulus.data,
+ decodedKey.modulus.length);
+ CFDictionarySetValue(dict, CFSTR("_rsam"), modulus);
+ CFRef<CFDataRef> exponent = CFDataCreate(kCFAllocatorDefault, decodedKey.pubExponent.data,
+ decodedKey.pubExponent.length);
+ CFDictionarySetValue(dict, CFSTR("_rsae"), exponent);
+ }
+ }
+
+ result = dict;
+
+ END_SECKEYAPI
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-const-variable"
+static CSSM_DB_NAME_ATTR(kInfoKeyLabel, kSecKeyLabel, (char*) "Label", 0, NULL, BLOB);
+#pragma clang diagnostic pop
+
+static SecKeyRef SecCDSAKeyCopyPublicKey(SecKeyRef privateKey) {
+ CFErrorRef *error;
+ BEGIN_SECKEYAPI(SecKeyRef, NULL)
+
+ result = NULL;
+ KeyItem *key = privateKey->key;
+ CFRef<CFDataRef> label = SecCDSAKeyCopyLabel(privateKey);
+ if (label) {
+ // Lookup public key in the database.
+ DbUniqueRecord uniqueId;
+ SSDb ssDb(dynamic_cast<SSDbImpl *>(&(*key->keychain()->database())));
+ SSDbCursor dbCursor(ssDb, 1);
+ dbCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
+ dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, CssmData(CFDataRef(label)));
+ if (dbCursor->next(NULL, NULL, uniqueId)) {
+ Item publicKey = key->keychain()->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, uniqueId);
+ result = reinterpret_cast<SecKeyRef>(publicKey->handle());
+ }
+ }
+
+ if (result == NULL && key->publicKey()) {
+ SecPointer<KeyItem> publicKey(new KeyItem(key->publicKey()));
+ result = reinterpret_cast<SecKeyRef>(publicKey->handle());
+ }
+
+ END_SECKEYAPI
+}
+
+static KeyItem *SecCDSAKeyPrepareParameters(SecKeyRef key, SecKeyOperationType &operation, SecKeyAlgorithm algorithm,
+ CSSM_ALGORITHMS &baseAlgorithm, CSSM_ALGORITHMS &secondaryAlgorithm,
+ CSSM_ALGORITHMS &paddingAlgorithm, CFIndex &inputSizeLimit) {
+ KeyItem *keyItem = key->key;
+ CSSM_KEYCLASS keyClass = keyItem->key()->header().keyClass();
+ baseAlgorithm = keyItem->key()->header().algorithm();
+ switch (baseAlgorithm) {
+ case CSSM_ALGID_RSA:
+ if ((keyClass == CSSM_KEYCLASS_PRIVATE_KEY && operation == kSecKeyOperationTypeSign) ||
+ (keyClass == CSSM_KEYCLASS_PUBLIC_KEY && operation == kSecKeyOperationTypeVerify)) {
+ if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureRaw)) {
+ secondaryAlgorithm = CSSM_ALGID_NONE;
+ paddingAlgorithm = CSSM_PADDING_NONE;
+ inputSizeLimit = 0;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw)) {
+ secondaryAlgorithm = CSSM_ALGID_NONE;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = -11;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1)) {
+ secondaryAlgorithm = CSSM_ALGID_SHA1;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = 20;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA224)) {
+ secondaryAlgorithm = CSSM_ALGID_SHA224;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = 224 / 8;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256)) {
+ secondaryAlgorithm = CSSM_ALGID_SHA256;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = 256 / 8;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384)) {
+ secondaryAlgorithm = CSSM_ALGID_SHA384;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = 384 / 8;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512)) {
+ secondaryAlgorithm = CSSM_ALGID_SHA512;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = 512 / 8;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSASignatureDigestPKCS1v15MD5)) {
+ secondaryAlgorithm = CSSM_ALGID_MD5;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = 16;
+ } else {
+ return NULL;
+ }
+ } else if ((keyClass == CSSM_KEYCLASS_PRIVATE_KEY && operation == kSecKeyOperationTypeDecrypt) ||
+ (keyClass == CSSM_KEYCLASS_PUBLIC_KEY && operation == kSecKeyOperationTypeEncrypt)) {
+ if (CFEqual(algorithm, kSecKeyAlgorithmRSAEncryptionRaw)) {
+ secondaryAlgorithm = CSSM_ALGID_NONE;
+ paddingAlgorithm = CSSM_PADDING_NONE;
+ inputSizeLimit = 0;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmRSAEncryptionPKCS1)) {
+ secondaryAlgorithm = CSSM_ALGID_NONE;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ inputSizeLimit = operation == kSecKeyOperationTypeEncrypt ? -11 : 0;
+ } else {
+ return NULL;
+ }
+ } else if (keyClass == CSSM_KEYCLASS_PUBLIC_KEY && operation == kSecKeyOperationTypeDecrypt &&
+ CFEqual(algorithm, kSecKeyAlgorithmRSAEncryptionRaw)) {
+ // Raw RSA decryption is identical to raw RSA encryption, so lets use encryption instead of decryption,
+ // because CDSA keys refuses to perform decrypt using public key.
+ operation = kSecKeyOperationTypeEncrypt;
+ secondaryAlgorithm = CSSM_ALGID_NONE;
+ paddingAlgorithm = CSSM_PADDING_NONE;
+ inputSizeLimit = 0;
+ } else {
+ return NULL;
+ }
+ break;
+ case CSSM_ALGID_ECDSA:
+ if ((keyClass == CSSM_KEYCLASS_PRIVATE_KEY && operation == kSecKeyOperationTypeSign) ||
+ (keyClass == CSSM_KEYCLASS_PUBLIC_KEY && operation == kSecKeyOperationTypeVerify)) {
+ if (CFEqual(algorithm, kSecKeyAlgorithmECDSASignatureRFC4754)) {
+ secondaryAlgorithm = CSSM_ALGID_NONE;
+ paddingAlgorithm = CSSM_PADDING_SIGRAW;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmECDSASignatureDigestX962)) {
+ secondaryAlgorithm = CSSM_ALGID_NONE;
+ paddingAlgorithm = CSSM_PADDING_PKCS1;
+ } else {
+ return NULL;
+ }
+ } else if (keyClass == CSSM_KEYCLASS_PRIVATE_KEY && operation == kSecKeyOperationTypeKeyExchange) {
+ if (CFEqual(algorithm,kSecKeyAlgorithmECDHKeyExchangeStandard) ||
+ CFEqual(algorithm, kSecKeyAlgorithmECDHKeyExchangeCofactor)) {
+ baseAlgorithm = CSSM_ALGID_ECDH;
+ } else if (CFEqual(algorithm, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1) ||
+ CFEqual(algorithm, kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA1)) {
+ baseAlgorithm = CSSM_ALGID_ECDH_X963_KDF;
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+ break;
+ default:
+ MacOSError::throwMe(errSecParam);
+ }
+ return keyItem;
+}
+
+static CFDataRef
+SecCDSAKeyCopyPaddedPlaintext(SecKeyRef key, CFDataRef plaintext, SecKeyAlgorithm algorithm) {
+ CFIndex blockSize = key->key->key().header().LogicalKeySizeInBits / 8;
+ CFIndex plaintextLength = CFDataGetLength(plaintext);
+ if ((algorithm == kSecKeyAlgorithmRSAEncryptionRaw || algorithm == kSecKeyAlgorithmRSASignatureRaw)
+ && plaintextLength < blockSize) {
+ // Pre-pad with zeroes.
+ CFMutableDataRef result(CFDataCreateMutable(kCFAllocatorDefault, blockSize));
+ CFDataSetLength(result, blockSize);
+ CFDataReplaceBytes(result, CFRangeMake(blockSize - plaintextLength, plaintextLength),
+ CFDataGetBytePtr(plaintext), plaintextLength);
+ return result;
+ } else {
+ return CFDataRef(CFRetain(plaintext));
+ }
+}
+
+static CFTypeRef SecCDSAKeyCopyOperationResult(SecKeyRef key, SecKeyOperationType operation, SecKeyAlgorithm algorithm,
+ CFArrayRef allAlgorithms, SecKeyOperationMode mode,
+ CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
+ BEGIN_SECKEYAPI(CFTypeRef, kCFNull)
+ CFIndex inputSizeLimit = 0;
+ CSSM_ALGORITHMS baseAlgorithm, secondaryAlgorithm, paddingAlgorithm;
+ KeyItem *keyItem = SecCDSAKeyPrepareParameters(key, operation, algorithm, baseAlgorithm, secondaryAlgorithm, paddingAlgorithm, inputSizeLimit);
+ if (keyItem == NULL) {
+ // Operation/algorithm/key combination is not supported.
+ return kCFNull;
+ } else if (mode == kSecKeyOperationModeCheckIfSupported) {
+ // Operation is supported and caller wants to just know that.
+ return kCFBooleanTrue;
+ } else if (baseAlgorithm == CSSM_ALGID_RSA) {
+ if (inputSizeLimit <= 0) {
+ inputSizeLimit += SecCDSAKeyGetBlockSize(key);
+ }
+ if (CFDataGetLength((CFDataRef)in1) > inputSizeLimit) {
+ MacOSError::throwMe(errSecParam);
+ }
+ }
+
+ switch (operation) {
+ case kSecKeyOperationTypeSign: {
+ CssmClient::Sign signContext(keyItem->csp(), baseAlgorithm, secondaryAlgorithm);
+ signContext.key(keyItem->key());
+ signContext.cred(keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_SIGN, key->credentialType));
+ signContext.add(CSSM_ATTRIBUTE_PADDING, paddingAlgorithm);
+ CFRef<CFDataRef> input = SecCDSAKeyCopyPaddedPlaintext(key, CFRef<CFDataRef>::check(in1, errSecParam), algorithm);
+ CssmAutoData signature(signContext.allocator());
+ signContext.sign(CssmData(CFDataRef(input)), signature.get());
+ result = CFDataCreate(NULL, static_cast<const UInt8 *>(signature.data()), CFIndex(signature.length()));
+ break;
+ }
+ case kSecKeyOperationTypeVerify: {
+ CssmClient::Verify verifyContext(keyItem->csp(), baseAlgorithm, secondaryAlgorithm);
+ verifyContext.key(keyItem->key());
+ verifyContext.cred(keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ANY, key->credentialType));
+ verifyContext.add(CSSM_ATTRIBUTE_PADDING, paddingAlgorithm);
+ CFRef<CFDataRef> input = SecCDSAKeyCopyPaddedPlaintext(key, CFRef<CFDataRef>::check(in1, errSecParam), algorithm);
+ verifyContext.verify(CssmData(CFDataRef(input)), CssmData(CFRef<CFDataRef>::check(in2, errSecParam)));
+ result = kCFBooleanTrue;
+ break;
+ }
+ case kSecKeyOperationTypeEncrypt: {
+ CssmClient::Encrypt encryptContext(keyItem->csp(), baseAlgorithm);
+ encryptContext.key(keyItem->key());
+ encryptContext.padding(paddingAlgorithm);
+ encryptContext.cred(keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ENCRYPT, key->credentialType));
+ CFRef<CFDataRef> input = SecCDSAKeyCopyPaddedPlaintext(key, CFRef<CFDataRef>::check(in1, errSecParam), algorithm);
+ CssmAutoData output(encryptContext.allocator()), remainingData(encryptContext.allocator());
+ size_t length = encryptContext.encrypt(CssmData(CFDataRef(input)), output.get(), remainingData.get());
+ result = CFDataCreateMutable(kCFAllocatorDefault, output.length() + remainingData.length());
+ CFDataAppendBytes(CFMutableDataRef(result), static_cast<const UInt8 *>(output.data()), output.length());
+ CFDataAppendBytes(CFMutableDataRef(result), static_cast<const UInt8 *>(remainingData.data()), remainingData.length());
+ CFDataSetLength(CFMutableDataRef(result), length);
+ break;
+ }
+ case kSecKeyOperationTypeDecrypt: {
+ CssmClient::Decrypt decryptContext(keyItem->csp(), baseAlgorithm);
+ decryptContext.key(keyItem->key());
+ decryptContext.padding(paddingAlgorithm);
+ decryptContext.cred(keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_DECRYPT, key->credentialType));
+ CssmAutoData output(decryptContext.allocator()), remainingData(decryptContext.allocator());
+ size_t length = decryptContext.decrypt(CssmData(CFRef<CFDataRef>::check(in1, errSecParam)),
+ output.get(), remainingData.get());
+ result = CFDataCreateMutable(kCFAllocatorDefault, output.length() + remainingData.length());
+ CFDataAppendBytes(CFMutableDataRef(result), static_cast<const UInt8 *>(output.data()), output.length());
+ CFDataAppendBytes(CFMutableDataRef(result), static_cast<const UInt8 *>(remainingData.data()), remainingData.length());
+ CFDataSetLength(CFMutableDataRef(result), length);
+ break;
+ }
+ case kSecKeyOperationTypeKeyExchange: {
+ CFIndex requestedLength = 0;
+ CssmData sharedInfo;
+ switch (baseAlgorithm) {
+ case CSSM_ALGID_ECDH:
+ requestedLength = (keyItem->key().header().LogicalKeySizeInBits + 7) / 8;
+ break;
+ case CSSM_ALGID_ECDH_X963_KDF:
+ CFDictionaryRef params = CFRef<CFDictionaryRef>::check(in2, errSecParam);
+ CFTypeRef value = params ? CFDictionaryGetValue(params, kSecKeyKeyExchangeParameterRequestedSize) : NULL;
+ if (value == NULL || CFGetTypeID(value) != CFNumberGetTypeID() ||
+ !CFNumberGetValue(CFNumberRef(value), kCFNumberCFIndexType, &requestedLength)) {
+ MacOSError::throwMe(errSecParam);
+ }
+ value = CFDictionaryGetValue(params, kSecKeyKeyExchangeParameterSharedInfo);
+ if (value != NULL && CFGetTypeID(value) == CFDataGetTypeID()) {
+ sharedInfo = CssmData(CFDataRef(value));
+ }
+ break;
+ }
+
+ CssmClient::DeriveKey derive(keyItem->csp(), baseAlgorithm, CSSM_ALGID_AES, uint32(requestedLength * 8));
+ derive.key(keyItem->key());
+ derive.cred(keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_DERIVE, kSecCredentialTypeDefault));
+ derive.salt(sharedInfo);
+ CssmData param(CFRef<CFDataRef>::check(in1, errSecParam));
+ Key derivedKey = derive(¶m, KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE));
+
+ // Export raw data of newly derived key (by wrapping with an empty key).
+ CssmClient::WrapKey wrapper(keyItem->csp(), CSSM_ALGID_NONE);
+ Key wrappedKey = wrapper(derivedKey);
+ result = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)wrappedKey->data(), CFIndex(wrappedKey->length()));
+ break;
+ }
+ default:
+ break;
+ }
+
+ END_SECKEYAPI
+}
+
+static Boolean SecCDSAKeyIsEqual(SecKeyRef key1, SecKeyRef key2) {
+ CFErrorRef *error;
+ BEGIN_SECKEYAPI(Boolean, false)
+
+ result = key1->key->equal(*key2->key);
+
+ END_SECKEYAPI
+}
+
+static Boolean SecCDSAKeySetParameter(SecKeyRef key, CFStringRef name, CFPropertyListRef value, CFErrorRef *error) {
+ BEGIN_SECKEYAPI(Boolean, false)
+
+ if (CFEqual(name, kSecUseAuthenticationUI)) {
+ key->credentialType = CFEqual(value, kSecUseAuthenticationUIAllow) ? kSecCredentialTypeDefault : kSecCredentialTypeNoUI;
+ result = true;
+ } else {
+ result = SecError(errSecUnimplemented, error, CFSTR("Unsupported parameter '%@' for SecKeyCDSASetParameter"), name);
+ }
+
+ END_SECKEYAPI
+}
+
+const SecKeyDescriptor kSecCDSAKeyDescriptor = {
+ .version = kSecKeyDescriptorVersion,
+ .name = "CDSAKey",
+
+ .init = SecCDSAKeyInit,
+ .destroy = SecCDSAKeyDestroy,
+ .blockSize = SecCDSAKeyGetBlockSize,
+ .getAlgorithmID = SecCDSAKeyGetAlgorithmId,
+ .copyDictionary = SecCDSAKeyCopyAttributeDictionary,
+ .copyPublic = SecCDSAKeyCopyPublicBytes,
+ .copyExternalRepresentation = SecCDSAKeyCopyExternalRepresentation,
+ .copyPublicKey = SecCDSAKeyCopyPublicKey,
+ .copyOperationResult = SecCDSAKeyCopyOperationResult,
+ .isEqual = SecCDSAKeyIsEqual,
+ .setParameter = SecCDSAKeySetParameter,
+};
+
+namespace Security {
+ namespace KeychainCore {
+ SecCFObject *KeyItem::fromSecKeyRef(CFTypeRef ptr) {
+ if (ptr == NULL || CFGetTypeID(ptr) != SecKeyGetTypeID()) {
+ return NULL;
+ }
+
+ SecKeyRef key = static_cast<SecKeyRef>(const_cast<void *>(ptr));
+ if (key->key_class == &kSecCDSAKeyDescriptor) {
+ return static_cast<SecCFObject *>(key->key);
+ }
+
+ if (key->cdsaKey == NULL) {
+ // Create CDSA key from exported data of existing key.
+ CFRef<CFDataRef> keyData = SecKeyCopyExternalRepresentation(key, NULL);
+ CFRef<CFDictionaryRef> keyAttributes = SecKeyCopyAttributes(key);
+ if (keyData && keyAttributes) {
+ key->cdsaKey = SecKeyCreateFromData(keyAttributes, keyData, NULL);
+ }
+ }
+
+ return (key->cdsaKey != NULL) ? key->cdsaKey->key : NULL;
+ }
+
+ // You need to hold this key's MutexForObject when you run this
+ void KeyItem::attachSecKeyRef() const {
+ SecKeyRef key = SecKeyCreate(NULL, &kSecCDSAKeyDescriptor, reinterpret_cast<const uint8_t *>(this), 0, 0);
+ key->key->mWeakSecKeyRef = key;
+ }
+
+ }