#include "cmslocal.h"
#include "secitem.h"
#include "secoid.h"
#include "cryptohi.h"
#include <security_asn1/secasn1.h>
#include <security_asn1/secerr.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecKeyPriv.h>
#include <Security/Security.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/SecCmsBase.h>
#include <Security/secasn1t.h>
#include <security_asn1/plarenas.h>
#include <Security/keyTemplates.h>
OSStatus
SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert,
SecSymmetricKeyRef bulkkey,
CSSM_DATA_PTR encKey)
{
OSStatus rv;
SecPublicKeyRef publickey;
rv = SecCertificateCopyPublicKey(cert,&publickey);
if (publickey == NULL)
return SECFailure;
rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey);
CFRelease(publickey);
return rv;
}
OSStatus
SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp,
SecPublicKeyRef publickey,
SecSymmetricKeyRef bulkkey, CSSM_DATA_PTR encKey)
{
OSStatus rv;
unsigned int 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
rv = SecKeyGetStrengthInBits(publickey, NULL, &data_len);
if (rv)
goto loser;
data_len >>= 2;
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, CSSM_DATA_PTR 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, CSSM_DATA_PTR encKey, CSSM_DATA_PTR *pparams, void *pwfn_arg)
{
SECOidTag certalgtag;
SECOidTag encalgtag;
OSStatus rv = SECFailure;
CSSM_DATA_PTR params = NULL;
OSStatus err;
SecSymmetricKeyRef tek;
SecCertificateRef ourCert;
SecPublicKeyRef ourPubKey, *publickey = NULL;
SecPrivateKeyRef ourPrivKey = NULL;
SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
SecCmsSMIMEKEAParameters keaParams;
PLArenaPool *arena = NULL;
extern const SecAsn1Template *nss_cms_get_kea_template(SecCmsKEATemplateSelector whichTemplate);
const SECAlgorithmID *algid;
(void) memset(&keaParams, 0, sizeof(keaParams));
SecCertificateGetAlgorithmID(cert,&algid);
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, CSSM_DATA_PTR 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,
CSSM_DATA_PTR encKey, CSSM_DATA_PTR ukm, SECAlgorithmID *keyEncAlg,
CSSM_DATA_PTR pubKey)
{
#if 0
SECOidTag certalgtag;
SECOidTag encalgtag;
OSStatus rv;
CSSM_DATA_PTR 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, CSSM_DATA_PTR 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
#define CFRELEASE(cf) if(cf != NULL) { CFRelease(cf); }
#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...) printf(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;
CSSM_DATA entityUInfo;
CSSM_DATA 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 CSSM_RETURN cmsAddContextAttribute(
CSSM_CC_HANDLE CCHandle,
uint32 AttributeType,
uint32 AttributeLength,
ContextAttrType attrType,
const void *AttributePtr,
uint32 attributeInt)
{
CSSM_CONTEXT_ATTRIBUTE newAttr;
CSSM_RETURN crtn;
newAttr.AttributeType = AttributeType;
newAttr.AttributeLength = AttributeLength;
if(attrType == CAT_Uint32) {
newAttr.Attribute.Uint32 = attributeInt;
}
else {
newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr;
}
crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
if(crtn) {
CSSM_PERROR("CSSM_UpdateContextAttributes", crtn);
}
return crtn;
}
static CSSM_RETURN cmsGenRand(
CSSM_CSP_HANDLE cspHand,
CSSM_SIZE len,
uint8 *randOut)
{
CSSM_CC_HANDLE ccHand = 0;
CSSM_DATA randData = {len, randOut};
CSSM_RETURN crtn = CSSM_CSP_CreateRandomGenContext(cspHand,
CSSM_ALGID_APPLE_YARROW,
NULL,
len,
&ccHand);
if(crtn) {
CSSM_PERROR("CSSM_CSP_CreateRandomGenContext", crtn);
return crtn;
}
crtn = CSSM_GenerateRandom(ccHand, &randData);
CSSM_DeleteContext(ccHand);
if(crtn) {
CSSM_PERROR("CSSM_GenerateRandom", crtn);
}
return crtn;
}
static void int32ToBytes(
uint32_t i,
unsigned char *b)
{
int dex;
for(dex=3; dex>=0; dex--) {
b[dex] = i;
i >>= 8;
}
}
static OSStatus cmsNullWrapKey(
CSSM_CSP_HANDLE cspHand,
const CSSM_KEY *refKey,
CSSM_KEY_PTR rawKey)
{
CSSM_DATA descData = {0, 0};
CSSM_RETURN crtn;
CSSM_CC_HANDLE ccHand;
CSSM_ACCESS_CREDENTIALS creds;
uint32 keyAttr;
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
memset(rawKey, 0, sizeof(CSSM_KEY));
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
CSSM_ALGID_NONE,
CSSM_ALGMODE_NONE,
&creds,
NULL, NULL, CSSM_PADDING_NONE,
0, &ccHand);
if(crtn) {
CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", crtn);
return crtn;
}
keyAttr = rawKey->KeyHeader.KeyAttr;
keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
CSSM_KEYATTR_MODIFIABLE);
keyAttr |= CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
crtn = CSSM_WrapKey(ccHand,
&creds,
refKey,
&descData,
rawKey);
if(crtn != CSSM_OK) {
CSSM_PERROR("CSSM_WrapKey", crtn);
}
CSSM_DeleteContext(ccHand);
return crtn;
}
static void cmsFreeCssmMemory(
CSSM_HANDLE hand,
void *p)
{
CSSM_API_MEMORY_FUNCS memFuncs;
CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
if(crtn) {
return;
}
memFuncs.free_func(p, memFuncs.AllocRef);
}
static OSStatus encrAlgInfo(
SECOidTag oidTag,
uint32 *keySizeBits,
CSSM_ENCRYPT_MODE *mode)
{
*keySizeBits = 64;
*mode = CSSM_ALGMODE_CBCPadIV8;
switch(oidTag) {
case SEC_OID_RC2_CBC:
case SEC_OID_RC4:
case SEC_OID_RC5_CBC_PAD:
dprintf("encrAlgInfo: key size unknowable\n");
return errSecDataNotAvailable;
case SEC_OID_DES_EDE3_CBC:
*keySizeBits = 192;
break;
case SEC_OID_DES_EDE:
case SEC_OID_DES_ECB:
*mode = CSSM_ALGMODE_ECB;
break;
case SEC_OID_DES_CBC:
*mode = CSSM_ALGMODE_CBC;
break;
case SEC_OID_AES_128_CBC:
*keySizeBits = 128;
break;
case SEC_OID_AES_192_CBC:
*keySizeBits = 192;
break;
case SEC_OID_AES_256_CBC:
*keySizeBits = 256;
break;
case SEC_OID_AES_128_ECB:
*keySizeBits = 128;
*mode = CSSM_ALGMODE_ECB;
break;
case SEC_OID_AES_192_ECB:
*keySizeBits = 192;
*mode = CSSM_ALGMODE_ECB;
break;
case SEC_OID_AES_256_ECB:
*keySizeBits = 256;
*mode = CSSM_ALGMODE_ECB;
break;
case SEC_OID_DES_OFB:
*mode = CSSM_ALGMODE_OFB;
break;
case SEC_OID_DES_CFB:
*mode = CSSM_ALGMODE_CFB;
break;
default:
dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag);
return errSecDataNotAvailable;
}
return noErr;
}
#pragma mark ---- ECDH CEK key wrap ----
OSStatus
SecCmsUtilEncryptSymKeyECDH(
PLArenaPool *poolp,
SecCertificateRef cert,
SecSymmetricKeyRef key,
CSSM_DATA_PTR encKey,
CSSM_DATA_PTR ukm,
SECAlgorithmID *keyEncAlg,
CSSM_DATA_PTR pubKey)
{
OSStatus rv = noErr;
CSSM_KEY ourPrivKeyCssm;
CSSM_KEY ourPubKeyCssm;
SecKeyRef theirPubKeyRef = NULL;
CSSM_KEY_PTR theirPubKeyCssm = NULL;
const CSSM_KEY *cekCssmRef = NULL;
uint32 ecdhKeySizeBits;
CSSM_CSP_HANDLE rawCspHand = SecCspHandleForAlgorithm(CSSM_ALGID_ECDH);
CSSM_CC_HANDLE ccHand = 0;
CSSM_RETURN crtn;
CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"};
SECAlgorithmID kekAlgId;
uint8 iv[ECDH_KEK_IV_LEN_BYTES];
CSSM_DATA ivData = {ECDH_KEK_IV_LEN_BYTES, iv};
SECOidData *kekOid;
ECC_CMS_SharedInfo sharedInfo;
CSSM_DATA sharedInfoEnc = {0, NULL};
uint8 nullData[2] = {SEC_ASN1_NULL, 0};
uint8 keyLenAsBytes[4];
CSSM_KEY kekDerive;
CSSM_DATA certData;
CSSM_CL_HANDLE clHand;
CSSM_ACCESS_CREDENTIALS creds;
CSSM_DATA paramData = {0, NULL};
CSSM_KEY cekCssm;
CSSM_CSP_HANDLE refCspHand;
CSSM_SIZE bytesEncrypted;
CSSM_DATA remData = {0, NULL};
CSSM_DATA ctext = {0, NULL};
CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKey;
if(rawCspHand == 0) {
return internalComponentErr;
}
memset(&ourPrivKeyCssm, 0, sizeof(CSSM_KEY));
memset(&ourPubKeyCssm, 0, sizeof(CSSM_KEY));
memset(&cekCssm, 0, sizeof(CSSM_KEY));
memset(&kekDerive, 0, sizeof(kekDerive));
encKey->Data = NULL;
encKey->Length = 0;
rv = SecCertificateGetData(cert, &certData);
if(rv) {
CSSM_PERROR("SecCertificateGetData", rv);
return rv;
}
rv = SecCertificateGetCLHandle(cert, &clHand);
if(rv) {
CSSM_PERROR("SecCertificateGetCLHandle", rv);
return rv;
}
rv = CSSM_CL_CertGetKeyInfo(clHand, &certData, &theirPubKeyCssm);
if(rv) {
CSSM_PERROR("CSSM_CL_CertGetKeyInfo", rv);
return rv;
}
memset(&subjPubKey, 0, sizeof(subjPubKey));
if(SEC_ASN1DecodeItem(poolp, &subjPubKey, kSecAsn1SubjectPublicKeyInfoTemplate,
&theirPubKeyCssm->KeyData)) {
dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding SubjPubKey\n");
}
else {
if(subjPubKey.algorithm.parameters.Data != NULL) {
CSSM_DATA curveOid;
if(SEC_ASN1DecodeItem(poolp, &curveOid, kSecAsn1ObjectIDTemplate,
&subjPubKey.algorithm.parameters)) {
dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding curveOid\n");
}
else {
SECOidTag oidTag = SECOID_FindOIDTag(&curveOid);
switch(oidTag) {
case SEC_OID_SECP_256_R1:
case SEC_OID_SECP_384_R1:
case SEC_OID_SECP_521_R1:
break;
default:
dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported curveOid\n");
rv = CSSMERR_CSP_INVALID_KEY;
goto loser;
}
}
}
}
ecdhKeySizeBits = theirPubKeyCssm->KeyHeader.LogicalKeySizeInBits;
crtn = CSSM_CSP_CreateKeyGenContext(rawCspHand,
CSSM_ALGID_ECDSA,
ecdhKeySizeBits,
NULL, NULL, NULL, NULL, NULL, &ccHand);
if(crtn) {
CSSM_PERROR("CSSM_CSP_CreateKeyGenContext", crtn);
rv = crtn;
goto loser;
}
crtn = cmsAddContextAttribute(ccHand,
CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
sizeof(uint32),
CAT_Uint32,
NULL,
CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
if(crtn) {
CSSM_PERROR("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT)", crtn);
rv = crtn;
goto loser;
}
crtn = CSSM_GenerateKeyPair(ccHand,
CSSM_KEYUSE_DERIVE,
CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
&keyLabel,
&ourPubKeyCssm,
CSSM_KEYUSE_DERIVE,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE,
&keyLabel,
NULL, &ourPrivKeyCssm);
CSSM_DeleteContext(ccHand);
ccHand = 0;
if(crtn) {
CSSM_PERROR("CSSM_GenerateKeyPair", crtn);
rv = crtn;
goto loser;
}
pubKey->Length = ourPubKeyCssm.KeyData.Length;
pubKey->Data = (uint8 *)PORT_ArenaAlloc(poolp, pubKey->Length);
memmove(pubKey->Data, ourPubKeyCssm.KeyData.Data, pubKey->Length);
dumpBuf("sender's public key", pubKey);
ukm->Data = (uint8 *)PORT_ArenaAlloc(poolp, UKM_LENGTH);
ukm->Length = UKM_LENGTH;
crtn = cmsGenRand(rawCspHand, UKM_LENGTH, ukm->Data);
if(crtn) {
goto loser;
}
dumpBuf("sender UKM", ukm);
crtn = cmsGenRand(rawCspHand, ECDH_KEK_IV_LEN_BYTES, iv);
if(crtn) {
goto loser;
}
dumpBuf("sender IV", &ivData);
memset(&kekAlgId, 0, sizeof(kekAlgId));
if (!SEC_ASN1EncodeItem(poolp, &kekAlgId.parameters,
&ivData, kSecAsn1OctetStringTemplate)) {
rv = internalComponentErr;
goto loser;
}
kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
if(kekOid == NULL) {
dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
rv = internalComponentErr;
goto loser;
}
kekAlgId.algorithm = kekOid->oid;
memset(keyEncAlg, 0, sizeof(*keyEncAlg));
if (!SEC_ASN1EncodeItem(poolp, &keyEncAlg->parameters,
&kekAlgId, SECOID_AlgorithmIDTemplate)) {
rv = internalComponentErr;
goto loser;
}
kekOid = SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF);
if(kekOid == NULL) {
dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
rv = internalComponentErr;
goto loser;
}
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 = internalComponentErr;
goto loser;
}
dumpBuf("sender encoded SharedInfo", &sharedInfoEnc);
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
crtn = CSSM_CSP_CreateDeriveKeyContext(rawCspHand,
CSSM_ALGID_ECDH_X963_KDF,
ECDH_KEK_KEY_CSSM_ALGID, ECDH_KEK_KEY_LEN_BYTES * 8,
&creds,
&ourPrivKeyCssm, 0, &sharedInfoEnc, 0, &ccHand);
if(crtn) {
CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn);
rv = crtn;
goto loser;
}
crtn = cmsAddContextAttribute(ccHand,
CSSM_ATTRIBUTE_PUBLIC_KEY,
sizeof(CSSM_KEY),
CAT_Ptr,
(void *)theirPubKeyCssm,
0);
if(crtn) {
rv = crtn;
goto loser;
}
crtn = CSSM_DeriveKey(ccHand,
¶mData,
CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
&keyLabel,
NULL, &kekDerive);
if(crtn) {
CSSM_PERROR("CSSM_DeriveKey", crtn);
rv = crtn;
goto loser;
}
CSSM_DeleteContext(ccHand);
ccHand = 0;
rv = SecKeyGetCSSMKey(key, &cekCssmRef);
if(rv) {
CSSM_PERROR("SecKeyGetCSSMKey", rv);
goto loser;
}
rv = SecKeyGetCSPHandle(key, &refCspHand);
if(rv) {
CSSM_PERROR("SecKeyGetCSPHandle", rv);
goto loser;
}
rv = cmsNullWrapKey(refCspHand, cekCssmRef, &cekCssm);
if(rv) {
goto loser;
}
crtn = CSSM_CSP_CreateSymmetricContext(rawCspHand,
ECDH_KEK_ENCR_CSSM_ALGID,
CSSM_ALGMODE_CBCPadIV8,
NULL, &kekDerive,
&ivData, CSSM_PADDING_PKCS7,
NULL, &ccHand);
if(rv) {
CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv);
goto loser;
}
rv = CSSM_EncryptData(ccHand,
&cekCssm.KeyData,
1,
&ctext,
1,
&bytesEncrypted,
&remData);
if(rv) {
CSSM_PERROR("CSSM_EncryptData", rv);
goto loser;
}
encKey->Data = PORT_ArenaAlloc(poolp, bytesEncrypted);
encKey->Length = bytesEncrypted;
memmove(encKey->Data, ctext.Data, ctext.Length);
if(bytesEncrypted != ctext.Length) {
memmove(encKey->Data + ctext.Length, remData.Data, remData.Length);
}
dumpBuf("sender encKey", encKey);
loser:
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
CFRELEASE(theirPubKeyRef);
if(ourPubKeyCssm.KeyData.Data) {
CSSM_FreeKey(rawCspHand, NULL, &ourPubKeyCssm, CSSM_FALSE);
}
if(ourPrivKeyCssm.KeyData.Data) {
CSSM_FreeKey(rawCspHand, NULL, &ourPrivKeyCssm, CSSM_FALSE);
}
if(ctext.Data) {
cmsFreeCssmMemory(rawCspHand, ctext.Data);
}
if(remData.Data) {
cmsFreeCssmMemory(rawCspHand, remData.Data);
}
if(cekCssm.KeyData.Data) {
CSSM_FreeKey(refCspHand, NULL, &cekCssm, CSSM_FALSE);
}
if(kekDerive.KeyData.Data) {
CSSM_FreeKey(rawCspHand, NULL, &kekDerive, CSSM_FALSE);
}
if(theirPubKeyCssm) {
cmsFreeCssmMemory(clHand, theirPubKeyCssm->KeyData.Data);
cmsFreeCssmMemory(clHand, theirPubKeyCssm);
}
return rv;
}
#pragma mark ---- ECDH CEK key unwrap ----
SecSymmetricKeyRef
SecCmsUtilDecryptSymKeyECDH(
SecPrivateKeyRef privkey,
CSSM_DATA_PTR encKey,
CSSM_DATA_PTR ukm,
SECAlgorithmID *keyEncAlg,
SECOidTag bulkalgtag,
CSSM_DATA_PTR pubKey)
{
SecSymmetricKeyRef outKey = NULL;
OSStatus rv = noErr;
const CSSM_KEY *ourPrivKeyCssm;
PLArenaPool *pool = NULL;
SECAlgorithmID keyAlgParam;
SECOidData *kekOid = NULL;
CSSM_DATA iv = {0, NULL};
ECC_CMS_SharedInfo sharedInfo;
CSSM_DATA sharedInfoEnc = {0, NULL};
uint8 nullData[2] = {SEC_ASN1_NULL, 0};
uint8 keyLenAsBytes[4];
CSSM_ENCRYPT_MODE kekMode;
uint32 kekSizeBits;
CSSM_KEY kekDerive;
CSSM_RETURN crtn;
CSSM_ACCESS_CREDENTIALS creds;
CSSM_CSP_HANDLE refCspHand;
CSSM_CC_HANDLE ccHand = 0;
CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"};
const CSSM_ACCESS_CREDENTIALS *accessCred;
CSSM_KEY wrappedKey;
CSSM_KEY unwrappedKey;
CSSM_ALGORITHMS bulkAlg;
CSSM_DATA descriptiveData = {0, NULL};
dumpBuf("receiver encKey", encKey);
memset(&kekDerive, 0, sizeof(kekDerive));
rv = SecKeyGetCSSMKey(privkey, &ourPrivKeyCssm);
if(rv) {
CSSM_PERROR("SecKeyGetCSSMKey", rv);
goto loser;
}
pool = PORT_NewArena(1024);
if(pool == NULL) {
goto loser;
}
memset(&keyAlgParam, 0, sizeof(keyAlgParam));
if(SEC_ASN1DecodeItem(pool, &keyAlgParam, SECOID_AlgorithmIDTemplate,
&keyEncAlg->parameters)) {
dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
goto loser;
}
kekOid = SECOID_FindOID(&keyAlgParam.algorithm);
if(kekOid == NULL) {
dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
goto loser;
}
rv = encrAlgInfo(kekOid->offset, &kekSizeBits, &kekMode);
if(rv) {
goto loser;
}
if(SEC_ASN1DecodeItem(pool, &iv, kSecAsn1OctetStringTemplate,
&keyAlgParam.parameters)) {
dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
goto loser;
}
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)) {
rv = internalComponentErr;
goto loser;
}
dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc);
dumpBuf("receiver IV", &iv);
dumpBuf("receiver UKM", ukm);
dumpBuf("sender's public key", pubKey);
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
rv = SecKeyGetCSPHandle(privkey, &refCspHand);
if(rv) {
CSSM_PERROR("SecKeyGetCSPHandle", rv);
goto loser;
}
rv = SecKeyGetCredentials(privkey,
CSSM_ACL_AUTHORIZATION_DERIVE,
kSecCredentialTypeDefault,
&accessCred);
if (rv) {
CSSM_PERROR("SecKeyGetCredentials", rv);
goto loser;
}
crtn = CSSM_CSP_CreateDeriveKeyContext(refCspHand,
CSSM_ALGID_ECDH_X963_KDF,
kekOid->cssmAlgorithm, kekSizeBits,
&creds,
ourPrivKeyCssm, 0, &sharedInfoEnc, 0, &ccHand);
if(crtn) {
CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn);
goto loser;
}
crtn = CSSM_DeriveKey(ccHand,
pubKey, CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE,
&keyLabel,
NULL, &kekDerive);
CSSM_DeleteContext(ccHand);
ccHand = 0;
if(crtn) {
CSSM_PERROR("CSSM_DeriveKey", crtn);
goto loser;
}
crtn = CSSM_CSP_CreateSymmetricContext(refCspHand,
kekOid->cssmAlgorithm,
kekMode,
NULL, &kekDerive,
&iv,
CSSM_PADDING_PKCS7,
NULL, &ccHand);
if(rv) {
CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv);
goto loser;
}
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
bulkAlg = SECOID_FindyCssmAlgorithmByTag(bulkalgtag);
if(bulkAlg == CSSM_ALGID_NONE) {
dprintf("SecCmsUtilDecryptSymKeyECDH: unknown bulk alg\n");
goto loser;
}
wrappedKey.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION;
wrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED;
wrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
wrappedKey.KeyHeader.AlgorithmId = bulkAlg;
wrappedKey.KeyHeader.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
wrappedKey.KeyHeader.WrapAlgorithmId = kekOid->cssmAlgorithm;
wrappedKey.KeyHeader.WrapMode = CSSM_ALGMODE_NONE;
wrappedKey.KeyData = *encKey;
crtn = CSSM_UnwrapKey(ccHand,
NULL,
&wrappedKey,
CSSM_KEYUSE_DECRYPT,
CSSM_KEYATTR_EXTRACTABLE,
&keyLabel,
NULL,
&unwrappedKey,
&descriptiveData);
CSSM_DeleteContext(ccHand);
ccHand = 0;
if(crtn) {
CSSM_PERROR("CSSM_UnwrapKey", crtn);
goto loser;
}
rv = SecKeyCreateWithCSSMKey(&unwrappedKey, &outKey);
if (rv) {
CSSM_PERROR("SecKeyCreateWithCSSMKey", rv);
}
loser:
if(pool != NULL) {
PORT_FreeArena(pool, PR_FALSE);
}
if(kekDerive.KeyData.Data) {
CSSM_FreeKey(refCspHand, NULL, &kekDerive, CSSM_FALSE);
}
if(outKey == NULL) {
PORT_SetError(SEC_ERROR_NO_KEY);
}
return outKey;
}