Products > Test Equipment
Sniffing the Rigol's internal I2C bus
Git:
Does anybody have IDA sigs for the compiler and maybe crypto libraries used for the DS firmware please?
Git
zombie28:
I've got some good and bad news. The good news is that I finished implementation of signature verification algorithm and tested its correctness on some 'A'-type license codes. However the bad news is that encryption keys needed by this algorithm are not consistent among different memory dumps of different scopes. It looks like Rigol uses different key sets in 'A' product line, but I don't have enough memory dumps to tell whether it is per scope model, production week or maybe even per single unit. Even ECC public keys are different, so it requires breaking them every time the new key is issued. Fortunately Rigol didn't change elliptic curve parameters, so finding private key is a matter of milliseconds on a decent computer.
zombie28:
--- Quote from: tirulerbach on January 06, 2014, 06:24:31 pm ---How this binary 32 bit return value gets hashed by the ECC-signature verification?
--- End quote ---
I will publish complete algorithm soon.
zombie28:
So here it is:
--- Code: ---//
// Copyright (c) 2014 RIGLOL Technologies, Inc. All Rights Reversed.
// This product includes software developed by the OpenSSL Project
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include "rc5.h"
extern "C"
{
#include "miracl.h"
}
// ECC parameters
char A[] = "2982";
char B[] = "3408";
char P[] = "AEBF94CEE3E707";
char N[] = "AEBF94D5C6AA71";
char Gx[] = "7A3E808599A525";
char Gy[] = "28BE7FAFD2A052";
typedef unsigned char uint8;
typedef unsigned int uint32;
typedef unsigned long long uint64;
#define LICENSE_CODE_LENGTH 28
// sample key sets for different scopes
#if 1
// DS2D1542....
static const uint8 RC5Key1[16] = { 0x3F, 0x57, 0x8E, 0x1C, 0x44, 0x18, 0x34, 0xDD, 0xA5, 0x46, 0x21, 0x36, 0x32, 0x81, 0xFB, 0xCF };
static const uint8 RC5Key2[16] = { 0x14, 0xDC, 0x15, 0xAF, 0xA1, 0x48, 0x3D, 0x7D, 0x6A, 0xC1, 0xDC, 0xA1, 0x79, 0x8D, 0xAA, 0x3E };
static const uint8 XXTEAKey[16] = { 0x39, 0x69, 0xA2, 0x04, 0x55, 0x9C, 0x35, 0x52, 0x90, 0x44, 0xED, 0x85, 0x52, 0x16, 0x13, 0x32 };
char ECCPublicKey[] = "A51BF373712F7D";
#else
// DS2D1543....
static const uint8 RC5Key1[16] = { 0x41, 0x55, 0xBF, 0xD8, 0x2D, 0x42, 0x9E, 0xA6, 0x9B, 0x3E, 0xE7, 0xD7, 0xD5, 0x9C, 0x89, 0x06 };
static const uint8 RC5Key2[16] = { 0xB9, 0xBC, 0x53, 0xD8, 0xB8, 0xCE, 0x6C, 0xE3, 0x59, 0x45, 0x55, 0xAA, 0x89, 0x55, 0x65, 0x43 };
static const uint8 XXTEAKey[16] = { 0x86, 0xF4, 0xA0, 0x93, 0x0B, 0xC7, 0xED, 0x27, 0x6B, 0x2D, 0x6C, 0x2C, 0xE2, 0x93, 0x53, 0x5F };
char ECCPublicKey[] = "A0581020E5C012";
#endif
uint32 DecodeChar(char value)
{
char *charMap = "LRE8YFGHJK9SNBQ36MPVWXAZ2U45TC7D";
char *charPos = strchr(charMap, value);
return charPos == NULL ? 0 : charPos - charMap;
}
uint64 DecodeSignature(uint64 value)
{
uint32 shiftCount = value & 0x0f;
do value >>= 4; while(shiftCount-- > 0);
return value;
}
uint32 DecodeLicenseCode(char *licenseCode, uint64& sig1, uint64& sig2)
{
if(strlen(licenseCode) != LICENSE_CODE_LENGTH)
return 0;
uint8 licenseBits[35];
for(int i = 0, j = 0; i < LICENSE_CODE_LENGTH; i += 4, j += 5)
{
uint32 bitBuffer =
(DecodeChar(licenseCode[i]) << 15) +
(DecodeChar(licenseCode[i+1]) << 10) +
(DecodeChar(licenseCode[i+2]) << 5) +
DecodeChar(licenseCode[i+3]);
licenseBits[j] = bitBuffer >> 16;
licenseBits[j+1] = (bitBuffer >> 12) & 0xf;
licenseBits[j+2] = (bitBuffer >> 8) & 0xf;
licenseBits[j+3] = (bitBuffer >> 4) & 0xf;
licenseBits[j+4] = bitBuffer & 0xf;
}
uint64 RC5Block1 = 0;
uint64 RC5Block2 = 0;
for(int i = 0; i < 16; i++)
{
RC5Block1 |= uint64(licenseBits[i]) << i*4;
RC5Block2 |= uint64(licenseBits[i + 16]) << i*4;
}
RC5_32_KEY RC5Key;
RC5_32_set_key(&RC5Key, 16, RC5Key1, 16);
RC5_32_ecb_encrypt((uint8*)&RC5Block1, (uint8*)&RC5Block1, &RC5Key, 1);
RC5_32_set_key(&RC5Key, 16, RC5Key2, 16);
RC5_32_ecb_encrypt((uint8*)&RC5Block2, (uint8*)&RC5Block2, &RC5Key, 0);
// ECDSA signature
sig1 = DecodeSignature((RC5Block2 >> 8) | (uint64(licenseBits[33]) << 56));
sig2 = DecodeSignature(((RC5Block1 & 0xffffffffffff) << 8) | (RC5Block2 & 0xff) | (uint64(licenseBits[32]) << 56));
// option bits
return uint32(RC5Block1 >> 48) | (uint32(licenseBits[34]) << 16);
}
char* EncodeOptionBits(uint32 optionBits)
{
char *charMap = "XBCNEF8HJ3LMKPZRSAUVWTYQ5D4276G9";
char *optionString = new char[5];
for(int i = 0; i < 4; i++)
optionString[i] = charMap[(optionBits >> ((3-i)*5)) & 0x1f];
optionString[4] = '\0';
return optionString;
}
char* EncodeSerialNumber(char *serialNumber)
{
if(strlen(serialNumber) < 4)
return 0;
uint32 header = *((uint32*)serialNumber);
for(int i = 0; i < 16; i++)
header =
(header << 1) |
(header >> 31) ^
(header >> 30) & 1 ^
(header >> 28) & 1 ^
(header >> 22) & 1 ^
(header >> 13) & 1 ^
(header >> 6) & 1;
char* result = strdup(serialNumber);
*((uint32*)result) = header;
return result;
}
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void XXTEA(uint32 *v, int n, uint32 const key[4])
{
uint32 y, z, sum;
unsigned p, rounds, e;
if (n > 1)
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do {
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
} while (--rounds);
}
else if (n < -1)
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
} while ((sum -= DELTA) != 0);
}
}
uint64 ByteSwap64(uint64 value)
{
uint64 result = 0;
for(int i = 0; i < 8; i++)
{
result = (result << 8) | (value & 0xff);
value >>= 8;
}
return result;
}
bool VerifySignature(uint64 sig1, uint64 sig2, uint8 signatureDataHash[20])
{
mirsys(0x320, 16)->IOBASE = 16;
big p = mirvar(0);
big n = mirvar(0);
big a = mirvar(0);
big b = mirvar(0);
big x = mirvar(0);
big y = mirvar(0);
big lic1 = mirvar(0);
big lic2 = mirvar(0);
big hash = mirvar(0);
big m1 = mirvar(0);
big m2 = mirvar(0);
big v = mirvar(0);
big g = mirvar(0);
epoint *pt1 = epoint_init();
epoint *pt2 = epoint_init();
instr(a, A);
instr(b, B);
instr(p, P);
instr(n, N);
ecurve_init(a, b, p, MR_PROJECTIVE);
instr(x, Gx);
instr(y, Gy);
if (!epoint_set(x, y, 0, pt1))
{
printf("ERR: Point G is invalid\n");
exit(-1);
}
instr(x, ECCPublicKey);
if (!epoint_set(x, x, 1, pt2))
{
printf("ERR: Public key is invalid\n");
exit(-1);
}
bytes_to_big(20, (char*)signatureDataHash, hash);
sig1 = ByteSwap64(sig1);
bytes_to_big(8, (char*)&sig1, lic1);
sig2 = ByteSwap64(sig2);
bytes_to_big(8, (char*)&sig2, lic2);
xgcd(lic2, n, lic2, lic2, lic2);
copy(lic2, g);
mad(hash, g, g, n, n, m1);
mad(lic1, g, g, n, n, m2);
ecurve_mult2(m1, pt1, m2, pt2, pt1);
epoint_get(pt1, v, v);
divide(v, n, n);
return mr_compare(v, lic1) == 0;
}
bool VerifyLicenseCode(char *serialNumber, char *licenseCode)
{
uint64 sig1, sig2;
uint32 optionBits = DecodeLicenseCode(licenseCode, sig1, sig2);
char *optionString = EncodeOptionBits(optionBits);
char *encodedSerialNumber = EncodeSerialNumber(serialNumber);
uint32 optionStringLength = strlen(optionString);
uint32 encodedSerialNumberLength = strlen(encodedSerialNumber);
uint32 signatureDataLength = (encodedSerialNumberLength + optionStringLength + 3) & ~3;
uint8 *signatureData = new uint8[signatureDataLength];
memset(signatureData, 0, signatureDataLength);
memcpy(signatureData, encodedSerialNumber, encodedSerialNumberLength);
memcpy(signatureData + encodedSerialNumberLength, optionString, optionStringLength);
XXTEA((uint32*)signatureData, signatureDataLength >> 2, (uint32*)XXTEAKey);
uint8 hash[20];
sha sh;
shs_init(&sh);
for(int i = 0; i < signatureDataLength; i++)
shs_process(&sh, signatureData[i]);
shs_hash(&sh, (char*)hash);
return VerifySignature(sig1, sig2, hash);
}
--- End code ---
tirulerbach:
--- Quote from: zombie28 on January 06, 2014, 11:50:49 pm ---So here it is:
--- End quote ---
Thank you again, zombie28. Very valuable work. :-+
So now I have to rework my keygen. I was just to about to release a first shot. It will take some more time. Is anybody else writing a keygen, too?
--- Quote from: zombie28 on January 06, 2014, 11:22:37 pm ---However the bad news is that encryption keys needed by this algorithm are not consistent among different memory dumps of different scopes. It looks like Rigol uses different key sets in 'A' product line, but I don't have enough memory dumps to tell whether it is per scope model, production week or maybe even per single unit. Even ECC public keys are different, so it requires breaking them every time the new key is issued.
--- End quote ---
Where are the public keys stored? Flash? Then maybe it's a model dependency.
Navigation
[0] Message Index
[#] Next page
[*] Previous page
Go to full version