[关闭]
@RunZhi 2016-09-15T14:53:07.000000Z 字数 10269 阅读 2771

基本运算,求逆,求离散对数实现

密码学 编程 代数


前言

GF称为伽罗瓦域,又称有限域.定义详见Galois field.本文讨论GF(2^8)下加减乘除,求逆和离散对数的程序实现。

环境信息

操作系统:ubuntu 14.04
编程语言:C++
编译器:g++

各个运算的实现

以下介绍GF(2^8)各个部分实现细节。

相关定义

由于要多次使用字节类型,因此在本头文件中作一个宏定义

  1. #define u8 unsigned char

由于会设计到简单的多项式除法,因此用一个结构体保存作除后得到的商和余数.

  1. struct quorem
  2. {
  3. u8 first; // quotient
  4. u8 next; // remainder
  5. quorem(u8 i, u8 j){ first = i ; next = j; }
  6. };

为方便使用,定义一个类以表达GF(2^8)元素

  1. class GF_eight
  2. {
  3. // addition
  4. inline friend GF_eight operator+(const GF_eight& i, const GF_eight& j);
  5. // subtraction
  6. inline friend GF_eight operator-(const GF_eight& i, const GF_eight& j);
  7. // multiplication
  8. friend GF_eight operator*(const GF_eight& i, const GF_eight& j);
  9. // division
  10. friend GF_eight operator/(const GF_eight& i, const GF_eight& j);
  11. public:
  12. // Constructor
  13. GF_eight(u8 V = 0)
  14. {
  15. this->Value = V;
  16. }
  17. GF_eight(const GF_eight& V)
  18. {
  19. this->Value = V.Value;
  20. }
  21. // return value
  22. u8 getValue() const { return this->Value; }
  23. // Set the Value
  24. void setValue(u8 V) { this->Value = V;}
  25. // Check if it is a generator of GF(2^8)
  26. bool is_gen() const;
  27. // Obtain the inverse element
  28. GF_eight inverse() const;
  29. //Obtain the g^n
  30. GF_eight power(u8 n) const;
  31. //Obtain the dlog based g
  32. GF_eight log(const GF_eight& g) const;
  33. private:
  34. u8 Value;
  35. };

其中声明了暂时已实现的元素的操作.private域中的Value是实际保存的值.

加法与减法

中的元素可以看为一个位向量.由于系数是模2的,因此可以把加法看为异或,同样减法也是异或.

  1. inline GF_eight operator+(const GF_eight& i, const GF_eight& j)
  2. {
  3. return GF_eight(i.Value ^ j.Value);
  4. }
  5. inline GF_eight operator-(const GF_eight& i, const GF_eight& j)
  6. {
  7. return GF_eight(i.Value ^ j.Value);
  8. }

乘法

稍微麻烦一点的是乘法,实际上执行乘法运算的话只需要按照我们平时的多项式乘法,其中注意模2,乘出来后得到的多项式还要模去的模多项式:

但这样执行似乎太慢了,我们可以考虑一下更快的乘法.
首先:中代表零元素.因此有


但是这个有什么用呢?我们可以考虑一下如下运算

根据分配律,我们可以把它写成:

很显然,非常容易算,那么呢?还要按先乘后模吗?不需要,我们来看看怎么计算更好.

(这里用了群的结合律)
首先,移位一次:


既然是,那似乎应该继续移位一次。但我们先不这么做.注意,我们知道了:.它等价于:
因此,

至此,我们计算出了,接下来继续计算也是相同的办法.

基于这样的思想,写出的程序

  1. GF_eight operator*(const GF_eight& i, const GF_eight& j)
  2. {
  3. u8 lowest_bit = 0x01;
  4. u8 highest_bit = 0x80;
  5. u8 temp_j = j.Value;
  6. u8 temp_res = i.Value;
  7. u8 res = 0;
  8. u8 flag;
  9. if ( temp_j & lowest_bit )
  10. res = temp_res;
  11. temp_j >>= 1;
  12. while( temp_j != 0 )
  13. {
  14. flag = temp_res & highest_bit;
  15. temp_res <<= 1;
  16. // Plus 27 if b_8 == 1( flag == 1 )
  17. if( flag )
  18. temp_res ^= XXX;
  19. /* test code
  20. printf("%x\n",temp_res);
  21. */
  22. if(temp_j & lowest_bit)
  23. res ^= temp_res;
  24. temp_j >>= 1;
  25. }
  26. return GF_eight(res);
  27. }

注意:程序执行并不严格按照我之前的例子,但思想是一样的。

求逆

在讲如何实现除法之前,首先要考虑求逆.因为我们不能进行普通的多项式除法,因为这是在模下的运算。而求了逆,除法也不过是一个乘法罢了.

由于是一个域,它对乘法运算构成一个交换群。我们可以参考数论里的EGCD算法(扩展欧几里德算法)进行求逆。

EGCD算法由于需要的篇幅不小,因此讲解请参考欧几里德算法与扩展欧几里德算法。注意,这个网站上虽然说的是上的EGCD算法,但是实际上,只要把域元素替换为,把相应的运算进行修改,对相应的变量类型进行一定的修改,得到的新算法就是适用于的EGCD算法.如果你看不懂这句话,那真的需要学点代数了。

求逆的代码在此:

  1. GF_eight GF_eight::inverse() const
  2. {
  3. // Deal with the trivial cases
  4. if( this->Value == 0)
  5. {
  6. printf("Zero element doesn't have inverse!\n");
  7. return GF_eight(0);
  8. }
  9. if( this->Value == 1)
  10. return GF_eight(1);
  11. // Using Extended Algorithm
  12. u8 r1 = 0x8D;
  13. u8 r2 = this->Value;
  14. u8 r3 = 0;
  15. u8 v1 = 1;
  16. u8 v2 = 0;
  17. u8 v3 = 0;
  18. u8 w1 = 0;
  19. u8 w2 = 1;
  20. u8 w3 = 0;
  21. u8 q = 0;
  22. quorem p(0,0);
  23. GF_eight temp(0);
  24. // Because we use the byte to implement poly_div. So we must
  25. // deal with the highest bit of the 0x11B before using EGCD.
  26. // The division is actually the same as the usual division. I just do some operation explicitly( Because we are just allowed to implement in bytes.)
  27. p = poly_div(r1, r2);
  28. q = p.first;
  29. r3 = p.next;
  30. r3 <<= 1;
  31. r3 ^= 1;
  32. q <<= 1;
  33. p = poly_div(r3, r2);
  34. q ^= p.first;
  35. r3 = p.next;
  36. /* test code
  37. printf("q:%x\n",q);
  38. printf("r3:%x\n",r3);
  39. printf("v:%x\n",v3);
  40. printf("w:%x\n\n",w3);
  41. getchar();
  42. */
  43. r1 = r2;
  44. r2 = r3;
  45. v1 = 0;
  46. v2 = 1;
  47. w1 = 1;
  48. w2 = q;
  49. // Using EGCD
  50. while(1)
  51. {
  52. //Obtain the quotient and remainder
  53. p = poly_div(r1, r2);
  54. q = p.first;
  55. r3 = p.next;
  56. if( r3 == 0 ) break;
  57. // The multiplication must be token in GF(2^8)
  58. temp = GF_eight(q) * GF_eight(v2);
  59. v3 = v1 ^ (temp.getValue());
  60. temp = GF_eight(q) * GF_eight(w2);
  61. w3 = w1 ^ (temp.getValue());
  62. /* test code
  63. printf("q:%x\n",q);
  64. printf("r1:%x\n",r1);
  65. printf("r2:%x\n",r2);
  66. printf("r3:%x\n",r3);
  67. printf("v:%x\n",v3);
  68. printf("w:%x\n\n",w3);
  69. getchar();
  70. */
  71. r1 = r2;
  72. r2 = r3;
  73. v1 = v2;
  74. v2 = v3;
  75. w1 = w2;
  76. w2 = w3;
  77. }
  78. return GF_eight((u8)w2);

代码很长,我并不擅长写很简洁的代码。。。关于代码的部分解释,我想注释里面已经说了。这里的代码用到了函数poly_div,这个函数的声明为:

  1. static quorem poly_div(u8 ii, u8 jj)

声明为static是因为这个函数只是用于辅助求逆运算的,没必要被用在外面。这个函数完成的是两个字节变量的多项式除法运算 ,并把得到的商和余数保存到结构体quorem中。注意,我们执行的是普通的多项式除法。

除法

完成了求逆之后,就可以直接进行除法运算了:

  1. GF_eight operator/(const GF_eight& i, const GF_eight& j)
  2. {
  3. GF_eight temp = j.inverse();
  4. return i * temp;
  5. }

求离散对数

求离散对数的一种很普通的方法就是逐个穷尽进行寻找。但我们实际上可以做得精细一些.可以使用碰撞法

碰撞法的描述可参考:An Introduction to Mathematical Cryptography 的page80, Proposition 2.22:
碰撞法解DLP
这个算法正确性的证明显然不适合放在这篇文章这里,有想法的人可以自行搜索上面说的电子书进行学习。

同样地,这个原始的算法也是解决的离散对数的,但还是那句话,代数告诉我们这个算法可以移植到.

具体实现代码如下:

  1. GF_eight GF_eight::log(const GF_eight_pack::GF_eight &g) const
  2. {
  3. if( !g.is_gen() )
  4. {
  5. printf("The input is not a generator!\n");
  6. return 0;
  7. }
  8. GF_eight temp(1);
  9. GF_eight h(this->Value);
  10. // Using the simple algorithm:Collision method to solve g^x = h
  11. int N = 255;
  12. int n = sqrt(N)+1;
  13. // list for finding Collision
  14. u8 list[256];
  15. memset(list, 0, 256);
  16. // Computing g^0,g^1,...,g^n and record the index i
  17. u8 i = 0;
  18. do{
  19. list[temp.getValue()] = i;
  20. temp = temp * g;
  21. i++;
  22. }while( i <= n );
  23. temp = temp.inverse(); // temp = g^-(n+1)
  24. temp = temp * g; // temp = g^-n
  25. i = 0;
  26. u8 j = 0;
  27. do{
  28. i = list[h.getValue()];
  29. if( i != 0)
  30. break;
  31. list[h.getValue()] = j;
  32. h = h * temp;
  33. j++;
  34. }while( j <= n );
  35. return i + (j*n);
  36. }

这段代码里面包含了两个辅助函数 is_gen 和 power.它们一个是判断某元素是否是生成元,另一个是快速幂运算。它们的代码请参考后面的完整代码.

完整代码

  1. /*********************************************************************************
  2. *FileName: gf.hpp
  3. *Author: RunzhiZeng
  4. *Version: 3.1
  5. *Date: 16-09-12
  6. *Description:
  7. Implementation of add, sub, mul, div, inv and dlog of GF(2^8)
  8. *Others:
  9. For Computer Security course
  10. *History:
  11. 1. 16-09-12 V3.0:
  12. Implement the add, sub, mul and some other miscellaneous functions.
  13. 2. 16-09-13 V3.1:
  14. Implement the inv, div and dlog function,also including the power and poly div functions.
  15. **********************************************************************************/
  16. #ifndef _GF_H_
  17. #define _GF_H_
  18. #include<cstdio>
  19. #include<cmath>
  20. #include<cstring>
  21. namespace GF_eight_pack
  22. {
  23. #define u8 unsigned char
  24. // x^8
  25. const u8 XXX = 0x1B;
  26. // Struct used to save quotient and remainder
  27. struct quorem
  28. {
  29. u8 first; // quotient
  30. u8 next; // remainder
  31. quorem(u8 i, u8 j){ first = i ; next = j; }
  32. };
  33. class GF_eight
  34. {
  35. // addition
  36. inline friend GF_eight operator+(const GF_eight& i, const GF_eight& j);
  37. // subtraction
  38. inline friend GF_eight operator-(const GF_eight& i, const GF_eight& j);
  39. // multiplication
  40. friend GF_eight operator*(const GF_eight& i, const GF_eight& j);
  41. // division
  42. friend GF_eight operator/(const GF_eight& i, const GF_eight& j);
  43. public:
  44. // Constructor
  45. GF_eight(u8 V = 0)
  46. {
  47. this->Value = V;
  48. }
  49. GF_eight(const GF_eight& V)
  50. {
  51. this->Value = V.Value;
  52. }
  53. // return value
  54. u8 getValue() const { return this->Value; }
  55. // Set the Value
  56. void setValue(u8 V) { this->Value = V;}
  57. // Check if it is a generator of GF(2^8)
  58. bool is_gen() const;
  59. // Obtain the inverse element
  60. GF_eight inverse() const;
  61. //Obtain the g^n
  62. GF_eight power(u8 n) const;
  63. //Obtain the dlog based g
  64. GF_eight log(const GF_eight& g) const;
  65. private:
  66. u8 Value;
  67. };
  68. inline GF_eight operator+(const GF_eight& i, const GF_eight& j)
  69. {
  70. return GF_eight(i.Value ^ j.Value);
  71. }
  72. inline GF_eight operator-(const GF_eight& i, const GF_eight& j)
  73. {
  74. return GF_eight(i.Value ^ j.Value);
  75. }
  76. GF_eight operator*(const GF_eight& i, const GF_eight& j)
  77. {
  78. u8 lowest_bit = 0x01;
  79. u8 highest_bit = 0x80;
  80. u8 temp_j = j.Value;
  81. u8 temp_res = i.Value;
  82. u8 res = 0;
  83. u8 flag;
  84. if ( temp_j & lowest_bit )
  85. res = temp_res;
  86. temp_j >>= 1;
  87. while( temp_j != 0 )
  88. {
  89. flag = temp_res & highest_bit;
  90. temp_res <<= 1;
  91. // Plus 27 if b_8 == 1( flag == 1 )
  92. if( flag )
  93. temp_res ^= XXX;
  94. /* test code
  95. printf("%x\n",temp_res);
  96. */
  97. if(temp_j & lowest_bit)
  98. res ^= temp_res;
  99. temp_j >>= 1;
  100. }
  101. return GF_eight(res);
  102. }
  103. GF_eight operator/(const GF_eight& i, const GF_eight& j)
  104. {
  105. GF_eight temp = j.inverse();
  106. return i * temp;
  107. }
  108. ////////////* Auxiliary functions for finding inverse*///////////////////
  109. // Auxiliary function for poly_div
  110. // Check if i's degree is equal to j's
  111. static inline bool equal_degree(u8 i, u8 j)
  112. {
  113. return ((i ^ j) < i && (i ^ j) < j);
  114. }
  115. // Poly division in GF(2^n). n equals to 8 in this function
  116. static quorem poly_div(u8 ii, u8 jj)
  117. {
  118. u8 shift = 0;
  119. u8 temp = 1;
  120. u8 res = 0;
  121. // Trival Case
  122. if( jj == 0 )
  123. {
  124. printf("Error divisor\n");
  125. return quorem(0, 0);
  126. }
  127. if( ii == 0 )
  128. return quorem(0, jj);
  129. if( jj == 1 )
  130. return quorem(ii, 0);
  131. // When ii's degree is larger or equal to jj's degree
  132. u8 temp_j = jj;
  133. res = 0;
  134. while( equal_degree(ii, jj) || ii > jj )
  135. {
  136. temp_j = jj;
  137. // Right shift the divisor until the divisor's degree is equal to the dividend's
  138. while(!equal_degree(ii, temp_j))
  139. {
  140. shift++;
  141. temp_j <<= 1;
  142. }
  143. /* test code
  144. printf("%x\n",temp_j);
  145. */
  146. temp <<= shift;
  147. ii ^= temp_j;
  148. res ^= temp;
  149. shift = 0;
  150. temp = 1;
  151. }
  152. return quorem(res, ii);
  153. }
  154. /////////////////////////////////////////////////////////////////////////
  155. // Finding inverse using EGCD
  156. GF_eight GF_eight::inverse() const
  157. {
  158. // Deal with the trivial cases
  159. if( this->Value == 0)
  160. {
  161. printf("Zero element doesn't have inverse!\n");
  162. return GF_eight(0);
  163. }
  164. if( this->Value == 1)
  165. return GF_eight(1);
  166. // Using Extended Algorithm
  167. u8 r1 = 0x8D;
  168. u8 r2 = this->Value;
  169. u8 r3 = 0;
  170. u8 v1 = 1;
  171. u8 v2 = 0;
  172. u8 v3 = 0;
  173. u8 w1 = 0;
  174. u8 w2 = 1;
  175. u8 w3 = 0;
  176. u8 q = 0;
  177. quorem p(0,0);
  178. GF_eight temp(0);
  179. // Because we use the byte to implement poly_div. So we must
  180. // deal with the highest bit of the 0x11B before using EGCD.
  181. // The division is actually the same as the usual division. I just do some operation explicitly( Because we are just allowed to implement in bytes.)
  182. p = poly_div(r1, r2);
  183. q = p.first;
  184. r3 = p.next;
  185. r3 <<= 1;
  186. r3 ^= 1;
  187. q <<= 1;
  188. p = poly_div(r3, r2);
  189. q ^= p.first;
  190. r3 = p.next;
  191. /* test code
  192. printf("q:%x\n",q);
  193. printf("r3:%x\n",r3);
  194. printf("v:%x\n",v3);
  195. printf("w:%x\n\n",w3);
  196. getchar();
  197. */
  198. r1 = r2;
  199. r2 = r3;
  200. v1 = 0;
  201. v2 = 1;
  202. w1 = 1;
  203. w2 = q;
  204. // Using EGCD
  205. while(1)
  206. {
  207. //Obtain the quotient and remainder
  208. p = poly_div(r1, r2);
  209. q = p.first;
  210. r3 = p.next;
  211. if( r3 == 0 ) break;
  212. // The multiplication must be token in GF(2^8)
  213. temp = GF_eight(q) * GF_eight(v2);
  214. v3 = v1 ^ (temp.getValue());
  215. temp = GF_eight(q) * GF_eight(w2);
  216. w3 = w1 ^ (temp.getValue());
  217. /* test code
  218. printf("q:%x\n",q);
  219. printf("r1:%x\n",r1);
  220. printf("r2:%x\n",r2);
  221. printf("r3:%x\n",r3);
  222. printf("v:%x\n",v3);
  223. printf("w:%x\n\n",w3);
  224. getchar();
  225. */
  226. r1 = r2;
  227. r2 = r3;
  228. v1 = v2;
  229. v2 = v3;
  230. w1 = w2;
  231. w2 = w3;
  232. }
  233. return GF_eight((u8)w2);
  234. }
  235. bool GF_eight::is_gen() const
  236. {
  237. GF_eight g(this->Value);
  238. u8 cnt = 0;
  239. while( g.Value != 1 )
  240. {
  241. g = g * (*this);
  242. cnt = cnt + 1;
  243. if ( cnt == 254)
  244. return true;
  245. }
  246. return false;
  247. }
  248. GF_eight GF_eight::log(const GF_eight_pack::GF_eight &g) const
  249. {
  250. if( !g.is_gen() )
  251. {
  252. printf("The input is not a generator!\n");
  253. return 0;
  254. }
  255. GF_eight temp(1);
  256. GF_eight h(this->Value);
  257. // Using the simple algorithm:Collision method to solve g^x = h
  258. int N = 255;
  259. int n = sqrt(N)+1;
  260. // list for finding Collision
  261. u8 list[256];
  262. memset(list, 0, 256);
  263. // Computing g^0,g^1,...,g^n and record the index i
  264. u8 i = 0;
  265. do{
  266. list[temp.getValue()] = i;
  267. temp = temp * g;
  268. i++;
  269. }while( i <= n );
  270. temp = temp.inverse(); // temp = g^-(n+1)
  271. temp = temp * g; // temp = g^-n
  272. i = 0;
  273. u8 j = 0;
  274. do{
  275. i = list[h.getValue()];
  276. if( i != 0)
  277. break;
  278. list[h.getValue()] = j;
  279. h = h * temp;
  280. j++;
  281. }while( j <= n );
  282. return i + (j*n);
  283. }
  284. // Quick power
  285. GF_eight GF_eight::power(u8 n) const
  286. {
  287. GF_eight res(1);
  288. GF_eight a(this->Value);
  289. while( n > 0)
  290. {
  291. if ( n & 1 )
  292. res = res * a;
  293. n >>= 1;
  294. a = a * a;
  295. }
  296. return res;
  297. }
  298. #undef u8
  299. }
  300. #endif /* _GF_H_ */
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注