#include "cmslocal.h"
#include "SecAsn1Item.h"
#include "secoid.h"
#include "cryptohi.h"
#include <security_asn1/secasn1.h>
#include <security_asn1/secerr.h>
#include <security_asn1/secport.h>
#include <Security/Security.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecKeyPriv.h>
#include <CommonCrypto/CommonCryptor.h>
#include <CommonCrypto/CommonRandomSPI.h>
#include <CommonCrypto/CommonRandom.h>
OSStatus
SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert,
SecSymmetricKeyRef bulkkey,
SecAsn1Item * encKey)
{
OSStatus rv;
SecPublicKeyRef publickey = SecCertificateCopyKey(cert);
if (publickey == NULL)
return SECFailure;
rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey);
CFRelease(publickey);
return rv;
}
OSStatus
SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp,
SecPublicKeyRef publickey,
SecSymmetricKeyRef bulkkey, SecAsn1Item * encKey)
{
OSStatus rv;
size_t data_len;
void *mark = NULL;
mark = PORT_ArenaMark(poolp);
if (!mark)
goto loser;
#if 0
keyType = SECKEY_GetPublicKeyType(publickey);
PORT_Assert(keyType == rsaKey);
if (keyType != rsaKey) {
goto loser;
}
#endif
#if TARGET_OS_OSX
rv = SecKeyGetStrengthInBits(publickey, NULL, &data_len);
if (rv)
goto loser;
data_len = data_len / 8;
#else
data_len = SecKeyGetSize(publickey, kSecKeyEncryptedDataSize);
#endif
encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len);
encKey->Length = data_len;
if (encKey->Data == NULL)
goto loser;
rv = WRAP_PubWrapSymKey(publickey, bulkkey, encKey);
if (rv != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
if (mark) {
PORT_ArenaRelease(poolp, mark);
}
return SECFailure;
}
SecSymmetricKeyRef
SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey, SecAsn1Item * encKey, SECOidTag bulkalgtag)
{
return WRAP_PubUnwrapSymKey(privkey, encKey, bulkalgtag);
}
#if 0
extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[];
OSStatus
SecCmsUtilEncryptSymKeyMISSI(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef bulkkey,
SECOidTag symalgtag, SecAsn1Item * encKey, SecAsn1Item * *pparams, void *pwfn_arg)
{
SECOidTag certalgtag;
SECOidTag encalgtag;
OSStatus rv = SECFailure;
SecAsn1Item * params = NULL;
OSStatus err;
SecSymmetricKeyRef tek;
SecCertificateRef ourCert;
SecPublicKeyRef ourPubKey, *publickey = NULL;
SecPrivateKeyRef ourPrivKey = NULL;
SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
SecCmsSMIMEKEAParameters keaParams;
PLArenaPool *arena = NULL;
const SECAlgorithmID *algid;
(void) memset(&keaParams, 0, sizeof(keaParams));
#if USE_CDSA_CRYPTO
SecCertificateGetAlgorithmID(cert,&algid);
#endif
certalgtag = SECOID_GetAlgorithmTag(algid);
PORT_Assert(certalgtag == SEC_OID_MISSI_KEA_DSS_OLD ||
certalgtag == SEC_OID_MISSI_KEA_DSS ||
certalgtag == SEC_OID_MISSI_KEA);
#define SMIME_FORTEZZA_RA_LENGTH 128
#define SMIME_FORTEZZA_IV_LENGTH 24
#define SMIME_FORTEZZA_MAX_KEY_SIZE 256
encalgtag = SEC_OID_NETSCAPE_SMIME_KEA;
publickey = CERT_ExtractPublicKey(cert);
if (publickey == NULL) goto loser;
ourCert = PK11_FindBestKEAMatch(cert, pwfn_arg);
if (ourCert == NULL) goto loser;
arena = PORT_NewArena(1024);
if (arena == NULL)
goto loser;
ourPubKey = CERT_ExtractPublicKey(ourCert);
if (ourPubKey == NULL) {
CERT_DestroyCertificate(ourCert);
goto loser;
}
SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), &(ourPubKey->u.fortezza.KEAKey));
SECKEY_DestroyPublicKey(ourPubKey);
ourPubKey = NULL;
ourPrivKey = PK11_FindKeyByAnyCert(ourCert, pwfn_arg);
CERT_DestroyCertificate(ourCert);
if (!ourPrivKey)
goto loser;
keaParams.originatorRA.Data = (unsigned char *)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH);
keaParams.originatorRA.Length = SMIME_FORTEZZA_RA_LENGTH;
tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
&keaParams.originatorRA, NULL,
CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, pwfn_arg);
SECKEY_DestroyPublicKey(publickey);
SECKEY_DestroyPrivateKey(ourPrivKey);
publickey = NULL;
ourPrivKey = NULL;
if (!tek)
goto loser;
encKey->Data = (unsigned char *)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
if (encKey->Data == NULL) {
CFRelease(tek);
goto loser;
}
switch (PK11_AlgtagToMechanism(symalgtag)) {
case CKM_SKIPJACK_CBC64:
case CKM_SKIPJACK_ECB64:
case CKM_SKIPJACK_OFB64:
case CKM_SKIPJACK_CFB64:
case CKM_SKIPJACK_CFB32:
case CKM_SKIPJACK_CFB16:
case CKM_SKIPJACK_CFB8:
err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, tek, bulkkey, encKey);
whichKEA = SecCmsKEAUsesSkipjack;
break;
default:
keaParams.nonSkipjackIV.Data =
(unsigned char *)PORT_ArenaAlloc(arena, SMIME_FORTEZZA_IV_LENGTH);
keaParams.nonSkipjackIV.Length = SMIME_FORTEZZA_IV_LENGTH;
err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, tek, bulkkey, encKey);
if (err != SECSuccess)
goto loser;
if (encKey->Length != PK11_GetKeyLength(bulkkey)) {
if (SEC_ASN1EncodeInteger(arena, &keaParams.bulkKeySize, PK11_GetKeyLength(bulkkey)) == NULL)
err = (OSStatus)PORT_GetError();
else
whichKEA = SecCmsKEAUsesNonSkipjackWithPaddedEncKey;
}
else
whichKEA = SecCmsKEAUsesNonSkipjack;
break;
}
CFRelease(tek);
if (err != SECSuccess)
goto loser;
PORT_Assert(whichKEA != SecCmsKEAInvalid);
params = SEC_ASN1EncodeItem(poolp, NULL, &keaParams, nss_cms_get_kea_template(whichKEA));
if (params == NULL)
goto loser;
*pparams = params;
rv = SECSuccess;
loser:
if (arena)
PORT_FreeArena(arena, PR_FALSE);
if (publickey)
SECKEY_DestroyPublicKey(publickey);
if (ourPrivKey)
SECKEY_DestroyPrivateKey(ourPrivKey);
return rv;
}
SecSymmetricKeyRef
SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey, SecAsn1Item * encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
{
OSStatus err;
CK_MECHANISM_TYPE bulkType;
SecSymmetricKeyRef tek;
SecPublicKeyRef originatorPubKey;
SecCmsSMIMEKEAParameters keaParams;
SecSymmetricKeyRef bulkkey;
int bulkLength;
(void) memset(&keaParams, 0, sizeof(keaParams));
err = SEC_ASN1DecodeItem(NULL, &keaParams, NSS_SMIMEKEAParamTemplateAllParams,
&(keyEncAlg->parameters));
if (err != SECSuccess)
goto loser;
originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
keaParams.originatorKEAKey.Length);
if (originatorPubKey == NULL)
goto loser;
tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
&keaParams.originatorRA, NULL,
CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, pwfn_arg);
SECKEY_DestroyPublicKey(originatorPubKey);
if (tek == NULL)
goto loser;
bulkType = PK11_AlgtagToMechanism(bulkalgtag);
switch (bulkType) {
case CKM_SKIPJACK_CBC64:
case CKM_SKIPJACK_ECB64:
case CKM_SKIPJACK_OFB64:
case CKM_SKIPJACK_CFB64:
case CKM_SKIPJACK_CFB32:
case CKM_SKIPJACK_CFB16:
case CKM_SKIPJACK_CFB8:
bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
break;
default:
if (keaParams.bulkKeySize.Length > 0) {
err = SEC_ASN1DecodeItem(NULL, &bulkLength,
SEC_ASN1_GET(SEC_IntegerTemplate),
&keaParams.bulkKeySize);
if (err != SECSuccess)
goto loser;
}
bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV,
encKey, bulkType, CKA_DECRYPT, bulkLength);
break;
}
return bulkkey;
loser:
return NULL;
}
OSStatus
SecCmsUtilEncryptSymKeyESDH(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef key,
SecAsn1Item * encKey, SecAsn1Item * *ukm, SECAlgorithmID *keyEncAlg,
SecAsn1Item * pubKey)
{
#if 0
SECOidTag certalgtag;
SECOidTag encalgtag;
OSStatus rv;
SecAsn1Item * params = NULL;
int data_len;
OSStatus err;
SecSymmetricKeyRef tek;
SecCertificateRef ourCert;
SecPublicKeyRef ourPubKey;
SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
publickey = CERT_ExtractPublicKey(cert);
if (publickey == NULL) goto loser;
ourCert = PK11_FindBestKEAMatch(cert, wincx);
if (ourCert == NULL) goto loser;
arena = PORT_NewArena(1024);
if (arena == NULL) goto loser;
ourPubKey = CERT_ExtractPublicKey(ourCert);
if (ourPubKey == NULL)
{
goto loser;
}
SECITEM_CopyItem(arena, pubKey, &(ourPubKey->u.fortezza.KEAKey));
SECKEY_DestroyPublicKey(ourPubKey);
ourPubKey = NULL;
ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
CERT_DestroyCertificate(ourCert);
if (!ourPrivKey) goto loser;
if (ukm) {
ukm->Data = (unsigned char*)PORT_ArenaZAlloc(arena,);
ukm->Length = ;
}
kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
ukm, NULL,
CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, wincx);
SECKEY_DestroyPublicKey(publickey);
SECKEY_DestroyPrivateKey(ourPrivKey);
publickey = NULL;
ourPrivKey = NULL;
if (!kek)
goto loser;
encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
if (encKey->Data == NULL)
{
CFRelease(kek);
goto loser;
}
switch (PK11_AlgtagToMechanism(enccinfo->encalg))
{
case CKM_SKIPJACK_CFB8:
err = PK11_WrapSymKey(CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
whichKEA = SecCmsKEAUsesSkipjack;
break;
case CKM_SKIPJACK_CFB8:
err = PK11_WrapSymKey(CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
whichKEA = SecCmsKEAUsesSkipjack;
break;
default:
err = SECFailure;
break;
}
CFRelease(kek);
if (err != SECSuccess)
goto loser;
PORT_Assert(whichKEA != SecCmsKEAInvalid);
params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
if (params == NULL)
goto loser;
rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
if (rv != SECSuccess)
goto loser;
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
if (publickey) {
SECKEY_DestroyPublicKey(publickey);
}
if (ourPrivKey) {
SECKEY_DestroyPrivateKey(ourPrivKey);
}
#endif
return SECFailure;
}
SecSymmetricKeyRef
SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey, SecAsn1Item * encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
{
#if 0
OSStatus err;
CK_MECHANISM_TYPE bulkType;
SecSymmetricKeyRef tek;
SecPublicKeyRef originatorPubKey;
SecCmsSMIMEKEAParameters keaParams;
originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
keaParams.originatorKEAKey.Length);
if (originatorPubKey == NULL)
goto loser;
tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
&keaParams.originatorRA, NULL,
CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, pwfn_arg);
SECKEY_DestroyPublicKey(originatorPubKey);
if (tek == NULL)
goto loser;
bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
return bulkkey;
loser:
#endif
return NULL;
}
#endif
#pragma mark ---- ECDH support functions ----
#ifdef NDEBUG
#define CSSM_PERROR(f, r)
#define dprintf(args...)
#else
#define CSSM_PERROR(f, r) cssmPerror(f, r)
#define dprintf(args...) fprintf(stderr, args)
#endif
#define UKM_LENGTH 8
#define ECDH_KEK_ALG_TAG SEC_OID_DES_EDE3_CBC
#define ECDH_KEK_KEY_CSSM_ALGID CSSM_ALGID_3DES_3KEY
#define ECDH_KEK_ENCR_CSSM_ALGID CSSM_ALGID_3DES_3KEY_EDE
#define ECDH_KEK_KEY_LEN_BYTES 24
#define ECDH_KEK_IV_LEN_BYTES 8
#define CMS_DUMP_BUFS 0
#if CMS_DUMP_BUFS
static void dumpBuf(
const char *label,
const CSSM_DATA *cd)
{
unsigned dex;
printf("%s:\n ", label);
for(dex=0; dex<cd->Length; dex++) {
printf("%02X ", cd->Data[dex]);
if(((dex % 16) == 15) && (dex != (cd->Length - 1))) {
printf("\n ");
}
}
putchar('\n');
}
#else
#define dumpBuf(l, d)
#endif
typedef struct {
SECAlgorithmID algId;
SecAsn1Item entityUInfo;
SecAsn1Item suppPubInfo;
} ECC_CMS_SharedInfo;
static const SecAsn1Template ECC_CMS_SharedInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ECC_CMS_SharedInfo) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(ECC_CMS_SharedInfo,entityUInfo),
kSecAsn1OctetStringTemplate },
{ SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 2,
offsetof(ECC_CMS_SharedInfo,suppPubInfo),
kSecAsn1OctetStringTemplate },
{ 0 }
};
typedef enum {
CAT_Uint32,
CAT_Ptr
} ContextAttrType;
static void int32ToBytes(
uint32_t i,
unsigned char *b)
{
int dex;
for(dex=3; dex>=0; dex--) {
b[dex] = i;
i >>= 8;
}
}
static OSStatus encrAlgInfo(
SECOidTag oidTag,
uint32_t *keySizeBits,
CCAlgorithm *algorithm,
CCOptions *options)
{
*keySizeBits = 64;
*options = kCCOptionPKCS7Padding;
switch(oidTag) {
case SEC_OID_RC2_CBC:
case SEC_OID_RC4:
case SEC_OID_RC5_CBC_PAD:
dprintf("encrAlgInfo: key size unknowable\n");
return errSecNotAvailable;
case SEC_OID_DES_EDE:
*options = kCCOptionECBMode;
case SEC_OID_DES_EDE3_CBC:
*keySizeBits = 192;
*algorithm = kCCAlgorithm3DES;
break;
case SEC_OID_DES_ECB:
*options = kCCOptionECBMode;
case SEC_OID_DES_CBC:
*algorithm = kCCAlgorithmDES;
break;
case SEC_OID_AES_128_CBC:
*keySizeBits = 128;
*algorithm = kCCAlgorithmAES;
break;
case SEC_OID_AES_192_CBC:
*keySizeBits = 192;
*algorithm = kCCAlgorithmAES;
break;
case SEC_OID_AES_256_CBC:
*keySizeBits = 256;
*algorithm = kCCAlgorithmAES;
break;
case SEC_OID_AES_128_ECB:
*keySizeBits = 128;
*algorithm = kCCAlgorithmAES;
*options = kCCOptionECBMode;
break;
case SEC_OID_AES_192_ECB:
*keySizeBits = 192;
*algorithm = kCCAlgorithmAES;
*options = kCCOptionECBMode;
break;
case SEC_OID_AES_256_ECB:
*keySizeBits = 256;
*algorithm = kCCAlgorithmAES;
*options = kCCOptionECBMode;
break;
default:
dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag);
return errSecNotAvailable;
}
return noErr;
}
#pragma mark ---- ECDH CEK key wrap ----
OSStatus
SecCmsUtilEncryptSymKeyECDH(
PLArenaPool *poolp,
SecCertificateRef cert,
SecSymmetricKeyRef key,
SecAsn1Item *encKey,
SecAsn1Item *ukm,
SECAlgorithmID *keyEncAlg,
SecAsn1Item *pubKey)
{
OSStatus rv = noErr;
SecKeyRef theirPubKey = NULL, ourPubKey = NULL, ourPrivKey = NULL;
CFDictionaryRef theirKeyAttrs = NULL, ourKeyParams = NULL, kekParams = NULL;
uint8_t iv[ECDH_KEK_IV_LEN_BYTES];
SecAsn1Item ivData = { ECDH_KEK_IV_LEN_BYTES, iv };
SECAlgorithmID kekAlgId;
SECOidData *kekOid;
ECC_CMS_SharedInfo sharedInfo;
SecAsn1Item sharedInfoEnc = {0, NULL};
uint8_t nullData[2] = {SEC_ASN1_NULL, 0};
uint8_t keyLenAsBytes[4];
CFDataRef sharedInfoData = NULL, kekData = NULL, ourPubData = NULL;
CFNumberRef kekLen = NULL;
CFErrorRef error = NULL;
CCCryptorRef ciphercc = NULL;
encKey->Data = NULL;
encKey->Length = 0;
theirPubKey = SecCertificateCopyKey(cert);
if (rv || !theirPubKey) {
dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get public key from cert, %d\n", (int)rv);
goto out;
}
theirKeyAttrs = SecKeyCopyAttributes(theirPubKey);
if (!theirKeyAttrs) {
dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get key attributes\n");
goto out;
}
CFStringRef keyType = NULL;
CFNumberRef keySizeNum = NULL;
keyType = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeyType);
keySizeNum = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeySizeInBits);
if (!CFEqual(kSecAttrKeyTypeECSECPrimeRandom, keyType)) {
dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported key type\n");
rv = SEC_ERROR_INVALID_KEY;
goto out;
}
const void *keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits};
const void *values[] = { keyType, keySizeNum };
ourKeyParams = CFDictionaryCreate(NULL, keys, values, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
rv = SecKeyGeneratePair(ourKeyParams, &ourPubKey, &ourPrivKey);
if (rv || !ourPubKey || !ourPrivKey) {
dprintf("SecKeyGeneratePair: unable to generate ECDH key pair, %d\n", (int)rv);
goto out;
}
ukm->Data = PORT_Alloc(UKM_LENGTH);
ukm->Length = UKM_LENGTH;
rv = CCRandomCopyBytes(kCCRandomDefault, ukm->Data, UKM_LENGTH);
if (rv || !ukm->Data) {
dprintf("CCRandomGenerateBytes failed, %d", (int)rv);
goto out;
}
ukm->Length = UKM_LENGTH;
rv = CCRandomCopyBytes(kCCRandomDefault, iv, ECDH_KEK_IV_LEN_BYTES);
if (rv) {
dprintf("CCRandomGenerateBytes failed, %d", (int)rv);
goto out;
}
dumpBuf("sender IV", &ivData);
memset(&kekAlgId, 0, sizeof(kekAlgId));
if (!SEC_ASN1EncodeItem(poolp, &kekAlgId.parameters,
&ivData, kSecAsn1OctetStringTemplate)) {
rv = errSecInternalComponent;
goto out;
}
kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
if(kekOid == NULL) {
dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
rv = errSecInternalComponent;
goto out;
}
kekAlgId.algorithm = kekOid->oid;
memset(keyEncAlg, 0, sizeof(*keyEncAlg));
if (!SEC_ASN1EncodeItem(poolp, &keyEncAlg->parameters,
&kekAlgId, SECOID_AlgorithmIDTemplate)) {
rv = errSecInternalComponent;
goto out;
}
kekOid = SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF);
if(kekOid == NULL) {
dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
rv = errSecInternalComponent;
goto out;
}
keyEncAlg->algorithm = kekOid->oid;
memset(&sharedInfo, 0, sizeof(sharedInfo));
kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
sharedInfo.algId.algorithm = kekOid->oid;
sharedInfo.algId.parameters.Data = nullData;
sharedInfo.algId.parameters.Length = 2;
sharedInfo.entityUInfo = *ukm;
int32ToBytes(ECDH_KEK_KEY_LEN_BYTES << 3, keyLenAsBytes);
sharedInfo.suppPubInfo.Length = 4;
sharedInfo.suppPubInfo.Data = keyLenAsBytes;
if (!SEC_ASN1EncodeItem(poolp, &sharedInfoEnc,
&sharedInfo, ECC_CMS_SharedInfoTemplate)) {
rv = errSecInternalComponent;
goto out;
}
dumpBuf("sender encoded SharedInfo", &sharedInfoEnc);
sharedInfoData = CFDataCreate(NULL, sharedInfoEnc.Data, sharedInfoEnc.Length);
int32_t ecdh_key_key_len = ECDH_KEK_KEY_LEN_BYTES;
kekLen = CFNumberCreate(NULL, kCFNumberSInt32Type, &ecdh_key_key_len);
const void *kekKeys[] = { kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo };
const void *kekValues[] = { kekLen, sharedInfoData };
kekParams = CFDictionaryCreate(NULL, kekKeys, kekValues, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
kekData = SecKeyCopyKeyExchangeResult(ourPrivKey, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1,
theirPubKey, kekParams, &error);
if (error) {
dprintf("SecKeyCopyKeyExchangeResult: failed\n");
goto out;
}
rv = CCCryptorCreate(kCCEncrypt, kCCAlgorithm3DES, kCCOptionPKCS7Padding,
CFDataGetBytePtr(kekData), CFDataGetLength(kekData), iv, &ciphercc);
if (rv) {
dprintf("CCCryptorCreate failed: %d\n", (int)rv);
goto out;
}
size_t expectedEncKeyLength = CCCryptorGetOutputLength(ciphercc, CFDataGetLength(key), true);
encKey->Data = PORT_ArenaAlloc(poolp, expectedEncKeyLength);
size_t bytes_output = 0;
rv = CCCryptorUpdate(ciphercc, CFDataGetBytePtr(key), CFDataGetLength(key), encKey->Data, expectedEncKeyLength, &bytes_output);
if (rv) {
dprintf("CCCryptorUpdate failed: %d\n", (int)rv);
goto out;
}
size_t final_bytes_output = 0;
rv = CCCryptorFinal(ciphercc, encKey->Data+bytes_output, expectedEncKeyLength - bytes_output, &final_bytes_output);
if (rv) {
dprintf("CCCryptorFinal failed: %d\n", (int)rv);
goto out;
}
encKey->Length = bytes_output + final_bytes_output;
ourPubData = SecKeyCopyExternalRepresentation(ourPubKey, &error);
if (error) {
dprintf("SecKeyCopyExternalRepresentation failed\n");
goto out;
}
pubKey->Length = CFDataGetLength(ourPubData);
pubKey->Data = malloc(pubKey->Length);
if (pubKey->Data) {
memcpy(pubKey->Data, CFDataGetBytePtr(ourPubData), pubKey->Length);
} else {
rv = errSecAllocate;
}
pubKey->Length <<= 3;
out:
if (theirPubKey) { CFRelease(theirPubKey); }
if (theirKeyAttrs) { CFRelease(theirKeyAttrs); }
if (ourKeyParams) { CFRelease(ourKeyParams); }
if (ourPubKey) { CFRelease(ourPubKey); }
if (ourPrivKey) { CFRelease(ourPrivKey); }
if (sharedInfoData) { CFRelease(sharedInfoData); }
if (kekLen) { CFRelease(kekLen); }
if (kekParams) { CFRelease(kekParams); }
if (kekData) { CFRelease(kekData); }
if (error) { CFRelease(error); }
if (ciphercc) { CCCryptorRelease(ciphercc); }
if (ourPubData) { CFRelease(ourPubData); }
if (rv && encKey->Data) {
PORT_Free(encKey->Data);
encKey->Data = NULL;
encKey->Length = 0;
}
if (rv && ukm->Data) {
PORT_Free(ukm->Data);
ukm->Data = NULL;
ukm->Length = 0;
}
return rv;
}
#pragma mark ---- ECDH CEK key unwrap ----
SecSymmetricKeyRef
SecCmsUtilDecryptSymKeyECDH(
SecPrivateKeyRef privkey,
SecAsn1Item *encKey,
SecAsn1Item *ukm,
SECAlgorithmID *keyEncAlg,
SECOidTag bulkalgtag,
SecAsn1Item *pubKey)
{
SecSymmetricKeyRef outKey = NULL;
OSStatus rv = noErr;
PLArenaPool *pool = NULL;
SECAlgorithmID keyAlgParam;
SECOidData *kekOid = NULL;
SecAsn1Item iv = {0, NULL};
ECC_CMS_SharedInfo sharedInfo;
SecAsn1Item sharedInfoEnc = {0, NULL};
uint8_t nullData[2] = {SEC_ASN1_NULL, 0};
uint8_t keyLenAsBytes[4];
uint32_t kekSizeBits;
SecKeyRef theirPubKey = NULL;
CFDictionaryRef theirKeyAttrs = NULL, kekParams = NULL;
CFDataRef sharedInfoData = NULL, theirPubData= NULL, kekData = NULL;
CFNumberRef kekLen = NULL, theirKeyLen = NULL;
CFErrorRef error = NULL;
CCAlgorithm alg;
CCOptions options = 0;
CCCryptorRef ciphercc = NULL;
size_t theirKeySizeInBits = 0;
pool = PORT_NewArena(1024);
if(pool == NULL) {
goto out;
}
memset(&keyAlgParam, 0, sizeof(keyAlgParam));
if(SEC_ASN1DecodeItem(pool, &keyAlgParam, SECOID_AlgorithmIDTemplate,
&keyEncAlg->parameters)) {
dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
goto out;
}
kekOid = SECOID_FindOID(&keyAlgParam.algorithm);
if(kekOid == NULL) {
dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
goto out;
}
rv = encrAlgInfo(kekOid->offset, &kekSizeBits, &alg, &options);
if(rv) {
goto out;
}
if(SEC_ASN1DecodeItem(pool, &iv, kSecAsn1OctetStringTemplate,
&keyAlgParam.parameters)) {
dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
goto out;
}
memset(&sharedInfo, 0, sizeof(sharedInfo));
sharedInfo.algId.algorithm = kekOid->oid;
sharedInfo.algId.parameters.Data = nullData;
sharedInfo.algId.parameters.Length = 2;
sharedInfo.entityUInfo = *ukm;
int32ToBytes(kekSizeBits, keyLenAsBytes);
sharedInfo.suppPubInfo.Length = 4;
sharedInfo.suppPubInfo.Data = keyLenAsBytes;
if (!SEC_ASN1EncodeItem(pool, &sharedInfoEnc,
&sharedInfo, ECC_CMS_SharedInfoTemplate)) {
goto out;
}
dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc);
dumpBuf("receiver IV", &iv);
dumpBuf("receiver UKM", ukm);
dumpBuf("sender's public key", pubKey);
theirKeySizeInBits = pubKey->Length;
pubKey->Length = (theirKeySizeInBits + 7) >> 3;
theirPubData = CFDataCreate(NULL, pubKey->Data, pubKey->Length);
theirKeyLen = CFNumberCreate(NULL, kCFNumberSInt64Type, &theirKeySizeInBits);
const void *keys[] = { kSecAttrKeyType, kSecAttrKeyClass, kSecAttrKeySizeInBits };
const void *values[] = { kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClassPublic, theirKeyLen};
theirKeyAttrs = CFDictionaryCreate(NULL, keys, values, 3,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
theirPubKey = SecKeyCreateWithData(theirPubData, theirKeyAttrs, &error);
if (error) {
dprintf("SecKeyCreateWithData: failed\n");
goto out;
}
sharedInfoData = CFDataCreate(NULL, sharedInfoEnc.Data, sharedInfoEnc.Length);
int32_t ecdh_key_key_len = (kekSizeBits + 7) >> 3;
kekLen = CFNumberCreate(NULL, kCFNumberSInt32Type, &ecdh_key_key_len);
const void *kekKeys[] = { kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo };
const void *kekValues[] = { kekLen, sharedInfoData };
kekParams = CFDictionaryCreate(NULL, kekKeys, kekValues, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
kekData = SecKeyCopyKeyExchangeResult(privkey, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1,
theirPubKey, kekParams, &error);
if (error) {
dprintf("SecKeyCopyKeyExchangeResult: failed\n");
goto out;
}
SecAsn1Item cek = { 0, NULL };
rv = CCCryptorCreate(kCCDecrypt, alg, options,
CFDataGetBytePtr(kekData), CFDataGetLength(kekData), iv.Data, &ciphercc);
if (rv) {
dprintf("CCCryptorCreate failed: %d\n", (int)rv);
goto out;
}
size_t expectedKeyLength = CCCryptorGetOutputLength(ciphercc, encKey->Length, true);
cek.Data = PORT_ArenaAlloc(pool, expectedKeyLength);
size_t bytes_output = 0;
rv = CCCryptorUpdate(ciphercc, encKey->Data, encKey->Length, cek.Data, expectedKeyLength, &bytes_output);
if (rv) {
dprintf("CCCryptorUpdate failed: %d\n", (int)rv);
goto out;
}
size_t final_bytes_output = 0;
rv = CCCryptorFinal(ciphercc, cek.Data+bytes_output, expectedKeyLength - bytes_output, &final_bytes_output);
if (rv) {
dprintf("CCCryptorFinal failed: %d\n", (int)rv);
goto out;
}
cek.Length = bytes_output + final_bytes_output;
outKey = (SecSymmetricKeyRef)CFDataCreate(NULL, cek.Data, cek.Length);
out:
if (pool != NULL) {
PORT_FreeArena(pool, PR_FALSE);
}
if (theirPubData) { CFRelease(theirPubData); }
if (theirKeyLen) { CFRelease(theirKeyLen); }
if (theirPubKey) { CFRelease(theirPubKey); }
if (theirKeyAttrs) { CFRelease(theirKeyAttrs); }
if (sharedInfoData) { CFRelease(sharedInfoData); }
if (kekLen) { CFRelease(kekLen); }
if (kekParams) { CFRelease(kekParams); }
if (kekData) { CFRelease(kekData); }
if (error) { CFRelease(error); }
if (ciphercc) { CCCryptorRelease(ciphercc); }
if (outKey == NULL) {
PORT_SetError(SEC_ERROR_NO_KEY);
}
return outKey;
}