security/nss/lib/smime/cmspubkey.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/smime/cmspubkey.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,285 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * CMS public key crypto
    1.10 + */
    1.11 +
    1.12 +#include "cmslocal.h"
    1.13 +
    1.14 +#include "cert.h"
    1.15 +#include "key.h"
    1.16 +#include "secasn1.h"
    1.17 +#include "secitem.h"
    1.18 +#include "secoid.h"
    1.19 +#include "pk11func.h"
    1.20 +#include "secerr.h"
    1.21 +
    1.22 +/* ====== RSA ======================================================================= */
    1.23 +
    1.24 +/*
    1.25 + * NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
    1.26 + *
    1.27 + * this function takes a symmetric key and encrypts it using an RSA public key
    1.28 + * according to PKCS#1 and RFC2633 (S/MIME)
    1.29 + */
    1.30 +SECStatus
    1.31 +NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, 
    1.32 +                              PK11SymKey *bulkkey,
    1.33 +                              SECItem *encKey)
    1.34 +{
    1.35 +    SECStatus rv;
    1.36 +    SECKEYPublicKey *publickey;
    1.37 +
    1.38 +    publickey = CERT_ExtractPublicKey(cert);
    1.39 +    if (publickey == NULL)
    1.40 +	return SECFailure;
    1.41 +
    1.42 +    rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey);
    1.43 +    SECKEY_DestroyPublicKey(publickey);
    1.44 +    return rv;
    1.45 +}
    1.46 +
    1.47 +SECStatus
    1.48 +NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp, 
    1.49 +                                    SECKEYPublicKey *publickey, 
    1.50 +                                    PK11SymKey *bulkkey, SECItem *encKey)
    1.51 +{
    1.52 +    SECStatus rv;
    1.53 +    int data_len;
    1.54 +    KeyType keyType;
    1.55 +    void *mark = NULL;
    1.56 +
    1.57 +
    1.58 +    mark = PORT_ArenaMark(poolp);
    1.59 +    if (!mark)
    1.60 +	goto loser;
    1.61 +
    1.62 +    /* sanity check */
    1.63 +    keyType = SECKEY_GetPublicKeyType(publickey);
    1.64 +    PORT_Assert(keyType == rsaKey);
    1.65 +    if (keyType != rsaKey) {
    1.66 +	goto loser;
    1.67 +    }
    1.68 +    /* allocate memory for the encrypted key */
    1.69 +    data_len = SECKEY_PublicKeyStrength(publickey);	/* block size (assumed to be > keylen) */
    1.70 +    encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len);
    1.71 +    encKey->len = data_len;
    1.72 +    if (encKey->data == NULL)
    1.73 +	goto loser;
    1.74 +
    1.75 +    /* encrypt the key now */
    1.76 +    rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION),
    1.77 +				publickey, bulkkey, encKey);
    1.78 +
    1.79 +    if (rv != SECSuccess)
    1.80 +	goto loser;
    1.81 +
    1.82 +    PORT_ArenaUnmark(poolp, mark);
    1.83 +    return SECSuccess;
    1.84 +
    1.85 +loser:
    1.86 +    if (mark) {
    1.87 +	PORT_ArenaRelease(poolp, mark);
    1.88 +    }
    1.89 +    return SECFailure;
    1.90 +}
    1.91 +
    1.92 +/*
    1.93 + * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
    1.94 + *
    1.95 + * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
    1.96 + * key handle. Please note that the actual unwrapped key data may not be allowed to leave
    1.97 + * a hardware token...
    1.98 + */
    1.99 +PK11SymKey *
   1.100 +NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag)
   1.101 +{
   1.102 +    /* that's easy */
   1.103 +    CK_MECHANISM_TYPE target;
   1.104 +    PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
   1.105 +    target = PK11_AlgtagToMechanism(bulkalgtag);
   1.106 +    if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
   1.107 +	PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   1.108 +	return NULL;
   1.109 +    }
   1.110 +    return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0);
   1.111 +}
   1.112 +
   1.113 +/* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
   1.114 +
   1.115 +SECStatus
   1.116 +NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
   1.117 +			SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg,
   1.118 +			SECItem *pubKey)
   1.119 +{
   1.120 +#if 0 /* not yet done */
   1.121 +    SECOidTag certalgtag;	/* the certificate's encryption algorithm */
   1.122 +    SECOidTag encalgtag;	/* the algorithm used for key exchange/agreement */
   1.123 +    SECStatus rv;
   1.124 +    SECItem *params = NULL;
   1.125 +    int data_len;
   1.126 +    SECStatus err;
   1.127 +    PK11SymKey *tek;
   1.128 +    CERTCertificate *ourCert;
   1.129 +    SECKEYPublicKey *ourPubKey;
   1.130 +    NSSCMSKEATemplateSelector whichKEA = NSSCMSKEAInvalid;
   1.131 +
   1.132 +    certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
   1.133 +    PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
   1.134 +
   1.135 +    /* We really want to show our KEA tag as the key exchange algorithm tag. */
   1.136 +    encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
   1.137 +
   1.138 +    /* Get the public key of the recipient. */
   1.139 +    publickey = CERT_ExtractPublicKey(cert);
   1.140 +    if (publickey == NULL) goto loser;
   1.141 +
   1.142 +    /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
   1.143 +    /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
   1.144 +    if (ourCert == NULL) goto loser;
   1.145 +
   1.146 +    arena = PORT_NewArena(1024);
   1.147 +    if (arena == NULL) goto loser;
   1.148 +
   1.149 +    /* While we're here, extract the key pair's public key data and copy it into */
   1.150 +    /* the outgoing parameters. */
   1.151 +    /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
   1.152 +    if (ourPubKey == NULL)
   1.153 +    {
   1.154 +	goto loser;
   1.155 +    }
   1.156 +    SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
   1.157 +    SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
   1.158 +    ourPubKey = NULL;
   1.159 +
   1.160 +    /* Extract our private key in order to derive the KEA key. */
   1.161 +    ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
   1.162 +    CERT_DestroyCertificate(ourCert); /* we're done with this */
   1.163 +    if (!ourPrivKey) goto loser;
   1.164 +
   1.165 +    /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
   1.166 +    if (ukm) {
   1.167 +	ukm->data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
   1.168 +	ukm->len = /* XXXX */;
   1.169 +    }
   1.170 +
   1.171 +    /* Generate the KEK (key exchange key) according to RFC2631 which we use
   1.172 +     * to wrap the bulk encryption key. */
   1.173 +    kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
   1.174 +			 ukm, NULL,
   1.175 +			 /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
   1.176 +			 CKA_WRAP, 0, wincx);
   1.177 +
   1.178 +    SECKEY_DestroyPublicKey(publickey);
   1.179 +    SECKEY_DestroyPrivateKey(ourPrivKey);
   1.180 +    publickey = NULL;
   1.181 +    ourPrivKey = NULL;
   1.182 +    
   1.183 +    if (!kek)
   1.184 +	goto loser;
   1.185 +
   1.186 +    /* allocate space for the encrypted CEK (bulk key) */
   1.187 +    encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
   1.188 +    encKey->len = SMIME_FORTEZZA_MAX_KEY_SIZE;
   1.189 +
   1.190 +    if (encKey->data == NULL)
   1.191 +    {
   1.192 +	PK11_FreeSymKey(kek);
   1.193 +	goto loser;
   1.194 +    }
   1.195 +
   1.196 +
   1.197 +    /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
   1.198 +    /* bulk encryption algorithm */
   1.199 +    switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
   1.200 +    {
   1.201 +    case /* XXXX */CKM_SKIPJACK_CFB8:
   1.202 +	err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
   1.203 +	whichKEA = NSSCMSKEAUsesSkipjack;
   1.204 +	break;
   1.205 +    case /* XXXX */CKM_SKIPJACK_CFB8:
   1.206 +	err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
   1.207 +	whichKEA = NSSCMSKEAUsesSkipjack;
   1.208 +	break;
   1.209 +    default:
   1.210 +	/* XXXX what do we do here? Neither RC2 nor 3DES... */
   1.211 +        err = SECFailure;
   1.212 +        /* set error */
   1.213 +	break;
   1.214 +    }
   1.215 +
   1.216 +    PK11_FreeSymKey(kek);	/* we do not need the KEK anymore */
   1.217 +    if (err != SECSuccess)
   1.218 +	goto loser;
   1.219 +
   1.220 +    PORT_Assert(whichKEA != NSSCMSKEAInvalid);
   1.221 +
   1.222 +    /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
   1.223 +    /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
   1.224 +    params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
   1.225 +    if (params == NULL)
   1.226 +	goto loser;
   1.227 +
   1.228 +    /* now set keyEncAlg */
   1.229 +    rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
   1.230 +    if (rv != SECSuccess)
   1.231 +	goto loser;
   1.232 +
   1.233 +    /* XXXXXXX this is not right yet */
   1.234 +loser:
   1.235 +    if (arena) {
   1.236 +	PORT_FreeArena(arena, PR_FALSE);
   1.237 +    }
   1.238 +    if (publickey) {
   1.239 +        SECKEY_DestroyPublicKey(publickey);
   1.240 +    }
   1.241 +    if (ourPrivKey) {
   1.242 +        SECKEY_DestroyPrivateKey(ourPrivKey);
   1.243 +    }
   1.244 +#endif
   1.245 +    return SECFailure;
   1.246 +}
   1.247 +
   1.248 +PK11SymKey *
   1.249 +NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
   1.250 +{
   1.251 +#if 0 /* not yet done */
   1.252 +    SECStatus err;
   1.253 +    CK_MECHANISM_TYPE bulkType;
   1.254 +    PK11SymKey *tek;
   1.255 +    SECKEYPublicKey *originatorPubKey;
   1.256 +    NSSCMSSMIMEKEAParameters keaParams;
   1.257 +
   1.258 +   /* XXXX get originator's public key */
   1.259 +   originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
   1.260 +			   keaParams.originatorKEAKey.len);
   1.261 +   if (originatorPubKey == NULL)
   1.262 +      goto loser;
   1.263 +    
   1.264 +   /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
   1.265 +      The Derive function generates a shared secret and combines it with the originatorRA
   1.266 +      data to come up with an unique session key */
   1.267 +   tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
   1.268 +			 &keaParams.originatorRA, NULL,
   1.269 +			 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
   1.270 +			 CKA_WRAP, 0, pwfn_arg);
   1.271 +   SECKEY_DestroyPublicKey(originatorPubKey);	/* not needed anymore */
   1.272 +   if (tek == NULL)
   1.273 +	goto loser;
   1.274 +    
   1.275 +    /* Now that we have the TEK, unwrap the bulk key
   1.276 +       with which to decrypt the message. */
   1.277 +    /* Skipjack is being used as the bulk encryption algorithm.*/
   1.278 +    /* Unwrap the bulk key. */
   1.279 +    bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
   1.280 +				encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
   1.281 +
   1.282 +    return bulkkey;
   1.283 +
   1.284 +loser:
   1.285 +#endif
   1.286 +    return NULL;
   1.287 +}
   1.288 +

mercurial