Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
compal-decrypt.c
/*
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;
}
@GMMan

This comment has been minimized.

Copy link

@GMMan GMMan commented Sep 22, 2020

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:

bf 13 80 38 45 92 da 08
4c 4f 37 c2 34 6e 9d c1
2a e9 76 94 3b 5d 1f 34

IV is 8 bytes of zeroes (CBC mode of operation):

00 00 00 00 00 00 00 00

This should work on firmware based on Texas Instruments/Intel Puma SDKs. This includes some Motorola/Arris modems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.