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