/* -*- 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 "secitem.h" static CRMFPOPChoice crmf_get_popchoice_from_der(SECItem *derPOP) { CRMFPOPChoice retChoice; switch (derPOP->data[0] & 0x0f) { case 0: retChoice = crmfRAVerified; break; case 1: retChoice = crmfSignature; break; case 2: retChoice = crmfKeyEncipherment; break; case 3: retChoice = crmfKeyAgreement; break; default: retChoice = crmfNoPOPChoice; break; } return retChoice; } static SECStatus crmf_decode_process_raverified(CRMFCertReqMsg *inCertReqMsg) { CRMFProofOfPossession *pop; /* Just set up the structure so that the message structure * looks like one that was created using the API */ pop = inCertReqMsg->pop; pop->popChoice.raVerified.data = NULL; pop->popChoice.raVerified.len = 0; return SECSuccess; } static SECStatus crmf_decode_process_signature(CRMFCertReqMsg *inCertReqMsg) { return SEC_ASN1Decode(inCertReqMsg->poolp, &inCertReqMsg->pop->popChoice.signature, CRMFPOPOSigningKeyTemplate, (const char*)inCertReqMsg->derPOP.data, inCertReqMsg->derPOP.len); } static CRMFPOPOPrivKeyChoice crmf_get_messagechoice_from_der(SECItem *derPOP) { CRMFPOPOPrivKeyChoice retChoice; switch (derPOP->data[2] & 0x0f) { case 0: retChoice = crmfThisMessage; break; case 1: retChoice = crmfSubsequentMessage; break; case 2: retChoice = crmfDHMAC; break; default: retChoice = crmfNoMessage; } return retChoice; } static SECStatus crmf_decode_process_popoprivkey(CRMFCertReqMsg *inCertReqMsg) { /* We've got a union, so a pointer to one POPOPrivKey * struct is the same as having a pointer to the other * one. */ CRMFPOPOPrivKey *popoPrivKey = &inCertReqMsg->pop->popChoice.keyEncipherment; SECItem *derPOP, privKeyDer; SECStatus rv; derPOP = &inCertReqMsg->derPOP; popoPrivKey->messageChoice = crmf_get_messagechoice_from_der(derPOP); if (popoPrivKey->messageChoice == crmfNoMessage) { return SECFailure; } /* If we ever encounter BER encodings of this, we'll get in trouble*/ switch (popoPrivKey->messageChoice) { case crmfThisMessage: case crmfDHMAC: privKeyDer.data = &derPOP->data[5]; privKeyDer.len = derPOP->len - 5; break; case crmfSubsequentMessage: privKeyDer.data = &derPOP->data[4]; privKeyDer.len = derPOP->len - 4; break; default: rv = SECFailure; } rv = SECITEM_CopyItem(inCertReqMsg->poolp, &popoPrivKey->message.subsequentMessage, &privKeyDer); if (rv != SECSuccess) { return rv; } if (popoPrivKey->messageChoice == crmfThisMessage || popoPrivKey->messageChoice == crmfDHMAC) { popoPrivKey->message.thisMessage.len = CRMF_BYTES_TO_BITS(privKeyDer.len) - (int)derPOP->data[4]; } return SECSuccess; } static SECStatus crmf_decode_process_keyagreement(CRMFCertReqMsg *inCertReqMsg) { return crmf_decode_process_popoprivkey(inCertReqMsg); } static SECStatus crmf_decode_process_keyencipherment(CRMFCertReqMsg *inCertReqMsg) { SECStatus rv; rv = crmf_decode_process_popoprivkey(inCertReqMsg); if (rv != SECSuccess) { return rv; } if (inCertReqMsg->pop->popChoice.keyEncipherment.messageChoice == crmfDHMAC) { /* Key Encipherment can not use the dhMAC option for * POPOPrivKey. */ return SECFailure; } return SECSuccess; } static SECStatus crmf_decode_process_pop(CRMFCertReqMsg *inCertReqMsg) { SECItem *derPOP; PRArenaPool *poolp; CRMFProofOfPossession *pop; void *mark; SECStatus rv; derPOP = &inCertReqMsg->derPOP; poolp = inCertReqMsg->poolp; if (derPOP->data == NULL) { /* There is no Proof of Possession field in this message. */ return SECSuccess; } mark = PORT_ArenaMark(poolp); pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession); if (pop == NULL) { goto loser; } pop->popUsed = crmf_get_popchoice_from_der(derPOP); if (pop->popUsed == crmfNoPOPChoice) { /* A bad encoding of CRMF. Not a valid tag was given to the * Proof Of Possession field. */ goto loser; } inCertReqMsg->pop = pop; switch (pop->popUsed) { case crmfRAVerified: rv = crmf_decode_process_raverified(inCertReqMsg); break; case crmfSignature: rv = crmf_decode_process_signature(inCertReqMsg); break; case crmfKeyEncipherment: rv = crmf_decode_process_keyencipherment(inCertReqMsg); break; case crmfKeyAgreement: rv = crmf_decode_process_keyagreement(inCertReqMsg); break; default: rv = SECFailure; } if (rv != SECSuccess) { goto loser; } PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: PORT_ArenaRelease(poolp, mark); inCertReqMsg->pop = NULL; return SECFailure; } static SECStatus crmf_decode_process_single_control(PRArenaPool *poolp, CRMFControl *inControl) { const SEC_ASN1Template *asn1Template = NULL; inControl->tag = SECOID_FindOIDTag(&inControl->derTag); asn1Template = crmf_get_pkiarchiveoptions_subtemplate(inControl); PORT_Assert (asn1Template != NULL); /* We've got a union, so passing a pointer to one element of the * union is the same as passing a pointer to any of the other * members of the union. */ return SEC_ASN1Decode(poolp, &inControl->value.archiveOptions, asn1Template, (const char*)inControl->derValue.data, inControl->derValue.len); } static SECStatus crmf_decode_process_controls(CRMFCertReqMsg *inCertReqMsg) { int i, numControls; SECStatus rv; PRArenaPool *poolp; CRMFControl **controls; numControls = CRMF_CertRequestGetNumControls(inCertReqMsg->certReq); controls = inCertReqMsg->certReq->controls; poolp = inCertReqMsg->poolp; for (i=0; i < numControls; i++) { rv = crmf_decode_process_single_control(poolp, controls[i]); if (rv != SECSuccess) { return SECFailure; } } return SECSuccess; } static SECStatus crmf_decode_process_single_reqmsg(CRMFCertReqMsg *inCertReqMsg) { SECStatus rv; rv = crmf_decode_process_pop(inCertReqMsg); if (rv != SECSuccess) { goto loser; } rv = crmf_decode_process_controls(inCertReqMsg); if (rv != SECSuccess) { goto loser; } inCertReqMsg->certReq->certTemplate.numExtensions = CRMF_CertRequestGetNumberOfExtensions(inCertReqMsg->certReq); inCertReqMsg->isDecoded = PR_TRUE; rv = SECSuccess; loser: return rv; } CRMFCertReqMsg* CRMF_CreateCertReqMsgFromDER (const char * buf, long len) { PRArenaPool *poolp; CRMFCertReqMsg *certReqMsg; SECStatus rv; poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); if (poolp == NULL) { goto loser; } certReqMsg = PORT_ArenaZNew (poolp, CRMFCertReqMsg); if (certReqMsg == NULL) { goto loser; } certReqMsg->poolp = poolp; rv = SEC_ASN1Decode(poolp, certReqMsg, CRMFCertReqMsgTemplate, buf, len); if (rv != SECSuccess) { goto loser; } rv = crmf_decode_process_single_reqmsg(certReqMsg); if (rv != SECSuccess) { goto loser; } return certReqMsg; loser: if (poolp != NULL) { PORT_FreeArena(poolp, PR_FALSE); } return NULL; } CRMFCertReqMessages* CRMF_CreateCertReqMessagesFromDER(const char *buf, long len) { long arenaSize; int i; SECStatus rv; PRArenaPool *poolp; CRMFCertReqMessages *certReqMsgs; PORT_Assert (buf != NULL); /* Wanna make sure the arena is big enough to store all of the requests * coming in. We'll guestimate according to the length of the buffer. */ arenaSize = len * 1.5; poolp = PORT_NewArena(arenaSize); if (poolp == NULL) { return NULL; } certReqMsgs = PORT_ArenaZNew(poolp, CRMFCertReqMessages); if (certReqMsgs == NULL) { goto loser; } certReqMsgs->poolp = poolp; rv = SEC_ASN1Decode(poolp, certReqMsgs, CRMFCertReqMessagesTemplate, buf, len); if (rv != SECSuccess) { goto loser; } for (i=0; certReqMsgs->messages[i] != NULL; i++) { /* The sub-routines expect the individual messages to have * an arena. We'll give them one temporarily. */ certReqMsgs->messages[i]->poolp = poolp; rv = crmf_decode_process_single_reqmsg(certReqMsgs->messages[i]); if (rv != SECSuccess) { goto loser; } certReqMsgs->messages[i]->poolp = NULL; } return certReqMsgs; loser: PORT_FreeArena(poolp, PR_FALSE); return NULL; }