diff options
-rw-r--r-- | utils/sbtools/Makefile | 22 | ||||
-rw-r--r-- | utils/sbtools/crypto.c | 188 | ||||
-rw-r--r-- | utils/sbtools/crypto.h | 56 | ||||
-rw-r--r-- | utils/sbtools/elftosb.c | 270 | ||||
-rw-r--r-- | utils/sbtools/misc.c | 174 | ||||
-rw-r--r-- | utils/sbtools/misc.h | 48 | ||||
-rw-r--r-- | utils/sbtools/sbtoelf.c | 274 |
7 files changed, 720 insertions, 312 deletions
diff --git a/utils/sbtools/Makefile b/utils/sbtools/Makefile index dc9c0966a7..15d3adb8a1 100644 --- a/utils/sbtools/Makefile +++ b/utils/sbtools/Makefile | |||
@@ -1,10 +1,22 @@ | |||
1 | DEFINES=-DCRYPTO_LIBUSB | ||
2 | CC=gcc | ||
3 | LD=gcc | ||
4 | CFLAGS=-g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) | ||
5 | LDFLAGS=`pkg-config --libs libusb-1.0` | ||
6 | |||
1 | all: elftosb sbtoelf | 7 | all: elftosb sbtoelf |
2 | 8 | ||
3 | sbtoelf: sbtoelf.c crc.c crypto.h aes128.c sha1.c elf.c sb.h | 9 | %.o: %.c |
4 | gcc -g -std=c99 -o $@ -W -Wall $^ | 10 | $(CC) $(CFLAGS) -c -o $@ $< |
11 | |||
12 | sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o elf.o misc.o | ||
13 | $(LD) $(LDFLAGS) -o $@ $^ | ||
5 | 14 | ||
6 | elftosb: elftosb.c crc.c crypto.h aes128.c sha1.c elf.c sb.h dbparser.h dbparser.c | 15 | elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o |
7 | gcc -g -std=c99 -o $@ -W -Wall $^ | 16 | $(LD) $(LDFLAGS) -o $@ $^ |
8 | 17 | ||
9 | clean: | 18 | clean: |
10 | rm -fr elftosb sbtoelf | 19 | rm -fr *.o |
20 | |||
21 | veryclean: | ||
22 | rm -rf sbtoelf elftosb | ||
diff --git a/utils/sbtools/crypto.c b/utils/sbtools/crypto.c new file mode 100644 index 0000000000..d4afc6c816 --- /dev/null +++ b/utils/sbtools/crypto.c | |||
@@ -0,0 +1,188 @@ | |||
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 "crypto.h" | ||
22 | #include <stdio.h> | ||
23 | #include <stdbool.h> | ||
24 | #ifdef CRYPTO_LIBUSB | ||
25 | #include "libusb.h" | ||
26 | #endif | ||
27 | #include "misc.h" | ||
28 | |||
29 | static enum crypto_method_t cur_method = CRYPTO_NONE; | ||
30 | static byte key[16]; | ||
31 | static uint16_t usb_vid, usb_pid; | ||
32 | |||
33 | void crypto_setup(enum crypto_method_t method, void *param) | ||
34 | { | ||
35 | cur_method = method; | ||
36 | switch(method) | ||
37 | { | ||
38 | case CRYPTO_KEY: | ||
39 | memcpy(key, param, sizeof(key)); | ||
40 | break; | ||
41 | case CRYPTO_USBOTP: | ||
42 | { | ||
43 | uint32_t value = *(uint32_t *)param; | ||
44 | usb_vid = value >> 16; | ||
45 | usb_pid = value & 0xffff; | ||
46 | break; | ||
47 | } | ||
48 | default: | ||
49 | break; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | int crypto_apply( | ||
54 | byte *in_data, /* Input data */ | ||
55 | byte *out_data, /* Output data (or NULL) */ | ||
56 | int nr_blocks, /* Number of blocks (one block=16 bytes) */ | ||
57 | byte iv[16], /* Key */ | ||
58 | byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ | ||
59 | int encrypt) | ||
60 | { | ||
61 | if(cur_method == CRYPTO_KEY) | ||
62 | { | ||
63 | cbc_mac(in_data, out_data, nr_blocks, key, iv, out_cbc_mac, encrypt); | ||
64 | return CRYPTO_ERROR_SUCCESS; | ||
65 | } | ||
66 | #ifdef CRYPTO_LIBUSB | ||
67 | else if(cur_method == CRYPTO_USBOTP) | ||
68 | { | ||
69 | if(out_cbc_mac && !encrypt) | ||
70 | memcpy(*out_cbc_mac, in_data + 16 * (nr_blocks - 1), 16); | ||
71 | |||
72 | libusb_device_handle *handle = NULL; | ||
73 | libusb_context *ctx; | ||
74 | /* init library */ | ||
75 | libusb_init(&ctx); | ||
76 | libusb_set_debug(NULL,3); | ||
77 | /* open device */ | ||
78 | handle = libusb_open_device_with_vid_pid(ctx, usb_vid, usb_pid); | ||
79 | if(handle == NULL) | ||
80 | { | ||
81 | printf("usbotp: cannot open device %04x:%04x\n", usb_vid, usb_pid); | ||
82 | return CRYPTO_ERROR_NODEVICE; | ||
83 | } | ||
84 | /* get device pointer */ | ||
85 | libusb_device *mydev = libusb_get_device(handle); | ||
86 | if(g_debug) | ||
87 | printf("usbotp: device found at %d:%d\n", libusb_get_bus_number(mydev), | ||
88 | libusb_get_device_address(mydev)); | ||
89 | int config_id; | ||
90 | /* explore configuration */ | ||
91 | libusb_get_configuration(handle, &config_id); | ||
92 | struct libusb_config_descriptor *config; | ||
93 | libusb_get_active_config_descriptor(mydev, &config); | ||
94 | |||
95 | if(g_debug) | ||
96 | { | ||
97 | printf("usbotp: configuration: %d\n", config_id); | ||
98 | printf("usbotp: interfaces: %d\n", config->bNumInterfaces); | ||
99 | } | ||
100 | |||
101 | const struct libusb_endpoint_descriptor *endp = NULL; | ||
102 | int intf, intf_alt; | ||
103 | for(intf = 0; intf < config->bNumInterfaces; intf++) | ||
104 | for(intf_alt = 0; intf_alt < config->interface[intf].num_altsetting; intf_alt++) | ||
105 | for(int ep = 0; ep < config->interface[intf].altsetting[intf_alt].bNumEndpoints; ep++) | ||
106 | { | ||
107 | endp = &config->interface[intf].altsetting[intf_alt].endpoint[ep]; | ||
108 | if((endp->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_INTERRUPT && | ||
109 | (endp->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) | ||
110 | goto Lfound; | ||
111 | } | ||
112 | libusb_close(handle); | ||
113 | printf("usbotp: No suitable endpoint found\n"); | ||
114 | return CRYPTO_ERROR_BADENDP; | ||
115 | |||
116 | if(g_debug) | ||
117 | { | ||
118 | printf("usbotp: use interface %d, alt %d\n", intf, intf_alt); | ||
119 | printf("usbotp: use endpoint %d\n", endp->bEndpointAddress); | ||
120 | } | ||
121 | Lfound: | ||
122 | if(libusb_claim_interface(handle, intf) != 0) | ||
123 | { | ||
124 | if(g_debug) | ||
125 | printf("usbotp: claim error\n"); | ||
126 | return CRYPTO_ERROR_CLAIMFAIL; | ||
127 | } | ||
128 | |||
129 | int buffer_size = 16 + 16 * nr_blocks; | ||
130 | unsigned char *buffer = xmalloc(buffer_size); | ||
131 | memcpy(buffer, iv, 16); | ||
132 | memcpy(buffer + 16, in_data, 16 * nr_blocks); | ||
133 | int ret = libusb_control_transfer(handle, | ||
134 | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, | ||
135 | 0xaa, encrypt ? 0xeeee : 0xdddd, 0, buffer, buffer_size, 1000); | ||
136 | if(ret < 0) | ||
137 | { | ||
138 | if(g_debug) | ||
139 | printf("usbotp: control transfer failed: %d\n", ret); | ||
140 | libusb_release_interface(handle, intf); | ||
141 | libusb_close(handle); | ||
142 | return CRYPTO_ERROR_DEVREJECT; | ||
143 | } | ||
144 | |||
145 | int recv_size; | ||
146 | ret = libusb_interrupt_transfer(handle, endp->bEndpointAddress, buffer, | ||
147 | buffer_size, &recv_size, 1000); | ||
148 | libusb_release_interface(handle, intf); | ||
149 | libusb_close(handle); | ||
150 | |||
151 | if(ret < 0) | ||
152 | { | ||
153 | if(g_debug) | ||
154 | printf("usbotp: interrupt transfer failed: %d\n", ret); | ||
155 | return CRYPTO_ERROR_DEVSILENT; | ||
156 | } | ||
157 | if(recv_size != buffer_size) | ||
158 | { | ||
159 | if(g_debug) | ||
160 | printf("usbotp: device returned %d bytes, expected %d\n", recv_size, | ||
161 | buffer_size); | ||
162 | return CRYPTO_ERROR_DEVERR; | ||
163 | } | ||
164 | |||
165 | if(out_data) | ||
166 | memcpy(out_data, buffer + 16, 16 * nr_blocks); | ||
167 | if(out_cbc_mac && encrypt) | ||
168 | memcpy(*out_cbc_mac, buffer + buffer_size - 16, 16); | ||
169 | |||
170 | return CRYPTO_ERROR_SUCCESS; | ||
171 | } | ||
172 | #endif | ||
173 | else | ||
174 | return CRYPTO_ERROR_BADSETUP; | ||
175 | } | ||
176 | |||
177 | int crypto_cbc( | ||
178 | byte *in_data, /* Input data */ | ||
179 | byte *out_data, /* Output data (or NULL) */ | ||
180 | int nr_blocks, /* Number of blocks (one block=16 bytes) */ | ||
181 | struct crypto_key_t *key, /* Key */ | ||
182 | byte iv[16], /* IV */ | ||
183 | byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ | ||
184 | int encrypt) | ||
185 | { | ||
186 | crypto_setup(key->method, (void *)key->u.param); | ||
187 | return crypto_apply(in_data, out_data, nr_blocks, iv, out_cbc_mac, encrypt); | ||
188 | } | ||
diff --git a/utils/sbtools/crypto.h b/utils/sbtools/crypto.h index 0662ef8587..51f44406db 100644 --- a/utils/sbtools/crypto.h +++ b/utils/sbtools/crypto.h | |||
@@ -18,6 +18,9 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #ifndef __CRYPTO_H__ | ||
22 | #define __CRYPTO_H__ | ||
23 | |||
21 | #include <stdio.h> | 24 | #include <stdio.h> |
22 | #include <stdint.h> | 25 | #include <stdint.h> |
23 | #include <string.h> | 26 | #include <string.h> |
@@ -39,6 +42,57 @@ void cbc_mac( | |||
39 | int encrypt /* 1 to encrypt, 0 to decrypt */ | 42 | int encrypt /* 1 to encrypt, 0 to decrypt */ |
40 | ); | 43 | ); |
41 | 44 | ||
45 | /* crypto.c */ | ||
46 | enum crypto_method_t | ||
47 | { | ||
48 | CRYPTO_NONE, /* disable */ | ||
49 | CRYPTO_KEY, /* key */ | ||
50 | CRYPTO_USBOTP, /* use usbotp device */ | ||
51 | }; | ||
52 | |||
53 | /* parameter can be: | ||
54 | * - CRYPTO_KEY: array of 16-bytes (the key) | ||
55 | * - CRYPTO_USBOTP: 32-bit integer: vid << 16 | pid */ | ||
56 | void crypto_setup(enum crypto_method_t method, void *param); | ||
57 | |||
58 | #define CRYPTO_ERROR_SUCCESS 0 | ||
59 | #define CRYPTO_ERROR_BADSETUP -1 /* bad crypto setup */ | ||
60 | #define CRYPTO_ERROR_NODEVICE -2 /* no device with vid:pid */ | ||
61 | #define CRYPTO_ERROR_BADENDP -3 /* device doesn't have the required endpoints */ | ||
62 | #define CRYPTO_ERROR_CLAIMFAIL -4 /* device interface claim error */ | ||
63 | #define CRYPTO_ERROR_DEVREJECT -5 /* device rejected cypto operation */ | ||
64 | #define CRYPTO_ERROR_DEVSILENT -6 /* device did not notify completion */ | ||
65 | #define CRYPTO_ERROR_DEVERR -7 /* device did something wrong (like return too small buffer) */ | ||
66 | /* return 0 on success, <0 on error */ | ||
67 | int crypto_apply( | ||
68 | byte *in_data, /* Input data */ | ||
69 | byte *out_data, /* Output data (or NULL) */ | ||
70 | int nr_blocks, /* Number of blocks (one block=16 bytes) */ | ||
71 | byte iv[16], /* IV */ | ||
72 | byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ | ||
73 | int encrypt); | ||
74 | |||
75 | /* all-in-one function */ | ||
76 | struct crypto_key_t | ||
77 | { | ||
78 | enum crypto_method_t method; | ||
79 | union | ||
80 | { | ||
81 | byte key[16]; | ||
82 | uint32_t vid_pid; | ||
83 | byte param[0]; | ||
84 | }u; | ||
85 | }; | ||
86 | |||
87 | int crypto_cbc( | ||
88 | byte *in_data, /* Input data */ | ||
89 | byte *out_data, /* Output data (or NULL) */ | ||
90 | int nr_blocks, /* Number of blocks (one block=16 bytes) */ | ||
91 | struct crypto_key_t *key, /* Key */ | ||
92 | byte iv[16], /* IV */ | ||
93 | byte (*out_cbc_mac)[16], /* CBC-MAC of the result (or NULL) */ | ||
94 | int encrypt); | ||
95 | |||
42 | /* crc.c */ | 96 | /* crc.c */ |
43 | uint32_t crc(byte *data, int size); | 97 | uint32_t crc(byte *data, int size); |
44 | uint32_t crc_continue(uint32_t previous_crc, byte *data, int size); | 98 | uint32_t crc_continue(uint32_t previous_crc, byte *data, int size); |
@@ -56,3 +110,5 @@ void sha_1_block(struct sha_1_params_t *params, uint32_t cur_hash[5], byte *data | |||
56 | void sha_1_update(struct sha_1_params_t *params, byte *buffer, int size); | 110 | void sha_1_update(struct sha_1_params_t *params, byte *buffer, int size); |
57 | void sha_1_finish(struct sha_1_params_t *params); | 111 | void sha_1_finish(struct sha_1_params_t *params); |
58 | void sha_1_output(struct sha_1_params_t *params, byte *out); | 112 | void sha_1_output(struct sha_1_params_t *params, byte *out); |
113 | |||
114 | #endif /* __CRYPTO_H__ */ | ||
diff --git a/utils/sbtools/elftosb.c b/utils/sbtools/elftosb.c index 17b72417cf..5e65ba3261 100644 --- a/utils/sbtools/elftosb.c +++ b/utils/sbtools/elftosb.c | |||
@@ -21,13 +21,8 @@ | |||
21 | 21 | ||
22 | #define _ISOC99_SOURCE | 22 | #define _ISOC99_SOURCE |
23 | #include <stdio.h> | 23 | #include <stdio.h> |
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <errno.h> | 24 | #include <errno.h> |
28 | #include <unistd.h> | ||
29 | #include <stdlib.h> | 25 | #include <stdlib.h> |
30 | #include <inttypes.h> | ||
31 | #include <string.h> | 26 | #include <string.h> |
32 | #include <ctype.h> | 27 | #include <ctype.h> |
33 | #include <time.h> | 28 | #include <time.h> |
@@ -39,143 +34,18 @@ | |||
39 | #include "elf.h" | 34 | #include "elf.h" |
40 | #include "sb.h" | 35 | #include "sb.h" |
41 | #include "dbparser.h" | 36 | #include "dbparser.h" |
37 | #include "misc.h" | ||
42 | 38 | ||
43 | #define _STR(a) #a | ||
44 | #define STR(a) _STR(a) | ||
45 | |||
46 | #define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0) | ||
47 | #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0) | ||
48 | |||
49 | bool g_debug = false; | ||
50 | char **g_extern; | 39 | char **g_extern; |
51 | int g_extern_count; | 40 | int g_extern_count; |
52 | 41 | ||
53 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) | 42 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) |
54 | 43 | ||
55 | /** | 44 | #define crypto_cbc(...) \ |
56 | * Misc | 45 | do { int ret = crypto_cbc(__VA_ARGS__); \ |
57 | */ | 46 | if(ret != CRYPTO_ERROR_SUCCESS) \ |
58 | 47 | bug("crypto_cbc error: %d\n", ret); \ | |
59 | char *s_getenv(const char *name) | 48 | }while(0) |
60 | { | ||
61 | char *s = getenv(name); | ||
62 | return s ? s : ""; | ||
63 | } | ||
64 | |||
65 | void generate_random_data(void *buf, size_t sz) | ||
66 | { | ||
67 | static int rand_fd = -1; | ||
68 | if(rand_fd == -1) | ||
69 | rand_fd = open("/dev/urandom", O_RDONLY); | ||
70 | if(rand_fd == -1) | ||
71 | bugp("failed to open /dev/urandom"); | ||
72 | if(read(rand_fd, buf, sz) != (ssize_t)sz) | ||
73 | bugp("failed to read /dev/urandom"); | ||
74 | } | ||
75 | |||
76 | void *xmalloc(size_t s) /* malloc helper, used in elf.c */ | ||
77 | { | ||
78 | void * r = malloc(s); | ||
79 | if(!r) bugp("malloc"); | ||
80 | return r; | ||
81 | } | ||
82 | |||
83 | int convxdigit(char digit, byte *val) | ||
84 | { | ||
85 | if(digit >= '0' && digit <= '9') | ||
86 | { | ||
87 | *val = digit - '0'; | ||
88 | return 0; | ||
89 | } | ||
90 | else if(digit >= 'A' && digit <= 'F') | ||
91 | { | ||
92 | *val = digit - 'A' + 10; | ||
93 | return 0; | ||
94 | } | ||
95 | else if(digit >= 'a' && digit <= 'f') | ||
96 | { | ||
97 | *val = digit - 'a' + 10; | ||
98 | return 0; | ||
99 | } | ||
100 | else | ||
101 | return 1; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * Key file parsing | ||
106 | */ | ||
107 | |||
108 | typedef byte (*key_array_t)[16]; | ||
109 | |||
110 | int g_nr_keys; | ||
111 | key_array_t g_key_array; | ||
112 | |||
113 | static void add_keys(key_array_t ka, int kac) | ||
114 | { | ||
115 | key_array_t new_ka = xmalloc((g_nr_keys + kac) * 16); | ||
116 | memcpy(new_ka, g_key_array, g_nr_keys * 16); | ||
117 | memcpy(new_ka + g_nr_keys, ka, kac * 16); | ||
118 | free(g_key_array); | ||
119 | g_key_array = new_ka; | ||
120 | g_nr_keys += kac; | ||
121 | } | ||
122 | |||
123 | static key_array_t read_keys(const char *key_file, 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 | if(g_debug) | ||
139 | printf("Parsing key file '%s'...\n", key_file); | ||
140 | *num_keys = size ? 1 : 0; | ||
141 | char *ptr = buf; | ||
142 | /* allow trailing newline at the end (but no space after it) */ | ||
143 | while(ptr != buf + size && (ptr + 1) != buf + size) | ||
144 | { | ||
145 | if(*ptr++ == '\n') | ||
146 | (*num_keys)++; | ||
147 | } | ||
148 | |||
149 | key_array_t keys = xmalloc(sizeof(byte[16]) * *num_keys); | ||
150 | int pos = 0; | ||
151 | for(int i = 0; i < *num_keys; i++) | ||
152 | { | ||
153 | /* skip ws */ | ||
154 | while(pos < size && isspace(buf[pos])) | ||
155 | pos++; | ||
156 | /* enough space ? */ | ||
157 | if((pos + 32) > size) | ||
158 | bugp("invalid key file"); | ||
159 | for(int j = 0; j < 16; j++) | ||
160 | { | ||
161 | byte a, b; | ||
162 | if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b)) | ||
163 | bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n"); | ||
164 | keys[i][j] = (a << 4) | b; | ||
165 | } | ||
166 | if(g_debug) | ||
167 | { | ||
168 | printf("Add key: "); | ||
169 | for(int j = 0; j < 16; j++) | ||
170 | printf("%02x", keys[i][j]); | ||
171 | printf("\n"); | ||
172 | } | ||
173 | pos += 32; | ||
174 | } | ||
175 | free(buf); | ||
176 | |||
177 | return keys; | ||
178 | } | ||
179 | 49 | ||
180 | /** | 50 | /** |
181 | * command file to sb conversion | 51 | * command file to sb conversion |
@@ -224,9 +94,9 @@ struct sb_file_t | |||
224 | 94 | ||
225 | static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) | 95 | static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) |
226 | { | 96 | { |
227 | if(lseek(*(int *)user, addr, SEEK_SET) == (off_t)-1) | 97 | if(fseek((FILE *)user, addr, SEEK_SET) == -1) |
228 | return false; | 98 | return false; |
229 | return read(*(int *)user, buf, count) == (ssize_t)count; | 99 | return fread(buf, 1, count, (FILE *)user) == count; |
230 | } | 100 | } |
231 | 101 | ||
232 | static void elf_printf(void *user, bool error, const char *fmt, ...) | 102 | static void elf_printf(void *user, bool error, const char *fmt, ...) |
@@ -264,14 +134,14 @@ static void load_elf_by_id(struct cmd_file_t *cmd_file, const char *id) | |||
264 | resolve_extern(src); | 134 | resolve_extern(src); |
265 | /* load it */ | 135 | /* load it */ |
266 | src->type = CMD_SRC_ELF; | 136 | src->type = CMD_SRC_ELF; |
267 | int fd = open(src->filename, O_RDONLY); | 137 | FILE *fd = fopen(src->filename, "rb"); |
268 | if(fd < 0) | 138 | if(fd == NULL) |
269 | bug("cannot open '%s' (id '%s')\n", src->filename, id); | 139 | bug("cannot open '%s' (id '%s')\n", src->filename, id); |
270 | if(g_debug) | 140 | if(g_debug) |
271 | printf("Loading ELF file '%s'...\n", src->filename); | 141 | printf("Loading ELF file '%s'...\n", src->filename); |
272 | elf_init(&src->elf); | 142 | elf_init(&src->elf); |
273 | src->loaded = elf_read_file(&src->elf, elf_read, elf_printf, &fd); | 143 | src->loaded = elf_read_file(&src->elf, elf_read, elf_printf, fd); |
274 | close(fd); | 144 | fclose(fd); |
275 | if(!src->loaded) | 145 | if(!src->loaded) |
276 | bug("error loading elf file '%s' (id '%s')\n", src->filename, id); | 146 | bug("error loading elf file '%s' (id '%s')\n", src->filename, id); |
277 | elf_translate_addresses(&src->elf); | 147 | elf_translate_addresses(&src->elf); |
@@ -291,16 +161,17 @@ static void load_bin_by_id(struct cmd_file_t *cmd_file, const char *id) | |||
291 | resolve_extern(src); | 161 | resolve_extern(src); |
292 | /* load it */ | 162 | /* load it */ |
293 | src->type = CMD_SRC_BIN; | 163 | src->type = CMD_SRC_BIN; |
294 | int fd = open(src->filename, O_RDONLY); | 164 | FILE *fd = fopen(src->filename, "rb"); |
295 | if(fd < 0) | 165 | if(fd == NULL) |
296 | bug("cannot open '%s' (id '%s')\n", src->filename, id); | 166 | bug("cannot open '%s' (id '%s')\n", src->filename, id); |
297 | if(g_debug) | 167 | if(g_debug) |
298 | printf("Loading BIN file '%s'...\n", src->filename); | 168 | printf("Loading BIN file '%s'...\n", src->filename); |
299 | src->bin.size = lseek(fd, 0, SEEK_END); | 169 | fseek(fd, 0, SEEK_END); |
300 | lseek(fd, 0, SEEK_SET); | 170 | src->bin.size = ftell(fd); |
171 | fseek(fd, 0, SEEK_SET); | ||
301 | src->bin.data = xmalloc(src->bin.size); | 172 | src->bin.data = xmalloc(src->bin.size); |
302 | read(fd, src->bin.data, src->bin.size); | 173 | fread(src->bin.data, 1, src->bin.size, fd); |
303 | close(fd); | 174 | fclose(fd); |
304 | src->loaded = true; | 175 | src->loaded = true; |
305 | } | 176 | } |
306 | 177 | ||
@@ -767,12 +638,12 @@ void produce_sb_instruction(struct sb_inst_t *inst, | |||
767 | 638 | ||
768 | static void produce_sb_file(struct sb_file_t *sb, const char *filename) | 639 | static void produce_sb_file(struct sb_file_t *sb, const char *filename) |
769 | { | 640 | { |
770 | int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, | 641 | FILE *fd = fopen(filename, "wb"); |
771 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | 642 | if(fd == NULL) |
772 | if(fd < 0) | ||
773 | bugp("cannot open output file"); | 643 | bugp("cannot open output file"); |
774 | 644 | ||
775 | byte real_key[16]; | 645 | struct crypto_key_t real_key; |
646 | real_key.method = CRYPTO_KEY; | ||
776 | byte crypto_iv[16]; | 647 | byte crypto_iv[16]; |
777 | byte (*cbc_macs)[16] = xmalloc(16 * g_nr_keys); | 648 | byte (*cbc_macs)[16] = xmalloc(16 * g_nr_keys); |
778 | /* init CBC-MACs */ | 649 | /* init CBC-MACs */ |
@@ -782,7 +653,7 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
782 | fill_gaps(sb); | 653 | fill_gaps(sb); |
783 | compute_sb_offsets(sb); | 654 | compute_sb_offsets(sb); |
784 | 655 | ||
785 | generate_random_data(real_key, sizeof(real_key)); | 656 | generate_random_data(real_key.u.key, 16); |
786 | 657 | ||
787 | /* global SHA-1 */ | 658 | /* global SHA-1 */ |
788 | struct sha_1_params_t file_sha1; | 659 | struct sha_1_params_t file_sha1; |
@@ -791,13 +662,13 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
791 | struct sb_header_t sb_hdr; | 662 | struct sb_header_t sb_hdr; |
792 | produce_sb_header(sb, &sb_hdr); | 663 | produce_sb_header(sb, &sb_hdr); |
793 | sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr)); | 664 | sha_1_update(&file_sha1, (byte *)&sb_hdr, sizeof(sb_hdr)); |
794 | write(fd, &sb_hdr, sizeof(sb_hdr)); | 665 | fwrite(&sb_hdr, 1, sizeof(sb_hdr), fd); |
795 | 666 | ||
796 | memcpy(crypto_iv, &sb_hdr, 16); | 667 | memcpy(crypto_iv, &sb_hdr, 16); |
797 | 668 | ||
798 | /* update CBC-MACs */ | 669 | /* update CBC-MACs */ |
799 | for(int i = 0; i < g_nr_keys; i++) | 670 | for(int i = 0; i < g_nr_keys; i++) |
800 | cbc_mac((byte *)&sb_hdr, NULL, sizeof(sb_hdr) / BLOCK_SIZE, g_key_array[i], | 671 | crypto_cbc((byte *)&sb_hdr, NULL, sizeof(sb_hdr) / BLOCK_SIZE, &g_key_array[i], |
801 | cbc_macs[i], &cbc_macs[i], 1); | 672 | cbc_macs[i], &cbc_macs[i], 1); |
802 | 673 | ||
803 | /* produce and write section headers */ | 674 | /* produce and write section headers */ |
@@ -806,21 +677,21 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
806 | struct sb_section_header_t sb_sec_hdr; | 677 | struct sb_section_header_t sb_sec_hdr; |
807 | produce_sb_section_header(&sb->sections[i], &sb_sec_hdr); | 678 | produce_sb_section_header(&sb->sections[i], &sb_sec_hdr); |
808 | sha_1_update(&file_sha1, (byte *)&sb_sec_hdr, sizeof(sb_sec_hdr)); | 679 | sha_1_update(&file_sha1, (byte *)&sb_sec_hdr, sizeof(sb_sec_hdr)); |
809 | write(fd, &sb_sec_hdr, sizeof(sb_sec_hdr)); | 680 | fwrite(&sb_sec_hdr, 1, sizeof(sb_sec_hdr), fd); |
810 | /* update CBC-MACs */ | 681 | /* update CBC-MACs */ |
811 | for(int j = 0; j < g_nr_keys; j++) | 682 | for(int j = 0; j < g_nr_keys; j++) |
812 | cbc_mac((byte *)&sb_sec_hdr, NULL, sizeof(sb_sec_hdr) / BLOCK_SIZE, | 683 | crypto_cbc((byte *)&sb_sec_hdr, NULL, sizeof(sb_sec_hdr) / BLOCK_SIZE, |
813 | g_key_array[j], cbc_macs[j], &cbc_macs[j], 1); | 684 | &g_key_array[j], cbc_macs[j], &cbc_macs[j], 1); |
814 | } | 685 | } |
815 | /* produce key dictionary */ | 686 | /* produce key dictionary */ |
816 | for(int i = 0; i < g_nr_keys; i++) | 687 | for(int i = 0; i < g_nr_keys; i++) |
817 | { | 688 | { |
818 | struct sb_key_dictionary_entry_t entry; | 689 | struct sb_key_dictionary_entry_t entry; |
819 | memcpy(entry.hdr_cbc_mac, cbc_macs[i], 16); | 690 | memcpy(entry.hdr_cbc_mac, cbc_macs[i], 16); |
820 | cbc_mac(real_key, entry.key, sizeof(real_key) / BLOCK_SIZE, g_key_array[i], | 691 | crypto_cbc(real_key.u.key, entry.key, 1, &g_key_array[i], |
821 | crypto_iv, NULL, 1); | 692 | crypto_iv, NULL, 1); |
822 | 693 | ||
823 | write(fd, &entry, sizeof(entry)); | 694 | fwrite(&entry, 1, sizeof(entry), fd); |
824 | sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry)); | 695 | sha_1_update(&file_sha1, (byte *)&entry, sizeof(entry)); |
825 | } | 696 | } |
826 | 697 | ||
@@ -836,7 +707,7 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
836 | byte a, b; | 707 | byte a, b; |
837 | if(convxdigit(key[2 * i], &a) || convxdigit(key[2 * i + 1], &b)) | 708 | if(convxdigit(key[2 * i], &a) || convxdigit(key[2 * i + 1], &b)) |
838 | bugp("Cannot override real key: key should be a 128-bit key written in hexadecimal\n"); | 709 | bugp("Cannot override real key: key should be a 128-bit key written in hexadecimal\n"); |
839 | real_key[i] = (a << 4) | b; | 710 | real_key.u.key[i] = (a << 4) | b; |
840 | } | 711 | } |
841 | } | 712 | } |
842 | if(strlen(s_getenv("SB_OVERRIDE_IV")) != 0) | 713 | if(strlen(s_getenv("SB_OVERRIDE_IV")) != 0) |
@@ -858,7 +729,7 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
858 | { | 729 | { |
859 | printf("Real key: "); | 730 | printf("Real key: "); |
860 | for(int j = 0; j < 16; j++) | 731 | for(int j = 0; j < 16; j++) |
861 | printf("%02x", real_key[j]); | 732 | printf("%02x", real_key.u.key[j]); |
862 | printf("\n"); | 733 | printf("\n"); |
863 | printf("IV : "); | 734 | printf("IV : "); |
864 | for(int j = 0; j < 16; j++) | 735 | for(int j = 0; j < 16; j++) |
@@ -872,10 +743,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
872 | struct sb_instruction_tag_t tag_cmd; | 743 | struct sb_instruction_tag_t tag_cmd; |
873 | produce_section_tag_cmd(&sb->sections[i], &tag_cmd, (i + 1) == sb_hdr.nr_sections); | 744 | produce_section_tag_cmd(&sb->sections[i], &tag_cmd, (i + 1) == sb_hdr.nr_sections); |
874 | if(g_nr_keys > 0) | 745 | if(g_nr_keys > 0) |
875 | cbc_mac((byte *)&tag_cmd, (byte *)&tag_cmd, sizeof(tag_cmd) / BLOCK_SIZE, | 746 | crypto_cbc((byte *)&tag_cmd, (byte *)&tag_cmd, sizeof(tag_cmd) / BLOCK_SIZE, |
876 | real_key, crypto_iv, NULL, 1); | 747 | &real_key, crypto_iv, NULL, 1); |
877 | sha_1_update(&file_sha1, (byte *)&tag_cmd, sizeof(tag_cmd)); | 748 | sha_1_update(&file_sha1, (byte *)&tag_cmd, sizeof(tag_cmd)); |
878 | write(fd, &tag_cmd, sizeof(tag_cmd)); | 749 | fwrite(&tag_cmd, 1, sizeof(tag_cmd), fd); |
879 | /* produce other commands */ | 750 | /* produce other commands */ |
880 | byte cur_cbc_mac[16]; | 751 | byte cur_cbc_mac[16]; |
881 | memcpy(cur_cbc_mac, crypto_iv, 16); | 752 | memcpy(cur_cbc_mac, crypto_iv, 16); |
@@ -888,10 +759,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
888 | struct sb_instruction_common_t cmd; | 759 | struct sb_instruction_common_t cmd; |
889 | produce_sb_instruction(inst, &cmd); | 760 | produce_sb_instruction(inst, &cmd); |
890 | if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) | 761 | if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) |
891 | cbc_mac((byte *)&cmd, (byte *)&cmd, sizeof(cmd) / BLOCK_SIZE, | 762 | crypto_cbc((byte *)&cmd, (byte *)&cmd, sizeof(cmd) / BLOCK_SIZE, |
892 | real_key, cur_cbc_mac, &cur_cbc_mac, 1); | 763 | &real_key, cur_cbc_mac, &cur_cbc_mac, 1); |
893 | sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd)); | 764 | sha_1_update(&file_sha1, (byte *)&cmd, sizeof(cmd)); |
894 | write(fd, &cmd, sizeof(cmd)); | 765 | fwrite(&cmd, 1, sizeof(cmd), fd); |
895 | } | 766 | } |
896 | /* data */ | 767 | /* data */ |
897 | if(inst->inst == SB_INST_LOAD || inst->inst == SB_INST_DATA) | 768 | if(inst->inst == SB_INST_LOAD || inst->inst == SB_INST_DATA) |
@@ -901,10 +772,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
901 | memcpy(data, inst->data, inst->size); | 772 | memcpy(data, inst->data, inst->size); |
902 | memcpy(data + inst->size, inst->padding, inst->padding_size); | 773 | memcpy(data + inst->size, inst->padding, inst->padding_size); |
903 | if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) | 774 | if(g_nr_keys > 0 && !sb->sections[i].is_cleartext) |
904 | cbc_mac(data, data, sz / BLOCK_SIZE, | 775 | crypto_cbc(data, data, sz / BLOCK_SIZE, |
905 | real_key, cur_cbc_mac, &cur_cbc_mac, 1); | 776 | &real_key, cur_cbc_mac, &cur_cbc_mac, 1); |
906 | sha_1_update(&file_sha1, data, sz); | 777 | sha_1_update(&file_sha1, data, sz); |
907 | write(fd, data, sz); | 778 | fwrite(data, 1, sz, fd); |
908 | free(data); | 779 | free(data); |
909 | } | 780 | } |
910 | } | 781 | } |
@@ -915,10 +786,10 @@ static void produce_sb_file(struct sb_file_t *sb, const char *filename) | |||
915 | sha_1_output(&file_sha1, final_sig); | 786 | sha_1_output(&file_sha1, final_sig); |
916 | generate_random_data(final_sig + 20, 12); | 787 | generate_random_data(final_sig + 20, 12); |
917 | if(g_nr_keys > 0) | 788 | if(g_nr_keys > 0) |
918 | cbc_mac(final_sig, final_sig, 2, real_key, crypto_iv, NULL, 1); | 789 | crypto_cbc(final_sig, final_sig, 2, &real_key, crypto_iv, NULL, 1); |
919 | write(fd, final_sig, 32); | 790 | fwrite(final_sig, 1, 32, fd); |
920 | 791 | ||
921 | close(fd); | 792 | fclose(fd); |
922 | } | 793 | } |
923 | 794 | ||
924 | void usage(void) | 795 | void usage(void) |
@@ -931,10 +802,16 @@ void usage(void) | |||
931 | printf(" -d/--debug\tEnable debug output\n"); | 802 | printf(" -d/--debug\tEnable debug output\n"); |
932 | printf(" -k <file>\tAdd key file\n"); | 803 | printf(" -k <file>\tAdd key file\n"); |
933 | printf(" -z\t\tAdd zero key\n"); | 804 | printf(" -z\t\tAdd zero key\n"); |
805 | printf(" --single-key <key>\tAdd single key\n"); | ||
806 | printf(" --usb-otp <vid>:<pid>\tAdd USB OTP device\n"); | ||
934 | exit(1); | 807 | exit(1); |
935 | } | 808 | } |
936 | 809 | ||
937 | static byte g_zero_key[16] = {0}; | 810 | static struct crypto_key_t g_zero_key = |
811 | { | ||
812 | .method = CRYPTO_KEY, | ||
813 | .u.key = {0} | ||
814 | }; | ||
938 | 815 | ||
939 | int main(int argc, char **argv) | 816 | int main(int argc, char **argv) |
940 | { | 817 | { |
@@ -947,6 +824,8 @@ int main(int argc, char **argv) | |||
947 | { | 824 | { |
948 | {"help", no_argument, 0, '?'}, | 825 | {"help", no_argument, 0, '?'}, |
949 | {"debug", no_argument, 0, 'd'}, | 826 | {"debug", no_argument, 0, 'd'}, |
827 | {"single-key", required_argument, 0, 's'}, | ||
828 | {"usb-otp", required_argument, 0, 'u'}, | ||
950 | {0, 0, 0, 0} | 829 | {0, 0, 0, 0} |
951 | }; | 830 | }; |
952 | 831 | ||
@@ -979,6 +858,42 @@ int main(int argc, char **argv) | |||
979 | add_keys(&g_zero_key, 1); | 858 | add_keys(&g_zero_key, 1); |
980 | break; | 859 | break; |
981 | } | 860 | } |
861 | case 's': | ||
862 | { | ||
863 | struct crypto_key_t key; | ||
864 | key.method = CRYPTO_KEY; | ||
865 | if(strlen(optarg) != 32) | ||
866 | bug("The key given in argument is invalid"); | ||
867 | for(int i = 0; i < 16; i++) | ||
868 | { | ||
869 | byte a, b; | ||
870 | if(convxdigit(optarg[2 * i], &a) || convxdigit(optarg[2 * i + 1], &b)) | ||
871 | bugp("The key given in argument is invalid\n"); | ||
872 | key.u.key[i] = (a << 4) | b; | ||
873 | } | ||
874 | add_keys(&key, 1); | ||
875 | break; | ||
876 | } | ||
877 | case 'u': | ||
878 | { | ||
879 | int vid, pid; | ||
880 | char *p = strchr(optarg, ':'); | ||
881 | if(p == NULL) | ||
882 | bug("Invalid VID/PID\n"); | ||
883 | |||
884 | char *end; | ||
885 | vid = strtol(optarg, &end, 16); | ||
886 | if(end != p) | ||
887 | bug("Invalid VID/PID\n"); | ||
888 | pid = strtol(p + 1, &end, 16); | ||
889 | if(end != (optarg + strlen(optarg))) | ||
890 | bug("Invalid VID/PID\n"); | ||
891 | struct crypto_key_t key; | ||
892 | key.method = CRYPTO_USBOTP; | ||
893 | key.u.vid_pid = vid << 16 | pid; | ||
894 | add_keys(&key, 1); | ||
895 | break; | ||
896 | } | ||
982 | default: | 897 | default: |
983 | abort(); | 898 | abort(); |
984 | } | 899 | } |
@@ -997,9 +912,8 @@ int main(int argc, char **argv) | |||
997 | printf("key: %d\n", g_nr_keys); | 912 | printf("key: %d\n", g_nr_keys); |
998 | for(int i = 0; i < g_nr_keys; i++) | 913 | for(int i = 0; i < g_nr_keys; i++) |
999 | { | 914 | { |
1000 | for(int j = 0; j < 16; j++) | 915 | printf(" "); |
1001 | printf(" %02x", g_key_array[i][j]); | 916 | print_key(&g_key_array[i], true); |
1002 | printf("\n"); | ||
1003 | } | 917 | } |
1004 | 918 | ||
1005 | for(int i = 0; i < g_extern_count; i++) | 919 | for(int i = 0; i < g_extern_count; i++) |
diff --git a/utils/sbtools/misc.c b/utils/sbtools/misc.c new file mode 100644 index 0000000000..09a8919aef --- /dev/null +++ b/utils/sbtools/misc.c | |||
@@ -0,0 +1,174 @@ | |||
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 | |||
27 | bool g_debug = false; | ||
28 | |||
29 | /** | ||
30 | * Misc | ||
31 | */ | ||
32 | |||
33 | char *s_getenv(const char *name) | ||
34 | { | ||
35 | char *s = getenv(name); | ||
36 | return s ? s : ""; | ||
37 | } | ||
38 | |||
39 | void generate_random_data(void *buf, size_t sz) | ||
40 | { | ||
41 | FILE *rand_fd = fopen("/dev/urandom", "rb"); | ||
42 | if(rand_fd == NULL) | ||
43 | bugp("failed to open /dev/urandom"); | ||
44 | if(fread(buf, 1, sz, rand_fd) != sz) | ||
45 | bugp("failed to read /dev/urandom"); | ||
46 | fclose(rand_fd); | ||
47 | } | ||
48 | |||
49 | void *xmalloc(size_t s) | ||
50 | { | ||
51 | void * r = malloc(s); | ||
52 | if(!r) bugp("malloc"); | ||
53 | return r; | ||
54 | } | ||
55 | |||
56 | int convxdigit(char digit, byte *val) | ||
57 | { | ||
58 | if(digit >= '0' && digit <= '9') | ||
59 | { | ||
60 | *val = digit - '0'; | ||
61 | return 0; | ||
62 | } | ||
63 | else if(digit >= 'A' && digit <= 'F') | ||
64 | { | ||
65 | *val = digit - 'A' + 10; | ||
66 | return 0; | ||
67 | } | ||
68 | else if(digit >= 'a' && digit <= 'f') | ||
69 | { | ||
70 | *val = digit - 'a' + 10; | ||
71 | return 0; | ||
72 | } | ||
73 | else | ||
74 | return 1; | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Key file parsing | ||
79 | */ | ||
80 | int g_nr_keys; | ||
81 | key_array_t g_key_array; | ||
82 | |||
83 | void add_keys(key_array_t ka, int kac) | ||
84 | { | ||
85 | key_array_t new_ka = xmalloc((g_nr_keys + kac) * sizeof(struct crypto_key_t)); | ||
86 | memcpy(new_ka, g_key_array, g_nr_keys * sizeof(struct crypto_key_t)); | ||
87 | memcpy(new_ka + g_nr_keys, ka, kac * sizeof(struct crypto_key_t)); | ||
88 | free(g_key_array); | ||
89 | g_key_array = new_ka; | ||
90 | g_nr_keys += kac; | ||
91 | } | ||
92 | |||
93 | key_array_t read_keys(const char *key_file, int *num_keys) | ||
94 | { | ||
95 | int size; | ||
96 | FILE *fd = fopen(key_file, "r"); | ||
97 | if(fd == NULL) | ||
98 | bugp("opening key file failed"); | ||
99 | fseek(fd, 0, SEEK_END); | ||
100 | size = ftell(fd); | ||
101 | fseek(fd, 0, SEEK_SET); | ||
102 | char *buf = xmalloc(size); | ||
103 | if(fread(buf, size, 1, fd) != (size_t)size) | ||
104 | bugp("reading key file"); | ||
105 | fclose(fd); | ||
106 | |||
107 | if(g_debug) | ||
108 | printf("Parsing key file '%s'...\n", key_file); | ||
109 | *num_keys = size ? 1 : 0; | ||
110 | char *ptr = buf; | ||
111 | /* allow trailing newline at the end (but no space after it) */ | ||
112 | while(ptr != buf + size && (ptr + 1) != buf + size) | ||
113 | { | ||
114 | if(*ptr++ == '\n') | ||
115 | (*num_keys)++; | ||
116 | } | ||
117 | |||
118 | key_array_t keys = xmalloc(sizeof(struct crypto_key_t) * *num_keys); | ||
119 | int pos = 0; | ||
120 | for(int i = 0; i < *num_keys; i++) | ||
121 | { | ||
122 | /* skip ws */ | ||
123 | while(pos < size && isspace(buf[pos])) | ||
124 | pos++; | ||
125 | /* enough space ? */ | ||
126 | if((pos + 32) > size) | ||
127 | bugp("invalid key file"); | ||
128 | keys[i].method = CRYPTO_KEY; | ||
129 | for(int j = 0; j < 16; j++) | ||
130 | { | ||
131 | byte a, b; | ||
132 | if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b)) | ||
133 | bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n"); | ||
134 | keys[i].u.key[j] = (a << 4) | b; | ||
135 | } | ||
136 | if(g_debug) | ||
137 | { | ||
138 | printf("Add key: "); | ||
139 | for(int j = 0; j < 16; j++) | ||
140 | printf("%02x", keys[i].u.key[j]); | ||
141 | printf("\n"); | ||
142 | } | ||
143 | pos += 32; | ||
144 | } | ||
145 | free(buf); | ||
146 | |||
147 | return keys; | ||
148 | } | ||
149 | |||
150 | void print_hex(byte *data, int len, bool newline) | ||
151 | { | ||
152 | for(int i = 0; i < len; i++) | ||
153 | printf("%02X ", data[i]); | ||
154 | if(newline) | ||
155 | printf("\n"); | ||
156 | } | ||
157 | |||
158 | void print_key(struct crypto_key_t *key, bool newline) | ||
159 | { | ||
160 | switch(key->method) | ||
161 | { | ||
162 | case CRYPTO_KEY: | ||
163 | print_hex(key->u.key, 16, false); | ||
164 | break; | ||
165 | case CRYPTO_USBOTP: | ||
166 | printf("USB-OTP(%04x:%04x)", key->u.vid_pid >> 16, key->u.vid_pid & 0xffff); | ||
167 | break; | ||
168 | case CRYPTO_NONE: | ||
169 | printf("none"); | ||
170 | break; | ||
171 | } | ||
172 | if(newline) | ||
173 | printf("\n"); | ||
174 | } | ||
diff --git a/utils/sbtools/misc.h b/utils/sbtools/misc.h new file mode 100644 index 0000000000..545285eafc --- /dev/null +++ b/utils/sbtools/misc.h | |||
@@ -0,0 +1,48 @@ | |||
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 "crypto.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(a) do { perror("ERROR: "a); exit(1); } while(0) | ||
32 | |||
33 | extern bool g_debug; | ||
34 | |||
35 | typedef struct crypto_key_t *key_array_t; | ||
36 | int g_nr_keys; | ||
37 | key_array_t g_key_array; | ||
38 | |||
39 | char *s_getenv(const char *name); | ||
40 | void generate_random_data(void *buf, size_t sz); | ||
41 | void *xmalloc(size_t s); | ||
42 | int convxdigit(char digit, byte *val); | ||
43 | void print_hex(byte *data, int len, bool newline); | ||
44 | void add_keys(key_array_t ka, int kac); | ||
45 | key_array_t read_keys(const char *key_file, int *num_keys); | ||
46 | void print_key(struct crypto_key_t *key, bool newline); | ||
47 | |||
48 | #endif /* __MISC_H__ */ | ||
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 | ||