在数学中,有限域(或称伽罗华域)是一个包含有限元素的域。与其他域一样,有限域是进行加减乘除运算都有定义并且满足特定规则的集合。其中加法和乘法必须满足交换、结合和分配的规律。加法和乘法具有封闭性,即加法和乘法结果仍然是域中的元素。
伽罗华域一般用 G F ( 2 M ) GF(2^M) GF(2M)表示,这个域中含有 2 M 2^M 2M个元素。 G F ( 2 M ) GF(2^M) GF(2M)上的四则运算是基于多项式运算的,上过学的应该都知道什么是多项式,一般都是这种结构 f ( x ) = x 6 + x 4 + x 2 + x + 1 f(x) = x^6 + x^ 4 + x^2 + x + 1 f(x)=x6+x4+x2+x+1 。
本原多项式 (primitive polynomial)是一种特殊的不可约多项式。当一个域上的本原多项式确定了,这个域上的运算也就确定了。本原多项式一般通过查表得知,同一个域往往有多个本原多项式。
G F ( 2 M ) GF(2^M) GF(2M)域,当M=8时,其中比较常见的一个本原多项式为 P ( x ) = x 8 + x 4 + x 3 + x 2 + 1 P(x)=x^8+x^4+x^3+x^2+1 P(x)=x8+x4+x3+x2+1。 G F ( 2 8 ) GF(2^8) GF(28)域也是计算机领域用的比较多的一种域,因为一字节等于8比特嘛。研究这个玩意也是因为AES加密中列混合变换中用到了伽罗华域运算,但是 AES加解密算法中,使用的不可约多项式(irreducible
polynomial)为 P ( x ) = x 8 + x 4 + x 3 + x + 1 P(x)=x^8+x^4+x^3+x+1 P(x)=x8+x4+x3+x+1,所以下面主要讨论AES算法中用到的 G F ( 2 8 ) GF(2^8) GF(28)域,多项式为 P ( x ) = x 8 + x 4 + x 3 + x + 1 P(x)=x^8+x^4+x^3+x+1 P(x)=x8+x4+x3+x+1乘法的实现。
在伽罗华域中,加法是模2运算,也就是忽略进位的加法,等同于计算机中的XOR异或,即 1^1=0, 1^0=1, 0^0=0
。
伽罗华域中的乘法是基于多项式运算的,比如: 5 = 00000101 b = ( 2 2 + 1 ) 5=00000101b=(2^2+1) 5=00000101b=(22+1),对应多项式为 ( x 2 + 1 ) (x^2+1) (x2+1)。
举例:
在相乘得到的多项式结果中,如果x的次数大于7,就需要对多项式在GF(2)上关于本原多项式P(x)求余数,即 m o d P ( x ) mod P(x) modP(x)。
因为加法为模2加法,相同项相加为0,所以减法可以当成加法来计算。
基于多项式 P ( x ) = x 8 + x 4 + x 3 + x 2 + 1 P(x)=x^8+x^4+x^3+x^2+1 P(x)=x8+x4+x3+x2+1:
基于多项式 P ( x ) = x 8 + x 4 + x 3 + x + 1 P(x)=x^8+x^4+x^3+x+1 P(x)=x8+x4+x3+x+1:
因为在AES算法列混合环节中用到了伽罗华域乘法,所以接下来的代码实现使用AES算法指定的不可约多项式 P ( x ) = x 8 + x 4 + x 3 + x + 1 P(x)=x^8+x^4+x^3+x+1 P(x)=x8+x4+x3+x+1进行分析。
为了方便编程我们先找找规律,假设函数GMul(u, v)表示伽罗华域乘法,先看与2相乘的伽罗华域计算,即GMul(2, v),v、u不分左右:
看上式,假如v对应的多项式x的次数大于7,即v的最高位为1,也就是v>>7 == 1
的话就进行 m o d P ( x ) mod P(x) modP(x) 化简,比如:
2 ∗ 129 = x ∗ ( x 7 + 1 ) = x 8 + x = ( x 8 + x ) + P ( x ) 2*129=x*(x^7+1)=x^8+x=(x^8+x)+P(x) 2∗129=x∗(x7+1)=x8+x=(x8+x)+P(x)
= ( x 8 + x ) + ( x 8 + x 4 + x 3 + x + 1 ) = x 4 + x 3 + 1 =(x^8+x)+(x^8+x^4+x^3+x+1)=x^4+x^3+1 =(x8+x)+(x8+x4+x3+x+1)=x4+x3+1
= 00011001 = 0x19 = 00000010 ∧ 00011011 = 0x02 ∧ 0x1B = ( 129 < < 1 ) ∧ 0x1B = 00011001 = \text {0x19} = 00000010 \land 00011011 = \text {0x02} \land \text {0x1B}=(129<<1) \land \text{0x1B} =00011001=0x19=00000010∧00011011=0x02∧0x1B=(129<<1)∧0x1B
2 ∗ 176 = x ∗ ( x 7 + x 5 + x 4 ) = x 8 + x 6 + x 5 = ( x 8 + x 6 + x 5 ) + P ( x ) 2*176=x*(x^7+x^5+x^4)=x^8+x^6+x^5=(x^8+x^6+x^5)+P(x) 2∗176=x∗(x7+x5+x4)=x8+x6+x5=(x8+x6+x5)+P(x)
= ( x 8 + x 6 + x 5 ) + ( x 8 + x 4 + x 3 + x + 1 ) = x 6 + x 5 + x 4 + x 3 + x + 1 =(x^8+x^6+x^5)+(x^8+x^4+x^3+x+1)=x^6+x^5+x^4+x^3+x+1 =(x8+x6+x5)+(x8+x4+x3+x+1)=x6+x5+x4+x3+x+1
= 01111011 = 0x7B = 0110 , 0000 ∧ 0001 , 1011 = 0x60 ∧ 0x1B = ( 176 < < 1 ) ∧ 0x1B = 0111 1011 = \text {0x7B} = 0110,0000 \land 0001,1011 = \text {0x60} \land \text {0x1B}=(176<<1) \land \text{0x1B} =01111011=0x7B=0110,0000∧0001,1011=0x60∧0x1B=(176<<1)∧0x1B
从上面的3个例子可以总结一个规律:
G M u l ( 2 , v ) = { v < < 1 , if ( v > > 7 ) is 0x00 . ( v < < 1 ) ∧ 0x1B , if ( v > > 7 ) is 0x01 . GMul(2, v)= \begin{cases} v<<1, & \text {if $(v >> 7)$ is 0x00}. \\ (v<<1) \land \text {0x1B}, & \text {if $(v>>7)$ is 0x01}. \end{cases} GMul(2,v)={ v<<1,(v<<1)∧0x1B,if (v>>7) is 0x00.if (v>>7) is 0x01.
3 ∗ v = ( 2 + 1 ) ∗ v = G M u l ( 2 , v ) + v 3*v=(2+1)*v=GMul(2, v) + v 3∗v=(2+1)∗v=GMul(2,v)+v= GMul(2, v) ^ v
4 ∗ v = 2 ∗ 2 ∗ v 4*v=2*2*v 4∗v=2∗2∗v= GMul(2, GMul(2, v))
7 ∗ v = 00000111 = ( 2 ∗ 2 + 2 + 1 ) 7*v= 00000111 =(2*2 + 2 + 1) 7∗v=00000111=(2∗2+2+1)= GMul(2, GMul(2, v)) ^ GMul(2, v) ^ v
8 ∗ v = ( 2 ∗ 2 ∗ 2 ) ∗ v 8*v=(2*2*2)*v 8∗v=(2∗2∗2)∗v=GMul(2, GMul(2, GMul(2, v)))
8=2^3,循环3次GMul(2,v)
11111111 b = 2 7 + 2 6 + 2 5 + 2 4 + 2 3 + 2 2 + 2 + 1 11111111b = 2^7+2^6+2^5+2^4+2^3+2^2+2+1 11111111b=27+26+25+24+23+22+2+1
所以相当于:GMul(2,v)循环7次 ^ GMul(2,v)循环6次 … GMul(2,v)循环1次 ^ v
根据上面总结的公式可以很容易用代码实现GMul(2, v)
uint8_t GMul2(uint8_t v){
int flag = (v & 0x80);
v <<= 1;
if (flag) {
v ^= 0x1B; /* P(x) = x^8 + x^4 + x^3 + x + 1 */
}
return v;
}
根据上述对GMul(u, v)的总结,可以用一下代码实现:
uint8_t GMul(uint8_t u, uint8_t v) {
uint8_t p = 0;
for (int i = 0; i < 8; ++i) {
if (u & 0x01) {
p ^= v;
}
v = GMul2(v); //调用GMul(2,v)
u >>= 1;
}
return p;
}
为了代码整洁可以写成一个通用的函数,AES算法实现中列混合环节可以直接调用下面的函数:
uint8_t GMul(uint8_t u, uint8_t v) {
uint8_t p = 0;
for (int i = 0; i < 8; ++i) {
if (u & 0x01) {
p ^= v;
}
int flag = (v & 0x80);
v <<= 1;
if (flag) {
v ^= 0x1B; /* P(x) = x^8 + x^4 + x^3 + x + 1 */
}
u >>= 1;
}
return p;
}