| /* -*- 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; |
| } |