genesys: scaler: Remove FuGenesysMtkFooter and XOR encryption

This commit is contained in:
Gaël PORTAY 2022-05-31 16:32:38 +02:00 committed by Gaël PORTAY
parent 0c8079b13c
commit 0fbd5e17fe
4 changed files with 80 additions and 376 deletions

View File

@ -15,6 +15,8 @@
#include "fu-genesys-scaler-device.h"
#include "fu-genesys-scaler-firmware.h"
#define GENESYS_SCALER_BANK_SIZE 0x200000U
#define GENESYS_SCALER_MSTAR_READ 0x7a
#define GENESYS_SCALER_MSTAR_WRITE 0x7b
#define GENESYS_SCALER_MSTAR_DATA_OUT 0x7c
@ -72,7 +74,6 @@ struct _FuGenesysScalerDevice {
guint16 gpio_out_reg;
guint16 gpio_en_reg;
guint8 gpio_val;
FuGenesysMtkFooter footer;
};
G_DEFINE_TYPE(FuGenesysScalerDevice, fu_genesys_scaler_device, FU_TYPE_DEVICE)
@ -1720,70 +1721,27 @@ fu_genesys_scaler_device_prepare_firmware(FuDevice *device,
{
FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device);
g_autoptr(FuFirmware) firmware = fu_genesys_scaler_firmware_new();
g_autoptr(FuFirmware) footer = fu_firmware_new();
g_autoptr(FuFirmware) payload = fu_firmware_new();
g_autoptr(GBytes) fw_payload = NULL;
g_autoptr(GBytes) fw_footer = NULL;
g_autoptr(GBytes) blob_payload = NULL;
g_autoptr(GBytes) blob_public_key = NULL;
/* parse firmware */
if (!fu_firmware_parse(firmware, fw, flags, error))
return NULL;
/* payload */
fw_payload = fu_common_bytes_new_offset(fw,
0,
g_bytes_get_size(fw) - sizeof(FuGenesysMtkFooter),
error);
if (fw_payload == NULL)
/* check public-key */
blob_public_key =
fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_SIGNATURE, error);
if (blob_public_key == NULL)
return NULL;
if (!fu_firmware_parse(payload, fw_payload, flags, error))
return NULL;
fu_firmware_set_id(payload, FU_FIRMWARE_ID_PAYLOAD);
fu_firmware_add_image(firmware, payload);
/* check size */
if (g_bytes_get_size(fw_payload) > fu_device_get_firmware_size_max(device)) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware too large, got 0x%x, expected <= 0x%x",
(guint)g_bytes_get_size(fw_payload),
(guint)fu_device_get_firmware_size_max(device));
return NULL;
}
/* footer */
fw_footer = fu_common_bytes_new_offset(fw,
g_bytes_get_size(fw) - sizeof(FuGenesysMtkFooter),
sizeof(FuGenesysMtkFooter),
error);
if (!fu_firmware_parse(footer, fw_footer, flags, error))
return NULL;
if (!fu_memcpy_safe((guint8 *)&self->footer,
sizeof(self->footer),
0, /* dst */
g_bytes_get_data(fw_footer, NULL),
g_bytes_get_size(fw_footer),
0, /* src */
sizeof(self->footer),
error))
return NULL;
fu_genesys_scaler_firmware_decrypt((guint8 *)&self->footer, sizeof(self->footer));
if (g_getenv("FWUPD_GENESYS_SCALER_VERBOSE") != NULL) {
fu_common_dump_raw(G_LOG_DOMAIN,
"Footer",
(const guint8 *)&self->footer,
sizeof(self->footer));
"PublicKey",
g_bytes_get_data(blob_public_key, NULL),
g_bytes_get_size(blob_public_key));
}
if (memcmp(self->footer.data.header.default_head,
MTK_RSA_HEADER,
sizeof(self->footer.data.header.default_head)) != 0) {
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid footer");
return NULL;
}
if (memcmp(&self->footer.data.public_key,
if (memcmp(g_bytes_get_data(blob_public_key, NULL),
&self->public_key,
sizeof(self->footer.data.public_key) && sizeof(self->public_key)) != 0 &&
sizeof(self->public_key)) != 0 &&
(flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
g_set_error_literal(error,
FWUPD_ERROR,
@ -1791,8 +1749,20 @@ fu_genesys_scaler_device_prepare_firmware(FuDevice *device,
"mismatch public-key");
return NULL;
}
fu_firmware_set_id(footer, FU_FIRMWARE_ID_HEADER);
fu_firmware_add_image(firmware, footer);
/* check size */
blob_payload = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_PAYLOAD, error);
if (blob_payload == NULL)
return NULL;
if (g_bytes_get_size(blob_payload) > fu_device_get_firmware_size_max(device)) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware too large, got 0x%x, expected <= 0x%x",
(guint)g_bytes_get_size(blob_payload),
(guint)fu_device_get_firmware_size_max(device));
return NULL;
}
/* success */
return g_steal_pointer(&firmware);
@ -1806,7 +1776,7 @@ fu_genesys_scaler_device_write_firmware(FuDevice *device,
GError **error)
{
FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device);
guint addr = fu_firmware_get_addr(firmware);
guint addr = 0;
gsize size;
const guint8 *data;
g_autofree guint8 *buf = NULL;
@ -1818,11 +1788,8 @@ fu_genesys_scaler_device_write_firmware(FuDevice *device,
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 54, NULL);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 42, NULL);
/* sanity check */
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE) && addr == 0) {
g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "invalid address");
return FALSE;
}
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE))
addr = GENESYS_SCALER_BANK_SIZE;
payload = fu_firmware_get_image_by_id(firmware, FU_FIRMWARE_ID_PAYLOAD, error);
if (payload == NULL)

View File

@ -12,26 +12,11 @@
struct _FuGenesysScalerFirmware {
FuFirmwareClass parent_instance;
FuGenesysMtkFooter footer;
guint protect_sector_addr[2];
gsize protect_sector_size[2];
guint public_key_addr;
gsize public_key_size;
guint second_image_program_addr;
FuGenesysPublicKey public_key;
};
G_DEFINE_TYPE(FuGenesysScalerFirmware, fu_genesys_scaler_firmware, FU_TYPE_FIRMWARE)
void
fu_genesys_scaler_firmware_decrypt(guint8 *buf, gsize bufsz)
{
const gchar *key = "mstar";
const gsize keylen = strlen(key);
for (guint i = 0; i < bufsz; i++)
buf[i] ^= key[i % keylen];
}
static gboolean
fu_genesys_scaler_firmware_parse(FuFirmware *firmware,
GBytes *fw,
@ -43,87 +28,41 @@ fu_genesys_scaler_firmware_parse(FuFirmware *firmware,
FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware);
gsize bufsz = 0;
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
g_autoptr(FuFirmware) firmware_payload = NULL;
g_autoptr(FuFirmware) firmware_public_key = NULL;
g_autoptr(GBytes) blob_payload = NULL;
g_autoptr(GBytes) blob_public_key = NULL;
if (!fu_memcpy_safe((guint8 *)&self->footer,
sizeof(self->footer),
if (!fu_memcpy_safe((guint8 *)&self->public_key,
sizeof(self->public_key),
0, /* dst */
buf,
bufsz,
bufsz - sizeof(self->footer), /* src */
sizeof(self->footer),
bufsz - sizeof(self->public_key), /* src */
sizeof(self->public_key),
error))
return FALSE;
fu_genesys_scaler_firmware_decrypt((guint8 *)&self->footer, sizeof(self->footer));
if (memcmp(self->footer.data.header.default_head,
MTK_RSA_HEADER,
sizeof(self->footer.data.header.default_head)) != 0) {
g_autofree gchar *str = NULL;
str = fu_common_strsafe((const gchar *)self->footer.data.header.default_head,
sizeof(self->footer.data.header.default_head));
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"invalid footer, expected %s, and got %s",
MTK_RSA_HEADER,
str);
fu_common_dump_raw(G_LOG_DOMAIN,
"PublicKey",
(const guint8 *)&self->public_key,
sizeof(self->public_key));
if (memcmp(self->public_key.N, "N = ", 4) != 0 ||
memcmp(self->public_key.E, "E = ", 4) != 0) {
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid public-key");
return FALSE;
}
if (self->footer.data.header.configuration_setting.bits.second_image) {
guint32 addr;
if (!fu_common_read_uint32_safe(
self->footer.data.header.second_image_program_addr,
sizeof(self->footer.data.header.second_image_program_addr),
0,
&addr,
G_LITTLE_ENDIAN,
error))
return FALSE;
if (addr % GENESYS_SCALER_BANK_SIZE != 0) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid address, expected a multiple of 0x%x, and got 0x%x",
GENESYS_SCALER_BANK_SIZE,
addr);
return FALSE;
}
self->second_image_program_addr = addr;
fu_firmware_set_addr(firmware, addr);
}
if (self->footer.data.header.configuration_setting.bits.decrypt_mode) {
if (!fu_common_read_uint32_safe(
self->footer.data.header.scaler_public_key_addr,
sizeof(self->footer.data.header.scaler_public_key_addr),
0,
&self->public_key_addr,
G_LITTLE_ENDIAN,
error))
return FALSE;
self->public_key_size = 0x1000;
}
/* set payload */
blob_payload = g_bytes_new(buf, bufsz - sizeof(self->public_key));
firmware_payload = fu_firmware_new_from_bytes(blob_payload);
fu_firmware_set_id(firmware_payload, FU_FIRMWARE_ID_PAYLOAD);
fu_firmware_add_image(firmware, firmware_payload);
if (self->footer.data.header.configuration_setting.bits.special_protect_sector) {
if (self->footer.data.header.protect_sector[0].area.size) {
self->protect_sector_addr[0] =
(self->footer.data.header.protect_sector[0].area.addr_high << 16) |
(self->footer.data.header.protect_sector[0].area.addr_low[1] << 8) |
(self->footer.data.header.protect_sector[0].area.addr_low[0]);
self->protect_sector_addr[0] *= 0x1000;
self->protect_sector_size[0] =
self->footer.data.header.protect_sector[0].area.size * 0x1000;
}
if (self->footer.data.header.protect_sector[1].area.size) {
self->protect_sector_addr[1] =
(self->footer.data.header.protect_sector[1].area.addr_high << 16) |
(self->footer.data.header.protect_sector[1].area.addr_low[1] << 8) |
(self->footer.data.header.protect_sector[1].area.addr_low[0]);
self->protect_sector_addr[1] *= 0x1000;
self->protect_sector_size[1] =
self->footer.data.header.protect_sector[1].area.size * 0x1000;
}
}
/* set public-key */
blob_public_key = g_bytes_new(&self->public_key, sizeof(self->public_key));
firmware_public_key = fu_firmware_new_from_bytes(blob_public_key);
fu_firmware_set_id(firmware_public_key, FU_FIRMWARE_ID_SIGNATURE);
fu_firmware_add_image(firmware, firmware_public_key);
/* success */
return TRUE;
@ -135,82 +74,14 @@ fu_genesys_scaler_firmware_export(FuFirmware *firmware,
XbBuilderNode *bn)
{
FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware);
gchar N[0x200 + 1] = {'\0'};
gchar E[0x006 + 1] = {'\0'};
if (self->footer.data.header.model_name[0] != '\0') {
fu_xmlb_builder_insert_kv(bn,
"model_name",
(const gchar *)self->footer.data.header.model_name);
}
if (self->footer.data.header.scaler_group[0] != '\0') {
fu_xmlb_builder_insert_kv(bn,
"scaler_group",
(const gchar *)self->footer.data.header.scaler_group);
}
if (self->footer.data.header.panel_type[0] != '\0') {
fu_xmlb_builder_insert_kv(bn,
"panel_type",
(const gchar *)self->footer.data.header.panel_type);
}
if (self->footer.data.header.scaler_packet_date[0] != '\0') {
fu_xmlb_builder_insert_kv(
bn,
"scaler_packet_date",
(const gchar *)self->footer.data.header.scaler_packet_date);
}
if (self->footer.data.header.scaler_packet_version[0] != '\0') {
fu_xmlb_builder_insert_kv(
bn,
"scaler_packet_version",
(const gchar *)self->footer.data.header.scaler_packet_version);
}
fu_xmlb_builder_insert_kx(bn,
"configuration_setting",
self->footer.data.header.configuration_setting.r8);
memcpy(N, self->public_key.N + 4, sizeof(N) - 1);
fu_xmlb_builder_insert_kv(bn, "N", N);
if (self->footer.data.header.configuration_setting.bits.second_image)
fu_xmlb_builder_insert_kx(bn,
"second_image_program_addr",
self->second_image_program_addr);
if (self->footer.data.header.configuration_setting.bits.decrypt_mode) {
gchar N[0x200 + 1] = {'\0'};
gchar E[0x006 + 1] = {'\0'};
fu_xmlb_builder_insert_kx(bn, "public_key_addr", self->public_key_addr);
fu_xmlb_builder_insert_kx(bn, "public_key_size", self->public_key_size);
memcpy(N, self->footer.data.public_key.N + 4, sizeof(N) - 1);
fu_xmlb_builder_insert_kv(bn, "N", N);
memcpy(E, self->footer.data.public_key.E + 4, sizeof(E) - 1);
fu_xmlb_builder_insert_kv(bn, "E", E);
}
if (self->footer.data.header.configuration_setting.bits.special_protect_sector) {
if (self->protect_sector_size[0]) {
fu_xmlb_builder_insert_kx(bn,
"protect_sector_addr0",
self->protect_sector_addr[0]);
fu_xmlb_builder_insert_kx(bn,
"protect_sector_size0",
self->protect_sector_size[0]);
}
if (self->protect_sector_size[1]) {
fu_xmlb_builder_insert_kx(bn,
"protect_sector_addr1",
self->protect_sector_addr[1]);
fu_xmlb_builder_insert_kx(bn,
"protect_sector_size1",
self->protect_sector_size[1]);
}
}
if (self->footer.data.header.configuration_setting.bits.boot_code_size_in_header) {
fu_xmlb_builder_insert_kx(bn,
"boot_code_size",
self->footer.data.header.boot_code_size);
}
memcpy(E, self->public_key.E + 4, sizeof(E) - 1);
fu_xmlb_builder_insert_kv(bn, "E", E);
}
static gboolean
@ -220,10 +91,23 @@ fu_genesys_scaler_firmware_build(FuFirmware *firmware, XbNode *n, GError **error
const gchar *tmp;
/* optional properties */
tmp = xb_node_query_text(n, "model_name", NULL);
tmp = xb_node_query_text(n, "N", NULL);
if (tmp != NULL) {
if (!fu_memcpy_safe((guint8 *)&self->footer.data.header.model_name,
sizeof(self->footer.data.header.model_name),
if (!fu_memcpy_safe((guint8 *)&self->public_key.N,
sizeof(self->public_key.N),
0x0, /* dst */
(const guint8 *)tmp,
strlen(tmp),
0x0, /* src */
strlen(tmp),
error))
return FALSE;
}
tmp = xb_node_query_text(n, "E", NULL);
if (tmp != NULL) {
if (!fu_memcpy_safe((guint8 *)&self->public_key.E,
sizeof(self->public_key.E),
0x0, /* dst */
(const guint8 *)tmp,
strlen(tmp),
@ -241,7 +125,6 @@ static GBytes *
fu_genesys_scaler_firmware_write(FuFirmware *firmware, GError **error)
{
FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware);
FuGenesysMtkFooter footer = {0x0};
g_autoptr(GByteArray) buf = g_byte_array_new();
g_autoptr(GBytes) blob = NULL;
@ -251,27 +134,8 @@ fu_genesys_scaler_firmware_write(FuFirmware *firmware, GError **error)
return NULL;
fu_byte_array_append_bytes(buf, blob);
/* "encrypted" footer */
if (!fu_memcpy_safe((guint8 *)&footer,
sizeof(footer),
0, /* dst */
(guint8 *)&self->footer,
sizeof(self->footer),
0, /* src */
sizeof(footer),
error))
return NULL;
if (!fu_memcpy_safe((guint8 *)&footer.data.header.default_head,
sizeof(footer.data.header.default_head),
0, /* dst */
(guint8 *)&MTK_RSA_HEADER,
strlen(MTK_RSA_HEADER),
0, /* src */
strlen(MTK_RSA_HEADER),
error))
return NULL;
fu_genesys_scaler_firmware_decrypt((guint8 *)&footer, sizeof(footer));
g_byte_array_append(buf, (const guint8 *)&footer, sizeof(footer));
/* public-key */
g_byte_array_append(buf, (const guint8 *)&self->public_key, sizeof(self->public_key));
/* success */
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));

View File

@ -19,132 +19,5 @@ G_DECLARE_FINAL_TYPE(FuGenesysScalerFirmware,
#define GENESYS_SCALER_BANK_SIZE 0x200000U
#define MTK_RSA_HEADER "MTK_RSA_HEADER"
typedef struct __attribute__((packed)) {
guint8 default_head[14];
guint8 reserved_0e_0f[2];
guint8 model_name[16];
guint8 reserved_20;
guint8 size[2];
guint8 reserved_23_27[5];
guint8 scaler_group[10];
guint8 reserved_32_53[34];
guint8 panel_type[10];
guint8 scaler_packet_date[8];
guint8 reserved_66_67[2];
guint8 scaler_packet_version[4];
guint8 reserved_6c_7f[20];
union {
guint8 r8;
struct {
/*
* Decrypt Mode:
*
* 0: Scaler decrypt
* 1: ISP Tool decrypt
*/
guint8 decrypt_mode : 1;
/*
* Second Image:
*
* 0: 1st image or dual image; programming address at 0x000000
* 1: 2nd image; programming address set by .second_image_program_addr
*/
guint8 second_image : 1;
/*
* Dual image turn:
*
* 0: fix second programing address set by .second_image_program_addr
* 1: support Dual image turn rule
* - TSUM: Not supported
* - MST9U: ISP Tool need update to DUT least version image address
* - HAWK: Not supported
*/
guint8 dual_image_turn : 1;
/*
* Special Protect Sector:
*
* 0: No Special Protect sector
* 1: Support Special Protect sector
*/
guint8 special_protect_sector : 1;
/*
* HAWK bypass mode
*
* 0: No support HAWK bypass mode
* 1: Support HAWK bypass mode
*/
guint8 hawk_bypass_mode : 1;
/*
* Boot Code Size in header
*
* 0: Follow original search bin address rule
* 1: Get Boot code size from header set by .boot_code_size
*/
guint8 boot_code_size_in_header : 1;
/* Reserved */
guint8 reserved_6_7 : 2;
} __attribute__((packed)) bits;
} configuration_setting;
guint8 reserved_81_85[5];
/* If configuration_setting.bits.second_image set */
guint8 second_image_program_addr[4];
/*
* If configuration_setting.bits.decrypt is set
*
* TSUM/HAWK: ISP Tool need protect flash public address cant erase and write
* MST9U: Not supported
*/
guint8 scaler_public_key_addr[4];
/*
* If configuration_setting.bits.special_protect_sector is set
*
* ISP Tool can't erase "Special Protect Sector" area.
*
* [19:00]: Protect continuous sector start.
* [23:20]: Protect sector continuous number.
*
* Examples: If need to protect FA000 ~FFFFF, Special Protect sector = 0x6000FA;
* If need to protect FA000 only, Special Protect sector = 0x1000FA;
* If no need to protect, Special Protect sector = 0x000000;
*/
union {
guint8 r24[3];
struct {
guint8 addr_low[2];
guint8 addr_high : 4;
guint8 size : 4;
} __attribute__((packed)) area;
} protect_sector[2];
/*
* If configuration.bits .second_image and .dual_image_turn are set
* and .boot_code_size.
*/
guint32 boot_code_size;
} FuGenesysMtkRsaHeader;
typedef union __attribute__((packed)) {
guint8 raw[0x312];
struct {
FuGenesysPublicKey public_key;
FuGenesysMtkRsaHeader header;
} data;
} FuGenesysMtkFooter;
void
fu_genesys_scaler_firmware_decrypt(guint8 *buf, gsize bufsz);
FuFirmware *
fu_genesys_scaler_firmware_new(void);