diff options
Diffstat (limited to 'utils/sbtools/sbtoelf.c')
-rw-r--r-- | utils/sbtools/sbtoelf.c | 274 |
1 files changed, 145 insertions, 129 deletions
diff --git a/utils/sbtools/sbtoelf.c b/utils/sbtools/sbtoelf.c index fb4567bed9..3c1c750582 100644 --- a/utils/sbtools/sbtoelf.c +++ b/utils/sbtools/sbtoelf.c | |||
@@ -28,21 +28,19 @@ | |||
28 | 28 | ||
29 | #define _ISOC99_SOURCE /* snprintf() */ | 29 | #define _ISOC99_SOURCE /* snprintf() */ |
30 | #include <stdio.h> | 30 | #include <stdio.h> |
31 | #include <sys/types.h> | ||
32 | #include <sys/stat.h> | ||
33 | #include <fcntl.h> | ||
34 | #include <errno.h> | 31 | #include <errno.h> |
35 | #include <unistd.h> | ||
36 | #include <stdlib.h> | 32 | #include <stdlib.h> |
37 | #include <inttypes.h> | ||
38 | #include <string.h> | 33 | #include <string.h> |
39 | #include <ctype.h> | 34 | #include <ctype.h> |
40 | #include <time.h> | 35 | #include <time.h> |
36 | #include <stdarg.h> | ||
41 | #include <strings.h> | 37 | #include <strings.h> |
38 | #include <getopt.h> | ||
42 | 39 | ||
43 | #include "crypto.h" | 40 | #include "crypto.h" |
44 | #include "elf.h" | 41 | #include "elf.h" |
45 | #include "sb.h" | 42 | #include "sb.h" |
43 | #include "misc.h" | ||
46 | 44 | ||
47 | #if 1 /* ANSI colors */ | 45 | #if 1 /* ANSI colors */ |
48 | 46 | ||
@@ -60,104 +58,24 @@ char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; | |||
60 | # define color(a) | 58 | # define color(a) |
61 | #endif | 59 | #endif |
62 | 60 | ||
63 | #define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0) | ||
64 | #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0) | ||
65 | |||
66 | /* all blocks are sized as a multiple of 0x1ff */ | 61 | /* all blocks are sized as a multiple of 0x1ff */ |
67 | #define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff) | 62 | #define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff) |
68 | 63 | ||
69 | /* If you find a firmware that breaks the known format ^^ */ | 64 | /* If you find a firmware that breaks the known format ^^ */ |
70 | #define assert(a) do { if(!(a)) { fprintf(stderr,"Assertion \"%s\" failed in %s() line %d!\n\nPlease send us your firmware!\n",#a,__func__,__LINE__); exit(1); } } while(0) | 65 | #define assert(a) do { if(!(a)) { fprintf(stderr,"Assertion \"%s\" failed in %s() line %d!\n\nPlease send us your firmware!\n",#a,__func__,__LINE__); exit(1); } } while(0) |
71 | 66 | ||
67 | #define crypto_cbc(...) \ | ||
68 | do { int ret = crypto_cbc(__VA_ARGS__); \ | ||
69 | if(ret != CRYPTO_ERROR_SUCCESS) \ | ||
70 | bug("crypto_cbc error: %d\n", ret); \ | ||
71 | }while(0) | ||
72 | |||
72 | /* globals */ | 73 | /* globals */ |
73 | 74 | ||
74 | uint8_t *g_buf; /* file content */ | 75 | uint8_t *g_buf; /* file content */ |
75 | #define PREFIX_SIZE 128 | 76 | char *g_out_prefix; |
76 | char out_prefix[PREFIX_SIZE]; | 77 | bool g_debug; |
77 | const char *key_file; | 78 | bool g_raw_mode; |
78 | |||
79 | char *s_getenv(const char *name) | ||
80 | { | ||
81 | char *s = getenv(name); | ||
82 | return s ? s : ""; | ||
83 | } | ||
84 | |||
85 | void *xmalloc(size_t s) /* malloc helper, used in elf.c */ | ||
86 | { | ||
87 | void * r = malloc(s); | ||
88 | if(!r) bugp("malloc"); | ||
89 | return r; | ||
90 | } | ||
91 | |||
92 | static void print_hex(byte *data, int len, bool newline) | ||
93 | { | ||
94 | for(int i = 0; i < len; i++) | ||
95 | printf("%02X ", data[i]); | ||
96 | if(newline) | ||
97 | printf("\n"); | ||
98 | } | ||
99 | |||
100 | static int convxdigit(char digit, byte *val) | ||
101 | { | ||
102 | if(digit >= '0' && digit <= '9') | ||
103 | { | ||
104 | *val = digit - '0'; | ||
105 | return 0; | ||
106 | } | ||
107 | else if(digit >= 'A' && digit <= 'F') | ||
108 | { | ||
109 | *val = digit - 'A' + 10; | ||
110 | return 0; | ||
111 | } | ||
112 | else if(digit >= 'a' && digit <= 'f') | ||
113 | { | ||
114 | *val = digit - 'a' + 10; | ||
115 | return 0; | ||
116 | } | ||
117 | else | ||
118 | return 1; | ||
119 | } | ||
120 | |||
121 | typedef byte (*key_array_t)[16]; | ||
122 | |||
123 | static key_array_t read_keys(int num_keys) | ||
124 | { | ||
125 | int size; | ||
126 | struct stat st; | ||
127 | int fd = open(key_file,O_RDONLY); | ||
128 | if(fd == -1) | ||
129 | bugp("opening key file failed"); | ||
130 | if(fstat(fd,&st) == -1) | ||
131 | bugp("key file stat() failed"); | ||
132 | size = st.st_size; | ||
133 | char *buf = xmalloc(size); | ||
134 | if(read(fd, buf, size) != (ssize_t)size) | ||
135 | bugp("reading key file"); | ||
136 | close(fd); | ||
137 | |||
138 | key_array_t keys = xmalloc(sizeof(byte[16]) * num_keys); | ||
139 | int pos = 0; | ||
140 | for(int i = 0; i < num_keys; i++) | ||
141 | { | ||
142 | /* skip ws */ | ||
143 | while(pos < size && isspace(buf[pos])) | ||
144 | pos++; | ||
145 | /* enough space ? */ | ||
146 | if((pos + 32) > size) | ||
147 | bugp("invalid key file (not enough keys)"); | ||
148 | for(int j = 0; j < 16; j++) | ||
149 | { | ||
150 | byte a, b; | ||
151 | if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b)) | ||
152 | bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n"); | ||
153 | keys[i][j] = (a << 4) | b; | ||
154 | } | ||
155 | pos += 32; | ||
156 | } | ||
157 | free(buf); | ||
158 | |||
159 | return keys; | ||
160 | } | ||
161 | 79 | ||
162 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) | 80 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) |
163 | 81 | ||
@@ -195,17 +113,21 @@ static void extract_elf_section(struct elf_params_t *elf, int count, const char | |||
195 | 113 | ||
196 | static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent) | 114 | static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent) |
197 | { | 115 | { |
198 | char filename[PREFIX_SIZE + 32]; | 116 | char *filename = xmalloc(strlen(g_out_prefix) + strlen(name) + 5); |
199 | snprintf(filename, sizeof filename, "%s%s.bin", out_prefix, name); | 117 | if(g_out_prefix) |
200 | FILE *fd = fopen(filename, "wb"); | 118 | { |
201 | if (fd != NULL) { | 119 | sprintf(filename, "%s%s.bin", g_out_prefix, name); |
202 | fwrite(buf, size, 1, fd); | 120 | FILE *fd = fopen(filename, "wb"); |
203 | fclose(fd); | 121 | if (fd != NULL) |
122 | { | ||
123 | fwrite(buf, size, 1, fd); | ||
124 | fclose(fd); | ||
125 | } | ||
204 | } | 126 | } |
205 | if(data_sec) | 127 | if(data_sec) |
206 | return; | 128 | return; |
207 | 129 | ||
208 | snprintf(filename, sizeof filename, "%s%s", out_prefix, name); | 130 | sprintf(filename, "%s%s", g_out_prefix, name); |
209 | 131 | ||
210 | /* elf construction */ | 132 | /* elf construction */ |
211 | struct elf_params_t elf; | 133 | struct elf_params_t elf; |
@@ -484,19 +406,23 @@ static void extract(unsigned long filesize) | |||
484 | printf("0x%08x\n", sb_header->first_boot_sec_id); | 406 | printf("0x%08x\n", sb_header->first_boot_sec_id); |
485 | 407 | ||
486 | /* encryption cbc-mac */ | 408 | /* encryption cbc-mac */ |
487 | key_array_t keys = NULL; /* array of 16-bytes keys */ | ||
488 | byte real_key[16]; | 409 | byte real_key[16]; |
489 | bool valid_key = false; /* false until a matching key was found */ | 410 | bool valid_key = false; /* false until a matching key was found */ |
490 | if(sb_header->nr_keys > 0) | 411 | if(sb_header->nr_keys > 0) |
491 | { | 412 | { |
492 | keys = read_keys(sb_header->nr_keys); | 413 | if(sb_header->nr_keys > g_nr_keys) |
414 | { | ||
415 | color(GREY); | ||
416 | bug("SB file has %d keys but only %d were specified on command line\n", | ||
417 | sb_header->nr_keys, g_nr_keys); | ||
418 | } | ||
493 | color(BLUE); | 419 | color(BLUE); |
494 | printf("Encryption data\n"); | 420 | printf("Encryption data\n"); |
495 | for(int i = 0; i < sb_header->nr_keys; i++) | 421 | for(int i = 0; i < sb_header->nr_keys; i++) |
496 | { | 422 | { |
497 | color(RED); | 423 | color(RED); |
498 | printf(" Key %d: ", i); | 424 | printf(" Key %d: ", i); |
499 | print_hex(keys[i], 16, true); | 425 | print_key(&g_key_array[i], true); |
500 | color(GREEN); | 426 | color(GREEN); |
501 | printf(" CBC-MAC of headers: "); | 427 | printf(" CBC-MAC of headers: "); |
502 | 428 | ||
@@ -512,8 +438,8 @@ static void extract(unsigned long filesize) | |||
512 | byte computed_cbc_mac[16]; | 438 | byte computed_cbc_mac[16]; |
513 | byte zero[16]; | 439 | byte zero[16]; |
514 | memset(zero, 0, 16); | 440 | memset(zero, 0, 16); |
515 | cbc_mac(g_buf, NULL, sb_header->header_size + sb_header->nr_sections, | 441 | crypto_cbc(g_buf, NULL, sb_header->header_size + sb_header->nr_sections, |
516 | keys[i], zero, &computed_cbc_mac, 1); | 442 | &g_key_array[i], zero, &computed_cbc_mac, 1); |
517 | color(RED); | 443 | color(RED); |
518 | bool ok = memcmp(dict_entry->hdr_cbc_mac, computed_cbc_mac, 16) == 0; | 444 | bool ok = memcmp(dict_entry->hdr_cbc_mac, computed_cbc_mac, 16) == 0; |
519 | if(ok) | 445 | if(ok) |
@@ -533,7 +459,7 @@ static void extract(unsigned long filesize) | |||
533 | byte decrypted_key[16]; | 459 | byte decrypted_key[16]; |
534 | byte iv[16]; | 460 | byte iv[16]; |
535 | memcpy(iv, g_buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */ | 461 | memcpy(iv, g_buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */ |
536 | cbc_mac(dict_entry->key, decrypted_key, 1, keys[i], iv, NULL, 0); | 462 | crypto_cbc(dict_entry->key, decrypted_key, 1, &g_key_array[i], iv, NULL, 0); |
537 | printf(" Decrypted key : "); | 463 | printf(" Decrypted key : "); |
538 | color(YELLOW); | 464 | color(YELLOW); |
539 | print_hex(decrypted_key, 16, false); | 465 | print_hex(decrypted_key, 16, false); |
@@ -769,37 +695,127 @@ static void extract(unsigned long filesize) | |||
769 | printf(" Failed\n"); | 695 | printf(" Failed\n"); |
770 | } | 696 | } |
771 | 697 | ||
772 | int main(int argc, const char **argv) | 698 | void usage(void) |
773 | { | 699 | { |
774 | int fd; | 700 | printf("Usage: sbtoelf [options] sb-file\n"); |
775 | struct stat st; | 701 | printf("Options:\n"); |
776 | if(argc != 3 && argc != 4) | 702 | printf(" -?/--help\tDisplay this message\n"); |
777 | { | 703 | printf(" -o <file>\tSet output prefix\n"); |
778 | printf("Usage: %s <firmware> <key file> [<out prefix>]\n",*argv); | 704 | printf(" -d/--debug\tEnable debug output\n"); |
779 | printf("To use raw command mode, set environment variable SB_RAW_CMD to YES\n"); | 705 | printf(" -k <file>\tAdd key file\n"); |
780 | return 1; | 706 | printf(" -z\t\tAdd zero key\n"); |
781 | } | 707 | printf(" -r\t\tUse raw command mode\n"); |
708 | printf(" --single-key <key>\tAdd single key\n"); | ||
709 | printf(" --usb-otp <vid>:<pid>\tAdd USB OTP device\n"); | ||
710 | exit(1); | ||
711 | } | ||
782 | 712 | ||
783 | if(argc == 4) | 713 | static struct crypto_key_t g_zero_key = |
784 | snprintf(out_prefix, PREFIX_SIZE, "%s", argv[3]); | 714 | { |
785 | else | 715 | .method = CRYPTO_KEY, |
786 | strcpy(out_prefix, ""); | 716 | .u.key = {0} |
717 | }; | ||
787 | 718 | ||
788 | if( (fd = open(argv[1], O_RDONLY)) == -1 ) | 719 | int main(int argc, char **argv) |
789 | bugp("opening firmware failed"); | 720 | { |
721 | while(1) | ||
722 | { | ||
723 | static struct option long_options[] = | ||
724 | { | ||
725 | {"help", no_argument, 0, '?'}, | ||
726 | {"debug", no_argument, 0, 'd'}, | ||
727 | {"single-key", required_argument, 0, 's'}, | ||
728 | {"usb-otp", required_argument, 0, 'u'}, | ||
729 | {0, 0, 0, 0} | ||
730 | }; | ||
731 | |||
732 | int c = getopt_long(argc, argv, "?do:k:zr", long_options, NULL); | ||
733 | if(c == -1) | ||
734 | break; | ||
735 | switch(c) | ||
736 | { | ||
737 | case -1: | ||
738 | break; | ||
739 | case 'd': | ||
740 | g_debug = true; | ||
741 | break; | ||
742 | case '?': | ||
743 | usage(); | ||
744 | break; | ||
745 | case 'o': | ||
746 | g_out_prefix = optarg; | ||
747 | break; | ||
748 | case 'k': | ||
749 | { | ||
750 | int kac; | ||
751 | key_array_t ka = read_keys(optarg, &kac); | ||
752 | add_keys(ka, kac); | ||
753 | break; | ||
754 | } | ||
755 | case 'z': | ||
756 | { | ||
757 | add_keys(&g_zero_key, 1); | ||
758 | break; | ||
759 | } | ||
760 | case 's': | ||
761 | { | ||
762 | struct crypto_key_t key; | ||
763 | key.method = CRYPTO_KEY; | ||
764 | if(strlen(optarg) != 32) | ||
765 | bug("The key given in argument is invalid"); | ||
766 | for(int i = 0; i < 16; i++) | ||
767 | { | ||
768 | byte a, b; | ||
769 | if(convxdigit(optarg[2 * i], &a) || convxdigit(optarg[2 * i + 1], &b)) | ||
770 | bugp("The key given in argument is invalid\n"); | ||
771 | key.u.key[i] = (a << 4) | b; | ||
772 | } | ||
773 | add_keys(&key, 1); | ||
774 | break; | ||
775 | } | ||
776 | case 'u': | ||
777 | { | ||
778 | int vid, pid; | ||
779 | char *p = strchr(optarg, ':'); | ||
780 | if(p == NULL) | ||
781 | bug("Invalid VID/PID\n"); | ||
782 | |||
783 | char *end; | ||
784 | vid = strtol(optarg, &end, 16); | ||
785 | if(end != p) | ||
786 | bug("Invalid VID/PID\n"); | ||
787 | pid = strtol(p + 1, &end, 16); | ||
788 | if(end != (optarg + strlen(optarg))) | ||
789 | bug("Invalid VID/PID\n"); | ||
790 | struct crypto_key_t key; | ||
791 | key.method = CRYPTO_USBOTP; | ||
792 | key.u.vid_pid = vid << 16 | pid; | ||
793 | add_keys(&key, 1); | ||
794 | break; | ||
795 | } | ||
796 | default: | ||
797 | abort(); | ||
798 | } | ||
799 | } | ||
790 | 800 | ||
791 | key_file = argv[2]; | 801 | if(argc - optind != 1) |
802 | bug("Missing sb file or too many files after options\n"); | ||
792 | 803 | ||
793 | if(fstat(fd, &st) == -1) | 804 | const char *sb_file = argv[optind]; |
794 | bugp("firmware stat() failed"); | 805 | FILE *fd = fopen(sb_file, "rb"); |
806 | if(fd == NULL) | ||
807 | bug("Cannot open input file\n"); | ||
808 | fseek(fd, 0, SEEK_END); | ||
809 | size_t size = ftell(fd); | ||
810 | fseek(fd, 0, SEEK_SET); | ||
795 | 811 | ||
796 | g_buf = xmalloc(st.st_size); | 812 | g_buf = xmalloc(size); |
797 | if(read(fd, g_buf, st.st_size) != (ssize_t)st.st_size) /* load the whole file into memory */ | 813 | if(fread(g_buf, 1, size, fd) != size) /* load the whole file into memory */ |
798 | bugp("reading firmware"); | 814 | bugp("reading firmware"); |
799 | 815 | ||
800 | close(fd); | 816 | fclose(fd); |
801 | 817 | ||
802 | extract(st.st_size); | 818 | extract(size); |
803 | 819 | ||
804 | color(OFF); | 820 | color(OFF); |
805 | 821 | ||