1 // SPDX-License-Identifier: GPL-2.0-or-later OR MIT
3 * Copyright (C) 2023 Sebastian Schaper <openwrt@sebastianschaper.net>
5 * This tool encrypts factory images for certain D-Link Devices
6 * manufactured by SGE / T&W, e.g. COVR-C1200, COVR-P2500, DIR-882, ...
9 * ./dlink-sge-image DEVICE_MODEL infile outfile [-d: decrypt]
13 #include "dlink-sge-image.h"
15 #include <openssl/evp.h>
16 #include <openssl/pem.h>
18 #include <arpa/inet.h>
25 #define HEAD_MAGIC "SHRS"
26 #define HEAD_MAGIC_LEN 4
27 #define SHA512_DIGEST_LENGTH 64
28 #define RSA_KEY_LENGTH_BYTES 512
29 #define AES_BLOCK_SIZE 16
30 #define HEADER_LEN 1756
32 unsigned char aes_iv
[AES_BLOCK_SIZE
];
34 unsigned char readbuf
[BUFSIZE
];
35 unsigned char encbuf
[BUFSIZE
];
37 unsigned int read_bytes
;
38 unsigned long read_total
;
41 unsigned char vendor_key
[AES_BLOCK_SIZE
];
43 const EVP_CIPHER
*aes128
;
44 EVP_CIPHER_CTX
*aes_ctx
;
49 int pass_cb(char *buf
, int size
, int rwflag
, void *u
)
51 char *tmp
= "12345678";
52 size_t len
= strlen(tmp
);
56 memcpy(buf
, tmp
, len
);
60 void image_encrypt(void)
64 EVP_MD_CTX
*digest_before
;
65 EVP_MD_CTX
*digest_post
;
66 EVP_MD_CTX
*digest_vendor
;
67 EVP_PKEY
*signing_key
;
68 EVP_PKEY_CTX
*rsa_ctx
;
69 uint32_t payload_length_before
, pad_len
, sizebuf
;
70 unsigned char md_before
[SHA512_DIGEST_LENGTH
];
71 unsigned char md_post
[SHA512_DIGEST_LENGTH
];
72 unsigned char md_vendor
[SHA512_DIGEST_LENGTH
];
73 unsigned char sigret
[RSA_KEY_LENGTH_BYTES
];
75 char footer
[] = {0x00, 0x00, 0x00, 0x00, 0x30};
77 // seek to position 1756 (begin of AES-encrypted data),
78 // write image headers later
79 memset(buf
, 0, HEADER_LEN
);
80 fwrite(&buf
, 1, HEADER_LEN
, output_file
);
81 digest_before
= EVP_MD_CTX_new();
82 digest_post
= EVP_MD_CTX_new();
83 digest_vendor
= EVP_MD_CTX_new();
84 sha512
= EVP_sha512();
85 EVP_DigestInit_ex(digest_before
, sha512
, NULL
);
86 EVP_DigestInit_ex(digest_post
, sha512
, NULL
);
87 EVP_DigestInit_ex(digest_vendor
, sha512
, NULL
);
89 signing_key
= PEM_read_bio_PrivateKey(rsa_private_bio
, NULL
, pass_cb
, NULL
);
90 rsa_ctx
= EVP_PKEY_CTX_new(signing_key
, NULL
);
92 EVP_PKEY_sign_init(rsa_ctx
);
93 EVP_PKEY_CTX_set_signature_md(rsa_ctx
, sha512
);
95 memcpy(&aes_iv
, &salt
, AES_BLOCK_SIZE
);
96 aes_ctx
= EVP_CIPHER_CTX_new();
97 EVP_EncryptInit_ex(aes_ctx
, aes128
, NULL
, &vendor_key
[0], aes_iv
);
98 EVP_CIPHER_CTX_set_padding(aes_ctx
, 0);
101 while ((read_bytes
= fread(&readbuf
, 1, BUFSIZE
, input_file
)) == BUFSIZE
) {
102 EVP_DigestUpdate(digest_before
, &readbuf
[0], read_bytes
);
103 read_total
+= read_bytes
;
105 EVP_EncryptUpdate(aes_ctx
, encbuf
, &outlen
, &readbuf
[0], BUFSIZE
);
106 fwrite(&encbuf
, 1, BUFSIZE
, output_file
);
108 EVP_DigestUpdate(digest_post
, &encbuf
[0], BUFSIZE
);
111 // handle last block of data (read_bytes < BUFSIZE)
112 EVP_DigestUpdate(digest_before
, &readbuf
[0], read_bytes
);
113 read_total
+= read_bytes
;
115 pad_len
= AES_BLOCK_SIZE
- (read_total
% AES_BLOCK_SIZE
);
117 pad_len
= AES_BLOCK_SIZE
;
118 memset(&readbuf
[read_bytes
], 0, pad_len
);
120 EVP_EncryptUpdate(aes_ctx
, encbuf
, &outlen
, &readbuf
[0], read_bytes
+ pad_len
);
121 EVP_CIPHER_CTX_free(aes_ctx
);
122 fwrite(&encbuf
, 1, read_bytes
+ pad_len
, output_file
);
124 EVP_DigestUpdate(digest_post
, &encbuf
[0], read_bytes
+ pad_len
);
127 payload_length_before
= read_total
;
128 printf("\npayload_length_before: %li\n", read_total
);
130 // copy digest state, since we need another one with vendor key appended
131 EVP_MD_CTX_copy_ex(digest_vendor
, digest_before
);
133 EVP_DigestFinal_ex(digest_before
, &md_before
[0], NULL
);
134 EVP_MD_CTX_free(digest_before
);
136 printf("\ndigest_before: ");
137 for (i
= 0; i
< SHA512_DIGEST_LENGTH
; i
++)
138 printf("%02x", md_before
[i
]);
140 EVP_DigestUpdate(digest_vendor
, &vendor_key
[0], AES_BLOCK_SIZE
);
141 EVP_DigestFinal_ex(digest_vendor
, &md_vendor
[0], NULL
);
142 EVP_MD_CTX_free(digest_vendor
);
144 printf("\ndigest_vendor: ");
145 for (i
= 0; i
< SHA512_DIGEST_LENGTH
; i
++)
146 printf("%02x", md_vendor
[i
]);
148 EVP_DigestFinal_ex(digest_post
, &md_post
[0], NULL
);
149 EVP_MD_CTX_free(digest_post
);
151 printf("\ndigest_post: ");
152 for (i
= 0; i
< SHA512_DIGEST_LENGTH
; i
++)
153 printf("%02x", md_post
[i
]);
155 fwrite(&footer
, 1, 5, output_file
);
157 // go back to file header and write all the digests and signatures
158 fseek(output_file
, 0, SEEK_SET
);
160 fwrite(HEAD_MAGIC
, 1, HEAD_MAGIC_LEN
, output_file
);
162 // write payload length before
163 sizebuf
= htonl(payload_length_before
);
164 fwrite((char *) &sizebuf
, 1, 4, output_file
);
166 // write payload length post
167 payload_length_before
+= pad_len
;
168 sizebuf
= htonl(payload_length_before
);
169 fwrite((char *) &sizebuf
, 1, 4, output_file
);
171 // write salt and digests
172 fwrite(salt
, 1, AES_BLOCK_SIZE
, output_file
);
173 fwrite(&md_vendor
[0], 1, SHA512_DIGEST_LENGTH
, output_file
);
174 fwrite(&md_before
[0], 1, SHA512_DIGEST_LENGTH
, output_file
);
175 fwrite(&md_post
[0], 1, SHA512_DIGEST_LENGTH
, output_file
);
177 // zero-fill rsa_pub field, unused in header
178 memset(sigret
, 0, RSA_KEY_LENGTH_BYTES
);
179 fwrite(&sigret
[0], 1, RSA_KEY_LENGTH_BYTES
, output_file
);
182 EVP_PKEY_sign(rsa_ctx
, &sigret
[0], &siglen
, &md_before
[0], SHA512_DIGEST_LENGTH
);
183 printf("\nsigned before:\n");
184 for (i
= 0; i
< RSA_KEY_LENGTH_BYTES
; i
++)
185 printf("%02x", sigret
[i
]);
186 fwrite(&sigret
[0], 1, RSA_KEY_LENGTH_BYTES
, output_file
);
189 EVP_PKEY_sign(rsa_ctx
, &sigret
[0], &siglen
, &md_post
[0], SHA512_DIGEST_LENGTH
);
190 printf("\nsigned post:\n");
191 for (i
= 0; i
< RSA_KEY_LENGTH_BYTES
; i
++)
192 printf("%02x", sigret
[i
]);
193 fwrite(&sigret
[0], 1, RSA_KEY_LENGTH_BYTES
, output_file
);
200 void image_decrypt(void)
203 uint32_t payload_length_before
, payload_length_post
, pad_len
;
204 char salt
[AES_BLOCK_SIZE
];
205 char md_vendor
[SHA512_DIGEST_LENGTH
];
206 char md_before
[SHA512_DIGEST_LENGTH
];
207 char md_post
[SHA512_DIGEST_LENGTH
];
208 EVP_PKEY
*signing_key
;
209 EVP_PKEY_CTX
*rsa_ctx
;
210 unsigned char rsa_sign_before
[RSA_KEY_LENGTH_BYTES
];
211 unsigned char rsa_sign_post
[RSA_KEY_LENGTH_BYTES
];
212 unsigned char md_post_actual
[SHA512_DIGEST_LENGTH
];
213 unsigned char md_before_actual
[SHA512_DIGEST_LENGTH
];
214 unsigned char md_vendor_actual
[SHA512_DIGEST_LENGTH
];
215 const EVP_MD
*sha512
;
216 EVP_MD_CTX
*digest_before
;
217 EVP_MD_CTX
*digest_post
;
218 EVP_MD_CTX
*digest_vendor
;
220 printf("\ndecrypt mode\n");
222 if (fread(&magic
, 1, HEAD_MAGIC_LEN
, input_file
) == 0)
224 if (strncmp(magic
, HEAD_MAGIC
, HEAD_MAGIC_LEN
) != 0) {
225 fprintf(stderr
, "Input File header magic does not match '%s'.\n"
226 "Maybe this file is not encrypted?\n", HEAD_MAGIC
);
230 if (fread((char *) &payload_length_before
, 1, 4, input_file
) == 0)
232 if (fread((char *) &payload_length_post
, 1, 4, input_file
) == 0)
234 payload_length_before
= ntohl(payload_length_before
);
235 payload_length_post
= ntohl(payload_length_post
);
237 if (fread(salt
, 1, AES_BLOCK_SIZE
, input_file
) == 0)
239 if (fread(md_vendor
, 1, SHA512_DIGEST_LENGTH
, input_file
) == 0)
241 if (fread(md_before
, 1, SHA512_DIGEST_LENGTH
, input_file
) == 0)
243 if (fread(md_post
, 1, SHA512_DIGEST_LENGTH
, input_file
) == 0)
247 if (fread(readbuf
, 1, RSA_KEY_LENGTH_BYTES
, input_file
) == 0)
250 if (fread(rsa_sign_before
, 1, RSA_KEY_LENGTH_BYTES
, input_file
) == 0)
252 if (fread(rsa_sign_post
, 1, RSA_KEY_LENGTH_BYTES
, input_file
) == 0)
255 // file should be at position HEADER_LEN now, start AES decryption
256 digest_before
= EVP_MD_CTX_new();
257 digest_post
= EVP_MD_CTX_new();
258 digest_vendor
= EVP_MD_CTX_new();
259 sha512
= EVP_sha512();
260 EVP_DigestInit_ex(digest_before
, sha512
, NULL
);
261 EVP_DigestInit_ex(digest_post
, sha512
, NULL
);
262 EVP_DigestInit_ex(digest_vendor
, sha512
, NULL
);
264 memcpy(&aes_iv
, &salt
, AES_BLOCK_SIZE
);
265 aes_ctx
= EVP_CIPHER_CTX_new();
266 EVP_DecryptInit_ex(aes_ctx
, aes128
, NULL
, &vendor_key
[0], aes_iv
);
267 EVP_CIPHER_CTX_set_padding(aes_ctx
, 0);
269 pad_len
= payload_length_post
- payload_length_before
;
271 while (read_total
< payload_length_post
) {
272 if (read_total
+ BUFSIZE
<= payload_length_post
)
273 read_bytes
= fread(&readbuf
, 1, BUFSIZE
, input_file
);
275 read_bytes
= fread(&readbuf
, 1, payload_length_post
- read_total
, \
278 read_total
+= read_bytes
;
280 EVP_DigestUpdate(digest_post
, &readbuf
[0], read_bytes
);
282 EVP_DecryptUpdate(aes_ctx
, encbuf
, &outlen
, &readbuf
[0], read_bytes
);
284 // only update digest_before until payload_length_before,
285 // do not hash decrypted padding
286 if (read_total
> payload_length_before
) {
287 // only calc hash for data before padding
288 EVP_DigestUpdate(digest_before
, &encbuf
[0], read_bytes
- pad_len
);
289 fwrite(&encbuf
[0], 1, read_bytes
- pad_len
, output_file
);
291 // copy digest state, since we need another one with vendor key appended
292 EVP_MD_CTX_copy_ex(digest_vendor
, digest_before
);
295 EVP_DigestUpdate(digest_vendor
, &vendor_key
[0], AES_BLOCK_SIZE
);
297 // calc hash for all of read_bytes
298 EVP_DigestUpdate(digest_before
, &encbuf
[0], read_bytes
);
299 fwrite(&encbuf
[0], 1, read_bytes
, output_file
);
305 EVP_CIPHER_CTX_free(aes_ctx
);
307 EVP_DigestFinal_ex(digest_post
, &md_post_actual
[0], NULL
);
308 EVP_MD_CTX_free(digest_post
);
310 printf("\ndigest_post: ");
311 for (i
= 0; i
< SHA512_DIGEST_LENGTH
; i
++)
312 printf("%02x", md_post_actual
[i
]);
314 if (strncmp(md_post
, (char *) md_post_actual
, SHA512_DIGEST_LENGTH
) != 0) {
315 fprintf(stderr
, "SHA512 post does not match file contents.\n");
319 EVP_DigestFinal_ex(digest_before
, &md_before_actual
[0], NULL
);
320 EVP_MD_CTX_free(digest_before
);
322 printf("\ndigest_before: ");
323 for (i
= 0; i
< SHA512_DIGEST_LENGTH
; i
++)
324 printf("%02x", md_before_actual
[i
]);
326 if (strncmp(md_before
, (char *) md_before_actual
, SHA512_DIGEST_LENGTH
) != 0) {
327 fprintf(stderr
, "SHA512 before does not match decrypted payload.\n");
331 EVP_DigestFinal_ex(digest_vendor
, &md_vendor_actual
[0], NULL
);
332 EVP_MD_CTX_free(digest_vendor
);
334 printf("\ndigest_vendor: ");
335 for (i
= 0; i
< SHA512_DIGEST_LENGTH
; i
++)
336 printf("%02x", md_vendor_actual
[i
]);
338 if (strncmp(md_vendor
, (char *) md_vendor_actual
, SHA512_DIGEST_LENGTH
) != 0) {
339 fprintf(stderr
, "SHA512 vendor does not match decrypted payload padded" \
340 " with vendor key.\n");
344 signing_key
= PEM_read_bio_PrivateKey(rsa_private_bio
, NULL
, pass_cb
, NULL
);
345 rsa_ctx
= EVP_PKEY_CTX_new(signing_key
, NULL
);
346 EVP_PKEY_verify_init(rsa_ctx
);
347 EVP_PKEY_CTX_set_signature_md(rsa_ctx
, sha512
);
349 if (EVP_PKEY_verify(rsa_ctx
, &rsa_sign_before
[0], RSA_KEY_LENGTH_BYTES
, \
350 &md_before_actual
[0], SHA512_DIGEST_LENGTH
)) {
351 printf("\nsignature before verification success");
353 fprintf(stderr
, "Signature before verification failed.\nThe decrypted" \
354 " image file may however be flashable via bootloader recovery.\n");
357 if (EVP_PKEY_verify(rsa_ctx
, &rsa_sign_post
[0], RSA_KEY_LENGTH_BYTES
, \
358 &md_post_actual
[0], SHA512_DIGEST_LENGTH
)) {
359 printf("\nsignature post verification success");
361 fprintf(stderr
, "Signature post verification failed.\nThe decrypted" \
362 " image file may however be flashable via bootloader recovery.\n");
370 fprintf(stderr
, "Error reading header fields from input file.\n");
378 generate legacy vendor key for COVR-C1200, COVR-P2500, DIR-882, DIR-2660, ...
379 decrypt ciphertext key2 using aes128 with key1 and iv, write result to *vkey
381 void generate_vendorkey_legacy(unsigned char *vkey
)
384 memcpy(&aes_iv
, &iv
, AES_BLOCK_SIZE
);
385 aes_ctx
= EVP_CIPHER_CTX_new();
386 EVP_DecryptInit_ex(aes_ctx
, aes128
, NULL
, &key1
[0], &aes_iv
[0]);
387 EVP_CIPHER_CTX_set_padding(aes_ctx
, 0);
388 EVP_DecryptUpdate(aes_ctx
, vkey
, &outlen
, &key2
[0], AES_BLOCK_SIZE
);
389 EVP_CIPHER_CTX_free(aes_ctx
);
393 helper function for generate_vendorkey_dimgkey()
394 deinterleave input in chunks of 8 bytes according to pattern,
395 last block shorter than 8 bytes is appended in reverse order
397 void deinterleave(unsigned char *enk
, size_t len
, unsigned char *vkey
)
399 unsigned char i
, pattern
= 0;
401 while (len
>= INTERLEAVE_BLOCK_SIZE
)
403 for (i
= 0; i
< INTERLEAVE_BLOCK_SIZE
; i
++)
404 *(vkey
+ i
) = *(enk
+ interleaving_pattern
[pattern
][i
]);
406 vkey
+= INTERLEAVE_BLOCK_SIZE
;
407 enk
+= INTERLEAVE_BLOCK_SIZE
;
408 len
-= INTERLEAVE_BLOCK_SIZE
;
410 if (pattern
++ >= INTERLEAVE_BLOCK_SIZE
)
414 for (i
= 0; i
< len
; i
++)
415 *(vkey
+ i
) = *(enk
+ (len
- i
- 1));
419 generate vendor key for COVR-X1860, DIR-X3260, ...
420 base64 decode enk, pass to deinterleave, result will be in *vkey
422 void generate_vendorkey_dimgkey(const unsigned char *enk
, size_t len
, unsigned char *vkey
)
424 unsigned char *decode_buf
= malloc(3 * (len
/ 4));
426 EVP_ENCODE_CTX
*base64_ctx
= EVP_ENCODE_CTX_new();
427 EVP_DecodeInit(base64_ctx
);
428 EVP_DecodeUpdate(base64_ctx
, decode_buf
, &outlen
, enk
, len
);
429 EVP_DecodeFinal(base64_ctx
, decode_buf
+ outlen
, &outlen
);
430 EVP_ENCODE_CTX_free(base64_ctx
);
432 // limit deinterleaving output to first 16 bytes
433 deinterleave(decode_buf
, AES_BLOCK_SIZE
, vkey
);
436 int main(int argc
, char **argv
)
438 if (argc
< 3 || argc
> 5) {
439 fprintf(stderr
, "Usage:\n"
440 "\tdlink-sge-image DEVICE_MODEL infile outfile [-d: decrypt]\n\n"
441 "DEVICE_MODEL can be any of:\n"
452 "Any other value will default to COVR-C1200/P2500/DIR-8xx keys\n"
453 "which may work to decrypt images for several further devices,\n"
454 "however there are currently no private keys known that would\n"
455 "allow for signing images to be used for flashing those devices.\n\n"
460 input_file
= fopen(argv
[2], "rb");
461 if (input_file
== NULL
) {
462 fprintf(stderr
, "Input File %s could not be opened.\n", argv
[2]);
466 output_file
= fopen(argv
[3], "wb");
467 if (input_file
== NULL
) {
468 fprintf(stderr
, "Output File %s could not be opened.\n", argv
[3]);
473 aes128
= EVP_aes_128_cbc();
475 if (strncmp(argv
[1], "COVR-X1860", 10) == 0)
477 generate_vendorkey_dimgkey(enk_covrx1860
, sizeof(enk_covrx1860
), &vendor_key
[0]);
478 rsa_private_bio
= BIO_new_mem_buf(key_covrx1860_pem
, -1);
480 else if (strncmp(argv
[1], "DIR-X3260", 9) == 0)
482 generate_vendorkey_dimgkey(enk_dirx3260
, sizeof(enk_dirx3260
), &vendor_key
[0]);
483 rsa_private_bio
= BIO_new_mem_buf(key_dirx3260_pem
, -1);
485 else if (strncmp(argv
[1], "DIR-1260", 8) == 0)
487 generate_vendorkey_legacy(&vendor_key
[0]);
488 rsa_private_bio
= BIO_new_mem_buf(key_dir1260_pem
, -1);
490 else if (strncmp(argv
[1], "DIR-2150", 8) == 0)
492 generate_vendorkey_legacy(&vendor_key
[0]);
493 rsa_private_bio
= BIO_new_mem_buf(key_dir2150_pem
, -1);
497 /* COVR-C1200, COVR-P2500, DIR-853, DIR-867, DIR-878, DIR-882, DIR-1935 */
498 generate_vendorkey_legacy(&vendor_key
[0]);
499 rsa_private_bio
= BIO_new_mem_buf(key_legacy_pem
, -1);
502 printf("\nvendor_key: ");
503 for (i
= 0; i
< AES_BLOCK_SIZE
; i
++)
504 printf("%02x", vendor_key
[i
]);
506 if (argc
== 5 && strncmp(argv
[4], "-d", 2) == 0)