xref: /aosp_15_r20/external/vboot_reference/host/lib/host_p11.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2023 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <dlfcn.h>
11 
12 #include <pkcs11.h>
13 
14 #include "2common.h"
15 #include "host_p11.h"
16 #include "vboot_host.h"
17 #include "util_misc.h"
18 
19 struct pkcs11_key {
20 	CK_OBJECT_HANDLE handle;
21 	CK_SESSION_HANDLE session;
22 };
23 
24 // We only maintain one global p11 module at a time.
25 static CK_FUNCTION_LIST_PTR p11 = NULL;
26 
pkcs11_load(const char * mspec,CK_FUNCTION_LIST_PTR_PTR funcs)27 static void *pkcs11_load(const char *mspec, CK_FUNCTION_LIST_PTR_PTR funcs)
28 {
29 	void *mod;
30 	CK_RV rv;
31 	CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR p11);
32 
33 	if (mspec == NULL)
34 		return NULL;
35 
36 	mod = dlopen(mspec, RTLD_LAZY);
37 	if (mod == NULL) {
38 		fprintf(stderr, "dlopen failed: %s\n", dlerror());
39 		return NULL;
40 	}
41 
42 	/* Get the list of function pointers */
43 	c_get_function_list =
44 		(CK_RV(*)(CK_FUNCTION_LIST_PTR_PTR))dlsym(mod, "C_GetFunctionList");
45 	if (!c_get_function_list)
46 		goto err;
47 	rv = c_get_function_list(funcs);
48 	if (rv == CKR_OK)
49 		return mod;
50 	fprintf(stderr, "C_GetFunctionList failed 0x%lx", rv);
51 err:
52 	dlclose(mod);
53 	return NULL;
54 }
55 
pkcs11_find(CK_SESSION_HANDLE session,CK_ATTRIBUTE attributes[],CK_ULONG num_attributes,CK_OBJECT_HANDLE * object)56 static vb2_error_t pkcs11_find(CK_SESSION_HANDLE session, CK_ATTRIBUTE attributes[],
57 			       CK_ULONG num_attributes, CK_OBJECT_HANDLE *object)
58 {
59 	CK_RV result = p11->C_FindObjectsInit(session, attributes, num_attributes);
60 	if (result != CKR_OK)
61 		return VB2_ERROR_UNKNOWN;
62 
63 	CK_ULONG object_count = 1;
64 	result = p11->C_FindObjects(session, object, 1, &object_count);
65 	if (result != CKR_OK || object_count == 0)
66 		return VB2_ERROR_UNKNOWN;
67 
68 	result = p11->C_FindObjectsFinal(session);
69 	if (result != CKR_OK)
70 		return VB2_ERROR_UNKNOWN;
71 
72 	return VB2_SUCCESS;
73 }
74 
75 static enum vb2_hash_algorithm
pkcs11_mechanism_type_to_hash_alg(CK_MECHANISM_TYPE p11_mechanism)76 pkcs11_mechanism_type_to_hash_alg(CK_MECHANISM_TYPE p11_mechanism)
77 {
78 	switch (p11_mechanism) {
79 	case CKM_SHA1_RSA_PKCS:
80 		return VB2_HASH_SHA1;
81 	case CKM_SHA256_RSA_PKCS:
82 		return VB2_HASH_SHA256;
83 	case CKM_SHA512_RSA_PKCS:
84 		return VB2_HASH_SHA512;
85 	}
86 	return VB2_HASH_INVALID;
87 }
88 
pkcs11_init(const char * pkcs11_lib)89 vb2_error_t pkcs11_init(const char *pkcs11_lib)
90 {
91 	static char *loaded_pkcs11_lib = NULL;
92 	static void *pkcs11_mod = NULL;
93 	if (pkcs11_lib == NULL) {
94 		fprintf(stderr, "Missing the path of pkcs11 library\n");
95 		return VB2_ERROR_UNKNOWN;
96 	}
97 	if (loaded_pkcs11_lib) {
98 		/* Return success if the same pkcs11 library is already loaded */
99 		if (strcmp(loaded_pkcs11_lib, pkcs11_lib) == 0)
100 			return VB2_SUCCESS;
101 		fprintf(stderr, "Pkcs11 module is already loaded\n");
102 		return VB2_ERROR_UNKNOWN;
103 	}
104 
105 	pkcs11_mod = pkcs11_load(pkcs11_lib, &p11);
106 	if (pkcs11_mod == NULL) {
107 		fprintf(stderr, "Failed to load pkcs11 library '%s'\n", pkcs11_lib);
108 		return VB2_ERROR_UNKNOWN;
109 	}
110 
111 	CK_RV result = p11->C_Initialize(NULL);
112 	if (result != CKR_OK) {
113 		fprintf(stderr, "Failed to C_Initialize\n");
114 		dlclose(pkcs11_mod);
115 		pkcs11_mod = NULL;
116 		return VB2_ERROR_UNKNOWN;
117 	}
118 	loaded_pkcs11_lib = strdup(pkcs11_lib);
119 	return VB2_SUCCESS;
120 }
121 
pkcs11_get_key(int slot_id,char * label)122 struct pkcs11_key *pkcs11_get_key(int slot_id, char *label)
123 {
124 	if (!p11) {
125 		fprintf(stderr, "pkcs11 is not loaded\n");
126 		return NULL;
127 	}
128 
129 	struct pkcs11_key *p11_key = malloc(sizeof(struct pkcs11_key));
130 	if (!p11_key) {
131 		fprintf(stderr, "Failed to allocate pkcs11 key\n");
132 		return NULL;
133 	}
134 
135 	CK_RV result = p11->C_OpenSession(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL,
136 					  NULL, &p11_key->session);
137 
138 	if (result != CKR_OK) {
139 		fprintf(stderr, "Failed to open session with slot id %d\n", slot_id);
140 		free(p11_key);
141 		return NULL;
142 	}
143 
144 	/* Find the private key */
145 	CK_OBJECT_CLASS class_value = CKO_PRIVATE_KEY;
146 	CK_ATTRIBUTE attributes[] = {
147 		{CKA_CLASS, &class_value, sizeof(class_value)},
148 		{CKA_LABEL, label, strlen(label)},
149 	};
150 	if (pkcs11_find(p11_key->session, attributes, ARRAY_SIZE(attributes),
151 			&p11_key->handle) != VB2_SUCCESS) {
152 		fprintf(stderr, "Failed to find the key with label '%s'\n", label);
153 		pkcs11_free_key(p11_key);
154 		return NULL;
155 	}
156 
157 	return p11_key;
158 }
159 
pkcs11_get_hash_alg(struct pkcs11_key * p11_key)160 enum vb2_hash_algorithm pkcs11_get_hash_alg(struct pkcs11_key *p11_key)
161 {
162 	/* For PKCS#11 modules that support CKA_ALLOWED_MECHANISMS, we'll use the attribute
163 	 * to determine the correct mechanism to use. However, not all PKCS#11 modules
164 	 * support CKA_ALLOWED_MECHANISMS. In the event that we need to support such a
165 	 * module, we'll then need to determine the the mechanism to use from the key type
166 	 * and key size. That probably involves assuming we'll use PKCS#1 v1.5 padding for
167 	 * RSA. */
168 	CK_ATTRIBUTE mechanism_attr = {CKA_ALLOWED_MECHANISMS, NULL, 0};
169 	if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &mechanism_attr, 1) !=
170 	    CKR_OK) {
171 		fprintf(stderr, "Failed to get mechanisum attribute length\n");
172 		return VB2_HASH_INVALID;
173 	}
174 	mechanism_attr.pValue = malloc(mechanism_attr.ulValueLen);
175 	if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &mechanism_attr, 1) !=
176 	    CKR_OK) {
177 		fprintf(stderr, "Failed to get mechanisum attribute value\n");
178 		free(mechanism_attr.pValue);
179 		return VB2_HASH_INVALID;
180 	}
181 	CK_MECHANISM_TYPE *mechanisms = mechanism_attr.pValue;
182 	uint32_t mechanism_count = mechanism_attr.ulValueLen / sizeof(CK_MECHANISM_TYPE);
183 	enum vb2_hash_algorithm hash_alg = VB2_HASH_INVALID;
184 	for (int i = 0; i < mechanism_count; ++i) {
185 		hash_alg = pkcs11_mechanism_type_to_hash_alg(mechanisms[i]);
186 		if (hash_alg != VB2_HASH_INVALID)
187 			break;
188 	}
189 	free(mechanism_attr.pValue);
190 	return hash_alg;
191 }
192 
pkcs11_get_sig_alg(struct pkcs11_key * p11_key)193 enum vb2_signature_algorithm pkcs11_get_sig_alg(struct pkcs11_key *p11_key)
194 {
195 	if (!p11) {
196 		fprintf(stderr, "pkcs11 is not loaded\n");
197 		return VB2_SIG_INVALID;
198 	}
199 	CK_ULONG modulus_bits = 0;
200 	CK_ATTRIBUTE modulus_attr = {CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits)};
201 	if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &modulus_attr, 1) !=
202 	    CKR_OK) {
203 		fprintf(stderr, "Failed to get modulus bits\n");
204 		return VB2_SIG_INVALID;
205 	}
206 
207 	CK_ATTRIBUTE exponent_attr = {CKA_PUBLIC_EXPONENT, NULL, 0};
208 	if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &exponent_attr, 1) !=
209 	    CKR_OK) {
210 		fprintf(stderr, "Failed to get exponent attribute length\n");
211 		return VB2_SIG_INVALID;
212 	}
213 	CK_ULONG exp_size = exponent_attr.ulValueLen;
214 	if (exp_size > 4) {
215 		fprintf(stderr, "Exponent size is too large\n");
216 		return VB2_SIG_INVALID;
217 	}
218 	exponent_attr.pValue = malloc(exp_size);
219 	if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &exponent_attr, 1) !=
220 	    CKR_OK) {
221 		fprintf(stderr, "Failed to get exponent attribute value\n");
222 		free(exponent_attr.pValue);
223 		return VB2_SIG_INVALID;
224 	}
225 	// Parse the CKA_PUBLIC_EXPONENT in Big-endian.
226 	CK_BYTE *exp_value = exponent_attr.pValue;
227 	uint32_t exp = 0;
228 	for (int i = 0; i < exp_size; ++i)
229 		exp = (exp << 8) + exp_value[i];
230 	free(exponent_attr.pValue);
231 
232 	return vb2_get_sig_alg(exp, modulus_bits);
233 }
234 
pkcs11_get_modulus(struct pkcs11_key * p11_key,uint32_t * sizeptr)235 uint8_t *pkcs11_get_modulus(struct pkcs11_key *p11_key, uint32_t *sizeptr)
236 {
237 	if (!p11) {
238 		fprintf(stderr, "pkcs11 is not loaded\n");
239 		return NULL;
240 	}
241 	CK_ATTRIBUTE modulus_attr = {CKA_MODULUS, NULL, 0};
242 	if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &modulus_attr, 1) !=
243 	    CKR_OK) {
244 		fprintf(stderr, "Failed to get modulus attribute length\n");
245 		return NULL;
246 	}
247 	CK_ULONG modulus_size = modulus_attr.ulValueLen;
248 	modulus_attr.pValue = malloc(modulus_size);
249 	if (p11->C_GetAttributeValue(p11_key->session, p11_key->handle, &modulus_attr, 1) !=
250 	    CKR_OK) {
251 		fprintf(stderr, "Failed to get modulus attribute value\n");
252 		free(modulus_attr.pValue);
253 		return NULL;
254 	}
255 	*sizeptr = modulus_size;
256 	return modulus_attr.pValue;
257 }
258 
pkcs11_sign(struct pkcs11_key * p11_key,enum vb2_hash_algorithm hash_alg,const uint8_t * data,int data_size,uint8_t * sig,uint32_t sig_size)259 vb2_error_t pkcs11_sign(struct pkcs11_key *p11_key, enum vb2_hash_algorithm hash_alg,
260 			const uint8_t *data, int data_size, uint8_t *sig, uint32_t sig_size)
261 {
262 	if (!p11) {
263 		fprintf(stderr, "pkcs11 is not loaded\n");
264 		return VB2_ERROR_UNKNOWN;
265 	}
266 
267 	CK_MECHANISM mechanism;
268 	switch (hash_alg) {
269 	case VB2_HASH_SHA1:
270 		mechanism.mechanism = CKM_SHA1_RSA_PKCS;
271 		break;
272 	case VB2_HASH_SHA256:
273 		mechanism.mechanism = CKM_SHA256_RSA_PKCS;
274 		break;
275 	case VB2_HASH_SHA512:
276 		mechanism.mechanism = CKM_SHA512_RSA_PKCS;
277 		break;
278 	default:
279 		fprintf(stderr, "Unsupported hash algorithm %d\n", hash_alg);
280 		return VB2_ERROR_UNKNOWN;
281 	}
282 	mechanism.pParameter = NULL;
283 	mechanism.ulParameterLen = 0;
284 
285 	CK_RV result = p11->C_SignInit(p11_key->session, &mechanism, p11_key->handle);
286 	if (result != CKR_OK) {
287 		fprintf(stderr, "Failed to sign init\n");
288 		return VB2_ERROR_UNKNOWN;
289 	}
290 	CK_ULONG ck_sig_size = sig_size;
291 	result = p11->C_Sign(p11_key->session, (unsigned char *)data, data_size, sig,
292 			     &ck_sig_size);
293 	if (result != CKR_OK) {
294 		fprintf(stderr, "Failed to sign\n");
295 		return VB2_ERROR_UNKNOWN;
296 	}
297 	return VB2_SUCCESS;
298 }
299 
pkcs11_free_key(struct pkcs11_key * p11_key)300 void pkcs11_free_key(struct pkcs11_key *p11_key)
301 {
302 	if (!p11) {
303 		fprintf(stderr, "pkcs11 is not loaded\n");
304 		return;
305 	}
306 	CK_RV result = p11->C_CloseSession(p11_key->session);
307 	if (result != CKR_OK)
308 		fprintf(stderr, "Failed to close session\n");
309 	free(p11_key);
310 }
311