blob: 2c2407e49fbc01b47cb2202ed4038e3cf1924aca [file] [log] [blame]
/* Copyright 2023 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <dlfcn.h>
#include <nss/pkcs11.h>
#include "2common.h"
#include "host_p11.h"
#include "vboot_host.h"
#include "util_misc.h"
// We only maintain one global p11 module at a time.
static CK_FUNCTION_LIST_PTR p11 = NULL;
static void *pkcs11_load(const char *mspec, CK_FUNCTION_LIST_PTR_PTR funcs)
{
void *mod;
CK_RV rv;
CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR p11);
if (mspec == NULL)
return NULL;
mod = dlopen(mspec, RTLD_LAZY);
if (mod == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
return NULL;
}
/* Get the list of function pointers */
c_get_function_list =
(CK_RV(*)(CK_FUNCTION_LIST_PTR_PTR))dlsym(mod, "C_GetFunctionList");
if (!c_get_function_list)
goto err;
rv = c_get_function_list(funcs);
if (rv == CKR_OK)
return mod;
fprintf(stderr, "C_GetFunctionList failed 0x%lx", rv);
err:
dlclose(mod);
return NULL;
}
static vb2_error_t pkcs11_find(CK_SESSION_HANDLE session, CK_ATTRIBUTE attributes[],
CK_ULONG num_attributes, CK_OBJECT_HANDLE *object)
{
CK_RV result = p11->C_FindObjectsInit(session, attributes, num_attributes);
if (result != CKR_OK)
return VB2_ERROR_UNKNOWN;
CK_ULONG object_count = 1;
result = p11->C_FindObjects(session, object, 1, &object_count);
if (result != CKR_OK || object_count == 0)
return VB2_ERROR_UNKNOWN;
result = p11->C_FindObjectsFinal(session);
if (result != CKR_OK)
return VB2_ERROR_UNKNOWN;
return VB2_SUCCESS;
}
static enum vb2_hash_algorithm
pkcs11_mechanism_type_to_hash_alg(CK_MECHANISM_TYPE p11_mechanism)
{
switch (p11_mechanism) {
case CKM_SHA1_RSA_PKCS:
return VB2_HASH_SHA1;
case CKM_SHA256_RSA_PKCS:
return VB2_HASH_SHA256;
case CKM_SHA512_RSA_PKCS:
return VB2_HASH_SHA512;
}
return VB2_HASH_INVALID;
}
vb2_error_t pkcs11_init(const char *pkcs11_lib)
{
static char *loaded_pkcs11_lib = NULL;
static void *pkcs11_mod = NULL;
if (pkcs11_lib == NULL) {
fprintf(stderr, "Missing the path of pkcs11 library\n");
return VB2_ERROR_UNKNOWN;
}
if (loaded_pkcs11_lib) {
/* Return success if the same pkcs11 library is already loaded */
if (strcmp(loaded_pkcs11_lib, pkcs11_lib) == 0)
return VB2_SUCCESS;
fprintf(stderr, "Pkcs11 module is already loaded\n");
return VB2_ERROR_UNKNOWN;
}
pkcs11_mod = pkcs11_load(pkcs11_lib, &p11);
if (pkcs11_mod == NULL) {
fprintf(stderr, "Failed to load pkcs11 library '%s'\n", pkcs11_lib);
return VB2_ERROR_UNKNOWN;
}
CK_RV result = p11->C_Initialize(NULL);
if (result != CKR_OK) {
fprintf(stderr, "Failed to C_Initialize\n");
dlclose(pkcs11_mod);
pkcs11_mod = NULL;
return VB2_ERROR_UNKNOWN;
}
loaded_pkcs11_lib = strdup(pkcs11_lib);
return VB2_SUCCESS;
}
vb2_error_t pkcs11_get_key(int slot_id, char *label, struct pkcs11_key *p11_key)
{
if (!p11) {
fprintf(stderr, "pkcs11 is not loaded\n");
return VB2_ERROR_UNKNOWN;
}
CK_RV result = p11->C_OpenSession(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL,
NULL, &p11_key->session);
if (result != CKR_OK) {
fprintf(stderr, "Failed to open session with slot id %d\n", slot_id);
return VB2_ERROR_UNKNOWN;
}
/* Find the private key */
CK_OBJECT_CLASS class_value = CKO_PRIVATE_KEY;
CK_ATTRIBUTE attributes[] = {
{CKA_CLASS, &class_value, sizeof(class_value)},
{CKA_LABEL, label, strlen(label)},
};
if (pkcs11_find(p11_key->session, attributes, ARRAY_SIZE(attributes),
&p11_key->handle) != VB2_SUCCESS) {
fprintf(stderr, "Failed to find the key with label '%s'\n", label);
return VB2_ERROR_UNKNOWN;
}
return VB2_SUCCESS;
}
enum vb2_hash_algorithm pkcs11_get_hash_alg(struct pkcs11_key *p11_key)
{
/* For PKCS#11 modules that support CKA_ALLOWED_MECHANISMS, we'll use the attribute
* to determine the correct mechanism to use. However, not all PKCS#11 modules
* support CKA_ALLOWED_MECHANISMS. In the event that we need to support such a
* module, we'll then need to determine the the mechanism to use from the key type
* and key size. That probably involves assuming we'll use PKCS#1 v1.5 padding for
* RSA. */
CK_ATTRIBUTE mechanism_attr = {CKA_ALLOWED_MECHANISMS, NULL, 0};
if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &mechanism_attr, 1) !=
CKR_OK) {
fprintf(stderr, "Failed to get mechanisum attribute length\n");
return VB2_HASH_INVALID;
}
mechanism_attr.pValue = malloc(mechanism_attr.ulValueLen);
if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &mechanism_attr, 1) !=
CKR_OK) {
fprintf(stderr, "Failed to get mechanisum attribute value\n");
free(mechanism_attr.pValue);
return VB2_HASH_INVALID;
}
CK_MECHANISM_TYPE *mechanisms = mechanism_attr.pValue;
uint32_t mechanism_count = mechanism_attr.ulValueLen / sizeof(CK_MECHANISM_TYPE);
enum vb2_hash_algorithm hash_alg = VB2_HASH_INVALID;
for (int i = 0; i < mechanism_count; ++i) {
hash_alg = pkcs11_mechanism_type_to_hash_alg(mechanisms[i]);
if (hash_alg != VB2_HASH_INVALID)
break;
}
free(mechanism_attr.pValue);
return hash_alg;
}
enum vb2_signature_algorithm pkcs11_get_sig_alg(struct pkcs11_key *p11_key)
{
if (!p11) {
fprintf(stderr, "pkcs11 is not loaded\n");
return VB2_SIG_INVALID;
}
CK_ULONG modulus_bits = 0;
CK_ATTRIBUTE modulus_attr = {CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits)};
if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &modulus_attr, 1) !=
CKR_OK) {
fprintf(stderr, "Failed to get modulus bits\n");
return VB2_SIG_INVALID;
}
CK_ATTRIBUTE exponent_attr = {CKA_PUBLIC_EXPONENT, NULL, 0};
if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &exponent_attr, 1) !=
CKR_OK) {
fprintf(stderr, "Failed to get exponent attribute length\n");
return VB2_SIG_INVALID;
}
CK_ULONG exp_size = exponent_attr.ulValueLen;
if (exp_size > 4) {
fprintf(stderr, "Exponent size is too large\n");
return VB2_SIG_INVALID;
}
exponent_attr.pValue = malloc(exp_size);
if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &exponent_attr, 1) !=
CKR_OK) {
fprintf(stderr, "Failed to get exponent attribute value\n");
free(exponent_attr.pValue);
return VB2_SIG_INVALID;
}
// Parse the CKA_PUBLIC_EXPONENT in Big-endian.
CK_BYTE *exp_value = exponent_attr.pValue;
uint32_t exp = 0;
for (int i = 0; i < exp_size; ++i)
exp = (exp << 8) + exp_value[i];
free(exponent_attr.pValue);
return vb2_get_sig_alg(exp, modulus_bits);
}
vb2_error_t pkcs11_sign(struct pkcs11_key *p11_key, enum vb2_hash_algorithm hash_alg,
const uint8_t *data, int data_size, uint8_t *sig, CK_ULONG sig_size)
{
if (!p11) {
fprintf(stderr, "pkcs11 is not loaded\n");
return VB2_ERROR_UNKNOWN;
}
CK_MECHANISM mechanism;
switch (hash_alg) {
case VB2_HASH_SHA1:
mechanism.mechanism = CKM_SHA1_RSA_PKCS;
break;
case VB2_HASH_SHA256:
mechanism.mechanism = CKM_SHA256_RSA_PKCS;
break;
case VB2_HASH_SHA512:
mechanism.mechanism = CKM_SHA512_RSA_PKCS;
break;
default:
fprintf(stderr, "Unsupported hash algorithm %d\n", hash_alg);
return VB2_ERROR_UNKNOWN;
}
mechanism.pParameter = NULL;
mechanism.ulParameterLen = 0;
CK_RV result = p11->C_SignInit(p11_key->session, &mechanism, p11_key->handle);
if (result != CKR_OK) {
fprintf(stderr, "Failed to sign init\n");
return VB2_ERROR_UNKNOWN;
}
result =
p11->C_Sign(p11_key->session, (unsigned char *)data, data_size, sig, &sig_size);
if (result != CKR_OK) {
fprintf(stderr, "Failed to sign\n");
return VB2_ERROR_UNKNOWN;
}
return VB2_SUCCESS;
}
void pkcs11_free_key(struct pkcs11_key *p11_key)
{
if (!p11) {
fprintf(stderr, "pkcs11 is not loaded\n");
return;
}
CK_RV result = p11->C_CloseSession(p11_key->session);
if (result != CKR_OK)
fprintf(stderr, "Failed to close session\n");
free(p11_key);
}