寻找有限域同构映射
一、有限域简介
有限域亦称伽罗瓦域(Galois Fields),是伽罗瓦于 18 世纪 30 年代研究代数方程根式求解问题时引出的概念。有限域在密码学、近代编码、计算机理论、组合数学等方面有着广泛的应用
在抽象代数中,域是一个对加法和乘法封闭的集合,其中要求每个元素都有加法逆元,每个非零元素都有乘法逆元。若域 \(F\) 只包含有限个元素,则称为有限域,有限域中元素的个数称为有限域的阶。可以证明,有限域的阶必为素数的幂,即有限域的阶可表示为 \(p^n\)(\(p\) 是素数,\(n\) 是正整数)。有限域通常记为 \(GF(p^n)\)
二、有限域同构
因为网上似乎都没有如何寻找有限域同构映射的资料,而正好我上课学到了相关的内容,在此结合自己的理解作简单的介绍。因为不是数学系相关的学生,而且对有限域的理解仅处于皮毛,有很多定理没办法给出详细的证明,在逻辑上或许会多有漏洞,如果读者发现我的漏洞希望能够指出。
(虽然指出了我也不一定能听懂,但还是希望能够指出)
另外,有限域的相关知识(例如有限域的性质,有限域的构造等)将默认读者已经了解,在阅读后续内容前先保证对上述知识有所了解
关于复合域强烈建议参考这篇博客: AES 和 SM4 S 盒复合域实现方法,本文提到的内容参考了论文《适合SMS4算法硬件实现的S盒构造新方法》中给出的求解同构映射的方法 (--- 2022.04.28 更新 ---)
上面的链接好像打不开了,不过我在github上好像找到了对应的作者,SM4-with-AESENCLAST 这个链接是对应的资料。
很多寻找有限域同构的方法采用的都是正规基之类的,但我这块不是很了解,可能没法给各位满意的答复。我这里提到的方法是《适合 SMS4 算法硬件实现的 S 盒构造新方法》(徐艳华、白雪飞、郭立)这篇论文中的采用的方法,但论文里边原理部分感觉不是很清楚。这篇博客是我根据上课学到的内容结合自己的理解写的,原理上应该会有纰漏 (--- 2022.12.27 更新 ---)
关于复合域优化SM4算法强烈建议参考下方论文: (--- 2023.05.27 更新 ---)
[1] 陈晨,郭华,王闯等.一种基于复合域的国密SM4算法快速软件实现方法[J].密码学报,2023,10(02):289-305.
2.1 寻找同构映射思路
首先,任意相同阶数的有限域都同构。假设 \(p(x)\) 和 \(q(x)\) 都是域 \(F_p\) 上的 \(n\) 阶不可约多项式,那么有限域 \(F_1 = F_p[x] / p(x)\) 和 \(F_2 = F_p[x] / q(x)\) 都是 \(p^n\) 阶有限域。
有下面两条重要定理:
- 如果假设 \(\alpha\) 是多项式 \(p(x)\) 的一个根,那么有限域 \(F_1\) 可以表示成 \(F_p[\alpha]\)
- 如果假设 \(\beta\) 是多项式 \(q(x)\) 的一个根,那么有限域 \(F_2\) 可以表示成 \(F_p[\beta]\)
现在需要找到一个两者的同构映射 \(\phi\)
首先,根 \(\alpha\) 和 \(\beta\) 满足 \(p(\alpha)=0\) 和 \(q(\beta)=0\)
直接将 \(\alpha\) 映射到 \(\beta\) 是不可行的,因为线性关系无法保证,即无法保证 \(p(\beta)=0\)
那么如果找到 \(\beta ' \in F_p[\beta]\) 使得 \(p(\beta')=0\) 成立,这样就可以构造 \(\alpha \to \beta '\) 的映射
2.2 寻找同构映射例子
求有限域 \(Z_3[x] / (x^2 + 1)\) 和 \(Z_3[x] / (x^2 + x +2)\) 之间的同构映射
记 \(p(x) = x^2 + 1\) ,\(q(x) = x^2 + x + 2\)
且有 \(\alpha = \overline{x} \; mod \; p(x)\) 为 \(p(x)\) 的一个根
且有 \(\beta = \overline{x} \; mod \; q(x)\) 为 \(q(x)\) 的一个根
寻找得到 \(\beta' = \beta + 2\) ,使得 \(p(\beta') = (\beta + 2)^2 + 1 = \beta^2 + \beta + 2 = 0\)
那么对应的映射为 \(\alpha \to \beta + 2\)
也就是原先 \(Z_3[\alpha]\) 中的元素 \(a_1 \alpha + a_0\) 将被映射成 \(a_1 (\beta + 2) + a_0 = a_1 \beta + (2a_1 + a_0) \in Z_3[\beta]\)
或者说原先 \(Z_3[x] / (x^2 + 1)\) 中的元素 \(a_1 x + a_0\) 将被映射到 \(Z_3[x] / (x^2 + x +2)\) 中的 \(a_1 x + (2a_1 + a_0)\)
2.3 同构映射逆映射
在 2.2 中已经求出了一个同构映射 \(\alpha \to \beta + 2\)
很容易发现 \(q(\alpha - 2) = (\alpha - 2)^2 + (\alpha - 2) + 2 = \alpha^2 + 1 = 0\)
也就是说逆映射为 \(\beta \to \alpha - 2\)
三、复合域与同构
3.1 复合域
接下来介绍一个稍微复杂的有限域——复合域,复合域在密码学中具有非常大的用途,将一个高阶的有限域转化为两个或多个低阶有限域的复合,便于后续分析
记 \(p(x) = x^2 + x + 4\) ,\(F_2[x] / p(x)\) 为 \(2^2\) 阶有限域
记 \(q(x) = x^4 + x^3 + 1\) ,\(F_2[x] / q(x)\)为 \(2^4\) 阶有限域
那么将两者复合得到 \(F : b_1 x + b_2 \; mod \; p(x), \; b_i \in F_2[x] / q(x)\) ,为 \((2^4)^2 = 2^8\) 阶有限域(复合域)
3.2 复合域同构
通过一个例子来了解求复合域同构的过程
记 \(F_2[x] / r(x)\) 为 \(2^8\) 阶有限域, \(r(x) = x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + 1\)
记 \(F[x] / (p(x), q(y)) = (a_3 y^3 + a_2 y^2 + a_1 y + a_0) x + (b_3 y^3 + b_2 y^2 + b_1 y + b_0)\) 为上述 3.1 中的 \((2^4)^2\) 阶有限域
求 \(F_2[x] / r(x) \to F[x] / (p(x), q(x))\) 的一个同构映射
首先,需要遍历 \(F[x] / (p(x), q(y))\) 的全部元素,找到其中一个 \(\beta'\) 满足 \(r(\beta') = 0\) 的元素 \((y^2 + 1)x + (y^3 + y^2 + y)\)
记 \(\alpha = \overline{x} \; mod \; r(x)\) 为 \(r(x)\) 的根
那么同构映射为 \(\alpha \to \beta'\)
四、映射矩阵
4.1 有限域与矩阵
在密码学中,常将多项式表示为矩阵的形式,例如在 \(GF(2^8)\) 上的多项式
假设 \(p(x)\) 和 \(q(x)\) 都是 \(F_2\) 上的 \(8\) 次不可约多项式,那么可以构成 \(2^8\) 阶有限域 \(F_2[x]/p(x)\) 和 \(F_2[x]/q(x)\)
假设 \(\alpha\) 是多项式 \(p(x)\) 的一个根,那么有限域 \(F_2[x]/p(x)\) 可以表示成 \(F_2[\alpha]\)
假设 \(\beta\) 是多项式 \(q(x)\) 的一个根,那么有限域 \(F_2[x]/q(x)\) 可以表示成 \(F_2[\beta]\)
且假设 \(c_7 x^7 + c_6 x^6 + c_5 x^5 + \cdots + c_1 x + c_0\) 是 \(F_2[x]/p(x)\) 上的一个多项式,那么该多项式也可以等价于 \(c_7 \alpha^7 + c_6 \alpha^6 + c_5 \alpha^5 \cdots c_1 \alpha + c_0\) ,即
4.2 多项式矩阵
假设 \(\alpha^i = a_{7,i} x^7 + a_{6,i} x^6 + \cdots a_{1,i} x + a_{0,i}\)
那么
当 \(\alpha = \overline{x} \; mod \; p(x)\) 时,上式为单位阵 \(I\)
4.3 同构映射与矩阵
假设 \(c_7 x^7 + c_6 x^6 + c_5 x^5 + \cdots + c_1 x + c_0\) 是 \(F_2[x]/p(x)\) 上的多项式
假设 \(d_7 x^7 + d_6 x^6 + d_5 x^5 + \cdots + d_1 x + d_0\) 是 \(F_2[x]/q(x)\) 上的多项式
记它们两者的矩阵表示为 \(C\) 和 \(D\)
要找到两个域的映射关系,将一个多项式映射到另一个多项式
也就是要找到一个映射矩阵 \(T\) ,使得一个多项式的矩阵表示 \(C\) 和另一个多项式的矩阵表示 \(D\) 满足 \(TC = D\)
上文提到,可以找到一个同构映射 \(\alpha \to \beta'\) ,使得有限域 \(F_2[\alpha]\) 和有限域 \(F_2[\beta]\) 同构
根据同构关系的线性性质,有 \(\alpha^i \to \beta'^i\) ,故映射为
记 \(\alpha\) 对应的矩阵(如4.2所示)为 \(A\) , \(\beta'\) 对应的矩阵为 \(B\)
那么映射矩阵 \(T\) 满足 \(TAC = BC\) ,即 \(T= B A^{-1}\)
为了计算方便,一般选取的根 \(\alpha = \overline{x} \; mod \; p(x)\)
那么 \(\alpha\) 对应的矩阵 \(A\) 即为单位阵 \(I\)
那么 \(T = B\)
假设 \(\beta'^i = b_{7,i} x^7 + b_{6,i} x^6 + \cdots b_{1,i} x + b_{0,i}\)
那么
4.4 一般算法流程
首先,需要找到两个域 \(F_2[x]/p(x)\) 和 \(F_2[x]/q(x)\) 满足映射关系的 \(\alpha\) 和 \(\beta'\)
其中 \(\alpha = \overline{x} \; mod \; p(x)\) , \(\beta = \overline{x} \; mod \; q(x)\)
且 \(p(\beta') = 0, \beta' \in F_2[\beta] = F_2[x]/q(x)\)
计算 \(\beta'^i, i = 0, 1, \cdots , 7\) ,并将 \(\beta'^i\) 的系数值置于矩阵 \(T\) 的倒数第 \(i\) 列(从0开始)
五、代码实现(寻找同构映射)
有限域的四则运算:点击此处跳转
假设 \(p(x) = x^8 + x^4 + x^3 + x + 1\) ,\(q(x) = x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + 1\)
需要求解 \(F_2[x]/p(x)\) 和 \(F_2[x]/q(x)\) 之间的映射关系
5.1 有限域类(GF28)
def gf2_mul(a: int, b: int, poly: int) -> int:
"""有限域乘法"""
ans = 0
digit = poly.bit_length() - 1
while b:
if b & 1:
ans = ans ^ a
a, b = a << 1, b >> 1
if a >> digit:
a = a ^ poly
return ans
class GF256:
def __init__(self, value, poly):
self.value = value
self.poly = poly
def __add__(self, other):
"""加法"""
return GF256(self.value ^ other.value, self.poly)
def __sub__(self, other):
"""减法"""
return GF256(self.value ^ other.value, self.poly)
def __mul__(self, other):
"""乘法"""
return GF256(gf2_mul(self.value, other.value, self.poly), self.poly)
def __pow__(self, power, modulo=None):
"""幂"""
res = GF256(1, self.poly)
for i in range(power):
res = res * self
return res
5.2 p(x)和q(x)定义
px = 0b100011011 # x^8 + x^4 + x^3 + x + 1
qx = 0b111110101 # x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + 1
p = lambda x: x ** 8 + x ** 4 + x ** 3 + x + x ** 0
q = lambda x: x ** 8 + x ** 7 + x ** 6 + x ** 5 + x ** 4 + x ** 2 + x ** 0
5.3 遍历搜索
for i in range(2 ** 8):
# 遍历 F_2[x]/q(x) 的元素
t = GF256(i, qx)
if p(t).value == 0: # 满足p(t)=0
print(bin(t.value))
得到8个结果
0b100000
0b110011
0b111110
0b1110000
0b10011111
0b10100110
0b10101010
0b11001110
5.4 结果
令 \(\alpha = \overline{x} \; mod \; p(x)\) 为 \(p(x)\) 的一个根
令 \(\beta = \overline{x} \; mod \; q(x)\) 为 \(q(x)\) 的一个根
上述结果也就是找到了8个映射关系,分别为
值 | 关系 |
---|---|
0b100000 | \(\alpha \to \beta^5\) |
0b110011 | \(\alpha \to \beta^5 + \beta^4 + \beta + \beta^0\) |
0b111110 | \(\alpha \to \beta^5 + \beta^4 + \beta^3 + \beta^2 + \beta^1\) |
0b1110000 | \(\alpha \to \beta^6 + \beta^5 + \beta^4\) |
0b10011111 | \(\alpha \to \beta^7 + \beta^4 + \beta^3 + \beta^2 + \beta^1 + \beta^0\) |
0b10100110 | \(\alpha \to \beta^7 + \beta^5 + \beta^2 + \beta^1\) |
0b10101010 | \(\alpha \to \beta^7 + \beta^5 + \beta^3 + \beta^1\) |
0b11001110 | \(\alpha \to \beta^7 + \beta^6 + \beta^3 + \beta^2 + \beta^1\) |
六、有限域同构与密码学
在例如 AES 和 SM4 这类对称密码的 S 盒变换的底层,所使用的就是有限域 \(GF(2^8)\) 的求逆变换(除了求逆还涉及一些仿射变换)。
如果采用硬件(例如FPGA)实现密码算法,可将 \(GF(2^8)\) 的同构于 \(GF((2^4)^2)\) 复合域甚至同构于 \(GF(((2^2)^2)^2)\) 复合域,便于分析有限域的求逆表达式,使用逻辑函数代替原先的查表运算,能够节省门电路资源
如果使用软件实现,可以利用有限域同构关系将 SM4 的 S盒 转化为 AES 的 S盒,利用 AES 指令集完成 SM4 的 S盒 操作
七、求解复合域同构映射的示例代码
(--- 2023.11.24 更新 ---)
下面给出复合域同构映射的示例python代码,用于搜索SM4算法对应的复合域,参考论文是陈晨等人的《一种基于复合域的国密SM4算法快速软件实现方法》。论文里有比本博客更详细的原理介绍,流程详见论文中的算法1.
陈晨,郭华,王闯等.一种基于复合域的国密SM4算法快速软件实现方法[J].密码学报,2023,10(02):289-305.
python代码需要提前安装好一个有关复合域运算的库 gf2lib,通过指令 pip install gf2lib
即可安装。下方代码实现了Gf242复合域类以及Gf28有限域类,使用论文中的搜索算法搜索出复合条件的映射矩阵。代码的输出结果和论文中给出的数据完全一致
这段代码有一些需要注意的地方(--- 2024.04.28 更新 ---):
- 如果有针对其它有限域进行搜索的需求,需要修改GF242和GF28中的
__mul__
函数,修改成对应的有限域乘法;还有gf242_p
和gf28_p
函数 - 对于某些有限域,可能会存在没有输出“本原元”的搜索结果,可能是因为代码中存在逻辑的漏洞,我也不清楚原因,也不知道如何修补。但后续代码并没有用到搜索本原元的结果,搜索映射矩阵时,beta的初始值默认是GF242的根(感谢
菜鸡本菜233
的指出) - 对于某些有限域,可能会存在重复搜索的现象,即输出了两边映射矩阵。受限于我自身的知识储备不足,我也不清楚原因,也不知道如何修补。但我代码中对搜索出的映射矩阵进行了断言检测,如果不发生断言异常,可以认为搜索结果是没有问题的(感谢
菜鸡本菜233
的指出)
# -*- coding:utf-8 -*-
"""
需要安装的包:gf2lib
类:Gf242
GF(2^4^2)复合域元素,为F[x]/(P(x),Q(y)),P(x) = x^2 + x + 4, Q(y) = y^4 + y^3 + 1
支持加、减、乘、幂、判等运算
(1,0)代表单位元e
类:Gf28
GF(2^8)元素,为F[x]/r(x), r(x) = x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + 1,即SM4复合域
支持加、减、乘、幂、判等运算
函数:gf28_items
函数:gf242_items
函数:gf242_p
函数:gf28_p
函数:cast_gf28_to_gf242
函数:cast_gf242_to_gf28
功能1:搜索GF(2^4^2)本原元
功能2:搜索指定GF(2^8)和GF(2^4^2)的映射矩阵
"""
from math import gcd
from gf2lib import gf2poly
from gf2lib.gf2matrix import GF2Matrix
from typing import Tuple, List
class Gf28:
@staticmethod
def one():
"""单位元"""
return Gf28(1)
@staticmethod
def zero():
"""零元"""
return Gf28(0)
def __init__(self, n: int):
self.n = n
def __mul__(self, other):
sm4_poly = 0b111110101 # x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + 1
return Gf28(gf2poly.mul(self.n, other.n, sm4_poly))
def __add__(self, other):
return Gf28(self.n ^ other.n)
def __pow__(self, power, modulo=None):
assert not (power == 0 and self.n == 0)
r = Gf28.one()
for _ in range(power):
r = r * self
return r
def __eq__(self, other):
return self.n == other.n
def __str__(self):
return str(self.n)
class Gf242:
def __init__(self, a: Tuple[int, int]):
self.a = a
@staticmethod
def gf24_mul(a: int, b: int) -> int:
gf24_poly = 0b11001 # y^4+y^3+1
return gf2poly.mul(a, b, gf24_poly)
@staticmethod
def one():
"""单位元"""
return Gf242((1, 0))
@staticmethod
def zero():
"""零元"""
return Gf242((0, 0))
def __add__(self, other):
return Gf242((self.a[0] ^ other.a[0], self.a[1] ^ other.a[1]))
def __mul__(self, other):
# P(x) = x^2 + x + 4
a0, a1 = self.a[0], self.a[1]
b0, b1 = other.a[0], other.a[1]
# c2x^2 + c1x + c0
c0 = Gf242.gf24_mul(a0, b0)
c1 = Gf242.gf24_mul(a0, b1) ^ Gf242.gf24_mul(a1, b0)
c2 = Gf242.gf24_mul(a1, b1)
# c2x^2 = c2x + 4*c2
r0 = c0 ^ Gf242.gf24_mul(c2, 4)
r1 = c1 ^ c2
return Gf242((r0, r1))
def __pow__(self, power, modulo=None):
assert not (power == 0 and self == Gf242.zero())
r = Gf242.one()
for _ in range(power):
r = r * self
return r
def __eq__(self, other):
return self.a == other.a
def __str__(self):
return str(self.a)
def gf28_items() -> List[Gf28]:
"""返回GF(2^8)所有元素"""
return [Gf28(a) for a in range(256)]
def gf242_items() -> List[Gf242]:
"""返回GF(2^4^2)中所有元素"""
return [Gf242((a0, a1)) for a0 in range(16) for a1 in range(16)]
def gf242_p(a: Gf242) -> Gf242:
"""带入至GF(2^4^2)多项式"""
return a * a + a + Gf242((4, 0))
def gf28_p(a):
"""带入至GF(2^8)多项式"""
# x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + 1
return a ** 8 + a ** 7 + a ** 6 + a ** 5 + a ** 4 + a ** 2 + a ** 0
def cast_gf28_to_gf242(a: Gf28, T: GF2Matrix) -> Gf242:
"""将GF(2^8)元素映射至GF(2^4^2)
:param a: GF(2^8)元素
:param T: 映射矩阵
:return: T*a
"""
b = T * GF2Matrix.from_int(a.n, 8)
b = b.to_int()
return Gf242((b & 0x0F, b >> 4))
def cast_gf242_to_gf28(a: Gf242, T: GF2Matrix) -> Gf28:
"""将GF(2^4^2)元素映射至GF(2^8)
:param a: GF(2^4^2)元素
:param T: 映射矩阵
:return: T*a
"""
b = T * GF2Matrix.from_int((a.a[1] << 4) + a.a[0], 8)
b = b.to_int()
return Gf28(b)
# # 计算GF242本原元,得到(0,1)和(1,1),即x和1+x
# print("GF242本原元:")
# for g in gf242_items():
# flag = True
# if gf242_p(g) != Gf242.zero():
# flag = False
# t = g
# for i in range(2, 255):
# t = t * g
# if t == Gf242.one():
# flag = False
# if flag:
# print(g)
#
# print("搜索映射矩阵:beta=", (0, 1))
# result = []
# # step1 选择本原元 x
# beta = Gf242((0, 1))
# flag = [1 for _ in range(256)]
# t = 1
# while t <= 255:
# # step2
# beta_t = beta ** t
# if gf28_p(beta_t) == Gf242.zero():
# result.append(beta_t)
# t = t + 1
# # step3
# else:
# for j in range(8):
# flag[(t * (2 ** j)) % 255] = 0
# # step4,5
# t = t + 1
# while flag[t] == 0 or gcd(t, 255) > 1:
# t = t + 1
# if not t <= 255:
# break
result = []
for g in gf242_items()[1:]:
if gf28_p(g) == Gf242.zero():
result.append(g)
for beta in result:
# 打印结果
print("满足条件的beta^t: ", beta, ", 映射矩阵 col 0-7: ", end='')
for i in range(7, -1, -1):
a = beta ** i
print((a.a[1] << 4) + a.a[0], end=', ')
print()
# 计算映射矩阵
M = GF2Matrix.from_int(0b00000001, 8) # (0, 0, ..., 1)
for i in range(1, 8):
a = beta ** i
a = (a.a[1] << 4) + a.a[0]
M = GF2Matrix.from_int(a, 8) | M
# 计算映射和逆映射矩阵
T = M
T_inv = M.inverse()
# # 测试乘法是否满足同构,遍历所有元素
# for i in range(256):
# for j in range(256):
# a28, b28 = Gf28(i), Gf28(j)
# a242 = cast_gf28_to_gf242(a28, T)
# b242 = cast_gf28_to_gf242(b28, T)
# assert a28 * b28 == cast_gf242_to_gf28(a242 * b242, T_inv)
# assert a28 + b28 == cast_gf242_to_gf28(a242 + b242, T_inv)
# 打印映射矩阵
for item in T.transpose().matrix:
print(item)