/src/nss/lib/smime/cmspubkey.c
Line | Count | Source |
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 | | #include "secder.h" |
19 | | #include "prerr.h" |
20 | | #include "sechash.h" |
21 | | |
22 | | /* ====== RSA ======================================================================= */ |
23 | | |
24 | | /* |
25 | | * NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA |
26 | | * |
27 | | * this function takes a symmetric key and encrypts it using an RSA public key |
28 | | * according to PKCS#1 and RFC2633 (S/MIME) |
29 | | */ |
30 | | SECStatus |
31 | | NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, |
32 | | PK11SymKey *bulkkey, |
33 | | SECItem *encKey) |
34 | 0 | { |
35 | 0 | SECStatus rv; |
36 | 0 | SECKEYPublicKey *publickey; |
37 | |
|
38 | 0 | publickey = CERT_ExtractPublicKey(cert); |
39 | 0 | if (publickey == NULL) |
40 | 0 | return SECFailure; |
41 | | |
42 | 0 | rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey); |
43 | 0 | SECKEY_DestroyPublicKey(publickey); |
44 | 0 | return rv; |
45 | 0 | } |
46 | | |
47 | | SECStatus |
48 | | NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp, |
49 | | SECKEYPublicKey *publickey, |
50 | | PK11SymKey *bulkkey, SECItem *encKey) |
51 | 0 | { |
52 | 0 | SECStatus rv; |
53 | 0 | int data_len; |
54 | 0 | KeyType keyType; |
55 | 0 | void *mark = NULL; |
56 | |
|
57 | 0 | mark = PORT_ArenaMark(poolp); |
58 | 0 | if (!mark) |
59 | 0 | goto loser; |
60 | | |
61 | | /* sanity check */ |
62 | 0 | keyType = SECKEY_GetPublicKeyType(publickey); |
63 | 0 | PORT_Assert(keyType == rsaKey); |
64 | 0 | if (keyType != rsaKey) { |
65 | 0 | goto loser; |
66 | 0 | } |
67 | | /* allocate memory for the encrypted key */ |
68 | 0 | data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */ |
69 | 0 | encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, data_len); |
70 | 0 | encKey->len = data_len; |
71 | 0 | if (encKey->data == NULL) |
72 | 0 | goto loser; |
73 | | |
74 | | /* encrypt the key now */ |
75 | 0 | rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION), |
76 | 0 | publickey, bulkkey, encKey); |
77 | |
|
78 | 0 | if (rv != SECSuccess) |
79 | 0 | goto loser; |
80 | | |
81 | 0 | PORT_ArenaUnmark(poolp, mark); |
82 | 0 | return SECSuccess; |
83 | | |
84 | 0 | loser: |
85 | 0 | if (mark) { |
86 | 0 | PORT_ArenaRelease(poolp, mark); |
87 | 0 | } |
88 | 0 | return SECFailure; |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key |
93 | | * |
94 | | * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric |
95 | | * key handle. Please note that the actual unwrapped key data may not be allowed to leave |
96 | | * a hardware token... |
97 | | */ |
98 | | PK11SymKey * |
99 | | NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag) |
100 | 0 | { |
101 | | /* that's easy */ |
102 | 0 | CK_MECHANISM_TYPE target; |
103 | 0 | PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN); |
104 | 0 | target = PK11_AlgtagToMechanism(bulkalgtag); |
105 | 0 | if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) { |
106 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
107 | 0 | return NULL; |
108 | 0 | } |
109 | 0 | return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0); |
110 | 0 | } |
111 | | |
112 | | typedef struct RSA_OAEP_CMS_paramsStr RSA_OAEP_CMS_params; |
113 | | struct RSA_OAEP_CMS_paramsStr { |
114 | | SECAlgorithmID *hashFunc; |
115 | | SECAlgorithmID *maskGenFunc; |
116 | | SECAlgorithmID *pSourceFunc; |
117 | | }; |
118 | | |
119 | | SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) |
120 | | SEC_ASN1_MKSUB(SEC_OctetStringTemplate) |
121 | | |
122 | | static const SEC_ASN1Template seckey_PointerToAlgorithmIDTemplate[] = { |
123 | | { SEC_ASN1_POINTER | SEC_ASN1_XTRN, 0, |
124 | | SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) } |
125 | | }; |
126 | | |
127 | | static const SEC_ASN1Template RSA_OAEP_CMS_paramsTemplate[] = { |
128 | | { SEC_ASN1_SEQUENCE, |
129 | | 0, NULL, sizeof(RSA_OAEP_CMS_params) }, |
130 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | |
131 | | SEC_ASN1_CONTEXT_SPECIFIC | 0, |
132 | | offsetof(RSA_OAEP_CMS_params, hashFunc), |
133 | | seckey_PointerToAlgorithmIDTemplate }, |
134 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | |
135 | | SEC_ASN1_CONTEXT_SPECIFIC | 1, |
136 | | offsetof(RSA_OAEP_CMS_params, maskGenFunc), |
137 | | seckey_PointerToAlgorithmIDTemplate }, |
138 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | |
139 | | SEC_ASN1_CONTEXT_SPECIFIC | 2, |
140 | | offsetof(RSA_OAEP_CMS_params, pSourceFunc), |
141 | | seckey_PointerToAlgorithmIDTemplate }, |
142 | | { 0 } |
143 | | }; |
144 | | |
145 | | /* |
146 | | * NSS_CMSUtil_DecryptSymKey_RSA_OAEP - unwrap a RSA-wrapped symmetric key |
147 | | * |
148 | | * this function takes an RSA-OAEP-wrapped symmetric key and unwraps it, returning a symmetric |
149 | | * key handle. Please note that the actual unwrapped key data may not be allowed to leave |
150 | | * a hardware token... |
151 | | */ |
152 | | PK11SymKey * |
153 | | NSS_CMSUtil_DecryptSymKey_RSA_OAEP(SECKEYPrivateKey *privkey, SECItem *parameters, SECItem *encKey, SECOidTag bulkalgtag) |
154 | 0 | { |
155 | 0 | CK_RSA_PKCS_OAEP_PARAMS oaep_params; |
156 | 0 | RSA_OAEP_CMS_params encoded_params; |
157 | 0 | SECAlgorithmID mgf1hashAlg; |
158 | 0 | SECOidTag mgfAlgtag, pSourcetag; |
159 | 0 | SECItem encoding_params, params; |
160 | 0 | PK11SymKey *bulkkey = NULL; |
161 | 0 | SECStatus rv; |
162 | |
|
163 | 0 | CK_MECHANISM_TYPE target; |
164 | 0 | PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN); |
165 | 0 | target = PK11_AlgtagToMechanism(bulkalgtag); |
166 | 0 | if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) { |
167 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
168 | 0 | return NULL; |
169 | 0 | } |
170 | | |
171 | 0 | PORT_Memset(&encoded_params, 0, sizeof(RSA_OAEP_CMS_params)); |
172 | 0 | PORT_Memset(&mgf1hashAlg, 0, sizeof(SECAlgorithmID)); |
173 | 0 | PORT_Memset(&encoding_params, 0, sizeof(SECItem)); |
174 | | |
175 | | /* Set default values for the OAEP parameters */ |
176 | 0 | oaep_params.hashAlg = CKM_SHA_1; |
177 | 0 | oaep_params.mgf = CKG_MGF1_SHA1; |
178 | 0 | oaep_params.source = CKZ_DATA_SPECIFIED; |
179 | 0 | oaep_params.pSourceData = NULL; |
180 | 0 | oaep_params.ulSourceDataLen = 0; |
181 | |
|
182 | 0 | if (parameters->len == 2) { |
183 | | /* For some reason SEC_ASN1DecodeItem cannot process parameters if it is an emtpy SEQUENCE */ |
184 | | /* Just check that this is a properly encoded empty SEQUENCE */ |
185 | | /* TODO: Investigate if there a better way to handle this as part of decoding. */ |
186 | 0 | if ((parameters->data[0] != 0x30) || (parameters->data[1] != 0)) { |
187 | 0 | return NULL; |
188 | 0 | } |
189 | 0 | } else { |
190 | 0 | rv = SEC_ASN1DecodeItem(NULL, &encoded_params, RSA_OAEP_CMS_paramsTemplate, parameters); |
191 | 0 | if (rv != SECSuccess) { |
192 | 0 | goto loser; |
193 | 0 | } |
194 | 0 | if (encoded_params.hashFunc != NULL) { |
195 | 0 | oaep_params.hashAlg = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(encoded_params.hashFunc)); |
196 | 0 | } |
197 | 0 | if (encoded_params.maskGenFunc != NULL) { |
198 | 0 | mgfAlgtag = SECOID_GetAlgorithmTag(encoded_params.maskGenFunc); |
199 | 0 | if (mgfAlgtag != SEC_OID_PKCS1_MGF1) { |
200 | | /* MGF1 is the only supported mask generation function */ |
201 | 0 | goto loser; |
202 | 0 | } |
203 | | /* The parameters field contains an AlgorithmIdentifier specifying the |
204 | | * hash function to use with MGF1. |
205 | | */ |
206 | 0 | rv = SEC_ASN1DecodeItem(NULL, &mgf1hashAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), |
207 | 0 | &encoded_params.maskGenFunc->parameters); |
208 | 0 | if (rv != SECSuccess) { |
209 | 0 | goto loser; |
210 | 0 | } |
211 | 0 | oaep_params.mgf = SEC_GetMgfTypeByOidTag(SECOID_GetAlgorithmTag(&mgf1hashAlg)); |
212 | 0 | if (!oaep_params.mgf) { |
213 | 0 | goto loser; |
214 | 0 | } |
215 | 0 | } |
216 | 0 | if (encoded_params.pSourceFunc != NULL) { |
217 | 0 | pSourcetag = SECOID_GetAlgorithmTag(encoded_params.pSourceFunc); |
218 | 0 | if (pSourcetag != SEC_OID_PKCS1_PSPECIFIED) { |
219 | 0 | goto loser; |
220 | 0 | } |
221 | | /* The encoding parameters (P) are provided as an OCTET STRING in the parameters field. */ |
222 | 0 | if (encoded_params.pSourceFunc->parameters.len != 0) { |
223 | 0 | rv = SEC_ASN1DecodeItem(NULL, &encoding_params, SEC_ASN1_GET(SEC_OctetStringTemplate), |
224 | 0 | &encoded_params.pSourceFunc->parameters); |
225 | 0 | if (rv != SECSuccess) { |
226 | 0 | goto loser; |
227 | 0 | } |
228 | 0 | oaep_params.ulSourceDataLen = encoding_params.len; |
229 | 0 | oaep_params.pSourceData = encoding_params.data; |
230 | 0 | } |
231 | 0 | } |
232 | 0 | } |
233 | 0 | params.type = siBuffer; |
234 | 0 | params.data = (unsigned char *)&oaep_params; |
235 | 0 | params.len = sizeof(CK_RSA_PKCS_OAEP_PARAMS); |
236 | 0 | bulkkey = PK11_PubUnwrapSymKeyWithMechanism(privkey, CKM_RSA_PKCS_OAEP, ¶ms, |
237 | 0 | encKey, target, CKA_DECRYPT, 0); |
238 | |
|
239 | 0 | loser: |
240 | 0 | PORT_Free(oaep_params.pSourceData); |
241 | 0 | if (encoded_params.hashFunc) { |
242 | 0 | SECOID_DestroyAlgorithmID(encoded_params.hashFunc, PR_TRUE); |
243 | 0 | } |
244 | 0 | if (encoded_params.maskGenFunc) { |
245 | 0 | SECOID_DestroyAlgorithmID(encoded_params.maskGenFunc, PR_TRUE); |
246 | 0 | } |
247 | 0 | if (encoded_params.pSourceFunc) { |
248 | 0 | SECOID_DestroyAlgorithmID(encoded_params.pSourceFunc, PR_TRUE); |
249 | 0 | } |
250 | 0 | SECOID_DestroyAlgorithmID(&mgf1hashAlg, PR_FALSE); |
251 | 0 | return bulkkey; |
252 | 0 | } |
253 | | |
254 | | /* ====== ESECDH (Ephemeral-Static Elliptic Curve Diffie-Hellman) =========== */ |
255 | | |
256 | | typedef struct ECC_CMS_SharedInfoStr ECC_CMS_SharedInfo; |
257 | | struct ECC_CMS_SharedInfoStr { |
258 | | SECAlgorithmID *keyInfo; /* key-encryption algorithm */ |
259 | | SECItem entityUInfo; /* ukm */ |
260 | | SECItem suppPubInfo; /* length of KEK in bits as 32-bit number */ |
261 | | }; |
262 | | |
263 | | static const SEC_ASN1Template ECC_CMS_SharedInfoTemplate[] = { |
264 | | { SEC_ASN1_SEQUENCE, |
265 | | 0, NULL, sizeof(ECC_CMS_SharedInfo) }, |
266 | | { SEC_ASN1_XTRN | SEC_ASN1_POINTER, |
267 | | offsetof(ECC_CMS_SharedInfo, keyInfo), |
268 | | SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
269 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | |
270 | | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
271 | | offsetof(ECC_CMS_SharedInfo, entityUInfo), |
272 | | SEC_ASN1_SUB(SEC_OctetStringTemplate) }, |
273 | | { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | |
274 | | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, |
275 | | offsetof(ECC_CMS_SharedInfo, suppPubInfo), |
276 | | SEC_ASN1_SUB(SEC_OctetStringTemplate) }, |
277 | | { 0 } |
278 | | }; |
279 | | |
280 | | /* Inputs: |
281 | | * keyInfo: key-encryption algorithm |
282 | | * ukm: ukm field of KeyAgreeRecipientInfo |
283 | | * KEKsize: length of KEK in bytes |
284 | | * |
285 | | * Output: DER encoded ECC-CMS-SharedInfo |
286 | | */ |
287 | | static SECItem * |
288 | | Create_ECC_CMS_SharedInfo(PLArenaPool *poolp, |
289 | | SECAlgorithmID *keyInfo, SECItem *ukm, unsigned int KEKsize) |
290 | 0 | { |
291 | 0 | ECC_CMS_SharedInfo SI; |
292 | 0 | unsigned char suppPubInfo[4] = { 0 }; |
293 | |
|
294 | 0 | SI.keyInfo = keyInfo; |
295 | 0 | SI.entityUInfo.type = ukm->type; |
296 | 0 | SI.entityUInfo.data = ukm->data; |
297 | 0 | SI.entityUInfo.len = ukm->len; |
298 | |
|
299 | 0 | SI.suppPubInfo.type = siBuffer; |
300 | 0 | SI.suppPubInfo.data = suppPubInfo; |
301 | 0 | SI.suppPubInfo.len = sizeof(suppPubInfo); |
302 | |
|
303 | 0 | if (KEKsize > 0x1FFFFFFF) { // ensure KEKsize * 8 fits in 4 bytes. |
304 | 0 | return NULL; |
305 | 0 | } |
306 | 0 | KEKsize *= 8; |
307 | 0 | suppPubInfo[0] = (KEKsize & 0xFF000000) >> 24; |
308 | 0 | suppPubInfo[1] = (KEKsize & 0xFF0000) >> 16; |
309 | 0 | suppPubInfo[2] = (KEKsize & 0xFF00) >> 8; |
310 | 0 | suppPubInfo[3] = KEKsize & 0xFF; |
311 | |
|
312 | 0 | return SEC_ASN1EncodeItem(poolp, NULL, &SI, ECC_CMS_SharedInfoTemplate); |
313 | 0 | } |
314 | | |
315 | | /* this will generate a key pair, compute the shared secret, */ |
316 | | /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */ |
317 | | /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. |
318 | | * The ukm is optional per RFC 5753. Pass a NULL value to request an empty ukm. |
319 | | * Pass a SECItem with the size set to zero, to request allocating a random |
320 | | * ukm of a default size. Or provide an explicit ukm that was defined by the caller. |
321 | | */ |
322 | | SECStatus |
323 | | NSS_CMSUtil_EncryptSymKey_ESECDH(PLArenaPool *poolp, CERTCertificate *cert, |
324 | | PK11SymKey *bulkkey, SECItem *encKey, |
325 | | PRBool genUkm, SECItem *ukm, |
326 | | SECAlgorithmID *keyEncAlg, SECItem *pubKey, |
327 | | void *wincx) |
328 | 0 | { |
329 | 0 | SECOidTag certalgtag; /* the certificate's encryption algorithm */ |
330 | 0 | SECStatus rv; |
331 | 0 | SECStatus err; |
332 | 0 | PK11SymKey *kek; |
333 | 0 | SECKEYPublicKey *publickey = NULL; |
334 | 0 | SECKEYPublicKey *ourPubKey = NULL; |
335 | 0 | SECKEYPrivateKey *ourPrivKey = NULL; |
336 | 0 | unsigned int bulkkey_size, kek_size; |
337 | 0 | SECAlgorithmID keyWrapAlg; |
338 | 0 | SECOidTag keyEncAlgtag; |
339 | 0 | SECItem keyWrapAlg_params, *keyEncAlg_params, *SharedInfo; |
340 | 0 | CK_MECHANISM_TYPE keyDerivationType, keyWrapMech; |
341 | 0 | CK_ULONG kdf; |
342 | |
|
343 | 0 | if (genUkm && (ukm->len != 0 || ukm->data != NULL)) { |
344 | 0 | PORT_SetError(PR_INVALID_ARGUMENT_ERROR); |
345 | 0 | return SECFailure; |
346 | 0 | } |
347 | | |
348 | 0 | certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); |
349 | 0 | PORT_Assert(certalgtag == SEC_OID_ANSIX962_EC_PUBLIC_KEY); |
350 | 0 | if (certalgtag != SEC_OID_ANSIX962_EC_PUBLIC_KEY) { |
351 | 0 | return SECFailure; |
352 | 0 | } |
353 | | |
354 | | /* Get the public key of the recipient. */ |
355 | 0 | publickey = CERT_ExtractPublicKey(cert); |
356 | 0 | if (publickey == NULL) |
357 | 0 | goto loser; |
358 | | |
359 | 0 | ourPrivKey = SECKEY_CreateECPrivateKey(&publickey->u.ec.DEREncodedParams, |
360 | 0 | &ourPubKey, wincx); |
361 | 0 | if (!ourPrivKey || !ourPubKey) { |
362 | 0 | goto loser; |
363 | 0 | } |
364 | | |
365 | 0 | rv = SECITEM_CopyItem(poolp, pubKey, &ourPubKey->u.ec.publicValue); |
366 | 0 | if (rv != SECSuccess) { |
367 | 0 | goto loser; |
368 | 0 | } |
369 | | |
370 | | /* pubKey will be encoded as a BIT STRING, so pubKey->len must be length in bits |
371 | | * rather than bytes */ |
372 | 0 | pubKey->len = pubKey->len * 8; |
373 | |
|
374 | 0 | SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */ |
375 | 0 | ourPubKey = NULL; |
376 | |
|
377 | 0 | bulkkey_size = PK11_GetKeyLength(bulkkey); |
378 | 0 | if (bulkkey_size == 0) { |
379 | 0 | goto loser; |
380 | 0 | } |
381 | | |
382 | | /* Parameters are supposed to be absent for AES key wrap algorithms. |
383 | | * However, Microsoft Outlook cannot decrypt message unless |
384 | | * parameters field is NULL. */ |
385 | 0 | keyWrapAlg_params.len = 2; |
386 | 0 | keyWrapAlg_params.data = (unsigned char *)PORT_ArenaAlloc(poolp, keyWrapAlg_params.len); |
387 | 0 | if (keyWrapAlg_params.data == NULL) |
388 | 0 | goto loser; |
389 | | |
390 | 0 | keyWrapAlg_params.data[0] = SEC_ASN1_NULL; |
391 | 0 | keyWrapAlg_params.data[1] = 0; |
392 | | |
393 | | /* RFC5753 specifies id-aes128-wrap as the mandatory to support algorithm. |
394 | | * So, use id-aes128-wrap unless bulkkey provides more than 128 bits of |
395 | | * security. If bulkkey provides more than 128 bits of security, use |
396 | | * the algorithms from KE Set 2 in RFC 6318. */ |
397 | | |
398 | | /* TODO: NIST Special Publication SP 800-56A requires the use of |
399 | | * Cofactor ECDH. However, RFC 6318 specifies the use of |
400 | | * dhSinglePass-stdDH-sha256kdf-scheme or dhSinglePass-stdDH-sha384kdf-scheme |
401 | | * for Suite B. The reason for this is that all of the NIST recommended |
402 | | * prime curves in FIPS 186-3 (including the Suite B curves) have a cofactor |
403 | | * of 1, and so for these curves standard and cofactor ECDH are the same. |
404 | | * This code should really look at the key's parameters and choose cofactor |
405 | | * ECDH if the curve has a cofactor other than 1. */ |
406 | 0 | if ((PK11_GetMechanism(bulkkey) == CKM_AES_CBC) && (bulkkey_size > 16)) { |
407 | 0 | rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_256_KEY_WRAP, &keyWrapAlg_params); |
408 | 0 | kek_size = 32; |
409 | 0 | keyWrapMech = CKM_NSS_AES_KEY_WRAP; |
410 | 0 | kdf = CKD_SHA384_KDF; |
411 | 0 | keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME; |
412 | 0 | keyDerivationType = CKM_ECDH1_DERIVE; |
413 | 0 | } else { |
414 | 0 | rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_128_KEY_WRAP, &keyWrapAlg_params); |
415 | 0 | kek_size = 16; |
416 | 0 | keyWrapMech = CKM_NSS_AES_KEY_WRAP; |
417 | 0 | kdf = CKD_SHA256_KDF; |
418 | 0 | keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME; |
419 | 0 | keyDerivationType = CKM_ECDH1_DERIVE; |
420 | 0 | } |
421 | 0 | if (rv != SECSuccess) { |
422 | 0 | goto loser; |
423 | 0 | } |
424 | | |
425 | | /* ukm is optional, but RFC 5753 says that originators SHOULD include the ukm. |
426 | | * I made ukm 64 bytes, since RFC 2631 states that UserKeyingMaterial must |
427 | | * contain 512 bits for Diffie-Hellman key agreement. */ |
428 | | |
429 | 0 | if (genUkm) { |
430 | 0 | ukm->type = siBuffer; |
431 | 0 | ukm->len = 64; |
432 | 0 | ukm->data = (unsigned char *)PORT_ArenaAlloc(poolp, ukm->len); |
433 | |
|
434 | 0 | if (ukm->data == NULL) { |
435 | 0 | goto loser; |
436 | 0 | } |
437 | 0 | rv = PK11_GenerateRandom(ukm->data, ukm->len); |
438 | 0 | if (rv != SECSuccess) { |
439 | 0 | goto loser; |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | 0 | SharedInfo = Create_ECC_CMS_SharedInfo(poolp, &keyWrapAlg, |
444 | 0 | ukm, |
445 | 0 | kek_size); |
446 | 0 | if (!SharedInfo) { |
447 | 0 | goto loser; |
448 | 0 | } |
449 | | |
450 | | /* Generate the KEK (key exchange key) according to RFC5753 which we use |
451 | | * to wrap the bulk encryption key. */ |
452 | 0 | kek = PK11_PubDeriveWithKDF(ourPrivKey, publickey, PR_TRUE, |
453 | 0 | NULL, NULL, |
454 | 0 | keyDerivationType, keyWrapMech, |
455 | 0 | CKA_WRAP, kek_size, kdf, SharedInfo, NULL); |
456 | 0 | if (!kek) { |
457 | 0 | goto loser; |
458 | 0 | } |
459 | | |
460 | 0 | SECKEY_DestroyPublicKey(publickey); |
461 | 0 | SECKEY_DestroyPrivateKey(ourPrivKey); |
462 | 0 | publickey = NULL; |
463 | 0 | ourPrivKey = NULL; |
464 | | |
465 | | /* allocate space for the encrypted CEK (bulk key) */ |
466 | | /* AES key wrap produces an output 64-bits longer than |
467 | | * the input AES CEK (RFC 3565, Section 2.3.2) */ |
468 | 0 | encKey->len = bulkkey_size + 8; |
469 | 0 | encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, encKey->len); |
470 | |
|
471 | 0 | if (encKey->data == NULL) { |
472 | 0 | PK11_FreeSymKey(kek); |
473 | 0 | goto loser; |
474 | 0 | } |
475 | | |
476 | 0 | err = PK11_WrapSymKey(keyWrapMech, NULL, kek, bulkkey, encKey); |
477 | |
|
478 | 0 | PK11_FreeSymKey(kek); /* we do not need the KEK anymore */ |
479 | 0 | if (err != SECSuccess) { |
480 | 0 | goto loser; |
481 | 0 | } |
482 | | |
483 | 0 | keyEncAlg_params = SEC_ASN1EncodeItem(poolp, NULL, &keyWrapAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)); |
484 | 0 | if (keyEncAlg_params == NULL) |
485 | 0 | goto loser; |
486 | 0 | keyEncAlg_params->type = siBuffer; |
487 | | |
488 | | /* now set keyEncAlg */ |
489 | 0 | rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, keyEncAlgtag, keyEncAlg_params); |
490 | 0 | if (rv != SECSuccess) { |
491 | 0 | goto loser; |
492 | 0 | } |
493 | | |
494 | 0 | return SECSuccess; |
495 | | |
496 | 0 | loser: |
497 | 0 | if (publickey) { |
498 | 0 | SECKEY_DestroyPublicKey(publickey); |
499 | 0 | } |
500 | 0 | if (ourPubKey) { |
501 | 0 | SECKEY_DestroyPublicKey(ourPubKey); |
502 | 0 | } |
503 | 0 | if (ourPrivKey) { |
504 | 0 | SECKEY_DestroyPrivateKey(ourPrivKey); |
505 | 0 | } |
506 | 0 | return SECFailure; |
507 | 0 | } |
508 | | |
509 | | /* TODO: Move to pk11wrap and export? */ |
510 | | static int |
511 | | cms_GetKekSizeFromKeyWrapAlgTag(SECOidTag keyWrapAlgtag) |
512 | 0 | { |
513 | 0 | switch (keyWrapAlgtag) { |
514 | 0 | case SEC_OID_AES_128_KEY_WRAP: |
515 | 0 | return 16; |
516 | 0 | case SEC_OID_AES_192_KEY_WRAP: |
517 | 0 | return 24; |
518 | 0 | case SEC_OID_AES_256_KEY_WRAP: |
519 | 0 | return 32; |
520 | 0 | default: |
521 | 0 | break; |
522 | 0 | } |
523 | 0 | return 0; |
524 | 0 | } |
525 | | |
526 | | /* TODO: Move to smimeutil and export? */ |
527 | | static CK_ULONG |
528 | | cms_GetKdfFromKeyEncAlgTag(SECOidTag keyEncAlgtag) |
529 | 0 | { |
530 | 0 | switch (keyEncAlgtag) { |
531 | 0 | case SEC_OID_DHSINGLEPASS_STDDH_SHA1KDF_SCHEME: |
532 | 0 | case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA1KDF_SCHEME: |
533 | 0 | return CKD_SHA1_KDF; |
534 | 0 | case SEC_OID_DHSINGLEPASS_STDDH_SHA224KDF_SCHEME: |
535 | 0 | case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA224KDF_SCHEME: |
536 | 0 | return CKD_SHA224_KDF; |
537 | 0 | case SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME: |
538 | 0 | case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA256KDF_SCHEME: |
539 | 0 | return CKD_SHA256_KDF; |
540 | 0 | case SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME: |
541 | 0 | case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA384KDF_SCHEME: |
542 | 0 | return CKD_SHA384_KDF; |
543 | 0 | case SEC_OID_DHSINGLEPASS_STDDH_SHA512KDF_SCHEME: |
544 | 0 | case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA512KDF_SCHEME: |
545 | 0 | return CKD_SHA512_KDF; |
546 | 0 | default: |
547 | 0 | break; |
548 | 0 | } |
549 | 0 | return 0; |
550 | 0 | } |
551 | | |
552 | | PK11SymKey * |
553 | | NSS_CMSUtil_DecryptSymKey_ECDH(SECKEYPrivateKey *privkey, SECItem *encKey, |
554 | | SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, |
555 | | SECItem *ukm, NSSCMSOriginatorIdentifierOrKey *oiok, |
556 | | void *wincx) |
557 | 0 | { |
558 | 0 | SECAlgorithmID keyWrapAlg; |
559 | 0 | SECOidTag keyEncAlgtag, keyWrapAlgtag; |
560 | 0 | CK_MECHANISM_TYPE target, keyDerivationType, keyWrapMech; |
561 | 0 | CK_ULONG kdf; |
562 | 0 | PK11SymKey *kek = NULL, *bulkkey = NULL; |
563 | 0 | int kek_size; |
564 | 0 | SECKEYPublicKey originatorpublickey; |
565 | 0 | SECItem *oiok_publicKey, *SharedInfo = NULL; |
566 | 0 | SECStatus rv; |
567 | |
|
568 | 0 | PORT_Memset(&keyWrapAlg, 0, sizeof(SECAlgorithmID)); |
569 | |
|
570 | 0 | PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN); |
571 | 0 | target = PK11_AlgtagToMechanism(bulkalgtag); |
572 | 0 | if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) { |
573 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
574 | 0 | goto loser; |
575 | 0 | } |
576 | | |
577 | 0 | keyEncAlgtag = SECOID_GetAlgorithmTag(keyEncAlg); |
578 | 0 | keyDerivationType = PK11_AlgtagToMechanism(keyEncAlgtag); |
579 | 0 | if ((keyEncAlgtag == SEC_OID_UNKNOWN) || |
580 | 0 | (keyDerivationType == CKM_INVALID_MECHANISM)) { |
581 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
582 | 0 | goto loser; |
583 | 0 | } |
584 | | |
585 | 0 | rv = SEC_ASN1DecodeItem(NULL, &keyWrapAlg, |
586 | 0 | SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), &(keyEncAlg->parameters)); |
587 | 0 | if (rv != SECSuccess) { |
588 | 0 | goto loser; |
589 | 0 | } |
590 | | |
591 | 0 | keyWrapAlgtag = SECOID_GetAlgorithmTag(&keyWrapAlg); |
592 | 0 | keyWrapMech = PK11_AlgtagToMechanism(keyWrapAlgtag); |
593 | 0 | if ((keyWrapAlgtag == SEC_OID_UNKNOWN) || |
594 | 0 | (keyWrapMech == CKM_INVALID_MECHANISM)) { |
595 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
596 | 0 | goto loser; |
597 | 0 | } |
598 | | |
599 | 0 | kek_size = cms_GetKekSizeFromKeyWrapAlgTag(keyWrapAlgtag); |
600 | 0 | if (!kek_size) { |
601 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
602 | 0 | goto loser; |
603 | 0 | } |
604 | | |
605 | 0 | kdf = cms_GetKdfFromKeyEncAlgTag(keyEncAlgtag); |
606 | 0 | if (!kdf) { |
607 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
608 | 0 | goto loser; |
609 | 0 | } |
610 | | |
611 | | /* Get originator's public key */ |
612 | | /* TODO: Add support for static-static ECDH */ |
613 | 0 | if (oiok->identifierType != NSSCMSOriginatorIDOrKey_OriginatorPublicKey) { |
614 | 0 | PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE); |
615 | 0 | goto loser; |
616 | 0 | } |
617 | | |
618 | | /* PK11_PubDeriveWithKDF only uses the keyType u.ec.publicValue.data |
619 | | * and u.ec.publicValue.len from the originator's public key. */ |
620 | 0 | oiok_publicKey = &(oiok->id.originatorPublicKey.publicKey); |
621 | 0 | originatorpublickey.keyType = ecKey; |
622 | 0 | originatorpublickey.u.ec.publicValue.data = oiok_publicKey->data; |
623 | 0 | originatorpublickey.u.ec.publicValue.len = oiok_publicKey->len / 8; |
624 | |
|
625 | 0 | SharedInfo = Create_ECC_CMS_SharedInfo(NULL, &keyWrapAlg, ukm, kek_size); |
626 | 0 | if (!SharedInfo) { |
627 | 0 | goto loser; |
628 | 0 | } |
629 | | |
630 | | /* Generate the KEK (key exchange key) according to RFC5753 which we use |
631 | | * to wrap the bulk encryption key. */ |
632 | 0 | kek = PK11_PubDeriveWithKDF(privkey, &originatorpublickey, PR_TRUE, |
633 | 0 | NULL, NULL, |
634 | 0 | keyDerivationType, keyWrapMech, |
635 | 0 | CKA_WRAP, kek_size, kdf, SharedInfo, wincx); |
636 | |
|
637 | 0 | SECITEM_FreeItem(SharedInfo, PR_TRUE); |
638 | |
|
639 | 0 | if (kek == NULL) { |
640 | 0 | goto loser; |
641 | 0 | } |
642 | | |
643 | 0 | bulkkey = PK11_UnwrapSymKey(kek, keyWrapMech, NULL, encKey, target, CKA_UNWRAP, 0); |
644 | 0 | PK11_FreeSymKey(kek); /* we do not need the KEK anymore */ |
645 | 0 | loser: |
646 | 0 | SECOID_DestroyAlgorithmID(&keyWrapAlg, PR_FALSE); |
647 | 0 | return bulkkey; |
648 | 0 | } |