/* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1995-2000 Intel Corporation. All rights reserved. */ #include #include #include #include #define MAX_PUBLIC_KEY_TEMPLATES (20) #define MAX_PRIVATE_KEY_TEMPLATES (24) #define MAX_SECRET_KEY_TEMPLATES (24) typedef struct { KMF_ALGORITHM_INDEX kmfAlgorithmId; CK_KEY_TYPE ckKeyType; CK_MECHANISM_TYPE signmech; CK_MECHANISM_TYPE vfymech; CK_MECHANISM_TYPE hashmech; } ALG_INFO; static const ALG_INFO alg_info_map[] = { { KMF_ALGID_RSA, CKK_RSA, CKM_RSA_PKCS, CKM_RSA_PKCS, 0}, { KMF_ALGID_DSA, CKK_DSA, CKM_DSA, CKM_DSA, CKM_SHA_1 }, { KMF_ALGID_ECDSA, CKK_EC, CKM_ECDSA, CKM_ECDSA, CKM_SHA_1 }, { KMF_ALGID_SHA1WithDSA, CKK_DSA, CKM_DSA, CKM_DSA, CKM_SHA_1 }, { KMF_ALGID_SHA256WithDSA, CKK_DSA, CKM_DSA, CKM_DSA, CKM_SHA256 }, /* * For RSA, the verify can be done using a single mechanism, * but signing must be done separately because not all hardware * tokens support the combined hash+key operations. */ { KMF_ALGID_MD5WithRSA, CKK_RSA, CKM_RSA_PKCS, CKM_MD5_RSA_PKCS, CKM_MD5}, { KMF_ALGID_SHA1WithRSA, CKK_RSA, CKM_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_SHA_1}, { KMF_ALGID_SHA256WithRSA, CKK_RSA, CKM_RSA_PKCS, CKM_SHA256_RSA_PKCS, CKM_SHA256}, { KMF_ALGID_SHA384WithRSA, CKK_RSA, CKM_RSA_PKCS, CKM_SHA384_RSA_PKCS, CKM_SHA384}, { KMF_ALGID_SHA512WithRSA, CKK_RSA, CKM_RSA_PKCS, CKM_SHA512_RSA_PKCS, CKM_SHA512}, { KMF_ALGID_SHA1WithECDSA, CKK_EC, CKM_ECDSA, CKM_ECDSA, CKM_SHA_1}, { KMF_ALGID_SHA256WithECDSA, CKK_EC, CKM_ECDSA, CKM_ECDSA, CKM_SHA256}, { KMF_ALGID_SHA384WithECDSA, CKK_EC, CKM_ECDSA, CKM_ECDSA, CKM_SHA384}, { KMF_ALGID_SHA512WithECDSA, CKK_EC, CKM_ECDSA, CKM_ECDSA, CKM_SHA512} }; KMF_RETURN get_pk11_data(KMF_ALGORITHM_INDEX AlgId, CK_KEY_TYPE *keytype, CK_MECHANISM_TYPE *signmech, CK_MECHANISM_TYPE *hashmech, boolean_t vfy) { uint32_t uIndex; uint32_t uMapSize = sizeof (alg_info_map) / sizeof (ALG_INFO); for (uIndex = 0; uIndex < uMapSize; uIndex++) { if (alg_info_map[uIndex].kmfAlgorithmId == AlgId) { if (keytype) *keytype = alg_info_map[uIndex].ckKeyType; if (hashmech) *hashmech = alg_info_map[uIndex].hashmech; if (signmech) *signmech = (vfy ? alg_info_map[uIndex].vfymech : alg_info_map[uIndex].signmech); return (KMF_OK); } } /* no match */ return (KMF_ERR_BAD_ALGORITHM); } KMF_RETURN kmf_create_pk11_session(CK_SESSION_HANDLE *sessionp, CK_MECHANISM_TYPE wanted_mech, CK_FLAGS wanted_flags) { CK_RV rv; KMF_RETURN ret; KMF_RETURN kmf_rv = KMF_OK; CK_SLOT_ID_PTR pSlotList; CK_ULONG pulCount; CK_MECHANISM_INFO info; int i; ret = init_pk11(); if (ret != KMF_OK) return (ret); rv = C_GetSlotList(0, NULL, &pulCount); if (rv != CKR_OK) { kmf_rv = KMF_ERR_UNINITIALIZED; goto out; } pSlotList = (CK_SLOT_ID_PTR) malloc(pulCount * sizeof (CK_SLOT_ID)); if (pSlotList == NULL) { kmf_rv = KMF_ERR_MEMORY; goto out; } rv = C_GetSlotList(0, pSlotList, &pulCount); if (rv != CKR_OK) { kmf_rv = KMF_ERR_UNINITIALIZED; goto out; } for (i = 0; i < pulCount; i++) { rv = C_GetMechanismInfo(pSlotList[i], wanted_mech, &info); if (rv == CKR_OK && (info.flags & wanted_flags) == wanted_flags) break; } if (i < pulCount) { rv = C_OpenSession(pSlotList[i], CKF_SERIAL_SESSION, NULL, NULL, sessionp); if (rv != CKR_OK) { kmf_rv = KMF_ERR_UNINITIALIZED; } } else { kmf_rv = KMF_ERR_UNINITIALIZED; } out: if (pSlotList != NULL) free(pSlotList); return (kmf_rv); } /* * Name: PKCS_AddTemplate * * Description: * Adds a CK_ATTRIBUTE value to an existing array of CK_ATTRIBUTES. Will * not expand the array beyond the maximum specified size. * * Returns: * TRUE - Attribute value succesfully added. * FALSE - Maximum array size would be exceded. */ static int PKCS_AddTemplate(CK_ATTRIBUTE *pTemplate, CK_ULONG *ckNumTemplates, CK_ULONG ckMaxTemplates, CK_ATTRIBUTE_TYPE ckAttribCode, CK_BYTE * pckBuffer, CK_ULONG ckBufferLen) { if (*ckNumTemplates >= ckMaxTemplates) { return (FALSE); } pTemplate[*ckNumTemplates].type = ckAttribCode; pTemplate[*ckNumTemplates].pValue = pckBuffer; pTemplate[*ckNumTemplates].ulValueLen = ckBufferLen; (*ckNumTemplates)++; return (TRUE); } /* * Convert an SPKI data record to PKCS#11 * public key object. */ static KMF_RETURN PKCS_CreatePublicKey( const KMF_X509_SPKI *pKey, CK_SESSION_HANDLE ckSession, CK_OBJECT_HANDLE *pckPublicKey) { KMF_RETURN mrReturn = KMF_OK; CK_RV ckRv; CK_ATTRIBUTE ckTemplate[MAX_PUBLIC_KEY_TEMPLATES]; CK_ULONG ckNumTemplates = 0; /* Common object attributes */ CK_OBJECT_CLASS ckObjClass = CKO_PUBLIC_KEY; CK_BBOOL ckToken = 0; CK_BBOOL ckPrivate = 0; /* Common key attributes */ CK_KEY_TYPE ckKeyType; CK_BBOOL ckDerive = CK_FALSE; /* Common public key attributes */ CK_BBOOL ckEncrypt = 1; CK_BBOOL ckVerify = 1; CK_BBOOL ckVerifyRecover = CK_FALSE; CK_BBOOL ckWrap = CK_FALSE; /* Key part array */ KMF_DATA KeyParts[KMF_MAX_PUBLIC_KEY_PARTS]; uint32_t i, uNumKeyParts = KMF_MAX_PUBLIC_KEY_PARTS; KMF_ALGORITHM_INDEX AlgorithmId; /* Parse the keyblob */ (void) memset(KeyParts, 0, sizeof (KeyParts)); AlgorithmId = x509_algoid_to_algid( (KMF_OID *)&pKey->algorithm.algorithm); if (AlgorithmId == KMF_ALGID_NONE) return (KMF_ERR_BAD_ALGORITHM); mrReturn = ExtractSPKIData(pKey, AlgorithmId, KeyParts, &uNumKeyParts); if (mrReturn != KMF_OK) return (mrReturn); /* Fill in the common object attributes */ if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_CLASS, (CK_BYTE *)&ckObjClass, sizeof (ckObjClass)) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_TOKEN, (CK_BYTE *)&ckToken, sizeof (ckToken)) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_PRIVATE, (CK_BYTE *)&ckPrivate, sizeof (ckPrivate))) { mrReturn = KMF_ERR_INTERNAL; goto cleanup; } /* Fill in the common key attributes */ if (get_pk11_data(AlgorithmId, &ckKeyType, NULL, NULL, 0)) { goto cleanup; } if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_KEY_TYPE, (CK_BYTE *)&ckKeyType, sizeof (ckKeyType)) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_DERIVE, (CK_BYTE *)&ckDerive, sizeof (ckDerive))) { mrReturn = KMF_ERR_INTERNAL; goto cleanup; } /* Add common public key attributes */ if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_ENCRYPT, (CK_BYTE *)&ckEncrypt, sizeof (ckEncrypt)) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_VERIFY, (CK_BYTE *)&ckVerify, sizeof (ckVerify)) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_VERIFY_RECOVER, (CK_BYTE *)&ckVerifyRecover, sizeof (ckVerifyRecover)) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_WRAP, (CK_BYTE *)&ckWrap, sizeof (ckWrap))) { mrReturn = KMF_ERR_INTERNAL; goto cleanup; } /* Add algorithm specific attributes */ switch (ckKeyType) { case CKK_RSA: if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_MODULUS, (CK_BYTE *)KeyParts[KMF_RSA_MODULUS].Data, (CK_ULONG)KeyParts[KMF_RSA_MODULUS].Length) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_PUBLIC_EXPONENT, (CK_BYTE *)KeyParts[KMF_RSA_PUBLIC_EXPONENT].Data, (CK_ULONG)KeyParts[KMF_RSA_PUBLIC_EXPONENT].Length)) { mrReturn = KMF_ERR_INTERNAL; goto cleanup; } break; case CKK_DSA: if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_PRIME, (CK_BYTE *)KeyParts[KMF_DSA_PRIME].Data, (CK_ULONG)KeyParts[KMF_DSA_PRIME].Length) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_SUBPRIME, (CK_BYTE *)KeyParts[KMF_DSA_SUB_PRIME].Data, (CK_ULONG)KeyParts[KMF_DSA_SUB_PRIME].Length) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_BASE, (CK_BYTE *)KeyParts[KMF_DSA_BASE].Data, (CK_ULONG)KeyParts[KMF_DSA_BASE].Length) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_VALUE, (CK_BYTE *)KeyParts[KMF_DSA_PUBLIC_VALUE].Data, (CK_ULONG)KeyParts[KMF_DSA_PUBLIC_VALUE].Length)) { mrReturn = KMF_ERR_INTERNAL; goto cleanup; } break; case CKK_EC: if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_EC_POINT, (CK_BYTE *)KeyParts[KMF_ECDSA_POINT].Data, (CK_ULONG)KeyParts[KMF_ECDSA_POINT].Length) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, MAX_PUBLIC_KEY_TEMPLATES, CKA_EC_PARAMS, (CK_BYTE *)KeyParts[KMF_ECDSA_PARAMS].Data, (CK_ULONG)KeyParts[KMF_ECDSA_PARAMS].Length)) { mrReturn = KMF_ERR_INTERNAL; goto cleanup; } break; default: mrReturn = KMF_ERR_BAD_PARAMETER; } if (mrReturn == KMF_OK) { /* Instantiate the object */ ckRv = C_CreateObject(ckSession, ckTemplate, ckNumTemplates, pckPublicKey); if (ckRv != CKR_OK) mrReturn = KMF_ERR_INTERNAL; } cleanup: for (i = 0; i < uNumKeyParts; i++) { kmf_free_data(&KeyParts[i]); } return (mrReturn); } /* * PKCS_AcquirePublicKeyHandle * * Given an assymetric key keyblob, attempts to find the appropriate * public key. * * Methods of finding the public key: * - Public Key with data present: * Parses the key and creates a temporary session object. * - Public Key with handle: * The handle is type converted and returned. Validity of the handle is * not checked. * - Public Key with label: * Attempts to find a public key with the corresponding label. */ static KMF_RETURN PKCS_AcquirePublicKeyHandle(CK_SESSION_HANDLE ckSession, const KMF_X509_SPKI *pKey, CK_KEY_TYPE ckRequestedKeyType, CK_OBJECT_HANDLE *pckKeyHandle) { KMF_RETURN mrReturn = KMF_OK; /* Key searching variables */ CK_OBJECT_HANDLE ckKeyHandle = 0; CK_OBJECT_CLASS ckObjClass; CK_KEY_TYPE ckKeyType; CK_ATTRIBUTE ckTemplate[3]; CK_ULONG ckNumTemplates; static const CK_ULONG ckMaxTemplates = (sizeof (ckTemplate) / sizeof (CK_ATTRIBUTE)); CK_RV ckRv; /* Extract the data from the SPKI into individual fields */ mrReturn = PKCS_CreatePublicKey(pKey, ckSession, &ckKeyHandle); if (mrReturn != KMF_OK) return (mrReturn); /* Fetch the key class and algorithm from the object */ ckNumTemplates = 0; if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, ckMaxTemplates, CKA_CLASS, (CK_BYTE *)&ckObjClass, sizeof (ckObjClass)) || !PKCS_AddTemplate(ckTemplate, &ckNumTemplates, ckMaxTemplates, CKA_KEY_TYPE, (CK_BYTE *)&ckKeyType, sizeof (ckKeyType))) { (void) C_DestroyObject(ckSession, ckKeyHandle); return (KMF_ERR_INTERNAL); } ckRv = C_GetAttributeValue(ckSession, ckKeyHandle, ckTemplate, ckNumTemplates); if (ckRv != CKR_OK) { (void) C_DestroyObject(ckSession, ckKeyHandle); return (ckRv); } /* Make sure the results match the expected values */ if ((ckKeyType != ckRequestedKeyType) || (ckObjClass != CKO_PUBLIC_KEY)) { (void) C_DestroyObject(ckSession, ckKeyHandle); return (KMF_ERR_BAD_KEY_FORMAT); } /* Set the return values */ *pckKeyHandle = ckKeyHandle; return (KMF_OK); } /* * Utility routine for verifying generic data using * the cryptographic framework (PKCS#11). * There are situations where we want to force this * operation to happen in a specific keystore. * For example: * libelfsign.so.1 verifies signatures on crypto libraries. * We must use pkcs11 functions to verify the pkcs11 * plugins in order to keep the validation within the * Cryptographic Framework's FIPS-140 boundary. To avoid * a circular dependency, pksc11_softtoken.so.1 is * interposed by libkcfd.so.1 via kcfd, which prevents * libpkcs11.so.1's interfaces from being used when libkmf.so.1 * is called from kcfd. * * This also saves code and time because verify operations * only use public keys and do not need acccess to any * keystore specific functions. */ KMF_RETURN PKCS_VerifyData(KMF_HANDLE_T handle, KMF_ALGORITHM_INDEX AlgorithmId, KMF_X509_SPKI *keyp, KMF_DATA *data, KMF_DATA *signature) { KMF_RETURN rv = KMF_OK; CK_RV ckRv; KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; CK_MECHANISM ckMechanism; CK_MECHANISM_TYPE mechtype, hashmech; CK_OBJECT_HANDLE ckKeyHandle = 0; CK_KEY_TYPE pk11keytype; CK_SESSION_HANDLE ckSession = 0; CK_ATTRIBUTE subprime = { CKA_SUBPRIME, NULL, 0 }; CK_BYTE *dataptr; CK_ULONG datalen; KMF_DATA hashData = { 0, NULL }; uchar_t digest[1024]; if (AlgorithmId == KMF_ALGID_NONE) return (KMF_ERR_BAD_ALGORITHM); if (get_pk11_data(AlgorithmId, &pk11keytype, &mechtype, &hashmech, 1)) return (KMF_ERR_BAD_ALGORITHM); /* * Verify in metaslot/softtoken since only the public key is needed * and not all hardware tokens support the combined [hash]-RSA/DSA/EC * mechanisms. */ rv = kmf_create_pk11_session(&ckSession, mechtype, 0); if (rv != KMF_OK) return (rv); /* Fetch the verifying key */ rv = PKCS_AcquirePublicKeyHandle(ckSession, keyp, pk11keytype, &ckKeyHandle); if (rv != KMF_OK) { (void) C_CloseSession(ckSession); return (rv); } dataptr = data->Data; datalen = data->Length; /* * For some mechanisms, we must compute the hash separately * and then do the verify. */ if (hashmech != 0 && (mechtype == CKM_ECDSA || mechtype == CKM_DSA || mechtype == CKM_RSA_PKCS)) { hashData.Data = digest; hashData.Length = sizeof (digest); rv = PKCS_DigestData(handle, ckSession, hashmech, data, &hashData, (mechtype == CKM_RSA_PKCS)); if (rv) goto cleanup; dataptr = hashData.Data; datalen = hashData.Length; } if (mechtype == CKM_DSA && hashmech == CKM_SHA256) { /* * FIPS 186-3 says that when using DSA * the hash must be truncated to the size of the * subprime. */ ckRv = C_GetAttributeValue(ckSession, ckKeyHandle, &subprime, 1); if (ckRv != CKR_OK) { kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; rv = KMF_ERR_INTERNAL; goto cleanup; } datalen = subprime.ulValueLen; } ckMechanism.mechanism = mechtype; ckMechanism.pParameter = NULL; ckMechanism.ulParameterLen = 0; ckRv = C_VerifyInit(ckSession, &ckMechanism, ckKeyHandle); if (ckRv != CKR_OK) { kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; rv = KMF_ERR_INTERNAL; goto cleanup; } ckRv = C_Verify(ckSession, dataptr, datalen, (CK_BYTE *)signature->Data, (CK_ULONG)signature->Length); if (ckRv != CKR_OK) { kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; rv = KMF_ERR_INTERNAL; } cleanup: if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); (void) C_CloseSession(ckSession); return (rv); } KMF_RETURN PKCS_EncryptData(KMF_HANDLE_T kmfh, KMF_ALGORITHM_INDEX AlgorithmId, KMF_X509_SPKI *keyp, KMF_DATA *plaintext, KMF_DATA *ciphertext) { KMF_RETURN rv = KMF_OK; CK_RV ckRv; CK_MECHANISM ckMechanism; CK_MECHANISM_TYPE mechtype; CK_KEY_TYPE keytype; CK_OBJECT_HANDLE ckKeyHandle = 0; CK_SESSION_HANDLE ckSession = 0; CK_ULONG out_len = 0, in_len = 0, total_encrypted = 0; uint8_t *in_data, *out_data; int i, blocks, block_size; CK_ATTRIBUTE ckTemplate[2]; CK_ULONG ckNumTemplates; CK_ULONG ckMaxTemplates = (sizeof (ckTemplate) / sizeof (CK_ATTRIBUTE)); if (get_pk11_data(AlgorithmId, &keytype, &mechtype, NULL, 0)) return (KMF_ERR_BAD_ALGORITHM); rv = kmf_create_pk11_session(&ckSession, mechtype, CKF_ENCRYPT); if (rv != KMF_OK) return (rv); /* Get the public key used in encryption */ rv = PKCS_AcquirePublicKeyHandle(ckSession, keyp, keytype, &ckKeyHandle); if (rv != KMF_OK) { (void) C_CloseSession(ckSession); return (rv); } /* Get the modulus length */ ckNumTemplates = 0; if (!PKCS_AddTemplate(ckTemplate, &ckNumTemplates, ckMaxTemplates, CKA_MODULUS, (CK_BYTE *)NULL, sizeof (CK_ULONG))) { if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); (void) C_CloseSession(ckSession); return (KMF_ERR_INTERNAL); } ckRv = C_GetAttributeValue(ckSession, ckKeyHandle, ckTemplate, ckNumTemplates); if (ckRv != CKR_OK) { if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; (void) C_CloseSession(ckSession); return (KMF_ERR_INTERNAL); } out_len = ckTemplate[0].ulValueLen; if (out_len > ciphertext->Length) { if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); (void) C_CloseSession(ckSession); return (KMF_ERR_BUFFER_SIZE); } ckMechanism.mechanism = mechtype; ckMechanism.pParameter = NULL_PTR; ckMechanism.ulParameterLen = 0; /* Compute the fixed input data length for single-part encryption */ block_size = out_len - 11; in_data = plaintext->Data; out_data = ciphertext->Data; blocks = plaintext->Length/block_size; for (i = 0; i < blocks; i++) { ckRv = C_EncryptInit(ckSession, &ckMechanism, ckKeyHandle); if (ckRv != CKR_OK) { if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; (void) C_CloseSession(ckSession); return (KMF_ERR_INTERNAL); } ckRv = C_Encrypt(ckSession, (CK_BYTE_PTR)in_data, block_size, (CK_BYTE_PTR)out_data, &out_len); if (ckRv != CKR_OK) { if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; (void) C_CloseSession(ckSession); return (KMF_ERR_INTERNAL); } out_data += out_len; total_encrypted += out_len; in_data += block_size; } if (plaintext->Length % block_size) { /* Encrypt the remaining data */ ckRv = C_EncryptInit(ckSession, &ckMechanism, ckKeyHandle); if (ckRv != CKR_OK) { if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; (void) C_CloseSession(ckSession); return (KMF_ERR_INTERNAL); } in_len = plaintext->Length % block_size; ckRv = C_Encrypt(ckSession, (CK_BYTE_PTR)in_data, in_len, (CK_BYTE_PTR)out_data, &out_len); if (ckRv != CKR_OK) { if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckRv; (void) C_CloseSession(ckSession); return (KMF_ERR_INTERNAL); } out_data += out_len; total_encrypted += out_len; in_data += in_len; } ciphertext->Length = total_encrypted; if (ckKeyHandle != 0) (void) C_DestroyObject(ckSession, ckKeyHandle); (void) C_CloseSession(ckSession); return (rv); } static void create_id_hash(KMF_DATA *IDInput, KMF_DATA *IDOutput) { SHA1_CTX ctx; SHA1Init(&ctx); SHA1Update(&ctx, IDInput->Data, IDInput->Length); SHA1Final(IDOutput->Data, &ctx); IDOutput->Length = SHA1_DIGEST_LENGTH; } KMF_RETURN GetIDFromSPKI(KMF_X509_SPKI *spki, KMF_DATA *ID) { KMF_RETURN rv = KMF_OK; KMF_DATA KeyParts[KMF_MAX_PUBLIC_KEY_PARTS]; uint32_t uNumKeyParts = KMF_MAX_PUBLIC_KEY_PARTS; KMF_ALGORITHM_INDEX algId; int i; if (ID == NULL || spki == NULL) return (KMF_ERR_BAD_PARAMETER); ID->Data = (uchar_t *)malloc(SHA1_HASH_LENGTH); if (ID->Data == NULL) return (KMF_ERR_MEMORY); ID->Length = SHA1_HASH_LENGTH; algId = x509_algoid_to_algid(&spki->algorithm.algorithm); if (algId == KMF_ALGID_NONE) return (KMF_ERR_BAD_ALGORITHM); rv = ExtractSPKIData(spki, algId, KeyParts, &uNumKeyParts); if (rv != KMF_OK) return (rv); /* Check the KEY algorithm */ if (algId == KMF_ALGID_RSA) { create_id_hash(&KeyParts[KMF_RSA_MODULUS], ID); } else if (algId == KMF_ALGID_DSA) { create_id_hash(&KeyParts[KMF_DSA_PUBLIC_VALUE], ID); } else if (algId == KMF_ALGID_SHA1WithECDSA || algId == KMF_ALGID_ECDSA) { create_id_hash(&KeyParts[KMF_ECDSA_POINT], ID); } else { /* We only support RSA and DSA keys for now */ rv = KMF_ERR_BAD_ALGORITHM; } for (i = 0; i < uNumKeyParts; i++) { if (KeyParts[i].Data != NULL) free(KeyParts[i].Data); } if (rv != KMF_OK && ID->Data != NULL) { free(ID->Data); ID->Data = NULL; ID->Length = 0; } return (rv); } /* * For PKCS1 encoding (necessary for RSA signatures), we * must prepend the following prefixes before computing * the digest. */ static uchar_t SHA1_DER_PREFIX[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static uchar_t MD5_DER_PREFIX[] = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; static uchar_t SHA256_DER_PREFIX[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; static uchar_t SHA384_DER_PREFIX[] = {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; static uchar_t SHA512_DER_PREFIX[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}; #define MAX_SHA2_DIGEST_LENGTH 64 /* * Compute hashes using metaslot (or softtoken). * Not all hardware tokens support the combined HASH + RSA/EC * Signing operations so it is safer to separate the hashing * from the signing. This function generates a hash using a * separate session. The resulting digest can be signed later. */ KMF_RETURN PKCS_DigestData(KMF_HANDLE_T handle, CK_SESSION_HANDLE hSession, CK_MECHANISM_TYPE mechtype, KMF_DATA *tobesigned, KMF_DATA *output, boolean_t pkcs1_encoding) { KMF_RETURN rv = KMF_OK; CK_RV ckrv; CK_MECHANISM mechanism; KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; CK_BYTE outbuf[MAX_SHA2_DIGEST_LENGTH + sizeof (SHA512_DER_PREFIX)]; CK_ULONG outlen = sizeof (outbuf); mechanism.mechanism = mechtype; mechanism.pParameter = NULL; mechanism.ulParameterLen = 0; ckrv = C_DigestInit(hSession, &mechanism); if (ckrv != CKR_OK) { kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckrv; rv = KMF_ERR_INTERNAL; goto end; } ckrv = C_Digest(hSession, tobesigned->Data, tobesigned->Length, outbuf, &outlen); if (ckrv != CKR_OK) { kmfh->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; kmfh->lasterr.errcode = ckrv; rv = KMF_ERR_INTERNAL; } if (pkcs1_encoding) { uchar_t *pfx; int pfxlen; switch (mechtype) { case CKM_MD5: pfx = MD5_DER_PREFIX; pfxlen = sizeof (MD5_DER_PREFIX); break; case CKM_SHA_1: pfx = SHA1_DER_PREFIX; pfxlen = sizeof (SHA1_DER_PREFIX); break; case CKM_SHA256: pfx = SHA256_DER_PREFIX; pfxlen = sizeof (SHA256_DER_PREFIX); break; case CKM_SHA384: pfx = SHA384_DER_PREFIX; pfxlen = sizeof (SHA384_DER_PREFIX); break; case CKM_SHA512: pfx = SHA512_DER_PREFIX; pfxlen = sizeof (SHA512_DER_PREFIX); break; default: rv = KMF_ERR_BAD_ALGORITHM; goto end; } (void) memcpy(output->Data, pfx, pfxlen); (void) memcpy(output->Data + pfxlen, outbuf, outlen); output->Length = outlen + pfxlen; } else { (void) memcpy(output->Data, outbuf, outlen); output->Length = outlen; } end: return (rv); }