summaryrefslogtreecommitdiff
path: root/security/nss/lib/crmf/crmfpop.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/crmf/crmfpop.c')
-rw-r--r--security/nss/lib/crmf/crmfpop.c605
1 files changed, 605 insertions, 0 deletions
diff --git a/security/nss/lib/crmf/crmfpop.c b/security/nss/lib/crmf/crmfpop.c
new file mode 100644
index 000000000..83cc43369
--- /dev/null
+++ b/security/nss/lib/crmf/crmfpop.c
@@ -0,0 +1,605 @@
+/* -*- Mode: C; tab-width: 8 -*-*/
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+
+#include "crmf.h"
+#include "crmfi.h"
+#include "secasn1.h"
+#include "keyhi.h"
+#include "cryptohi.h"
+
+#define CRMF_DEFAULT_ALLOC_SIZE 1024
+
+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)
+{
+ SECItem *dummy;
+ CRMFProofOfPossession *pop;
+ PRArenaPool *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;
+ dummy = SEC_ASN1EncodeItem(poolp, &(inCertReqMsg->derPOP),
+ &(pop->popChoice.raVerified),
+ CRMFRAVerifiedTemplate);
+ return SECSuccess;
+ loser:
+ PORT_ArenaRelease(poolp, mark);
+ return SECFailure;
+}
+
+SECOidTag
+crmf_map_keytag_to_signtag(SECOidTag inTag)
+{
+ switch (inTag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_DSS:
+ return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
+ default:
+ /* Put this in here to kill warnings. */
+ break;
+ }
+ return inTag;
+}
+
+SECOidTag
+crmf_get_key_sign_tag(SECKEYPublicKey *inPubKey)
+{
+ CERTSubjectPublicKeyInfo *spki;
+ SECOidTag tag;
+
+ spki = SECKEY_CreateSubjectPublicKeyInfo(inPubKey);
+ if (spki == NULL) {
+ return SEC_OID_UNKNOWN;
+ }
+ tag = SECOID_GetAlgorithmTag(&spki->algorithm);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ return crmf_map_keytag_to_signtag(tag);
+}
+
+SECAlgorithmID*
+crmf_create_poposignkey_algid(PRArenaPool *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(PRArenaPool *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]);
+ 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(PRArenaPool *poolp,
+ CRMFPOPOSigningKey *crmfSignKey,
+ CRMFCertRequest *certReq,
+ SECKEYPrivateKey *inKey,
+ SECAlgorithmID *inAlgId)
+{
+ SECItem derCertReq;
+ SECItem certReqSig;
+ 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(PRArenaPool *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;
+ PRArenaPool *poolp;
+ SECItem derDest = {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, &derDest);
+ 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), &derDest);
+ PORT_Free (derDest.data);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ PORT_ArenaUnmark(poolp,mark);
+ return SECSuccess;
+
+ loser:
+ PORT_ArenaRelease(poolp,mark);
+ if (derDest.data != NULL) {
+ PORT_Free(derDest.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(PRArenaPool *poolp,
+ CRMFCertReqMsg *inCertReqMsg,
+ CRMFPOPOPrivKey *popoPrivKey,
+ const SEC_ASN1Template *privKeyTemplate)
+{
+ struct crmfEncoderArg encoderArg;
+ SECItem derDest;
+ SECStatus rv;
+ void *mark;
+ const SEC_ASN1Template *subDerTemplate;
+
+ mark = PORT_ArenaMark(poolp);
+ rv = crmf_init_encoder_callback_arg(&encoderArg, &derDest);
+ 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 > derDest.len+2) {
+ void *dummy = PORT_Realloc(derDest.data, derDest.len+2);
+ if (dummy == NULL) {
+ goto loser;
+ }
+ derDest.data = dummy;
+ }
+ PORT_Memmove(&derDest.data[2], &derDest.data[0], derDest.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
+ */
+ derDest.data[0] = (unsigned char)privKeyTemplate->kind;
+ derDest.data[1] = (unsigned char)derDest.len;
+ derDest.len += 2;
+ rv = SECITEM_CopyItem(poolp, &inCertReqMsg->derPOP, &derDest);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ PORT_Free(derDest.data);
+ PORT_ArenaUnmark(poolp, mark);
+ return SECSuccess;
+ loser:
+ PORT_ArenaRelease(poolp, mark);
+ if (derDest.data) {
+ PORT_Free(derDest.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)
+{
+ PRArenaPool *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_subseqmessage(CRMFCertReqMsg *inCertReqMsg,
+ CRMFSubseqMessOptions subsequentMessage,
+ CRMFPOPChoice inChoice)
+{
+ void *mark;
+ PRArenaPool *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:
+ /* This case should be added in the future. */
+ default:
+ rv = SECFailure;
+ }
+ return rv;
+}
+