michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * CMS public key crypto michael@0: */ michael@0: michael@0: #include "cmslocal.h" michael@0: michael@0: #include "cert.h" michael@0: #include "key.h" michael@0: #include "secasn1.h" michael@0: #include "secitem.h" michael@0: #include "secoid.h" michael@0: #include "pk11func.h" michael@0: #include "secerr.h" michael@0: michael@0: /* ====== RSA ======================================================================= */ michael@0: michael@0: /* michael@0: * NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA michael@0: * michael@0: * this function takes a symmetric key and encrypts it using an RSA public key michael@0: * according to PKCS#1 and RFC2633 (S/MIME) michael@0: */ michael@0: SECStatus michael@0: NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, michael@0: PK11SymKey *bulkkey, michael@0: SECItem *encKey) michael@0: { michael@0: SECStatus rv; michael@0: SECKEYPublicKey *publickey; michael@0: michael@0: publickey = CERT_ExtractPublicKey(cert); michael@0: if (publickey == NULL) michael@0: return SECFailure; michael@0: michael@0: rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey); michael@0: SECKEY_DestroyPublicKey(publickey); michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp, michael@0: SECKEYPublicKey *publickey, michael@0: PK11SymKey *bulkkey, SECItem *encKey) michael@0: { michael@0: SECStatus rv; michael@0: int data_len; michael@0: KeyType keyType; michael@0: void *mark = NULL; michael@0: michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: if (!mark) michael@0: goto loser; michael@0: michael@0: /* sanity check */ michael@0: keyType = SECKEY_GetPublicKeyType(publickey); michael@0: PORT_Assert(keyType == rsaKey); michael@0: if (keyType != rsaKey) { michael@0: goto loser; michael@0: } michael@0: /* allocate memory for the encrypted key */ michael@0: data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */ michael@0: encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len); michael@0: encKey->len = data_len; michael@0: if (encKey->data == NULL) michael@0: goto loser; michael@0: michael@0: /* encrypt the key now */ michael@0: rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION), michael@0: publickey, bulkkey, encKey); michael@0: michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if (mark) { michael@0: PORT_ArenaRelease(poolp, mark); michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key michael@0: * michael@0: * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric michael@0: * key handle. Please note that the actual unwrapped key data may not be allowed to leave michael@0: * a hardware token... michael@0: */ michael@0: PK11SymKey * michael@0: NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag) michael@0: { michael@0: /* that's easy */ michael@0: CK_MECHANISM_TYPE target; michael@0: PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN); michael@0: target = PK11_AlgtagToMechanism(bulkalgtag); michael@0: if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); michael@0: return NULL; michael@0: } michael@0: return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0); michael@0: } michael@0: michael@0: /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */ michael@0: michael@0: SECStatus michael@0: NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key, michael@0: SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg, michael@0: SECItem *pubKey) michael@0: { michael@0: #if 0 /* not yet done */ michael@0: SECOidTag certalgtag; /* the certificate's encryption algorithm */ michael@0: SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */ michael@0: SECStatus rv; michael@0: SECItem *params = NULL; michael@0: int data_len; michael@0: SECStatus err; michael@0: PK11SymKey *tek; michael@0: CERTCertificate *ourCert; michael@0: SECKEYPublicKey *ourPubKey; michael@0: NSSCMSKEATemplateSelector whichKEA = NSSCMSKEAInvalid; michael@0: michael@0: certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); michael@0: PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY); michael@0: michael@0: /* We really want to show our KEA tag as the key exchange algorithm tag. */ michael@0: encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN; michael@0: michael@0: /* Get the public key of the recipient. */ michael@0: publickey = CERT_ExtractPublicKey(cert); michael@0: if (publickey == NULL) goto loser; michael@0: michael@0: /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */ michael@0: /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx); michael@0: if (ourCert == NULL) goto loser; michael@0: michael@0: arena = PORT_NewArena(1024); michael@0: if (arena == NULL) goto loser; michael@0: michael@0: /* While we're here, extract the key pair's public key data and copy it into */ michael@0: /* the outgoing parameters. */ michael@0: /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert); michael@0: if (ourPubKey == NULL) michael@0: { michael@0: goto loser; michael@0: } michael@0: SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey)); michael@0: SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */ michael@0: ourPubKey = NULL; michael@0: michael@0: /* Extract our private key in order to derive the KEA key. */ michael@0: ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx); michael@0: CERT_DestroyCertificate(ourCert); /* we're done with this */ michael@0: if (!ourPrivKey) goto loser; michael@0: michael@0: /* If ukm desired, prepare it - allocate enough space (filled with zeros). */ michael@0: if (ukm) { michael@0: ukm->data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */); michael@0: ukm->len = /* XXXX */; michael@0: } michael@0: michael@0: /* Generate the KEK (key exchange key) according to RFC2631 which we use michael@0: * to wrap the bulk encryption key. */ michael@0: kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE, michael@0: ukm, NULL, michael@0: /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP, michael@0: CKA_WRAP, 0, wincx); michael@0: michael@0: SECKEY_DestroyPublicKey(publickey); michael@0: SECKEY_DestroyPrivateKey(ourPrivKey); michael@0: publickey = NULL; michael@0: ourPrivKey = NULL; michael@0: michael@0: if (!kek) michael@0: goto loser; michael@0: michael@0: /* allocate space for the encrypted CEK (bulk key) */ michael@0: encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE); michael@0: encKey->len = SMIME_FORTEZZA_MAX_KEY_SIZE; michael@0: michael@0: if (encKey->data == NULL) michael@0: { michael@0: PK11_FreeSymKey(kek); michael@0: goto loser; michael@0: } michael@0: michael@0: michael@0: /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */ michael@0: /* bulk encryption algorithm */ michael@0: switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg)) michael@0: { michael@0: case /* XXXX */CKM_SKIPJACK_CFB8: michael@0: err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey); michael@0: whichKEA = NSSCMSKEAUsesSkipjack; michael@0: break; michael@0: case /* XXXX */CKM_SKIPJACK_CFB8: michael@0: err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey); michael@0: whichKEA = NSSCMSKEAUsesSkipjack; michael@0: break; michael@0: default: michael@0: /* XXXX what do we do here? Neither RC2 nor 3DES... */ michael@0: err = SECFailure; michael@0: /* set error */ michael@0: break; michael@0: } michael@0: michael@0: PK11_FreeSymKey(kek); /* we do not need the KEK anymore */ michael@0: if (err != SECSuccess) michael@0: goto loser; michael@0: michael@0: PORT_Assert(whichKEA != NSSCMSKEAInvalid); michael@0: michael@0: /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */ michael@0: /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */ michael@0: params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA)); michael@0: if (params == NULL) michael@0: goto loser; michael@0: michael@0: /* now set keyEncAlg */ michael@0: rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: michael@0: /* XXXXXXX this is not right yet */ michael@0: loser: michael@0: if (arena) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: if (publickey) { michael@0: SECKEY_DestroyPublicKey(publickey); michael@0: } michael@0: if (ourPrivKey) { michael@0: SECKEY_DestroyPrivateKey(ourPrivKey); michael@0: } michael@0: #endif michael@0: return SECFailure; michael@0: } michael@0: michael@0: PK11SymKey * michael@0: NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg) michael@0: { michael@0: #if 0 /* not yet done */ michael@0: SECStatus err; michael@0: CK_MECHANISM_TYPE bulkType; michael@0: PK11SymKey *tek; michael@0: SECKEYPublicKey *originatorPubKey; michael@0: NSSCMSSMIMEKEAParameters keaParams; michael@0: michael@0: /* XXXX get originator's public key */ michael@0: originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data, michael@0: keaParams.originatorKEAKey.len); michael@0: if (originatorPubKey == NULL) michael@0: goto loser; michael@0: michael@0: /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key. michael@0: The Derive function generates a shared secret and combines it with the originatorRA michael@0: data to come up with an unique session key */ michael@0: tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE, michael@0: &keaParams.originatorRA, NULL, michael@0: CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, michael@0: CKA_WRAP, 0, pwfn_arg); michael@0: SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */ michael@0: if (tek == NULL) michael@0: goto loser; michael@0: michael@0: /* Now that we have the TEK, unwrap the bulk key michael@0: with which to decrypt the message. */ michael@0: /* Skipjack is being used as the bulk encryption algorithm.*/ michael@0: /* Unwrap the bulk key. */ michael@0: bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL, michael@0: encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0); michael@0: michael@0: return bulkkey; michael@0: michael@0: loser: michael@0: #endif michael@0: return NULL; michael@0: } michael@0: