LibTomMath 库大数赋值、内存存储、及数据转换
我们在做RSA的时候,一定会遇到数据存储问题。比如OpenSSL中的大数存储,我们可以通过字符串赋值,但在内存中看到大数的数据时,发现数据是反向存储的。
例如,我们将字符串 “1234567890ABCDEF” 赋值为一个大数,
而大数在内存中的格式为:0x00492750 EF CD AB 90 78 56 34 12
这个就是数据的大尾和小尾区别。
好比我们写是数字:一万两千三百四十五 12345 ,高位在左侧,低位在右端,我们称之为大尾模式,或大端模式,
对于内存中,我们平时看到的 DWORD 数据 0x12345678, 在内存中存储顺序为:78 56 34 12 , 将高位存储在右端高地址,这种叫小端(小尾)模式。
OpenSSL中大数的存储也是小端(小尾)模式,高位再高地址,所以我们在内存中看到的此类的大数HEX数据,提取出来后,需要将ASCII字符串先倒序后(转换为大端模式),再将其赋值BIG_NUM,才可以正确运算。
下面说一下 TomMath 库中大数的格式及内存形式,一下为库的一些数据转换函数,看起来很复杂的样子:
int mp_unsigned_bin_size(const mp_int *a);
int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c);
int mp_to_unsigned_bin(const mp_int *a, unsigned char *b);
int mp_to_unsigned_bin_n(const mp_int *a, unsigned char *b, unsigned long *outlen);
int mp_signed_bin_size(const mp_int *a);
int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c);
int mp_to_signed_bin(const mp_int *a,unsigned char *b);
int mp_to_signed_bin_n(const mp_int *a, unsigned char *b, unsigned long *outlen);
int mp_read_radix(mp_int *a, const char *str, int radix); // 高位-->低位小尾字符序列 <内存中为大尾模式>
int mp_toradix(const mp_int *a, char *str, int radix); // 转为 小尾序列
int mp_toradix_n(const mp_int *a, char *str, int radix, int maxlen);
int mp_radix_size(const mp_int *a, int radix, int *size);
mp_read_radix 从ASCII读取大数,同Openssl,我们写个代码来分析一下(库源码也是开源的,有兴趣可以读一下):
char sn[] = "BB7F51983FD8707FD6227C23DEF5D5377A5A737CEF3C5252E578EFE136DF87B50473F9341F1640C8D258034E14C16993FCE6C6B8C3CEEB65FC8FBCD8EB77B3B05AC7C4D09E0FA1BA2EFE87D3184DB6718AE41A7CAD89B8DCE0FE80CEB523D5D647F9DB58A31D2E71AC677E67FA6E75820736C9893761EE4ACD11F31DBDC349EF";
char szBuffer0 = {0};
unsigned char szBuffer1 = {0};
unsigned char szBuffer2 = {0};
mp_intcc;
mp_init(&cc);
int nn = 0;
nn = mp_read_radix( &cc, sn, 0x10 ); // 从ASCII字符串初始化大数ASCII数据为大端(大尾)模式
nn = mp_toradix( &cc, szBuffer0, 0x10 ); // 将大数转为 ASCII 字符串(大尾模式)
nn =mp_to_signed_bin( &cc, szBuffer1 );// 将大数转换为带符号的 BIN(hex)数据( HEX数据为大尾模式)
nn =mp_to_unsigned_bin( &cc, szBuffer2 ); // ( HEX数据为大尾模式)
// mp_read_radix 字符串变为大数后的模型,TomMath 是以 DWORD (0xFFF-FFFF)为单元来作为基本单元的,其他的大数则是以字节 0xFF 作为技术单元。
// 这个你如果自己写过大数库就自然了解这些区别了,不多解释,所以我们如果直接提取 TomMath 的内存数据是不可以直接使用的,但是转换起来也比较简单:
hex:
LibTomMath 库中 RSA 的算法实现也比较简单,先用 RSA-tools 构造一组1024的 N、D、e,再随意找一段小于1024为的数据作为M,计算结束后将大数 mp_toradix 转换为 ASCII,即可得到我们输入的ASCII 字符串 M,ASCII顺序即为高位-->低位。
void TestC()
{
char * szN = "98AE442902D70AC203229CB2586C2AB36CE31B01FB428AADC7CD2C3DF8F4508E930ED6603C4145B10EEA5996C3473FD131EB20780F2FE4F4239B988E04626D5E9C2C4FE71F5135F903250C161C978C120C3E9D9E7F6A2F75F38F74C501352EACC028CCEE15BE04ED34980CE3915B2957218A474D982C77261C8B6E4AB85920C9";
char * szD = "19B93F6C32A7E7664E1850A1D2A4719C7A492CD764F15D4D83BB747BEB985731A00E25D0317662FCD60ED5FFB6D3C2B35511207B190DC4A81C6F87FB85A68C7D814A7086C221586B14F4BDA7F55D5C59DC32E903480D5616D3F5BA7398762B5ACD6468E56CD7402E1F87FB88A764F3727C7EEB49FB80CE7DFC930E335F2FEA83";
char * szE = "10003";
char * szM = "20E832BD15F3651FB298E54CE623DF8DFA5323827650098A1353558A43D5D7BB644466DBFA2F65E81F79CD9BBAF419DB0A3F0A1AC4B18F95848759F6CE4E23899AB148A953FBB857CA20BA732EE5432353DD063F187D9A55565E048C97705DF0305DC8AEEBC613494C2BE857971F1FB5C82A7BDB90891B2814FFEBDBFC";
char szC = {0};
mp_int n;
mp_int e;
mp_int d;
mp_int m;
mp_int c;
mp_int mm;
mp_init(&n);
mp_init(&e);
mp_init(&d);
mp_init(&m);
mp_init(&c);
mp_init(&mm);
int nn = 0;
nn = mp_read_radix( &n, szN, 0x10 );
nn = mp_read_radix( &d, szD, 0x10 );
nn = mp_read_radix( &e, szE, 0x10 );
nn = mp_read_radix( &m, szM, 0x10 );
// c = m^e (mod n)
mp_exptmod( &m, &e, &n, &c );
// m = c^d (mod n)
mp_exptmod( &c, &d, &n, &mm );
char szBuffer;
nn = mp_toradix(&mm, szBuffer, 0x10);
// Hex ==> BigNum int 为单位
// 内存模式为 max: 0xFFF-FFFF
// mp_toradix 即可将大数转换为 ASCII(大端(大尾)模式)即我们初始化大数的字符串
return ;
}
收藏,学习,谢谢校长 学习了,谢谢校长。 学习了,多谢分享。 非常不错,非常感谢!
页:
[1]