gecko/security/nss/lib/crmf/crmfcont.c
2008-06-06 08:40:11 -04:00

1191 lines
32 KiB
C

/* -*- Mode: C; tab-width: 8 -*-*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 the Initial Developer are Copyright (C) 1994-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "crmf.h"
#include "crmfi.h"
#include "pk11func.h"
#include "keyhi.h"
#include "secoid.h"
static SECStatus
crmf_modify_control_array (CRMFCertRequest *inCertReq, int count)
{
if (count > 0) {
void *dummy = PORT_Realloc(inCertReq->controls,
sizeof(CRMFControl*)*(count+2));
if (dummy == NULL) {
return SECFailure;
}
inCertReq->controls = dummy;
} else {
inCertReq->controls = PORT_ZNewArray(CRMFControl*, 2);
}
return (inCertReq->controls == NULL) ? SECFailure : SECSuccess ;
}
static SECStatus
crmf_add_new_control(CRMFCertRequest *inCertReq,SECOidTag inTag,
CRMFControl **destControl)
{
SECOidData *oidData;
SECStatus rv;
PRArenaPool *poolp;
int numControls = 0;
CRMFControl *newControl;
CRMFControl **controls;
void *mark;
poolp = inCertReq->poolp;
if (poolp == NULL) {
return SECFailure;
}
mark = PORT_ArenaMark(poolp);
if (inCertReq->controls != NULL) {
while (inCertReq->controls[numControls] != NULL)
numControls++;
}
rv = crmf_modify_control_array(inCertReq, numControls);
if (rv != SECSuccess) {
goto loser;
}
controls = inCertReq->controls;
oidData = SECOID_FindOIDByTag(inTag);
newControl = *destControl = PORT_ArenaZNew(poolp,CRMFControl);
if (newControl == NULL) {
goto loser;
}
rv = SECITEM_CopyItem(poolp, &newControl->derTag, &oidData->oid);
if (rv != SECSuccess) {
goto loser;
}
newControl->tag = inTag;
controls[numControls] = newControl;
controls[numControls+1] = NULL;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
*destControl = NULL;
return SECFailure;
}
static SECStatus
crmf_add_secitem_control(CRMFCertRequest *inCertReq, SECItem *value,
SECOidTag inTag)
{
SECStatus rv;
CRMFControl *newControl;
void *mark;
rv = crmf_add_new_control(inCertReq, inTag, &newControl);
if (rv != SECSuccess) {
return rv;
}
mark = PORT_ArenaMark(inCertReq->poolp);
rv = SECITEM_CopyItem(inCertReq->poolp, &newControl->derValue, value);
if (rv != SECSuccess) {
PORT_ArenaRelease(inCertReq->poolp, mark);
return rv;
}
PORT_ArenaUnmark(inCertReq->poolp, mark);
return SECSuccess;
}
SECStatus
CRMF_CertRequestSetRegTokenControl(CRMFCertRequest *inCertReq, SECItem *value)
{
return crmf_add_secitem_control(inCertReq, value,
SEC_OID_PKIX_REGCTRL_REGTOKEN);
}
SECStatus
CRMF_CertRequestSetAuthenticatorControl (CRMFCertRequest *inCertReq,
SECItem *value)
{
return crmf_add_secitem_control(inCertReq, value,
SEC_OID_PKIX_REGCTRL_AUTHENTICATOR);
}
SECStatus
crmf_destroy_encrypted_value(CRMFEncryptedValue *inEncrValue, PRBool freeit)
{
if (inEncrValue != NULL) {
if (inEncrValue->intendedAlg) {
SECOID_DestroyAlgorithmID(inEncrValue->intendedAlg, PR_TRUE);
inEncrValue->intendedAlg = NULL;
}
if (inEncrValue->symmAlg) {
SECOID_DestroyAlgorithmID(inEncrValue->symmAlg, PR_TRUE);
inEncrValue->symmAlg = NULL;
}
if (inEncrValue->encSymmKey.data) {
PORT_Free(inEncrValue->encSymmKey.data);
inEncrValue->encSymmKey.data = NULL;
}
if (inEncrValue->keyAlg) {
SECOID_DestroyAlgorithmID(inEncrValue->keyAlg, PR_TRUE);
inEncrValue->keyAlg = NULL;
}
if (inEncrValue->valueHint.data) {
PORT_Free(inEncrValue->valueHint.data);
inEncrValue->valueHint.data = NULL;
}
if (inEncrValue->encValue.data) {
PORT_Free(inEncrValue->encValue.data);
inEncrValue->encValue.data = NULL;
}
if (freeit) {
PORT_Free(inEncrValue);
}
}
return SECSuccess;
}
SECStatus
CRMF_DestroyEncryptedValue(CRMFEncryptedValue *inEncrValue)
{
return crmf_destroy_encrypted_value(inEncrValue, PR_TRUE);
}
SECStatus
crmf_copy_encryptedvalue_secalg(PRArenaPool *poolp,
SECAlgorithmID *srcAlgId,
SECAlgorithmID **destAlgId)
{
SECAlgorithmID *newAlgId;
SECStatus rv;
newAlgId = (poolp != NULL) ? PORT_ArenaZNew(poolp, SECAlgorithmID) :
PORT_ZNew(SECAlgorithmID);
if (newAlgId == NULL) {
return SECFailure;
}
rv = SECOID_CopyAlgorithmID(poolp, newAlgId, srcAlgId);
if (rv != SECSuccess) {
if (!poolp) {
SECOID_DestroyAlgorithmID(newAlgId, PR_TRUE);
}
return rv;
}
*destAlgId = newAlgId;
return rv;
}
SECStatus
crmf_copy_encryptedvalue(PRArenaPool *poolp,
CRMFEncryptedValue *srcValue,
CRMFEncryptedValue *destValue)
{
SECStatus rv;
if (srcValue->intendedAlg != NULL) {
rv = crmf_copy_encryptedvalue_secalg(poolp,
srcValue->intendedAlg,
&destValue->intendedAlg);
if (rv != SECSuccess) {
goto loser;
}
}
if (srcValue->symmAlg != NULL) {
rv = crmf_copy_encryptedvalue_secalg(poolp,
srcValue->symmAlg,
&destValue->symmAlg);
if (rv != SECSuccess) {
goto loser;
}
}
if (srcValue->encSymmKey.data != NULL) {
rv = crmf_make_bitstring_copy(poolp,
&destValue->encSymmKey,
&srcValue->encSymmKey);
if (rv != SECSuccess) {
goto loser;
}
}
if (srcValue->keyAlg != NULL) {
rv = crmf_copy_encryptedvalue_secalg(poolp,
srcValue->keyAlg,
&destValue->keyAlg);
if (rv != SECSuccess) {
goto loser;
}
}
if (srcValue->valueHint.data != NULL) {
rv = SECITEM_CopyItem(poolp,
&destValue->valueHint,
&srcValue->valueHint);
if (rv != SECSuccess) {
goto loser;
}
}
if (srcValue->encValue.data != NULL) {
rv = crmf_make_bitstring_copy(poolp,
&destValue->encValue,
&srcValue->encValue);
if (rv != SECSuccess) {
goto loser;
}
}
return SECSuccess;
loser:
if (poolp == NULL && destValue != NULL) {
crmf_destroy_encrypted_value(destValue, PR_FALSE);
}
return SECFailure;
}
SECStatus
crmf_copy_encryptedkey(PRArenaPool *poolp,
CRMFEncryptedKey *srcEncrKey,
CRMFEncryptedKey *destEncrKey)
{
SECStatus rv;
void *mark = NULL;
if (poolp != NULL) {
mark = PORT_ArenaMark(poolp);
}
switch (srcEncrKey->encKeyChoice) {
case crmfEncryptedValueChoice:
rv = crmf_copy_encryptedvalue(poolp,
&srcEncrKey->value.encryptedValue,
&destEncrKey->value.encryptedValue);
break;
case crmfEnvelopedDataChoice:
destEncrKey->value.envelopedData =
SEC_PKCS7CopyContentInfo(srcEncrKey->value.envelopedData);
rv = (destEncrKey->value.envelopedData != NULL) ? SECSuccess:
SECFailure;
break;
default:
rv = SECFailure;
}
if (rv != SECSuccess) {
goto loser;
}
destEncrKey->encKeyChoice = srcEncrKey->encKeyChoice;
if (mark) {
PORT_ArenaUnmark(poolp, mark);
}
return SECSuccess;
loser:
if (mark) {
PORT_ArenaRelease(poolp, mark);
}
return SECFailure;
}
static CRMFPKIArchiveOptions*
crmf_create_encr_pivkey_option(CRMFEncryptedKey *inEncryptedKey)
{
CRMFPKIArchiveOptions *newArchOpt;
SECStatus rv;
newArchOpt = PORT_ZNew(CRMFPKIArchiveOptions);
if (newArchOpt == NULL) {
goto loser;
}
rv = crmf_copy_encryptedkey(NULL, inEncryptedKey,
&newArchOpt->option.encryptedKey);
if (rv != SECSuccess) {
goto loser;
}
newArchOpt->archOption = crmfEncryptedPrivateKey;
return newArchOpt;
loser:
if (newArchOpt != NULL) {
CRMF_DestroyPKIArchiveOptions(newArchOpt);
}
return NULL;
}
static CRMFPKIArchiveOptions*
crmf_create_keygen_param_option(SECItem *inKeyGenParams)
{
CRMFPKIArchiveOptions *newArchOptions;
SECStatus rv;
newArchOptions = PORT_ZNew(CRMFPKIArchiveOptions);
if (newArchOptions == NULL) {
goto loser;
}
newArchOptions->archOption = crmfKeyGenParameters;
rv = SECITEM_CopyItem(NULL, &newArchOptions->option.keyGenParameters,
inKeyGenParams);
if (rv != SECSuccess) {
goto loser;
}
return newArchOptions;
loser:
if (newArchOptions != NULL) {
CRMF_DestroyPKIArchiveOptions(newArchOptions);
}
return NULL;
}
static CRMFPKIArchiveOptions*
crmf_create_arch_rem_gen_privkey(PRBool archiveRemGenPrivKey)
{
unsigned char value;
SECItem *dummy;
CRMFPKIArchiveOptions *newArchOptions;
value = (archiveRemGenPrivKey) ? hexTrue : hexFalse;
newArchOptions = PORT_ZNew(CRMFPKIArchiveOptions);
if (newArchOptions == NULL) {
goto loser;
}
dummy = SEC_ASN1EncodeItem(NULL,
&newArchOptions->option.archiveRemGenPrivKey,
&value, SEC_ASN1_GET(SEC_BooleanTemplate));
PORT_Assert (dummy == &newArchOptions->option.archiveRemGenPrivKey);
if (dummy != &newArchOptions->option.archiveRemGenPrivKey) {
SECITEM_FreeItem (dummy, PR_TRUE);
goto loser;
}
newArchOptions->archOption = crmfArchiveRemGenPrivKey;
return newArchOptions;
loser:
if (newArchOptions != NULL) {
CRMF_DestroyPKIArchiveOptions(newArchOptions);
}
return NULL;
}
CRMFPKIArchiveOptions*
CRMF_CreatePKIArchiveOptions(CRMFPKIArchiveOptionsType inType, void *data)
{
CRMFPKIArchiveOptions* retOptions;
PORT_Assert(data != NULL);
if (data == NULL) {
return NULL;
}
switch(inType) {
case crmfEncryptedPrivateKey:
retOptions = crmf_create_encr_pivkey_option((CRMFEncryptedKey*)data);
break;
case crmfKeyGenParameters:
retOptions = crmf_create_keygen_param_option((SECItem*)data);
break;
case crmfArchiveRemGenPrivKey:
retOptions = crmf_create_arch_rem_gen_privkey(*(PRBool*)data);
break;
default:
retOptions = NULL;
}
return retOptions;
}
static SECStatus
crmf_destroy_encrypted_key(CRMFEncryptedKey *inEncrKey, PRBool freeit)
{
PORT_Assert(inEncrKey != NULL);
if (inEncrKey != NULL) {
switch (inEncrKey->encKeyChoice){
case crmfEncryptedValueChoice:
crmf_destroy_encrypted_value(&inEncrKey->value.encryptedValue,
PR_FALSE);
break;
case crmfEnvelopedDataChoice:
SEC_PKCS7DestroyContentInfo(inEncrKey->value.envelopedData);
break;
default:
break;
}
if (freeit) {
PORT_Free(inEncrKey);
}
}
return SECSuccess;
}
SECStatus
crmf_destroy_pkiarchiveoptions(CRMFPKIArchiveOptions *inArchOptions,
PRBool freeit)
{
PORT_Assert(inArchOptions != NULL);
if (inArchOptions != NULL) {
switch (inArchOptions->archOption) {
case crmfEncryptedPrivateKey:
crmf_destroy_encrypted_key(&inArchOptions->option.encryptedKey,
PR_FALSE);
break;
case crmfKeyGenParameters:
case crmfArchiveRemGenPrivKey:
/* This is a union, so having a pointer to one is like
* having a pointer to both.
*/
SECITEM_FreeItem(&inArchOptions->option.keyGenParameters,
PR_FALSE);
break;
case crmfNoArchiveOptions:
break;
}
if (freeit) {
PORT_Free(inArchOptions);
}
}
return SECSuccess;
}
SECStatus
CRMF_DestroyPKIArchiveOptions(CRMFPKIArchiveOptions *inArchOptions)
{
return crmf_destroy_pkiarchiveoptions(inArchOptions, PR_TRUE);
}
static CK_MECHANISM_TYPE
crmf_get_non_pad_mechanism(CK_MECHANISM_TYPE type)
{
switch (type) {
case CKM_DES3_CBC_PAD:
return CKM_DES3_CBC;
case CKM_CAST5_CBC_PAD:
return CKM_CAST5_CBC;
case CKM_DES_CBC_PAD:
return CKM_DES_CBC;
case CKM_IDEA_CBC_PAD:
return CKM_IDEA_CBC;
case CKM_CAST3_CBC_PAD:
return CKM_CAST3_CBC;
case CKM_CAST_CBC_PAD:
return CKM_CAST_CBC;
case CKM_RC5_CBC_PAD:
return CKM_RC5_CBC;
case CKM_RC2_CBC_PAD:
return CKM_RC2_CBC;
case CKM_CDMF_CBC_PAD:
return CKM_CDMF_CBC;
}
return type;
}
static CK_MECHANISM_TYPE
crmf_get_pad_mech_from_tag(SECOidTag oidTag)
{
CK_MECHANISM_TYPE mechType;
SECOidData *oidData;
oidData = SECOID_FindOIDByTag(oidTag);
mechType = (CK_MECHANISM_TYPE)oidData->mechanism;
return PK11_GetPadMechanism(mechType);
}
static CK_MECHANISM_TYPE
crmf_get_best_privkey_wrap_mechanism(PK11SlotInfo *slot)
{
CK_MECHANISM_TYPE privKeyPadMechs[] = { CKM_DES3_CBC_PAD,
CKM_CAST5_CBC_PAD,
CKM_DES_CBC_PAD,
CKM_IDEA_CBC_PAD,
CKM_CAST3_CBC_PAD,
CKM_CAST_CBC_PAD,
CKM_RC5_CBC_PAD,
CKM_RC2_CBC_PAD,
CKM_CDMF_CBC_PAD };
int mechCount = sizeof(privKeyPadMechs)/sizeof(privKeyPadMechs[0]);
int i;
for (i=0; i < mechCount; i++) {
if (PK11_DoesMechanism(slot, privKeyPadMechs[i])) {
return privKeyPadMechs[i];
}
}
return CKM_INVALID_MECHANISM;
}
CK_MECHANISM_TYPE
CRMF_GetBestWrapPadMechanism(PK11SlotInfo *slot)
{
return crmf_get_best_privkey_wrap_mechanism(slot);
}
static SECItem*
crmf_get_iv(CK_MECHANISM_TYPE mechType)
{
int iv_size = PK11_GetIVLength(mechType);
SECItem *iv;
SECStatus rv;
iv = PORT_ZNew(SECItem);
if (iv == NULL) {
return NULL;
}
if (iv_size == 0) {
iv->data = NULL;
iv->len = 0;
return iv;
}
iv->data = PORT_NewArray(unsigned char, iv_size);
if (iv->data == NULL) {
iv->len = 0;
return iv;
}
iv->len = iv_size;
rv = PK11_GenerateRandom(iv->data, iv->len);
if (rv != SECSuccess) {
PORT_Free(iv->data);
iv->data = NULL;
iv->len = 0;
}
return iv;
}
SECItem*
CRMF_GetIVFromMechanism(CK_MECHANISM_TYPE mechType)
{
return crmf_get_iv(mechType);
}
CK_MECHANISM_TYPE
crmf_get_mechanism_from_public_key(SECKEYPublicKey *inPubKey)
{
CERTSubjectPublicKeyInfo *spki = NULL;
SECOidTag tag;
spki = SECKEY_CreateSubjectPublicKeyInfo(inPubKey);
if (spki == NULL) {
return CKM_INVALID_MECHANISM;
}
tag = SECOID_FindOIDTag(&spki->algorithm.algorithm);
SECKEY_DestroySubjectPublicKeyInfo(spki);
spki = NULL;
return PK11_AlgtagToMechanism(tag);
}
SECItem*
crmf_get_public_value(SECKEYPublicKey *pubKey, SECItem *dest)
{
SECItem *src;
switch(pubKey->keyType) {
case dsaKey:
src = &pubKey->u.dsa.publicValue;
break;
case rsaKey:
src = &pubKey->u.rsa.modulus;
break;
case dhKey:
src = &pubKey->u.dh.publicValue;
break;
default:
src = NULL;
break;
}
if (!src) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return NULL;
}
if (dest != NULL) {
SECStatus rv = SECITEM_CopyItem(NULL, dest, src);
if (rv != SECSuccess) {
dest = NULL;
}
} else {
dest = SECITEM_ArenaDupItem(NULL, src);
}
return dest;
}
static SECItem*
crmf_decode_params(SECItem *inParams)
{
SECItem *params;
SECStatus rv = SECFailure;
PRArenaPool *poolp;
poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
if (poolp == NULL) {
return NULL;
}
params = PORT_ArenaZNew(poolp, SECItem);
if (params) {
rv = SEC_ASN1DecodeItem(poolp, params,
SEC_ASN1_GET(SEC_OctetStringTemplate),
inParams);
}
params = (rv == SECSuccess) ? SECITEM_ArenaDupItem(NULL, params) : NULL;
PORT_FreeArena(poolp, PR_FALSE);
return params;
}
static int
crmf_get_key_size_from_mech(CK_MECHANISM_TYPE mechType)
{
CK_MECHANISM_TYPE keyGen = PK11_GetKeyGen(mechType);
switch (keyGen) {
case CKM_CDMF_KEY_GEN:
case CKM_DES_KEY_GEN:
return 8;
case CKM_DES2_KEY_GEN:
return 16;
case CKM_DES3_KEY_GEN:
return 24;
}
return 0;
}
SECStatus
crmf_encrypted_value_unwrap_priv_key(PRArenaPool *poolp,
CRMFEncryptedValue *encValue,
SECKEYPrivateKey *privKey,
SECKEYPublicKey *newPubKey,
SECItem *nickname,
PK11SlotInfo *slot,
unsigned char keyUsage,
SECKEYPrivateKey **unWrappedKey,
void *wincx)
{
PK11SymKey *wrappingKey = NULL;
CK_MECHANISM_TYPE wrapMechType;
SECOidTag oidTag;
SECItem *params = NULL, *publicValue = NULL;
int keySize, origLen;
CK_KEY_TYPE keyType;
CK_ATTRIBUTE_TYPE *usage = NULL;
CK_ATTRIBUTE_TYPE rsaUsage[] = {
CKA_UNWRAP, CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER };
CK_ATTRIBUTE_TYPE dsaUsage[] = { CKA_SIGN };
CK_ATTRIBUTE_TYPE dhUsage[] = { CKA_DERIVE };
int usageCount = 0;
oidTag = SECOID_GetAlgorithmTag(encValue->symmAlg);
wrapMechType = crmf_get_pad_mech_from_tag(oidTag);
keySize = crmf_get_key_size_from_mech(wrapMechType);
wrappingKey = PK11_PubUnwrapSymKey(privKey, &encValue->encSymmKey,
wrapMechType, CKA_UNWRAP, keySize);
if (wrappingKey == NULL) {
goto loser;
}/* Make the length a byte length instead of bit length*/
params = (encValue->symmAlg != NULL) ?
crmf_decode_params(&encValue->symmAlg->parameters) : NULL;
origLen = encValue->encValue.len;
encValue->encValue.len = CRMF_BITS_TO_BYTES(origLen);
publicValue = crmf_get_public_value(newPubKey, NULL);
switch(newPubKey->keyType) {
default:
case rsaKey:
keyType = CKK_RSA;
switch (keyUsage & (KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE)) {
case KU_KEY_ENCIPHERMENT:
usage = rsaUsage;
usageCount = 2;
break;
case KU_DIGITAL_SIGNATURE:
usage = &rsaUsage[2];
usageCount = 2;
break;
case KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE:
case 0: /* default to everything */
usage = rsaUsage;
usageCount = 4;
break;
}
break;
case dhKey:
keyType = CKK_DH;
usage = dhUsage;
usageCount = sizeof(dhUsage)/sizeof(dhUsage[0]);
break;
case dsaKey:
keyType = CKK_DSA;
usage = dsaUsage;
usageCount = sizeof(dsaUsage)/sizeof(dsaUsage[0]);
break;
}
PORT_Assert(usage != NULL);
PORT_Assert(usageCount != 0);
*unWrappedKey = PK11_UnwrapPrivKey(slot, wrappingKey, wrapMechType, params,
&encValue->encValue, nickname,
publicValue, PR_TRUE,PR_TRUE,
keyType, usage, usageCount, wincx);
encValue->encValue.len = origLen;
if (*unWrappedKey == NULL) {
goto loser;
}
SECITEM_FreeItem (publicValue, PR_TRUE);
if (params!= NULL) {
SECITEM_FreeItem(params, PR_TRUE);
}
PK11_FreeSymKey(wrappingKey);
return SECSuccess;
loser:
*unWrappedKey = NULL;
return SECFailure;
}
CRMFEncryptedValue *
crmf_create_encrypted_value_wrapped_privkey(SECKEYPrivateKey *inPrivKey,
SECKEYPublicKey *inCAKey,
CRMFEncryptedValue *destValue)
{
SECItem wrappedPrivKey, wrappedSymKey;
SECItem encodedParam, *dummy;
SECStatus rv;
CK_MECHANISM_TYPE pubMechType, symKeyType;
unsigned char *wrappedSymKeyBits;
unsigned char *wrappedPrivKeyBits;
SECItem *iv = NULL;
SECOidTag tag;
PK11SymKey *symKey;
PK11SlotInfo *slot;
SECAlgorithmID *symmAlg;
CRMFEncryptedValue *myEncrValue = NULL;
encodedParam.data = NULL;
wrappedSymKeyBits = PORT_NewArray(unsigned char, MAX_WRAPPED_KEY_LEN);
wrappedPrivKeyBits = PORT_NewArray(unsigned char, MAX_WRAPPED_KEY_LEN);
if (wrappedSymKeyBits == NULL || wrappedPrivKeyBits == NULL) {
goto loser;
}
if (destValue == NULL) {
myEncrValue = destValue = PORT_ZNew(CRMFEncryptedValue);
if (destValue == NULL) {
goto loser;
}
}
pubMechType = crmf_get_mechanism_from_public_key(inCAKey);
if (pubMechType == CKM_INVALID_MECHANISM) {
/* XXX I should probably do something here for non-RSA
* keys that are in certs. (ie DSA)
* XXX or at least SET AN ERROR CODE.
*/
goto loser;
}
slot = inPrivKey->pkcs11Slot;
PORT_Assert(slot != NULL);
symKeyType = crmf_get_best_privkey_wrap_mechanism(slot);
symKey = PK11_KeyGen(slot, symKeyType, NULL, 0, NULL);
if (symKey == NULL) {
goto loser;
}
wrappedSymKey.data = wrappedSymKeyBits;
wrappedSymKey.len = MAX_WRAPPED_KEY_LEN;
rv = PK11_PubWrapSymKey(pubMechType, inCAKey, symKey, &wrappedSymKey);
if (rv != SECSuccess) {
goto loser;
}
/* Make the length of the result a Bit String length. */
wrappedSymKey.len <<= 3;
wrappedPrivKey.data = wrappedPrivKeyBits;
wrappedPrivKey.len = MAX_WRAPPED_KEY_LEN;
iv = crmf_get_iv(symKeyType);
rv = PK11_WrapPrivKey(slot, symKey, inPrivKey, symKeyType, iv,
&wrappedPrivKey, NULL);
PK11_FreeSymKey(symKey);
if (rv != SECSuccess) {
goto loser;
}
/* Make the length of the result a Bit String length. */
wrappedPrivKey.len <<= 3;
rv = crmf_make_bitstring_copy(NULL,
&destValue->encValue,
&wrappedPrivKey);
if (rv != SECSuccess) {
goto loser;
}
rv = crmf_make_bitstring_copy(NULL,
&destValue->encSymmKey,
&wrappedSymKey);
if (rv != SECSuccess) {
goto loser;
}
destValue->symmAlg = symmAlg = PORT_ZNew(SECAlgorithmID);
if (symmAlg == NULL) {
goto loser;
}
dummy = SEC_ASN1EncodeItem(NULL, &encodedParam, iv,
SEC_ASN1_GET(SEC_OctetStringTemplate));
if (dummy != &encodedParam) {
SECITEM_FreeItem(dummy, PR_TRUE);
goto loser;
}
symKeyType = crmf_get_non_pad_mechanism(symKeyType);
tag = PK11_MechanismToAlgtag(symKeyType);
rv = SECOID_SetAlgorithmID(NULL, symmAlg, tag, &encodedParam);
if (rv != SECSuccess) {
goto loser;
}
SECITEM_FreeItem(&encodedParam, PR_FALSE);
PORT_Free(wrappedPrivKeyBits);
PORT_Free(wrappedSymKeyBits);
SECITEM_FreeItem(iv, PR_TRUE);
return destValue;
loser:
if (iv != NULL) {
SECITEM_FreeItem(iv, PR_TRUE);
}
if (myEncrValue != NULL) {
crmf_destroy_encrypted_value(myEncrValue, PR_TRUE);
}
if (wrappedSymKeyBits != NULL) {
PORT_Free(wrappedSymKeyBits);
}
if (wrappedPrivKeyBits != NULL) {
PORT_Free(wrappedPrivKeyBits);
}
if (encodedParam.data != NULL) {
SECITEM_FreeItem(&encodedParam, PR_FALSE);
}
return NULL;
}
CRMFEncryptedKey*
CRMF_CreateEncryptedKeyWithEncryptedValue (SECKEYPrivateKey *inPrivKey,
CERTCertificate *inCACert)
{
SECKEYPublicKey *caPubKey = NULL;
CRMFEncryptedKey *encKey = NULL;
CRMFEncryptedValue *dummy;
PORT_Assert(inPrivKey != NULL && inCACert != NULL);
if (inPrivKey == NULL || inCACert == NULL) {
return NULL;
}
caPubKey = CERT_ExtractPublicKey(inCACert);
if (caPubKey == NULL) {
goto loser;
}
encKey = PORT_ZNew(CRMFEncryptedKey);
if (encKey == NULL) {
goto loser;
}
dummy = crmf_create_encrypted_value_wrapped_privkey(inPrivKey,
caPubKey,
&encKey->value.encryptedValue);
PORT_Assert(dummy == &encKey->value.encryptedValue);
/* We won't add the der value here, but rather when it
* becomes part of a certificate request.
*/
SECKEY_DestroyPublicKey(caPubKey);
encKey->encKeyChoice = crmfEncryptedValueChoice;
return encKey;
loser:
if (encKey != NULL) {
CRMF_DestroyEncryptedKey(encKey);
}
if (caPubKey != NULL) {
SECKEY_DestroyPublicKey(caPubKey);
}
return NULL;
}
SECStatus
CRMF_DestroyEncryptedKey(CRMFEncryptedKey *inEncrKey)
{
return crmf_destroy_encrypted_key(inEncrKey, PR_TRUE);
}
SECStatus
crmf_copy_pkiarchiveoptions(PRArenaPool *poolp,
CRMFPKIArchiveOptions *destOpt,
CRMFPKIArchiveOptions *srcOpt)
{
SECStatus rv;
destOpt->archOption = srcOpt->archOption;
switch (srcOpt->archOption) {
case crmfEncryptedPrivateKey:
rv = crmf_copy_encryptedkey(poolp,
&srcOpt->option.encryptedKey,
&destOpt->option.encryptedKey);
break;
case crmfKeyGenParameters:
case crmfArchiveRemGenPrivKey:
/* We've got a union, so having a pointer to one is just
* like having a pointer to the other one.
*/
rv = SECITEM_CopyItem(poolp,
&destOpt->option.keyGenParameters,
&srcOpt->option.keyGenParameters);
break;
default:
rv = SECFailure;
}
return rv;
}
static SECStatus
crmf_check_and_adjust_archoption(CRMFControl *inControl)
{
CRMFPKIArchiveOptions *options;
options = &inControl->value.archiveOptions;
if (options->archOption == crmfNoArchiveOptions) {
/* It hasn't been set, so figure it out from the
* der.
*/
switch (inControl->derValue.data[0] & 0x0f) {
case 0:
options->archOption = crmfEncryptedPrivateKey;
break;
case 1:
options->archOption = crmfKeyGenParameters;
break;
case 2:
options->archOption = crmfArchiveRemGenPrivKey;
break;
default:
/* We've got bad DER. Return an error. */
return SECFailure;
}
}
return SECSuccess;
}
static const SEC_ASN1Template *
crmf_get_pkiarchive_subtemplate(CRMFControl *inControl)
{
const SEC_ASN1Template *retTemplate;
SECStatus rv;
/*
* We could be in the process of decoding, in which case the
* archOption field will not be set. Let's check it and set
* it accordingly.
*/
rv = crmf_check_and_adjust_archoption(inControl);
if (rv != SECSuccess) {
return NULL;
}
switch (inControl->value.archiveOptions.archOption) {
case crmfEncryptedPrivateKey:
retTemplate = CRMFEncryptedKeyWithEncryptedValueTemplate;
inControl->value.archiveOptions.option.encryptedKey.encKeyChoice =
crmfEncryptedValueChoice;
break;
default:
retTemplate = NULL;
}
return retTemplate;
}
const SEC_ASN1Template*
crmf_get_pkiarchiveoptions_subtemplate(CRMFControl *inControl)
{
const SEC_ASN1Template *retTemplate;
switch (inControl->tag) {
case SEC_OID_PKIX_REGCTRL_REGTOKEN:
case SEC_OID_PKIX_REGCTRL_AUTHENTICATOR:
retTemplate = SEC_ASN1_GET(SEC_UTF8StringTemplate);
break;
case SEC_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS:
retTemplate = crmf_get_pkiarchive_subtemplate(inControl);
break;
case SEC_OID_PKIX_REGCTRL_PKIPUBINFO:
case SEC_OID_PKIX_REGCTRL_OLD_CERT_ID:
case SEC_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY:
/* We don't support these controls, so we fail for now.*/
retTemplate = NULL;
break;
default:
retTemplate = NULL;
}
return retTemplate;
}
static SECStatus
crmf_encode_pkiarchiveoptions(PRArenaPool *poolp, CRMFControl *inControl)
{
const SEC_ASN1Template *asn1Template;
asn1Template = crmf_get_pkiarchiveoptions_subtemplate(inControl);
/* 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.
*/
SEC_ASN1EncodeItem(poolp, &inControl->derValue,
&inControl->value.archiveOptions, asn1Template);
if (inControl->derValue.data == NULL) {
goto loser;
}
return SECSuccess;
loser:
return SECFailure;
}
SECStatus
CRMF_CertRequestSetPKIArchiveOptions(CRMFCertRequest *inCertReq,
CRMFPKIArchiveOptions *inOptions)
{
CRMFControl *newControl;
PRArenaPool *poolp;
SECStatus rv;
void *mark;
PORT_Assert(inCertReq != NULL && inOptions != NULL);
if (inCertReq == NULL || inOptions == NULL) {
return SECFailure;
}
poolp = inCertReq->poolp;
mark = PORT_ArenaMark(poolp);
rv = crmf_add_new_control(inCertReq,
SEC_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS,
&newControl);
if (rv != SECSuccess) {
goto loser;
}
rv = crmf_copy_pkiarchiveoptions(poolp,
&newControl->value.archiveOptions,
inOptions);
if (rv != SECSuccess) {
goto loser;
}
rv = crmf_encode_pkiarchiveoptions(poolp, newControl);
if (rv != SECSuccess) {
goto loser;
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
static SECStatus
crmf_destroy_control(CRMFControl *inControl, PRBool freeit)
{
PORT_Assert(inControl != NULL);
if (inControl != NULL) {
SECITEM_FreeItem(&inControl->derTag, PR_FALSE);
SECITEM_FreeItem(&inControl->derValue, PR_FALSE);
/* None of the other tags require special processing at
* the moment when freeing because they are not supported,
* but if/when they are, add the necessary routines here.
* If all controls are supported, then every member of the
* union inControl->value will have a case that deals with
* it in the following switch statement.
*/
switch (inControl->tag) {
case SEC_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS:
crmf_destroy_pkiarchiveoptions(&inControl->value.archiveOptions,
PR_FALSE);
break;
default:
/* Put this here to get rid of all those annoying warnings.*/
break;
}
if (freeit) {
PORT_Free(inControl);
}
}
return SECSuccess;
}
SECStatus
CRMF_DestroyControl(CRMFControl *inControl)
{
return crmf_destroy_control(inControl, PR_TRUE);
}
static SECOidTag
crmf_controltype_to_tag(CRMFControlType inControlType)
{
SECOidTag retVal;
switch(inControlType) {
case crmfRegTokenControl:
retVal = SEC_OID_PKIX_REGCTRL_REGTOKEN;
break;
case crmfAuthenticatorControl:
retVal = SEC_OID_PKIX_REGCTRL_AUTHENTICATOR;
break;
case crmfPKIPublicationInfoControl:
retVal = SEC_OID_PKIX_REGCTRL_PKIPUBINFO;
break;
case crmfPKIArchiveOptionsControl:
retVal = SEC_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS;
break;
case crmfOldCertIDControl:
retVal = SEC_OID_PKIX_REGCTRL_OLD_CERT_ID;
break;
case crmfProtocolEncrKeyControl:
retVal = SEC_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY;
break;
default:
retVal = SEC_OID_UNKNOWN;
break;
}
return retVal;
}
PRBool
CRMF_CertRequestIsControlPresent(CRMFCertRequest *inCertReq,
CRMFControlType inControlType)
{
SECOidTag controlTag;
int i;
PORT_Assert(inCertReq != NULL);
if (inCertReq == NULL || inCertReq->controls == NULL) {
return PR_FALSE;
}
controlTag = crmf_controltype_to_tag(inControlType);
for (i=0; inCertReq->controls[i] != NULL; i++) {
if (inCertReq->controls[i]->tag == controlTag) {
return PR_TRUE;
}
}
return PR_FALSE;
}