summaryrefslogtreecommitdiff
path: root/security/nss/lib/crmf/crmfcont.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/crmf/crmfcont.c')
-rw-r--r--security/nss/lib/crmf/crmfcont.c1190
1 files changed, 1190 insertions, 0 deletions
diff --git a/security/nss/lib/crmf/crmfcont.c b/security/nss/lib/crmf/crmfcont.c
new file mode 100644
index 000000000..3f096f545
--- /dev/null
+++ b/security/nss/lib/crmf/crmfcont.c
@@ -0,0 +1,1190 @@
+/* -*- 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;
+}
+