9c02ed50afcdec34ce68078e805065497973224f
2 * This program is designed to sign firmware images so they are accepted
3 * by D-Link DIR-882 R1 WebUIs.
5 * Copyright (C) 2020 Andrew Pikler
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/types.h>
32 #define MD5_HASH_LEN 16
35 typedef struct _md5_digest_t
{
36 uint8_t digest
[MD5_HASH_LEN
];
39 typedef struct _salt_t
{
45 void read_file_bytes(FILE* f
, MD5_CTX
* md5_ctx
) {
46 uint8_t buf
[BUF_SIZE
];
50 while (0 != (bytes_read
= fread(buf
, sizeof(uint8_t), BUF_SIZE
, f
))) {
51 MD5_Update(md5_ctx
, buf
, bytes_read
);
55 printf("Error: expected to be at EOF\n");
60 void add_magic_bytes(FILE* f
) {
61 char magic_bytes
[] = { 0x00, 0xc0, 0xff, 0xee };
62 size_t magic_bytes_len
= 4;
63 fwrite(magic_bytes
, magic_bytes_len
, 1, f
);
67 * Add the signature produced by this salt to the file
68 * The signature consists by creating an MD5 digest wht the salt bytes plus
69 * all of the bytes in the firmware file, then adding the magic bytes to the
72 void add_signature(FILE* f
, salt_t
* salt
) {
76 MD5_Init(&md5_context
);
77 MD5_Update(&md5_context
, salt
->salt_bin
, salt
->salt_bin_len
);
78 read_file_bytes(f
, &md5_context
);
79 MD5_Final(digest
.digest
, &md5_context
);
81 fwrite(&digest
.digest
, sizeof(uint8_t), MD5_HASH_LEN
, f
);
85 void add_version_suffix(FILE* f
) {
86 char* version_suffix
= "c0ffeef0rge";
87 fseek(f
, 0, SEEK_END
);
88 fwrite(version_suffix
, sizeof(char), strlen(version_suffix
), f
);
91 int asciihex_to_int(char c
) {
92 if(c
>= '0' && c
<= 'F')
95 if(c
>= 'a' && c
<= 'f')
101 * Verify this is a valid hex string to convert
103 void verify_valid_hex_str(char* s
) {
105 int s_len
= strlen(s
);
107 printf("invalid empty salt: %s\n", s
);
111 if (s_len
% 2 != 0) {
112 printf("invalid odd len salt: %s\n", s
);
116 for (i
= 0; i
< s_len
; ++i
) {
117 if (asciihex_to_int(s
[i
]) < 0) {
118 printf("invalid salt (invalid hex char): %s\n", s
);
125 * Convert a hex ascii string to an allocated binary array. This array must be free'd
127 uint8_t* convert_hex_to_bin(char * s
) {
129 int s_len
= strlen(s
);
131 uint8_t* ret
= malloc(s_len
/ 2);
132 for (i
= 0; i
< s_len
; i
+= 2) {
133 ret
[i
/ 2] = (asciihex_to_int(s
[i
]) << 4) | asciihex_to_int(s
[i
+ 1]);
139 void init_salt(salt_t
* salt
, char * salt_ascii
) {
140 salt
->salt_ascii
= salt_ascii
;
141 salt
->salt_bin
= convert_hex_to_bin(salt_ascii
);
142 salt
->salt_bin_len
= strlen(salt_ascii
) / 2;
145 void free_salt(salt_t
* salt
) {
146 free(salt
->salt_bin
);
150 * Verify that the arguments are valid, or exit with failure
152 void verify_args(int argc
, char** argv
) {
156 printf("Usage: %s <firmware file> <signing hash1> <signing hash2> ... <signing hash n>\n", argv
[0]);
160 for (i
= 2; i
< argc
; i
++) {
161 verify_valid_hex_str(argv
[i
]);
165 FILE* make_out_file(char* filename
) {
166 uint8_t buf
[BUF_SIZE
];
168 char* suffix
= ".new";
169 int new_filename_len
= strlen(filename
) + strlen(suffix
) + 1;
170 char* new_filename
= malloc(new_filename_len
);
171 strcpy(new_filename
, filename
);
172 strcat(new_filename
, suffix
);
174 FILE* f
= fopen(filename
, "r+");
176 printf("cannot open file %s\n", filename
);
180 FILE* out
= fopen(new_filename
, "w+");
183 printf("cannot open file %s\n", filename
);
187 while (0 != (bytes_read
= fread(buf
, sizeof(uint8_t), BUF_SIZE
, f
))) {
188 fwrite(buf
, sizeof(uint8_t), bytes_read
, out
);
195 * Sign the firmware file after all of our checks have completed
197 void sign_firmware(char* filename
, char** salts
, int num_salts
) {
200 FILE* f
= make_out_file(filename
);
202 // add a version suffix string - dlink versions do something similar before the first signature
203 add_version_suffix(f
);
205 //for each of the salts we are supplied with
206 for (i
= 0; i
< num_salts
; i
++) {
207 char* salt_str
= salts
[i
];
208 // convert this str to binary
209 init_salt(&salt
, salt_str
);
211 // add the signature to the firmware file produced from this salt
212 add_signature(f
, &salt
);
214 printf("Signed with salt: %s\n", salt_str
);
221 int main(int argc
, char ** argv
) {
222 verify_args(argc
, argv
);
223 sign_firmware(argv
[1], argv
+2, argc
-2);