Blame nss/lib/smime/cmspubkey.c

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