From 2b52b3df6fd21f21e397b74b49544436f711ce30 Mon Sep 17 00:00:00 2001 From: Andrew Pikler Date: Mon, 5 Oct 2020 17:13:38 +0300 Subject: [PATCH] firmware: add tool for signing d-link ru router factory firmware images Some Russian d-link routers require that their firmware be signed with a salted md5 checksum followed by the bytes 0x00 0xc0 0xff 0xee. This tool signs factory images the OEM's firmware accepts them. Signed-off-by: Andrew Pikler --- Makefile | 1 + src/sign_dlink_ru.c | 225 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/sign_dlink_ru.c diff --git a/Makefile b/Makefile index 9a68b80..6074ecc 100644 --- a/Makefile +++ b/Makefile @@ -84,6 +84,7 @@ define Host/Compile $(call cc,pc1crypt) $(call cc,ptgen cyg_crc32) $(call cc,seama md5) + $(call cc,sign_dlink_ru md5,-Wall) $(call cc,spw303v) $(call cc,srec2bin) $(call cc,tplink-safeloader md5,-Wall --std=gnu99) diff --git a/src/sign_dlink_ru.c b/src/sign_dlink_ru.c new file mode 100644 index 0000000..9c02ed5 --- /dev/null +++ b/src/sign_dlink_ru.c @@ -0,0 +1,225 @@ +/* + * This program is designed to sign firmware images so they are accepted + * by D-Link DIR-882 R1 WebUIs. + * + * Copyright (C) 2020 Andrew Pikler + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "md5.h" + +#define BUF_SIZE 4096 +#define MD5_HASH_LEN 16 + + +typedef struct _md5_digest_t { + uint8_t digest[MD5_HASH_LEN]; +} md5_digest_t; + +typedef struct _salt_t { + char* salt_ascii; + uint8_t* salt_bin; + size_t salt_bin_len; +} salt_t; + +void read_file_bytes(FILE* f, MD5_CTX* md5_ctx) { + uint8_t buf[BUF_SIZE]; + size_t bytes_read; + rewind(f); + + while (0 != (bytes_read = fread(buf, sizeof(uint8_t), BUF_SIZE, f))) { + MD5_Update(md5_ctx, buf, bytes_read); + } + + if (!feof(f)) { + printf("Error: expected to be at EOF\n"); + exit(-1); + } +} + +void add_magic_bytes(FILE* f) { + char magic_bytes[] = { 0x00, 0xc0, 0xff, 0xee }; + size_t magic_bytes_len = 4; + fwrite(magic_bytes, magic_bytes_len, 1, f); +} + +/** + * Add the signature produced by this salt to the file + * The signature consists by creating an MD5 digest wht the salt bytes plus + * all of the bytes in the firmware file, then adding the magic bytes to the + * file + */ +void add_signature(FILE* f, salt_t* salt) { + md5_digest_t digest; + MD5_CTX md5_context; + + MD5_Init(&md5_context); + MD5_Update(&md5_context, salt->salt_bin, salt->salt_bin_len); + read_file_bytes(f, &md5_context); + MD5_Final(digest.digest, &md5_context); + + fwrite(&digest.digest, sizeof(uint8_t), MD5_HASH_LEN, f); + add_magic_bytes(f); +} + +void add_version_suffix(FILE* f) { + char* version_suffix = "c0ffeef0rge"; + fseek(f, 0, SEEK_END); + fwrite(version_suffix, sizeof(char), strlen(version_suffix), f); +} + +int asciihex_to_int(char c) { + if(c >= '0' && c <= 'F') + return c - '0'; + + if(c >= 'a' && c <= 'f') + return 10 + c - 'a'; + return -1; +} + +/** + * Verify this is a valid hex string to convert + */ +void verify_valid_hex_str(char* s) { + int i; + int s_len = strlen(s); + if (s_len == 0) { + printf("invalid empty salt: %s\n", s); + exit(-1); + } + + if (s_len % 2 != 0) { + printf("invalid odd len salt: %s\n", s); + exit(-1); + } + + for (i = 0; i < s_len; ++i) { + if (asciihex_to_int(s[i]) < 0) { + printf("invalid salt (invalid hex char): %s\n", s); + exit(-1); + } + } +} + +/** + * Convert a hex ascii string to an allocated binary array. This array must be free'd + */ +uint8_t* convert_hex_to_bin(char * s) { + int i; + int s_len = strlen(s); + + uint8_t* ret = malloc(s_len / 2); + for (i = 0; i < s_len; i += 2) { + ret[i / 2] = (asciihex_to_int(s[i]) << 4) | asciihex_to_int(s[i + 1]); + } + + return ret; +} + +void init_salt(salt_t* salt, char * salt_ascii) { + salt->salt_ascii = salt_ascii; + salt->salt_bin = convert_hex_to_bin(salt_ascii); + salt->salt_bin_len = strlen(salt_ascii) / 2; +} + +void free_salt(salt_t* salt) { + free(salt->salt_bin); +} + +/** + * Verify that the arguments are valid, or exit with failure + */ +void verify_args(int argc, char** argv) { + int i; + + if (argc < 3) { + printf("Usage: %s ... \n", argv[0]); + exit(1); + } + + for (i = 2; i < argc; i++) { + verify_valid_hex_str(argv[i]); + } +} + +FILE* make_out_file(char* filename) { + uint8_t buf[BUF_SIZE]; + int bytes_read; + char* suffix = ".new"; + int new_filename_len = strlen(filename) + strlen(suffix) + 1; + char* new_filename = malloc(new_filename_len); + strcpy(new_filename, filename); + strcat(new_filename, suffix); + + FILE* f = fopen(filename, "r+"); + if (!f) { + printf("cannot open file %s\n", filename); + exit(2); + } + + FILE* out = fopen(new_filename, "w+"); + free(new_filename); + if (!out) { + printf("cannot open file %s\n", filename); + exit(2); + } + + while (0 != (bytes_read = fread(buf, sizeof(uint8_t), BUF_SIZE, f))) { + fwrite(buf, sizeof(uint8_t), bytes_read, out); + } + fclose(f); + return out; +} + +/** + * Sign the firmware file after all of our checks have completed + */ +void sign_firmware(char* filename, char** salts, int num_salts) { + int i; + salt_t salt; + FILE* f = make_out_file(filename); + + // add a version suffix string - dlink versions do something similar before the first signature + add_version_suffix(f); + + //for each of the salts we are supplied with + for (i = 0; i < num_salts; i++) { + char* salt_str = salts[i]; + // convert this str to binary + init_salt(&salt, salt_str); + + // add the signature to the firmware file produced from this salt + add_signature(f, &salt); + free_salt(&salt); + printf("Signed with salt: %s\n", salt_str); + } + + fclose(f); +} + + +int main(int argc, char ** argv) { + verify_args(argc, argv); + sign_firmware(argv[1], argv+2, argc-2); + return 0; +} -- 2.30.2