326 lines
7.8 KiB
C
Executable File
326 lines
7.8 KiB
C
Executable File
/*
|
|
* (C) Copyright 2007-2015
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
* Jerry Wang <wangflord@allwinnertech.com>
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <securestorage.h>
|
|
#include <smc.h>
|
|
#include <u-boot/crc.h>
|
|
#include <sunxi_crypto.h>
|
|
#include <sunxi_board.h>
|
|
|
|
|
|
#define HDCP_MAIGC (0x5aa5a55a)
|
|
#define HDCP_KEY_LEN (308)
|
|
#define AES_16_BYTES_ALIGN (12)
|
|
#define HDCP_BUFFER_LEN (HDCP_KEY_LEN + AES_16_BYTES_ALIGN)
|
|
|
|
#ifdef CONFIG_SUNXI_HDCP_HASH
|
|
#define HDCP_MD5_LEN (16)
|
|
#define RAW_HDCP_KEY_LEN (288)
|
|
#define HDCP_FILE_SIZE (352 + HDCP_MD5_LEN)
|
|
#else
|
|
#define HDCP_FILE_SIZE (352)
|
|
#endif
|
|
|
|
#ifdef CONFIG_RSSK_INIT
|
|
#define RSSK_SIZE_BITS (256)
|
|
#define RSSK_SIZE_BYTES (RSSK_SIZE_BITS >> 3)
|
|
#endif
|
|
|
|
typedef struct {
|
|
char name[64];
|
|
u32 len;
|
|
u32 res;
|
|
u8 *key_data;
|
|
}
|
|
sunxi_efuse_key_info_t;
|
|
|
|
|
|
typedef struct{
|
|
unsigned int magic; //Magic number, Value is 0x5aa5a55a
|
|
unsigned int version; //Version of the structure
|
|
unsigned int len; //Data length from start,but not include CRC
|
|
unsigned char rak[16]; //Random 128 bit AES CBC key in small-endian
|
|
unsigned char data[HDCP_BUFFER_LEN]; //HDCP key(308B), Padding HDCP key length to 16 bytes align after encrypt(320B)
|
|
#ifdef CONFIG_SUNXI_HDCP_HASH
|
|
unsigned char md5[HDCP_MD5_LEN]; /*MD5 hash value of raw HDCP Key*/
|
|
#endif
|
|
unsigned int crc; //CRC32 value of the above data
|
|
}hdcp_object;
|
|
|
|
static unsigned char raw_hdcp_key[HDCP_BUFFER_LEN];
|
|
|
|
extern void sunxi_dump(void *addr, unsigned int size);
|
|
extern int sunxi_efuse_write(void *key_buf);
|
|
extern int sunxi_efuse_read(void *key_name, void *read_buf, int *len);
|
|
|
|
int hdcp_key_parse(unsigned char *keydata, int keylen)
|
|
{
|
|
int ret;
|
|
hdcp_object *obj = (hdcp_object *)keydata;
|
|
|
|
if (keylen < HDCP_FILE_SIZE)
|
|
{
|
|
printf("hdcp_key_parse: hdcp file is bad, size: %d\n", keylen);
|
|
return -1;
|
|
}
|
|
if (obj->magic != HDCP_MAIGC)
|
|
{
|
|
printf("hdcp_key_parse: hdcp magic is bad\n");
|
|
return -1;
|
|
}
|
|
if (obj->crc != crc32(0, (void*)obj, sizeof(hdcp_object)-4))
|
|
{
|
|
printf("hdcp_key_parse: hdcp crc is bad\n");
|
|
return -1;
|
|
}
|
|
#ifdef DUMP_KEY
|
|
printf("hdcp, AES KEY:\n");
|
|
sunxi_dump(obj->rak, 16);
|
|
printf("hdcp encrypt data:\n");
|
|
sunxi_dump(obj->data, HDCP_BUFFER_LEN);
|
|
#endif
|
|
memset(raw_hdcp_key, 0x0, HDCP_BUFFER_LEN);
|
|
ret = smc_aes_algorithm((char*)raw_hdcp_key,(char*)(obj->data), HDCP_BUFFER_LEN,
|
|
(char*)(obj->rak), 0, 1);
|
|
if (ret < 0)
|
|
{
|
|
printf("sunxi_aes_decrypt: failed\n");
|
|
return -1;
|
|
}
|
|
#ifdef DUMP_KEY
|
|
printf("hdcp decrypt data:\n");
|
|
sunxi_dump(raw_hdcp_key,HDCP_BUFFER_LEN);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
void hdcp_key_convert(unsigned char *keyi,unsigned char *keyo)
|
|
{
|
|
unsigned i;
|
|
for(i=0; i<5; i++)
|
|
{
|
|
keyo[i] = keyi[i];
|
|
}
|
|
|
|
keyo[5] = keyo[6] = 0;
|
|
|
|
for(i=0; i<280; i++)
|
|
{
|
|
keyo[7+i] = keyi[8+i];
|
|
}
|
|
|
|
keyo[287] = 0;
|
|
}
|
|
|
|
#ifdef CONFIG_SUNXI_HDCP_HASH
|
|
int verify_hdcp_key_sha(unsigned char *raw_md5, char *buffer_convert)
|
|
{
|
|
unsigned char hash_of_hdcp[HDCP_MD5_LEN];
|
|
int ret;
|
|
char key_buf[RAW_HDCP_KEY_LEN + 32];
|
|
|
|
memset(hash_of_hdcp, 0, HDCP_MD5_LEN);
|
|
memset(key_buf, 0, RAW_HDCP_KEY_LEN);
|
|
|
|
char *align = (char *)(((u32)key_buf+31)&(~31));
|
|
memcpy((void *)align, (void *)buffer_convert, RAW_HDCP_KEY_LEN);
|
|
if (sunxi_md5_calc((u8 *)hash_of_hdcp, HDCP_MD5_LEN, (u8 *)align, RAW_HDCP_KEY_LEN)) {
|
|
printf("sunxi_md5_calc: failed\n");
|
|
return -1;
|
|
}
|
|
#ifdef DUMP_KEY
|
|
printf("hdcp md5 sha:\n");
|
|
sunxi_dump(hash_of_hdcp, 16);
|
|
#endif
|
|
ret = memcmp((void *)hash_of_hdcp, (void *)raw_md5, (unsigned int)HDCP_MD5_LEN);
|
|
if (ret != 0) {
|
|
printf("hash compare is not correct\n");
|
|
printf(">>>>>>>hash of file<<<<<<<<<<\n");
|
|
sunxi_dump(hash_of_hdcp, HDCP_MD5_LEN);
|
|
printf(">>>>>>>hash in certif<<<<<<<<<<\n");
|
|
sunxi_dump(raw_md5, HDCP_MD5_LEN);
|
|
printf("hdcp key md5 compare erro\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
int sunxi_deal_hdcp_key_hash(char *keydata, char *buffer_convert)
|
|
{
|
|
int ret;
|
|
hdcp_object *obj = (hdcp_object *)keydata;
|
|
sunxi_efuse_key_info_t efuse_key_info;
|
|
const char *name = "hdcphash";
|
|
|
|
if (obj->md5 == NULL) {
|
|
printf("hdcp key file md5 is NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = verify_hdcp_key_sha(obj->md5, buffer_convert);
|
|
if (ret < 0) {
|
|
printf("verify_hdcp_key_sha: failed\n");
|
|
return -1;
|
|
}
|
|
|
|
strcpy(efuse_key_info.name, name);
|
|
efuse_key_info.len = HDCP_MD5_LEN;
|
|
efuse_key_info.key_data = obj->md5;
|
|
|
|
if (sunxi_get_securemode() == SUNXI_NORMAL_MODE) {
|
|
if (sunxi_efuse_write(&efuse_key_info)) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (arm_svc_efuse_write(&efuse_key_info)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
#ifdef CONFIG_RSSK_INIT
|
|
int sunxi_deal_rssk_key(void)
|
|
{
|
|
int ret;
|
|
sunxi_efuse_key_info_t efuse_key_info;
|
|
const char *name = "rssk";
|
|
unsigned char rssk_buf[RSSK_SIZE_BYTES];
|
|
|
|
ret = sunxi_create_rssk(rssk_buf, RSSK_SIZE_BYTES);
|
|
if (ret < 0) {
|
|
printf("sunxi_create_rssk fail\n");
|
|
return -1;
|
|
}
|
|
#ifdef DUMP_KEY
|
|
printf("rssk data:\n");
|
|
sunxi_dump(rssk_buf, RSSK_SIZE_BYTES);
|
|
#endif
|
|
strcpy(efuse_key_info.name, name);
|
|
efuse_key_info.len = RSSK_SIZE_BYTES;
|
|
efuse_key_info.key_data = rssk_buf;
|
|
|
|
if (sunxi_get_securemode() == SUNXI_NORMAL_MODE) {
|
|
if (sunxi_efuse_write(&efuse_key_info)) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
printf("rssk ready to burn\n");
|
|
ret = arm_svc_efuse_write(&efuse_key_info);
|
|
if (ret == 1) {
|
|
printf(" rssk has been already burned \n");
|
|
} else if (ret < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
int sunxi_deal_hdcp_key(char *keydata, int keylen)
|
|
{
|
|
int ret ;
|
|
char buffer_convert[4096];
|
|
|
|
if (hdcp_key_parse((unsigned char *)keydata, keylen))
|
|
{
|
|
printf("hdcp_key_parse failed\n");
|
|
return -1;
|
|
}
|
|
hdcp_key_convert((unsigned char *)raw_hdcp_key, (unsigned char *)buffer_convert);
|
|
#ifdef CONFIG_SUNXI_HDCP_HASH
|
|
ret = sunxi_deal_hdcp_key_hash(keydata, buffer_convert);
|
|
if (ret < 0) {
|
|
printf("sunxi_deal_hdcp_key_hash failed\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_RSSK_INIT
|
|
ret = sunxi_deal_rssk_key();
|
|
if (ret < 0) {
|
|
printf("sunxi_deal_rssk_key failed\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
ret = sunxi_secure_object_down("hdcpkey", buffer_convert, SUNXI_HDCP_KEY_LEN,1,0);
|
|
if(ret<0)
|
|
{
|
|
printf("sunxi secure storage write failed\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|