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