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