1071 lines
26 KiB
C
Executable File
1071 lines
26 KiB
C
Executable File
/*
|
|
* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-protocol-msg.c
|
|
*
|
|
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
|
|
#include "exynos-hdcp2-config.h"
|
|
#include "exynos-hdcp2-protocol-msg.h"
|
|
#include "exynos-hdcp2-teeif.h"
|
|
#include "exynos-hdcp2.h"
|
|
#include "exynos-hdcp2-log.h"
|
|
#include "exynos-hdcp2-misc.h"
|
|
#include "dp_link/exynos-hdcp2-dplink-protocol-msg.h"
|
|
|
|
/* HDCP protocol message capulate & decapulate functions for IIA */
|
|
static int cap_ake_init(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_ake_transmitter_info(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_ake_no_stored_km(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_ake_stored_km(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_ake_send_cert(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_ake_receiver_info(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_ake_send_rrx(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_ake_send_h_prime(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_ake_send_pairing_info(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_lc_init(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_rtt_challenge(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_lc_send_l_prime(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_rtt_ready(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_ske_send_eks(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_RepeaterAuth_send_ReceiverID_List(uint8_t *m,size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_RepeaterAuth_Send_Ack(uint8_t *m,size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_Receiver_AuthStatus(uint8_t *m,size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int cap_RepeaterAuth_Stream_Manage(uint8_t *m,size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
static int decap_RepeaterAuth_Stream_Ready(uint8_t *m,size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx);
|
|
int (*proto_iia[])(uint8_t *, size_t *,
|
|
struct hdcp_tx_ctx *,
|
|
struct hdcp_rx_ctx *) = {
|
|
cap_ake_init,
|
|
decap_ake_send_cert,
|
|
cap_ake_no_stored_km,
|
|
cap_ake_stored_km,
|
|
decap_ake_send_rrx,
|
|
decap_ake_send_h_prime,
|
|
decap_ake_send_pairing_info,
|
|
cap_lc_init,
|
|
decap_lc_send_l_prime,
|
|
cap_ske_send_eks,
|
|
decap_RepeaterAuth_send_ReceiverID_List,
|
|
decap_rtt_ready,
|
|
cap_rtt_challenge,
|
|
cap_RepeaterAuth_Send_Ack,
|
|
cap_RepeaterAuth_Stream_Manage,
|
|
decap_RepeaterAuth_Stream_Ready,
|
|
decap_Receiver_AuthStatus,
|
|
cap_ake_transmitter_info,
|
|
decap_ake_receiver_info,
|
|
};
|
|
|
|
/* HDCP protocol message capulate & decapulate functions for DP_LINK */
|
|
extern int (*proto_dp[])(uint8_t *, size_t *,
|
|
struct hdcp_tx_ctx *,
|
|
struct hdcp_rx_ctx *);
|
|
|
|
int ske_generate_sessionkey(uint32_t lk_type, uint8_t *enc_skey, int share_skey)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_generate_skey(lk_type,
|
|
enc_skey, HDCP_SKE_SKEY_LEN,
|
|
share_skey);
|
|
if (ret) {
|
|
hdcp_err("generate_session_key() is failed with %x\n", ret);
|
|
return ERR_GENERATE_SESSION_KEY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ske_generate_riv(uint8_t *out)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_generate_riv(out, HDCP_RTX_BYTE_LEN);
|
|
if (ret) {
|
|
hdcp_err("teei_generate_riv() is failed with %x\n", ret);
|
|
return ERR_GENERATE_NON_SECKEY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lc_generate_rn(uint8_t *out, size_t out_len)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_gen_rn(out, out_len);
|
|
if (ret) {
|
|
hdcp_err("lc_generate_rn() is failed with %x\n", ret);
|
|
return ERR_GENERATE_NON_SECKEY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lc_make_hmac(struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx, uint32_t lk_type)
|
|
{
|
|
int ret;
|
|
|
|
if ((rx_ctx->version != HDCP_VERSION_2_0) &&
|
|
tx_ctx->lc_precomp && rx_ctx->lc_precomp) {
|
|
ret = teei_gen_lc_hmac(lk_type,
|
|
tx_ctx->lsb16_hmac);
|
|
if (ret) {
|
|
hdcp_err("compute_lc_whmac() is failed with %x\n", ret);
|
|
return ERR_COMPUTE_LC_HMAC;
|
|
}
|
|
} else {
|
|
hdcp_err("LC precomp is not supported\n");
|
|
return ERR_COMPUTE_LC_HMAC;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int lc_compare_hmac(uint8_t *rx_hmac, size_t hmac_len)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_compare_lc_hmac(rx_hmac, hmac_len);
|
|
if (ret) {
|
|
hdcp_err("compare_lc_hmac_val() is failed with %x\n", ret);
|
|
return ERR_COMPARE_LC_HMAC;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int ake_generate_rtx(uint32_t lk_type, uint8_t *out, size_t out_len)
|
|
{
|
|
int ret;
|
|
|
|
/* IIA Spec does not use TxCaps */
|
|
ret = teei_gen_rtx(lk_type, out, out_len, NULL, 0);
|
|
if (ret) {
|
|
hdcp_err("generate rtx is failed with 0x%x\n", ret);
|
|
return ERR_GENERATE_NON_SECKEY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ake_set_tx_info(struct hdcp_tx_ctx *tx_ctx)
|
|
{
|
|
int ret;
|
|
uint8_t version[HDCP_VERSION_LEN];
|
|
uint8_t caps_mask[HDCP_CAPABILITY_MASK_LEN];
|
|
|
|
/* Init Version & Precomputation */
|
|
ret = teei_get_txinfo(version, HDCP_VERSION_LEN,
|
|
caps_mask, HDCP_CAPABILITY_MASK_LEN);
|
|
if (ret) {
|
|
hdcp_err("Get Tx info is failed with 0x%x\n", ret);
|
|
return ERR_GET_TX_INFO;
|
|
}
|
|
hdcp_debug("Tx: version(0x%02x) Caps_maks(0x%02x%02x)\n",
|
|
version[0], caps_mask[0], caps_mask[1]);
|
|
|
|
tx_ctx->version = version[0];
|
|
tx_ctx->lc_precomp = caps_mask[1];
|
|
|
|
/* todo: check seq_num_M init position*/
|
|
tx_ctx->seq_num_M = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int ake_set_rx_info(struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
uint8_t version[HDCP_VERSION_LEN];
|
|
uint8_t caps_mask[HDCP_CAPABILITY_MASK_LEN];
|
|
|
|
|
|
version[0] = rx_ctx->version;
|
|
caps_mask[0] = 0x00; /* reserved field in IIA spec */
|
|
caps_mask[1] = rx_ctx->lc_precomp;
|
|
|
|
/* Init Version & Precomputation */
|
|
ret = teei_set_rxinfo(version, HDCP_VERSION_LEN,
|
|
caps_mask, HDCP_CAPABILITY_MASK_LEN);
|
|
if (ret) {
|
|
hdcp_err("Get Tx info is failed with 0x%x\n", ret);
|
|
return ERR_SET_RX_INFO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ake_set_rrx(uint8_t *rrx, size_t rrx_len)
|
|
{
|
|
int ret;
|
|
|
|
/* set Rrx value */
|
|
ret = teei_set_rrx(rrx, rrx_len);
|
|
if (ret) {
|
|
hdcp_err("set rrx is failed with 0x%x\n", ret);
|
|
return ERR_SET_RRX;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ake_verify_cert(uint8_t *cert, size_t cert_len,
|
|
uint8_t *rrx, size_t rrx_len,
|
|
uint8_t *rx_caps, size_t rx_caps_len)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_verify_cert(cert, cert_len,
|
|
rrx, rrx_len,
|
|
rx_caps, rx_caps_len);
|
|
if (ret) {
|
|
hdcp_err("teei_verify_cert() is failed with %x\n", ret);
|
|
return ERR_VERIFY_CERT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ake_generate_masterkey(uint32_t lk_type, uint8_t *enc_mkey, size_t elen)
|
|
{
|
|
int ret;
|
|
|
|
/* Generate Encrypted & Wrapped Master Key */
|
|
ret = teei_generate_master_key(lk_type, enc_mkey, elen);
|
|
if (ret) {
|
|
hdcp_err("generate_master_key() is failed with %x\n", ret);
|
|
return ERR_GENERATE_MASTERKEY;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ake_compare_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_compare_ake_hmac(rx_hmac, rx_hmac_len);
|
|
if (ret) {
|
|
hdcp_err("teei_compare_hmac() is failed with %x\n", ret);
|
|
return ERR_COMPUTE_AKE_HMAC;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ake_store_master_key(uint8_t *ekh_mkey, size_t ekh_mkey_len)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_set_pairing_info(ekh_mkey, ekh_mkey_len);
|
|
if (ret) {
|
|
hdcp_err("teei_store_pairing_info() is failed with %x\n", ret);
|
|
return ERR_STORE_MASTERKEY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ake_find_masterkey(int *found_km,
|
|
uint8_t *ekh_mkey, size_t ekh_mkey_len,
|
|
uint8_t *m, size_t m_len)
|
|
{
|
|
int ret;
|
|
|
|
ret = teei_get_pairing_info(ekh_mkey, ekh_mkey_len, m, m_len);
|
|
if (ret) {
|
|
if (ret == E_HDCP_PRO_INVALID_RCV_ID) {
|
|
hdcp_info("RCV id is not found\n");
|
|
*found_km = 0;
|
|
return 0;
|
|
} else {
|
|
*found_km = 0;
|
|
hdcp_err("teei_store_pairing_info() is failed with %x\n", ret);
|
|
return ERR_FIND_MASTERKEY;
|
|
}
|
|
}
|
|
|
|
*found_km = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cap_ake_init(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
size_t rtx_len;
|
|
int ret;
|
|
|
|
/* Buffer Check */
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
rtx_len = HDCP_AKE_RTX_BYTE_LEN;
|
|
|
|
/* Generate rtx */
|
|
ret = ake_generate_rtx(HDCP_LINK_TYPE_IIA, tx_ctx->rtx, rtx_len);
|
|
if (ret) {
|
|
hdcp_err("failed to generate rtx\n");
|
|
return ERR_GENERATE_RTX;
|
|
}
|
|
|
|
/* Make Message */
|
|
m[0] = AKE_INIT;
|
|
memcpy(&m[1], tx_ctx->rtx, rtx_len);
|
|
*m_len = 1 + rtx_len;
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("HDCP: cap_ake_init(%lu) \n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cap_ake_transmitter_info(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
/* Set info */
|
|
ret = ake_set_tx_info(tx_ctx);
|
|
if (ret) {
|
|
hdcp_err("failed to set info\n");
|
|
return ERR_SET_INFO;
|
|
}
|
|
|
|
|
|
/* Make Message */
|
|
m[0] = AKE_TRANSMITTER_INFO;
|
|
m[1] = 0x0;
|
|
m[2] = 0x06;
|
|
m[3] = tx_ctx->version;
|
|
m[4] = 0x0;
|
|
m[5] = tx_ctx->lc_precomp;
|
|
*m_len = 6;
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("cap_ake_transmitter_info(%lu) \n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cap_ake_no_stored_km(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
uint8_t enc_mkey[HDCP_AKE_ENCKEY_BYTE_LEN];
|
|
int ret;
|
|
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) ||
|
|
(rx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
/* Verify Certificate */
|
|
/* IIA spec does not define rrx and RxCaps with Cert message */
|
|
ret = ake_verify_cert(rx_ctx->cert, sizeof(rx_ctx->cert),
|
|
NULL, 0,
|
|
&(rx_ctx->repeater), HDCP_RX_CAPS_LEN);
|
|
if (ret)
|
|
/* authentication failure &
|
|
* aborts the authentication protocol */
|
|
return ret;
|
|
|
|
/* Generate/Encrypt master key */
|
|
ret = ake_generate_masterkey(HDCP_LINK_TYPE_IIA,
|
|
enc_mkey, sizeof(enc_mkey));
|
|
/* Generate/Encrypt master key */
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Make Message */
|
|
m[0] = AKE_NO_STORED_KM;
|
|
memcpy(&m[1], enc_mkey, HDCP_AKE_ENCKEY_BYTE_LEN);
|
|
*m_len = 1 + HDCP_AKE_ENCKEY_BYTE_LEN;
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("cap_ake_no_stored_km(%lu)\n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cap_ake_stored_km(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
int found_km;
|
|
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
/* Generate/Encrypt master key */
|
|
ret = ake_find_masterkey(&found_km,
|
|
tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN,
|
|
tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
|
|
if (ret || !found_km) {
|
|
hdcp_err("find_masterkey() is failed with 0x%x\n", ret);
|
|
return -1;
|
|
}
|
|
|
|
/* Make Message */
|
|
m[0] = AKE_STORED_KM;
|
|
memcpy(&m[1], tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN);
|
|
memcpy(&m[1] + HDCP_AKE_EKH_MKEY_BYTE_LEN, tx_ctx->m, HDCP_AKE_M_BYTE_LEN);
|
|
*m_len = 1 + HDCP_AKE_EKH_MKEY_BYTE_LEN + HDCP_AKE_M_BYTE_LEN;
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("cap_ake_stored_km(%lu) \n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int decap_ake_send_cert(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_received_msg(m, *m_len, 2 + HDCP_RX_CERT_LEN, AKE_SEND_CERT);
|
|
if (ret)
|
|
return ret;
|
|
|
|
rx_ctx->repeater = m[1];
|
|
memcpy(rx_ctx->cert, &m[2], HDCP_RX_CERT_LEN);
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("decap_ake_send_cert(%lu) \n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int decap_ake_receiver_info(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_received_msg(m, *m_len, 6, AKE_RECEIVER_INFO);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if ((m[2] < 6) && (m[1] == 0)) {
|
|
hdcp_err("AKE_Receiver_Info, send wrong length\n");
|
|
return ERR_WRONG_MESSAGE_LENGTH;
|
|
}
|
|
|
|
rx_ctx->version = m[3];
|
|
rx_ctx->lc_precomp = m[5];
|
|
|
|
ret = ake_set_rx_info(rx_ctx);
|
|
if (ret) {
|
|
hdcp_err("AKE set rx info failed. ret(0x%x)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("decap_ake_receiver_info()\n");
|
|
hdcp_debug("version: 0x%02x\n", rx_ctx->version);
|
|
hdcp_debug("HDCP lc_precomp: 0x%02x\n", rx_ctx->lc_precomp);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int decap_ake_send_rrx(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
uint8_t rrx[HDCP_RRX_BYTE_LEN] = {0};
|
|
|
|
ret = check_received_msg(m, *m_len, 1 + HDCP_RRX_BYTE_LEN, AKE_SEND_RRX);
|
|
if (ret)
|
|
return ret;
|
|
|
|
memcpy(rx_ctx->rrx, &m[1], HDCP_RRX_BYTE_LEN);
|
|
memcpy(rrx, &m[1], HDCP_RRX_BYTE_LEN);
|
|
ret = ake_set_rrx(rrx, HDCP_RRX_BYTE_LEN);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("decap_ake_send_rrx\n");
|
|
hdcp_hexdump(rx_ctx->rrx, HDCP_RRX_BYTE_LEN);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int decap_ake_send_h_prime(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_received_msg(m, *m_len, 1 + HDCP_HMAC_SHA256_LEN,
|
|
AKE_SEND_H_PRIME);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* compare H == H' */
|
|
ret = ake_compare_hmac(&m[1], HDCP_HMAC_SHA256_LEN);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("decap_ake_send_h_prime\n");
|
|
hdcp_debug("given hmac:\n");
|
|
hdcp_hexdump(&m[1], HDCP_HMAC_SHA256_LEN);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int decap_ake_send_pairing_info(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_received_msg(m, *m_len, 1 + HDCP_AKE_EKH_MKEY_BYTE_LEN,
|
|
AKE_SEND_PAIRING_INFO);
|
|
if (ret)
|
|
return ret;
|
|
|
|
memcpy(tx_ctx->ekh_mkey, &m[1], HDCP_AKE_EKH_MKEY_BYTE_LEN);
|
|
|
|
/* Store the Key */
|
|
ret = ake_store_master_key(tx_ctx->ekh_mkey, HDCP_AKE_EKH_MKEY_BYTE_LEN);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#ifdef HDCP_AKE_DEBUG
|
|
hdcp_debug("decap_ake_send_pairing_info(%lu)\n", *m_len);
|
|
hdcp_debug("rx_ctx->ekh_mkey:\n");
|
|
hdcp_hexdump(tx_ctx->ekh_mkey, HDCP_AKE_MKEY_BYTE_LEN);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cap_lc_init(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) || (rx_ctx ==
|
|
NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
/* Generate rn */
|
|
ret = lc_generate_rn(tx_ctx->rn, HDCP_RTX_BYTE_LEN);
|
|
if (ret) {
|
|
hdcp_err("failed to generate rtx\n");
|
|
return ERR_GENERATE_RN;
|
|
}
|
|
|
|
/* Make Message */
|
|
m[0] = LC_INIT;
|
|
memcpy(&m[1], tx_ctx->rn, HDCP_RTX_BYTE_LEN);
|
|
*m_len = 1 + HDCP_RTX_BYTE_LEN;
|
|
|
|
if ((rx_ctx->version != HDCP_VERSION_2_0) &&
|
|
tx_ctx->lc_precomp &&
|
|
rx_ctx->lc_precomp) {
|
|
/* compute HMAC,
|
|
* return the least significant 128-bits,
|
|
* the most significant 128-bits wrapped */
|
|
ret = lc_make_hmac(tx_ctx, rx_ctx, 0); // last param 0 have to be checked, PKY
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HDCP_LC_DEBUG
|
|
hdcp_debug("LC_Init(%u)\n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cap_rtt_challenge(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
/* Make Message */
|
|
m[0] = RTT_CHALLENGE;
|
|
memcpy(&m[1], tx_ctx->lsb16_hmac, HDCP_HMAC_SHA256_LEN / 2);
|
|
*m_len = 1 + (HDCP_HMAC_SHA256_LEN / 2);
|
|
|
|
#ifdef HDCP_LC_DEBUG
|
|
hdcp_debug("RTT_Challenge(%lu)\n", *m_len);
|
|
hdcp_hexdump(m, *m_lne);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int decap_lc_send_l_prime(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
uint8_t *rx_hmac;
|
|
size_t len;
|
|
int ret;
|
|
|
|
/* find length depending precomputation */
|
|
if ((rx_ctx->version == HDCP_VERSION_2_0) || !tx_ctx->lc_precomp ||
|
|
!rx_ctx->lc_precomp)
|
|
len = HDCP_HMAC_SHA256_LEN;
|
|
else
|
|
len = HDCP_HMAC_SHA256_LEN/2;
|
|
|
|
ret = check_received_msg(m, *m_len, 1 + len, LC_SEND_L_PRIME);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* No_precomputation: compare hmac
|
|
precomputation: compare the most significant 128bits of L & L' */
|
|
rx_hmac = &m[1];
|
|
ret = lc_compare_hmac(rx_hmac, len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#ifdef HDCP_LC_DEBUG
|
|
hdcp_debug("LC_Send_L_prime\n");
|
|
hdcp_debug("rx_hmac:\n");
|
|
hdcp_hexdump(rx_hmac, len);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int decap_rtt_ready(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_received_msg(m, *m_len, 1, RTT_READY);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#ifdef HDCP_LC_DEBUG
|
|
hdcp_debug("RTT_Ready\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cap_ske_send_eks(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
uint8_t enc_skey[HDCP_AKE_MKEY_BYTE_LEN];
|
|
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL) ||
|
|
(rx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
/* Generate riv */
|
|
if (!tx_ctx->share_skey) {
|
|
ret = ske_generate_riv(tx_ctx->riv);
|
|
if (ret)
|
|
return ERR_GENERATE_RIV;
|
|
}
|
|
|
|
/* Generate encrypted Session Key */
|
|
ret = ske_generate_sessionkey(0, enc_skey, tx_ctx->share_skey);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Make Message */
|
|
m[0] = SKE_SEND_EKS;
|
|
memcpy(&m[1], enc_skey, HDCP_AKE_MKEY_BYTE_LEN);
|
|
memcpy(&m[1 + HDCP_AKE_MKEY_BYTE_LEN], tx_ctx->riv, HDCP_RTX_BYTE_LEN);
|
|
*m_len = 1 + HDCP_AKE_MKEY_BYTE_LEN + HDCP_RTX_BYTE_LEN;
|
|
|
|
#ifdef HDCP_SKE_DEBUG
|
|
hdcp_debug("SKE_Send_Eks(%lu)\n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef TEST_HDCP_V2_0
|
|
int parse_rcvid_list(uint8_t *msg, struct hdcp_tx_ctx *tx_ctx)
|
|
{
|
|
/* get PRE META values */
|
|
tx_ctx->rcv_list.devs_exd = (uint8_t)*msg;
|
|
tx_ctx->rcv_list.cascade_exd = (uint8_t)*(msg + 1);
|
|
|
|
/* get META values */
|
|
msg += HDCP_RP_RCV_LIST_PRE_META_LEN;
|
|
tx_ctx->rcv_list.devs_count = (uint8_t)*msg;
|
|
tx_ctx->rcv_list.depth = (uint8_t)*(msg + 1);
|
|
memcpy(tx_ctx->rcv_list.hmac_prime, (uint8_t *)(msg + 2), 32);
|
|
|
|
/* get receiver ID list */
|
|
msg += 34;
|
|
memcpy(tx_ctx->rcv_list.rcv_id, msg, tx_ctx->rcv_list.devs_count * HDCP_RCV_ID_LEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void convert_rcvlist2authmsg(struct hdcp_rcvlist *rcv_list, uint8_t *src_msg, size_t *msg_len)
|
|
{
|
|
int i;
|
|
*msg_len = 0;
|
|
|
|
for (i = 0; i < rcv_list->devs_count; i++) {
|
|
memcpy(src_msg + *msg_len, rcv_list->rcv_id[i], HDCP_RCV_ID_LEN);
|
|
*msg_len += HDCP_RCV_ID_LEN;
|
|
}
|
|
|
|
/* concatinate DEPTH */
|
|
memcpy(src_msg + *msg_len, &rcv_list->depth, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate DEVICE COUNT */
|
|
memcpy(src_msg + *msg_len, &rcv_list->devs_count, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate MAX DEVS EXCEEDED */
|
|
memcpy(src_msg + *msg_len, &rcv_list->devs_exd, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate MAX CASCADE EXCEEDED */
|
|
memcpy(src_msg + *msg_len, &rcv_list->cascade_exd, 1);
|
|
*msg_len += 1;
|
|
}
|
|
#else
|
|
int parse_rcvid_list(uint8_t *msg, struct hdcp_tx_ctx *tx_ctx)
|
|
{
|
|
/* get PRE META values */
|
|
tx_ctx->rpauth_info.devs_exd = (uint8_t)*msg;
|
|
tx_ctx->rpauth_info.cascade_exd = (uint8_t)*(msg + 1);
|
|
|
|
/* get META values */
|
|
msg += HDCP_RP_RCV_LIST_PRE_META_LEN;
|
|
tx_ctx->rpauth_info.devs_count = (uint8_t)*msg;
|
|
tx_ctx->rpauth_info.depth = (uint8_t)*(msg + 1);
|
|
tx_ctx->rpauth_info.hdcp2_down = (uint8_t)*(msg + 2);
|
|
tx_ctx->rpauth_info.hdcp1_down = (uint8_t)*(msg + 3);
|
|
memcpy(tx_ctx->rpauth_info.seq_num_v, (uint8_t *)(msg + 4), 3);
|
|
memcpy(tx_ctx->rpauth_info.v_prime, (uint8_t *)(msg + 7), 16);
|
|
|
|
/* get receiver ID list */
|
|
msg += HDCP_RP_RCV_LIST_META_LEN;
|
|
if (tx_ctx->rpauth_info.devs_count > HDCP_RCV_DEVS_COUNT_MAX) {
|
|
hdcp_err("invalid DEVS count (%d)\n", tx_ctx->rpauth_info.devs_count);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(tx_ctx->rpauth_info.u_rcvid.arr, msg, tx_ctx->rpauth_info.devs_count * HDCP_RCV_ID_LEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void convert_rcvlist2authmsg(struct hdcp_rpauth_info *rpauth_info, uint8_t *src_msg, size_t *msg_len)
|
|
{
|
|
int i;
|
|
*msg_len = 0;
|
|
|
|
for (i = 0; i < rpauth_info->devs_count; i++) {
|
|
memcpy(src_msg + *msg_len, rpauth_info->u_rcvid.arr[i], HDCP_RCV_ID_LEN);
|
|
*msg_len += HDCP_RCV_ID_LEN;
|
|
}
|
|
|
|
/* concatinate DEPTH */
|
|
memcpy(src_msg + *msg_len, &rpauth_info->depth, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate DEVICE COUNT */
|
|
memcpy(src_msg + *msg_len, &rpauth_info->devs_count, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate MAX DEVS EXCEEDED */
|
|
memcpy(src_msg + *msg_len, &rpauth_info->devs_exd, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate MAX CASCADE EXCEEDED */
|
|
memcpy(src_msg + *msg_len, &rpauth_info->cascade_exd, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate HDCP2 REPEATER DOWNSTREAM */
|
|
memcpy(src_msg + *msg_len, &rpauth_info->hdcp2_down, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate HDCP1 DEVICE DOWNSTREAM */
|
|
memcpy(src_msg + *msg_len, &rpauth_info->hdcp1_down, 1);
|
|
*msg_len += 1;
|
|
|
|
/* concatinate seq_num_v */
|
|
memcpy(src_msg + *msg_len, &rpauth_info->seq_num_v, 3);
|
|
*msg_len += 3;
|
|
}
|
|
#endif
|
|
|
|
static int decap_RepeaterAuth_send_ReceiverID_List(uint8_t *m,
|
|
size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
size_t hmac_prime_len;
|
|
size_t msg_len;
|
|
uint8_t source_msg[256];
|
|
int ret;
|
|
|
|
if (rx_ctx->version == HDCP_VERSION_2_0)
|
|
hmac_prime_len = HDCP_HMAC_SHA256_LEN;
|
|
else
|
|
hmac_prime_len = HDCP_HMAC_SHA256_LEN/2;
|
|
|
|
ret = check_received_msg(m, *m_len, 0, REPEATERAUTH_SEND_RECEIVERID_LIST);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (parse_rcvid_list(m + 1, tx_ctx))
|
|
return -1;
|
|
|
|
convert_rcvlist2authmsg(&tx_ctx->rpauth_info, source_msg, &msg_len);
|
|
|
|
ret = teei_set_rcvlist_info(NULL, NULL, tx_ctx->rpauth_info.v_prime,
|
|
source_msg, tx_ctx->rpauth_info.v, &(tx_ctx->rpauth_info.valid));
|
|
|
|
tx_ctx->rpauth_info.valid = 0;
|
|
if (ret) {
|
|
tx_ctx->rpauth_info.valid = 1;
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HDCP_TX_REPEATER_DEBUG
|
|
hdcp_debug("decap_RepeaterAuth_send_ReceiverID_List valid (%u)\n", tx_ctx->rpauth_info.valid);
|
|
hdcp_hexdump(tx_ctx->rpauth_info.v, HDCP_RP_HMAC_V_LEN / 2);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cap_RepeaterAuth_Send_Ack(uint8_t *m, size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
/* Make Message */
|
|
m[0] = REPEATERAUTH_SEND_ACK;
|
|
if(tx_ctx->rpauth_info.valid == 0)
|
|
memcpy(&m[1], tx_ctx->rpauth_info.v, HDCP_RP_HMAC_V_LEN / 2);
|
|
else
|
|
return ERR_SEND_ACK;
|
|
|
|
*m_len = 1 + (HDCP_RP_HMAC_V_LEN / 2);
|
|
|
|
#ifdef HDCP_TX_REPEATER_DEBUG
|
|
hdcp_debug("make_RepeaterAuth_Send_Ack(%u)\n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int decap_Receiver_AuthStatus(uint8_t *m,
|
|
size_t *m_len, struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_received_msg(m, (int)*m_len, 0, RECEIVER_AUTHSTATUS);
|
|
if (ret)
|
|
return ret;
|
|
|
|
tx_ctx->rp_reauth = m[3];
|
|
#ifdef HDCP_TX_REPEATER_DEBUG
|
|
hdcp_debug("get_Receiver_AuthStatus(%u)\n", *m_len);
|
|
hdcp_debug("receiver reauth req: %u\n", tx_ctx->rp_reauth);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int cap_RepeaterAuth_Stream_Manage(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int i;
|
|
uint8_t *dst;
|
|
uint16_t stmp;
|
|
uint32_t itmp;
|
|
|
|
if ((m == NULL) || (m_len == NULL) || (tx_ctx == NULL))
|
|
return ERR_WRONG_BUFFER;
|
|
|
|
if (tx_ctx->stream_ctrl.str_num > HDCP_TX_REPEATER_MAX_STREAM)
|
|
return ERR_EXCEED_MAX_STREAM;
|
|
|
|
/* Make Message */
|
|
m[0] = REPEATERAUTH_STREAM_MANAGE;
|
|
itmp = htonl(tx_ctx->seq_num_M);
|
|
memcpy(&m[1], (uint8_t *)&itmp + 1, 3);
|
|
stmp = htons(tx_ctx->stream_ctrl.str_num);
|
|
memcpy(&m[4], &stmp, sizeof(stmp));
|
|
|
|
for (i = 0; i < tx_ctx->stream_ctrl.str_num; i++) {
|
|
dst = (uint8_t *)(&m[6] + STREAM_INFO_SIZE * i);
|
|
itmp = htonl(tx_ctx->stream_ctrl.str_info[i].ctr);
|
|
memcpy(dst, &itmp, sizeof(itmp));
|
|
stmp = htons(tx_ctx->stream_ctrl.str_info[i].pid);
|
|
memcpy(dst + sizeof(itmp), &stmp, sizeof(stmp));
|
|
dst[STREAM_INFO_SIZE - 1] = tx_ctx->stream_ctrl.str_info[i].type;
|
|
}
|
|
|
|
*m_len = HDCP_PROTO_MSG_ID_LEN + STREAM_ELEM_SIZE + STREAM_INFO_SIZE * i;
|
|
|
|
/* seq_num_M++ */
|
|
tx_ctx->seq_num_M++;
|
|
|
|
/* save message to make M */
|
|
memcpy(tx_ctx->strmsg, &m[6], *m_len - 6);
|
|
memcpy(tx_ctx->strmsg + *m_len - 6, &m[1], 3);
|
|
tx_ctx->strmsg_len = *m_len - 3;
|
|
|
|
#ifdef HDCP_TX_REPEATER_DEBUG
|
|
hdcp_debug("strmsg(len: %d): \n", tx_ctx->strmsg_len);
|
|
hdcp_hexdump(tx_ctx->strmsg, tx_ctx->strmsg_len);
|
|
|
|
hdcp_debug("message(len: %d): \n", *m_len);
|
|
hdcp_hexdump(m, *m_len);
|
|
|
|
hdcp_debug("seq_num_M: %d\n", tx_ctx->seq_num_M);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int decap_RepeaterAuth_Stream_Ready(uint8_t *m, size_t *m_len,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret = 0;
|
|
/* Not support yet*/
|
|
#if 0
|
|
ret = check_received_msg(m, *m_len, 1 + HDCP_HMAC_SHA256_LEN, REPEATERAUTH_STREAM_READY);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* compute M and compare M == M' */
|
|
ret = teei_verify_m_prime(&m[1], tx_ctx->strmsg, tx_ctx->strmsg_len);
|
|
if (ret)
|
|
return ret;
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
int cap_protocol_msg(uint8_t msg_id,
|
|
uint8_t *msg,
|
|
size_t *msg_len,
|
|
uint32_t lk_type,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
/* todo: check upper boundary */
|
|
int ret = 0;
|
|
int (**proto_func)(uint8_t *, size_t *,
|
|
struct hdcp_tx_ctx *,
|
|
struct hdcp_rx_ctx *);
|
|
|
|
if (lk_type == HDCP_LINK_TYPE_IIA)
|
|
proto_func = proto_iia;
|
|
#if defined(CONFIG_HDCP2_DP_ENABLE)
|
|
else if(lk_type == HDCP_LINK_TYPE_DP)
|
|
proto_func = proto_dp;
|
|
#endif
|
|
else {
|
|
hdcp_err("invalid link type(%d)\n", lk_type);
|
|
return -1;
|
|
}
|
|
|
|
if (msg_id > 1) {
|
|
ret = proto_func[msg_id - 2](msg, msg_len, tx_ctx, rx_ctx);
|
|
return ret;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int decap_protocol_msg(uint8_t msg_id,
|
|
uint8_t *msg,
|
|
size_t msg_len,
|
|
uint32_t lk_type,
|
|
struct hdcp_tx_ctx *tx_ctx,
|
|
struct hdcp_rx_ctx *rx_ctx)
|
|
{
|
|
int ret = 0;
|
|
int (**proto_func)(uint8_t *, size_t *,
|
|
struct hdcp_tx_ctx *,
|
|
struct hdcp_rx_ctx *);
|
|
|
|
/* todo: check upper boundary */
|
|
if (lk_type == HDCP_LINK_TYPE_IIA)
|
|
proto_func = proto_iia;
|
|
#if defined(CONFIG_HDCP2_DP_ENABLE)
|
|
else if(lk_type == HDCP_LINK_TYPE_DP)
|
|
proto_func = proto_dp;
|
|
#endif
|
|
else {
|
|
hdcp_err("HDCP: invalid link type(%d)\n", lk_type);
|
|
return -1;
|
|
}
|
|
|
|
if (msg_id > 1) {
|
|
ret = proto_func[msg_id - 2](msg, &msg_len, tx_ctx, rx_ctx);
|
|
return ret;
|
|
}
|
|
|
|
else
|
|
return -1;
|
|
}
|