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 message methods.
7 */
8
9 #include "cmslocal.h"
10
11 #include "cert.h"
12 #include "secasn1.h"
13 #include "secitem.h"
14 #include "secoid.h"
15 #include "pk11func.h"
16 #include "secerr.h"
17
18 /*
19 * NSS_CMSMessage_Create - create a CMS message object
20 *
21 * "poolp" - arena to allocate memory from, or NULL if new arena should be created
22 */
23 NSSCMSMessage *
NSS_CMSMessage_Create(PLArenaPool * poolp)24 NSS_CMSMessage_Create(PLArenaPool *poolp)
25 {
26 void *mark = NULL;
27 NSSCMSMessage *cmsg;
28 PRBool poolp_is_ours = PR_FALSE;
29
30 if (poolp == NULL) {
31 poolp = PORT_NewArena(1024); /* XXX what is right value? */
32 if (poolp == NULL) {
33 return NULL;
34 }
35 poolp_is_ours = PR_TRUE;
36 }
37
38 if (!poolp_is_ours)
39 mark = PORT_ArenaMark(poolp);
40
41 cmsg = (NSSCMSMessage *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSMessage));
42 if (cmsg == NULL ||
43 NSS_CMSContentInfo_Private_Init(&(cmsg->contentInfo)) != SECSuccess) {
44 if (!poolp_is_ours) {
45 if (mark) {
46 PORT_ArenaRelease(poolp, mark);
47 }
48 } else {
49 PORT_FreeArena(poolp, PR_FALSE);
50 }
51 return NULL;
52 }
53
54 cmsg->poolp = poolp;
55 cmsg->poolp_is_ours = poolp_is_ours;
56 cmsg->refCount = 1;
57
58 if (mark) {
59 PORT_ArenaUnmark(poolp, mark);
60 }
61
62 return cmsg;
63 }
64
65 /*
66 * NSS_CMSMessage_SetEncodingParams - set up a CMS message object for encoding or decoding
67 *
68 * "cmsg" - message object
69 * "pwfn", pwfn_arg" - callback function for getting token password
70 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
71 * "detached_digestalgs", "detached_digests" - digests from detached content
72 */
73 void
NSS_CMSMessage_SetEncodingParams(NSSCMSMessage * cmsg,PK11PasswordFunc pwfn,void * pwfn_arg,NSSCMSGetDecryptKeyCallback decrypt_key_cb,void * decrypt_key_cb_arg,SECAlgorithmID ** detached_digestalgs,SECItem ** detached_digests)74 NSS_CMSMessage_SetEncodingParams(NSSCMSMessage *cmsg,
75 PK11PasswordFunc pwfn, void *pwfn_arg,
76 NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
77 SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
78 {
79 if (cmsg == NULL) {
80 return;
81 }
82 if (pwfn) {
83 PK11_SetPasswordFunc(pwfn);
84 }
85
86 cmsg->pwfn_arg = pwfn_arg;
87 cmsg->decrypt_key_cb = decrypt_key_cb;
88 cmsg->decrypt_key_cb_arg = decrypt_key_cb_arg;
89 cmsg->detached_digestalgs = detached_digestalgs;
90 cmsg->detached_digests = detached_digests;
91 }
92
93 /*
94 * NSS_CMSMessage_Destroy - destroy a CMS message and all of its sub-pieces.
95 */
96 void
NSS_CMSMessage_Destroy(NSSCMSMessage * cmsg)97 NSS_CMSMessage_Destroy(NSSCMSMessage *cmsg)
98 {
99 if (cmsg == NULL)
100 return;
101
102 PORT_Assert(cmsg->refCount > 0);
103 if (cmsg->refCount <= 0) { /* oops */
104 return;
105 }
106
107 cmsg->refCount--; /* thread safety? */
108 if (cmsg->refCount > 0) {
109 return;
110 }
111
112 NSS_CMSContentInfo_Destroy(&(cmsg->contentInfo));
113
114 /* if poolp is not NULL, cmsg is the owner of its arena */
115 if (cmsg->poolp_is_ours) {
116 PORT_FreeArena(cmsg->poolp, PR_FALSE); /* XXX clear it? */
117 }
118 }
119
120 /*
121 * NSS_CMSMessage_Copy - return a copy of the given message.
122 *
123 * The copy may be virtual or may be real -- either way, the result needs
124 * to be passed to NSS_CMSMessage_Destroy later (as does the original).
125 */
126 NSSCMSMessage *
NSS_CMSMessage_Copy(NSSCMSMessage * cmsg)127 NSS_CMSMessage_Copy(NSSCMSMessage *cmsg)
128 {
129 if (cmsg == NULL) {
130 return NULL;
131 }
132
133 PORT_Assert(cmsg->refCount > 0);
134
135 cmsg->refCount++; /* XXX chrisk thread safety? */
136 return cmsg;
137 }
138
139 /*
140 * NSS_CMSMessage_GetArena - return a pointer to the message's arena pool
141 */
142 PLArenaPool *
NSS_CMSMessage_GetArena(NSSCMSMessage * cmsg)143 NSS_CMSMessage_GetArena(NSSCMSMessage *cmsg)
144 {
145 if (cmsg == NULL) {
146 return NULL;
147 }
148
149 return cmsg->poolp;
150 }
151
152 /*
153 * NSS_CMSMessage_GetContentInfo - return a pointer to the top level contentInfo
154 */
155 NSSCMSContentInfo *
NSS_CMSMessage_GetContentInfo(NSSCMSMessage * cmsg)156 NSS_CMSMessage_GetContentInfo(NSSCMSMessage *cmsg)
157 {
158 if (cmsg == NULL) {
159 return NULL;
160 }
161
162 return &(cmsg->contentInfo);
163 }
164
165 /*
166 * Return a pointer to the actual content.
167 * In the case of those types which are encrypted, this returns the *plain* content.
168 * In case of nested contentInfos, this descends and retrieves the innermost content.
169 */
170 SECItem *
NSS_CMSMessage_GetContent(NSSCMSMessage * cmsg)171 NSS_CMSMessage_GetContent(NSSCMSMessage *cmsg)
172 {
173 if (cmsg == NULL) {
174 return NULL;
175 }
176
177 /* this is a shortcut */
178 NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
179 SECItem *pItem = NSS_CMSContentInfo_GetInnerContent(cinfo);
180 return pItem;
181 }
182
183 /*
184 * NSS_CMSMessage_ContentLevelCount - count number of levels of CMS content objects in this message
185 *
186 * CMS data content objects do not count.
187 */
188 int
NSS_CMSMessage_ContentLevelCount(NSSCMSMessage * cmsg)189 NSS_CMSMessage_ContentLevelCount(NSSCMSMessage *cmsg)
190 {
191 int count = 0;
192 NSSCMSContentInfo *cinfo;
193
194 if (cmsg == NULL) {
195 return 0;
196 }
197
198 /* walk down the chain of contentinfos */
199 for (cinfo = &(cmsg->contentInfo); cinfo != NULL;) {
200 count++;
201 cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo);
202 }
203 return count;
204 }
205
206 /*
207 * NSS_CMSMessage_ContentLevel - find content level #n
208 *
209 * CMS data content objects do not count.
210 */
211 NSSCMSContentInfo *
NSS_CMSMessage_ContentLevel(NSSCMSMessage * cmsg,int n)212 NSS_CMSMessage_ContentLevel(NSSCMSMessage *cmsg, int n)
213 {
214 int count = 0;
215 NSSCMSContentInfo *cinfo;
216
217 if (cmsg == NULL) {
218 return NULL;
219 }
220
221 /* walk down the chain of contentinfos */
222 for (cinfo = &(cmsg->contentInfo); cinfo != NULL && count < n;
223 cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
224 count++;
225 }
226
227 return cinfo;
228 }
229
230 /*
231 * NSS_CMSMessage_ContainsCertsOrCrls - see if message contains certs along the way
232 */
233 PRBool
NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage * cmsg)234 NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage *cmsg)
235 {
236 NSSCMSContentInfo *cinfo;
237
238 if (cmsg == NULL) {
239 return PR_FALSE;
240 }
241
242 /* descend into CMS message */
243 for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
244 cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
245 if (!NSS_CMSType_IsData(NSS_CMSContentInfo_GetContentTypeTag(cinfo)))
246 continue; /* next level */
247
248 if (NSS_CMSSignedData_ContainsCertsOrCrls(cinfo->content.signedData))
249 return PR_TRUE;
250 /* callback here for generic wrappers? */
251 }
252 return PR_FALSE;
253 }
254
255 /*
256 * NSS_CMSMessage_IsEncrypted - see if message contains a encrypted submessage
257 */
258 PRBool
NSS_CMSMessage_IsEncrypted(NSSCMSMessage * cmsg)259 NSS_CMSMessage_IsEncrypted(NSSCMSMessage *cmsg)
260 {
261 NSSCMSContentInfo *cinfo;
262
263 if (cmsg == NULL) {
264 return PR_FALSE;
265 }
266
267 /* walk down the chain of contentinfos */
268 for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
269 cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
270 switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
271 case SEC_OID_PKCS7_ENVELOPED_DATA:
272 case SEC_OID_PKCS7_ENCRYPTED_DATA:
273 return PR_TRUE;
274 default:
275 /* callback here for generic wrappers? */
276 break;
277 }
278 }
279 return PR_FALSE;
280 }
281
282 /*
283 * NSS_CMSMessage_IsSigned - see if message contains a signed submessage
284 *
285 * If the CMS message has a SignedData with a signature (not just a SignedData)
286 * return true; false otherwise. This can/should be called before calling
287 * VerifySignature, which will always indicate failure if no signature is
288 * present, but that does not mean there even was a signature!
289 * Note that the content itself can be empty (detached content was sent
290 * another way); it is the presence of the signature that matters.
291 */
292 PRBool
NSS_CMSMessage_IsSigned(NSSCMSMessage * cmsg)293 NSS_CMSMessage_IsSigned(NSSCMSMessage *cmsg)
294 {
295 NSSCMSContentInfo *cinfo;
296
297 if (cmsg == NULL) {
298 return PR_FALSE;
299 }
300
301 /* walk down the chain of contentinfos */
302 for (cinfo = &(cmsg->contentInfo); cinfo != NULL;
303 cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
304 switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
305 case SEC_OID_PKCS7_SIGNED_DATA:
306 if (cinfo->content.signedData == NULL) {
307 return PR_FALSE;
308 }
309 if (!NSS_CMSArray_IsEmpty((void **)cinfo->content.signedData->signerInfos)) {
310 return PR_TRUE;
311 }
312 break;
313 default:
314 /* callback here for generic wrappers? */
315 break;
316 }
317 }
318 return PR_FALSE;
319 }
320
321 /*
322 * NSS_CMSMessage_IsContentEmpty - see if content is empty
323 *
324 * returns PR_TRUE is innermost content length is < minLen
325 * XXX need the encrypted content length (why?)
326 */
327 PRBool
NSS_CMSMessage_IsContentEmpty(NSSCMSMessage * cmsg,unsigned int minLen)328 NSS_CMSMessage_IsContentEmpty(NSSCMSMessage *cmsg, unsigned int minLen)
329 {
330 SECItem *item = NULL;
331
332 if (cmsg == NULL) {
333 return PR_TRUE;
334 }
335
336 item = NSS_CMSContentInfo_GetContent(NSS_CMSMessage_GetContentInfo(cmsg));
337
338 if (!item) {
339 return PR_TRUE;
340 } else if (item->len <= minLen) {
341 return PR_TRUE;
342 }
343
344 return PR_FALSE;
345 }
346