xref: /optee_os/ta/pkcs11/src/persistent_token.c (revision e86828f46493b322e7a595f91fa8a73855f9a6a7)
1c84ccd0aSEtienne Carriere // SPDX-License-Identifier: BSD-2-Clause
2c84ccd0aSEtienne Carriere /*
3c84ccd0aSEtienne Carriere  * Copyright (c) 2018-2020, Linaro Limited
4c84ccd0aSEtienne Carriere  */
5c84ccd0aSEtienne Carriere 
6c84ccd0aSEtienne Carriere #include <assert.h>
7c84ccd0aSEtienne Carriere #include <pkcs11_ta.h>
8c84ccd0aSEtienne Carriere #include <string.h>
9c84ccd0aSEtienne Carriere #include <string_ext.h>
10c84ccd0aSEtienne Carriere #include <tee_internal_api_extensions.h>
11c84ccd0aSEtienne Carriere #include <util.h>
12c84ccd0aSEtienne Carriere 
13c84ccd0aSEtienne Carriere #include "pkcs11_token.h"
14c84ccd0aSEtienne Carriere #include "pkcs11_helpers.h"
15c84ccd0aSEtienne Carriere 
1660659a86SEtienne Carriere #define PERSISTENT_OBJECT_ID_LEN	32
1760659a86SEtienne Carriere 
1860659a86SEtienne Carriere /*
1960659a86SEtienne Carriere  * Token persistent objects
2060659a86SEtienne Carriere  */
2160659a86SEtienne Carriere static TEE_Result get_db_file_name(struct ck_token *token,
2260659a86SEtienne Carriere 				   char *name, size_t size)
23c84ccd0aSEtienne Carriere {
2460659a86SEtienne Carriere 	int n = snprintf(name, size, "token.db.%u", get_token_id(token));
2560659a86SEtienne Carriere 
2660659a86SEtienne Carriere 	if (n < 0 || (size_t)n >= size)
2760659a86SEtienne Carriere 		return TEE_ERROR_SECURITY;
2860659a86SEtienne Carriere 	else
2960659a86SEtienne Carriere 		return TEE_SUCCESS;
3060659a86SEtienne Carriere }
3160659a86SEtienne Carriere 
3260659a86SEtienne Carriere static TEE_Result open_db_file(struct ck_token *token,
3360659a86SEtienne Carriere 			       TEE_ObjectHandle *out_hdl)
3460659a86SEtienne Carriere {
3560659a86SEtienne Carriere 	char file[PERSISTENT_OBJECT_ID_LEN] = { };
3660659a86SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
3760659a86SEtienne Carriere 
3860659a86SEtienne Carriere 	res = get_db_file_name(token, file, sizeof(file));
3960659a86SEtienne Carriere 	if (res)
4060659a86SEtienne Carriere 		return res;
4160659a86SEtienne Carriere 
4260659a86SEtienne Carriere 	return TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE, file, sizeof(file),
4360659a86SEtienne Carriere 					TEE_DATA_FLAG_ACCESS_READ |
4460659a86SEtienne Carriere 					TEE_DATA_FLAG_ACCESS_WRITE,
4560659a86SEtienne Carriere 					out_hdl);
4660659a86SEtienne Carriere }
4760659a86SEtienne Carriere 
4860659a86SEtienne Carriere static TEE_Result get_pin_file_name(struct ck_token *token,
4960659a86SEtienne Carriere 				    enum pkcs11_user_type user,
5060659a86SEtienne Carriere 				    char *name, size_t size)
5160659a86SEtienne Carriere {
5260659a86SEtienne Carriere 	int n = snprintf(name, size,
5360659a86SEtienne Carriere 			 "token.db.%u-pin%d", get_token_id(token), user);
5460659a86SEtienne Carriere 
5560659a86SEtienne Carriere 	if (n < 0 || (size_t)n >= size)
5660659a86SEtienne Carriere 		return TEE_ERROR_SECURITY;
5760659a86SEtienne Carriere 	else
5860659a86SEtienne Carriere 		return TEE_SUCCESS;
5960659a86SEtienne Carriere }
6060659a86SEtienne Carriere 
6160659a86SEtienne Carriere static TEE_Result open_pin_file(struct ck_token *token,
6260659a86SEtienne Carriere 				enum pkcs11_user_type user,
6360659a86SEtienne Carriere 				TEE_ObjectHandle *out_hdl)
6460659a86SEtienne Carriere {
6560659a86SEtienne Carriere 	char file[PERSISTENT_OBJECT_ID_LEN] = { };
6660659a86SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
6760659a86SEtienne Carriere 
6860659a86SEtienne Carriere 	res = get_pin_file_name(token, user, file, sizeof(file));
6960659a86SEtienne Carriere 	if (res)
7060659a86SEtienne Carriere 		return res;
7160659a86SEtienne Carriere 
7260659a86SEtienne Carriere 	return TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE, file, sizeof(file),
7360659a86SEtienne Carriere 					0, out_hdl);
74c84ccd0aSEtienne Carriere }
75c84ccd0aSEtienne Carriere 
76*e86828f4SJens Wiklander void update_persistent_db(struct ck_token *token)
77*e86828f4SJens Wiklander {
78*e86828f4SJens Wiklander 	TEE_Result res = TEE_ERROR_GENERIC;
79*e86828f4SJens Wiklander 	TEE_ObjectHandle db_hdl = TEE_HANDLE_NULL;
80*e86828f4SJens Wiklander 
81*e86828f4SJens Wiklander 	res = open_db_file(token, &db_hdl);
82*e86828f4SJens Wiklander 	if (res) {
83*e86828f4SJens Wiklander 		EMSG("Failed to open token persistent db: %#"PRIx32, res);
84*e86828f4SJens Wiklander 		TEE_Panic(0);
85*e86828f4SJens Wiklander 	}
86*e86828f4SJens Wiklander 	res = TEE_WriteObjectData(db_hdl, token->db_main,
87*e86828f4SJens Wiklander 				  sizeof(*token->db_main));
88*e86828f4SJens Wiklander 	if (res) {
89*e86828f4SJens Wiklander 		EMSG("Failed to write to token persistent db: %#"PRIx32, res);
90*e86828f4SJens Wiklander 		TEE_Panic(0);
91*e86828f4SJens Wiklander 	}
92*e86828f4SJens Wiklander 
93*e86828f4SJens Wiklander 	TEE_CloseObject(db_hdl);
94*e86828f4SJens Wiklander }
95*e86828f4SJens Wiklander 
96bef8bc68SJens Wiklander static enum pkcs11_rc do_hash(uint32_t user, const uint8_t *pin,
97bef8bc68SJens Wiklander 			      size_t pin_size, uint32_t salt,
98bef8bc68SJens Wiklander 			      uint8_t hash[TEE_MAX_HASH_SIZE])
99bef8bc68SJens Wiklander {
100bef8bc68SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
101bef8bc68SJens Wiklander 	TEE_OperationHandle oh = TEE_HANDLE_NULL;
102bef8bc68SJens Wiklander 	uint32_t sz = TEE_MAX_HASH_SIZE;
103bef8bc68SJens Wiklander 
104bef8bc68SJens Wiklander 	res = TEE_AllocateOperation(&oh, TEE_ALG_SHA256, TEE_MODE_DIGEST, 0);
105bef8bc68SJens Wiklander 	if (res)
106bef8bc68SJens Wiklander 		return tee2pkcs_error(res);
107bef8bc68SJens Wiklander 
108bef8bc68SJens Wiklander 	TEE_DigestUpdate(oh, &user, sizeof(user));
109bef8bc68SJens Wiklander 	TEE_DigestUpdate(oh, &salt, sizeof(salt));
110bef8bc68SJens Wiklander 	res = TEE_DigestDoFinal(oh, pin, pin_size, hash, &sz);
111bef8bc68SJens Wiklander 	TEE_FreeOperation(oh);
112bef8bc68SJens Wiklander 
113bef8bc68SJens Wiklander 	if (res)
114bef8bc68SJens Wiklander 		return PKCS11_CKR_GENERAL_ERROR;
115bef8bc68SJens Wiklander 
116bef8bc68SJens Wiklander 	memset(hash + sz, 0, TEE_MAX_HASH_SIZE - sz);
117bef8bc68SJens Wiklander 	return PKCS11_CKR_OK;
118bef8bc68SJens Wiklander }
119bef8bc68SJens Wiklander 
120bef8bc68SJens Wiklander enum pkcs11_rc hash_pin(enum pkcs11_user_type user, const uint8_t *pin,
121bef8bc68SJens Wiklander 			size_t pin_size, uint32_t *salt,
122bef8bc68SJens Wiklander 			uint8_t hash[TEE_MAX_HASH_SIZE])
123bef8bc68SJens Wiklander {
124bef8bc68SJens Wiklander 	enum pkcs11_rc rc = PKCS11_CKR_OK;
125bef8bc68SJens Wiklander 	uint32_t s = 0;
126bef8bc68SJens Wiklander 
127bef8bc68SJens Wiklander 	TEE_GenerateRandom(&s, sizeof(s));
128bef8bc68SJens Wiklander 	if (!s)
129bef8bc68SJens Wiklander 		s++;
130bef8bc68SJens Wiklander 
131bef8bc68SJens Wiklander 	rc = do_hash(user, pin, pin_size, s, hash);
132bef8bc68SJens Wiklander 	if (!rc)
133bef8bc68SJens Wiklander 		*salt = s;
134bef8bc68SJens Wiklander 	return rc;
135bef8bc68SJens Wiklander }
136bef8bc68SJens Wiklander 
137bef8bc68SJens Wiklander enum pkcs11_rc verify_pin(enum pkcs11_user_type user, const uint8_t *pin,
138bef8bc68SJens Wiklander 			  size_t pin_size, uint32_t salt,
139bef8bc68SJens Wiklander 			  const uint8_t hash[TEE_MAX_HASH_SIZE])
140bef8bc68SJens Wiklander {
141bef8bc68SJens Wiklander 	uint8_t tmp_hash[TEE_MAX_HASH_SIZE] = { 0 };
142bef8bc68SJens Wiklander 	enum pkcs11_rc rc = PKCS11_CKR_OK;
143bef8bc68SJens Wiklander 
144bef8bc68SJens Wiklander 	rc = do_hash(user, pin, pin_size, salt, tmp_hash);
145bef8bc68SJens Wiklander 	if (rc)
146bef8bc68SJens Wiklander 		return rc;
147bef8bc68SJens Wiklander 
148bef8bc68SJens Wiklander 	if (buf_compare_ct(tmp_hash, hash, TEE_MAX_HASH_SIZE))
149bef8bc68SJens Wiklander 		rc = PKCS11_CKR_PIN_INCORRECT;
150bef8bc68SJens Wiklander 
151bef8bc68SJens Wiklander 	return rc;
152bef8bc68SJens Wiklander }
153bef8bc68SJens Wiklander 
154fce35058SEtienne Carriere static void init_pin_keys(struct ck_token *token, enum pkcs11_user_type user)
155c84ccd0aSEtienne Carriere {
156c84ccd0aSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
157c84ccd0aSEtienne Carriere 	TEE_ObjectHandle key_hdl = TEE_HANDLE_NULL;
158c84ccd0aSEtienne Carriere 
15960659a86SEtienne Carriere 	res = open_pin_file(token, user, &key_hdl);
160c84ccd0aSEtienne Carriere 
16160659a86SEtienne Carriere 	if (res == TEE_SUCCESS)
16260659a86SEtienne Carriere 		DMSG("PIN key found");
163c84ccd0aSEtienne Carriere 
164c84ccd0aSEtienne Carriere 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
165c84ccd0aSEtienne Carriere 		TEE_Attribute attr = { };
166c84ccd0aSEtienne Carriere 		TEE_ObjectHandle hdl = TEE_HANDLE_NULL;
16760659a86SEtienne Carriere 		uint8_t pin_key[16] = { };
16860659a86SEtienne Carriere 		char file[PERSISTENT_OBJECT_ID_LEN] = { };
169c84ccd0aSEtienne Carriere 
170c84ccd0aSEtienne Carriere 		TEE_MemFill(&attr, 0, sizeof(attr));
171c84ccd0aSEtienne Carriere 
172c84ccd0aSEtienne Carriere 		TEE_GenerateRandom(pin_key, sizeof(pin_key));
173c84ccd0aSEtienne Carriere 		TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE,
174c84ccd0aSEtienne Carriere 				     pin_key, sizeof(pin_key));
175c84ccd0aSEtienne Carriere 
176c84ccd0aSEtienne Carriere 		res = TEE_AllocateTransientObject(TEE_TYPE_AES, 128, &hdl);
177c84ccd0aSEtienne Carriere 		if (res)
178c84ccd0aSEtienne Carriere 			TEE_Panic(0);
179c84ccd0aSEtienne Carriere 
180c84ccd0aSEtienne Carriere 		res = TEE_PopulateTransientObject(hdl, &attr, 1);
181c84ccd0aSEtienne Carriere 		if (res)
182c84ccd0aSEtienne Carriere 			TEE_Panic(0);
183c84ccd0aSEtienne Carriere 
18460659a86SEtienne Carriere 		res = get_pin_file_name(token, user, file, sizeof(file));
18560659a86SEtienne Carriere 		if (res)
18660659a86SEtienne Carriere 			TEE_Panic(0);
18760659a86SEtienne Carriere 
188c84ccd0aSEtienne Carriere 		res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE,
189c84ccd0aSEtienne Carriere 						 file, sizeof(file), 0, hdl,
190c84ccd0aSEtienne Carriere 						 pin_key, sizeof(pin_key),
191c84ccd0aSEtienne Carriere 						 &key_hdl);
192c84ccd0aSEtienne Carriere 		TEE_CloseObject(hdl);
193c84ccd0aSEtienne Carriere 
194c84ccd0aSEtienne Carriere 		if (res == TEE_SUCCESS)
19560659a86SEtienne Carriere 			DMSG("Token %u: PIN key created", get_token_id(token));
196c84ccd0aSEtienne Carriere 	}
197c84ccd0aSEtienne Carriere 
198c84ccd0aSEtienne Carriere 	if (res)
199c84ccd0aSEtienne Carriere 		TEE_Panic(res);
200c84ccd0aSEtienne Carriere 
201c84ccd0aSEtienne Carriere 	TEE_CloseObject(key_hdl);
202c84ccd0aSEtienne Carriere }
203c84ccd0aSEtienne Carriere 
204c84ccd0aSEtienne Carriere /*
20560659a86SEtienne Carriere  * Release resources relate to persistent database
20660659a86SEtienne Carriere  */
20760659a86SEtienne Carriere void close_persistent_db(struct ck_token *token __unused)
20860659a86SEtienne Carriere {
20960659a86SEtienne Carriere }
21060659a86SEtienne Carriere 
21160659a86SEtienne Carriere /*
212c84ccd0aSEtienne Carriere  * Return the token instance, either initialized from reset or initialized
213c84ccd0aSEtienne Carriere  * from the token persistent state if found.
214c84ccd0aSEtienne Carriere  */
215c84ccd0aSEtienne Carriere struct ck_token *init_persistent_db(unsigned int token_id)
216c84ccd0aSEtienne Carriere {
217c84ccd0aSEtienne Carriere 	struct ck_token *token = get_token(token_id);
218c84ccd0aSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
219c84ccd0aSEtienne Carriere 	TEE_ObjectHandle db_hdl = TEE_HANDLE_NULL;
22060659a86SEtienne Carriere 	/* Copy persistent database: main db and object db */
221c84ccd0aSEtienne Carriere 	struct token_persistent_main *db_main = NULL;
222c84ccd0aSEtienne Carriere 
223c84ccd0aSEtienne Carriere 	if (!token)
224c84ccd0aSEtienne Carriere 		return NULL;
225c84ccd0aSEtienne Carriere 
2269dbdd8cdSEtienne Carriere 	init_pin_keys(token, PKCS11_CKU_SO);
2279dbdd8cdSEtienne Carriere 	init_pin_keys(token, PKCS11_CKU_USER);
2289dbdd8cdSEtienne Carriere 	COMPILE_TIME_ASSERT(PKCS11_CKU_SO == 0 && PKCS11_CKU_USER == 1 &&
2299dbdd8cdSEtienne Carriere 			    PKCS11_MAX_USERS >= 2);
230c84ccd0aSEtienne Carriere 
231c84ccd0aSEtienne Carriere 	db_main = TEE_Malloc(sizeof(*db_main), TEE_MALLOC_FILL_ZERO);
232c84ccd0aSEtienne Carriere 	if (!db_main)
233c84ccd0aSEtienne Carriere 		goto error;
234c84ccd0aSEtienne Carriere 
23560659a86SEtienne Carriere 	res = open_db_file(token, &db_hdl);
236c84ccd0aSEtienne Carriere 
237c84ccd0aSEtienne Carriere 	if (res == TEE_SUCCESS) {
238c84ccd0aSEtienne Carriere 		uint32_t size = 0;
239c84ccd0aSEtienne Carriere 
240c84ccd0aSEtienne Carriere 		IMSG("PKCS11 token %u: load db", token_id);
241c84ccd0aSEtienne Carriere 
242c84ccd0aSEtienne Carriere 		size = sizeof(*db_main);
243c84ccd0aSEtienne Carriere 		res = TEE_ReadObjectData(db_hdl, db_main, size, &size);
244c84ccd0aSEtienne Carriere 		if (res || size != sizeof(*db_main))
245c84ccd0aSEtienne Carriere 			TEE_Panic(0);
246c84ccd0aSEtienne Carriere 	} else if (res == TEE_ERROR_ITEM_NOT_FOUND) {
24760659a86SEtienne Carriere 		char file[PERSISTENT_OBJECT_ID_LEN] = { };
24860659a86SEtienne Carriere 
249c84ccd0aSEtienne Carriere 		IMSG("PKCS11 token %u: init db", token_id);
250c84ccd0aSEtienne Carriere 
251c84ccd0aSEtienne Carriere 		TEE_MemFill(db_main, 0, sizeof(*db_main));
252c84ccd0aSEtienne Carriere 		TEE_MemFill(db_main->label, '*', sizeof(db_main->label));
253c84ccd0aSEtienne Carriere 
254c84ccd0aSEtienne Carriere 		db_main->flags = PKCS11_CKFT_SO_PIN_TO_BE_CHANGED |
255c84ccd0aSEtienne Carriere 				 PKCS11_CKFT_USER_PIN_TO_BE_CHANGED |
256c84ccd0aSEtienne Carriere 				 PKCS11_CKFT_RNG |
257c84ccd0aSEtienne Carriere 				 PKCS11_CKFT_DUAL_CRYPTO_OPERATIONS |
258c84ccd0aSEtienne Carriere 				 PKCS11_CKFT_LOGIN_REQUIRED;
259c84ccd0aSEtienne Carriere 
26060659a86SEtienne Carriere 		res = get_db_file_name(token, file, sizeof(file));
26160659a86SEtienne Carriere 		if (res)
26260659a86SEtienne Carriere 			TEE_Panic(0);
26360659a86SEtienne Carriere 
264c84ccd0aSEtienne Carriere 		res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE,
26560659a86SEtienne Carriere 						 file, sizeof(file),
266c84ccd0aSEtienne Carriere 						 TEE_DATA_FLAG_ACCESS_READ |
267c84ccd0aSEtienne Carriere 						 TEE_DATA_FLAG_ACCESS_WRITE,
268c84ccd0aSEtienne Carriere 						 TEE_HANDLE_NULL,
269c84ccd0aSEtienne Carriere 						 db_main, sizeof(*db_main),
270c84ccd0aSEtienne Carriere 						 &db_hdl);
271c84ccd0aSEtienne Carriere 		if (res) {
272c84ccd0aSEtienne Carriere 			EMSG("Failed to create db: %"PRIx32, res);
273c84ccd0aSEtienne Carriere 			goto error;
274c84ccd0aSEtienne Carriere 		}
275c84ccd0aSEtienne Carriere 	} else {
276c84ccd0aSEtienne Carriere 		goto error;
277c84ccd0aSEtienne Carriere 	}
278c84ccd0aSEtienne Carriere 
279c84ccd0aSEtienne Carriere 	token->db_main = db_main;
280c84ccd0aSEtienne Carriere 	TEE_CloseObject(db_hdl);
281c84ccd0aSEtienne Carriere 
282c84ccd0aSEtienne Carriere 	return token;
283c84ccd0aSEtienne Carriere 
284c84ccd0aSEtienne Carriere error:
285c84ccd0aSEtienne Carriere 	TEE_Free(db_main);
286c84ccd0aSEtienne Carriere 	if (db_hdl != TEE_HANDLE_NULL)
287c84ccd0aSEtienne Carriere 		TEE_CloseObject(db_hdl);
288c84ccd0aSEtienne Carriere 
289c84ccd0aSEtienne Carriere 	return NULL;
290c84ccd0aSEtienne Carriere }
291