summaryrefslogtreecommitdiffstats
path: root/utils/nwztools/upgtools
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-08-30 17:19:30 +1000
committerAmaury Pouly <amaury.pouly@gmail.com>2016-08-30 17:21:05 +1000
commitcf82f208e3ece54fd38cb7c90b77ad91aa3a4c8c (patch)
tree5fe88d8fdaa9306a28b65a2dad042e0b6577816e /utils/nwztools/upgtools
parentde8950d63da5ce199a94e3f094a7e13da3eeca6c (diff)
downloadrockbox-cf82f208e3ece54fd38cb7c90b77ad91aa3a4c8c.tar.gz
rockbox-cf82f208e3ece54fd38cb7c90b77ad91aa3a4c8c.tar.bz2
rockbox-cf82f208e3ece54fd38cb7c90b77ad91aa3a4c8c.zip
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
Diffstat (limited to 'utils/nwztools/upgtools')
-rw-r--r--utils/nwztools/upgtools/fwp.h3
-rw-r--r--utils/nwztools/upgtools/keysig_search.c108
-rw-r--r--utils/nwztools/upgtools/keysig_search.h4
-rw-r--r--utils/nwztools/upgtools/misc.c1
-rw-r--r--utils/nwztools/upgtools/misc.h2
-rw-r--r--utils/nwztools/upgtools/upgtool.c423
6 files changed, 267 insertions, 274 deletions
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 <string.h>
+#include <stdio.h>
-#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 <stdbool.h>
#include <stdint.h>
+#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;