summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2012-11-03 02:16:01 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2012-11-03 02:16:01 +0100
commitcb09e369fb1ed41b5724a45929a49b42c2718203 (patch)
tree7726be112eb489c07de82c82bec8f32d93855af0
parent8c1a9f508277ee3c3f36665ff9f60bc091529891 (diff)
downloadrockbox-cb09e369fb1ed41b5724a45929a49b42c2718203.tar.gz
rockbox-cb09e369fb1ed41b5724a45929a49b42c2718203.zip
Introduce upgtools for sony nwz players
This tool can unpack UPG archives for firmware updates. Change-Id: I32f5f1a84759198c7af4a4ecfd7aa65eaeda567a
-rw-r--r--utils/nwztools/upgtools/Makefile26
-rw-r--r--utils/nwztools/upgtools/fwp.c56
-rw-r--r--utils/nwztools/upgtools/fwp.h44
-rw-r--r--utils/nwztools/upgtools/keysig_search.c157
-rw-r--r--utils/nwztools/upgtools/keysig_search.h50
-rw-r--r--utils/nwztools/upgtools/mg.cpp67
-rw-r--r--utils/nwztools/upgtools/mg.h37
-rw-r--r--utils/nwztools/upgtools/misc.c53
-rw-r--r--utils/nwztools/upgtools/misc.h46
-rw-r--r--utils/nwztools/upgtools/upgtool.c542
10 files changed, 1078 insertions, 0 deletions
diff --git a/utils/nwztools/upgtools/Makefile b/utils/nwztools/upgtools/Makefile
new file mode 100644
index 0000000000..ad83c0e4c6
--- /dev/null
+++ b/utils/nwztools/upgtools/Makefile
@@ -0,0 +1,26 @@
1DEFINES=
2CC=gcc
3CXX=g++
4LD=g++
5PROFILE=
6CFLAGS=-g $(PROFILE) -std=c99 -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++`
7CXXFLAGS=-g $(PROFILE) -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++`
8LDFLAGS=$(PROFILE) `pkg-config --libs openssl` `pkg-config --libs libcrypto++` -lcrypt
9BINS=upgtool
10
11all: $(BINS)
12
13%.o: %.c
14 $(CC) $(CFLAGS) -c -o $@ $<
15
16%.o: %.cpp
17 $(CXX) $(CXXFLAGS) -c -o $@ $<
18
19upgtool: upgtool.o misc.o fwp.o mg.o keysig_search.o
20 $(LD) -o $@ $^ $(LDFLAGS)
21
22clean:
23 rm -fr *.o
24
25veryclean:
26 rm -rf $(BINS)
diff --git a/utils/nwztools/upgtools/fwp.c b/utils/nwztools/upgtools/fwp.c
new file mode 100644
index 0000000000..e739b8caef
--- /dev/null
+++ b/utils/nwztools/upgtools/fwp.c
@@ -0,0 +1,56 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdio.h>
22#include "fwp.h"
23#include "misc.h"
24#include "mg.h"
25#include <string.h>
26
27int fwp_read(void *in, int size, void *out, uint8_t *key)
28{
29 return mg_decrypt_fw(in, size, out, key);
30}
31
32static uint8_t g_key[NWZ_KEY_SIZE];
33
34void fwp_setkey(char key[NWZ_KEY_SIZE])
35{
36 memcpy(g_key, key, NWZ_KEY_SIZE);
37}
38
39int fwp_crypt(void *buf, int size, int mode)
40{
41 while(size >= NWZ_KEY_SIZE)
42 {
43 if(mode)
44 mg_decrypt_pass(buf, NWZ_KEY_SIZE, buf, g_key);
45 else
46 mg_encrypt_pass(buf, NWZ_KEY_SIZE, buf, g_key);
47 buf += NWZ_KEY_SIZE;
48 size -= NWZ_KEY_SIZE;
49 }
50 if(size != 0)
51 {
52 cprintf(GREY, "Cannot fwp_crypt non-multiple of 8!\n");
53 return -1;
54 }
55 return 0;
56} \ No newline at end of file
diff --git a/utils/nwztools/upgtools/fwp.h b/utils/nwztools/upgtools/fwp.h
new file mode 100644
index 0000000000..dcfe80027e
--- /dev/null
+++ b/utils/nwztools/upgtools/fwp.h
@@ -0,0 +1,44 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef __fwp_h__
22#define __fwp_h__
23
24#include <stdint.h>
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30#define NWZ_KAS_SIZE 32
31#define NWZ_KEYSIG_SIZE 51
32#define NWZ_KEY_SIZE 8
33#define NWZ_EXPKEY_SIZE (NWZ_KEY_SIZE * NWZ_KEY_SIZE)
34#define NWZ_DES_BLOCK 8
35
36int fwp_read(void *in, int size, void *out, uint8_t *key);
37void fwp_setkey(char key[8]);
38int fwp_crypt(void *buf, int size, int mode);
39
40#ifdef __cplusplus
41}
42#endif
43
44#endif /* __fwp_h__ */
diff --git a/utils/nwztools/upgtools/keysig_search.c b/utils/nwztools/upgtools/keysig_search.c
new file mode 100644
index 0000000000..6054ea43ba
--- /dev/null
+++ b/utils/nwztools/upgtools/keysig_search.c
@@ -0,0 +1,157 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "keysig_search.h"
22#include "misc.h"
23#include "mg.h"
24#include <string.h>
25
26#define HEX_MAJ
27
28static uint8_t g_cipher[8];
29static keysig_notify_fn_t g_notify;
30static uint8_t g_key[8];
31static void *g_user;
32static bool is_hex[256];
33static bool is_init = false;
34#ifdef HEX_MAJ
35static char hex_digits[] = "02468ABEF";
36#else
37static char hex_digits[] = "02468abef";
38#endif
39
40static void keysig_search_init()
41{
42 if(is_init) return;
43 is_init = true;
44 memset(is_hex, 0, sizeof(is_hex));
45 for(int i = '0'; i <= '9'; i++)
46 is_hex[i] = true;
47#ifdef HEX_MAJ
48 for(int i = 'A'; i <= 'F'; i++)
49#else
50 for(int i = 'a'; i <= 'f'; i++)
51#endif
52 is_hex[i] = true;
53}
54
55static inline bool is_full_ascii(uint8_t *arr)
56{
57 for(int i = 0; i < 8; i++)
58 if(!is_hex[arr[i]])
59 return false;
60 return true;
61}
62
63static inline bool check_stupid()
64{
65 uint8_t res[8];
66 mg_decrypt_fw(g_cipher, 8, res, g_key);
67 if(is_full_ascii(res))
68 return g_notify(g_user, g_key, res);
69 return false;
70}
71
72static bool search_stupid_rec(int rem_digit, int rem_letter, int pos)
73{
74 if(pos == 8)
75 return check_stupid();
76 if(rem_digit > 0)
77 {
78 for(int i = '0'; i <= '9'; i += 2)
79 {
80 g_key[pos] = i;
81 if(search_stupid_rec(rem_digit - 1, rem_letter, pos + 1))
82 return true;
83 }
84 }
85 if(rem_letter > 0)
86 {
87#ifdef HEX_MAJ
88 for(int i = 'a' - 1; i <= 'f'; i += 2)
89#else
90 for(int i = 'A' - 1; i <= 'F'; i += 2)
91#endif
92 {
93 g_key[pos] = i;
94 if(search_stupid_rec(rem_digit, rem_letter - 1, pos + 1))
95 return true;
96 }
97 }
98 return false;
99}
100
101static bool search_stupid(int rem_digit, int rem_letter)
102{
103 return search_stupid_rec(rem_digit, rem_letter, 0);
104}
105
106bool keysig_search_ascii_stupid(uint8_t *cipher, keysig_notify_fn_t notify, void *user)
107{
108 keysig_search_init();
109 memcpy(g_cipher, cipher, 8);
110 g_notify = notify;
111 g_user = user;
112#if 1
113 return search_stupid(4, 4) ||
114 search_stupid(3, 5) || search_stupid(5, 3) ||
115 search_stupid(2, 6) || search_stupid(6, 2) ||
116 search_stupid(1, 7) || search_stupid(7, 1) ||
117 search_stupid(0, 8) || search_stupid(8, 0);
118#else
119#define do(i) for(int a##i = 0; a##i < sizeof(hex_digits); a##i++) { g_key[i] = hex_digits[a##i];
120#define od() }
121
122 do(0)do(1)do(2)do(3)do(4)do(5)do(6)do(7)
123 if(check_stupid()) return true;
124 od()od()od()od()od()od()od()od()
125#undef do
126#undef od
127 return false;
128#endif
129}
130
131bool keysig_search_ascii_brute(uint8_t *cipher, keysig_notify_fn_t notify, void *user)
132{
133 keysig_search_init();
134 return false;
135}
136
137struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] =
138{
139 [KEYSIG_SEARCH_NONE] =
140 {
141 .name = "none",
142 .fn = NULL,
143 .comment = "don't use",
144 },
145 [KEYSIG_SEARCH_ASCII_STUPID] =
146 {
147 .name = "ascii-stupid",
148 .fn = keysig_search_ascii_stupid,
149 .comment = "Try to find a balance ascii key ignoring lsb"
150 },
151 [KEYSIG_SEARCH_ASCII_BRUTE] =
152 {
153 .name = "ascii-brute",
154 .fn = keysig_search_ascii_brute,
155 .comment = "Brute force all ASCII keys"
156 },
157};
diff --git a/utils/nwztools/upgtools/keysig_search.h b/utils/nwztools/upgtools/keysig_search.h
new file mode 100644
index 0000000000..46639dfb47
--- /dev/null
+++ b/utils/nwztools/upgtools/keysig_search.h
@@ -0,0 +1,50 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef __keysig_search_h__
22#define __keysig_search_h__
23
24#include <stdbool.h>
25#include <stdint.h>
26
27enum keysig_search_method_t
28{
29 KEYSIG_SEARCH_NONE = 0,
30 KEYSIG_SEARCH_FIRST,
31 KEYSIG_SEARCH_ASCII_STUPID = KEYSIG_SEARCH_FIRST,
32 KEYSIG_SEARCH_ASCII_BRUTE,
33 KEYSIG_SEARCH_LAST
34};
35
36/* notify returns true if the key seems ok */
37typedef bool (*keysig_notify_fn_t)(void *user, uint8_t key[8], uint8_t sig[8]);
38/* returns true if a key was accepted by notify */
39typedef bool (*keysig_search_fn_t)(uint8_t *cipher, keysig_notify_fn_t notify, void *user);
40
41struct keysig_search_desc_t
42{
43 const char *name;
44 const char *comment;
45 keysig_search_fn_t fn;
46};
47
48struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST];
49
50#endif /* __keysig_search_h__ */
diff --git a/utils/nwztools/upgtools/mg.cpp b/utils/nwztools/upgtools/mg.cpp
new file mode 100644
index 0000000000..8816259755
--- /dev/null
+++ b/utils/nwztools/upgtools/mg.cpp
@@ -0,0 +1,67 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "mg.h"
22#include <crypto++/cryptlib.h>
23#include <crypto++/modes.h>
24#include <crypto++/des.h>
25#include <crypto++/aes.h>
26#include <stdio.h>
27
28using namespace CryptoPP;
29namespace
30{
31 ECB_Mode< DES >::Decryption g_dec;
32 ECB_Mode< DES >::Encryption g_enc;
33
34 inline int dec_des_ecb(void *in, int size, void *out, uint8_t *key)
35 {
36 g_dec.SetKey(key, 8);
37 g_dec.ProcessData((byte*)out, (byte*)in, size);
38 return 0;
39 }
40
41 inline int enc_des_ecb(void *in, int size, void *out, uint8_t *key)
42 {
43 g_enc.SetKey(key, 8);
44 g_enc.ProcessData((byte*)out, (byte*)in, size);
45 return 0;
46 }
47}
48
49int mg_decrypt_fw(void *in, int size, void *out, uint8_t *key)
50{
51 return dec_des_ecb(in, size, out, key);
52}
53
54int mg_encrypt_fw(void *in, int size, void *out, uint8_t *key)
55{
56 return enc_des_ecb(in, size, out, key);
57}
58
59int mg_decrypt_pass(void *in, int size, void *out, uint8_t *key)
60{
61 return dec_des_ecb(in, size, out, key);
62}
63
64int mg_encrypt_pass(void *in, int size, void *out, uint8_t *key)
65{
66 return enc_des_ecb(in, size, out, key);
67}
diff --git a/utils/nwztools/upgtools/mg.h b/utils/nwztools/upgtools/mg.h
new file mode 100644
index 0000000000..a0c1f2ef65
--- /dev/null
+++ b/utils/nwztools/upgtools/mg.h
@@ -0,0 +1,37 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef __mg_h__
22#define __mg_h__
23
24#include <stdint.h>
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29int mg_decrypt_fw(void *in, int size, void *out, uint8_t *key);
30int mg_encrypt_fw(void *in, int size, void *out, uint8_t *key);
31int mg_decrypt_pass(void *in, int size, void *out, uint8_t *key);
32int mg_encrypt_pass(void *in, int size, void *out, uint8_t *key);
33#ifdef __cplusplus
34}
35#endif
36
37#endif /* __mg_h__ */ \ No newline at end of file
diff --git a/utils/nwztools/upgtools/misc.c b/utils/nwztools/upgtools/misc.c
new file mode 100644
index 0000000000..108235e7fd
--- /dev/null
+++ b/utils/nwztools/upgtools/misc.c
@@ -0,0 +1,53 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdlib.h>
22#include <stdio.h>
23#include <time.h>
24#include <ctype.h>
25#include "misc.h"
26
27char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
28
29char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
30char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
31char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
32char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
33char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
34
35static bool g_color_enable = true;
36
37void *xmalloc(size_t s)
38{
39 void * r = malloc(s);
40 if(!r) bugp("malloc");
41 return r;
42}
43
44void enable_color(bool enable)
45{
46 g_color_enable = enable;
47}
48
49void color(color_t c)
50{
51 if(g_color_enable)
52 printf("%s", (char *)c);
53}
diff --git a/utils/nwztools/upgtools/misc.h b/utils/nwztools/upgtools/misc.h
new file mode 100644
index 0000000000..96666a2eff
--- /dev/null
+++ b/utils/nwztools/upgtools/misc.h
@@ -0,0 +1,46 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef __MISC_H__
22#define __MISC_H__
23
24#include <stdbool.h>
25#include <stddef.h>
26
27#define _STR(a) #a
28#define STR(a) _STR(a)
29
30#define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0)
31#define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0)
32
33#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round))
34
35typedef char color_t[];
36
37extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE;
38void *xmalloc(size_t s);
39void color(color_t c);
40void enable_color(bool enable);
41
42#define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0)
43
44#define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0)
45
46#endif /* __MISC_H__ */
diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c
new file mode 100644
index 0000000000..73303e72d3
--- /dev/null
+++ b/utils/nwztools/upgtools/upgtool.c
@@ -0,0 +1,542 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdio.h>
22#include <stdint.h>
23#include <stdbool.h>
24#include <stdlib.h>
25#include <string.h>
26#include <getopt.h>
27#include <stdarg.h>
28#include <ctype.h>
29#include "misc.h"
30#include "elf.h"
31#include <sys/stat.h>
32#include <openssl/md5.h>
33#include "crypt.h"
34#include "fwp.h"
35#include "keysig_search.h"
36
37#ifndef MIN
38#define MIN(a,b) ((a) < (b) ? (a) : (b))
39#endif
40
41bool g_debug = false;
42static char *g_out_prefix = NULL;
43static char *g_in_file = NULL;
44bool g_force = false;
45static const char *g_model = NULL;
46static int g_model_index = -1;
47static char *g_kas = NULL;
48static char *g_key = NULL;
49static char *g_sig = NULL;
50
51enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE;
52
53
54#define let_the_force_flow(x) do { if(!g_force) return x; } while(0)
55#define continue_the_force(x) if(x) let_the_force_flow(x)
56
57#define check_field(v_exp, v_have, str_ok, str_bad) \
58 if((v_exp) != (v_have)) \
59 { cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \
60 else { cprintf(RED, str_ok); }
61
62static void print_hex(void *p, int size, int unit)
63{
64 uint8_t *p8 = p;
65 uint16_t *p16 = p;
66 uint32_t *p32 = p;
67 for(int i = 0; i < size; i += unit, p8++, p16++, p32++)
68 {
69 if(i != 0 && (i % 16) == 0)
70 printf("\n");
71 if(unit == 1)
72 printf(" %02x", *p8);
73 else if(unit == 2)
74 printf(" %04x", *p16);
75 else
76 printf(" %08x", *p32);
77 }
78}
79
80/* key and signature */
81struct nwz_kas_t
82{
83 char kas[NWZ_KAS_SIZE];
84};
85
86#define HAS_KAS (1 << 0)
87#define HAS_KEY (1 << 1)
88#define HAS_SIG (1 << 2)
89#define CONFIRMED (1 << 3)
90
91struct nwz_model_t
92{
93 const char *model;
94 unsigned flags;
95 struct nwz_kas_t kas;
96 char key[8];
97 char sig[8];
98};
99
100struct upg_md5_t
101{
102 uint8_t md5[16];
103}__attribute__((packed));
104
105struct upg_header_t
106{
107 char sig[8];
108 uint32_t nr_files;
109 uint32_t unk;
110} __attribute__((packed));
111
112struct upg_entry_t
113{
114 uint32_t offset;
115 uint32_t size;
116} __attribute__((packed));
117
118struct nwz_model_t g_model_list[] =
119{
120 { "nwz-e463", HAS_KAS | HAS_KEY | HAS_SIG | CONFIRMED, {"89d813f8f966efdebd9c9e0ea98156d2"}, "eb4431eb", "4f1d9cac" },
121 { "nwz-a86x", HAS_KEY | HAS_SIG, {""}, "c824e4e2", "7c262bb0" },
122 { "nw-a82x", HAS_KEY | HAS_SIG, {""}, "4df06482", "07fa0b6e" },
123};
124
125static int digit_value(char c)
126{
127 if(c >= '0' && c <= '9') return c - '0';
128 if(c >= 'a' && c <= 'f') return c - 'a' + 10;
129 if(c >= 'A' && c <= 'F') return c - 'A' + 10;
130 return -1;
131}
132
133static char hex_digit(unsigned v)
134{
135 return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x';
136}
137
138static int decrypt_keysig(char keysig[NWZ_KEYSIG_SIZE])
139{
140 uint8_t src[16];
141 for(int i = 32; i < NWZ_KEYSIG_SIZE; i++)
142 keysig[i] = 0;
143 for(int index = 0; index < 16; index++)
144 {
145 int a = digit_value(keysig[index * 2]);
146 int b = digit_value(keysig[index * 2 + 1]);
147 if(a < 0 || b < 0)
148 {
149 cprintf(GREY, "Invalid KAS !\n");
150 return -1;
151 }
152 src[index] = a << 4 | b;
153 }
154 fwp_setkey("ed295076");
155 fwp_crypt(src, sizeof(src), 1);
156 memcpy(keysig + 33, src, 8);
157 memcpy(keysig + 42, src + 8, 8);
158 return 0;
159}
160
161static bool upg_notify_keysig(void *user, uint8_t key[8], uint8_t sig[8])
162{
163 memcpy(user + 33, key, 8);
164 memcpy(user + 42, sig, 8);
165 return true;
166}
167
168static int do_upg(void *buf, long size)
169{
170 struct upg_md5_t *md5 = buf;
171 cprintf(BLUE, "Preliminary\n");
172 cprintf(GREEN, " MD5: ");
173 for(int i = 0; i < 16; i++)
174 cprintf(YELLOW, "%02x", md5->md5[i]);
175 printf(" ");
176
177 uint8_t actual_md5[MD5_DIGEST_LENGTH];
178 {
179 MD5_CTX c;
180 MD5_Init(&c);
181 MD5_Update(&c, md5 + 1, size - sizeof(struct upg_header_t));
182 MD5_Final(actual_md5, &c);
183 }
184 check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n");
185
186 if(g_model_index == -1 && g_keysig_search == KEYSIG_SEARCH_NONE && g_key == NULL && g_kas == NULL)
187 {
188 cprintf(GREY, "A KAS or a keysig is needed to decrypt the firmware\n");
189 cprintf(GREY, "You have the following options(see hel for more details):\n");
190 cprintf(GREY, "- select a model with a known KAS\n");
191 cprintf(GREY, "- specify an explicit KAS or key(+optional sig)\n");
192 cprintf(GREY, "- let me try to find the keysig(slow !)\n");
193 return 1;
194 }
195
196 struct nwz_kas_t kas;
197 char keysig[NWZ_KEYSIG_SIZE];
198
199 memset(kas.kas, '?', NWZ_KAS_SIZE);
200 memset(keysig, '?', NWZ_KEYSIG_SIZE);
201 keysig[32] = keysig[41] = keysig[50] = 0;
202
203 if(g_kas)
204 {
205 if(strlen(g_kas) != NWZ_KAS_SIZE)
206 {
207 cprintf(GREY, "The specified KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE);
208 return 4;
209 }
210 memcpy(keysig, g_kas, NWZ_KAS_SIZE);
211 decrypt_keysig(keysig);
212 g_kas = keysig;
213 g_key = keysig + 33;
214 g_sig = keysig + 42;
215 }
216 else if(g_key)
217 {
218 if(strlen(g_key) != 8)
219 {
220 cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n");
221 return 4;
222 }
223 if(g_sig && strlen(g_sig) != 8)
224 {
225 cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n");
226 return 5;
227 }
228
229 memcpy(keysig + 33, g_key, 8);
230 if(!g_sig)
231 cprintf(GREY, "Warning: you have specified a key but no sig, I won't be able to do any checks\n");
232 else
233 memcpy(keysig + 42, g_sig, 8);
234 g_key = keysig + 33;
235 if(g_sig)
236 g_sig = keysig + 42;
237 }
238 else if(g_model_index == -1)
239 {
240 cprintf(BLUE, "keysig Search\n");
241 cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name);
242 bool ok = keysig_search_desc[g_keysig_search].fn((void *)(md5 + 1), &upg_notify_keysig, keysig);
243 cprintf(GREEN, " Result: ");
244 cprintf(ok ? YELLOW : RED, "%s\n", ok ? "Key found" : "No key found");
245 if(!ok)
246 return 2;
247 g_key = keysig + 33;
248 g_sig = keysig + 42;
249 }
250 else
251 {
252 if(g_model_list[g_model_index].flags & HAS_KAS)
253 g_kas = g_model_list[g_model_index].kas.kas;
254 if(g_model_list[g_model_index].flags & HAS_KEY)
255 g_key = g_model_list[g_model_index].key;
256 if(g_model_list[g_model_index].flags & HAS_SIG)
257 g_sig = g_model_list[g_model_index].sig;
258
259 if(g_kas)
260 {
261 memcpy(keysig, g_kas, NWZ_KAS_SIZE);
262 decrypt_keysig(keysig);
263 g_kas = keysig;
264 g_key = keysig + 33;
265 g_sig = keysig + 42;
266 }
267 else
268 {
269 if(g_key)
270 {
271 memcpy(keysig + 33, g_key, 8);
272 g_key = keysig + 33;
273 }
274 if(g_sig)
275 {
276 memcpy(keysig + 42, g_sig, 8);
277 g_sig = keysig + 42;
278 }
279 }
280 }
281
282 if(!g_kas)
283 {
284 g_kas = keysig;
285 fwp_setkey("ed295076");
286 if(g_key)
287 {
288 memcpy(kas.kas, g_key, 8);
289 fwp_crypt(kas.kas, 8, 0);
290 for(int i = 0; i < 8; i++)
291 {
292 g_kas[2 * i] = hex_digit((kas.kas[i] >> 4) & 0xf);
293 g_kas[2 * i + 1] = hex_digit(kas.kas[i] & 0xf);
294 }
295 }
296 if(g_sig)
297 {
298 memcpy(kas.kas + 8, g_sig, 8);
299 fwp_crypt(kas.kas + 8, 8, 0);
300 for(int i = 8; i < 16; i++)
301 {
302 g_kas[2 * i] = hex_digit((kas.kas[i] >> 4) & 0xf);
303 g_kas[2 * i + 1] = hex_digit(kas.kas[i] & 0xf);
304 }
305 }
306 }
307
308 cprintf(BLUE, "Keys\n");
309 cprintf_field(" KAS: ", "%."STR(NWZ_KAS_SIZE)"s\n", g_kas);
310 cprintf_field(" Key: ", "%s\n", g_key);
311 if(g_sig)
312 cprintf_field(" Sig: ", "%s\n", g_sig);
313
314 struct upg_header_t *hdr = (void *)(md5 + 1);
315 int ret = fwp_read(hdr, sizeof(struct upg_header_t), hdr, (void *)g_key);
316 if(ret)
317 return ret;
318
319 cprintf(BLUE, "Header\n");
320 cprintf_field(" Signature:", " ");
321 for(int i = 0; i < 8; i++)
322 cprintf(YELLOW, "%c", isprint(hdr->sig[i]) ? hdr->sig[i] : '.');
323 if(g_sig)
324 {
325 check_field(memcmp(hdr->sig, g_sig, 8), 0, " OK\n", " Mismatch\n");
326 }
327 else
328 cprintf(RED, " Can't check\n");
329 cprintf_field(" Files: ", "%d\n", hdr->nr_files);
330 cprintf_field(" Unk: ", "0x%x\n", hdr->unk);
331
332 cprintf(BLUE, "Files\n");
333 struct upg_entry_t *entry = (void *)(hdr + 1);
334 for(unsigned i = 0; i < hdr->nr_files; i++, entry++)
335 {
336 int ret = fwp_read(entry, sizeof(struct upg_entry_t), entry, (void *)g_key);
337 if(ret)
338 return ret;
339 cprintf(GREY, " File");
340 cprintf(RED, " %d\n", i);
341 cprintf_field(" Offset: ", "0x%x\n", entry->offset);
342 cprintf_field(" Size: ", "0x%x\n", entry->size);
343
344 if(g_out_prefix)
345 {
346 char *str = malloc(strlen(g_out_prefix) + 32);
347 sprintf(str, "%s/%d.bin", g_out_prefix, i);
348 FILE *f = fopen(str, "wb");
349 if(f)
350 {
351 int ret = fwp_read(buf + entry->offset, entry->size,
352 buf + entry->offset, (void *)g_key);
353 if(ret)
354 return ret;
355 fwrite(buf + entry->offset, 1, entry->size, f);
356
357 fclose(f);
358 }
359 else
360 cprintf(GREY, "Cannot open '%s' for writing\n", str);
361 }
362 }
363
364 return 0;
365}
366
367static void usage(void)
368{
369 color(OFF);
370 printf("Usage: upgtool [options] firmware\n");
371 printf("Options:\n");
372 printf(" -o <prefix>\t\tSet output prefix\n");
373 printf(" -f/--force\t\tForce to continue on errors\n");
374 printf(" -?/--help\t\tDisplay this message\n");
375 printf(" -d/--debug\t\tDisplay debug messages\n");
376 printf(" -c/--no-color\t\tDisable color output\n");
377 printf(" -m/--model <model>\tSelect model (or ? to list them)\n");
378 printf(" -l/--search <method>\tTry to find the keysig\n");
379 printf(" -a/--kas <kas>\tForce KAS\n");
380 printf(" -k/--key <key>\tForce key\n");
381 printf(" -s/--sig <sig>\tForce sig\n");
382 printf("keysig search method:\n");
383 for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++)
384 printf(" %s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment);
385 exit(1);
386}
387
388int main(int argc, char **argv)
389{
390 while(1)
391 {
392 static struct option long_options[] =
393 {
394 {"help", no_argument, 0, '?'},
395 {"debug", no_argument, 0, 'd'},
396 {"no-color", no_argument, 0, 'c'},
397 {"force", no_argument, 0, 'f'},
398 {"model", required_argument, 0, 'm'},
399 {"search", required_argument, 0, 'l'},
400 {"kas", required_argument, 0, 'a'},
401 {"key", required_argument, 0, 'k'},
402 {"sig", required_argument, 0, 's'},
403 {0, 0, 0, 0}
404 };
405
406 int c = getopt_long(argc, argv, "?dcfo:m:l:a:k:s:", long_options, NULL);
407 if(c == -1)
408 break;
409 switch(c)
410 {
411 case -1:
412 break;
413 case 'c':
414 enable_color(false);
415 break;
416 case 'd':
417 g_debug = true;
418 break;
419 case 'f':
420 g_force = true;
421 break;
422 case '?':
423 usage();
424 break;
425 case 'o':
426 g_out_prefix = optarg;
427 break;
428 case 'm':
429 g_model = optarg;
430 break;
431 case 'l':
432 g_keysig_search = KEYSIG_SEARCH_NONE;
433 for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++)
434 if(strcmp(keysig_search_desc[i].name, optarg) == 0)
435 g_keysig_search = i;
436 if(g_keysig_search == KEYSIG_SEARCH_NONE)
437 {
438 cprintf(GREY, "Unknown keysig search method '%s'\n", optarg);
439 return 1;
440 }
441 break;
442 case 'a':
443 g_kas = optarg;
444 break;
445 case 'k':
446 g_key = optarg;
447 break;
448 case 's':
449 g_sig = optarg;
450 break;
451 default:
452 abort();
453 }
454 }
455
456 if(g_model && strcmp(g_model, "?") == 0)
457 {
458 cprintf(BLUE, "Model list:\n");
459 for(unsigned i = 0; i < sizeof(g_model_list) / sizeof(g_model_list[0]); i++)
460 {
461 cprintf(GREEN, " %s:", g_model_list[i].model);
462 if(g_model_list[i].flags & HAS_KAS)
463 {
464 cprintf(RED, " kas=");
465 cprintf(YELLOW, "%."STR(NWZ_KAS_SIZE)"s", g_model_list[i].kas.kas);
466 }
467 if(g_model_list[i].flags & HAS_KEY)
468 {
469 cprintf(RED, " key=");
470 cprintf(YELLOW, "%.8s", g_model_list[i].key);
471 }
472 if(g_model_list[i].flags & HAS_SIG)
473 {
474 cprintf(RED, " sig=");
475 cprintf(YELLOW, "%.8s", g_model_list[i].sig);
476 }
477 if(g_model_list[i].flags & CONFIRMED)
478 cprintf(RED, " confirmed");
479 else
480 cprintf(RED, " guessed");
481 printf("\n");
482 }
483 return 1;
484 }
485
486 if(g_model)
487 {
488 for(unsigned i = 0; i < sizeof(g_model_list) / sizeof(g_model_list[0]); i++)
489 if(strcmp(g_model, g_model_list[i].model) == 0)
490 g_model_index = i;
491 if(g_model_index == -1)
492 cprintf(GREY, "Warning: unknown model %s\n", g_model);
493 }
494
495 if(argc - optind != 1)
496 {
497 usage();
498 return 1;
499 }
500
501 g_in_file = argv[optind];
502 FILE *fin = fopen(g_in_file, "r");
503 if(fin == NULL)
504 {
505 perror("Cannot open boot file");
506 return 1;
507 }
508 fseek(fin, 0, SEEK_END);
509 long size = ftell(fin);
510 fseek(fin, 0, SEEK_SET);
511
512 void *buf = malloc(size);
513 if(buf == NULL)
514 {
515 perror("Cannot allocate memory");
516 return 1;
517 }
518
519 if(fread(buf, size, 1, fin) != 1)
520 {
521 perror("Cannot read file");
522 return 1;
523 }
524
525 fclose(fin);
526
527 int ret = do_upg(buf, size);
528 if(ret != 0)
529 {
530 cprintf(GREY, "Error: %d", ret);
531 if(!g_force)
532 cprintf(GREY, " (use --force to force processing)");
533 printf("\n");
534 ret = 2;
535 }
536 free(buf);
537
538 color(OFF);
539
540 return ret;
541}
542