/* -*- 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(PRArenaPool        *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(PRArenaPool     *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(PRArenaPool *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;
    PRArenaPool    *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)
{
    PRArenaPool     *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)
{
  PRArenaPool *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]);
}

