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