386 lines
9.5 KiB
C
386 lines
9.5 KiB
C
/*
|
|
* (C) Copyright 2018-2020
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
* wangwei <wangwei@allwinnertech.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
/*
|
|
* manage key involved with secure os, usually
|
|
* encrypt/decrypt by secure os
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <malloc.h>
|
|
#include <securestorage.h>
|
|
#include <sunxi_board.h>
|
|
#include <smc.h>
|
|
#include <sunxi_hdcp_key.h>
|
|
#include <sunxi_keybox.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#define KEY_NAME_MAX_SIZE 64
|
|
static uint8_t key_list_inited;
|
|
static char (*key_list)[KEY_NAME_MAX_SIZE];
|
|
static char key_list_cnt;
|
|
|
|
static int sunxi_keybox_init_key_list_from_env(void)
|
|
{
|
|
char *command_p = NULL;
|
|
int key_count = 0;
|
|
int key_idx = 0, key_name_char_idx = 0;
|
|
|
|
if (key_list_inited)
|
|
return -1;
|
|
command_p = env_get("keybox_list");
|
|
if (!command_p) {
|
|
pr_msg("keybox_list not set in env, leave empty\n");
|
|
key_list_inited = 1;
|
|
return 0;
|
|
}
|
|
/*key count equals ',' count plue one */
|
|
key_count = 1;
|
|
while (*command_p != '\0') {
|
|
if (*command_p == ',') {
|
|
key_count++;
|
|
}
|
|
command_p++;
|
|
}
|
|
|
|
key_list = (char(*)[KEY_NAME_MAX_SIZE])malloc(KEY_NAME_MAX_SIZE *
|
|
key_count);
|
|
if (key_list == NULL)
|
|
return -2;
|
|
key_idx = 0;
|
|
key_name_char_idx = 0;
|
|
command_p = env_get("keybox_list");
|
|
while (*command_p != '\0') {
|
|
if (*command_p == ',') {
|
|
key_list[key_idx][key_name_char_idx] = '\0';
|
|
key_idx++;
|
|
key_name_char_idx = 0;
|
|
if (key_idx >= key_count) {
|
|
break;
|
|
}
|
|
} else if (*command_p == ' ') {
|
|
/*skip space*/
|
|
} else if (key_name_char_idx < KEY_NAME_MAX_SIZE) {
|
|
key_list[key_idx][key_name_char_idx] = *command_p;
|
|
key_name_char_idx++;
|
|
}
|
|
command_p++;
|
|
}
|
|
key_list[key_idx][key_name_char_idx] = '\0';
|
|
|
|
key_list_cnt = key_count;
|
|
key_list_inited = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __maybe_unused int sunxi_keybox_init_key_list(char *key_name[],
|
|
int keyCount)
|
|
{
|
|
int i;
|
|
if (key_list_inited)
|
|
return -1;
|
|
key_list = (char(*)[KEY_NAME_MAX_SIZE])malloc(KEY_NAME_MAX_SIZE *
|
|
keyCount);
|
|
if (key_list == NULL)
|
|
return -2;
|
|
for (i = 0; i < keyCount; i++) {
|
|
strncpy(key_list[i], key_name[i], KEY_NAME_MAX_SIZE);
|
|
key_list[i][KEY_NAME_MAX_SIZE - 1] = '\0';
|
|
}
|
|
key_list_cnt = keyCount;
|
|
key_list_inited = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int search_key_in_linklist(const char *name)
|
|
{
|
|
struct sunxi_key_t *start =
|
|
ll_entry_start(struct sunxi_key_t, sunxi_keys);
|
|
const int len = ll_entry_count(struct sunxi_key_t, sunxi_keys);
|
|
int i;
|
|
for (i = 0; i < len; i++) {
|
|
if ((strlen(name) == strlen(start[i].name)) &&
|
|
(strcmp(name, start[i].name) == 0))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int try_reencrypt_and_install(const char *name, int replace)
|
|
{
|
|
sunxi_secure_storage_info_t secdata;
|
|
int data_len;
|
|
int ret;
|
|
char old_data[4096];
|
|
memset(old_data, 0, 4096);
|
|
ret = sunxi_secure_object_read(name, old_data, sizeof(old_data),
|
|
&data_len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
memset(&secdata, 0, sizeof(secdata));
|
|
ret = sunxi_secure_object_build(name, old_data, data_len, 0, 0,
|
|
(char *)&secdata);
|
|
|
|
ret = smc_tee_keybox_store(name, (void *)&secdata, sizeof(secdata));
|
|
if (ret) {
|
|
return -1;
|
|
} else {
|
|
if (replace) {
|
|
pr_msg("re_encrypt works, replace current data with reencrypted data\n");
|
|
ret = sunxi_secure_object_write(
|
|
secdata.name, (void *)&secdata,
|
|
SUNXI_SECURE_STORTAGE_INFO_HEAD_LEN +
|
|
secdata.len);
|
|
if (ret)
|
|
pr_msg("replace failed\n");
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define INSTALL_SUCCESS_AFTER_REENC 1
|
|
static int default_keybox_installation(const char *name)
|
|
{
|
|
sunxi_secure_storage_info_t secure_object;
|
|
memset(&secure_object, 0, sizeof(secure_object));
|
|
int ret;
|
|
ret = sunxi_secure_object_up(name, (void *)&secure_object,
|
|
sizeof(secure_object));
|
|
if (ret) {
|
|
pr_err("secure storage read %s fail with:%d\n", name, ret);
|
|
return -1;
|
|
}
|
|
|
|
ret = smc_tee_keybox_store(name, (void *)&secure_object,
|
|
sizeof(secure_object));
|
|
if (ret) {
|
|
pr_err("key install %s fail with:%d\n", name, ret);
|
|
|
|
if (strcmp(name, secure_object.name) != 0) {
|
|
pr_msg("try reencrypt and install key %s\n", name);
|
|
ret = try_reencrypt_and_install(name, 1);
|
|
if (!ret) {
|
|
return INSTALL_SUCCESS_AFTER_REENC;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sunxi_keybox_install_keys(void)
|
|
{
|
|
int i;
|
|
int ret;
|
|
struct sunxi_key_t *start =
|
|
ll_entry_start(struct sunxi_key_t, sunxi_keys);
|
|
int flush_required = 0;
|
|
|
|
if (key_list_inited == 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < key_list_cnt; i++) {
|
|
ret = search_key_in_linklist(key_list[i]);
|
|
|
|
if ((ret >= 0) && (start[ret].key_load_cb != NULL)) {
|
|
pr_msg("load key %s with regesited cb\n", key_list[i]);
|
|
start[ret].key_load_cb(key_list[i]);
|
|
continue;
|
|
}
|
|
|
|
pr_msg("load key %s with default cb\n", key_list[i]);
|
|
/* deafult behavior */
|
|
ret = default_keybox_installation(key_list[i]);
|
|
if (ret == INSTALL_SUCCESS_AFTER_REENC) {
|
|
/* reencrypt include a writing, need flush afterward */
|
|
flush_required = 1;
|
|
} else if (ret) {
|
|
continue;
|
|
}
|
|
|
|
#if defined(CONFIG_SUNXI_HDCP_IN_SECURESTORAGE)
|
|
if ((strlen("hdcpkey") == strlen(key_list[i])) &&
|
|
(strcmp("hdcpkey", key_list[i]) == 0)) {
|
|
ret = sunxi_hdcp_key_post_install();
|
|
if (ret) {
|
|
pr_err("key %s post install process failed\n",
|
|
key_list[i]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (flush_required) {
|
|
sunxi_secstorage_flush();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_keybox_has_key(char *key_name)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
for (i = 0; i < key_list_cnt; i++) {
|
|
if (strcmp(key_name, key_list[i]) == 0) {
|
|
ret = 1;
|
|
}
|
|
}
|
|
if (!ret)
|
|
ret = (search_key_in_linklist(key_name) >= 0);
|
|
return ret;
|
|
}
|
|
|
|
int sunxi_keybox_init(void)
|
|
{
|
|
int ret;
|
|
int workmode;
|
|
workmode = get_boot_work_mode();
|
|
if (workmode != WORK_MODE_BOOT)
|
|
return 0;
|
|
|
|
if (sunxi_probe_secure_os() == 0) {
|
|
pr_msg("no secure os for keybox operation\n");
|
|
return 0;
|
|
}
|
|
ret = sunxi_keybox_init_key_list_from_env();
|
|
if (ret != 0)
|
|
pr_err("sunxi keybox read env failed with:%d", ret);
|
|
sunxi_secure_storage_init();
|
|
ret = sunxi_keybox_install_keys();
|
|
if (ret != 0)
|
|
pr_err("sunxi keybox install failed with:%d", ret);
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_keybox_burn_key(const char *name, char *buf, int key_len, int encrypt,
|
|
int write_protect)
|
|
{
|
|
struct sunxi_key_t *start =
|
|
ll_entry_start(struct sunxi_key_t, sunxi_keys);
|
|
int i;
|
|
i = search_key_in_linklist(name);
|
|
if ((i >= 0) && (start[i].key_burn_cb != NULL)) {
|
|
pr_msg("burning key %s with regesited cb\n", name);
|
|
return start[i].key_burn_cb(name, buf, key_len, encrypt,
|
|
write_protect);
|
|
}
|
|
/* default behavior */
|
|
pr_msg("burning key %s with default cb\n", name);
|
|
return sunxi_secure_object_down(name, buf, key_len, encrypt,
|
|
write_protect);
|
|
}
|
|
|
|
#ifdef CONFIG_SUNXI_ANDROID_BOOT
|
|
/* android keybox keys */
|
|
|
|
const static char *android_trust_chain_map[8] = { "ec_key", "ec_cert1",
|
|
"ec_cert2", "ec_cert3",
|
|
"rsa_key", "rsa_cert1",
|
|
"rsa_cert2", "rsa_cert3" };
|
|
|
|
enum ANDROID_TRUST_CHAIN_INSTALL_STATUS_EN{
|
|
INSTALLING,
|
|
INSTALL_FAILED
|
|
};
|
|
|
|
int android_trust_chain_load_cb(const char *name)
|
|
{
|
|
/*
|
|
* ignore input name, use fix order to load key
|
|
* so do not need walk through trust chain key
|
|
* name every single install
|
|
*/
|
|
static int idx = 0;
|
|
static int trust_chain_install_status = INSTALLING;
|
|
pr_msg("trust chain key, load in fix order,"
|
|
"input key name ignored\n");
|
|
switch (trust_chain_install_status) {
|
|
case INSTALL_FAILED:
|
|
/*already installed, meaning less to install the rest*/
|
|
return -2;
|
|
break;
|
|
case INSTALLING:
|
|
default:
|
|
break;
|
|
}
|
|
if (default_keybox_installation(android_trust_chain_map[idx]) != 0) {
|
|
trust_chain_install_status = INSTALL_FAILED;
|
|
return -2;
|
|
} else {
|
|
if ((idx == ARRAY_SIZE(android_trust_chain_map) - 1) &&
|
|
(trust_chain_install_status != INSTALL_FAILED)) {
|
|
/* all success installed*/
|
|
env_set("android_trust_chain", "true");
|
|
}
|
|
}
|
|
idx++;
|
|
return 0;
|
|
}
|
|
SUNXI_KEYBOX_KEY(ec_key, NULL, android_trust_chain_load_cb);
|
|
SUNXI_KEYBOX_KEY(ec_cert1, NULL, android_trust_chain_load_cb);
|
|
SUNXI_KEYBOX_KEY(ec_cert2, NULL, android_trust_chain_load_cb);
|
|
SUNXI_KEYBOX_KEY(ec_cert3, NULL, android_trust_chain_load_cb);
|
|
SUNXI_KEYBOX_KEY(rsa_key, NULL, android_trust_chain_load_cb);
|
|
SUNXI_KEYBOX_KEY(rsa_cert1, NULL, android_trust_chain_load_cb);
|
|
SUNXI_KEYBOX_KEY(rsa_cert2, NULL, android_trust_chain_load_cb);
|
|
SUNXI_KEYBOX_KEY(rsa_cert3, NULL, android_trust_chain_load_cb);
|
|
|
|
int android_drmkey_load_cb(const char *name)
|
|
{
|
|
if (default_keybox_installation(name) != 0) {
|
|
return -1;
|
|
} else {
|
|
pr_msg("set android_drmkey to true\n");
|
|
env_set("android_drmkey", "true");
|
|
}
|
|
return 0;
|
|
}
|
|
SUNXI_KEYBOX_KEY(widevine, NULL, android_drmkey_load_cb);
|
|
#endif
|
|
|
|
#if defined (CONFIG_SUNXI_SDMMC) && defined (CONFIG_SUPPORT_EMMC_RPMB)
|
|
__weak int sunxi_mmc_rpmb_burn_key(char *buf)
|
|
{
|
|
return -5;
|
|
}
|
|
#define RPMB_SZ_MAC 32
|
|
#define RPMB_SZ_DATA 256
|
|
int rpmb_key_burn(__maybe_unused const char *name, char *buf, int len,
|
|
__maybe_unused int encrypt, __maybe_unused int write_protect)
|
|
{
|
|
int ret;
|
|
int storage_type = get_boot_storage_type();
|
|
switch (storage_type) {
|
|
case STORAGE_EMMC:
|
|
case STORAGE_EMMC0:
|
|
case STORAGE_EMMC3:
|
|
break;
|
|
default:
|
|
pr_err("not supported storage_type:%d\n", storage_type);
|
|
return -1;
|
|
}
|
|
if (len != RPMB_SZ_MAC) {
|
|
pr_err("invalid lengh %d, expected %d\n", len, RPMB_SZ_MAC);
|
|
return -2;
|
|
}
|
|
ret = sunxi_mmc_rpmb_burn_key(buf);
|
|
if (ret == -3) {
|
|
pr_msg("rpmb key burned, key valid, only store key\n");
|
|
} else if (ret == -2) {
|
|
pr_err("rpmb key burn failed, key not valid, skipped\n");
|
|
return ret;
|
|
} else if (!ret) {
|
|
pr_err("rpmb burn key failed\n");
|
|
return ret;
|
|
}
|
|
return sunxi_secure_object_down("rpmb_key", buf, len, 0, 1);
|
|
};
|
|
SUNXI_KEYBOX_KEY(rpmb_key, rpmb_key_burn, NULL);
|
|
#endif
|