As promised here is an implementation of what Rigol does in C using MIRACL.
Except a few tiny bits (see code comments) its 100% what they do.
Should compile on any linux box - RTFM -> file header
/*
** C implementation of rigol ds2k ECC-112 license key check
** version 0.1
** cybernet, 2013 - <cn@warp.at>
**
**
** to compile this you need MIRACL from [url]https://github.com/CertiVox/MIRACL[/url]
** download the master.zip into a new folder and run 'unzip -j -aa -L master.zip'
** then run 'bash linux' to build the miracle.a library
**
** once done compile this c file with:
**
** gcc main.c -I../MIRACL ../MIRACL/miracl.a -o riglol
**
** adapt -I and path to miracl.a to your environment
**
** RUN:
**
** ./riglol ???????-???????-???????-??????? DSA2Aserial
**
** WHAT THIS IS:
**
** an implementation that matches rigols license key check
** except a few deviations which are not yet reversed fully.
**
** WHAT THIS IS NOT:
**
** a keygen, something for scriptkiddies to ask questions about - RTFM instead
**
**
** more info: https://www.eevblog.com/forum/testgear/sniffing-the-rigol's-internal-i2c-bus/
** http://certivox.org/pages/viewpage.action?pageId=7635202
**
**
*/
#include <stdio.h>
#include "miracl.h"
#include <stdlib.h>
#include <string.h>
// primes, ecurve parameters, points from FW v.00.01.00.05 - probably always the same anyhow
unsigned char prime1[]="AEBF94CEE3E707";
unsigned char prime2[]="AEBF94D5C6AA71";
unsigned char curve_a[]="2982";
unsigned char curve_b[]="3408";
unsigned char point1[]="7A3E808599A525";
unsigned char point2[]="8445B2BE29E5C7";
// which chars to skip for option key
char skip_chars[] = { 2, 10, 18, 26 };
// some helper maps to convert key to hex
char map_ee00d0[]={ 0x0, 0x0, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2,
0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x8, 0x9, 0xa, 0xb,
0xc, 0x0, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15,0x16, 0x17 };
char map_20688[]={ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, /* 0-9 = 0x30 */
0x37, 0x37, 0x37, 0x37, 0x37, 0x37 }; /* A-F = 0x37 */
void show_help(void)
{
printf("\n");
printf("./riglol <licensekey> <serial>\n\n");
printf("<licensekey> can be given with or without dashes\n");
printf("<serial> DSA2...\n");
printf("\n");
exit(-1);
}
/*
** convert string to uppercase chars
*/
char *strtoupper(char *str)
{
char *newstr, *p;
p = newstr = (char*) strdup((char*)str);
while((*p++=toupper(*p)));
return newstr;
}
/*
** skip dashes in input string
*/
char *lic_skip_dash(char *str)
{
char *out, *c;
char i=0;
out=calloc(strlen(str)+1,1);
if (!out) return(NULL);
c=str;
i=0;
while (*c)
{
if (*c != '-')
out[i++]=*c++;
else *c++;
}
// printf("out: %s\n", out);
return(out);
}
/*
** return string with skipped 2nd,10th,18th,26th character
*/
char *lic_make_skipped_str(char * lic_raw)
{
char *c;
char i,j,k;
char *lic_skipped;
if (!lic_raw) return(NULL);
lic_skipped=calloc(strlen((char*)lic_raw)+1-sizeof(skip_chars), 1);
if (!lic_skipped) return(NULL);
c=lic_raw;
i=j=k=0;
while (*c)
{
j++;
if (skip_chars[i] == j)
i++;
else lic_skipped[k++]=*c;
c++;
}
return(lic_skipped);
}
/*
** return string of the skipped 2nd,10th,18th,26th character
*/
char *lic_make_skipped_chars(char * lic_raw)
{
char *c;
char i,j,k;
char *lic_skipped;
if (!lic_raw) return(NULL);
lic_skipped=calloc(sizeof(skip_chars)+1, 1);
if (!lic_skipped) return(NULL);
c=lic_raw;
i=j=k=0;
while (*c)
{
j++;
if (skip_chars[i] == j)
{
lic_skipped[k++]=*c;
i++;
}
c++;
}
return(lic_skipped);
}
/*
** conver to hex
*/
char char_to_hex(char i)
{
if ((i >= 'A') && (i <= 'F')) return(i-0x37);
if ((i >= '0') && (i <= '9')) return(i-0x30);
return(0x0);
}
/*
** map the skipped license string into hex bytes
**
*/
char *lic_code_map(char *lic_skipped)
{
char lv1,lv2;
char b1_mapped, b1_shifted, b1_remapped;
char b2_mapped, b2_shifted, b2_remapped;
char b3_mapped, b3_shifted, b3_remapped;
char b4_mapped, b4_shifted, b4_remapped;
char b5_shifted, b5_remapped;
char *lic_skipped_mapbytes;
unsigned int c=0;
lic_skipped_mapbytes=calloc(255, 1);
if (!lic_skipped_mapbytes) return(0);
lv1=lv2=0;
while(lv1 < strlen((char*)lic_skipped))
{
b1_mapped = map_ee00d0[ *(lic_skipped+lv1) - 0x30 ];
b1_shifted = (b1_mapped / 2) & 0xf;
b1_remapped = b1_shifted + map_20688[b1_shifted];
lic_skipped_mapbytes[lv2++]=b1_remapped;
b1_mapped = b1_mapped & 0x1;
b2_mapped = map_ee00d0[ *(lic_skipped+lv1+1) - 0x30 ];
b2_shifted = ((b1_mapped << 0x3) | (b2_mapped / 4)) & 0xF;
b2_remapped = b2_shifted + map_20688[b2_shifted];
lic_skipped_mapbytes[lv2++]=b2_remapped;
b3_mapped = map_ee00d0[ *(lic_skipped+lv1+2) - 0x30 ];
b3_shifted = ((b3_mapped / 8) | ( (b2_mapped & 0x3) << 2 )) & 0xF;
b3_remapped = b3_shifted + map_20688[b3_shifted];
lic_skipped_mapbytes[lv2++]=b3_remapped;
b4_mapped = map_ee00d0[ *(lic_skipped+lv1+3) - 0x30 ];
b4_shifted = ((b4_mapped / 16 ) |((b3_mapped & 0x7) << 0x1)) & 0xf;
b4_remapped = b4_shifted + map_20688[b4_shifted];
lic_skipped_mapbytes[lv2++]=b4_remapped;
b5_shifted = b4_mapped & 0xF;
b5_remapped = b5_shifted + map_20688[b5_shifted];
lic_skipped_mapbytes[lv2++]=b5_remapped;
lv1 = lv1 + 4;
c=c+5;
}
lic_skipped_mapbytes[c-1]=0;
return(lic_skipped_mapbytes);
}
/*
** MAIN
*/
int main(int argc, char *argv[0])
{
miracl *mip;
big p1,p2; // primes
big c1,c2; // curve parameters (A,B)
big tmp; // generic temp bigvar
big lic1,lic2; // the two parts of our license key
epoint *pt1,*pt2; // points on our curve
sha sh;
big sha_hash; // sha1 hash of serial + lic_opt
big m1,m2; // output from mad() func
big v;
big g; // gxcd output
char *lic_raw,*serial; // cmdline input
char *lic_opt,*lic_skipped; // options as given per license key (2nd,10th,18th,26th char)
char *lic_opt_mapbytes; // options in hex format - denotes type of option and the bitflags for the options
char *lic_skipped_mapbytes; // input into the ECC routines
char *lic_hash; // serial + lic_opt string
char *lic_1_mapbytes; // split lic_map_bytes on character 0xe (mapped)
char *lic_2_mapbytes; // second half
int i1,i2;
char *p;
// get our commandline input
// and do some basic checks
if (argc!=3) show_help();
lic_raw=(char*)strtoupper((char*)argv[1]); // make it uppercase
serial=(char*)strtoupper((char*)argv[2]); // make it uppercase
lic_raw=lic_skip_dash(lic_raw); // skip any dashes
if (strlen(lic_raw) < 0x1c) { fprintf(stderr, "license key is to short !\n"); exit(-1); }
if (strlen(lic_raw) > 0x1c) { fprintf(stderr, "license key is to long !\n"); exit(-1); }
if (strlen(serial) != 0xd) { fprintf(stderr, "serial has invalid length !\n"); exit(-1); }
// generate the strings and map the license key to hex
lic_skipped=lic_make_skipped_str(lic_raw); // licensekey minus 2nd,10th,18th,26th char
lic_opt=lic_make_skipped_chars(lic_raw); // licensekey 2nd,10th,18th,26th char
lic_skipped_mapbytes=lic_code_map(lic_skipped); // convert to base 16
lic_opt_mapbytes=lic_code_map(lic_opt); // same for the desired options
// split the lic_skipped_mapbytes
lic_1_mapbytes=strdup(lic_skipped_mapbytes);
i1=(char)(0x0e - char_to_hex(lic_skipped_mapbytes[0xe]));
if ((0xe < i1) || (i1 < 0)) i1=0;
lic_1_mapbytes[i1]=0;
lic_2_mapbytes=strdup(lic_skipped_mapbytes);
i2=(char)(0x1d-char_to_hex(lic_skipped_mapbytes[0x1d]));
if ((0x1d < i2) || (i2 < 0)) i2=0xf;
lic_2_mapbytes[i2]=0;
p=strdup(lic_2_mapbytes+0xf);
lic_2_mapbytes=strdup(p);
lic_hash=calloc(strlen(serial)+strlen(lic_opt)+1,1);
strcpy(lic_hash, serial);
strcat(lic_hash, lic_opt);
// our strings, inputs to the ECC/sha crypto part
printf("\nserial: %s\n", serial);
printf("license-key: %s\n", lic_raw);
printf("license-skipped: %s (%s)\n", lic_skipped, lic_skipped_mapbytes);
printf("license-1-mapped: %s\n", lic_1_mapbytes);
printf("license-2-mapped: %s\n", lic_2_mapbytes);
printf("license-options: %s (%s)\n", lic_opt, lic_opt_mapbytes);
printf("serial&options: %s\n", lic_hash);
// initialize MIRACL
mip=mirsys(0x320,0x10); // 800 bit precison, 16=hexmode
// initialize "big" variables used by MIRACL
tmp=mirvar(0);
p1=mirvar(0);
p2=mirvar(0);
c1=mirvar(0);
c2=mirvar(0);
lic1=mirvar(0);
lic2=mirvar(0);
sha_hash=mirvar(0);
m1=mirvar(0);
m2=mirvar(0);
v=mirvar(0);
g=mirvar(0);
// initalize the epoints used
pt1=epoint_init();
pt2=epoint_init();
// setup static items like rigol (primes, the elliptic curve)
instr(p1, prime1);
instr(p2, prime2);
instr(c1, curve_a);
instr(c2, curve_b);
// initialize ecurve and set the 2 points
ecurve_init(c1, c2, p1, MR_PROJECTIVE);
instr(tmp, point1);
if (!epoint_set(tmp, tmp, 0x0, pt1))
{
printf("ERR: '%s' not a point on the given curve\n", point1);
exit(-1);
}
instr(tmp, point2);
if (!epoint_set(tmp, tmp, 0x1, pt2))
{
printf("ERR: '%s' not a point on the given curve\n", point2);
exit(-1);
}
/*
* at this point initialization of the ECC crypto system is completed
** this is done only once on a rigol per powerup cycle
**
** now comes the part that is run for every key check
*/
// generate the sha1 hash for the serial+option string
shs_init(&sh);
p=lic_hash;
while(*p)
{
shs_process(&sh,*p);
p++;
}
shs_hash(&sh, lic_hash);
bytes_to_big(20, lic_hash, sha_hash);
mip->IOBASE = 16;
printf("sha1(serial&opts) "); cotnum(sha_hash, stdout); printf("\n");
// convert our 2 halves of the mapped license key into big's
instr(lic1, lic_1_mapbytes);
instr(lic2, lic_2_mapbytes);
// make sure lic1 and lic2 are not our p2 prime ? why ?
if (mr_compare(lic1, p2) >= 0x0)
{
printf("ERR: lic1 compare\n");
}
if (mr_compare(lic2, p2) >= 0x0)
{
printf("ERR: lic2 compare\n");
}
// here should be two unimplemented/unknown test functions again testing lic1/lic2 - tbc.
// they only seem to check a length nothing super important imho
// i will add them once i match them to MIRACL
// now the mathfoo starts ...
xgcd(lic2, p2, lic2, lic2, lic2); // xgcd(x, p, x, x, x,); -> x = 1/x mod p (p is prime)
copy(lic2,g); // copy output (lic2) to g for simplicity
mad(sha_hash, g, g, p2, p2, m1); // multiply and add - mad(x, x, x, w, x, x); -> x = x^2 / w
mad(lic1 , g, g, p2, p2, m2); // multiply and add - mad(x, x, x, w, x, x); -> x = x^2 / w
ecurve_mult2(m1, pt1, m2, pt2, pt1); // multiply and add - ecurve_mult2(e,p,ea,pa,pt) -> pt== e (x) p + ea (x) pa
// check if pt1 (output from ecurve_mult2) divided by the prime p2 matches lic1
// this is actually done slightly differnt in the firmware, but i was unable to find a match in MIRACL so far
// so i used the method from the ecsver.c example
epoint_get(pt1,v,v); // epoint_get(p, x, y); // extract x coordinate and lsb of y
divide(v,p2,p2); // quotient and remainder - divide(x,y,z) -> x = x (mod y)
// now we compare v to the first part of our license key (lic1)
// to give some idea of how the ECC-112 output changes with input print the compared value
printf("v: "); cotnum(v, stdout);
printf("lic1: "); cotnum(lic1, stdout); printf("\n");
if (mr_compare(v, lic1)==0)
{
printf(" License Key is VALID\n");
}
else
{
printf(" INVALID License Key\n");
}
printf("\n\n");
exit(0);
}