Last active
July 3, 2023 23:02
-
-
Save danielkucera/fa9df248f6e0ea5dc2908743360d5cb7 to your computer and use it in GitHub Desktop.
compal-decrypt.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Program for decrypting Compal CH7465LG private key | |
Compilation: | |
gcc -o compal-decrypt compal-decrypt.c -lcrypto | |
Running: | |
./compal-decrypt | |
usage: ./compal-decrypt <infile> <outfile> | |
Author: Carlo Meijer <carlo@youcontent.nl> | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/stat.h> | |
#include <string.h> | |
#include <openssl/des.h> | |
static unsigned char g_abKey[] = { | |
0xBE, 0x26, 0x01, 0xC4, 0x52, 0x76, 0x84, 0x4C, 0x9C, 0xDE, | |
0x13, 0x4D, 0xE7, 0x60, 0x2B, 0xD1, 0xDC, 0xA3, 0xAB, 0x87, | |
0x9A | |
}; | |
int sprinkle(unsigned char *a1, unsigned char *a2) // straight from IDA decompiler | |
{ | |
int result; // r0 | |
signed int v3; // r3 | |
int v4; // r2 | |
*a2 = *a1 & 0xFE; | |
a2[1] = (*a1 << 7) + ((a1[1] >> 1) & 0x7E); | |
a2[2] = (a1[1] << 6) + ((a1[2] >> 2) & 0x3E); | |
a2[3] = 0x20 * a1[2] + ((a1[3] >> 3) & 0x1E); | |
a2[4] = 0x10 * a1[3] + ((a1[4] >> 4) & 0xE); | |
a2[5] = 8 * a1[4] + ((a1[5] >> 5) & 6); | |
a2[6] = 4 * a1[5] + ((a1[6] >> 6) & 2); | |
a2[7] = 2 * a1[6]; | |
result = 0; | |
do | |
{ | |
v3 = 1; | |
v4 = (unsigned char)(a2[result] ^ 1); | |
for ( a2[result] = v4; ; v4 = (unsigned char)a2[result] ) | |
{ | |
if ( (v4 >> v3) & 1 ) | |
a2[result] = v4 ^ 1; | |
if ( ++v3 == 8 ) | |
break; | |
} | |
++result; | |
} | |
while ( result != 8 ); | |
return result; | |
} | |
void des3ABC_CBC_decrypt( | |
unsigned char *lpKey1, | |
unsigned char *lpKey2, | |
unsigned char *lpKey3, | |
unsigned char *lpIv, | |
unsigned char *lpInput, | |
unsigned int dwLength, | |
unsigned char *lpOutput | |
) { | |
int v12; // r7 | |
int v13; // r2 | |
int v14; // r2 | |
unsigned char abKey1[8]; // [sp+8h] [bp-40h] | |
unsigned char abKey2[8]; // [sp+10h] [bp-38h] | |
unsigned char abKey3[8]; // [sp+18h] [bp-30h] | |
char v18[40]; // [sp+20h] [bp-28h] | |
DES_key_schedule stKey1, stKey2, stKey3; | |
sprinkle(lpKey1, abKey1); | |
sprinkle(lpKey2, abKey2); | |
sprinkle(lpKey3, abKey3); | |
DES_set_key((DES_cblock *)abKey1, &stKey1); | |
DES_set_key((DES_cblock *)abKey2, &stKey2); | |
DES_set_key((DES_cblock *)abKey3, &stKey3); | |
if ( (dwLength + 7) >> 3 ) | |
{ | |
v12 = 0; | |
do | |
{ | |
DES_ecb3_encrypt((const_DES_cblock *)lpInput, (DES_cblock *)v18, &stKey1, &stKey2, &stKey3, DES_DECRYPT); | |
v13 = 0; | |
if ( v12 ) | |
{ | |
do | |
{ | |
lpOutput[v13] = v18[v13] ^ lpInput[v13 - 8]; | |
++v13; | |
} | |
while ( v13 != 8 ); | |
} | |
else | |
{ | |
do | |
{ | |
lpOutput[v13] = v18[v13] ^ lpIv[v13]; | |
v14 = v13 + 1; | |
if ( v14 == 8 ) | |
break; | |
lpOutput[v14] = v18[v14] ^ lpIv[v14]; | |
v13 = v14 + 1; | |
} | |
while ( v13 != 8 ); | |
} | |
++v12; | |
lpInput += 8; | |
lpOutput += 8; | |
} | |
while ( (dwLength + 7) >> 3 != v12 ); | |
} | |
} | |
int main(int argc, char **argv) { | |
if(argc != 3) { | |
goto _usage; | |
} | |
struct stat stInfile; | |
FILE *hInfile, *hOutfile; | |
unsigned char *lpInput, *lpOutput; | |
unsigned char abIv[8]; | |
memset(abIv, 0, 8); | |
unsigned int dwLength, dwOutLength; | |
hInfile = fopen(argv[1], "rb"); | |
if (hInfile == NULL) { | |
perror(argv[1]); | |
return 1; | |
} | |
fstat(fileno(hInfile), &stInfile); | |
dwLength = stInfile.st_size; | |
lpInput = malloc((dwLength + 7) & ~7); | |
lpOutput = malloc((dwLength + 7) & ~7); | |
fread(lpInput, dwLength, 1, hInfile); | |
if (dwLength & 8) { | |
memset(lpInput + dwLength, 0, 8 - (dwLength & 8)); | |
} | |
des3ABC_CBC_decrypt(g_abKey, &g_abKey[7], &g_abKey[14], abIv, lpInput, dwLength, lpOutput); | |
if (lpOutput[20] != 0x30 || lpOutput[21] != 0x82) { | |
printf ("[-] invalid format\n"); | |
return 1; | |
} | |
dwOutLength = lpOutput[23] + 4 + (lpOutput[22] << 8); | |
hOutfile = fopen(argv[2], "wb"); | |
if (hOutfile == NULL) { | |
perror(argv[2]); | |
return 1; | |
} | |
fwrite(lpOutput + 20, dwOutLength, 1, hOutfile); | |
fclose(hInfile); | |
fclose(hOutfile); | |
free(lpInput); | |
free(lpOutput); | |
printf ("[+] success!\n"); | |
return 0; | |
_usage: | |
printf("usage: %s <infile> <outfile>\n", argv[0]); | |
return 0; | |
} |
Thanks for your work.
Is it possible to make an encryption program using this method?
I'm interested in putting a non-encrypted private key from a different modem to this one.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
High level overview in case anyone stumbles across this:
The 3DES implementation used is very standard. The only thing different from a normal implementation is the keys it takes don't have parity bits. What
sprinkle()
does is expand the key and calculate parity bit for each byte of the expanded key. Other than that, both the 3DES and CBC mode are implemented as you'd expect.The decrypted blob consists of a SHA-1 hash of the key and the DER-encoded PKCS#8 private key. The hash is to the length of the key, not to the end of the file. The check for 0x3082 is for a DER
SEQUENCE
with length specified in 2 bytes. This happens to be what private keys in this application start with. Next two bytes are length, and you add 4 to it to get the size of the entire private key encoding.In case you want to use a regular 3DES implementation, here's the key with the parity bits filled in:
IV is 8 bytes of zeroes (CBC mode of operation):
This should work on firmware based on Texas Instruments/Intel Puma SDKs. This includes some Motorola/Arris modems.