summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--utils/nwztools/upgtools/Makefile2
-rw-r--r--utils/nwztools/upgtools/fwp.c57
-rw-r--r--utils/nwztools/upgtools/fwp.h48
-rw-r--r--utils/nwztools/upgtools/keysig_search.c5
-rw-r--r--utils/nwztools/upgtools/keysig_search.h5
-rw-r--r--utils/nwztools/upgtools/mg.cpp105
-rw-r--r--utils/nwztools/upgtools/mg.h26
-rw-r--r--utils/nwztools/upgtools/upg.c360
-rw-r--r--utils/nwztools/upgtools/upg.h58
-rw-r--r--utils/nwztools/upgtools/upgtool.c38
10 files changed, 446 insertions, 258 deletions
diff --git a/utils/nwztools/upgtools/Makefile b/utils/nwztools/upgtools/Makefile
index 046eb1a1a9..5dede1447c 100644
--- a/utils/nwztools/upgtools/Makefile
+++ b/utils/nwztools/upgtools/Makefile
@@ -30,7 +30,7 @@ all: $(BINS)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
-upgtool: upgtool.o upg.o misc.o fwp.o mg.o keysig_search.o md5.o
+upgtool: upgtool.o upg.o misc.o mg.o keysig_search.o md5.o
$(LD) -o $@ $^ $(LDFLAGS)
clean:
diff --git a/utils/nwztools/upgtools/fwp.c b/utils/nwztools/upgtools/fwp.c
deleted file mode 100644
index 7d8f8002a8..0000000000
--- a/utils/nwztools/upgtools/fwp.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2012 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 <string.h>
-#include <stdlib.h>
-#include "fwp.h"
-#include "misc.h"
-#include "mg.h"
-
-void fwp_read(void *in, int size, void *out, uint8_t *key)
-{
- mg_decrypt_fw(in, size, out, key);
-}
-
-void fwp_write(void *in, int size, void *out, uint8_t *key)
-{
- mg_encrypt_fw(in, size, out, key);
-}
-
-static uint8_t g_key[NWZ_KEY_SIZE];
-
-void fwp_setkey(char key[NWZ_KEY_SIZE])
-{
- memcpy(g_key, key, NWZ_KEY_SIZE);
-}
-
-void fwp_crypt(void *buf, int size, int mode)
-{
- while(size >= NWZ_KEY_SIZE)
- {
- if(mode)
- mg_decrypt_pass(buf, NWZ_KEY_SIZE, buf, g_key);
- else
- mg_encrypt_pass(buf, NWZ_KEY_SIZE, buf, g_key);
- buf += NWZ_KEY_SIZE;
- size -= NWZ_KEY_SIZE;
- }
- if(size != 0)
- abort(); /* size is not a multiple of 8 */
-}
diff --git a/utils/nwztools/upgtools/fwp.h b/utils/nwztools/upgtools/fwp.h
deleted file mode 100644
index 32fe260090..0000000000
--- a/utils/nwztools/upgtools/fwp.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2012 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.
- *
- ****************************************************************************/
-#ifndef __fwp_h__
-#define __fwp_h__
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define NWZ_KAS_SIZE 32
-#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
-#define NWZ_MD5_SIZE 16
-
-/* size must be a multiple of 8 */
-void fwp_read(void *in, int size, void *out, uint8_t *key);
-void fwp_write(void *in, int size, void *out, uint8_t *key);
-void fwp_setkey(char key[8]);
-void fwp_crypt(void *buf, int size, int mode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __fwp_h__ */
diff --git a/utils/nwztools/upgtools/keysig_search.c b/utils/nwztools/upgtools/keysig_search.c
index 2f234d6358..c16dae5260 100644
--- a/utils/nwztools/upgtools/keysig_search.c
+++ b/utils/nwztools/upgtools/keysig_search.c
@@ -218,11 +218,10 @@ typedef bool (*sig_validate_fn_t)(uint8_t *key);
static bool check_key(uint8_t key[NWZ_KEY_SIZE], sig_validate_fn_t validate)
{
struct upg_header_t hdr;
- mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr.sig), (void *)&hdr, key);
+ mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr), (void *)&hdr, key);
if(validate(hdr.sig))
{
- /* the signature looks correct, so decrypt the header futher to be sure */
- mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr), (void *)&hdr, key);
+ /* the signature looks correct, so check the header to be sure */
/* we expect the number of files to be small and the padding to be 0 */
if(hdr.nr_files == 0 || hdr.nr_files > 10 || hdr.pad != 0)
return false;
diff --git a/utils/nwztools/upgtools/keysig_search.h b/utils/nwztools/upgtools/keysig_search.h
index 877da2f89c..72649e042e 100644
--- a/utils/nwztools/upgtools/keysig_search.h
+++ b/utils/nwztools/upgtools/keysig_search.h
@@ -24,7 +24,10 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
-#include "fwp.h"
+
+/* these values are for the V1 format */
+#define NWZ_KEY_SIZE 8
+#define NWZ_SIG_SIZE 8
enum keysig_search_method_t
{
diff --git a/utils/nwztools/upgtools/mg.cpp b/utils/nwztools/upgtools/mg.cpp
index 66566770f9..4bab5d9c61 100644
--- a/utils/nwztools/upgtools/mg.cpp
+++ b/utils/nwztools/upgtools/mg.cpp
@@ -26,43 +26,96 @@
#include <stdio.h>
using namespace CryptoPP;
-namespace
+
+void mg_decrypt_fw(void *in, int size, void *out, uint8_t *key)
{
- inline void dec_des_ecb(void *in, int size, void *out, uint8_t *key)
- {
- ECB_Mode< DES >::Decryption dec;
- if(size % 8)
- abort(); /* size must be a multiple of 8 */
- dec.SetKey(key, 8);
- dec.ProcessData((byte*)out, (byte*)in, size);
- }
-
- inline void enc_des_ecb(void *in, int size, void *out, uint8_t *key)
- {
- ECB_Mode< DES >::Encryption enc;
- if(size % 8)
- abort(); /* size must be a multiple of 8 */
- enc.SetKey(key, 8);
- enc.ProcessData((byte*)out, (byte*)in, size);
- }
+ ECB_Mode< DES >::Decryption dec;
+ if(size % 8)
+ abort(); /* size must be a multiple of 8 */
+ dec.SetKey(key, 8);
+ dec.ProcessData((byte*)out, (byte*)in, size);
}
-void mg_decrypt_fw(void *in, int size, void *out, uint8_t *key)
+static ECB_Mode< DES >::Decryption g_des_ecb_dec;
+
+void des_ecb_dec_set_key(const uint8_t key[8])
{
- dec_des_ecb(in, size, out, key);
+ g_des_ecb_dec.SetKey(key, 8);
}
-void mg_encrypt_fw(void *in, int size, void *out, uint8_t *key)
+void des_ecb_dec(void *in, int size, void *out)
{
- enc_des_ecb(in, size, out, key);
+ if(size % 8)
+ abort(); /* size must be a multiple of 8 */
+ g_des_ecb_dec.ProcessData((byte*)out, (byte*)in, size);
}
-void mg_decrypt_pass(void *in, int size, void *out, uint8_t *key)
+static ECB_Mode< DES >::Encryption g_des_ecb_enc;
+
+void des_ecb_enc_set_key(const uint8_t key[8])
+{
+ g_des_ecb_enc.SetKey(key, 8);
+}
+
+void des_ecb_enc(void *in, int size, void *out)
+{
+ if(size % 8)
+ abort(); /* size must be a multiple of 8 */
+ g_des_ecb_enc.ProcessData((byte*)out, (byte*)in, size);
+}
+
+static ECB_Mode< AES >::Decryption g_aes_ecb_dec;
+
+void aes_ecb_dec_set_key(const uint8_t key[16])
+{
+ g_aes_ecb_dec.SetKey(key, 16);
+}
+
+void aes_ecb_dec(void *in, int size, void *out)
+{
+ if(size % 16)
+ abort(); /* size must be a multiple of 16 */
+ g_aes_ecb_dec.ProcessData((byte*)out, (byte*)in, size);
+}
+
+static ECB_Mode< AES >::Encryption g_aes_ecb_enc;
+
+void aes_ecb_enc_set_key(const uint8_t key[16])
+{
+ g_aes_ecb_enc.SetKey(key, 16);
+}
+
+void aes_ecb_enc(void *in, int size, void *out)
+{
+ if(size % 16)
+ abort(); /* size must be a multiple of 16 */
+ g_aes_ecb_enc.ProcessData((byte*)out, (byte*)in, size);
+}
+
+static CBC_Mode< AES >::Decryption g_aes_cbc_dec;
+
+void aes_cbc_dec_set_key_iv(const uint8_t key[16], const uint8_t iv[16])
+{
+ g_aes_cbc_dec.SetKeyWithIV(key, 16, iv);
+}
+
+void aes_cbc_dec(void *in, int size, void *out)
+{
+ if(size % 16)
+ abort(); /* size must be a multiple of 16 */
+ g_aes_cbc_dec.ProcessData((byte*)out, (byte*)in, size);
+}
+
+static CBC_Mode< AES >::Encryption g_aes_cbc_enc;
+
+void aes_cbc_enc_set_key_iv(const uint8_t key[16], const uint8_t iv[16])
{
- dec_des_ecb(in, size, out, key);
+ g_aes_cbc_enc.SetKeyWithIV(key, 16, iv);
}
-void mg_encrypt_pass(void *in, int size, void *out, uint8_t *key)
+void aes_cbc_enc(void *in, int size, void *out)
{
- enc_des_ecb(in, size, out, key);
+ if(size % 16)
+ abort(); /* size must be a multiple of 16 */
+ g_aes_cbc_enc.ProcessData((byte*)out, (byte*)in, size);
}
diff --git a/utils/nwztools/upgtools/mg.h b/utils/nwztools/upgtools/mg.h
index ef8dcd5ecb..fe6ec7c2f6 100644
--- a/utils/nwztools/upgtools/mg.h
+++ b/utils/nwztools/upgtools/mg.h
@@ -26,11 +26,27 @@
#ifdef __cplusplus
extern "C" {
#endif
-/* size must be a multiple of 8 */
-void mg_decrypt_fw(void *in, int size, void *out, uint8_t *key);
-void mg_encrypt_fw(void *in, int size, void *out, uint8_t *key);
-void mg_decrypt_pass(void *in, int size, void *out, uint8_t *key);
-void mg_encrypt_pass(void *in, int size, void *out, uint8_t *key);
+/* size must be a multiple of 8, this function is thread-safe */
+void mg_decrypt_fw(void *in, int size, void *out, uint8_t key[8]);
+
+/* for simplicity, these function use some global variables, this could be
+ * change if necessary in the future */
+
+/* DES: sizes must be a multiple of 8 */
+void des_ecb_dec_set_key(const uint8_t key[8]);
+void des_ecb_dec(void *in, int size, void *out);
+void des_ecb_enc_set_key(const uint8_t key[8]);
+void des_ecb_enc(void *in, int size, void *out);
+
+/* AES: size must be a multiple of 16 */
+void aes_ecb_dec_set_key(const uint8_t key[16]);
+void aes_ecb_dec(void *in, int size, void *out);
+void aes_ecb_enc_set_key(const uint8_t key[16]);
+void aes_ecb_enc(void *in, int size, void *out);
+void aes_cbc_dec_set_key_iv(const uint8_t key[16], const uint8_t iv[16]);
+void aes_cbc_dec(void *in, int size, void *out);
+void aes_cbc_enc_set_key_iv(const uint8_t key[16], const uint8_t iv[16]);
+void aes_cbc_enc(void *in, int size, void *out);
#ifdef __cplusplus
}
#endif
diff --git a/utils/nwztools/upgtools/upg.c b/utils/nwztools/upgtools/upg.c
index 599fbbeaf6..910c37a9bf 100644
--- a/utils/nwztools/upgtools/upg.c
+++ b/utils/nwztools/upgtools/upg.c
@@ -54,6 +54,12 @@ struct nwz_model_t g_model_list[] =
{ 0 }
};
+/* KEY/IV for pre-WM1/A30 models */
+static uint8_t g_des_passkey[9] = "ed295076";
+/* device after WM1/NW-A30 */
+static uint8_t g_aes_passkey[17] = "9cc4419c8bef488c";
+static uint8_t g_aes_iv[17] = "6063ce1efa1d543a";
+
static int digit_value(char c)
{
if(c >= '0' && c <= '9') return c - '0';
@@ -67,42 +73,83 @@ 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])
+int decrypt_keysig(const char *kas, char **key, char **sig)
{
- uint8_t src[NWZ_KAS_SIZE / 2];
- for(int index = 0; index < NWZ_KAS_SIZE / 2; index++)
+ int len = strlen(kas);
+ if(len % 2)
+ return -1; /* length must be a multiple of two */
+ uint8_t *src = malloc(len / 2);
+ for(int index = 0; index < len / 2; index++)
{
int a = digit_value(kas[index * 2]);
int b = digit_value(kas[index * 2 + 1]);
if(a < 0 || b < 0)
- return -1;
+ return -1; /* bad digit */
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);
+ if(*key == NULL)
+ *key = malloc(len / 4 + 1);
+ if(*sig == NULL)
+ *sig = malloc(len / 4 + 1);
+
+ if(len == 32)
+ {
+ /* Device before WM1/NW-A30 use DES */
+ des_ecb_dec_set_key(g_des_passkey);
+ des_ecb_dec(src, len / 2, src);
+ }
+ else if(len == 64)
+ {
+ /* device after WM1/NW-A30 */
+ aes_cbc_dec_set_key_iv(g_aes_passkey, g_aes_iv);
+ aes_cbc_dec(src, len / 2, src);
+ }
+ else
+ {
+ free(src);
+ return -1;
+ }
+ memcpy(*key, src, len / 4);
+ (*key)[len / 4] = 0;
+ memcpy(*sig, src + len / 4, len / 4);
+ (*sig)[len / 4] = 0;
+ free(src);
return 0;
}
-void encrypt_keysig(char kas[NWZ_KEY_SIZE],
- const char key[NWZ_SIG_SIZE], const char sig[NWZ_KAS_SIZE])
+void encrypt_keysig(char **kas, const char *key, const char *sig)
{
- 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++)
+ int len = strlen(key);
+ if(len != strlen(sig))
+ abort();
+ uint8_t *src = malloc(len * 2);
+ memcpy(src, key, len);
+ memcpy(src + len, sig, len);
+ if(len == 8)
+ {
+ des_ecb_enc_set_key(g_des_passkey);
+ des_ecb_enc(src, len * 2, src);
+ }
+ else if(len == 16)
{
- kas[2 * i] = hex_digit((src[i] >> 4) & 0xf);
- kas[2 * i + 1] = hex_digit(src[i] & 0xf);
+ aes_cbc_enc_set_key_iv(g_aes_passkey, g_aes_iv);
+ aes_cbc_enc(src, len * 2, src);
}
+ else
+ abort();
+ if(*kas == NULL)
+ *kas = malloc(len * 4 + 1);
+ for(int i = 0; i < len * 2; i++)
+ {
+ (*kas)[2 * i] = hex_digit((src[i] >> 4) & 0xf);
+ (*kas)[2 * i + 1] = hex_digit(src[i] & 0xf);
+ }
+ (*kas)[len * 4] = 0;
+ free(src);
}
-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)
+struct upg_file_t *upg_read_memory(void *buf, size_t size, const char *key,
+ const 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)
@@ -114,6 +161,8 @@ struct upg_file_t *upg_read_memory(void *buf, size_t size, char key[NWZ_KEY_SIZE
cprintf(YELLOW, "%02x", md5->md5[i]);
cprintf(OFF, " ");
+ int key_len = strlen(key);
+
/* check MD5 */
uint8_t actual_md5[NWZ_MD5_SIZE];
MD5_CalculateDigest(actual_md5, (md5 + 1), size - sizeof(struct upg_header_t));
@@ -125,72 +174,186 @@ struct upg_file_t *upg_read_memory(void *buf, size_t size, char key[NWZ_KEY_SIZE
}
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);
+ bool is_v2 = false;
+ void *hdr = (void *)(md5 + 1);
+ /* decrypt just the header */
+ if(key_len == 8)
+ {
+ des_ecb_dec_set_key((uint8_t *)key);
+ des_ecb_dec(hdr, sizeof(struct upg_header_t), hdr);
+ }
+ else if(key_len == 16)
+ {
+ aes_ecb_dec_set_key((uint8_t *)key);
+ aes_ecb_dec(hdr, sizeof(struct upg_header_v2_t), hdr);
+ is_v2 = true;
+ }
+ else
+ {
+ cprintf(GREY, "I don't know how to decrypt with a key of length %s\n", key_len);
+ return NULL;
+ }
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)
+ int nr_files = 0;
+ void *content = NULL;
+ if(!is_v2)
{
- if(memcmp(hdr->sig, sig, NWZ_SIG_SIZE) != 0)
+ struct upg_header_t *hdr_v1 = hdr;
+ cprintf_field(" Signature: ", "");
+ for(int i = 0; i < 8; i++)
+ cprintf_field("", "%c", isprint(hdr_v1->sig[i]) ? hdr_v1->sig[i] : '.');
+ if(sig)
{
- cprintf(RED, "Mismatch\n");
- err_printf(GREY, "Signature Mismatch\n");
- return NULL;
+ if(memcmp(hdr_v1->sig, sig, 8) != 0)
+ {
+ cprintf(RED, " Mismatch\n");
+ err_printf(GREY, "Signature Mismatch\n");
+ return NULL;
+ }
+ cprintf(RED, " Ok\n");
}
- cprintf(RED, " Ok\n");
+ else
+ cprintf(RED, " Can't check\n");
+ cprintf_field(" Files: ", "%d\n", hdr_v1->nr_files);
+ if(hdr_v1->pad != 0)
+ cprintf_field(" Pad: ", "0x%x\n", hdr_v1->pad);
+
+ nr_files = hdr_v1->nr_files;
+ content = hdr_v1 + 1;
}
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);
+ struct upg_header_v2_t *hdr_v2 = hdr;
+ cprintf_field(" Signature: ", "");
+ for(int i = 0; i < 16; i++)
+ cprintf_field("", "%c", isprint(hdr_v2->sig[i]) ? hdr_v2->sig[i] : '.');
+ if(sig)
+ {
+ if(memcmp(hdr_v2->sig, sig, 16) != 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_v2->nr_files);
+ if(hdr_v2->pad[0] != 0 || hdr_v2->pad[1] != 0 || hdr_v2->pad[2] != 0)
+ cprintf_field(" Pad: ", "0x%x 0x%x 0x%x\n", hdr_v2->pad[0], hdr_v2->pad[1], hdr_v2->pad[2]);
+
+ nr_files = hdr_v2->nr_files;
+ content = hdr_v2 + 1;
}
- /* 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->nr_files = 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++)
+ /* decrypt the file list */
+ if(key_len == 8)
+ des_ecb_dec(content, sizeof(struct upg_entry_t) * nr_files, content);
+ else if(key_len == 16)
+ aes_ecb_dec(content, sizeof(struct upg_entry_v2_t) * nr_files, content);
+
+ /* Extract files */
+ cprintf(BLUE, "Files\n");
+ struct upg_entry_t *entry_v1 = content;
+ struct upg_entry_v2_t *entry_v2 = content;
+ for(unsigned i = 0; i < nr_files; i++)
{
+ uint32_t offset, size;
+ cprintf(GREY, " File");
+ cprintf(RED, " %d\n", i);
+ if(!is_v2)
+ {
+ offset = entry_v1[i].offset;
+ size = entry_v1[i].size;
+ }
+ else
+ {
+ offset = entry_v2[i].offset;
+ size = entry_v2[i].size;
+ }
+ cprintf_field(" Offset: ", "0x%x\n", offset);
+ cprintf_field(" Size: ", "0x%x\n", size);
+ if(is_v2 && (entry_v2[i].pad[0] != 0 || entry_v2[i].pad[1] != 0))
+ cprintf_field(" Pad:", " %x %x\n", entry_v2[i].pad[0], entry_v2[i].pad[1]);
+
+ /* decrypt file content, we round up the size to make sure it's a multiple of 8/16 */
+ if(key_len == 8)
+ des_ecb_dec(buf + offset, ROUND_UP(size, 8), buf + offset);
+ else if(key_len == 16)
+ {
+ aes_cbc_dec_set_key_iv((uint8_t *)key, (uint8_t *)g_aes_iv);
+ aes_cbc_dec(buf + offset, ROUND_UP(size, 16), buf + offset);
+ }
+
+ /* in V2 of the format, some entries can be compressed using zlib but there is no marker for
+ * that; instead the OF has the fwpup program that can extract the nth entry of the archive
+ * and takes an optional -z flag to specify whether to uncompress(). Hence we don't support
+ * that at the moment. */
memset(&file->files[i], 0, sizeof(struct upg_file_entry_t));
- file->files[i].size = entry->size;
+ file->files[i].size = size;
file->files[i].data = malloc(file->files[i].size);
- memcpy(file->files[i].data, buf + entry->offset, entry->size);
+ memcpy(file->files[i].data, buf + offset, 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)
+void *upg_write_memory(struct upg_file_t *file, const char *key,
+ const char *sig, size_t *out_size, void *u, generic_printf_t printf)
{
+ int key_len = strlen(key);
+ if(strlen(sig) != key_len)
+ {
+ err_printf(GREY, "The key must have the same length as the signature\n");
+ return NULL;
+ }
if(file->nr_files == 0)
{
err_printf(GREY, "A UPG file must have at least one file\n");
return NULL;
}
+ if(key_len == 16 && file->nr_files == 1)
+ {
+ err_printf(RED, "This will probably not work: the firmware updater for this device expects at least two files in the archive.\n");
+ err_printf(RED, "The first one is a shell script and the second is a MD5 file. You can probably put whatever you want in this file,\n");
+ err_printf(RED, "even make it empty, but it needs to be there.\n");
+ /* let it run just in case */
+ }
+
+ bool is_v2 = false;
+ size_t min_chunk_size, hdr_sz, ent_sz;
+ if(key_len == 8)
+ {
+ min_chunk_size = 8;
+ hdr_sz = sizeof(struct upg_header_t);
+ ent_sz = sizeof(struct upg_entry_t);
+ }
+ else if(key_len == 16)
+ {
+ min_chunk_size = 16;
+ hdr_sz = sizeof(struct upg_header_v2_t);
+ ent_sz = sizeof(struct upg_entry_v2_t);
+ is_v2 = true;
+ }
+ else
+ {
+ cprintf(GREY, "I don't know how to decrypt with a key of length %s\n", key_len);
+ 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);
+ size_t tot_hdr_siz = hdr_sz + file->nr_files * ent_sz;
+ size_t tot_size = sizeof(struct upg_md5_t) + tot_hdr_siz;
for(int i = 0; i < file->nr_files; i++)
- tot_size += ROUND_UP(file->files[i].size, 8);
+ tot_size += ROUND_UP(file->files[i].size, min_chunk_size);
/* allocate buffer */
void *buf = malloc(tot_size);
@@ -198,40 +361,75 @@ void *upg_write_memory(struct upg_file_t *file, char key[NWZ_KEY_SIZE],
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;
+ if(!is_v2)
+ {
+ struct upg_header_t *hdr = (void *)(md5 + 1);
+ memcpy(hdr->sig, sig, 8);
+ hdr->nr_files = file->nr_files;
+ hdr->pad = 0;
+ }
+ else
+ {
+ struct upg_header_v2_t *hdr = (void *)(md5 + 1);
+ memcpy(hdr->sig, sig, 16);
+ hdr->nr_files = file->nr_files;
+ hdr->pad[0] = hdr->pad[1] = hdr->pad[2] = 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);
+ size_t offset = sizeof(struct upg_md5_t) + tot_hdr_siz;
+ struct upg_entry_t *entry_v1 = (void *)((uint8_t *)(md5 + 1) + hdr_sz);
+ struct upg_entry_v2_t *entry_v2 = (void *)entry_v1;
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;
+ cprintf_field(" Offset: ", "0x%lx\n", offset);
+ cprintf_field(" Size: ", "0x%lx\n", file->files[i].size);
+ if(!is_v2)
+ {
+ entry_v1[i].offset = offset;
+ entry_v1[i].size = file->files[i].size;
+ }
+ else
+ {
+ entry_v2[i].offset = offset;
+ entry_v2[i].size = file->files[i].size;
+ entry_v2[i].pad[0] = entry_v2[i].pad[1] = 0;
+ }
+ /* copy data to buffer, with padding */
+ size_t r_size = ROUND_UP(file->files[i].size, min_chunk_size);
+ void *data_ptr = (uint8_t *)buf + 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 in-place */
+ if(!is_v2)
+ {
+ des_ecb_enc_set_key((uint8_t *)key);
+ des_ecb_enc(data_ptr, r_size, data_ptr);
+ }
+ else
+ {
+ aes_cbc_enc_set_key_iv((uint8_t *)key, (uint8_t *)g_aes_iv);
+ aes_cbc_enc(data_ptr, r_size, data_ptr);
+ }
+
+ offset += r_size;
+ }
+ /* encrypt headers */
+ if(!is_v2)
+ {
+ des_ecb_enc_set_key((uint8_t *)key);
+ des_ecb_enc(md5 + 1, tot_hdr_siz, md5 + 1);
+ }
+ else
+ {
+ aes_ecb_enc_set_key((uint8_t *)key);
+ aes_ecb_enc(md5 + 1, tot_hdr_siz, md5 + 1);
}
- /* encrypt everything and hash everything */
- fwp_write(hdr, tot_size - sizeof(*md5), hdr, (void *)key);
- /* write final MD5 */
- MD5_CalculateDigest(md5->md5, (void *)hdr, tot_size - sizeof(*md5));
+ /* compute MD5 of the whole file */
+ MD5_CalculateDigest(md5->md5, md5 + 1, tot_size - sizeof(*md5));
*out_size = tot_size;
return buf;
}
diff --git a/utils/nwztools/upgtools/upg.h b/utils/nwztools/upgtools/upg.h
index bc7c9787c9..e6cdaba1f7 100644
--- a/utils/nwztools/upgtools/upg.h
+++ b/utils/nwztools/upgtools/upg.h
@@ -22,10 +22,9 @@
#define __UPG_H__
#include "misc.h"
-#include "fwp.h"
#include "mg.h"
-/** Firmware format
+/** Firmware format V1/V2
*
* The firmware starts with the MD5 hash of the entire file (except the MD5 hash
* itself of course). This is used to check that the file was not corrupted.
@@ -35,7 +34,20 @@
* the key and finding the right signature serves to authenticate the firmware.
* The header is followed by N entries (where N is the number of files) giving
* the offset, within the file, and size of each file. Note that the files in
- * the firmware have no name. */
+ * the firmware have no name. The only difference between V1 and V2 is that the
+ * size of the signature is 16 bytes instead of 8 and the upg entries are 16 bytes
+ * long so they are padded.
+ *
+ * There is, however a practical difference between how the OF performs the update on
+ * newer devices (and hence corrolates exactly with V2 usage). On these devices, the
+ * update script will first extract the first file (the bash script) and the second file
+ * which is called "md5.txt". At this point it then runs the script. Hence it is not
+ * important what the content of the second file is, it is not checked unless fwpup is
+ * called. For the records, here is an exerct of such a file:
+ * 838860800 eae2acabcd6523a750f61f5ea3e9a80b system.img
+ */
+
+#define NWZ_MD5_SIZE 16
struct upg_md5_t
{
@@ -44,7 +56,7 @@ struct upg_md5_t
struct upg_header_t
{
- uint8_t sig[NWZ_SIG_SIZE];
+ uint8_t sig[8];
uint32_t nr_files;
uint32_t pad; // make sure structure size is a multiple of 8
} __attribute__((packed));
@@ -55,6 +67,20 @@ struct upg_entry_t
uint32_t size;
} __attribute__((packed));
+struct upg_header_v2_t
+{
+ uint8_t sig[16];
+ uint32_t nr_files;
+ uint32_t pad[3]; // make sure structure size is a multiple of 16
+} __attribute__((packed));
+
+struct upg_entry_v2_t
+{
+ uint32_t offset;
+ uint32_t size;
+ uint32_t pad[2]; // make sure structure size is a multiple of 16
+} __attribute__((packed));
+
/** KAS / Key / Signature
*
* Since this is all very confusing, we need some terminology and notations:
@@ -131,7 +157,7 @@ struct nwz_model_t
* it is a KAS built from a key and sig brute-forced from an upgrade. In this
* case, the KAS might be different from the 'official' one although for all
* intent and purposes it should not make any difference. */
- char *kas;
+ const char *kas;
};
/* list of models with keys and status. Sentinel NULL entry at the end */
@@ -150,21 +176,21 @@ struct upg_file_t
struct upg_file_entry_t *files;
};
-/* decrypt a KAS into a key and signature, return <0 if the KAS contains a non-hex
- * character */
-int decrypt_keysig(const char kas[NWZ_KAS_SIZE], char key[NWZ_KEY_SIZE],
- char sig[NWZ_SIG_SIZE]);
-/* encrypt a key and signature into a KAS */
-void encrypt_keysig(char kas[NWZ_KEY_SIZE],
- const char key[NWZ_SIG_SIZE], const char sig[NWZ_KAS_SIZE]);
+/* IMPORTANT: all functions assume that the kas/key/sig are string and are zero terminated */
+
+/* Decrypt a KAS into a key and signature, return <0 if the KAS contains a non-hex
+ * character. The function will allocate key and sig if *key and/or *sig is NULL */
+int decrypt_keysig(const char *kas, char **key, char **sig);
+/* Encrypt a key and signature into a KAS, it will allocate kas if *kas is NULL */
+void encrypt_keysig(char **kas, const char *key, const char *sig);
/* Read a UPG file: return a structure on a success or NULL on error.
* Note that the memory buffer is modified to perform in-place decryption. */
-struct upg_file_t *upg_read_memory(void *file, size_t size, char key[NWZ_KEY_SIZE],
- char sig[NWZ_SIG_SIZE], void *u, generic_printf_t printf);
+struct upg_file_t *upg_read_memory(void *file, size_t size, const char *key,
+ const char *sig, void *u, generic_printf_t printf);
/* Write a UPG file: return a buffer containing the whole image, or NULL on error. */
-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);
+void *upg_write_memory(struct upg_file_t *file, const char *key,
+ const char *sig, size_t *out_size, void *u, generic_printf_t printf);
/* create empty upg file */
struct upg_file_t *upg_new(void);
/* append a file to a upg, data is NOT copied */
diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c
index ff2a1f60f7..e0ccc15c48 100644
--- a/utils/nwztools/upgtools/upgtool.c
+++ b/utils/nwztools/upgtools/upgtool.c
@@ -18,6 +18,7 @@
* KIND, either express or implied.
*
****************************************************************************/
+#define _XOPEN_SOURCE 500 /* for strdup */
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
@@ -30,7 +31,6 @@
#include "elf.h"
#include <sys/stat.h>
#include "crypt.h"
-#include "fwp.h"
#include "keysig_search.h"
#include "upg.h"
@@ -71,50 +71,49 @@ static bool upg_notify_keysig(void *user, uint8_t key[NWZ_KEY_SIZE],
uint8_t sig[NWZ_SIG_SIZE])
{
g_key = user;
- g_sig = user + NWZ_KEY_SIZE;
+ g_sig = user + 9;
memcpy(g_key, key, NWZ_KEY_SIZE);
+ g_key[8] = 0;
memcpy(g_sig, sig, NWZ_SIG_SIZE);
+ g_sig[8] = 0;
return true;
}
static int get_key_and_sig(bool is_extract, void *buf)
{
- static char keysig[NWZ_KEYSIG_SIZE];
- static char kas[NWZ_KAS_SIZE];
/* database lookup */
if(g_model_index != -1)
- g_kas = g_model_list[g_model_index].kas;
+ g_kas = strdup(g_model_list[g_model_index].kas);
/* always prefer KAS because it contains everything */
if(g_kas)
{
- if(strlen(g_kas) != NWZ_KAS_SIZE)
+ if(strlen(g_kas) != 32 && strlen(g_kas) != 64)
{
- cprintf(GREY, "The KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE);
+ cprintf(GREY, "The KAS has wrong length (must be 32 or 64 hex digits)\n");
return 4;
}
- g_key = keysig;
- g_sig = keysig + NWZ_KEY_SIZE;
- decrypt_keysig(g_kas, g_key, g_sig);
+ decrypt_keysig(g_kas, &g_key, &g_sig);
}
/* Otherwise require key and signature */
else if(g_key && g_sig)
{
/* check key and signature size */
- if(strlen(g_key) != 8)
+ if(strlen(g_key) != 8 && strlen(g_key) != 16)
{
- cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n");
+ cprintf(GREY, "The specified key has wrong length (must be 8 or 16 hex digits)\n");
return 4;
}
- if(strlen(g_sig) != 8)
+ if(strlen(g_sig) != strlen(g_key))
{
- cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n");
+ cprintf(GREY, "The specified sig has wrong length (must match key length)\n");
return 5;
}
}
/* for extraction, we offer a brute force search method from the MD5 */
else if(is_extract && g_keysig_search != KEYSIG_SEARCH_NONE)
{
+ static char keysig[18]; /* 8+NUL+8+NULL */
struct upg_md5_t *md5 = (void *)buf;
void *encrypted_hdr = (md5 + 1);
cprintf(BLUE, "keysig Search\n");
@@ -145,14 +144,13 @@ static int get_key_and_sig(bool is_extract, void *buf)
{
/* This is useful to print the KAS for the user when brute-forcing since
* the process will produce a key+sig and the database requires a KAS */
- g_kas = kas;
- encrypt_keysig(g_kas, g_key, g_sig);
+ 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: ", "%."STR(NWZ_KEY_SIZE)"s\n", g_key);
- cprintf_field(" Sig: ", "%."STR(NWZ_SIG_SIZE)"s\n", g_sig);
+ cprintf_field(" KAS: ", "%s\n", g_kas);
+ cprintf_field(" Key: ", "%s\n", g_key);
+ cprintf_field(" Sig: ", "%s\n", g_sig);
return 0;
}
@@ -268,7 +266,7 @@ static int create_upg(int argc, char **argv)
if(f == NULL)
{
upg_free(upg);
- printf(GREY, "Cannot open input file '%s': %m\n", argv[i + 1]);
+ cprintf(GREY, "Cannot open input file '%s': %m\n", argv[i + 1]);
return 1;
}
size_t size = filesize(f);