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