2021SC@SDUSC

文章标题

  • BIGNUM的结构
  • 大整数的加减法(绝对值加减)
    • 绝对值加减
    • 带符号加减
  • 大整数的乘法
    • 经典乘法
    • 递归和comba乘法
  • 小结

无论是SM2、SM9等国密算法还是以RSA为代表的国际标准算法或者任何其他密码算法,对于大整数都有一定的要求,例如RSA中公钥的生成,需要用到大素数的乘法模幂等运算。而像1024位这种大整数的运算,肯定不能用普通的整数的加减乘除运算方法,一个是int类型的变量不能容纳这么长的数,另一个是考虑到计算机的计算效率问题。既能实现大整数“big number”加减乘除乘方模幂等运算,还需要保证效率,是对大整数运算程序实现的要求。因为GMSSL是在OPENSSL之上进行的扩充,本篇分析了OPENSSL中bn文件夹中针对大整数的运算方法。

BIGNUM的结构

首先认识一下BIGNUM的结构,源代码来自\openssl-master\crypto\bn\bn_local.h

#define BN_ULONG     unsigned long
struct bignum_st {BN_ULONG *d;                /* Pointer to an array of 'BN_BITS2' bit* chunks. */int top;                    /* Index of last used d +1. *//* The next are internal book keeping for bn_expand. */int dmax;                   /* Size of the d array. */int neg;                    /* one if the number is negative */int flags;
};
typedef struct bignum_st BIGNUM;

其中
BN_ULONG 32比特无符号数,简称为“字”;
d 指针,指向表示大整数的数组;
dmax 数组的最大长度;
top 大整数的最高字,0≤top≤dmax-1;
neg 标记了大整数的符号,neg为1表示负数,为0则表示正数;
flags 大整数的属性,这一点后面提到时再详细说明。

举个简单的例子进行说明。比如定义一个最长可以表示4个字的大整数,则dmax=4;但是这个数可能只有低2个字有效,高2个字都为0,即d[3]=d[2]=0,d[1]≠0,则此时top=1。
原文链接:https://blog.csdn.net/samsho2/article/details/85772221

大整数的加减法(绝对值加减)

openssl-master\crypto\bn\bn_add.c

绝对值加减

int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)//绝对值加
{int max, min, dif;const BN_ULONG *ap, *bp;BN_ULONG *rp, carry, t1, t2;bn_check_top(a);bn_check_top(b);if (a->top < b->top) {const BIGNUM *tmp;tmp = a;a = b;b = tmp;}max = a->top;min = b->top;dif = max - min;if (bn_wexpand(r, max + 1) == NULL)return 0;r->top = max;ap = a->d;bp = b->d;rp = r->d;carry = bn_add_words(rp, ap, bp, min);rp += min;ap += min;while (dif) {dif--;t1 = *(ap++);t2 = (t1 + carry) & BN_MASK2;*(rp++) = t2;carry &= (t2 == 0);}*rp = carry;r->top += carry;r->neg = 0;bn_check_top(r);
return 1;
}
int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)//绝对值减
{int max, min, dif;BN_ULONG t1, t2, borrow, *rp;const BN_ULONG *ap, *bp;bn_check_top(a);bn_check_top(b);max = a->top;min = b->top;dif = max - min;if (dif < 0) {              /* hmm... should not be happening */ERR_raise(ERR_LIB_BN, BN_R_ARG2_LT_ARG3);return 0;}if (bn_wexpand(r, max) == NULL)return 0;ap = a->d;bp = b->d;rp = r->d;borrow = bn_sub_words(rp, ap, bp, min);ap += min;rp += min;while (dif) {dif--;t1 = *(ap++);t2 = (t1 - borrow) & BN_MASK2;*(rp++) = t2;borrow &= (t1 == 0);}while (max && *--rp == 0)max--;r->top = max;r->neg = 0;bn_pollute(r);return 1;
}

其中BN_ULONG bn_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, int n)与BN_ULONG bn_sub_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, int n)函数实现了低位的齐字加减法,并返回进位值赋给carry\borrow。算法思想主要利用了整数的加法进位时只可能进一位或者不进位,两大整数相加减时,位数少的与位数多的相同位数进行齐字加减,高位与carry\borrow相加减后放到高位,完成绝对值加减法。

带符号加减

int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)//大整数加
{int ret, r_neg, cmp_res;bn_check_top(a);bn_check_top(b);if (a->neg == b->neg) {r_neg = a->neg;ret = BN_uadd(r, a, b);} else {cmp_res = BN_ucmp(a, b);if (cmp_res > 0) {r_neg = a->neg;ret = BN_usub(r, a, b);} else if (cmp_res < 0) {r_neg = b->neg;ret = BN_usub(r, b, a);} else {r_neg = 0;BN_zero(r);ret = 1;}}r->neg = r_neg;bn_check_top(r);return ret;
}
int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)//大整数减
{int ret, r_neg, cmp_res;bn_check_top(a);bn_check_top(b);if (a->neg != b->neg) {r_neg = a->neg;ret = BN_uadd(r, a, b);} else {cmp_res = BN_ucmp(a, b);if (cmp_res > 0) {r_neg = a->neg;ret = BN_usub(r, a, b);} else if (cmp_res < 0) {r_neg = !b->neg;ret = BN_usub(r, b, a);} else {r_neg = 0;BN_zero(r);ret = 1;}}r->neg = r_neg;bn_check_top(r);return ret;
}

有了绝对值相加减后,带符号的大整数进行加减运算就变得比较简单了。
先进行判定a、b是否同号,若同号,结果r的符号赋上a的符号,然后进行绝对值加减;否则,进行绝对值比较,r的符号赋上绝对值大的数的符号,然后进行绝对值相减。

大整数的乘法

以下源代码来自openssl-master\crypto\bn\bn_mul.c

经典乘法

Openssl首先实现了最基本的经典乘法方式,也就是类似小学中学到的“竖式计算”的方法,在void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb)中实现了该方法。

void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb)
{BN_ULONG *rr;if (na < nb) {int itmp;BN_ULONG *ltmp;itmp = na;na = nb;nb = itmp;ltmp = a;a = b;b = ltmp;}rr = &(r[na]);if (nb <= 0) {(void)bn_mul_words(r, a, na, 0);return;} elserr[0] = bn_mul_words(r, a, na, b[0]);for (;;) {if (--nb <= 0)return;rr[1] = bn_mul_add_words(&(r[1]), a, na, b[1]);if (--nb <= 0)return;rr[2] = bn_mul_add_words(&(r[2]), a, na, b[2]);if (--nb <= 0)return;rr[3] = bn_mul_add_words(&(r[3]), a, na, b[3]);if (--nb <= 0)return;rr[4] = bn_mul_add_words(&(r[4]), a, na, b[4]);rr += 4;r += 4;b += 4;}
}

递归和comba乘法

Openssl使用的环境一般是对信息的加解密,故对大整数的使用一定是不能用int、long等类型进行直接表示的,也就是说普通的数乘方法无法直接在实际场景中使用。上述的bn_mul_normal函数只起到最顶层的作用,实际对大整数的乘法运算使用到了递归乘法和comba乘法。
当然,openssl也实现了大整数的递归乘法和comba乘法。对于大整数来说,递归乘法的时间复杂度要比普通数乘要小。同时,对大整数乘法问题进行分而治之,逐步变成可以解决的数乘问题。
void bn_mul_recursive
void bn_mul_part_recursive
void bn_mul_low_recursive
三个函数实现了对大整数的递归乘法。
void bn_mul_comba8
void bn_mul_comba4
函数实现了对大整数的comba乘法,有关comba算法的具体内容,不在此详细介绍。

通过ctx,使程序自己判断使用递归乘法还是comba乘法:

int bn_mul_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
{int ret = 0;int top, al, bl;BIGNUM *rr;
#if defined(BN_MUL_COMBA) || defined(BN_RECURSION)int i;
#endif
#ifdef BN_RECURSIONBIGNUM *t = NULL;int j = 0, k;
#endifbn_check_top(a);bn_check_top(b);bn_check_top(r);al = a->top;bl = b->top;if ((al == 0) || (bl == 0)) {BN_zero(r);return 1;}top = al + bl;BN_CTX_start(ctx);if ((r == a) || (r == b)) {if ((rr = BN_CTX_get(ctx)) == NULL)goto err;} elserr = r;#if defined(BN_MUL_COMBA) || defined(BN_RECURSION)i = al - bl;
#endif
#ifdef BN_MUL_COMBAif (i == 0) {# if 0if (al == 4) {if (bn_wexpand(rr, 8) == NULL)goto err;rr->top = 8;bn_mul_comba4(rr->d, a->d, b->d);goto end;}
# endifif (al == 8) {if (bn_wexpand(rr, 16) == NULL)goto err;rr->top = 16;bn_mul_comba8(rr->d, a->d, b->d);goto end;}}
#endif                          /* BN_MUL_COMBA */
#ifdef BN_RECURSIONif ((al >= BN_MULL_SIZE_NORMAL) && (bl >= BN_MULL_SIZE_NORMAL)) {if (i >= -1 && i <= 1) {/** Find out the power of two lower or equal to the longest of the* two numbers*/if (i >= 0) {j = BN_num_bits_word((BN_ULONG)al);}if (i == -1) {j = BN_num_bits_word((BN_ULONG)bl);}j = 1 << (j - 1);assert(j <= al || j <= bl);k = j + j;t = BN_CTX_get(ctx);if (t == NULL)goto err;if (al > j || bl > j) {if (bn_wexpand(t, k * 4) == NULL)goto err;if (bn_wexpand(rr, k * 4) == NULL)goto err;bn_mul_part_recursive(rr->d, a->d, b->d,j, al - j, bl - j, t->d);} else {            /* al <= j || bl <= j */if (bn_wexpand(t, k * 2) == NULL)goto err;if (bn_wexpand(rr, k * 2) == NULL)goto err;bn_mul_recursive(rr->d, a->d, b->d, j, al - j, bl - j, t->d);}rr->top = top;goto end;}}
#endif                          /* BN_RECURSION */if (bn_wexpand(rr, top) == NULL)goto err;rr->top = top;bn_mul_normal(rr->d, a->d, al, b->d, bl);#if defined(BN_MUL_COMBA) || defined(BN_RECURSION)end:
#endifrr->neg = a->neg ^ b->neg;rr->flags |= BN_FLG_FIXED_TOP;if (r != rr && BN_copy(r, rr) == NULL)goto err;ret = 1;err:bn_check_top(r);BN_CTX_end(ctx);return ret;
}

最后是大整数乘法中最上层的函数,对外只留下了一个乘法的接口,对外只需要输入两个大整数和上下文环境,就返回r=a*b。

int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
{int ret = bn_mul_fixed_top(r, a, b, ctx);bn_correct_top(r);bn_check_top(r);return ret;
}

小结

本篇文章主要对大整数的加减法和乘法运算进行了分析。和大整数相关的运算函数一直是底层代码中较为重要的一部分,下面将会对大整数的其他运算的实现进行代码方向的分析与学习。
对于大整数的运算操作,属于openssl甚至是密码学编程较为底层的内容。现阶段使用的密码,大部分都依赖于大整数的产生和运算。理解底层的运算内容可以更好地认识上层的函数实现,同时也进一步了解了底层的算法。

山东大学软件工程应用与实践——GMSSL开源库(十)——重要的大整数相关推荐

  1. 山东大学软件工程应用与实践——GMSSL开源库(四)——SM9数字签名算法及验证的源代码分析

    2021SC@SDUSC 目录 一.引言 二.密钥的生成数字签名与签名验证相关代码 1.判定函数 2.签名的初始化函数 3.签名执行函数 4.真正的签名函数 5.签名验证初始化函数 6.签名验证执行函 ...

  2. 山东大学软件工程应用与实践——GMSSL开源库(一) ——WINDOWS下GMSSL的安装与编译的超详细保姆级攻略

    2021SC@SDUSC GMSSL简介 GmSSL是一个开源的密码工具箱,支持SM2/SM3/SM4/SM9/ZUC等国密(国家商用密码)算法.SM2国密数字证书及基于SM2证书的SSL/TLS安全 ...

  3. 山东大学软件工程应用与实践——GMSSL开源库(九)——SM9密钥封装与公钥加密的源代码分析

    2021SC@SDUSC 文章目录 int SM9_wrap_key(密钥的封装) int SM9_unwrap_key(密钥的解封) int SM9_encrypt(公钥加密算法) int SM9_ ...

  4. 山东大学软件工程应用与实践——GMSSL开源库(二)——浅尝SM9

    2021SC@SDUSC 概述 为了降低公开密钥系统中密钥和证书管理的复杂性,以色列科学家.RSA算法发明人之一Adi Shamir在1984年提出了标识密码(Identity-Based Crypt ...

  5. 山东大学软件工程应用与实践——Pig代码综述

    2021SC@SDUSC 目录 一.Pig是什么? 二.Pig的特点 三.Pig安装部署 四.组内分工 一.Pig是什么? Pig 是Apache平台下的一个免费开源项目,是MapReduce的一个抽 ...

  6. <2021SC@SDUSC>博客(5)山东大学软件工程应用与实践JPress代码分析(四)

    2021SC@SDUSC 前言 在前面三篇文章中,我对 JPress 项目的基础框架 JFinal 和 JBoot 在使用层面进行了拆解与分析.在接下来的文章中,我将在代码层面对 JPress 项目中 ...

  7. <2021SC@SDUSC>博客(9)山东大学软件工程应用与实践Jpress代码分析(8)

    2021SC@SDUSC Lucene和ElasticSearch 在上一篇博客中提到的Elasticsearch是一个基于Lucene搜索引擎为核心构建的开源,分布式,RESTful搜索服务器.这里 ...

  8. 山东大学软件工程应用与实践——WeaselTSF(一)

    2021SC@SDUSC 文章目录 简单介绍 WeaselTSF.h 从本篇博客开始分析WeaselTFS这个包中的代码. 简单介绍 WeaselTSF.vcproj This is the main ...

  9. 山东大学软件工程应用与实践——使用CUDA/GPU技术加速密码运算(第五周)

    2021SC@SDUSC 很抱歉由于自身身体原因,本来打算这周对AES算法进行CPU和GPU的实际检测比较分析进行推迟.我决定对于SHA.AES.RSA三个算法在CPU和GPU性能对比放在最后几周. ...

最新文章

  1. 2017年第八届蓝桥杯省赛试题(JavaA组)
  2. Swift Playgrounds Learn to Code 2 final project Pyramid
  3. oracle case when exists()
  4. 白话科普系列——网站靠什么提升加载速度?
  5. 快速上手sqlserver profiler
  6. 分析一下shell(转)
  7. linux怎样自制库_如何制作自己的LINUX系统?
  8. 一、Objective-C之Runtime的概念
  9. hdf5文件和csv的区别_使用HDF5文件并创建CSV文件
  10. mysql重置密码报错,吐血整理
  11. linux pm2 权限,pm2 部署 node的三种方法示例
  12. 浏览器Firefox新标签页默认打开地址设置
  13. 联想y470上三代cpu_联想Y470笔记本i3处理器能换i5或者i7处理器嘛?
  14. 最优秀的开源库之GPUImage
  15. win10弹出计算机的内存不足,Win10系统提示“计算机内存不足”的解决方法
  16. Frida学习之旅(一)--Google Pixel手机的ROOT
  17. 计算机毕设(附源码)JAVA-SSM基于web的社团管理系统
  18. Required request body is missing: public错误
  19. 远程控制电脑会不会泄露隐私
  20. Linux网卡固件,CentOS下X710网卡升级驱动和固件脚本 | 聂扬帆博客

热门文章

  1. 2. 饶明新 校示范课六年级上册《数学广角-数与形》照片
  2. 【python】3分钟搞定音频剪辑,进阶版
  3. 从系统架构理解Spring Cloud Gateway的作用
  4. 居然嘲笑我们没有Excel的这个功能,呵呵
  5. 2021年国开专科计算机应用基础终结性考试试题 内附word和ppt答案 (文尾直接下载)
  6. excel如何绘制三线表头?
  7. 基于javaweb+jsp的宠物领养信息管理系统(JavaWeb JSP MySQL Servlet SSM SpringBoot Layui Ajax)
  8. 小学五年级计算机课评课,小学五年级英语评课稿
  9. ubuntu分区设置
  10. 2021-12-05 vue移动端卖座电影项目(六) 为详情页面Detail.vue获取后台数据并设置样式