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