/* -*- Mode: C; tab-width: 8 -*-*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "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; PLArenaPool *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(PLArenaPool *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(PLArenaPool *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(PLArenaPool *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; PLArenaPool *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(PLArenaPool *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; 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; } #ifdef DEBUG { CRMFEncryptedValue *dummy = crmf_create_encrypted_value_wrapped_privkey( inPrivKey, caPubKey, &encKey->value.encryptedValue); PORT_Assert(dummy == &encKey->value.encryptedValue); } #else crmf_create_encrypted_value_wrapped_privkey( inPrivKey, caPubKey, &encKey->value.encryptedValue); #endif /* 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(PLArenaPool *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(PLArenaPool *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; PLArenaPool *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; }