From cf82f208e3ece54fd38cb7c90b77ad91aa3a4c8c Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Tue, 30 Aug 2016 17:19:30 +1000 Subject: nwztools: cleanup the code There was a lot of copy and paste, and the code was just crap. This commit tries to clarify the code and also document the encryption procedure. Hopefully I didn't break anything. Change-Id: I257793010e7cf94f2b090b30bb8608359d3886e3 --- utils/nwztools/upgtools/fwp.h | 3 +- utils/nwztools/upgtools/keysig_search.c | 108 +++++--- utils/nwztools/upgtools/keysig_search.h | 4 +- utils/nwztools/upgtools/misc.c | 1 + utils/nwztools/upgtools/misc.h | 2 +- utils/nwztools/upgtools/upgtool.c | 423 ++++++++++++++------------------ 6 files changed, 267 insertions(+), 274 deletions(-) (limited to 'utils') diff --git a/utils/nwztools/upgtools/fwp.h b/utils/nwztools/upgtools/fwp.h index 7b527d47ba..0d928fbec1 100644 --- a/utils/nwztools/upgtools/fwp.h +++ b/utils/nwztools/upgtools/fwp.h @@ -28,8 +28,9 @@ extern "C" { #endif #define NWZ_KAS_SIZE 32 -#define NWZ_KEYSIG_SIZE 51 +#define NWZ_KEYSIG_SIZE 16 #define NWZ_KEY_SIZE 8 +#define NWZ_SIG_SIZE 8 #define NWZ_EXPKEY_SIZE (NWZ_KEY_SIZE * NWZ_KEY_SIZE) #define NWZ_DES_BLOCK 8 diff --git a/utils/nwztools/upgtools/keysig_search.c b/utils/nwztools/upgtools/keysig_search.c index 6054ea43ba..7652efa233 100644 --- a/utils/nwztools/upgtools/keysig_search.c +++ b/utils/nwztools/upgtools/keysig_search.c @@ -22,8 +22,24 @@ #include "misc.h" #include "mg.h" #include +#include -#define HEX_MAJ +/* Key search methods + * + * This code tries to find the key and signature of a device using an upgrade + * file. It more or less relies on brute force and makes the following assumptions. + * It assumes the key and the signature are hexadecimal strings (it appears to be + * true thus far). The code lists all possible keys and decrypts the first + * 8 bytes of the file. If the decrypted signature happens to be an hex string, + * the code reports the key and signature as potentially valid. Note that some + * key/sig pairs may not be valid but since the likelyhood of decrypting a + * random 8-byte sequence using an hex string key and to produce an hex string + * is very small, there should be almost no false positive. + * + * Since the key is supposedly random, the code starts by looking at "balanced" + * keys: keys with slightly more digits (0-9) than letters (a-f) and then moving + * towards very unbalanced strings (only digits or only letters). + */ static uint8_t g_cipher[8]; static keysig_notify_fn_t g_notify; @@ -31,11 +47,10 @@ static uint8_t g_key[8]; static void *g_user; static bool is_hex[256]; static bool is_init = false; -#ifdef HEX_MAJ -static char hex_digits[] = "02468ABEF"; -#else -static char hex_digits[] = "02468abef"; -#endif +static uint64_t g_tot_count; +static uint64_t g_cur_count; +static int g_last_percent; +static int g_last_subpercent; static void keysig_search_init() { @@ -44,11 +59,7 @@ static void keysig_search_init() memset(is_hex, 0, sizeof(is_hex)); for(int i = '0'; i <= '9'; i++) is_hex[i] = true; -#ifdef HEX_MAJ - for(int i = 'A'; i <= 'F'; i++) -#else for(int i = 'a'; i <= 'f'; i++) -#endif is_hex[i] = true; } @@ -63,6 +74,24 @@ static inline bool is_full_ascii(uint8_t *arr) static inline bool check_stupid() { uint8_t res[8]; + // display progress + g_cur_count++; + int percent = (g_cur_count * 100ULL) / g_tot_count; + int subpercent = ((g_cur_count * 1000ULL) / g_tot_count) % 10; + if(percent != g_last_percent) + { + cprintf(RED, "%d%%", percent); + fflush(stdout); + g_last_subpercent = 0; + } + if(subpercent != g_last_subpercent) + { + cprintf(WHITE, "."); + fflush(stdout); + } + g_last_percent = percent; + g_last_subpercent = subpercent; + mg_decrypt_fw(g_cipher, 8, res, g_key); if(is_full_ascii(res)) return g_notify(g_user, g_key, res); @@ -75,7 +104,7 @@ static bool search_stupid_rec(int rem_digit, int rem_letter, int pos) return check_stupid(); if(rem_digit > 0) { - for(int i = '0'; i <= '9'; i += 2) + for(int i = '0'; i <= '9'; i++) { g_key[pos] = i; if(search_stupid_rec(rem_digit - 1, rem_letter, pos + 1)) @@ -84,11 +113,7 @@ static bool search_stupid_rec(int rem_digit, int rem_letter, int pos) } if(rem_letter > 0) { -#ifdef HEX_MAJ - for(int i = 'a' - 1; i <= 'f'; i += 2) -#else - for(int i = 'A' - 1; i <= 'F'; i += 2) -#endif + for(int i = 'a' - 1; i <= 'f'; i++) { g_key[pos] = i; if(search_stupid_rec(rem_digit, rem_letter - 1, pos + 1)) @@ -100,6 +125,12 @@ static bool search_stupid_rec(int rem_digit, int rem_letter, int pos) static bool search_stupid(int rem_digit, int rem_letter) { + cprintf(WHITE, "\n Looking for keys with "); + cprintf(RED, "%d", rem_digit); + cprintf(WHITE, " digits and "); + cprintf(RED, "%d", rem_letter); + cprintf(WHITE, " letters..."); + fflush(stdout); return search_stupid_rec(rem_digit, rem_letter, 0); } @@ -109,28 +140,35 @@ bool keysig_search_ascii_stupid(uint8_t *cipher, keysig_notify_fn_t notify, void memcpy(g_cipher, cipher, 8); g_notify = notify; g_user = user; -#if 1 - return search_stupid(4, 4) || - search_stupid(3, 5) || search_stupid(5, 3) || - search_stupid(2, 6) || search_stupid(6, 2) || - search_stupid(1, 7) || search_stupid(7, 1) || - search_stupid(0, 8) || search_stupid(8, 0); -#else -#define do(i) for(int a##i = 0; a##i < sizeof(hex_digits); a##i++) { g_key[i] = hex_digits[a##i]; -#define od() } - - do(0)do(1)do(2)do(3)do(4)do(5)do(6)do(7) - if(check_stupid()) return true; - od()od()od()od()od()od()od()od() -#undef do -#undef od - return false; -#endif + // compute number of possibilities + g_cur_count = 0; + g_tot_count = 1; + g_last_percent = -1; + for(int i = 0; i < 8; i++) + g_tot_count *= 16ULL; + cprintf(WHITE, " Search space:"); + cprintf(RED, " %llu", (unsigned long long)g_tot_count); + // sorted by probability: + bool ret = search_stupid(5, 3) // 5 digits, 3 letters: 0.281632 + || search_stupid(6, 2) // 6 digits, 2 letters: 0.234693 + || search_stupid(4, 4) // 4 digits, 4 letters: 0.211224 + || search_stupid(7, 1) // 7 digits, 1 letters: 0.111759 + || search_stupid(3, 5) // 3 digits, 5 letters: 0.101388 + || search_stupid(2, 6) // 2 digits, 6 letters: 0.030416 + || search_stupid(8, 0) // 8 digits, 0 letters: 0.023283 + || search_stupid(1, 7) // 1 digits, 7 letters: 0.005214 + || search_stupid(0, 8);// 0 digits, 8 letters: 0.000391 + cprintf(OFF, "\n"); + return ret; } bool keysig_search_ascii_brute(uint8_t *cipher, keysig_notify_fn_t notify, void *user) { + (void) cipher; + (void) notify; + (void) user; keysig_search_init(); + cprintf(RED, "Unimplemented\n"); return false; } @@ -144,9 +182,9 @@ struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = }, [KEYSIG_SEARCH_ASCII_STUPID] = { - .name = "ascii-stupid", + .name = "ascii-hex", .fn = keysig_search_ascii_stupid, - .comment = "Try to find a balance ascii key ignoring lsb" + .comment = "Try to find an hexadecimal ascii string keysig" }, [KEYSIG_SEARCH_ASCII_BRUTE] = { diff --git a/utils/nwztools/upgtools/keysig_search.h b/utils/nwztools/upgtools/keysig_search.h index 46639dfb47..9009a73284 100644 --- a/utils/nwztools/upgtools/keysig_search.h +++ b/utils/nwztools/upgtools/keysig_search.h @@ -23,6 +23,7 @@ #include #include +#include "fwp.h" enum keysig_search_method_t { @@ -34,7 +35,8 @@ enum keysig_search_method_t }; /* notify returns true if the key seems ok */ -typedef bool (*keysig_notify_fn_t)(void *user, uint8_t key[8], uint8_t sig[8]); +typedef bool (*keysig_notify_fn_t)(void *user, uint8_t key[NWZ_KEY_SIZE], + uint8_t sig[NWZ_SIG_SIZE]); /* returns true if a key was accepted by notify */ typedef bool (*keysig_search_fn_t)(uint8_t *cipher, keysig_notify_fn_t notify, void *user); diff --git a/utils/nwztools/upgtools/misc.c b/utils/nwztools/upgtools/misc.c index 108235e7fd..00832cd585 100644 --- a/utils/nwztools/upgtools/misc.c +++ b/utils/nwztools/upgtools/misc.c @@ -31,6 +31,7 @@ char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' }; char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; +char WHITE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '7', 0x6d, '\0' }; static bool g_color_enable = true; diff --git a/utils/nwztools/upgtools/misc.h b/utils/nwztools/upgtools/misc.h index 96666a2eff..4e8294f1ee 100644 --- a/utils/nwztools/upgtools/misc.h +++ b/utils/nwztools/upgtools/misc.h @@ -34,7 +34,7 @@ typedef char color_t[]; -extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE; +extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE, WHITE; void *xmalloc(size_t s); void color(color_t c); void enable_color(bool enable); diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c index fc5cf0fb92..065cede63c 100644 --- a/utils/nwztools/upgtools/upgtool.c +++ b/utils/nwztools/upgtools/upgtool.c @@ -69,9 +69,9 @@ struct nwz_model_t { const char *model; unsigned flags; - char kas[NWZ_KAS_SIZE]; /* key and signature */ - char key[8]; - char sig[8]; + char *kas; + char *key; + char *sig; }; struct upg_md5_t @@ -81,7 +81,7 @@ struct upg_md5_t struct upg_header_t { - char sig[8]; + char sig[NWZ_SIG_SIZE]; uint32_t nr_files; uint32_t pad; // make sure structure size is a multiple of 8 } __attribute__((packed)); @@ -92,6 +92,72 @@ struct upg_entry_t uint32_t size; } __attribute__((packed)); +/** KAS / Key / Signature + * + * Since this is all very confusing, we need some terminology and notations: + * - [X, Y, Z] is a sequence of bytes, for example: + * [8, 0x89, 42] + * is a sequence of three bytes. + * - "abcdef" is a string: it is a sequences of bytes where each byte happens to + * be the ASCII encoding of a letter. So for example: + * "abc" = [97, 98, 99] + * because 'a' has ASCII encoding 97 and so one + * - HexString(Seq) refers to the string where each byte of the original sequence + * is represented in hexadecimal by two ASCII characters. For example: + * HexString([8, 0x89, 42]) = "08892a" + * because 8 = 0x08 so it represented by "08" and 42 = 0x2a. Note that the length + * of HexString(Seq) is always exactly twice the length of Seq. + * - DES(Seq,Pass) is the result of encrypting Seq with Pass using the DES cipher. + * Seq must be a sequence of 8 bytes (known as a block) and Pass must be a + * sequence of 8 bytes. The result is also a 8-byte sequence. + * - ECB_DES([Block0, Block1, ..., BlockN], Pass) + * = [DES(Block0,Pass), DES(Block1,Pass), ..., DES(BlockN,Pass)] + * where Blocki is a block (8 byte). + * + * + * A firmware upgrade file is always encrypted using a Key. To authenticate it, + * the upgrade file (before encryption) contains a Sig(nature). The pair (Key,Sig) + * is refered to as KeySig and is specific to each series. For example all + * NWZ-E46x use the same KeySig but the NWZ-E46x and NWZ-A86x use different KeySig. + * In the details, a Key is a sequence of 8 bytes and a Sig is also a sequence + * of 8 bytes. A KeySig is a simply the concatenation of the Key followed by + * the Sig, so it is a sequence of 16 bytes. Probably in an attempt to obfuscate + * things a little further, Sony never provides the KeySig directly but instead + * encrypts it using DES in ECB mode using a hardcoded password and provides + * the hexadecimal string of the result, known as the KAS, which is thus a string + * of 32 ASCII characters. + * Note that since DES works on blocks of 8 bytes and ECB encrypts blocks + * independently, it is the same to encrypt the KeySig as once or encrypt the Key + * and Sig separately. + * + * To summarize: + * Key = [K0, K1, K2, ..., K7] (8 bytes) (model specific) + * Sig = [S0, S1, S2, ..., S7] (8 bytes) (model specific) + * KeySig = [Key, Sig] = [K0, ... K7, S0, ..., S7] (16 bytes) + * FwpPass = "ed295076" (8 bytes) (never changes) + * EncKeySig = ECB_DES(KeySig, FwpPass) = [DES(Key, FwpPass), DES(Sig, FwpPass)] + * KAS = HexString(EncKeySig) (32 characters) + * + * In theory, the Key and Sig can be any 8-byte sequence. In practice, they always + * are strings, probably to make it easier to write them down. In many cases, the + * Key and Sig are even the hexadecimal string of 4-byte sequences but it is + * unclear if this is the result of pure luck, confused engineers, lazyness on + * Sony's part or by design. The following code assumes that Key and Sig are + * strings (though it could easily be fixed to work with anything if this is + * really needed). + * + * + * Here is a real example, from the NWZ-E46x Series: + * Key = "6173819e" (note that this is a string and even a hex string in this case) + * Sig = "30b82e5c" + * KeySig = [Key, Sig] = "6173819e30b82e5c" + * FwpPass = "ed295076" (never changes) + * EncKeySig = ECB_DES(KeySig, FwpPass) + * = [0x8a, 0x01, 0xb6, ..., 0xc5] (16 bytes) + * KAS = HexString(EncKeySig) = "8a01b624bfbfde4a1662a1772220e3c5" + * + */ + struct nwz_model_t g_model_list[] = { { "nwz-e45x", HAS_KAS | HAS_KEY | HAS_SIG | CONFIRMED, "8a01b624bfbfde4a1662a1772220e3c5", "6173819e", "30b82e5c"}, @@ -99,7 +165,7 @@ struct nwz_model_t g_model_list[] = { "nwz-a86x", HAS_KAS | HAS_KEY | HAS_SIG | CONFIRMED, "a7c4af6c28b8900a783f307c1ba538c5", "c824e4e2", "7c262bb0" }, /* The following keys were obtained by brute forcing firmware upgrades, * someone with a device needs to confirm that they work */ - { "nw-a82x", HAS_KEY | HAS_SIG, {""}, "4df06482", "07fa0b6e" }, + { "nw-a82x", HAS_KEY | HAS_SIG, "", "4df06482", "07fa0b6e" }, }; static int digit_value(char c) @@ -115,15 +181,14 @@ static char hex_digit(unsigned v) return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x'; } -static int decrypt_keysig(char keysig[NWZ_KEYSIG_SIZE]) +static int decrypt_keysig(const char kas[NWZ_KAS_SIZE], char key[NWZ_KEY_SIZE], + char sig[NWZ_SIG_SIZE]) { - uint8_t src[16]; - for(int i = 32; i < NWZ_KEYSIG_SIZE; i++) - keysig[i] = 0; - for(int index = 0; index < 16; index++) + uint8_t src[NWZ_KAS_SIZE / 2]; + for(int index = 0; index < NWZ_KAS_SIZE / 2; index++) { - int a = digit_value(keysig[index * 2]); - int b = digit_value(keysig[index * 2 + 1]); + int a = digit_value(kas[index * 2]); + int b = digit_value(kas[index * 2 + 1]); if(a < 0 || b < 0) { cprintf(GREY, "Invalid KAS !\n"); @@ -133,166 +198,161 @@ static int decrypt_keysig(char keysig[NWZ_KEYSIG_SIZE]) } fwp_setkey("ed295076"); fwp_crypt(src, sizeof(src), 1); - memcpy(keysig + 33, src, 8); - memcpy(keysig + 42, src + 8, 8); + memcpy(key, src, NWZ_KEY_SIZE); + memcpy(sig, src + NWZ_KEY_SIZE, NWZ_SIG_SIZE); return 0; } -static bool upg_notify_keysig(void *user, uint8_t key[8], uint8_t sig[8]) +static void encrypt_keysig(char kas[NWZ_KEY_SIZE], + const char key[NWZ_SIG_SIZE], const char sig[NWZ_KAS_SIZE]) { - memcpy(user + 33, key, 8); - memcpy(user + 42, sig, 8); - return true; + 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); + } } -static int do_upg(void *buf, long size) +/* user needs to be pointer to a NWZ_KEYSIG_SIZE-byte buffer, on success g_key + * and g_sig are updated to point to the key and sig in the buffer */ +static bool upg_notify_keysig(void *user, uint8_t key[NWZ_KEY_SIZE], + uint8_t sig[NWZ_SIG_SIZE]) { - struct upg_md5_t *md5 = buf; - cprintf(BLUE, "Preliminary\n"); - cprintf(GREEN, " MD5: "); - for(int i = 0; i < 16; i++) - cprintf(YELLOW, "%02x", md5->md5[i]); - printf(" "); - - 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); - } - check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n"); + g_key = user; + g_sig = user + NWZ_KEY_SIZE; + memcpy(g_key, key, NWZ_KEY_SIZE); + memcpy(g_sig, sig, NWZ_SIG_SIZE); + return true; +} - if(g_model_index == -1 && g_keysig_search == KEYSIG_SEARCH_NONE && g_key == NULL && g_kas == NULL) +static int get_key_and_sig(bool is_extract, void *encrypted_hdr) +{ + static char keysig[NWZ_KEYSIG_SIZE]; + static char kas[NWZ_KAS_SIZE]; + /* database lookup */ + if(g_model_index != -1) { - cprintf(GREY, "A KAS or a keysig is needed to decrypt the firmware\n"); - cprintf(GREY, "You have the following options(see help for more details):\n"); - cprintf(GREY, "- select a model with a known KAS\n"); - cprintf(GREY, "- specify an explicit KAS or key(+optional sig)\n"); - cprintf(GREY, "- let me try to find the keysig(slow !)\n"); - return 1; + if(g_model_list[g_model_index].flags & HAS_KAS) + g_kas = g_model_list[g_model_index].kas; + if(g_model_list[g_model_index].flags & HAS_KEY) + g_key = g_model_list[g_model_index].key; + if(g_model_list[g_model_index].flags & HAS_SIG) + g_sig = g_model_list[g_model_index].sig; } - char kas[NWZ_KAS_SIZE]; - char keysig[NWZ_KEYSIG_SIZE]; - - memset(kas, '?', NWZ_KAS_SIZE); - memset(keysig, '?', NWZ_KEYSIG_SIZE); - keysig[32] = keysig[41] = keysig[50] = 0; - + /* always prefer KAS because it contains everything */ if(g_kas) { if(strlen(g_kas) != NWZ_KAS_SIZE) { - cprintf(GREY, "The specified KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE); + cprintf(GREY, "The KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE); return 4; } - memcpy(keysig, g_kas, NWZ_KAS_SIZE); - decrypt_keysig(keysig); - g_kas = keysig; - g_key = keysig + 33; - g_sig = keysig + 42; + g_key = keysig; + g_sig = keysig + NWZ_KEY_SIZE; + decrypt_keysig(g_kas, g_key, g_sig); } - else if(g_key) + /* fall back to key and signature otherwise. The signature is not required + * when extracting but prevents from checking decryption */ + else if(g_key && (is_extract || g_sig)) { if(strlen(g_key) != 8) { cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n"); return 4; } - if(g_sig && strlen(g_sig) != 8) + + /* if there is a signature, it must have the correct size */ + if(g_sig) { - cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); - return 5; + if(strlen(g_sig) != 8) + { + cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); + return 5; + } } - - memcpy(keysig + 33, g_key, 8); - if(!g_sig) - cprintf(GREY, "Warning: you have specified a key but no sig, I won't be able to do any checks\n"); else - memcpy(keysig + 42, g_sig, 8); - g_key = keysig + 33; - if(g_sig) - g_sig = keysig + 42; + { + cprintf(GREY, "Warning: you have specified a key but no sig, I won't be able to do any checks\n"); + } } - else if(g_model_index == -1) + /* for extraction, we offer a brute force search method from the MD5 */ + else if(is_extract && g_keysig_search != KEYSIG_SEARCH_NONE) { cprintf(BLUE, "keysig Search\n"); cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name); - bool ok = keysig_search_desc[g_keysig_search].fn((void *)(md5 + 1), &upg_notify_keysig, keysig); + bool ok = keysig_search_desc[g_keysig_search].fn(encrypted_hdr, &upg_notify_keysig, keysig); cprintf(GREEN, " Result: "); cprintf(ok ? YELLOW : RED, "%s\n", ok ? "Key found" : "No key found"); if(!ok) return 2; - g_key = keysig + 33; - g_sig = keysig + 42; } else { - if(g_model_list[g_model_index].flags & HAS_KAS) - g_kas = g_model_list[g_model_index].kas; - if(g_model_list[g_model_index].flags & HAS_KEY) - g_key = g_model_list[g_model_index].key; - if(g_model_list[g_model_index].flags & HAS_SIG) - g_sig = g_model_list[g_model_index].sig; - - if(g_kas) - { - memcpy(keysig, g_kas, NWZ_KAS_SIZE); - decrypt_keysig(keysig); - g_kas = keysig; - g_key = keysig + 33; - g_sig = keysig + 42; - } - else - { - if(g_key) - { - memcpy(keysig + 33, g_key, 8); - g_key = keysig + 33; - } - if(g_sig) - { - memcpy(keysig + 42, g_sig, 8); - g_sig = keysig + 42; - } - } + cprintf(GREY, "A KAS or a keysig is needed to decrypt the firmware\n"); + cprintf(GREY, "You have the following options(see help for more details):\n"); + cprintf(GREY, "- select a model with a known KAS\n"); + cprintf(GREY, "- specify an explicit KAS or key+sig\n"); + if(is_extract) + cprintf(GREY, "- let me try to find the keysig(slow !)\n"); + return 1; } + /* If we only have the key and signature, we can create a "fake" KAS + * that decrypts to the same key and signature. Since it is not unique, + * it will generally not match the "official" one from Sony but will produce + * valid files anyway */ if(!g_kas) { - g_kas = keysig; - fwp_setkey("ed295076"); - if(g_key) - { - memcpy(kas, g_key, 8); - fwp_crypt(kas, 8, 0); - for(int i = 0; i < 8; i++) - { - g_kas[2 * i] = hex_digit((kas[i] >> 4) & 0xf); - g_kas[2 * i + 1] = hex_digit(kas[i] & 0xf); - } - } - if(g_sig) + if(!g_sig) { - memcpy(kas + 8, g_sig, 8); - fwp_crypt(kas + 8, 8, 0); - for(int i = 8; i < 16; i++) - { - g_kas[2 * i] = hex_digit((kas[i] >> 4) & 0xf); - g_kas[2 * i + 1] = hex_digit(kas[i] & 0xf); - } + /* if we extract and don't have a signature, just use a random + * one, we cannot check it anyway */ + g_sig = keysig; + memset(g_sig, '?', NWZ_SIG_SIZE); } + g_kas = kas; + encrypt_keysig(g_kas, g_key, g_sig); } cprintf(BLUE, "Keys\n"); cprintf_field(" KAS: ", "%."STR(NWZ_KAS_SIZE)"s\n", g_kas); - cprintf_field(" Key: ", "%s\n", g_key); + cprintf_field(" Key: ", "%."STR(NWZ_KEY_SIZE)"s\n", g_key); if(g_sig) - cprintf_field(" Sig: ", "%s\n", g_sig); + cprintf_field(" Sig: ", "%."STR(NWZ_SIG_SIZE)"s\n", g_sig); + + return 0; +} + +static int do_upg(void *buf, long size) +{ + struct upg_md5_t *md5 = buf; + cprintf(BLUE, "Preliminary\n"); + cprintf(GREEN, " MD5: "); + for(int i = 0; i < 16; i++) + cprintf(YELLOW, "%02x", md5->md5[i]); + printf(" "); + + 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); + } + check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n"); + + int ret = get_key_and_sig(true, md5 + 1); + if(ret != 0) + return ret; struct upg_header_t *hdr = (void *)(md5 + 1); - int ret = fwp_read(hdr, sizeof(struct upg_header_t), hdr, (void *)g_key); + ret = fwp_read(hdr, sizeof(struct upg_header_t), hdr, (void *)g_key); if(ret) return ret; @@ -336,7 +396,6 @@ static int do_upg(void *buf, long size) return ret; // but write the *good* amount of data fwrite(buf + entry->offset, 1, entry->size, f); - fclose(f); } else @@ -414,118 +473,10 @@ static int create_upg(int argc, char **argv) printf("You must specify a firmware filename\n"); usage(); } - - if(g_model_index == -1 && (g_key == NULL || g_sig == NULL) && g_kas == NULL) - { - cprintf(GREY, "A KAS or a keysig is needed to encrypt the firmware\n"); - cprintf(GREY, "You have the following options(see help for more details):\n"); - cprintf(GREY, "- select a model with a known KAS\n"); - cprintf(GREY, "- specify an explicit KAS or key+sig\n"); - return 1; - } - - char kas[NWZ_KAS_SIZE]; - char keysig[NWZ_KEYSIG_SIZE]; - memset(kas, '?', NWZ_KAS_SIZE); - memset(keysig, '?', NWZ_KEYSIG_SIZE); - keysig[32] = keysig[41] = keysig[50] = 0; - - if(g_kas) - { - if(strlen(g_kas) != NWZ_KAS_SIZE) - { - cprintf(GREY, "The specified KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE); - return 4; - } - memcpy(keysig, g_kas, NWZ_KAS_SIZE); - decrypt_keysig(keysig); - g_kas = keysig; - g_key = keysig + 33; - g_sig = keysig + 42; - } - else if(g_key) - { - if(strlen(g_key) != 8) - { - cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n"); - return 4; - } - if(strlen(g_sig) != 8) - { - cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); - return 5; - } - - memcpy(keysig + 33, g_key, 8); - if(!g_sig) - cprintf(GREY, "Warning: you have specified a key but no sig, I won't be able to do any checks\n"); - else - memcpy(keysig + 42, g_sig, 8); - g_key = keysig + 33; - g_sig = keysig + 42; - } - else if(g_model_index != -1) - { - if(g_model_list[g_model_index].flags & HAS_KAS) - g_kas = g_model_list[g_model_index].kas; - if(g_model_list[g_model_index].flags & HAS_KEY) - g_key = g_model_list[g_model_index].key; - if(g_model_list[g_model_index].flags & HAS_SIG) - g_sig = g_model_list[g_model_index].sig; - - if(g_key && g_sig) - { - memcpy(keysig + 33, g_key, 8); - g_key = keysig + 33; - memcpy(keysig + 42, g_sig, 8); - g_sig = keysig + 42; - } - else if(g_kas) - { - memcpy(keysig, g_kas, NWZ_KAS_SIZE); - decrypt_keysig(keysig); - g_kas = keysig; - g_key = keysig + 33; - g_sig = keysig + 42; - } - else - { - printf("Target doesn't have enough information to get key and sig\n"); - return 1; - } - } - else - { - printf("Kill me\n"); - return 1; - } - - if(!g_kas) - { - g_kas = keysig; - fwp_setkey("ed295076"); - memcpy(kas, g_key, 8); - fwp_crypt(kas, 8, 0); - for(int i = 0; i < 8; i++) - { - g_kas[2 * i] = hex_digit((kas[i] >> 4) & 0xf); - g_kas[2 * i + 1] = hex_digit(kas[i] & 0xf); - } - memcpy(kas + 8, g_sig, 8); - fwp_crypt(kas + 8, 8, 0); - for(int i = 8; i < 16; i++) - { - g_kas[2 * i] = hex_digit((kas[i] >> 4) & 0xf); - g_kas[2 * i + 1] = hex_digit(kas[i] & 0xf); - } - } - - cprintf(BLUE, "Keys\n"); - cprintf_field(" KAS: ", "%."STR(NWZ_KAS_SIZE)"s\n", g_kas); - cprintf_field(" Key: ", "%s\n", g_key); - if(g_sig) - cprintf_field(" Sig: ", "%s\n", g_sig); + int ret = get_key_and_sig(false, NULL); + if(ret != 0) + return ret; FILE *fout = fopen(argv[0], "wb"); if(fout == NULL) @@ -558,8 +509,8 @@ static int create_upg(int argc, char **argv) memcpy(hdr.sig, g_sig, 8); hdr.nr_files = nr_files; hdr.pad = 0; - - int ret = fwp_write(&hdr, sizeof(hdr), &hdr, (void *)g_key); + + ret = fwp_write(&hdr, sizeof(hdr), &hdr, (void *)g_key); if(ret) return ret; MD5_Update(&c, &hdr, sizeof(hdr)); @@ -573,7 +524,7 @@ static int create_upg(int argc, char **argv) entry.offset = offset; entry.size = filesize(files[i]); offset += ROUND_UP(entry.size, 8); // do it before encryption !! - + ret = fwp_write(&entry, sizeof(entry), &entry, (void *)g_key); if(ret) return ret; -- cgit v1.2.3