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