ramips: fix USW-Flex reversed switch-port order
[openwrt/staging/hauke.git] / tools / firmware-utils / src / sign_dlink_ru.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * This program is designed to sign firmware images so they are accepted
4 * by D-Link DIR-882 R1 WebUIs.
5 *
6 * Copyright (C) 2020 Andrew Pikler
7 */
8
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16
17 #include "md5.h"
18
19 #define BUF_SIZE 4096
20 #define MD5_HASH_LEN 16
21
22
23 typedef struct _md5_digest_t {
24 uint8_t digest[MD5_HASH_LEN];
25 } md5_digest_t;
26
27 typedef struct _salt_t {
28 char* salt_ascii;
29 uint8_t* salt_bin;
30 size_t salt_bin_len;
31 } salt_t;
32
33 void read_file_bytes(FILE* f, MD5_CTX* md5_ctx) {
34 uint8_t buf[BUF_SIZE];
35 size_t bytes_read;
36 rewind(f);
37
38 while (0 != (bytes_read = fread(buf, sizeof(uint8_t), BUF_SIZE, f))) {
39 MD5_Update(md5_ctx, buf, bytes_read);
40 }
41
42 if (!feof(f)) {
43 printf("Error: expected to be at EOF\n");
44 exit(-1);
45 }
46 }
47
48 void add_magic_bytes(FILE* f) {
49 char magic_bytes[] = { 0x00, 0xc0, 0xff, 0xee };
50 size_t magic_bytes_len = 4;
51 fwrite(magic_bytes, magic_bytes_len, 1, f);
52 }
53
54 /**
55 * Add the signature produced by this salt to the file
56 * The signature consists by creating an MD5 digest wht the salt bytes plus
57 * all of the bytes in the firmware file, then adding the magic bytes to the
58 * file
59 */
60 void add_signature(FILE* f, salt_t* salt) {
61 md5_digest_t digest;
62 MD5_CTX md5_context;
63
64 MD5_Init(&md5_context);
65 MD5_Update(&md5_context, salt->salt_bin, salt->salt_bin_len);
66 read_file_bytes(f, &md5_context);
67 MD5_Final(digest.digest, &md5_context);
68
69 fwrite(&digest.digest, sizeof(uint8_t), MD5_HASH_LEN, f);
70 add_magic_bytes(f);
71 }
72
73 void add_version_suffix(FILE* f) {
74 char* version_suffix = "c0ffeef0rge";
75 fseek(f, 0, SEEK_END);
76 fwrite(version_suffix, sizeof(char), strlen(version_suffix), f);
77 }
78
79 int asciihex_to_int(char c) {
80 if(c >= '0' && c <= 'F')
81 return c - '0';
82
83 if(c >= 'a' && c <= 'f')
84 return 10 + c - 'a';
85 return -1;
86 }
87
88 /**
89 * Verify this is a valid hex string to convert
90 */
91 void verify_valid_hex_str(char* s) {
92 int i;
93 int s_len = strlen(s);
94 if (s_len == 0) {
95 printf("invalid empty salt: %s\n", s);
96 exit(-1);
97 }
98
99 if (s_len % 2 != 0) {
100 printf("invalid odd len salt: %s\n", s);
101 exit(-1);
102 }
103
104 for (i = 0; i < s_len; ++i) {
105 if (asciihex_to_int(s[i]) < 0) {
106 printf("invalid salt (invalid hex char): %s\n", s);
107 exit(-1);
108 }
109 }
110 }
111
112 /**
113 * Convert a hex ascii string to an allocated binary array. This array must be free'd
114 */
115 uint8_t* convert_hex_to_bin(char * s) {
116 int i;
117 int s_len = strlen(s);
118
119 uint8_t* ret = malloc(s_len / 2);
120 for (i = 0; i < s_len; i += 2) {
121 ret[i / 2] = (asciihex_to_int(s[i]) << 4) | asciihex_to_int(s[i + 1]);
122 }
123
124 return ret;
125 }
126
127 void init_salt(salt_t* salt, char * salt_ascii) {
128 salt->salt_ascii = salt_ascii;
129 salt->salt_bin = convert_hex_to_bin(salt_ascii);
130 salt->salt_bin_len = strlen(salt_ascii) / 2;
131 }
132
133 void free_salt(salt_t* salt) {
134 free(salt->salt_bin);
135 }
136
137 /**
138 * Verify that the arguments are valid, or exit with failure
139 */
140 void verify_args(int argc, char** argv) {
141 int i;
142
143 if (argc < 3) {
144 printf("Usage: %s <firmware file> <signing hash1> <signing hash2> ... <signing hash n>\n", argv[0]);
145 exit(1);
146 }
147
148 for (i = 2; i < argc; i++) {
149 verify_valid_hex_str(argv[i]);
150 }
151 }
152
153 FILE* make_out_file(char* filename) {
154 uint8_t buf[BUF_SIZE];
155 int bytes_read;
156 char* suffix = ".new";
157 int new_filename_len = strlen(filename) + strlen(suffix) + 1;
158 char* new_filename = malloc(new_filename_len);
159 strcpy(new_filename, filename);
160 strcat(new_filename, suffix);
161
162 FILE* f = fopen(filename, "r+");
163 if (!f) {
164 printf("cannot open file %s\n", filename);
165 exit(2);
166 }
167
168 FILE* out = fopen(new_filename, "w+");
169 free(new_filename);
170 if (!out) {
171 printf("cannot open file %s\n", filename);
172 exit(2);
173 }
174
175 while (0 != (bytes_read = fread(buf, sizeof(uint8_t), BUF_SIZE, f))) {
176 fwrite(buf, sizeof(uint8_t), bytes_read, out);
177 }
178 fclose(f);
179 return out;
180 }
181
182 /**
183 * Sign the firmware file after all of our checks have completed
184 */
185 void sign_firmware(char* filename, char** salts, int num_salts) {
186 int i;
187 salt_t salt;
188 FILE* f = make_out_file(filename);
189
190 // add a version suffix string - dlink versions do something similar before the first signature
191 add_version_suffix(f);
192
193 //for each of the salts we are supplied with
194 for (i = 0; i < num_salts; i++) {
195 char* salt_str = salts[i];
196 // convert this str to binary
197 init_salt(&salt, salt_str);
198
199 // add the signature to the firmware file produced from this salt
200 add_signature(f, &salt);
201 free_salt(&salt);
202 printf("Signed with salt: %s\n", salt_str);
203 }
204
205 fclose(f);
206 }
207
208
209 int main(int argc, char ** argv) {
210 verify_args(argc, argv);
211 sign_firmware(argv[1], argv+2, argc-2);
212 return 0;
213 }