/src/nss/lib/smime/cmsencdata.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 encryptedData methods. |
7 | | */ |
8 | | |
9 | | #include "cmslocal.h" |
10 | | |
11 | | #include "keyhi.h" |
12 | | #include "secasn1.h" |
13 | | #include "secitem.h" |
14 | | #include "secoid.h" |
15 | | #include "pk11func.h" |
16 | | #include "prtime.h" |
17 | | #include "secerr.h" |
18 | | #include "secpkcs5.h" |
19 | | #include "smime.h" |
20 | | |
21 | | /* |
22 | | * NSS_CMSEncryptedData_Create - create an empty encryptedData object. |
23 | | * |
24 | | * "algorithm" specifies the bulk encryption algorithm to use. |
25 | | * "keysize" is the key size. |
26 | | * |
27 | | * An error results in a return value of NULL and an error set. |
28 | | * (Retrieve specific errors via PORT_GetError()/XP_GetError().) |
29 | | */ |
30 | | NSSCMSEncryptedData * |
31 | | NSS_CMSEncryptedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, |
32 | | int keysize) |
33 | 0 | { |
34 | 0 | void *mark; |
35 | 0 | NSSCMSEncryptedData *encd; |
36 | 0 | PLArenaPool *poolp; |
37 | 0 | SECAlgorithmID *pbe_algid; |
38 | 0 | SECStatus rv; |
39 | |
|
40 | 0 | poolp = cmsg->poolp; |
41 | |
|
42 | 0 | mark = PORT_ArenaMark(poolp); |
43 | |
|
44 | 0 | encd = PORT_ArenaZNew(poolp, NSSCMSEncryptedData); |
45 | 0 | if (encd == NULL) |
46 | 0 | goto loser; |
47 | | |
48 | 0 | encd->cmsg = cmsg; |
49 | | |
50 | | /* version is set in NSS_CMSEncryptedData_Encode_BeforeStart() */ |
51 | |
|
52 | 0 | if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) { |
53 | 0 | rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(encd->contentInfo), |
54 | 0 | algorithm, NULL, keysize); |
55 | 0 | } else { |
56 | | /* Assume password-based-encryption. |
57 | | * Note: we can't generate pkcs5v2 from this interface. |
58 | | * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting |
59 | | * non-PBE oids and assuming that they are pkcs5v2 oids, but |
60 | | * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular |
61 | | * CMS encrypted data, so we can't tell NSS_CMS_EncryptedData_Create |
62 | | * to create pkcs5v2 PBEs */ |
63 | 0 | pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL); |
64 | 0 | if (pbe_algid == NULL) { |
65 | 0 | rv = SECFailure; |
66 | 0 | } else { |
67 | 0 | rv = NSS_CMSContentInfo_SetContentEncAlgID(poolp, |
68 | 0 | &(encd->contentInfo), |
69 | 0 | pbe_algid, keysize); |
70 | 0 | SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE); |
71 | 0 | } |
72 | 0 | } |
73 | 0 | if (rv != SECSuccess) |
74 | 0 | goto loser; |
75 | | |
76 | 0 | PORT_ArenaUnmark(poolp, mark); |
77 | 0 | return encd; |
78 | | |
79 | 0 | loser: |
80 | 0 | PORT_ArenaRelease(poolp, mark); |
81 | 0 | return NULL; |
82 | 0 | } |
83 | | |
84 | | /* |
85 | | * NSS_CMSEncryptedData_Destroy - destroy an encryptedData object |
86 | | */ |
87 | | void |
88 | | NSS_CMSEncryptedData_Destroy(NSSCMSEncryptedData *encd) |
89 | 19 | { |
90 | | /* everything's in a pool, so don't worry about the storage */ |
91 | 19 | if (encd != NULL) { |
92 | 16 | NSS_CMSContentInfo_Destroy(&(encd->contentInfo)); |
93 | 16 | } |
94 | 19 | return; |
95 | 19 | } |
96 | | |
97 | | /* |
98 | | * NSS_CMSEncryptedData_GetContentInfo - return pointer to encryptedData object's contentInfo |
99 | | */ |
100 | | NSSCMSContentInfo * |
101 | | NSS_CMSEncryptedData_GetContentInfo(NSSCMSEncryptedData *encd) |
102 | 0 | { |
103 | 0 | return &(encd->contentInfo); |
104 | 0 | } |
105 | | |
106 | | /* |
107 | | * NSS_CMSEncryptedData_Encode_BeforeStart - do all the necessary things to a EncryptedData |
108 | | * before encoding begins. |
109 | | * |
110 | | * In particular: |
111 | | * - set the correct version value. |
112 | | * - get the encryption key |
113 | | */ |
114 | | SECStatus |
115 | | NSS_CMSEncryptedData_Encode_BeforeStart(NSSCMSEncryptedData *encd) |
116 | 0 | { |
117 | 0 | int version; |
118 | 0 | PK11SymKey *bulkkey = NULL; |
119 | 0 | SECItem *dummy; |
120 | 0 | NSSCMSContentInfo *cinfo = &(encd->contentInfo); |
121 | 0 | SECAlgorithmID *algid = NULL; |
122 | |
|
123 | 0 | if (NSS_CMSArray_IsEmpty((void **)encd->unprotectedAttr)) |
124 | 0 | version = NSS_CMS_ENCRYPTED_DATA_VERSION; |
125 | 0 | else |
126 | 0 | version = NSS_CMS_ENCRYPTED_DATA_VERSION_UPATTR; |
127 | |
|
128 | 0 | dummy = SEC_ASN1EncodeInteger(encd->cmsg->poolp, &(encd->version), version); |
129 | 0 | if (dummy == NULL) |
130 | 0 | return SECFailure; |
131 | | |
132 | | /* now get content encryption key (bulk key) by using our cmsg callback */ |
133 | 0 | if (encd->cmsg->decrypt_key_cb) { |
134 | 0 | algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo); |
135 | 0 | bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, algid); |
136 | 0 | } |
137 | 0 | if ((bulkkey == NULL) || (algid == NULL)) |
138 | 0 | return SECFailure; |
139 | | |
140 | | /* store the bulk key in the contentInfo so that the encoder can find it */ |
141 | 0 | NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); |
142 | 0 | PK11_FreeSymKey(bulkkey); |
143 | |
|
144 | 0 | return SECSuccess; |
145 | 0 | } |
146 | | |
147 | | /* |
148 | | * NSS_CMSEncryptedData_Encode_BeforeData - set up encryption |
149 | | */ |
150 | | SECStatus |
151 | | NSS_CMSEncryptedData_Encode_BeforeData(NSSCMSEncryptedData *encd) |
152 | 0 | { |
153 | 0 | NSSCMSContentInfo *cinfo; |
154 | 0 | PK11SymKey *bulkkey = NULL; |
155 | 0 | SECAlgorithmID *algid; |
156 | 0 | SECStatus rv = SECFailure; |
157 | |
|
158 | 0 | cinfo = &(encd->contentInfo); |
159 | | |
160 | | /* find bulkkey and algorithm - must have been set by NSS_CMSEncryptedData_Encode_BeforeStart */ |
161 | 0 | bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); |
162 | 0 | if (bulkkey == NULL) { |
163 | 0 | goto loser; |
164 | 0 | } |
165 | | |
166 | 0 | algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo); |
167 | 0 | if (algid == NULL) { |
168 | 0 | goto loser; |
169 | 0 | } |
170 | | |
171 | 0 | rv = NSS_CMSContentInfo_Private_Init(cinfo); |
172 | 0 | if (rv != SECSuccess) { |
173 | 0 | goto loser; |
174 | 0 | } |
175 | | |
176 | 0 | if (!NSS_SMIMEUtil_EncryptionAllowed(algid, bulkkey)) { |
177 | 0 | goto loser; |
178 | 0 | } |
179 | | |
180 | | /* this may modify algid (with IVs generated in a token). |
181 | | * it is therefore essential that algid is a pointer to the "real" contentEncAlg, |
182 | | * not just to a copy */ |
183 | 0 | cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(encd->cmsg->poolp, |
184 | 0 | bulkkey, algid); |
185 | 0 | if (cinfo->privateInfo->ciphcx == NULL) |
186 | 0 | goto loser; |
187 | | |
188 | 0 | rv = SECSuccess; |
189 | |
|
190 | 0 | loser: |
191 | 0 | if (bulkkey) { |
192 | 0 | PK11_FreeSymKey(bulkkey); |
193 | 0 | } |
194 | 0 | return rv; |
195 | 0 | } |
196 | | |
197 | | /* |
198 | | * NSS_CMSEncryptedData_Encode_AfterData - finalize this encryptedData for encoding |
199 | | */ |
200 | | SECStatus |
201 | | NSS_CMSEncryptedData_Encode_AfterData(NSSCMSEncryptedData *encd) |
202 | 0 | { |
203 | 0 | if (encd->contentInfo.privateInfo && encd->contentInfo.privateInfo->ciphcx) { |
204 | 0 | NSS_CMSCipherContext_Destroy(encd->contentInfo.privateInfo->ciphcx); |
205 | 0 | encd->contentInfo.privateInfo->ciphcx = NULL; |
206 | 0 | } |
207 | | |
208 | | /* nothing to do after data */ |
209 | 0 | return SECSuccess; |
210 | 0 | } |
211 | | |
212 | | /* |
213 | | * NSS_CMSEncryptedData_Decode_BeforeData - find bulk key & set up decryption |
214 | | */ |
215 | | SECStatus |
216 | | NSS_CMSEncryptedData_Decode_BeforeData(NSSCMSEncryptedData *encd) |
217 | 4 | { |
218 | 4 | PK11SymKey *bulkkey = NULL; |
219 | 4 | NSSCMSContentInfo *cinfo; |
220 | 4 | SECAlgorithmID *bulkalg; |
221 | 4 | SECStatus rv = SECFailure; |
222 | | |
223 | 4 | cinfo = &(encd->contentInfo); |
224 | | |
225 | 4 | bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo); |
226 | | |
227 | 4 | if (encd->cmsg->decrypt_key_cb == NULL) /* no callback? no key../ */ |
228 | 4 | goto loser; |
229 | | |
230 | 0 | bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, bulkalg); |
231 | 0 | if (bulkkey == NULL) |
232 | | /* no success finding a bulk key */ |
233 | 0 | goto loser; |
234 | | |
235 | 0 | NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); |
236 | |
|
237 | 0 | rv = NSS_CMSContentInfo_Private_Init(cinfo); |
238 | 0 | if (rv != SECSuccess) { |
239 | 0 | goto loser; |
240 | 0 | } |
241 | 0 | rv = SECFailure; |
242 | |
|
243 | 0 | if (!NSS_SMIMEUtil_DecryptionAllowed(bulkalg, bulkkey)) { |
244 | 0 | goto loser; |
245 | 0 | } |
246 | | |
247 | 0 | cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg); |
248 | 0 | if (cinfo->privateInfo->ciphcx == NULL) |
249 | 0 | goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */ |
250 | 0 | rv = SECSuccess; |
251 | |
|
252 | 4 | loser: |
253 | 4 | if (bulkkey) { |
254 | 0 | PK11_FreeSymKey(bulkkey); |
255 | 0 | } |
256 | 4 | return rv; |
257 | 0 | } |
258 | | |
259 | | /* |
260 | | * NSS_CMSEncryptedData_Decode_AfterData - finish decrypting this encryptedData's content |
261 | | */ |
262 | | SECStatus |
263 | | NSS_CMSEncryptedData_Decode_AfterData(NSSCMSEncryptedData *encd) |
264 | 3 | { |
265 | 3 | if (encd->contentInfo.privateInfo && encd->contentInfo.privateInfo->ciphcx) { |
266 | 0 | NSS_CMSCipherContext_Destroy(encd->contentInfo.privateInfo->ciphcx); |
267 | 0 | encd->contentInfo.privateInfo->ciphcx = NULL; |
268 | 0 | } |
269 | | |
270 | 3 | return SECSuccess; |
271 | 3 | } |
272 | | |
273 | | /* |
274 | | * NSS_CMSEncryptedData_Decode_AfterEnd - finish decoding this encryptedData |
275 | | */ |
276 | | SECStatus |
277 | | NSS_CMSEncryptedData_Decode_AfterEnd(NSSCMSEncryptedData *encd) |
278 | 2 | { |
279 | | /* apply final touches */ |
280 | 2 | return SECSuccess; |
281 | 2 | } |