• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firmware-utils/src/dlink-sge-image.c

  1 // SPDX-License-Identifier: GPL-2.0-or-later OR MIT
  2 /*
  3  * Copyright (C) 2023 Sebastian Schaper <openwrt@sebastianschaper.net>
  4  *
  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, ...
  7  *
  8  * Usage:
  9  *   ./dlink-sge-image DEVICE_MODEL infile outfile [-d: decrypt]
 10  *
 11  */
 12 
 13 #include "dlink-sge-image.h"
 14 
 15 #include <openssl/evp.h>
 16 #include <openssl/pem.h>
 17 
 18 #include <arpa/inet.h>
 19 #include <stdio.h>
 20 #include <stdlib.h>
 21 #include <string.h>
 22 
 23 #define BUFSIZE         4096
 24 
 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
 31 
 32 unsigned char aes_iv[AES_BLOCK_SIZE];
 33 
 34 unsigned char readbuf[BUFSIZE];
 35 unsigned char encbuf[BUFSIZE];
 36 
 37 unsigned int read_bytes;
 38 unsigned long read_total;
 39 unsigned int i;
 40 
 41 unsigned char vendor_key[AES_BLOCK_SIZE];
 42 BIO *rsa_private_bio;
 43 const EVP_CIPHER *aes128;
 44 EVP_CIPHER_CTX *aes_ctx;
 45 
 46 FILE *input_file;
 47 FILE *output_file;
 48 
 49 int pass_cb(char *buf, int size, int rwflag, void *u)
 50 {
 51     char *tmp = "12345678";
 52     size_t len = strlen(tmp);
 53 
 54     if (len > size)
 55         len = size;
 56     memcpy(buf, tmp, len);
 57     return len;
 58 }
 59 
 60 void image_encrypt(void)
 61 {
 62         char buf[HEADER_LEN];
 63         const EVP_MD *sha512;
 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];
 74         size_t siglen;
 75         char footer[] = {0x00, 0x00, 0x00, 0x00, 0x30};
 76 
 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);
 88 
 89         signing_key = PEM_read_bio_PrivateKey(rsa_private_bio, NULL, pass_cb, NULL);
 90         rsa_ctx = EVP_PKEY_CTX_new(signing_key, NULL);
 91 
 92         EVP_PKEY_sign_init(rsa_ctx);
 93         EVP_PKEY_CTX_set_signature_md(rsa_ctx, sha512);
 94 
 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);
 99         int outlen;
100 
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;
104 
105                 EVP_EncryptUpdate(aes_ctx, encbuf, &outlen, &readbuf[0], BUFSIZE);
106                 fwrite(&encbuf, 1, BUFSIZE, output_file);
107 
108                 EVP_DigestUpdate(digest_post, &encbuf[0], BUFSIZE);
109         }
110 
111         // handle last block of data (read_bytes < BUFSIZE)
112         EVP_DigestUpdate(digest_before, &readbuf[0], read_bytes);
113         read_total += read_bytes;
114 
115         pad_len = AES_BLOCK_SIZE - (read_total % AES_BLOCK_SIZE);
116         if (pad_len == 0)
117                 pad_len = AES_BLOCK_SIZE;
118         memset(&readbuf[read_bytes], 0, pad_len);
119 
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);
123 
124         EVP_DigestUpdate(digest_post, &encbuf[0], read_bytes + pad_len);
125 
126         fclose(input_file);
127         payload_length_before = read_total;
128         printf("\npayload_length_before: %li\n", read_total);
129 
130         // copy digest state, since we need another one with vendor key appended
131         EVP_MD_CTX_copy_ex(digest_vendor, digest_before);
132 
133         EVP_DigestFinal_ex(digest_before, &md_before[0], NULL);
134         EVP_MD_CTX_free(digest_before);
135 
136         printf("\ndigest_before: ");
137         for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
138                 printf("%02x", md_before[i]);
139 
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);
143 
144         printf("\ndigest_vendor: ");
145         for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
146                 printf("%02x", md_vendor[i]);
147 
148         EVP_DigestFinal_ex(digest_post, &md_post[0], NULL);
149         EVP_MD_CTX_free(digest_post);
150 
151         printf("\ndigest_post: ");
152         for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
153                 printf("%02x", md_post[i]);
154 
155         fwrite(&footer, 1, 5, output_file);
156 
157         // go back to file header and write all the digests and signatures
158         fseek(output_file, 0, SEEK_SET);
159 
160         fwrite(HEAD_MAGIC, 1, HEAD_MAGIC_LEN, output_file);
161 
162         // write payload length before
163         sizebuf = htonl(payload_length_before);
164         fwrite((char *) &sizebuf, 1, 4, output_file);
165 
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);
170 
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);
176 
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);
180 
181         // sign md_before
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);
187 
188         // sign md_post
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);
194 
195         printf("\n");
196 
197         fclose(output_file);
198 }
199 
200 void image_decrypt(void)
201 {
202         char magic[4];
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;
219 
220         printf("\ndecrypt mode\n");
221 
222         if (fread(&magic, 1, HEAD_MAGIC_LEN, input_file) == 0)
223                 goto error_read;
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);
227                 goto error;
228         }
229 
230         if (fread((char *) &payload_length_before, 1, 4, input_file) == 0)
231                 goto error_read;
232         if (fread((char *) &payload_length_post, 1, 4, input_file) == 0)
233                 goto error_read;
234         payload_length_before = ntohl(payload_length_before);
235         payload_length_post   = ntohl(payload_length_post);
236 
237         if (fread(salt, 1, AES_BLOCK_SIZE, input_file) == 0)
238                 goto error_read;
239         if (fread(md_vendor, 1, SHA512_DIGEST_LENGTH, input_file) == 0)
240                 goto error_read;
241         if (fread(md_before, 1, SHA512_DIGEST_LENGTH, input_file) == 0)
242                 goto error_read;
243         if (fread(md_post, 1, SHA512_DIGEST_LENGTH, input_file) == 0)
244                 goto error_read;
245 
246         // skip rsa_pub
247         if (fread(readbuf, 1, RSA_KEY_LENGTH_BYTES, input_file) == 0)
248                 goto error_read;
249 
250         if (fread(rsa_sign_before, 1, RSA_KEY_LENGTH_BYTES, input_file) == 0)
251                 goto error_read;
252         if (fread(rsa_sign_post, 1, RSA_KEY_LENGTH_BYTES, input_file) == 0)
253                 goto error_read;
254 
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);
263 
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);
268         int outlen;
269         pad_len = payload_length_post - payload_length_before;
270 
271         while (read_total < payload_length_post) {
272                 if (read_total + BUFSIZE <= payload_length_post)
273                         read_bytes = fread(&readbuf, 1, BUFSIZE, input_file);
274                 else
275                         read_bytes = fread(&readbuf, 1, payload_length_post - read_total, \
276                                 input_file);
277 
278                 read_total += read_bytes;
279 
280                 EVP_DigestUpdate(digest_post, &readbuf[0], read_bytes);
281 
282                 EVP_DecryptUpdate(aes_ctx, encbuf, &outlen, &readbuf[0], read_bytes);
283 
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);
290 
291                         // copy digest state, since we need another one with vendor key appended
292                         EVP_MD_CTX_copy_ex(digest_vendor, digest_before);
293 
294                         // append vendor_key
295                         EVP_DigestUpdate(digest_vendor, &vendor_key[0], AES_BLOCK_SIZE);
296                 } else {
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);
300                 }
301         }
302 
303         fclose(input_file);
304         fclose(output_file);
305         EVP_CIPHER_CTX_free(aes_ctx);
306 
307         EVP_DigestFinal_ex(digest_post, &md_post_actual[0], NULL);
308         EVP_MD_CTX_free(digest_post);
309 
310         printf("\ndigest_post: ");
311         for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
312                 printf("%02x", md_post_actual[i]);
313 
314         if (strncmp(md_post, (char *) md_post_actual, SHA512_DIGEST_LENGTH) != 0) {
315                 fprintf(stderr, "SHA512 post does not match file contents.\n");
316                 goto error;
317         }
318 
319         EVP_DigestFinal_ex(digest_before, &md_before_actual[0], NULL);
320         EVP_MD_CTX_free(digest_before);
321 
322         printf("\ndigest_before: ");
323         for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
324                 printf("%02x", md_before_actual[i]);
325 
326         if (strncmp(md_before, (char *) md_before_actual, SHA512_DIGEST_LENGTH) != 0) {
327                 fprintf(stderr, "SHA512 before does not match decrypted payload.\n");
328                 goto error;
329         }
330 
331         EVP_DigestFinal_ex(digest_vendor, &md_vendor_actual[0], NULL);
332         EVP_MD_CTX_free(digest_vendor);
333 
334         printf("\ndigest_vendor: ");
335         for (i = 0; i < SHA512_DIGEST_LENGTH; i++)
336                 printf("%02x", md_vendor_actual[i]);
337 
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");
341                 goto error;
342         }
343 
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);
348 
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");
352         } else {
353                 fprintf(stderr, "Signature before verification failed.\nThe decrypted" \
354                         " image file may however be flashable via bootloader recovery.\n");
355         }
356 
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");
360         } else {
361                 fprintf(stderr, "Signature post verification failed.\nThe decrypted" \
362                         " image file may however be flashable via bootloader recovery.\n");
363         }
364 
365         printf("\n");
366 
367         return;
368 
369 error_read:
370         fprintf(stderr, "Error reading header fields from input file.\n");
371 error:
372         fclose(input_file);
373         fclose(output_file);
374         exit(1);
375 }
376 
377 /*
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
380 */
381 void generate_vendorkey_legacy(unsigned char *vkey)
382 {
383         int outlen;
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);
390 }
391 
392 /*
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
396 */
397 void deinterleave(unsigned char *enk, size_t len, unsigned char *vkey)
398 {
399         unsigned char i, pattern = 0;
400 
401         while (len >= INTERLEAVE_BLOCK_SIZE)
402         {
403                 for (i = 0; i < INTERLEAVE_BLOCK_SIZE; i++)
404                         *(vkey + i) = *(enk + interleaving_pattern[pattern][i]);
405 
406                 vkey += INTERLEAVE_BLOCK_SIZE;
407                 enk += INTERLEAVE_BLOCK_SIZE;
408                 len -= INTERLEAVE_BLOCK_SIZE;
409 
410                 if (pattern++ >= INTERLEAVE_BLOCK_SIZE)
411                         pattern = 0;
412         }
413 
414         for (i = 0; i < len; i++)
415                 *(vkey + i) = *(enk + (len - i - 1));
416 }
417 
418 /*
419   generate vendor key for COVR-X1860, DIR-X3260, ...
420   base64 decode enk, pass to deinterleave, result will be in *vkey
421 */
422 void generate_vendorkey_dimgkey(const unsigned char *enk, size_t len, unsigned char *vkey)
423 {
424         unsigned char *decode_buf = malloc(3 * (len / 4));
425         int outlen;
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);
431 
432         // limit deinterleaving output to first 16 bytes
433         deinterleave(decode_buf, AES_BLOCK_SIZE, vkey);
434 }
435 
436 int main(int argc, char **argv)
437 {
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"
442                         "\tCOVR-C1200\n"
443                         "\tCOVR-P2500\n"
444                         "\tCOVR-X1860\n"
445                         "\tDIR-853\n"
446                         "\tDIR-867\n"
447                         "\tDIR-878\n"
448                         "\tDIR-882\n"
449                         "\tDIR-1935\n"
450                         "\tDIR-2150\n"
451                         "\tDIR-X3260\n\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"
456                         );
457                 exit(1);
458         }
459 
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]);
463                 exit(1);
464         }
465 
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]);
469                 fclose(input_file);
470                 exit(1);
471         }
472 
473         aes128 = EVP_aes_128_cbc();
474 
475         if (strncmp(argv[1], "COVR-X1860", 10) == 0)
476         {
477                 generate_vendorkey_dimgkey(enk_covrx1860, sizeof(enk_covrx1860), &vendor_key[0]);
478                 rsa_private_bio = BIO_new_mem_buf(key_covrx1860_pem, -1);
479         }
480         else if (strncmp(argv[1], "DIR-X3260", 9) == 0)
481         {
482                 generate_vendorkey_dimgkey(enk_dirx3260, sizeof(enk_dirx3260), &vendor_key[0]);
483                 rsa_private_bio = BIO_new_mem_buf(key_dirx3260_pem, -1);
484         }
485         else if (strncmp(argv[1], "DIR-1260", 8) == 0)
486         {
487                 generate_vendorkey_legacy(&vendor_key[0]);
488                 rsa_private_bio = BIO_new_mem_buf(key_dir1260_pem, -1);
489         }
490         else if (strncmp(argv[1], "DIR-2150", 8) == 0)
491         {
492                 generate_vendorkey_legacy(&vendor_key[0]);
493                 rsa_private_bio = BIO_new_mem_buf(key_dir2150_pem, -1);
494         }
495         else
496         {
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);
500         }
501 
502         printf("\nvendor_key: ");
503         for (i = 0; i < AES_BLOCK_SIZE; i++)
504                 printf("%02x", vendor_key[i]);
505 
506         if (argc == 5 && strncmp(argv[4], "-d", 2) == 0)
507                 image_decrypt();
508         else
509                 image_encrypt();
510 }
511 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt