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 +