security/nss/lib/smime/cmspubkey.c

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * CMS public key crypto
michael@0 7 */
michael@0 8
michael@0 9 #include "cmslocal.h"
michael@0 10
michael@0 11 #include "cert.h"
michael@0 12 #include "key.h"
michael@0 13 #include "secasn1.h"
michael@0 14 #include "secitem.h"
michael@0 15 #include "secoid.h"
michael@0 16 #include "pk11func.h"
michael@0 17 #include "secerr.h"
michael@0 18
michael@0 19 /* ====== RSA ======================================================================= */
michael@0 20
michael@0 21 /*
michael@0 22 * NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
michael@0 23 *
michael@0 24 * this function takes a symmetric key and encrypts it using an RSA public key
michael@0 25 * according to PKCS#1 and RFC2633 (S/MIME)
michael@0 26 */
michael@0 27 SECStatus
michael@0 28 NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert,
michael@0 29 PK11SymKey *bulkkey,
michael@0 30 SECItem *encKey)
michael@0 31 {
michael@0 32 SECStatus rv;
michael@0 33 SECKEYPublicKey *publickey;
michael@0 34
michael@0 35 publickey = CERT_ExtractPublicKey(cert);
michael@0 36 if (publickey == NULL)
michael@0 37 return SECFailure;
michael@0 38
michael@0 39 rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey);
michael@0 40 SECKEY_DestroyPublicKey(publickey);
michael@0 41 return rv;
michael@0 42 }
michael@0 43
michael@0 44 SECStatus
michael@0 45 NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp,
michael@0 46 SECKEYPublicKey *publickey,
michael@0 47 PK11SymKey *bulkkey, SECItem *encKey)
michael@0 48 {
michael@0 49 SECStatus rv;
michael@0 50 int data_len;
michael@0 51 KeyType keyType;
michael@0 52 void *mark = NULL;
michael@0 53
michael@0 54
michael@0 55 mark = PORT_ArenaMark(poolp);
michael@0 56 if (!mark)
michael@0 57 goto loser;
michael@0 58
michael@0 59 /* sanity check */
michael@0 60 keyType = SECKEY_GetPublicKeyType(publickey);
michael@0 61 PORT_Assert(keyType == rsaKey);
michael@0 62 if (keyType != rsaKey) {
michael@0 63 goto loser;
michael@0 64 }
michael@0 65 /* allocate memory for the encrypted key */
michael@0 66 data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */
michael@0 67 encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len);
michael@0 68 encKey->len = data_len;
michael@0 69 if (encKey->data == NULL)
michael@0 70 goto loser;
michael@0 71
michael@0 72 /* encrypt the key now */
michael@0 73 rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION),
michael@0 74 publickey, bulkkey, encKey);
michael@0 75
michael@0 76 if (rv != SECSuccess)
michael@0 77 goto loser;
michael@0 78
michael@0 79 PORT_ArenaUnmark(poolp, mark);
michael@0 80 return SECSuccess;
michael@0 81
michael@0 82 loser:
michael@0 83 if (mark) {
michael@0 84 PORT_ArenaRelease(poolp, mark);
michael@0 85 }
michael@0 86 return SECFailure;
michael@0 87 }
michael@0 88
michael@0 89 /*
michael@0 90 * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
michael@0 91 *
michael@0 92 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
michael@0 93 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
michael@0 94 * a hardware token...
michael@0 95 */
michael@0 96 PK11SymKey *
michael@0 97 NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag)
michael@0 98 {
michael@0 99 /* that's easy */
michael@0 100 CK_MECHANISM_TYPE target;
michael@0 101 PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
michael@0 102 target = PK11_AlgtagToMechanism(bulkalgtag);
michael@0 103 if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
michael@0 104 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
michael@0 105 return NULL;
michael@0 106 }
michael@0 107 return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0);
michael@0 108 }
michael@0 109
michael@0 110 /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
michael@0 111
michael@0 112 SECStatus
michael@0 113 NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
michael@0 114 SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg,
michael@0 115 SECItem *pubKey)
michael@0 116 {
michael@0 117 #if 0 /* not yet done */
michael@0 118 SECOidTag certalgtag; /* the certificate's encryption algorithm */
michael@0 119 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
michael@0 120 SECStatus rv;
michael@0 121 SECItem *params = NULL;
michael@0 122 int data_len;
michael@0 123 SECStatus err;
michael@0 124 PK11SymKey *tek;
michael@0 125 CERTCertificate *ourCert;
michael@0 126 SECKEYPublicKey *ourPubKey;
michael@0 127 NSSCMSKEATemplateSelector whichKEA = NSSCMSKEAInvalid;
michael@0 128
michael@0 129 certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
michael@0 130 PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
michael@0 131
michael@0 132 /* We really want to show our KEA tag as the key exchange algorithm tag. */
michael@0 133 encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
michael@0 134
michael@0 135 /* Get the public key of the recipient. */
michael@0 136 publickey = CERT_ExtractPublicKey(cert);
michael@0 137 if (publickey == NULL) goto loser;
michael@0 138
michael@0 139 /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
michael@0 140 /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
michael@0 141 if (ourCert == NULL) goto loser;
michael@0 142
michael@0 143 arena = PORT_NewArena(1024);
michael@0 144 if (arena == NULL) goto loser;
michael@0 145
michael@0 146 /* While we're here, extract the key pair's public key data and copy it into */
michael@0 147 /* the outgoing parameters. */
michael@0 148 /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
michael@0 149 if (ourPubKey == NULL)
michael@0 150 {
michael@0 151 goto loser;
michael@0 152 }
michael@0 153 SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
michael@0 154 SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
michael@0 155 ourPubKey = NULL;
michael@0 156
michael@0 157 /* Extract our private key in order to derive the KEA key. */
michael@0 158 ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
michael@0 159 CERT_DestroyCertificate(ourCert); /* we're done with this */
michael@0 160 if (!ourPrivKey) goto loser;
michael@0 161
michael@0 162 /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
michael@0 163 if (ukm) {
michael@0 164 ukm->data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
michael@0 165 ukm->len = /* XXXX */;
michael@0 166 }
michael@0 167
michael@0 168 /* Generate the KEK (key exchange key) according to RFC2631 which we use
michael@0 169 * to wrap the bulk encryption key. */
michael@0 170 kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
michael@0 171 ukm, NULL,
michael@0 172 /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
michael@0 173 CKA_WRAP, 0, wincx);
michael@0 174
michael@0 175 SECKEY_DestroyPublicKey(publickey);
michael@0 176 SECKEY_DestroyPrivateKey(ourPrivKey);
michael@0 177 publickey = NULL;
michael@0 178 ourPrivKey = NULL;
michael@0 179
michael@0 180 if (!kek)
michael@0 181 goto loser;
michael@0 182
michael@0 183 /* allocate space for the encrypted CEK (bulk key) */
michael@0 184 encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
michael@0 185 encKey->len = SMIME_FORTEZZA_MAX_KEY_SIZE;
michael@0 186
michael@0 187 if (encKey->data == NULL)
michael@0 188 {
michael@0 189 PK11_FreeSymKey(kek);
michael@0 190 goto loser;
michael@0 191 }
michael@0 192
michael@0 193
michael@0 194 /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
michael@0 195 /* bulk encryption algorithm */
michael@0 196 switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
michael@0 197 {
michael@0 198 case /* XXXX */CKM_SKIPJACK_CFB8:
michael@0 199 err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
michael@0 200 whichKEA = NSSCMSKEAUsesSkipjack;
michael@0 201 break;
michael@0 202 case /* XXXX */CKM_SKIPJACK_CFB8:
michael@0 203 err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
michael@0 204 whichKEA = NSSCMSKEAUsesSkipjack;
michael@0 205 break;
michael@0 206 default:
michael@0 207 /* XXXX what do we do here? Neither RC2 nor 3DES... */
michael@0 208 err = SECFailure;
michael@0 209 /* set error */
michael@0 210 break;
michael@0 211 }
michael@0 212
michael@0 213 PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
michael@0 214 if (err != SECSuccess)
michael@0 215 goto loser;
michael@0 216
michael@0 217 PORT_Assert(whichKEA != NSSCMSKEAInvalid);
michael@0 218
michael@0 219 /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
michael@0 220 /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
michael@0 221 params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
michael@0 222 if (params == NULL)
michael@0 223 goto loser;
michael@0 224
michael@0 225 /* now set keyEncAlg */
michael@0 226 rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
michael@0 227 if (rv != SECSuccess)
michael@0 228 goto loser;
michael@0 229
michael@0 230 /* XXXXXXX this is not right yet */
michael@0 231 loser:
michael@0 232 if (arena) {
michael@0 233 PORT_FreeArena(arena, PR_FALSE);
michael@0 234 }
michael@0 235 if (publickey) {
michael@0 236 SECKEY_DestroyPublicKey(publickey);
michael@0 237 }
michael@0 238 if (ourPrivKey) {
michael@0 239 SECKEY_DestroyPrivateKey(ourPrivKey);
michael@0 240 }
michael@0 241 #endif
michael@0 242 return SECFailure;
michael@0 243 }
michael@0 244
michael@0 245 PK11SymKey *
michael@0 246 NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
michael@0 247 {
michael@0 248 #if 0 /* not yet done */
michael@0 249 SECStatus err;
michael@0 250 CK_MECHANISM_TYPE bulkType;
michael@0 251 PK11SymKey *tek;
michael@0 252 SECKEYPublicKey *originatorPubKey;
michael@0 253 NSSCMSSMIMEKEAParameters keaParams;
michael@0 254
michael@0 255 /* XXXX get originator's public key */
michael@0 256 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
michael@0 257 keaParams.originatorKEAKey.len);
michael@0 258 if (originatorPubKey == NULL)
michael@0 259 goto loser;
michael@0 260
michael@0 261 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
michael@0 262 The Derive function generates a shared secret and combines it with the originatorRA
michael@0 263 data to come up with an unique session key */
michael@0 264 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
michael@0 265 &keaParams.originatorRA, NULL,
michael@0 266 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
michael@0 267 CKA_WRAP, 0, pwfn_arg);
michael@0 268 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
michael@0 269 if (tek == NULL)
michael@0 270 goto loser;
michael@0 271
michael@0 272 /* Now that we have the TEK, unwrap the bulk key
michael@0 273 with which to decrypt the message. */
michael@0 274 /* Skipjack is being used as the bulk encryption algorithm.*/
michael@0 275 /* Unwrap the bulk key. */
michael@0 276 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
michael@0 277 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
michael@0 278
michael@0 279 return bulkkey;
michael@0 280
michael@0 281 loser:
michael@0 282 #endif
michael@0 283 return NULL;
michael@0 284 }
michael@0 285

mercurial