|
1 /* -*- Mode: C; tab-width: 8 -*-*/ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 |
|
7 #include "crmf.h" |
|
8 #include "crmfi.h" |
|
9 #include "secitem.h" |
|
10 |
|
11 static CRMFPOPChoice |
|
12 crmf_get_popchoice_from_der(SECItem *derPOP) |
|
13 { |
|
14 CRMFPOPChoice retChoice; |
|
15 |
|
16 switch (derPOP->data[0] & 0x0f) { |
|
17 case 0: |
|
18 retChoice = crmfRAVerified; |
|
19 break; |
|
20 case 1: |
|
21 retChoice = crmfSignature; |
|
22 break; |
|
23 case 2: |
|
24 retChoice = crmfKeyEncipherment; |
|
25 break; |
|
26 case 3: |
|
27 retChoice = crmfKeyAgreement; |
|
28 break; |
|
29 default: |
|
30 retChoice = crmfNoPOPChoice; |
|
31 break; |
|
32 } |
|
33 return retChoice; |
|
34 } |
|
35 |
|
36 static SECStatus |
|
37 crmf_decode_process_raverified(CRMFCertReqMsg *inCertReqMsg) |
|
38 { |
|
39 CRMFProofOfPossession *pop; |
|
40 /* Just set up the structure so that the message structure |
|
41 * looks like one that was created using the API |
|
42 */ |
|
43 pop = inCertReqMsg->pop; |
|
44 pop->popChoice.raVerified.data = NULL; |
|
45 pop->popChoice.raVerified.len = 0; |
|
46 return SECSuccess; |
|
47 } |
|
48 |
|
49 static SECStatus |
|
50 crmf_decode_process_signature(CRMFCertReqMsg *inCertReqMsg) |
|
51 { |
|
52 PORT_Assert(inCertReqMsg->poolp); |
|
53 if (!inCertReqMsg->poolp) { |
|
54 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
55 return SECFailure; |
|
56 } |
|
57 return SEC_ASN1Decode(inCertReqMsg->poolp, |
|
58 &inCertReqMsg->pop->popChoice.signature, |
|
59 CRMFPOPOSigningKeyTemplate, |
|
60 (const char*)inCertReqMsg->derPOP.data, |
|
61 inCertReqMsg->derPOP.len); |
|
62 } |
|
63 |
|
64 static CRMFPOPOPrivKeyChoice |
|
65 crmf_get_messagechoice_from_der(SECItem *derPOP) |
|
66 { |
|
67 CRMFPOPOPrivKeyChoice retChoice; |
|
68 |
|
69 switch (derPOP->data[2] & 0x0f) { |
|
70 case 0: |
|
71 retChoice = crmfThisMessage; |
|
72 break; |
|
73 case 1: |
|
74 retChoice = crmfSubsequentMessage; |
|
75 break; |
|
76 case 2: |
|
77 retChoice = crmfDHMAC; |
|
78 break; |
|
79 default: |
|
80 retChoice = crmfNoMessage; |
|
81 } |
|
82 return retChoice; |
|
83 } |
|
84 |
|
85 static SECStatus |
|
86 crmf_decode_process_popoprivkey(CRMFCertReqMsg *inCertReqMsg) |
|
87 { |
|
88 /* We've got a union, so a pointer to one POPOPrivKey |
|
89 * struct is the same as having a pointer to the other |
|
90 * one. |
|
91 */ |
|
92 CRMFPOPOPrivKey *popoPrivKey = |
|
93 &inCertReqMsg->pop->popChoice.keyEncipherment; |
|
94 SECItem *derPOP, privKeyDer; |
|
95 SECStatus rv; |
|
96 |
|
97 derPOP = &inCertReqMsg->derPOP; |
|
98 popoPrivKey->messageChoice = crmf_get_messagechoice_from_der(derPOP); |
|
99 if (popoPrivKey->messageChoice == crmfNoMessage) { |
|
100 return SECFailure; |
|
101 } |
|
102 /* If we ever encounter BER encodings of this, we'll get in trouble*/ |
|
103 switch (popoPrivKey->messageChoice) { |
|
104 case crmfThisMessage: |
|
105 case crmfDHMAC: |
|
106 privKeyDer.type = derPOP->type; |
|
107 privKeyDer.data = &derPOP->data[5]; |
|
108 privKeyDer.len = derPOP->len - 5; |
|
109 break; |
|
110 case crmfSubsequentMessage: |
|
111 privKeyDer.type = derPOP->type; |
|
112 privKeyDer.data = &derPOP->data[4]; |
|
113 privKeyDer.len = derPOP->len - 4; |
|
114 break; |
|
115 default: |
|
116 return SECFailure; |
|
117 } |
|
118 |
|
119 rv = SECITEM_CopyItem(inCertReqMsg->poolp, |
|
120 &popoPrivKey->message.subsequentMessage, |
|
121 &privKeyDer); |
|
122 |
|
123 if (rv != SECSuccess) { |
|
124 return rv; |
|
125 } |
|
126 |
|
127 if (popoPrivKey->messageChoice == crmfThisMessage || |
|
128 popoPrivKey->messageChoice == crmfDHMAC) { |
|
129 |
|
130 popoPrivKey->message.thisMessage.len = |
|
131 CRMF_BYTES_TO_BITS(privKeyDer.len) - (int)derPOP->data[4]; |
|
132 |
|
133 } |
|
134 return SECSuccess; |
|
135 } |
|
136 |
|
137 static SECStatus |
|
138 crmf_decode_process_keyagreement(CRMFCertReqMsg *inCertReqMsg) |
|
139 { |
|
140 return crmf_decode_process_popoprivkey(inCertReqMsg); |
|
141 } |
|
142 |
|
143 static SECStatus |
|
144 crmf_decode_process_keyencipherment(CRMFCertReqMsg *inCertReqMsg) |
|
145 { |
|
146 SECStatus rv; |
|
147 |
|
148 rv = crmf_decode_process_popoprivkey(inCertReqMsg); |
|
149 if (rv != SECSuccess) { |
|
150 return rv; |
|
151 } |
|
152 if (inCertReqMsg->pop->popChoice.keyEncipherment.messageChoice == |
|
153 crmfDHMAC) { |
|
154 /* Key Encipherment can not use the dhMAC option for |
|
155 * POPOPrivKey. |
|
156 */ |
|
157 return SECFailure; |
|
158 } |
|
159 return SECSuccess; |
|
160 } |
|
161 |
|
162 static SECStatus |
|
163 crmf_decode_process_pop(CRMFCertReqMsg *inCertReqMsg) |
|
164 { |
|
165 SECItem *derPOP; |
|
166 PLArenaPool *poolp; |
|
167 CRMFProofOfPossession *pop; |
|
168 void *mark; |
|
169 SECStatus rv; |
|
170 |
|
171 derPOP = &inCertReqMsg->derPOP; |
|
172 poolp = inCertReqMsg->poolp; |
|
173 if (derPOP->data == NULL) { |
|
174 /* There is no Proof of Possession field in this message. */ |
|
175 return SECSuccess; |
|
176 } |
|
177 mark = PORT_ArenaMark(poolp); |
|
178 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession); |
|
179 if (pop == NULL) { |
|
180 goto loser; |
|
181 } |
|
182 pop->popUsed = crmf_get_popchoice_from_der(derPOP); |
|
183 if (pop->popUsed == crmfNoPOPChoice) { |
|
184 /* A bad encoding of CRMF. Not a valid tag was given to the |
|
185 * Proof Of Possession field. |
|
186 */ |
|
187 goto loser; |
|
188 } |
|
189 inCertReqMsg->pop = pop; |
|
190 switch (pop->popUsed) { |
|
191 case crmfRAVerified: |
|
192 rv = crmf_decode_process_raverified(inCertReqMsg); |
|
193 break; |
|
194 case crmfSignature: |
|
195 rv = crmf_decode_process_signature(inCertReqMsg); |
|
196 break; |
|
197 case crmfKeyEncipherment: |
|
198 rv = crmf_decode_process_keyencipherment(inCertReqMsg); |
|
199 break; |
|
200 case crmfKeyAgreement: |
|
201 rv = crmf_decode_process_keyagreement(inCertReqMsg); |
|
202 break; |
|
203 default: |
|
204 rv = SECFailure; |
|
205 } |
|
206 if (rv != SECSuccess) { |
|
207 goto loser; |
|
208 } |
|
209 PORT_ArenaUnmark(poolp, mark); |
|
210 return SECSuccess; |
|
211 |
|
212 loser: |
|
213 PORT_ArenaRelease(poolp, mark); |
|
214 inCertReqMsg->pop = NULL; |
|
215 return SECFailure; |
|
216 |
|
217 } |
|
218 |
|
219 static SECStatus |
|
220 crmf_decode_process_single_control(PLArenaPool *poolp, |
|
221 CRMFControl *inControl) |
|
222 { |
|
223 const SEC_ASN1Template *asn1Template = NULL; |
|
224 |
|
225 inControl->tag = SECOID_FindOIDTag(&inControl->derTag); |
|
226 asn1Template = crmf_get_pkiarchiveoptions_subtemplate(inControl); |
|
227 |
|
228 PORT_Assert (asn1Template != NULL); |
|
229 PORT_Assert (poolp != NULL); |
|
230 if (!asn1Template || !poolp) { |
|
231 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
232 return SECFailure; |
|
233 } |
|
234 /* We've got a union, so passing a pointer to one element of the |
|
235 * union is the same as passing a pointer to any of the other |
|
236 * members of the union. |
|
237 */ |
|
238 return SEC_ASN1Decode(poolp, &inControl->value.archiveOptions, |
|
239 asn1Template, (const char*)inControl->derValue.data, |
|
240 inControl->derValue.len); |
|
241 } |
|
242 |
|
243 static SECStatus |
|
244 crmf_decode_process_controls(CRMFCertReqMsg *inCertReqMsg) |
|
245 { |
|
246 int i, numControls; |
|
247 SECStatus rv; |
|
248 PLArenaPool *poolp; |
|
249 CRMFControl **controls; |
|
250 |
|
251 numControls = CRMF_CertRequestGetNumControls(inCertReqMsg->certReq); |
|
252 controls = inCertReqMsg->certReq->controls; |
|
253 poolp = inCertReqMsg->poolp; |
|
254 for (i=0; i < numControls; i++) { |
|
255 rv = crmf_decode_process_single_control(poolp, controls[i]); |
|
256 if (rv != SECSuccess) { |
|
257 return SECFailure; |
|
258 } |
|
259 } |
|
260 return SECSuccess; |
|
261 } |
|
262 |
|
263 static SECStatus |
|
264 crmf_decode_process_single_reqmsg(CRMFCertReqMsg *inCertReqMsg) |
|
265 { |
|
266 SECStatus rv; |
|
267 |
|
268 rv = crmf_decode_process_pop(inCertReqMsg); |
|
269 if (rv != SECSuccess) { |
|
270 goto loser; |
|
271 } |
|
272 |
|
273 rv = crmf_decode_process_controls(inCertReqMsg); |
|
274 if (rv != SECSuccess) { |
|
275 goto loser; |
|
276 } |
|
277 inCertReqMsg->certReq->certTemplate.numExtensions = |
|
278 CRMF_CertRequestGetNumberOfExtensions(inCertReqMsg->certReq); |
|
279 inCertReqMsg->isDecoded = PR_TRUE; |
|
280 rv = SECSuccess; |
|
281 loser: |
|
282 return rv; |
|
283 } |
|
284 |
|
285 CRMFCertReqMsg* |
|
286 CRMF_CreateCertReqMsgFromDER (const char * buf, long len) |
|
287 { |
|
288 PLArenaPool *poolp; |
|
289 CRMFCertReqMsg *certReqMsg; |
|
290 SECStatus rv; |
|
291 |
|
292 poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); |
|
293 if (poolp == NULL) { |
|
294 goto loser; |
|
295 } |
|
296 certReqMsg = PORT_ArenaZNew (poolp, CRMFCertReqMsg); |
|
297 if (certReqMsg == NULL) { |
|
298 goto loser; |
|
299 } |
|
300 certReqMsg->poolp = poolp; |
|
301 rv = SEC_ASN1Decode(poolp, certReqMsg, CRMFCertReqMsgTemplate, buf, len); |
|
302 if (rv != SECSuccess) { |
|
303 goto loser; |
|
304 } |
|
305 |
|
306 rv = crmf_decode_process_single_reqmsg(certReqMsg); |
|
307 if (rv != SECSuccess) { |
|
308 goto loser; |
|
309 } |
|
310 |
|
311 return certReqMsg; |
|
312 loser: |
|
313 if (poolp != NULL) { |
|
314 PORT_FreeArena(poolp, PR_FALSE); |
|
315 } |
|
316 return NULL; |
|
317 } |
|
318 |
|
319 CRMFCertReqMessages* |
|
320 CRMF_CreateCertReqMessagesFromDER(const char *buf, long len) |
|
321 { |
|
322 long arenaSize; |
|
323 int i; |
|
324 SECStatus rv; |
|
325 PLArenaPool *poolp; |
|
326 CRMFCertReqMessages *certReqMsgs; |
|
327 |
|
328 PORT_Assert (buf != NULL); |
|
329 /* Wanna make sure the arena is big enough to store all of the requests |
|
330 * coming in. We'll guestimate according to the length of the buffer. |
|
331 */ |
|
332 arenaSize = len + len/2; |
|
333 poolp = PORT_NewArena(arenaSize); |
|
334 if (poolp == NULL) { |
|
335 return NULL; |
|
336 } |
|
337 certReqMsgs = PORT_ArenaZNew(poolp, CRMFCertReqMessages); |
|
338 if (certReqMsgs == NULL) { |
|
339 goto loser; |
|
340 } |
|
341 certReqMsgs->poolp = poolp; |
|
342 rv = SEC_ASN1Decode(poolp, certReqMsgs, CRMFCertReqMessagesTemplate, |
|
343 buf, len); |
|
344 if (rv != SECSuccess) { |
|
345 goto loser; |
|
346 } |
|
347 for (i=0; certReqMsgs->messages[i] != NULL; i++) { |
|
348 /* The sub-routines expect the individual messages to have |
|
349 * an arena. We'll give them one temporarily. |
|
350 */ |
|
351 certReqMsgs->messages[i]->poolp = poolp; |
|
352 rv = crmf_decode_process_single_reqmsg(certReqMsgs->messages[i]); |
|
353 if (rv != SECSuccess) { |
|
354 goto loser; |
|
355 } |
|
356 certReqMsgs->messages[i]->poolp = NULL; |
|
357 } |
|
358 return certReqMsgs; |
|
359 |
|
360 loser: |
|
361 PORT_FreeArena(poolp, PR_FALSE); |
|
362 return NULL; |
|
363 } |