Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C; tab-width: 8 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "crmf.h"
#include "crmfi.h"
#include "secasn1.h"
#include "keyhi.h"
#include "cryptohi.h"
#define CRMF_DEFAULT_ALLOC_SIZE 1024U
SECStatus
crmf_init_encoder_callback_arg(struct crmfEncoderArg *encoderArg,
SECItem *derDest)
{
derDest->data = PORT_ZNewArray(unsigned char, CRMF_DEFAULT_ALLOC_SIZE);
if (derDest->data == NULL) {
return SECFailure;
}
derDest->len = 0;
encoderArg->allocatedLen = CRMF_DEFAULT_ALLOC_SIZE;
encoderArg->buffer = derDest;
return SECSuccess;
}
/* Caller should release or unmark the pool, instead of doing it here.
** But there are NO callers of this function at present...
*/
SECStatus
CRMF_CertReqMsgSetRAVerifiedPOP(CRMFCertReqMsg *inCertReqMsg)
{
CRMFProofOfPossession *pop;
PLArenaPool *poolp;
void *mark;
PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
poolp = inCertReqMsg->poolp;
mark = PORT_ArenaMark(poolp);
if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) {
return SECFailure;
}
pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
if (pop == NULL) {
goto loser;
}
pop->popUsed = crmfRAVerified;
pop->popChoice.raVerified.data = NULL;
pop->popChoice.raVerified.len = 0;
inCertReqMsg->pop = pop;
(void)SEC_ASN1EncodeItem(poolp, &(inCertReqMsg->derPOP),
&(pop->popChoice.raVerified),
CRMFRAVerifiedTemplate);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
static SECOidTag
crmf_get_key_sign_tag(SECKEYPublicKey *inPubKey)
{
/* maintain backward compatibility with older
* implementations */
if (inPubKey->keyType == rsaKey) {
return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
}
return SEC_GetSignatureAlgorithmOidTag(inPubKey->keyType, SEC_OID_UNKNOWN);
}
static SECAlgorithmID *
crmf_create_poposignkey_algid(PLArenaPool *poolp,
SECKEYPublicKey *inPubKey)
{
SECAlgorithmID *algID;
SECOidTag tag;
SECStatus rv;
void *mark;
mark = PORT_ArenaMark(poolp);
algID = PORT_ArenaZNew(poolp, SECAlgorithmID);
if (algID == NULL) {
goto loser;
}
tag = crmf_get_key_sign_tag(inPubKey);
if (tag == SEC_OID_UNKNOWN) {
goto loser;
}
rv = SECOID_SetAlgorithmID(poolp, algID, tag, NULL);
if (rv != SECSuccess) {
goto loser;
}
PORT_ArenaUnmark(poolp, mark);
return algID;
loser:
PORT_ArenaRelease(poolp, mark);
return NULL;
}
static CRMFPOPOSigningKeyInput *
crmf_create_poposigningkeyinput(PLArenaPool *poolp, CERTCertificate *inCert,
CRMFMACPasswordCallback fn, void *arg)
{
/* PSM isn't going to do this, so we'll fail here for now.*/
return NULL;
}
void
crmf_generic_encoder_callback(void *arg, const char *buf, unsigned long len,
int depth, SEC_ASN1EncodingPart data_kind)
{
struct crmfEncoderArg *encoderArg = (struct crmfEncoderArg *)arg;
unsigned char *cursor;
if (encoderArg->buffer->len + len > encoderArg->allocatedLen) {
int newSize = encoderArg->buffer->len + CRMF_DEFAULT_ALLOC_SIZE;
void *dummy = PORT_Realloc(encoderArg->buffer->data, newSize);
if (dummy == NULL) {
/* I really want to return an error code here */
PORT_Assert(0);
return;
}
encoderArg->buffer->data = dummy;
encoderArg->allocatedLen = newSize;
}
cursor = &(encoderArg->buffer->data[encoderArg->buffer->len]);
if (len) {
PORT_Memcpy(cursor, buf, len);
}
encoderArg->buffer->len += len;
}
static SECStatus
crmf_encode_certreq(CRMFCertRequest *inCertReq, SECItem *derDest)
{
struct crmfEncoderArg encoderArg;
SECStatus rv;
rv = crmf_init_encoder_callback_arg(&encoderArg, derDest);
if (rv != SECSuccess) {
return SECFailure;
}
return SEC_ASN1Encode(inCertReq, CRMFCertRequestTemplate,
crmf_generic_encoder_callback, &encoderArg);
}
static SECStatus
crmf_sign_certreq(PLArenaPool *poolp,
CRMFPOPOSigningKey *crmfSignKey,
CRMFCertRequest *certReq,
SECKEYPrivateKey *inKey,
SECAlgorithmID *inAlgId)
{
SECItem derCertReq = { siBuffer, NULL, 0 };
SECItem certReqSig = { siBuffer, NULL, 0 };
SECStatus rv = SECSuccess;
rv = crmf_encode_certreq(certReq, &derCertReq);
if (rv != SECSuccess) {
goto loser;
}
rv = SEC_SignData(&certReqSig, derCertReq.data, derCertReq.len,
inKey, SECOID_GetAlgorithmTag(inAlgId));
if (rv != SECSuccess) {
goto loser;
}
/* Now make it a part of the POPOSigningKey */
rv = SECITEM_CopyItem(poolp, &(crmfSignKey->signature), &certReqSig);
/* Convert this length to number of bits */
crmfSignKey->signature.len <<= 3;
loser:
if (derCertReq.data != NULL) {
PORT_Free(derCertReq.data);
}
if (certReqSig.data != NULL) {
PORT_Free(certReqSig.data);
}
return rv;
}
static SECStatus
crmf_create_poposignkey(PLArenaPool *poolp,
CRMFCertReqMsg *inCertReqMsg,
CRMFPOPOSigningKeyInput *signKeyInput,
SECKEYPrivateKey *inPrivKey,
SECAlgorithmID *inAlgID,
CRMFPOPOSigningKey *signKey)
{
CRMFCertRequest *certReq;
void *mark;
PRBool useSignKeyInput;
SECStatus rv;
PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL);
mark = PORT_ArenaMark(poolp);
if (signKey == NULL) {
goto loser;
}
certReq = inCertReqMsg->certReq;
useSignKeyInput = !(CRMF_DoesRequestHaveField(certReq, crmfSubject) &&
CRMF_DoesRequestHaveField(certReq, crmfPublicKey));
if (useSignKeyInput) {
goto loser;
} else {
rv = crmf_sign_certreq(poolp, signKey, certReq, inPrivKey, inAlgID);
if (rv != SECSuccess) {
goto loser;
}
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
SECStatus
CRMF_CertReqMsgSetSignaturePOP(CRMFCertReqMsg *inCertReqMsg,
SECKEYPrivateKey *inPrivKey,
SECKEYPublicKey *inPubKey,
CERTCertificate *inCertForInput,
CRMFMACPasswordCallback fn,
void *arg)
{
SECAlgorithmID *algID;
PLArenaPool *poolp;
SECItem derTemp = { siBuffer, NULL, 0 };
void *mark;
SECStatus rv;
CRMFPOPOSigningKeyInput *signKeyInput = NULL;
CRMFCertRequest *certReq;
CRMFProofOfPossession *pop;
struct crmfEncoderArg encoderArg;
PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL &&
inCertReqMsg->pop == NULL);
certReq = inCertReqMsg->certReq;
if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice ||
!CRMF_DoesRequestHaveField(certReq, crmfPublicKey)) {
return SECFailure;
}
poolp = inCertReqMsg->poolp;
mark = PORT_ArenaMark(poolp);
algID = crmf_create_poposignkey_algid(poolp, inPubKey);
if (!CRMF_DoesRequestHaveField(certReq, crmfSubject)) {
signKeyInput = crmf_create_poposigningkeyinput(poolp, inCertForInput,
fn, arg);
if (signKeyInput == NULL) {
goto loser;
}
}
pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
if (pop == NULL) {
goto loser;
}
rv = crmf_create_poposignkey(poolp, inCertReqMsg,
signKeyInput, inPrivKey, algID,
&(pop->popChoice.signature));
if (rv != SECSuccess) {
goto loser;
}
pop->popUsed = crmfSignature;
pop->popChoice.signature.algorithmIdentifier = algID;
inCertReqMsg->pop = pop;
rv = crmf_init_encoder_callback_arg(&encoderArg, &derTemp);
if (rv != SECSuccess) {
goto loser;
}
rv = SEC_ASN1Encode(&pop->popChoice.signature,
CRMFPOPOSigningKeyTemplate,
crmf_generic_encoder_callback, &encoderArg);
if (rv != SECSuccess) {
goto loser;
}
rv = SECITEM_CopyItem(poolp, &(inCertReqMsg->derPOP), &derTemp);
if (rv != SECSuccess) {
goto loser;
}
PORT_Free(derTemp.data);
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
if (derTemp.data != NULL) {
PORT_Free(derTemp.data);
}
return SECFailure;
}
static const SEC_ASN1Template *
crmf_get_popoprivkey_subtemplate(CRMFPOPOPrivKey *inPrivKey)
{
const SEC_ASN1Template *retTemplate = NULL;
switch (inPrivKey->messageChoice) {
case crmfThisMessage:
retTemplate = CRMFThisMessageTemplate;
break;
case crmfSubsequentMessage:
retTemplate = CRMFSubsequentMessageTemplate;
break;
case crmfDHMAC:
retTemplate = CRMFDHMACTemplate;
break;
default:
retTemplate = NULL;
}
return retTemplate;
}
static SECStatus
crmf_encode_popoprivkey(PLArenaPool *poolp,
CRMFCertReqMsg *inCertReqMsg,
CRMFPOPOPrivKey *popoPrivKey,
const SEC_ASN1Template *privKeyTemplate)
{
struct crmfEncoderArg encoderArg;
SECItem derTemp = { siBuffer, NULL, 0 };
SECStatus rv;
void *mark;
const SEC_ASN1Template *subDerTemplate;
mark = PORT_ArenaMark(poolp);
rv = crmf_init_encoder_callback_arg(&encoderArg, &derTemp);
if (rv != SECSuccess) {
goto loser;
}
subDerTemplate = crmf_get_popoprivkey_subtemplate(popoPrivKey);
/* We've got a union, so a pointer to one item is a pointer to
* all the items in the union.
*/
rv = SEC_ASN1Encode(&popoPrivKey->message.thisMessage,
subDerTemplate,
crmf_generic_encoder_callback, &encoderArg);
if (rv != SECSuccess) {
goto loser;
}
if (encoderArg.allocatedLen > derTemp.len + 2) {
void *dummy = PORT_Realloc(derTemp.data, derTemp.len + 2);
if (dummy == NULL) {
goto loser;
}
derTemp.data = dummy;
}
PORT_Memmove(&derTemp.data[2], &derTemp.data[0], derTemp.len);
/* I couldn't figure out how to get the ASN1 encoder to implicitly
* tag an implicitly tagged der blob. So I'm putting in the outter-
* most tag myself. -javi
*/
derTemp.data[0] = (unsigned char)privKeyTemplate->kind;
derTemp.data[1] = (unsigned char)derTemp.len;
derTemp.len += 2;
rv = SECITEM_CopyItem(poolp, &inCertReqMsg->derPOP, &derTemp);
if (rv != SECSuccess) {
goto loser;
}
PORT_Free(derTemp.data);
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
if (derTemp.data) {
PORT_Free(derTemp.data);
}
return SECFailure;
}
static const SEC_ASN1Template *
crmf_get_template_for_privkey(CRMFPOPChoice inChoice)
{
switch (inChoice) {
case crmfKeyAgreement:
return CRMFPOPOKeyAgreementTemplate;
case crmfKeyEncipherment:
return CRMFPOPOKeyEnciphermentTemplate;
default:
break;
}
return NULL;
}
static SECStatus
crmf_add_privkey_thismessage(CRMFCertReqMsg *inCertReqMsg, SECItem *encPrivKey,
CRMFPOPChoice inChoice)
{
PLArenaPool *poolp;
void *mark;
CRMFPOPOPrivKey *popoPrivKey;
CRMFProofOfPossession *pop;
SECStatus rv;
PORT_Assert(inCertReqMsg != NULL && encPrivKey != NULL);
poolp = inCertReqMsg->poolp;
mark = PORT_ArenaMark(poolp);
pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
if (pop == NULL) {
goto loser;
}
pop->popUsed = inChoice;
/* popChoice is a union, so getting a pointer to one
* field gives me a pointer to the other fields as
* well. This in essence points to both
* pop->popChoice.keyEncipherment and
* pop->popChoice.keyAgreement
*/
popoPrivKey = &pop->popChoice.keyEncipherment;
rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.thisMessage),
encPrivKey);
if (rv != SECSuccess) {
goto loser;
}
popoPrivKey->message.thisMessage.len <<= 3;
popoPrivKey->messageChoice = crmfThisMessage;
inCertReqMsg->pop = pop;
rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
crmf_get_template_for_privkey(inChoice));
if (rv != SECSuccess) {
goto loser;
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
static SECStatus
crmf_add_privkey_dhmac(CRMFCertReqMsg *inCertReqMsg, SECItem *dhmac,
CRMFPOPChoice inChoice)
{
PLArenaPool *poolp;
void *mark;
CRMFPOPOPrivKey *popoPrivKey;
CRMFProofOfPossession *pop;
SECStatus rv;
PORT_Assert(inCertReqMsg != NULL && dhmac != NULL);
poolp = inCertReqMsg->poolp;
mark = PORT_ArenaMark(poolp);
pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
if (pop == NULL) {
goto loser;
}
pop->popUsed = inChoice;
popoPrivKey = &pop->popChoice.keyAgreement;
rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.dhMAC),
dhmac);
if (rv != SECSuccess) {
goto loser;
}
popoPrivKey->message.dhMAC.len <<= 3;
popoPrivKey->messageChoice = crmfDHMAC;
inCertReqMsg->pop = pop;
rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
crmf_get_template_for_privkey(inChoice));
if (rv != SECSuccess) {
goto loser;
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
static SECStatus
crmf_add_privkey_subseqmessage(CRMFCertReqMsg *inCertReqMsg,
CRMFSubseqMessOptions subsequentMessage,
CRMFPOPChoice inChoice)
{
void *mark;
PLArenaPool *poolp;
CRMFProofOfPossession *pop;
CRMFPOPOPrivKey *popoPrivKey;
SECStatus rv;
const SEC_ASN1Template *privKeyTemplate;
if (subsequentMessage == crmfNoSubseqMess) {
return SECFailure;
}
poolp = inCertReqMsg->poolp;
mark = PORT_ArenaMark(poolp);
pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
if (pop == NULL) {
goto loser;
}
pop->popUsed = inChoice;
/*
* We have a union, so a pointer to one member of the union
* is also a member to another member of that same union.
*/
popoPrivKey = &pop->popChoice.keyEncipherment;
switch (subsequentMessage) {
case crmfEncrCert:
rv = crmf_encode_integer(poolp,
&(popoPrivKey->message.subsequentMessage),
0);
break;
case crmfChallengeResp:
rv = crmf_encode_integer(poolp,
&(popoPrivKey->message.subsequentMessage),
1);
break;
default:
goto loser;
}
if (rv != SECSuccess) {
goto loser;
}
popoPrivKey->messageChoice = crmfSubsequentMessage;
privKeyTemplate = crmf_get_template_for_privkey(inChoice);
inCertReqMsg->pop = pop;
rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
privKeyTemplate);
if (rv != SECSuccess) {
goto loser;
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
SECStatus
CRMF_CertReqMsgSetKeyEnciphermentPOP(CRMFCertReqMsg *inCertReqMsg,
CRMFPOPOPrivKeyChoice inKeyChoice,
CRMFSubseqMessOptions subseqMess,
SECItem *encPrivKey)
{
SECStatus rv;
PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) {
return SECFailure;
}
switch (inKeyChoice) {
case crmfThisMessage:
rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey,
crmfKeyEncipherment);
break;
case crmfSubsequentMessage:
rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess,
crmfKeyEncipherment);
break;
case crmfDHMAC:
default:
rv = SECFailure;
}
return rv;
}
SECStatus
CRMF_CertReqMsgSetKeyAgreementPOP(CRMFCertReqMsg *inCertReqMsg,
CRMFPOPOPrivKeyChoice inKeyChoice,
CRMFSubseqMessOptions subseqMess,
SECItem *encPrivKey)
{
SECStatus rv;
PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
switch (inKeyChoice) {
case crmfThisMessage:
rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey,
crmfKeyAgreement);
break;
case crmfSubsequentMessage:
rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess,
crmfKeyAgreement);
break;
case crmfDHMAC:
/* In this case encPrivKey should be the calculated dhMac
* as specified in RFC 2511 */
rv = crmf_add_privkey_dhmac(inCertReqMsg, encPrivKey,
crmfKeyAgreement);
break;
default:
rv = SECFailure;
}
return rv;
}