mirror of
				https://github.com/Rockbox/rockbox.git
				synced 2025-10-29 00:36:22 -04:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									de8950d63d
								
							
						
					
					
						commit
						cf82f208e3
					
				
					 6 changed files with 291 additions and 298 deletions
				
			
		|  | @ -28,8 +28,9 @@ extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define NWZ_KAS_SIZE    32 | #define NWZ_KAS_SIZE    32 | ||||||
| #define NWZ_KEYSIG_SIZE 51 | #define NWZ_KEYSIG_SIZE 16 | ||||||
| #define NWZ_KEY_SIZE    8 | #define NWZ_KEY_SIZE    8 | ||||||
|  | #define NWZ_SIG_SIZE    8 | ||||||
| #define NWZ_EXPKEY_SIZE (NWZ_KEY_SIZE * NWZ_KEY_SIZE) | #define NWZ_EXPKEY_SIZE (NWZ_KEY_SIZE * NWZ_KEY_SIZE) | ||||||
| #define NWZ_DES_BLOCK   8 | #define NWZ_DES_BLOCK   8 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,8 +22,24 @@ | ||||||
| #include "misc.h" | #include "misc.h" | ||||||
| #include "mg.h" | #include "mg.h" | ||||||
| #include <string.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 uint8_t g_cipher[8]; | ||||||
| static keysig_notify_fn_t g_notify; | static keysig_notify_fn_t g_notify; | ||||||
|  | @ -31,11 +47,10 @@ static uint8_t g_key[8]; | ||||||
| static void *g_user; | static void *g_user; | ||||||
| static bool is_hex[256]; | static bool is_hex[256]; | ||||||
| static bool is_init = false; | static bool is_init = false; | ||||||
| #ifdef HEX_MAJ | static uint64_t g_tot_count; | ||||||
| static char hex_digits[] = "02468ABEF"; | static uint64_t g_cur_count; | ||||||
| #else | static int g_last_percent; | ||||||
| static char hex_digits[] = "02468abef"; | static int g_last_subpercent; | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| static void keysig_search_init() | static void keysig_search_init() | ||||||
| { | { | ||||||
|  | @ -44,11 +59,7 @@ static void keysig_search_init() | ||||||
|     memset(is_hex, 0, sizeof(is_hex)); |     memset(is_hex, 0, sizeof(is_hex)); | ||||||
|     for(int i = '0'; i <= '9'; i++) |     for(int i = '0'; i <= '9'; i++) | ||||||
|         is_hex[i] = true; |         is_hex[i] = true; | ||||||
| #ifdef HEX_MAJ |  | ||||||
|     for(int i = 'A'; i <= 'F'; i++) |  | ||||||
| #else |  | ||||||
|     for(int i = 'a'; i <= 'f'; i++) |     for(int i = 'a'; i <= 'f'; i++) | ||||||
| #endif |  | ||||||
|         is_hex[i] = true; |         is_hex[i] = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -63,6 +74,24 @@ static inline bool is_full_ascii(uint8_t *arr) | ||||||
| static inline bool check_stupid() | static inline bool check_stupid() | ||||||
| { | { | ||||||
|     uint8_t res[8]; |     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); |     mg_decrypt_fw(g_cipher, 8, res, g_key); | ||||||
|     if(is_full_ascii(res)) |     if(is_full_ascii(res)) | ||||||
|         return g_notify(g_user, g_key, 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(); |         return check_stupid(); | ||||||
|     if(rem_digit > 0) |     if(rem_digit > 0) | ||||||
|     { |     { | ||||||
|         for(int i = '0'; i <= '9'; i += 2) |         for(int i = '0'; i <= '9'; i++) | ||||||
|         { |         { | ||||||
|             g_key[pos] = i; |             g_key[pos] = i; | ||||||
|             if(search_stupid_rec(rem_digit - 1, rem_letter, pos + 1)) |             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) |     if(rem_letter > 0) | ||||||
|     { |     { | ||||||
| #ifdef HEX_MAJ |         for(int i = 'a' - 1; i <= 'f'; i++) | ||||||
|         for(int i = 'a' - 1; i <= 'f'; i += 2) |  | ||||||
| #else |  | ||||||
|         for(int i = 'A' - 1; i <= 'F'; i += 2) |  | ||||||
| #endif |  | ||||||
|         { |         { | ||||||
|             g_key[pos] = i; |             g_key[pos] = i; | ||||||
|             if(search_stupid_rec(rem_digit, rem_letter - 1, pos + 1)) |             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) | 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); |     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); |     memcpy(g_cipher, cipher, 8); | ||||||
|     g_notify = notify; |     g_notify = notify; | ||||||
|     g_user = user; |     g_user = user; | ||||||
| #if 1 |     // compute number of possibilities
 | ||||||
|     return search_stupid(4, 4) || |     g_cur_count = 0; | ||||||
|         search_stupid(3, 5) || search_stupid(5, 3) || |     g_tot_count = 1; | ||||||
|         search_stupid(2, 6) || search_stupid(6, 2) || |     g_last_percent = -1; | ||||||
|         search_stupid(1, 7) || search_stupid(7, 1) || |     for(int i = 0; i < 8; i++) | ||||||
|         search_stupid(0, 8) || search_stupid(8, 0); |         g_tot_count *= 16ULL; | ||||||
| #else |     cprintf(WHITE, "    Search space:"); | ||||||
| #define do(i) for(int a##i = 0; a##i < sizeof(hex_digits); a##i++) { g_key[i] = hex_digits[a##i]; |     cprintf(RED, " %llu", (unsigned long long)g_tot_count); | ||||||
| #define od() } |     // sorted by probability:
 | ||||||
| 
 |     bool ret = search_stupid(5, 3) // 5 digits, 3 letters: 0.281632
 | ||||||
|     do(0)do(1)do(2)do(3)do(4)do(5)do(6)do(7) |         || search_stupid(6, 2) // 6 digits, 2 letters: 0.234693
 | ||||||
|         if(check_stupid()) return true; |         || search_stupid(4, 4) // 4 digits, 4 letters: 0.211224
 | ||||||
|     od()od()od()od()od()od()od()od() |         || search_stupid(7, 1) // 7 digits, 1 letters: 0.111759
 | ||||||
| #undef do |         || search_stupid(3, 5) // 3 digits, 5 letters: 0.101388
 | ||||||
| #undef od |         || search_stupid(2, 6) // 2 digits, 6 letters: 0.030416
 | ||||||
|     return false; |         || search_stupid(8, 0) // 8 digits, 0 letters: 0.023283
 | ||||||
| #endif |         || 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) | bool keysig_search_ascii_brute(uint8_t *cipher, keysig_notify_fn_t notify, void *user) | ||||||
| { | { | ||||||
|  |     (void) cipher; | ||||||
|  |     (void) notify; | ||||||
|  |     (void) user; | ||||||
|     keysig_search_init(); |     keysig_search_init(); | ||||||
|  |     cprintf(RED, "Unimplemented\n"); | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -144,9 +182,9 @@ struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = | ||||||
|     }, |     }, | ||||||
|     [KEYSIG_SEARCH_ASCII_STUPID] = |     [KEYSIG_SEARCH_ASCII_STUPID] = | ||||||
|     { |     { | ||||||
|         .name = "ascii-stupid", |         .name = "ascii-hex", | ||||||
|         .fn = keysig_search_ascii_stupid, |         .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] = |     [KEYSIG_SEARCH_ASCII_BRUTE] = | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | #include "fwp.h" | ||||||
| 
 | 
 | ||||||
| enum keysig_search_method_t | enum keysig_search_method_t | ||||||
| { | { | ||||||
|  | @ -34,7 +35,8 @@ enum keysig_search_method_t | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* notify returns true if the key seems ok */ | /* 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 */ | /* 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); | typedef bool (*keysig_search_fn_t)(uint8_t *cipher, keysig_notify_fn_t notify, void *user); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; | ||||||
| char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; | char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; | ||||||
| char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 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; | static bool g_color_enable = true; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -34,7 +34,7 @@ | ||||||
| 
 | 
 | ||||||
| typedef char color_t[]; | 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 *xmalloc(size_t s); | ||||||
| void color(color_t c); | void color(color_t c); | ||||||
| void enable_color(bool enable); | void enable_color(bool enable); | ||||||
|  |  | ||||||
|  | @ -69,9 +69,9 @@ struct nwz_model_t | ||||||
| { | { | ||||||
|     const char *model; |     const char *model; | ||||||
|     unsigned flags; |     unsigned flags; | ||||||
|     char kas[NWZ_KAS_SIZE]; /* key and signature */ |     char *kas; | ||||||
|     char key[8]; |     char *key; | ||||||
|     char sig[8]; |     char *sig; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct upg_md5_t | struct upg_md5_t | ||||||
|  | @ -81,7 +81,7 @@ struct upg_md5_t | ||||||
| 
 | 
 | ||||||
| struct upg_header_t | struct upg_header_t | ||||||
| { | { | ||||||
|     char sig[8]; |     char sig[NWZ_SIG_SIZE]; | ||||||
|     uint32_t nr_files; |     uint32_t nr_files; | ||||||
|     uint32_t pad; // make sure structure size is a multiple of 8
 |     uint32_t pad; // make sure structure size is a multiple of 8
 | ||||||
| } __attribute__((packed)); | } __attribute__((packed)); | ||||||
|  | @ -92,6 +92,72 @@ struct upg_entry_t | ||||||
|     uint32_t size; |     uint32_t size; | ||||||
| } __attribute__((packed)); | } __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[] = | struct nwz_model_t g_model_list[] = | ||||||
| { | { | ||||||
|     { "nwz-e45x", HAS_KAS | HAS_KEY | HAS_SIG | CONFIRMED, "8a01b624bfbfde4a1662a1772220e3c5", "6173819e", "30b82e5c"}, |     { "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" }, |     { "nwz-a86x", HAS_KAS | HAS_KEY | HAS_SIG | CONFIRMED, "a7c4af6c28b8900a783f307c1ba538c5", "c824e4e2", "7c262bb0" }, | ||||||
|     /* The following keys were obtained by brute forcing firmware upgrades,
 |     /* The following keys were obtained by brute forcing firmware upgrades,
 | ||||||
|      * someone with a device needs to confirm that they work */ |      * 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) | 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'; |     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]; |     uint8_t src[NWZ_KAS_SIZE / 2]; | ||||||
|     for(int i = 32; i < NWZ_KEYSIG_SIZE; i++) |     for(int index = 0; index < NWZ_KAS_SIZE / 2; index++) | ||||||
|         keysig[i] = 0; |  | ||||||
|     for(int index = 0; index < 16; index++) |  | ||||||
|     { |     { | ||||||
|         int a = digit_value(keysig[index * 2]); |         int a = digit_value(kas[index * 2]); | ||||||
|         int b = digit_value(keysig[index * 2 + 1]); |         int b = digit_value(kas[index * 2 + 1]); | ||||||
|         if(a < 0 || b < 0) |         if(a < 0 || b < 0) | ||||||
|         { |         { | ||||||
|             cprintf(GREY, "Invalid KAS !\n"); |             cprintf(GREY, "Invalid KAS !\n"); | ||||||
|  | @ -133,18 +198,137 @@ static int decrypt_keysig(char keysig[NWZ_KEYSIG_SIZE]) | ||||||
|     } |     } | ||||||
|     fwp_setkey("ed295076"); |     fwp_setkey("ed295076"); | ||||||
|     fwp_crypt(src, sizeof(src), 1); |     fwp_crypt(src, sizeof(src), 1); | ||||||
|     memcpy(keysig + 33, src, 8); |     memcpy(key, src, NWZ_KEY_SIZE); | ||||||
|     memcpy(keysig + 42, src + 8, 8); |     memcpy(sig, src + NWZ_KEY_SIZE, NWZ_SIG_SIZE); | ||||||
|     return 0; |     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); |     uint8_t src[NWZ_KAS_SIZE / 2]; | ||||||
|     memcpy(user + 42, sig, 8); |     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); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* 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]) | ||||||
|  | { | ||||||
|  |     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; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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) | ||||||
|  |     { | ||||||
|  |         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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* always prefer KAS because it contains everything */ | ||||||
|  |     if(g_kas) | ||||||
|  |     { | ||||||
|  |         if(strlen(g_kas) != NWZ_KAS_SIZE) | ||||||
|  |         { | ||||||
|  |             cprintf(GREY, "The KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE); | ||||||
|  |             return 4; | ||||||
|  |         } | ||||||
|  |         g_key = keysig; | ||||||
|  |         g_sig = keysig + NWZ_KEY_SIZE; | ||||||
|  |         decrypt_keysig(g_kas, g_key, g_sig); | ||||||
|  |     } | ||||||
|  |     /* 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 there is a signature, it must have the correct size */ | ||||||
|  |         if(g_sig) | ||||||
|  |         { | ||||||
|  |             if(strlen(g_sig) != 8) | ||||||
|  |             { | ||||||
|  |                 cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); | ||||||
|  |                 return 5; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             cprintf(GREY, "Warning: you have specified a key but no sig, I won't be able to do any checks\n"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /* 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(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; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         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) | ||||||
|  |     { | ||||||
|  |         if(!g_sig) | ||||||
|  |         { | ||||||
|  |             /* 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: ", "%."STR(NWZ_KEY_SIZE)"s\n", g_key); | ||||||
|  |     if(g_sig) | ||||||
|  |         cprintf_field("  Sig: ", "%."STR(NWZ_SIG_SIZE)"s\n", g_sig); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int do_upg(void *buf, long size) | static int do_upg(void *buf, long size) | ||||||
| { | { | ||||||
|     struct upg_md5_t *md5 = buf; |     struct upg_md5_t *md5 = buf; | ||||||
|  | @ -163,136 +347,12 @@ static int do_upg(void *buf, long size) | ||||||
|     } |     } | ||||||
|     check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n"); |     check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n"); | ||||||
| 
 | 
 | ||||||
|     if(g_model_index == -1 && g_keysig_search == KEYSIG_SEARCH_NONE && g_key == NULL && g_kas == NULL) |     int ret = get_key_and_sig(true, md5 + 1); | ||||||
|     { |     if(ret != 0) | ||||||
|         cprintf(GREY, "A KAS or a keysig is needed to decrypt the firmware\n"); |         return ret; | ||||||
|         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; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     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(g_sig && 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; |  | ||||||
|     } |  | ||||||
|     else if(g_model_index == -1) |  | ||||||
|     { |  | ||||||
|         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); |  | ||||||
|         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; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     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) |  | ||||||
|         { |  | ||||||
|             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); |  | ||||||
| 
 | 
 | ||||||
|     struct upg_header_t *hdr = (void *)(md5 + 1); |     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) |     if(ret) | ||||||
|         return ret; |         return ret; | ||||||
| 
 | 
 | ||||||
|  | @ -336,7 +396,6 @@ static int do_upg(void *buf, long size) | ||||||
|                     return ret; |                     return ret; | ||||||
|                 // but write the *good* amount of data
 |                 // but write the *good* amount of data
 | ||||||
|                 fwrite(buf + entry->offset, 1, entry->size, f); |                 fwrite(buf + entry->offset, 1, entry->size, f); | ||||||
|                  |  | ||||||
|                 fclose(f); |                 fclose(f); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|  | @ -415,117 +474,9 @@ static int create_upg(int argc, char **argv) | ||||||
|         usage(); |         usage(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(g_model_index == -1 && (g_key == NULL || g_sig == NULL) && g_kas == NULL) |     int ret = get_key_and_sig(false, NULL); | ||||||
|     { |     if(ret != 0) | ||||||
|         cprintf(GREY, "A KAS or a keysig is needed to encrypt the firmware\n"); |         return ret; | ||||||
|         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); |  | ||||||
| 
 | 
 | ||||||
|     FILE *fout = fopen(argv[0], "wb"); |     FILE *fout = fopen(argv[0], "wb"); | ||||||
|     if(fout == NULL) |     if(fout == NULL) | ||||||
|  | @ -559,7 +510,7 @@ static int create_upg(int argc, char **argv) | ||||||
|     hdr.nr_files = nr_files; |     hdr.nr_files = nr_files; | ||||||
|     hdr.pad = 0; |     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) |     if(ret) | ||||||
|         return ret; |         return ret; | ||||||
|     MD5_Update(&c, &hdr, sizeof(hdr)); |     MD5_Update(&c, &hdr, sizeof(hdr)); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue