| /*************************************************************************** |
| * __________ __ ___. |
| * 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 <stdio.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <getopt.h> |
| #include <stdarg.h> |
| #include <ctype.h> |
| #include "misc.h" |
| #include "elf.h" |
| #include <sys/stat.h> |
| #include <openssl/md5.h> |
| #include "crypt.h" |
| #include "fwp.h" |
| #include "keysig_search.h" |
| |
| #ifndef MIN |
| #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
| #endif |
| |
| bool g_debug = false; |
| static char *g_out_prefix = NULL; |
| static char *g_in_file = NULL; |
| bool g_force = false; |
| static const char *g_model = NULL; |
| static int g_model_index = -1; |
| static char *g_kas = NULL; |
| static char *g_key = NULL; |
| static char *g_sig = NULL; |
| |
| enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE; |
| |
| #define let_the_force_flow(x) do { if(!g_force) return x; } while(0) |
| #define continue_the_force(x) if(x) let_the_force_flow(x) |
| |
| #define check_field(v_exp, v_have, str_ok, str_bad) \ |
| if((v_exp) != (v_have)) \ |
| { cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \ |
| else { cprintf(RED, str_ok); } |
| |
| static void print_hex(void *p, int size, int unit) |
| { |
| uint8_t *p8 = p; |
| uint16_t *p16 = p; |
| uint32_t *p32 = p; |
| for(int i = 0; i < size; i += unit, p8++, p16++, p32++) |
| { |
| if(i != 0 && (i % 16) == 0) |
| printf("\n"); |
| if(unit == 1) |
| printf(" %02x", *p8); |
| else if(unit == 2) |
| printf(" %04x", *p16); |
| else |
| printf(" %08x", *p32); |
| } |
| } |
| |
| static void usage(void); |
| |
| /* key and signature */ |
| struct nwz_kas_t |
| { |
| char kas[NWZ_KAS_SIZE]; |
| }; |
| |
| #define HAS_KAS (1 << 0) |
| #define HAS_KEY (1 << 1) |
| #define HAS_SIG (1 << 2) |
| #define CONFIRMED (1 << 3) |
| |
| struct nwz_model_t |
| { |
| const char *model; |
| unsigned flags; |
| struct nwz_kas_t kas; |
| char key[8]; |
| char sig[8]; |
| }; |
| |
| struct upg_md5_t |
| { |
| uint8_t md5[16]; |
| }__attribute__((packed)); |
| |
| struct upg_header_t |
| { |
| char sig[8]; |
| uint32_t nr_files; |
| uint32_t pad; // make sure structure size is a multiple of 8 |
| } __attribute__((packed)); |
| |
| struct upg_entry_t |
| { |
| uint32_t offset; |
| uint32_t size; |
| } __attribute__((packed)); |
| |
| struct nwz_model_t g_model_list[] = |
| { |
| { "nwz-e463", HAS_KAS | HAS_KEY | HAS_SIG | CONFIRMED, {"89d813f8f966efdebd9c9e0ea98156d2"}, "eb4431eb", "4f1d9cac" }, |
| { "nwz-a86x", HAS_KEY | HAS_SIG, {""}, "c824e4e2", "7c262bb0" }, |
| { "nw-a82x", HAS_KEY | HAS_SIG, {""}, "4df06482", "07fa0b6e" }, |
| }; |
| |
| static int digit_value(char c) |
| { |
| if(c >= '0' && c <= '9') return c - '0'; |
| if(c >= 'a' && c <= 'f') return c - 'a' + 10; |
| if(c >= 'A' && c <= 'F') return c - 'A' + 10; |
| return -1; |
| } |
| |
| static char hex_digit(unsigned v) |
| { |
| return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x'; |
| } |
| |
| static int decrypt_keysig(char keysig[NWZ_KEYSIG_SIZE]) |
| { |
| uint8_t src[16]; |
| for(int i = 32; i < NWZ_KEYSIG_SIZE; i++) |
| keysig[i] = 0; |
| for(int index = 0; index < 16; index++) |
| { |
| int a = digit_value(keysig[index * 2]); |
| int b = digit_value(keysig[index * 2 + 1]); |
| if(a < 0 || b < 0) |
| { |
| cprintf(GREY, "Invalid KAS !\n"); |
| return -1; |
| } |
| src[index] = a << 4 | b; |
| } |
| fwp_setkey("ed295076"); |
| fwp_crypt(src, sizeof(src), 1); |
| memcpy(keysig + 33, src, 8); |
| memcpy(keysig + 42, src + 8, 8); |
| return 0; |
| } |
| |
| static bool upg_notify_keysig(void *user, uint8_t key[8], uint8_t sig[8]) |
| { |
| memcpy(user + 33, key, 8); |
| memcpy(user + 42, sig, 8); |
| return true; |
| } |
| |
| static int do_upg(void *buf, long size) |
| { |
| struct upg_md5_t *md5 = buf; |
| cprintf(BLUE, "Preliminary\n"); |
| cprintf(GREEN, " MD5: "); |
| for(int i = 0; i < 16; i++) |
| cprintf(YELLOW, "%02x", md5->md5[i]); |
| printf(" "); |
| |
| uint8_t actual_md5[MD5_DIGEST_LENGTH]; |
| { |
| MD5_CTX c; |
| MD5_Init(&c); |
| MD5_Update(&c, md5 + 1, size - sizeof(struct upg_header_t)); |
| MD5_Final(actual_md5, &c); |
| } |
| check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n"); |
| |
| if(g_model_index == -1 && g_keysig_search == KEYSIG_SEARCH_NONE && g_key == NULL && g_kas == NULL) |
| { |
| cprintf(GREY, "A KAS or a keysig is needed to decrypt the firmware\n"); |
| cprintf(GREY, "You have the following options(see help for more details):\n"); |
| cprintf(GREY, "- select a model with a known KAS\n"); |
| cprintf(GREY, "- specify an explicit KAS or key(+optional sig)\n"); |
| cprintf(GREY, "- let me try to find the keysig(slow !)\n"); |
| return 1; |
| } |
| |
| struct nwz_kas_t kas; |
| char keysig[NWZ_KEYSIG_SIZE]; |
| |
| memset(kas.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.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.kas, g_key, 8); |
| fwp_crypt(kas.kas, 8, 0); |
| for(int i = 0; i < 8; i++) |
| { |
| g_kas[2 * i] = hex_digit((kas.kas[i] >> 4) & 0xf); |
| g_kas[2 * i + 1] = hex_digit(kas.kas[i] & 0xf); |
| } |
| } |
| if(g_sig) |
| { |
| memcpy(kas.kas + 8, g_sig, 8); |
| fwp_crypt(kas.kas + 8, 8, 0); |
| for(int i = 8; i < 16; i++) |
| { |
| g_kas[2 * i] = hex_digit((kas.kas[i] >> 4) & 0xf); |
| g_kas[2 * i + 1] = hex_digit(kas.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); |
| int ret = fwp_read(hdr, sizeof(struct upg_header_t), hdr, (void *)g_key); |
| if(ret) |
| return ret; |
| |
| cprintf(BLUE, "Header\n"); |
| cprintf_field(" Signature:", " "); |
| for(int i = 0; i < 8; i++) |
| cprintf(YELLOW, "%c", isprint(hdr->sig[i]) ? hdr->sig[i] : '.'); |
| if(g_sig) |
| { |
| check_field(memcmp(hdr->sig, g_sig, 8), 0, " OK\n", " Mismatch\n"); |
| } |
| else |
| cprintf(RED, " Can't check\n"); |
| cprintf_field(" Files: ", "%d\n", hdr->nr_files); |
| cprintf_field(" Pad: ", "0x%x\n", hdr->pad); |
| |
| cprintf(BLUE, "Files\n"); |
| struct upg_entry_t *entry = (void *)(hdr + 1); |
| for(unsigned i = 0; i < hdr->nr_files; i++, entry++) |
| { |
| int ret = fwp_read(entry, sizeof(struct upg_entry_t), entry, (void *)g_key); |
| if(ret) |
| return ret; |
| cprintf(GREY, " File"); |
| cprintf(RED, " %d\n", i); |
| cprintf_field(" Offset: ", "0x%x\n", entry->offset); |
| cprintf_field(" Size: ", "0x%x\n", entry->size); |
| |
| if(g_out_prefix) |
| { |
| char *str = malloc(strlen(g_out_prefix) + 32); |
| sprintf(str, "%s%d.bin", g_out_prefix, i); |
| FILE *f = fopen(str, "wb"); |
| if(f) |
| { |
| // round up size, there is some padding done with random data |
| int crypt_size = ROUND_UP(entry->size, 8); |
| int ret = fwp_read(buf + entry->offset, crypt_size, |
| buf + entry->offset, (void *)g_key); |
| if(ret) |
| return ret; |
| // but write the *good* amount of data |
| fwrite(buf + entry->offset, 1, entry->size, f); |
| |
| fclose(f); |
| } |
| else |
| cprintf(GREY, "Cannot open '%s' for writing\n", str); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int extract_upg(int argc, char **argv) |
| { |
| if(argc == 0 || argc > 1) |
| { |
| if(argc == 0) |
| printf("You must specify a firmware file\n"); |
| else |
| printf("Extra arguments after firmware file\n"); |
| usage(); |
| } |
| |
| g_in_file = argv[0]; |
| FILE *fin = fopen(g_in_file, "r"); |
| if(fin == NULL) |
| { |
| perror("Cannot open boot file"); |
| return 1; |
| } |
| fseek(fin, 0, SEEK_END); |
| long size = ftell(fin); |
| fseek(fin, 0, SEEK_SET); |
| |
| void *buf = malloc(size); |
| if(buf == NULL) |
| { |
| perror("Cannot allocate memory"); |
| return 1; |
| } |
| |
| if(fread(buf, size, 1, fin) != 1) |
| { |
| perror("Cannot read file"); |
| return 1; |
| } |
| |
| fclose(fin); |
| |
| int ret = do_upg(buf, size); |
| if(ret != 0) |
| { |
| cprintf(GREY, "Error: %d", ret); |
| if(!g_force) |
| cprintf(GREY, " (use --force to force processing)"); |
| printf("\n"); |
| ret = 2; |
| } |
| free(buf); |
| |
| return ret; |
| } |
| |
| static long filesize(FILE *f) |
| { |
| long pos = ftell(f); |
| fseek(f, 0, SEEK_END); |
| long size = ftell(f); |
| fseek(f, pos, SEEK_SET); |
| return size; |
| } |
| |
| static int create_upg(int argc, char **argv) |
| { |
| if(argc == 0) |
| { |
| printf("You must specify a firmware filename\n"); |
| usage(); |
| } |
| |
| if(g_model_index == -1 && (g_key == NULL || g_sig == NULL) && g_kas == NULL) |
| { |
| cprintf(GREY, "A KAS or a keysig is needed to encrypt the firmware\n"); |
| cprintf(GREY, "You have the following options(see help for more details):\n"); |
| cprintf(GREY, "- select a model with a known KAS\n"); |
| cprintf(GREY, "- specify an explicit KAS or key+sig\n"); |
| return 1; |
| } |
| |
| struct nwz_kas_t kas; |
| char keysig[NWZ_KEYSIG_SIZE]; |
| |
| memset(kas.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.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.kas, g_key, 8); |
| fwp_crypt(kas.kas, 8, 0); |
| for(int i = 0; i < 8; i++) |
| { |
| g_kas[2 * i] = hex_digit((kas.kas[i] >> 4) & 0xf); |
| g_kas[2 * i + 1] = hex_digit(kas.kas[i] & 0xf); |
| } |
| memcpy(kas.kas + 8, g_sig, 8); |
| fwp_crypt(kas.kas + 8, 8, 0); |
| for(int i = 8; i < 16; i++) |
| { |
| g_kas[2 * i] = hex_digit((kas.kas[i] >> 4) & 0xf); |
| g_kas[2 * i + 1] = hex_digit(kas.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"); |
| if(fout == NULL) |
| { |
| printf("Cannot open output firmware file: %m\n"); |
| return 1; |
| } |
| |
| int nr_files = argc - 1; |
| FILE **files = malloc(nr_files * sizeof(FILE *)); |
| |
| for(int i = 0; i < nr_files; i++) |
| { |
| files[i] = fopen(argv[1 + i], "rb"); |
| if(files[i] == NULL) |
| { |
| printf("Cannot open input file '%s': %m\n", argv[i + 1]); |
| return 1; |
| } |
| } |
| |
| struct upg_md5_t md5; |
| memset(&md5, 0, sizeof(md5)); |
| MD5_CTX c; |
| MD5_Init(&c); |
| // output a dummy md5 sum |
| fwrite(&md5, 1, sizeof(md5), fout); |
| // output the encrypted signature |
| struct upg_header_t hdr; |
| memcpy(hdr.sig, g_sig, 8); |
| hdr.nr_files = nr_files; |
| hdr.pad = 0; |
| |
| int ret = fwp_write(&hdr, sizeof(hdr), &hdr, (void *)g_key); |
| if(ret) |
| return ret; |
| MD5_Update(&c, &hdr, sizeof(hdr)); |
| fwrite(&hdr, 1, sizeof(hdr), fout); |
| |
| // output file headers |
| long offset = sizeof(md5) + sizeof(hdr) + nr_files * sizeof(struct upg_entry_t); |
| for(int i = 0; i < nr_files; i++) |
| { |
| struct upg_entry_t entry; |
| entry.offset = offset; |
| entry.size = filesize(files[i]); |
| offset += ROUND_UP(entry.size, 8); // do it before encryption !! |
| |
| ret = fwp_write(&entry, sizeof(entry), &entry, (void *)g_key); |
| if(ret) |
| return ret; |
| MD5_Update(&c, &entry, sizeof(entry)); |
| fwrite(&entry, 1, sizeof(entry), fout); |
| } |
| |
| cprintf(BLUE, "Files\n"); |
| for(int i = 0; i < nr_files; i++) |
| { |
| long size = filesize(files[i]); |
| long r_size = ROUND_UP(size, 8); |
| cprintf(GREY, " File"); |
| cprintf(RED, " %d\n", i); |
| cprintf_field(" Offset: ", "0x%lx\n", ftell(fout)); |
| cprintf_field(" Size: ", "0x%lx\n", size); |
| |
| void *buf = malloc(r_size); |
| memset(buf, 0, r_size); |
| fread(buf, 1, size, files[i]); |
| fclose(files[i]); |
| |
| ret = fwp_write(buf, r_size, buf, (void *)g_key); |
| if(ret) |
| return ret; |
| MD5_Update(&c, buf, r_size); |
| fwrite(buf, 1, r_size, fout); |
| |
| free(buf); |
| } |
| |
| fseek(fout, 0, SEEK_SET); |
| MD5_Final(md5.md5, &c); |
| fwrite(&md5, 1, sizeof(md5), fout); |
| fclose(fout); |
| |
| return 0; |
| } |
| |
| static void usage(void) |
| { |
| color(OFF); |
| printf("Usage: upgtool [options] firmware [files...]\n"); |
| printf("Options:\n"); |
| printf(" -o <prefix>\t\tSet output prefix\n"); |
| printf(" -f/--force\t\tForce to continue on errors\n"); |
| printf(" -?/--help\t\tDisplay this message\n"); |
| printf(" -d/--debug\t\tDisplay debug messages\n"); |
| printf(" -c/--no-color\t\tDisable color output\n"); |
| printf(" -m/--model <model>\tSelect model (or ? to list them)\n"); |
| printf(" -l/--search <method>\tTry to find the keysig (implies -e)\n"); |
| printf(" -a/--kas <kas>\tForce KAS\n"); |
| printf(" -k/--key <key>\tForce key\n"); |
| printf(" -s/--sig <sig>\tForce sig\n"); |
| printf(" -e/--extract\t\tExtract a UPG archive\n"); |
| printf(" -c/--create\t\tCreate a UPG archive\n"); |
| printf("keysig search method:\n"); |
| for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++) |
| printf(" %s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment); |
| exit(1); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| bool extract = false; |
| bool create = false; |
| |
| if(argc <= 1) |
| usage(); |
| |
| while(1) |
| { |
| static struct option long_options[] = |
| { |
| {"help", no_argument, 0, '?'}, |
| {"debug", no_argument, 0, 'd'}, |
| {"no-color", no_argument, 0, 'n'}, |
| {"force", no_argument, 0, 'f'}, |
| {"model", required_argument, 0, 'm'}, |
| {"search", required_argument, 0, 'l'}, |
| {"kas", required_argument, 0, 'a'}, |
| {"key", required_argument, 0, 'k'}, |
| {"sig", required_argument, 0, 's'}, |
| {"extract", no_argument, 0, 'e'}, |
| {"create", no_argument, 0 ,'c'}, |
| {0, 0, 0, 0} |
| }; |
| |
| int c = getopt_long(argc, argv, "?dnfo:m:l:a:k:s:ec", long_options, NULL); |
| if(c == -1) |
| break; |
| switch(c) |
| { |
| case -1: |
| break; |
| case 'n': |
| enable_color(false); |
| break; |
| case 'd': |
| g_debug = true; |
| break; |
| case 'f': |
| g_force = true; |
| break; |
| case '?': |
| usage(); |
| break; |
| case 'o': |
| g_out_prefix = optarg; |
| break; |
| case 'm': |
| g_model = optarg; |
| break; |
| case 'l': |
| g_keysig_search = KEYSIG_SEARCH_NONE; |
| for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++) |
| if(strcmp(keysig_search_desc[i].name, optarg) == 0) |
| g_keysig_search = i; |
| if(g_keysig_search == KEYSIG_SEARCH_NONE) |
| { |
| cprintf(GREY, "Unknown keysig search method '%s'\n", optarg); |
| return 1; |
| } |
| extract = true; |
| break; |
| case 'a': |
| g_kas = optarg; |
| break; |
| case 'k': |
| g_key = optarg; |
| break; |
| case 's': |
| g_sig = optarg; |
| break; |
| case 'e': |
| extract = true; |
| break; |
| case 'c': |
| create = true; |
| break; |
| default: |
| abort(); |
| } |
| } |
| |
| if(g_model && strcmp(g_model, "?") == 0) |
| { |
| cprintf(BLUE, "Model list:\n"); |
| for(unsigned i = 0; i < sizeof(g_model_list) / sizeof(g_model_list[0]); i++) |
| { |
| cprintf(GREEN, " %s:", g_model_list[i].model); |
| if(g_model_list[i].flags & HAS_KAS) |
| { |
| cprintf(RED, " kas="); |
| cprintf(YELLOW, "%."STR(NWZ_KAS_SIZE)"s", g_model_list[i].kas.kas); |
| } |
| if(g_model_list[i].flags & HAS_KEY) |
| { |
| cprintf(RED, " key="); |
| cprintf(YELLOW, "%.8s", g_model_list[i].key); |
| } |
| if(g_model_list[i].flags & HAS_SIG) |
| { |
| cprintf(RED, " sig="); |
| cprintf(YELLOW, "%.8s", g_model_list[i].sig); |
| } |
| if(g_model_list[i].flags & CONFIRMED) |
| cprintf(RED, " confirmed"); |
| else |
| cprintf(RED, " guessed"); |
| printf("\n"); |
| } |
| return 1; |
| } |
| |
| if(g_model) |
| { |
| for(unsigned i = 0; i < sizeof(g_model_list) / sizeof(g_model_list[0]); i++) |
| if(strcmp(g_model, g_model_list[i].model) == 0) |
| g_model_index = i; |
| if(g_model_index == -1) |
| cprintf(GREY, "Warning: unknown model %s\n", g_model); |
| } |
| |
| if(!create && !extract) |
| { |
| printf("You must specify an action (extract or create)\n"); |
| return 1; |
| } |
| |
| if(create && extract) |
| { |
| printf("You cannot specify both create and extract\n"); |
| return 1; |
| } |
| |
| int ret = 0; |
| if(create) |
| ret = create_upg(argc - optind, argv + optind); |
| else if(extract) |
| ret = extract_upg(argc - optind, argv + optind); |
| else |
| { |
| printf("Die from lack of action\n"); |
| ret = 1; |
| } |
| |
| color(OFF); |
| |
| return ret; |
| } |
| |