/* -*- 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 "cmmf.h"
#include "cmmfi.h"
#include "secitem.h"
#include "keyhi.h"
#include "secder.h"

CRMFEncryptedKeyChoice
CRMF_EncryptedKeyGetChoice(CRMFEncryptedKey *inEncrKey)
{
    PORT_Assert(inEncrKey != NULL);
    if (inEncrKey == NULL) {
        return crmfNoEncryptedKeyChoice;
    }
    return inEncrKey->encKeyChoice;
}

CRMFEncryptedValue *
CRMF_EncryptedKeyGetEncryptedValue(CRMFEncryptedKey *inEncrKey)
{
    CRMFEncryptedValue *newEncrValue = NULL;
    SECStatus rv;

    PORT_Assert(inEncrKey != NULL);
    if (inEncrKey == NULL ||
        CRMF_EncryptedKeyGetChoice(inEncrKey) != crmfEncryptedValueChoice) {
        goto loser;
    }
    newEncrValue = PORT_ZNew(CRMFEncryptedValue);
    if (newEncrValue == NULL) {
        goto loser;
    }
    rv = crmf_copy_encryptedvalue(NULL, &inEncrKey->value.encryptedValue,
                                  newEncrValue);
    if (rv != SECSuccess) {
        goto loser;
    }
    return newEncrValue;
loser:
    if (newEncrValue != NULL) {
        CRMF_DestroyEncryptedValue(newEncrValue);
    }
    return NULL;
}

static SECItem *
crmf_get_encvalue_bitstring(SECItem *srcItem)
{
    SECItem *newItem = NULL;
    SECStatus rv;

    if (srcItem->data == NULL) {
        return NULL;
    }
    newItem = PORT_ZNew(SECItem);
    if (newItem == NULL) {
        goto loser;
    }
    rv = crmf_make_bitstring_copy(NULL, newItem, srcItem);
    if (rv != SECSuccess) {
        goto loser;
    }
    return newItem;
loser:
    if (newItem != NULL) {
        SECITEM_FreeItem(newItem, PR_TRUE);
    }
    return NULL;
}

SECItem *
CRMF_EncryptedValueGetEncSymmKey(CRMFEncryptedValue *inEncValue)
{
    if (inEncValue == NULL) {
        return NULL;
    }
    return crmf_get_encvalue_bitstring(&inEncValue->encSymmKey);
}

SECItem *
CRMF_EncryptedValueGetEncValue(CRMFEncryptedValue *inEncrValue)
{
    if (inEncrValue == NULL || inEncrValue->encValue.data == NULL) {
        return NULL;
    }
    return crmf_get_encvalue_bitstring(&inEncrValue->encValue);
}

static SECAlgorithmID *
crmf_get_encvalue_algid(SECAlgorithmID *srcAlg)
{
    SECStatus rv;
    SECAlgorithmID *newAlgID;

    if (srcAlg == NULL) {
        return NULL;
    }
    rv = crmf_copy_encryptedvalue_secalg(NULL, srcAlg, &newAlgID);
    if (rv != SECSuccess) {
        return NULL;
    }
    return newAlgID;
}

SECAlgorithmID *
CRMF_EncryptedValueGetIntendedAlg(CRMFEncryptedValue *inEncValue)
{
    if (inEncValue == NULL) {
        return NULL;
    }
    return crmf_get_encvalue_algid(inEncValue->intendedAlg);
}

SECAlgorithmID *
CRMF_EncryptedValueGetKeyAlg(CRMFEncryptedValue *inEncValue)
{
    if (inEncValue == NULL) {
        return NULL;
    }
    return crmf_get_encvalue_algid(inEncValue->keyAlg);
}

SECAlgorithmID *
CRMF_EncryptedValueGetSymmAlg(CRMFEncryptedValue *inEncValue)
{
    if (inEncValue == NULL) {
        return NULL;
    }
    return crmf_get_encvalue_algid(inEncValue->symmAlg);
}

SECItem *
CRMF_EncryptedValueGetValueHint(CRMFEncryptedValue *inEncValue)
{
    if (inEncValue == NULL || inEncValue->valueHint.data == NULL) {
        return NULL;
    }
    return SECITEM_DupItem(&inEncValue->valueHint);
}

SECStatus
CRMF_PKIArchiveOptionsGetArchiveRemGenPrivKey(CRMFPKIArchiveOptions *inOpt,
                                              PRBool *destVal)
{
    if (inOpt == NULL || destVal == NULL ||
        CRMF_PKIArchiveOptionsGetOptionType(inOpt) != crmfArchiveRemGenPrivKey) {
        return SECFailure;
    }
    *destVal = (inOpt->option.archiveRemGenPrivKey.data[0] == hexFalse)
                   ? PR_FALSE
                   : PR_TRUE;
    return SECSuccess;
}

CRMFEncryptedKey *
CRMF_PKIArchiveOptionsGetEncryptedPrivKey(CRMFPKIArchiveOptions *inOpts)
{
    CRMFEncryptedKey *newEncrKey = NULL;
    SECStatus rv;

    PORT_Assert(inOpts != NULL);
    if (inOpts == NULL ||
        CRMF_PKIArchiveOptionsGetOptionType(inOpts) != crmfEncryptedPrivateKey) {
        return NULL;
    }
    newEncrKey = PORT_ZNew(CRMFEncryptedKey);
    if (newEncrKey == NULL) {
        goto loser;
    }
    rv = crmf_copy_encryptedkey(NULL, &inOpts->option.encryptedKey,
                                newEncrKey);
    if (rv != SECSuccess) {
        goto loser;
    }
    return newEncrKey;
loser:
    if (newEncrKey != NULL) {
        CRMF_DestroyEncryptedKey(newEncrKey);
    }
    return NULL;
}

SECItem *
CRMF_PKIArchiveOptionsGetKeyGenParameters(CRMFPKIArchiveOptions *inOptions)
{
    if (inOptions == NULL ||
        CRMF_PKIArchiveOptionsGetOptionType(inOptions) != crmfKeyGenParameters ||
        inOptions->option.keyGenParameters.data == NULL) {
        return NULL;
    }
    return SECITEM_DupItem(&inOptions->option.keyGenParameters);
}

CRMFPKIArchiveOptionsType
CRMF_PKIArchiveOptionsGetOptionType(CRMFPKIArchiveOptions *inOptions)
{
    PORT_Assert(inOptions != NULL);
    if (inOptions == NULL) {
        return crmfNoArchiveOptions;
    }
    return inOptions->archOption;
}

static SECStatus
crmf_extract_long_from_item(SECItem *intItem, long *destLong)
{
    *destLong = DER_GetInteger(intItem);
    return (*destLong == -1) ? SECFailure : SECSuccess;
}

SECStatus
CRMF_POPOPrivGetKeySubseqMess(CRMFPOPOPrivKey *inKey,
                              CRMFSubseqMessOptions *destOpt)
{
    long value;
    SECStatus rv;

    PORT_Assert(inKey != NULL);
    if (inKey == NULL ||
        inKey->messageChoice != crmfSubsequentMessage) {
        return SECFailure;
    }
    rv = crmf_extract_long_from_item(&inKey->message.subsequentMessage, &value);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    switch (value) {
        case 0:
            *destOpt = crmfEncrCert;
            break;
        case 1:
            *destOpt = crmfChallengeResp;
            break;
        default:
            rv = SECFailure;
    }
    if (rv != SECSuccess) {
        return rv;
    }
    return SECSuccess;
}

CRMFPOPOPrivKeyChoice
CRMF_POPOPrivKeyGetChoice(CRMFPOPOPrivKey *inPrivKey)
{
    PORT_Assert(inPrivKey != NULL);
    if (inPrivKey != NULL) {
        return inPrivKey->messageChoice;
    }
    return crmfNoMessage;
}

SECStatus
CRMF_POPOPrivKeyGetDHMAC(CRMFPOPOPrivKey *inKey, SECItem *destMAC)
{
    PORT_Assert(inKey != NULL);
    if (inKey == NULL || inKey->message.dhMAC.data == NULL) {
        return SECFailure;
    }
    return crmf_make_bitstring_copy(NULL, destMAC, &inKey->message.dhMAC);
}

SECStatus
CRMF_POPOPrivKeyGetThisMessage(CRMFPOPOPrivKey *inKey,
                               SECItem *destString)
{
    PORT_Assert(inKey != NULL);
    if (inKey == NULL ||
        inKey->messageChoice != crmfThisMessage) {
        return SECFailure;
    }

    return crmf_make_bitstring_copy(NULL, destString,
                                    &inKey->message.thisMessage);
}

SECAlgorithmID *
CRMF_POPOSigningKeyGetAlgID(CRMFPOPOSigningKey *inSignKey)
{
    SECAlgorithmID *newAlgId = NULL;
    SECStatus rv;

    PORT_Assert(inSignKey != NULL);
    if (inSignKey == NULL) {
        return NULL;
    }
    newAlgId = PORT_ZNew(SECAlgorithmID);
    if (newAlgId == NULL) {
        goto loser;
    }
    rv = SECOID_CopyAlgorithmID(NULL, newAlgId,
                                inSignKey->algorithmIdentifier);
    if (rv != SECSuccess) {
        goto loser;
    }
    return newAlgId;

loser:
    if (newAlgId != NULL) {
        SECOID_DestroyAlgorithmID(newAlgId, PR_TRUE);
    }
    return NULL;
}

SECItem *
CRMF_POPOSigningKeyGetInput(CRMFPOPOSigningKey *inSignKey)
{
    PORT_Assert(inSignKey != NULL);
    if (inSignKey == NULL || inSignKey->derInput.data == NULL) {
        return NULL;
    }
    return SECITEM_DupItem(&inSignKey->derInput);
}

SECItem *
CRMF_POPOSigningKeyGetSignature(CRMFPOPOSigningKey *inSignKey)
{
    SECItem *newSig = NULL;
    SECStatus rv;

    PORT_Assert(inSignKey != NULL);
    if (inSignKey == NULL) {
        return NULL;
    }
    newSig = PORT_ZNew(SECItem);
    if (newSig == NULL) {
        goto loser;
    }
    rv = crmf_make_bitstring_copy(NULL, newSig, &inSignKey->signature);
    if (rv != SECSuccess) {
        goto loser;
    }
    return newSig;
loser:
    if (newSig != NULL) {
        SECITEM_FreeItem(newSig, PR_TRUE);
    }
    return NULL;
}

static SECStatus
crmf_copy_poposigningkey(PLArenaPool *poolp,
                         CRMFPOPOSigningKey *inPopoSignKey,
                         CRMFPOPOSigningKey *destPopoSignKey)
{
    SECStatus rv;

    /* We don't support use of the POPOSigningKeyInput, so we'll only
     * store away the DER encoding.
     */
    if (inPopoSignKey->derInput.data != NULL) {
        rv = SECITEM_CopyItem(poolp, &destPopoSignKey->derInput,
                              &inPopoSignKey->derInput);
    }
    destPopoSignKey->algorithmIdentifier = (poolp == NULL) ? PORT_ZNew(SECAlgorithmID)
                                                           : PORT_ArenaZNew(poolp, SECAlgorithmID);

    if (destPopoSignKey->algorithmIdentifier == NULL) {
        goto loser;
    }
    rv = SECOID_CopyAlgorithmID(poolp, destPopoSignKey->algorithmIdentifier,
                                inPopoSignKey->algorithmIdentifier);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = crmf_make_bitstring_copy(poolp, &destPopoSignKey->signature,
                                  &inPopoSignKey->signature);
    if (rv != SECSuccess) {
        goto loser;
    }
    return SECSuccess;
loser:
    if (poolp == NULL) {
        CRMF_DestroyPOPOSigningKey(destPopoSignKey);
    }
    return SECFailure;
}

static SECStatus
crmf_copy_popoprivkey(PLArenaPool *poolp,
                      CRMFPOPOPrivKey *srcPrivKey,
                      CRMFPOPOPrivKey *destPrivKey)
{
    SECStatus rv;

    destPrivKey->messageChoice = srcPrivKey->messageChoice;
    switch (destPrivKey->messageChoice) {
        case crmfThisMessage:
        case crmfDHMAC:
            /* I've got a union, so taking the address of one, will also give
             * me a pointer to the other (eg, message.dhMAC)
             */
            rv = crmf_make_bitstring_copy(poolp, &destPrivKey->message.thisMessage,
                                          &srcPrivKey->message.thisMessage);
            break;
        case crmfSubsequentMessage:
            rv = SECITEM_CopyItem(poolp, &destPrivKey->message.subsequentMessage,
                                  &srcPrivKey->message.subsequentMessage);
            break;
        default:
            rv = SECFailure;
    }

    if (rv != SECSuccess && poolp == NULL) {
        CRMF_DestroyPOPOPrivKey(destPrivKey);
    }
    return rv;
}

static CRMFProofOfPossession *
crmf_copy_pop(PLArenaPool *poolp, CRMFProofOfPossession *srcPOP)
{
    CRMFProofOfPossession *newPOP;
    SECStatus rv;

    /*
     * Proof Of Possession structures are always part of the Request
     * message, so there will always be an arena for allocating memory.
     */
    if (poolp == NULL) {
        return NULL;
    }
    newPOP = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
    if (newPOP == NULL) {
        return NULL;
    }
    switch (srcPOP->popUsed) {
        case crmfRAVerified:
            newPOP->popChoice.raVerified.data = NULL;
            newPOP->popChoice.raVerified.len = 0;
            break;
        case crmfSignature:
            rv = crmf_copy_poposigningkey(poolp, &srcPOP->popChoice.signature,
                                          &newPOP->popChoice.signature);
            if (rv != SECSuccess) {
                goto loser;
            }
            break;
        case crmfKeyEncipherment:
        case crmfKeyAgreement:
            /* We've got a union, so a pointer to one, is a pointer to the
             * other one.
             */
            rv = crmf_copy_popoprivkey(poolp, &srcPOP->popChoice.keyEncipherment,
                                       &newPOP->popChoice.keyEncipherment);
            if (rv != SECSuccess) {
                goto loser;
            }
            break;
        default:
            goto loser;
    }
    newPOP->popUsed = srcPOP->popUsed;
    return newPOP;

loser:
    return NULL;
}

static CRMFCertReqMsg *
crmf_copy_cert_req_msg(CRMFCertReqMsg *srcReqMsg)
{
    CRMFCertReqMsg *newReqMsg;
    PLArenaPool *poolp;

    poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
    if (poolp == NULL) {
        return NULL;
    }
    newReqMsg = PORT_ArenaZNew(poolp, CRMFCertReqMsg);
    if (newReqMsg == NULL) {
        PORT_FreeArena(poolp, PR_TRUE);
        return NULL;
    }

    newReqMsg->poolp = poolp;
    newReqMsg->certReq = crmf_copy_cert_request(poolp, srcReqMsg->certReq);
    if (newReqMsg->certReq == NULL) {
        goto loser;
    }
    newReqMsg->pop = crmf_copy_pop(poolp, srcReqMsg->pop);
    if (newReqMsg->pop == NULL) {
        goto loser;
    }
    /* None of my set/get routines operate on the regInfo field, so
     * for now, that won't get copied over.
     */
    return newReqMsg;

loser:
    if (newReqMsg != NULL) {
        CRMF_DestroyCertReqMsg(newReqMsg);
    }
    return NULL;
}

CRMFCertReqMsg *
CRMF_CertReqMessagesGetCertReqMsgAtIndex(CRMFCertReqMessages *inReqMsgs,
                                         int index)
{
    int numMsgs;

    PORT_Assert(inReqMsgs != NULL && index >= 0);
    if (inReqMsgs == NULL) {
        return NULL;
    }
    numMsgs = CRMF_CertReqMessagesGetNumMessages(inReqMsgs);
    if (index < 0 || index >= numMsgs) {
        return NULL;
    }
    return crmf_copy_cert_req_msg(inReqMsgs->messages[index]);
}

int
CRMF_CertReqMessagesGetNumMessages(CRMFCertReqMessages *inCertReqMsgs)
{
    int numMessages = 0;

    PORT_Assert(inCertReqMsgs != NULL);
    if (inCertReqMsgs == NULL) {
        return 0;
    }
    while (inCertReqMsgs->messages[numMessages] != NULL) {
        numMessages++;
    }
    return numMessages;
}

CRMFCertRequest *
CRMF_CertReqMsgGetCertRequest(CRMFCertReqMsg *inCertReqMsg)
{
    PLArenaPool *poolp = NULL;
    CRMFCertRequest *newCertReq = NULL;

    PORT_Assert(inCertReqMsg != NULL);

    poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
    if (poolp == NULL) {
        goto loser;
    }
    newCertReq = crmf_copy_cert_request(poolp, inCertReqMsg->certReq);
    if (newCertReq == NULL) {
        goto loser;
    }
    newCertReq->poolp = poolp;
    return newCertReq;
loser:
    if (poolp != NULL) {
        PORT_FreeArena(poolp, PR_FALSE);
    }
    return NULL;
}

SECStatus
CRMF_CertReqMsgGetID(CRMFCertReqMsg *inCertReqMsg, long *destID)
{
    PORT_Assert(inCertReqMsg != NULL && destID != NULL);
    if (inCertReqMsg == NULL || inCertReqMsg->certReq == NULL) {
        return SECFailure;
    }
    return crmf_extract_long_from_item(&inCertReqMsg->certReq->certReqId,
                                       destID);
}

SECStatus
CRMF_CertReqMsgGetPOPKeyAgreement(CRMFCertReqMsg *inCertReqMsg,
                                  CRMFPOPOPrivKey **destKey)
{
    PORT_Assert(inCertReqMsg != NULL && destKey != NULL);
    if (inCertReqMsg == NULL || destKey == NULL ||
        CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfKeyAgreement) {
        return SECFailure;
    }
    *destKey = PORT_ZNew(CRMFPOPOPrivKey);
    if (*destKey == NULL) {
        return SECFailure;
    }
    return crmf_copy_popoprivkey(NULL,
                                 &inCertReqMsg->pop->popChoice.keyAgreement,
                                 *destKey);
}

SECStatus
CRMF_CertReqMsgGetPOPKeyEncipherment(CRMFCertReqMsg *inCertReqMsg,
                                     CRMFPOPOPrivKey **destKey)
{
    PORT_Assert(inCertReqMsg != NULL && destKey != NULL);
    if (inCertReqMsg == NULL || destKey == NULL ||
        CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfKeyEncipherment) {
        return SECFailure;
    }
    *destKey = PORT_ZNew(CRMFPOPOPrivKey);
    if (*destKey == NULL) {
        return SECFailure;
    }
    return crmf_copy_popoprivkey(NULL,
                                 &inCertReqMsg->pop->popChoice.keyEncipherment,
                                 *destKey);
}

SECStatus
CRMF_CertReqMsgGetPOPOSigningKey(CRMFCertReqMsg *inCertReqMsg,
                                 CRMFPOPOSigningKey **destKey)
{
    CRMFProofOfPossession *pop;
    PORT_Assert(inCertReqMsg != NULL);
    if (inCertReqMsg == NULL) {
        return SECFailure;
    }
    pop = inCertReqMsg->pop;
    ;
    if (pop->popUsed != crmfSignature) {
        return SECFailure;
    }
    *destKey = PORT_ZNew(CRMFPOPOSigningKey);
    if (*destKey == NULL) {
        return SECFailure;
    }
    return crmf_copy_poposigningkey(NULL, &pop->popChoice.signature, *destKey);
}

static SECStatus
crmf_copy_name(CERTName *destName, CERTName *srcName)
{
    PLArenaPool *poolp = NULL;
    SECStatus rv;

    if (destName->arena != NULL) {
        poolp = destName->arena;
    } else {
        poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
    }
    if (poolp == NULL) {
        return SECFailure;
    }
    /* Need to do this so that CERT_CopyName doesn't free out
     * the arena from underneath us.
     */
    destName->arena = NULL;
    rv = CERT_CopyName(poolp, destName, srcName);
    destName->arena = poolp;
    return rv;
}

SECStatus
CRMF_CertRequestGetCertTemplateIssuer(CRMFCertRequest *inCertReq,
                                      CERTName *destIssuer)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfIssuer)) {
        return crmf_copy_name(destIssuer,
                              inCertReq->certTemplate.issuer);
    }
    return SECFailure;
}

SECStatus
CRMF_CertRequestGetCertTemplateIssuerUID(CRMFCertRequest *inCertReq,
                                         SECItem *destIssuerUID)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfIssuerUID)) {
        return crmf_make_bitstring_copy(NULL, destIssuerUID,
                                        &inCertReq->certTemplate.issuerUID);
    }
    return SECFailure;
}

SECStatus
CRMF_CertRequestGetCertTemplatePublicKey(CRMFCertRequest *inCertReq,
                                         CERTSubjectPublicKeyInfo *destPublicKey)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfPublicKey)) {
        return SECKEY_CopySubjectPublicKeyInfo(NULL, destPublicKey,
                                               inCertReq->certTemplate.publicKey);
    }
    return SECFailure;
}

SECStatus
CRMF_CertRequestGetCertTemplateSerialNumber(CRMFCertRequest *inCertReq,
                                            long *serialNumber)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfSerialNumber)) {
        return crmf_extract_long_from_item(&inCertReq->certTemplate.serialNumber,
                                           serialNumber);
    }
    return SECFailure;
}

SECStatus
CRMF_CertRequestGetCertTemplateSigningAlg(CRMFCertRequest *inCertReq,
                                          SECAlgorithmID *destAlg)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfSigningAlg)) {
        return SECOID_CopyAlgorithmID(NULL, destAlg,
                                      inCertReq->certTemplate.signingAlg);
    }
    return SECFailure;
}

SECStatus
CRMF_CertRequestGetCertTemplateSubject(CRMFCertRequest *inCertReq,
                                       CERTName *destSubject)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfSubject)) {
        return crmf_copy_name(destSubject, inCertReq->certTemplate.subject);
    }
    return SECFailure;
}

SECStatus
CRMF_CertRequestGetCertTemplateSubjectUID(CRMFCertRequest *inCertReq,
                                          SECItem *destSubjectUID)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfSubjectUID)) {
        return crmf_make_bitstring_copy(NULL, destSubjectUID,
                                        &inCertReq->certTemplate.subjectUID);
    }
    return SECFailure;
}

SECStatus
CRMF_CertRequestGetCertTemplateVersion(CRMFCertRequest *inCertReq,
                                       long *version)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfVersion)) {
        return crmf_extract_long_from_item(&inCertReq->certTemplate.version,
                                           version);
    }
    return SECFailure;
}

static SECStatus
crmf_copy_validity(CRMFGetValidity *destValidity,
                   CRMFOptionalValidity *src)
{
    SECStatus rv;

    destValidity->notBefore = destValidity->notAfter = NULL;
    if (src->notBefore.data != NULL) {
        rv = crmf_create_prtime(&src->notBefore,
                                &destValidity->notBefore);
        if (rv != SECSuccess) {
            return rv;
        }
    }
    if (src->notAfter.data != NULL) {
        rv = crmf_create_prtime(&src->notAfter,
                                &destValidity->notAfter);
        if (rv != SECSuccess) {
            return rv;
        }
    }
    return SECSuccess;
}

SECStatus
CRMF_CertRequestGetCertTemplateValidity(CRMFCertRequest *inCertReq,
                                        CRMFGetValidity *destValidity)
{
    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return SECFailure;
    }
    if (CRMF_DoesRequestHaveField(inCertReq, crmfValidity)) {
        return crmf_copy_validity(destValidity,
                                  inCertReq->certTemplate.validity);
    }
    return SECFailure;
}

CRMFControl *
CRMF_CertRequestGetControlAtIndex(CRMFCertRequest *inCertReq, int index)
{
    CRMFControl *newControl, *srcControl;
    int numControls;
    SECStatus rv;

    PORT_Assert(inCertReq != NULL);
    if (inCertReq == NULL) {
        return NULL;
    }
    numControls = CRMF_CertRequestGetNumControls(inCertReq);
    if (index >= numControls || index < 0) {
        return NULL;
    }
    newControl = PORT_ZNew(CRMFControl);
    if (newControl == NULL) {
        return NULL;
    }
    srcControl = inCertReq->controls[index];
    newControl->tag = srcControl->tag;
    rv = SECITEM_CopyItem(NULL, &newControl->derTag, &srcControl->derTag);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = SECITEM_CopyItem(NULL, &newControl->derValue,
                          &srcControl->derValue);
    if (rv != SECSuccess) {
        goto loser;
    }
    /* Copy over the PKIArchiveOptions stuff */
    switch (srcControl->tag) {
        case SEC_OID_PKIX_REGCTRL_REGTOKEN:
        case SEC_OID_PKIX_REGCTRL_AUTHENTICATOR:
            /* No further processing necessary for these types. */
            rv = SECSuccess;
            break;
        case SEC_OID_PKIX_REGCTRL_OLD_CERT_ID:
        case SEC_OID_PKIX_REGCTRL_PKIPUBINFO:
        case SEC_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY:
            /* These aren't supported yet, so no post-processing will
             * be done at this time.  But we don't want to fail in case
             * we read in DER that has one of these options.
             */
            rv = SECSuccess;
            break;
        case SEC_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS:
            rv = crmf_copy_pkiarchiveoptions(NULL,
                                             &newControl->value.archiveOptions,
                                             &srcControl->value.archiveOptions);
            break;
        default:
            rv = SECFailure;
    }
    if (rv != SECSuccess) {
        goto loser;
    }
    return newControl;
loser:
    if (newControl != NULL) {
        CRMF_DestroyControl(newControl);
    }
    return NULL;
}

static SECItem *
crmf_copy_control_value(CRMFControl *inControl)
{
    return SECITEM_DupItem(&inControl->derValue);
}

SECItem *
CRMF_ControlGetAuthenticatorControlValue(CRMFControl *inControl)
{
    PORT_Assert(inControl != NULL);
    if (inControl == NULL ||
        CRMF_ControlGetControlType(inControl) != crmfAuthenticatorControl) {
        return NULL;
    }
    return crmf_copy_control_value(inControl);
}

CRMFControlType
CRMF_ControlGetControlType(CRMFControl *inControl)
{
    CRMFControlType retType;

    PORT_Assert(inControl != NULL);
    switch (inControl->tag) {
        case SEC_OID_PKIX_REGCTRL_REGTOKEN:
            retType = crmfRegTokenControl;
            break;
        case SEC_OID_PKIX_REGCTRL_AUTHENTICATOR:
            retType = crmfAuthenticatorControl;
            break;
        case SEC_OID_PKIX_REGCTRL_PKIPUBINFO:
            retType = crmfPKIPublicationInfoControl;
            break;
        case SEC_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS:
            retType = crmfPKIArchiveOptionsControl;
            break;
        case SEC_OID_PKIX_REGCTRL_OLD_CERT_ID:
            retType = crmfOldCertIDControl;
            break;
        case SEC_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY:
            retType = crmfProtocolEncrKeyControl;
            break;
        default:
            retType = crmfNoControl;
    }
    return retType;
}

CRMFPKIArchiveOptions *
CRMF_ControlGetPKIArchiveOptions(CRMFControl *inControl)
{
    CRMFPKIArchiveOptions *newOpt = NULL;
    SECStatus rv;

    PORT_Assert(inControl != NULL);
    if (inControl == NULL ||
        CRMF_ControlGetControlType(inControl) != crmfPKIArchiveOptionsControl) {
        goto loser;
    }
    newOpt = PORT_ZNew(CRMFPKIArchiveOptions);
    if (newOpt == NULL) {
        goto loser;
    }
    rv = crmf_copy_pkiarchiveoptions(NULL, newOpt,
                                     &inControl->value.archiveOptions);
    if (rv != SECSuccess) {
        goto loser;
    }

loser:
    if (newOpt != NULL) {
        CRMF_DestroyPKIArchiveOptions(newOpt);
    }
    return NULL;
}

SECItem *
CRMF_ControlGetRegTokenControlValue(CRMFControl *inControl)
{
    PORT_Assert(inControl != NULL);
    if (inControl == NULL ||
        CRMF_ControlGetControlType(inControl) != crmfRegTokenControl) {
        return NULL;
    }
    return crmf_copy_control_value(inControl);
    ;
}

CRMFCertExtension *
CRMF_CertRequestGetExtensionAtIndex(CRMFCertRequest *inCertReq,
                                    int index)
{
    int numExtensions;

    PORT_Assert(inCertReq != NULL);
    numExtensions = CRMF_CertRequestGetNumberOfExtensions(inCertReq);
    if (index >= numExtensions || index < 0) {
        return NULL;
    }
    return crmf_copy_cert_extension(NULL,
                                    inCertReq->certTemplate.extensions[index]);
}
