From 92ecbd5fb8a7c8e939b1b4dde82cc6c9ba9d41af Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Wed, 4 Jan 2017 16:36:27 +0100 Subject: nwztools/upg: move upg handling to its own file, completely rework kas handling This was a huge mess, the new is much cleaner hopefully. Change-Id: I43663d021dc8bc31662d3923e1c3da22d987ebf9 --- utils/nwztools/upgtools/upg.c | 260 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 utils/nwztools/upgtools/upg.c (limited to 'utils/nwztools/upgtools/upg.c') diff --git a/utils/nwztools/upgtools/upg.c b/utils/nwztools/upgtools/upg.c new file mode 100644 index 0000000000..44d3eca789 --- /dev/null +++ b/utils/nwztools/upgtools/upg.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 Amaury Pouly + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "upg.h" +#include +#include +#include +#include + +struct nwz_model_t g_model_list[] = +{ + { "nwz-e450", true, "8a01b624bfbfde4a1662a1772220e3c5" }, + { "nwz-e460", true, "89d813f8f966efdebd9c9e0ea98156d2" }, + { "nwz-a860", true, "a7c4af6c28b8900a783f307c1ba538c5" }, + { "nwz-a850", true, "a2efb9168616c2e84d78291295c1aa5d" }, + { "nwz-e470", true, "e4144baaa2707913f17b5634034262c4" }, + { "nwz-e580", true, "6e25f79812eca7ceed04819d833e80af" }, + /* The following keys were obtained by brute forcing firmware upgrades, + * someone with a device needs to confirm that they work */ + { "nw-a820", false, "0c9869c268e0eaa6d1ba62daab09cebc" }, + { "nwz-a10", false, "a4605e0628c9c3baeb5142ce9cb834d6" }, + { "nwz-a20", false, "e9d7185e5ac183bf26e9a5b66f983c0b" }, + { "nwz-zx100", false, "2c0bf029804f73e073154388743f84d2" }, + { 0 } +}; + +static int digit_value(char c) +{ + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'a' && c <= 'f') return c - 'a' + 10; + if(c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} + +static char hex_digit(unsigned v) +{ + return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x'; +} + +int decrypt_keysig(const char kas[NWZ_KAS_SIZE], char key[NWZ_KEY_SIZE], + char sig[NWZ_SIG_SIZE]) +{ + uint8_t src[NWZ_KAS_SIZE / 2]; + for(int index = 0; index < NWZ_KAS_SIZE / 2; index++) + { + int a = digit_value(kas[index * 2]); + int b = digit_value(kas[index * 2 + 1]); + if(a < 0 || b < 0) + return -1; + src[index] = a << 4 | b; + } + fwp_setkey("ed295076"); + fwp_crypt(src, sizeof(src), 1); + memcpy(key, src, NWZ_KEY_SIZE); + memcpy(sig, src + NWZ_KEY_SIZE, NWZ_SIG_SIZE); + return 0; +} + +void encrypt_keysig(char kas[NWZ_KEY_SIZE], + const char key[NWZ_SIG_SIZE], const char sig[NWZ_KAS_SIZE]) +{ + uint8_t src[NWZ_KAS_SIZE / 2]; + fwp_setkey("ed295076"); + memcpy(src, key, NWZ_KEY_SIZE); + memcpy(src + NWZ_KEY_SIZE, sig, NWZ_SIG_SIZE); + fwp_crypt(src, sizeof(src), 0); + for(int i = 0; i < NWZ_KAS_SIZE / 2; i++) + { + kas[2 * i] = hex_digit((src[i] >> 4) & 0xf); + kas[2 * i + 1] = hex_digit(src[i] & 0xf); + } +} + +struct upg_file_t *upg_read_memory(void *buf, size_t size, char key[NWZ_KEY_SIZE], + char *sig, void *u, generic_printf_t printf) +{ +#define cprintf(col, ...) printf(u, false, col, __VA_ARGS__) +#define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0) +#define err_printf(col, ...) printf(u, true, col, __VA_ARGS__) + struct upg_md5_t *md5 = buf; + cprintf(BLUE, "Preliminary\n"); + cprintf(GREEN, " MD5: "); + for(int i = 0; i < MD5_DIGEST_LENGTH; i++) + cprintf(YELLOW, "%02x", md5->md5[i]); + cprintf(OFF, " "); + + /* check MD5 */ + uint8_t actual_md5[MD5_DIGEST_LENGTH]; + { + MD5_CTX c; + MD5_Init(&c); + MD5_Update(&c, md5 + 1, size - sizeof(struct upg_header_t)); + MD5_Final(actual_md5, &c); + } + if(memcmp(actual_md5, md5->md5, MD5_DIGEST_LENGTH) != 0) + { + cprintf(RED, "Mismatch\n"); + err_printf(GREY, "MD5 Mismatch\n"); + return NULL; + } + cprintf(RED, "Ok\n"); + + struct upg_header_t *hdr = (void *)(md5 + 1); + /* decrypt the whole file at once */ + fwp_read(hdr, size - sizeof(*md5), hdr, (void *)key); + + cprintf(BLUE, "Header\n"); + cprintf_field(" Signature:", " "); + for(int i = 0; i < NWZ_SIG_SIZE; i++) + cprintf(YELLOW, "%c", isprint(hdr->sig[i]) ? hdr->sig[i] : '.'); + if(sig) + { + if(memcmp(hdr->sig, sig, NWZ_SIG_SIZE) != 0) + { + cprintf(RED, "Mismatch\n"); + err_printf(GREY, "Signature Mismatch\n"); + return NULL; + } + cprintf(RED, " Ok\n"); + } + else + cprintf(RED, " Can't check\n"); + cprintf_field(" Files: ", "%d\n", hdr->nr_files); + cprintf_field(" Pad: ", "0x%x\n", hdr->pad); + + + /* Do a first pass to decrypt in-place */ + cprintf(BLUE, "Files\n"); + struct upg_entry_t *entry = (void *)(hdr + 1); + for(unsigned i = 0; i < hdr->nr_files; i++, entry++) + { + cprintf(GREY, " File"); + cprintf(RED, " %d\n", i); + cprintf_field(" Offset: ", "0x%x\n", entry->offset); + cprintf_field(" Size: ", "0x%x\n", entry->size); + } + /* Do a second pass to create the file structure */ + /* create file */ + struct upg_file_t *file = malloc(sizeof(struct upg_file_t)); + memset(file, 0, sizeof(struct upg_file_t)); + file->nr_files = hdr->nr_files; + file->files = malloc(sizeof(struct upg_file_entry_t) * file->nr_files); + + entry = (void *)(hdr + 1); + for(unsigned i = 0; i < hdr->nr_files; i++, entry++) + { + memset(&file->files[i], 0, sizeof(struct upg_file_entry_t)); + file->files[i].size = entry->size; + file->files[i].data = malloc(file->files[i].size); + memcpy(file->files[i].data, buf + entry->offset, entry->size); + } + + return file; +} + +void *upg_write_memory(struct upg_file_t *file, char key[NWZ_KEY_SIZE], + char sig[NWZ_SIG_SIZE], size_t *out_size, void *u, generic_printf_t printf) +{ + if(file->nr_files == 0) + { + err_printf(GREY, "A UPG file must have at least one file\n"); + return NULL; + } + /* compute total size and create buffer */ + size_t tot_size = sizeof(struct upg_md5_t) + sizeof(struct upg_header_t) + + file->nr_files * sizeof(struct upg_entry_t); + for(int i = 0; i < file->nr_files; i++) + tot_size += ROUND_UP(file->files[i].size, 8); + /* allocate buffer */ + void *buf = malloc(tot_size); + + /* create md5 context, we push data to the context as we create it */ + struct upg_md5_t *md5 = buf; + memset(md5, 0, sizeof(*md5)); + /* create the encrypted signature and header */ + struct upg_header_t *hdr = (void *)(md5 + 1); + memcpy(hdr->sig, sig, NWZ_SIG_SIZE); + hdr->nr_files = file->nr_files; + hdr->pad = 0; + + /* create file headers */ + size_t offset = sizeof(*md5) + sizeof(*hdr) + file->nr_files * sizeof(struct upg_entry_t); + struct upg_entry_t *entry = (void *)(hdr + 1); + cprintf(BLUE, "Files\n"); + for(int i = 0; i < file->nr_files; i++) + { + entry[i].offset = offset; + entry[i].size = file->files[i].size; + offset += ROUND_UP(entry[i].size, 8); /* pad each file to a multiple of 8 for encryption */ + + cprintf(GREY, " File"); + cprintf(RED, " %d\n", i); + cprintf_field(" Offset: ", "0x%lx\n", entry[i].offset); + cprintf_field(" Size: ", "0x%lx\n", entry[i].size); + } + + /* add file data */ + for(int i = 0; i < file->nr_files; i++) + { + /* copy data to buffer, and then encrypt in-place */ + size_t r_size = ROUND_UP(file->files[i].size, 8); + void *data_ptr = (uint8_t *)buf + entry[i].offset; + memset(data_ptr, 0, r_size); /* the padding will be zero 0 */ + memcpy(data_ptr, file->files[i].data, file->files[i].size); + } + /* encrypt everything and hash everything */ + fwp_write(hdr, tot_size - sizeof(*md5), hdr, (void *)key); + /* write final MD5 */ + { + MD5_CTX c; + MD5_Init(&c); + MD5_Update(&c, (void *)hdr, tot_size - sizeof(*md5)); + MD5_Final(md5->md5, &c); + } + *out_size = tot_size; + return buf; +} + +struct upg_file_t *upg_new(void) +{ + struct upg_file_t *f = malloc(sizeof(struct upg_file_t)); + memset(f, 0, sizeof(struct upg_file_t)); + return f; +} + +void upg_append(struct upg_file_t *file, void *data, size_t size) +{ + file->files = realloc(file->files, (file->nr_files + 1) * sizeof(struct upg_file_entry_t)); + file->files[file->nr_files].data = data; + file->files[file->nr_files].size = size; + file->nr_files++; +} + +void upg_free(struct upg_file_t *file) +{ + if(file) + { + for(int i = 0; i < file->nr_files; i++) + free(file->files[i].data); + free(file->files); + } + free(file); +} -- cgit v1.2.3