| /* -*- 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 "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) |
| { |
| PORT_Assert(inCertReqMsg->poolp); |
| if (!inCertReqMsg->poolp) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| 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.type = derPOP->type; |
| privKeyDer.data = &derPOP->data[5]; |
| privKeyDer.len = derPOP->len - 5; |
| break; |
| case crmfSubsequentMessage: |
| privKeyDer.type = derPOP->type; |
| privKeyDer.data = &derPOP->data[4]; |
| privKeyDer.len = derPOP->len - 4; |
| break; |
| default: |
| return 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; |
| PLArenaPool *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(PLArenaPool *poolp, |
| CRMFControl *inControl) |
| { |
| const SEC_ASN1Template *asn1Template = NULL; |
| |
| inControl->tag = SECOID_FindOIDTag(&inControl->derTag); |
| asn1Template = crmf_get_pkiarchiveoptions_subtemplate(inControl); |
| |
| PORT_Assert(asn1Template != NULL); |
| PORT_Assert(poolp != NULL); |
| if (!asn1Template || !poolp) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| /* 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; |
| PLArenaPool *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) |
| { |
| PLArenaPool *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; |
| PLArenaPool *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 + len / 2; |
| 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; |
| } |