Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 #include "crmf.h"
8 #include "crmfi.h"
9 #include "secasn1.h"
10 #include "keyhi.h"
11 #include "cryptohi.h"
13 #define CRMF_DEFAULT_ALLOC_SIZE 1024
15 SECStatus
16 crmf_init_encoder_callback_arg (struct crmfEncoderArg *encoderArg,
17 SECItem *derDest)
18 {
19 derDest->data = PORT_ZNewArray(unsigned char, CRMF_DEFAULT_ALLOC_SIZE);
20 if (derDest->data == NULL) {
21 return SECFailure;
22 }
23 derDest->len = 0;
24 encoderArg->allocatedLen = CRMF_DEFAULT_ALLOC_SIZE;
25 encoderArg->buffer = derDest;
26 return SECSuccess;
28 }
30 /* Caller should release or unmark the pool, instead of doing it here.
31 ** But there are NO callers of this function at present...
32 */
33 SECStatus
34 CRMF_CertReqMsgSetRAVerifiedPOP(CRMFCertReqMsg *inCertReqMsg)
35 {
36 SECItem *dummy;
37 CRMFProofOfPossession *pop;
38 PLArenaPool *poolp;
39 void *mark;
41 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
42 poolp = inCertReqMsg->poolp;
43 mark = PORT_ArenaMark(poolp);
44 if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) {
45 return SECFailure;
46 }
47 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
48 if (pop == NULL) {
49 goto loser;
50 }
51 pop->popUsed = crmfRAVerified;
52 pop->popChoice.raVerified.data = NULL;
53 pop->popChoice.raVerified.len = 0;
54 inCertReqMsg->pop = pop;
55 dummy = SEC_ASN1EncodeItem(poolp, &(inCertReqMsg->derPOP),
56 &(pop->popChoice.raVerified),
57 CRMFRAVerifiedTemplate);
58 return SECSuccess;
59 loser:
60 PORT_ArenaRelease(poolp, mark);
61 return SECFailure;
62 }
64 static SECOidTag
65 crmf_get_key_sign_tag(SECKEYPublicKey *inPubKey)
66 {
67 /* maintain backward compatibility with older
68 * implementations */
69 if (inPubKey->keyType == rsaKey) {
70 return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
71 }
72 return SEC_GetSignatureAlgorithmOidTag(inPubKey->keyType, SEC_OID_UNKNOWN);
73 }
75 static SECAlgorithmID*
76 crmf_create_poposignkey_algid(PLArenaPool *poolp,
77 SECKEYPublicKey *inPubKey)
78 {
79 SECAlgorithmID *algID;
80 SECOidTag tag;
81 SECStatus rv;
82 void *mark;
84 mark = PORT_ArenaMark(poolp);
85 algID = PORT_ArenaZNew(poolp, SECAlgorithmID);
86 if (algID == NULL) {
87 goto loser;
88 }
89 tag = crmf_get_key_sign_tag(inPubKey);
90 if (tag == SEC_OID_UNKNOWN) {
91 goto loser;
92 }
93 rv = SECOID_SetAlgorithmID(poolp, algID, tag, NULL);
94 if (rv != SECSuccess) {
95 goto loser;
96 }
97 PORT_ArenaUnmark(poolp, mark);
98 return algID;
99 loser:
100 PORT_ArenaRelease(poolp, mark);
101 return NULL;
102 }
104 static CRMFPOPOSigningKeyInput*
105 crmf_create_poposigningkeyinput(PLArenaPool *poolp, CERTCertificate *inCert,
106 CRMFMACPasswordCallback fn, void *arg)
107 {
108 /* PSM isn't going to do this, so we'll fail here for now.*/
109 return NULL;
110 }
112 void
113 crmf_generic_encoder_callback(void *arg, const char* buf, unsigned long len,
114 int depth, SEC_ASN1EncodingPart data_kind)
115 {
116 struct crmfEncoderArg *encoderArg = (struct crmfEncoderArg*)arg;
117 unsigned char *cursor;
119 if (encoderArg->buffer->len + len > encoderArg->allocatedLen) {
120 int newSize = encoderArg->buffer->len+CRMF_DEFAULT_ALLOC_SIZE;
121 void *dummy = PORT_Realloc(encoderArg->buffer->data, newSize);
122 if (dummy == NULL) {
123 /* I really want to return an error code here */
124 PORT_Assert(0);
125 return;
126 }
127 encoderArg->buffer->data = dummy;
128 encoderArg->allocatedLen = newSize;
129 }
130 cursor = &(encoderArg->buffer->data[encoderArg->buffer->len]);
131 PORT_Memcpy (cursor, buf, len);
132 encoderArg->buffer->len += len;
133 }
135 static SECStatus
136 crmf_encode_certreq(CRMFCertRequest *inCertReq, SECItem *derDest)
137 {
138 struct crmfEncoderArg encoderArg;
139 SECStatus rv;
141 rv = crmf_init_encoder_callback_arg (&encoderArg, derDest);
142 if (rv != SECSuccess) {
143 return SECFailure;
144 }
145 return SEC_ASN1Encode(inCertReq, CRMFCertRequestTemplate,
146 crmf_generic_encoder_callback, &encoderArg);
147 }
149 static SECStatus
150 crmf_sign_certreq(PLArenaPool *poolp,
151 CRMFPOPOSigningKey *crmfSignKey,
152 CRMFCertRequest *certReq,
153 SECKEYPrivateKey *inKey,
154 SECAlgorithmID *inAlgId)
155 {
156 SECItem derCertReq = { siBuffer, NULL, 0 };
157 SECItem certReqSig = { siBuffer, NULL, 0 };
158 SECStatus rv = SECSuccess;
160 rv = crmf_encode_certreq(certReq, &derCertReq);
161 if (rv != SECSuccess) {
162 goto loser;
163 }
164 rv = SEC_SignData(&certReqSig, derCertReq.data, derCertReq.len,
165 inKey,SECOID_GetAlgorithmTag(inAlgId));
166 if (rv != SECSuccess) {
167 goto loser;
168 }
170 /* Now make it a part of the POPOSigningKey */
171 rv = SECITEM_CopyItem(poolp, &(crmfSignKey->signature), &certReqSig);
172 /* Convert this length to number of bits */
173 crmfSignKey->signature.len <<= 3;
175 loser:
176 if (derCertReq.data != NULL) {
177 PORT_Free(derCertReq.data);
178 }
179 if (certReqSig.data != NULL) {
180 PORT_Free(certReqSig.data);
181 }
182 return rv;
183 }
185 static SECStatus
186 crmf_create_poposignkey(PLArenaPool *poolp,
187 CRMFCertReqMsg *inCertReqMsg,
188 CRMFPOPOSigningKeyInput *signKeyInput,
189 SECKEYPrivateKey *inPrivKey,
190 SECAlgorithmID *inAlgID,
191 CRMFPOPOSigningKey *signKey)
192 {
193 CRMFCertRequest *certReq;
194 void *mark;
195 PRBool useSignKeyInput;
196 SECStatus rv;
198 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL);
199 mark = PORT_ArenaMark(poolp);
200 if (signKey == NULL) {
201 goto loser;
202 }
203 certReq = inCertReqMsg->certReq;
204 useSignKeyInput = !(CRMF_DoesRequestHaveField(certReq,crmfSubject) &&
205 CRMF_DoesRequestHaveField(certReq,crmfPublicKey));
207 if (useSignKeyInput) {
208 goto loser;
209 } else {
210 rv = crmf_sign_certreq(poolp, signKey, certReq,inPrivKey, inAlgID);
211 if (rv != SECSuccess) {
212 goto loser;
213 }
214 }
215 PORT_ArenaUnmark(poolp,mark);
216 return SECSuccess;
217 loser:
218 PORT_ArenaRelease(poolp,mark);
219 return SECFailure;
220 }
222 SECStatus
223 CRMF_CertReqMsgSetSignaturePOP(CRMFCertReqMsg *inCertReqMsg,
224 SECKEYPrivateKey *inPrivKey,
225 SECKEYPublicKey *inPubKey,
226 CERTCertificate *inCertForInput,
227 CRMFMACPasswordCallback fn,
228 void *arg)
229 {
230 SECAlgorithmID *algID;
231 PLArenaPool *poolp;
232 SECItem derTemp = {siBuffer, NULL, 0};
233 void *mark;
234 SECStatus rv;
235 CRMFPOPOSigningKeyInput *signKeyInput = NULL;
236 CRMFCertRequest *certReq;
237 CRMFProofOfPossession *pop;
238 struct crmfEncoderArg encoderArg;
240 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL &&
241 inCertReqMsg->pop == NULL);
242 certReq = inCertReqMsg->certReq;
243 if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice ||
244 !CRMF_DoesRequestHaveField(certReq, crmfPublicKey)) {
245 return SECFailure;
246 }
247 poolp = inCertReqMsg->poolp;
248 mark = PORT_ArenaMark(poolp);
249 algID = crmf_create_poposignkey_algid(poolp, inPubKey);
251 if(!CRMF_DoesRequestHaveField(certReq,crmfSubject)) {
252 signKeyInput = crmf_create_poposigningkeyinput(poolp, inCertForInput,
253 fn, arg);
254 if (signKeyInput == NULL) {
255 goto loser;
256 }
257 }
259 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
260 if (pop == NULL) {
261 goto loser;
262 }
264 rv = crmf_create_poposignkey(poolp, inCertReqMsg,
265 signKeyInput, inPrivKey, algID,
266 &(pop->popChoice.signature));
267 if (rv != SECSuccess) {
268 goto loser;
269 }
271 pop->popUsed = crmfSignature;
272 pop->popChoice.signature.algorithmIdentifier = algID;
273 inCertReqMsg->pop = pop;
275 rv = crmf_init_encoder_callback_arg (&encoderArg, &derTemp);
276 if (rv != SECSuccess) {
277 goto loser;
278 }
279 rv = SEC_ASN1Encode(&pop->popChoice.signature,
280 CRMFPOPOSigningKeyTemplate,
281 crmf_generic_encoder_callback, &encoderArg);
282 if (rv != SECSuccess) {
283 goto loser;
284 }
285 rv = SECITEM_CopyItem(poolp, &(inCertReqMsg->derPOP), &derTemp);
286 if (rv != SECSuccess) {
287 goto loser;
288 }
289 PORT_Free (derTemp.data);
290 PORT_ArenaUnmark(poolp,mark);
291 return SECSuccess;
293 loser:
294 PORT_ArenaRelease(poolp,mark);
295 if (derTemp.data != NULL) {
296 PORT_Free(derTemp.data);
297 }
298 return SECFailure;
299 }
301 static const SEC_ASN1Template*
302 crmf_get_popoprivkey_subtemplate(CRMFPOPOPrivKey *inPrivKey)
303 {
304 const SEC_ASN1Template *retTemplate = NULL;
306 switch (inPrivKey->messageChoice) {
307 case crmfThisMessage:
308 retTemplate = CRMFThisMessageTemplate;
309 break;
310 case crmfSubsequentMessage:
311 retTemplate = CRMFSubsequentMessageTemplate;
312 break;
313 case crmfDHMAC:
314 retTemplate = CRMFDHMACTemplate;
315 break;
316 default:
317 retTemplate = NULL;
318 }
319 return retTemplate;
320 }
322 static SECStatus
323 crmf_encode_popoprivkey(PLArenaPool *poolp,
324 CRMFCertReqMsg *inCertReqMsg,
325 CRMFPOPOPrivKey *popoPrivKey,
326 const SEC_ASN1Template *privKeyTemplate)
327 {
328 struct crmfEncoderArg encoderArg;
329 SECItem derTemp = { siBuffer, NULL, 0 };
330 SECStatus rv;
331 void *mark;
332 const SEC_ASN1Template *subDerTemplate;
334 mark = PORT_ArenaMark(poolp);
335 rv = crmf_init_encoder_callback_arg(&encoderArg, &derTemp);
336 if (rv != SECSuccess) {
337 goto loser;
338 }
339 subDerTemplate = crmf_get_popoprivkey_subtemplate(popoPrivKey);
340 /* We've got a union, so a pointer to one item is a pointer to
341 * all the items in the union.
342 */
343 rv = SEC_ASN1Encode(&popoPrivKey->message.thisMessage,
344 subDerTemplate,
345 crmf_generic_encoder_callback, &encoderArg);
346 if (rv != SECSuccess) {
347 goto loser;
348 }
349 if (encoderArg.allocatedLen > derTemp.len+2) {
350 void *dummy = PORT_Realloc(derTemp.data, derTemp.len+2);
351 if (dummy == NULL) {
352 goto loser;
353 }
354 derTemp.data = dummy;
355 }
356 PORT_Memmove(&derTemp.data[2], &derTemp.data[0], derTemp.len);
357 /* I couldn't figure out how to get the ASN1 encoder to implicitly
358 * tag an implicitly tagged der blob. So I'm putting in the outter-
359 * most tag myself. -javi
360 */
361 derTemp.data[0] = (unsigned char)privKeyTemplate->kind;
362 derTemp.data[1] = (unsigned char)derTemp.len;
363 derTemp.len += 2;
364 rv = SECITEM_CopyItem(poolp, &inCertReqMsg->derPOP, &derTemp);
365 if (rv != SECSuccess) {
366 goto loser;
367 }
368 PORT_Free(derTemp.data);
369 PORT_ArenaUnmark(poolp, mark);
370 return SECSuccess;
371 loser:
372 PORT_ArenaRelease(poolp, mark);
373 if (derTemp.data) {
374 PORT_Free(derTemp.data);
375 }
376 return SECFailure;
377 }
379 static const SEC_ASN1Template*
380 crmf_get_template_for_privkey(CRMFPOPChoice inChoice)
381 {
382 switch (inChoice) {
383 case crmfKeyAgreement:
384 return CRMFPOPOKeyAgreementTemplate;
385 case crmfKeyEncipherment:
386 return CRMFPOPOKeyEnciphermentTemplate;
387 default:
388 break;
389 }
390 return NULL;
391 }
393 static SECStatus
394 crmf_add_privkey_thismessage(CRMFCertReqMsg *inCertReqMsg, SECItem *encPrivKey,
395 CRMFPOPChoice inChoice)
396 {
397 PLArenaPool *poolp;
398 void *mark;
399 CRMFPOPOPrivKey *popoPrivKey;
400 CRMFProofOfPossession *pop;
401 SECStatus rv;
403 PORT_Assert(inCertReqMsg != NULL && encPrivKey != NULL);
404 poolp = inCertReqMsg->poolp;
405 mark = PORT_ArenaMark(poolp);
406 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
407 if (pop == NULL) {
408 goto loser;
409 }
410 pop->popUsed = inChoice;
411 /* popChoice is a union, so getting a pointer to one
412 * field gives me a pointer to the other fields as
413 * well. This in essence points to both
414 * pop->popChoice.keyEncipherment and
415 * pop->popChoice.keyAgreement
416 */
417 popoPrivKey = &pop->popChoice.keyEncipherment;
419 rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.thisMessage),
420 encPrivKey);
421 if (rv != SECSuccess) {
422 goto loser;
423 }
424 popoPrivKey->message.thisMessage.len <<= 3;
425 popoPrivKey->messageChoice = crmfThisMessage;
426 inCertReqMsg->pop = pop;
427 rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
428 crmf_get_template_for_privkey(inChoice));
429 if (rv != SECSuccess) {
430 goto loser;
431 }
432 PORT_ArenaUnmark(poolp, mark);
433 return SECSuccess;
435 loser:
436 PORT_ArenaRelease(poolp, mark);
437 return SECFailure;
438 }
440 static SECStatus
441 crmf_add_privkey_dhmac(CRMFCertReqMsg *inCertReqMsg, SECItem *dhmac,
442 CRMFPOPChoice inChoice)
443 {
444 PLArenaPool *poolp;
445 void *mark;
446 CRMFPOPOPrivKey *popoPrivKey;
447 CRMFProofOfPossession *pop;
448 SECStatus rv;
450 PORT_Assert(inCertReqMsg != NULL && dhmac != NULL);
451 poolp = inCertReqMsg->poolp;
452 mark = PORT_ArenaMark(poolp);
453 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
454 if (pop == NULL) {
455 goto loser;
456 }
457 pop->popUsed = inChoice;
458 popoPrivKey = &pop->popChoice.keyAgreement;
460 rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.dhMAC),
461 dhmac);
462 if (rv != SECSuccess) {
463 goto loser;
464 }
465 popoPrivKey->message.dhMAC.len <<= 3;
466 popoPrivKey->messageChoice = crmfDHMAC;
467 inCertReqMsg->pop = pop;
468 rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
469 crmf_get_template_for_privkey(inChoice));
470 if (rv != SECSuccess) {
471 goto loser;
472 }
473 PORT_ArenaUnmark(poolp, mark);
474 return SECSuccess;
476 loser:
477 PORT_ArenaRelease(poolp, mark);
478 return SECFailure;
479 }
481 static SECStatus
482 crmf_add_privkey_subseqmessage(CRMFCertReqMsg *inCertReqMsg,
483 CRMFSubseqMessOptions subsequentMessage,
484 CRMFPOPChoice inChoice)
485 {
486 void *mark;
487 PLArenaPool *poolp;
488 CRMFProofOfPossession *pop;
489 CRMFPOPOPrivKey *popoPrivKey;
490 SECStatus rv;
491 const SEC_ASN1Template *privKeyTemplate;
493 if (subsequentMessage == crmfNoSubseqMess) {
494 return SECFailure;
495 }
496 poolp = inCertReqMsg->poolp;
497 mark = PORT_ArenaMark(poolp);
498 pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
499 if (pop == NULL) {
500 goto loser;
501 }
503 pop->popUsed = inChoice;
504 /*
505 * We have a union, so a pointer to one member of the union
506 * is also a member to another member of that same union.
507 */
508 popoPrivKey = &pop->popChoice.keyEncipherment;
510 switch (subsequentMessage) {
511 case crmfEncrCert:
512 rv = crmf_encode_integer(poolp,
513 &(popoPrivKey->message.subsequentMessage),
514 0);
515 break;
516 case crmfChallengeResp:
517 rv = crmf_encode_integer(poolp,
518 &(popoPrivKey->message.subsequentMessage),
519 1);
520 break;
521 default:
522 goto loser;
523 }
524 if (rv != SECSuccess) {
525 goto loser;
526 }
527 popoPrivKey->messageChoice = crmfSubsequentMessage;
528 privKeyTemplate = crmf_get_template_for_privkey(inChoice);
529 inCertReqMsg->pop = pop;
530 rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
531 privKeyTemplate);
533 if (rv != SECSuccess) {
534 goto loser;
535 }
536 PORT_ArenaUnmark(poolp, mark);
537 return SECSuccess;
538 loser:
539 PORT_ArenaRelease(poolp, mark);
540 return SECFailure;
541 }
543 SECStatus
544 CRMF_CertReqMsgSetKeyEnciphermentPOP(CRMFCertReqMsg *inCertReqMsg,
545 CRMFPOPOPrivKeyChoice inKeyChoice,
546 CRMFSubseqMessOptions subseqMess,
547 SECItem *encPrivKey)
548 {
549 SECStatus rv;
551 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
552 if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) {
553 return SECFailure;
554 }
555 switch (inKeyChoice) {
556 case crmfThisMessage:
557 rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey,
558 crmfKeyEncipherment);
559 break;
560 case crmfSubsequentMessage:
561 rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess,
562 crmfKeyEncipherment);
563 break;
564 case crmfDHMAC:
565 default:
566 rv = SECFailure;
567 }
568 return rv;
569 }
571 SECStatus
572 CRMF_CertReqMsgSetKeyAgreementPOP (CRMFCertReqMsg *inCertReqMsg,
573 CRMFPOPOPrivKeyChoice inKeyChoice,
574 CRMFSubseqMessOptions subseqMess,
575 SECItem *encPrivKey)
576 {
577 SECStatus rv;
579 PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
580 switch (inKeyChoice) {
581 case crmfThisMessage:
582 rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey,
583 crmfKeyAgreement);
584 break;
585 case crmfSubsequentMessage:
586 rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess,
587 crmfKeyAgreement);
588 break;
589 case crmfDHMAC:
590 /* In this case encPrivKey should be the calculated dhMac
591 * as specified in RFC 2511 */
592 rv = crmf_add_privkey_dhmac(inCertReqMsg, encPrivKey,
593 crmfKeyAgreement);
594 break;
595 default:
596 rv = SECFailure;
597 }
598 return rv;
599 }