1. 程式人生 > 其它 >AES-Rijndael有限域(Galois Field)GF(2^8)運算的介紹與實現(PHP版)

AES-Rijndael有限域(Galois Field)GF(2^8)運算的介紹與實現(PHP版)

1.前言

 最近做微信小程式開發,小程式裡面對敏感資料的加密採用了 AES -128-CBC的對稱加密方式。所以想寫一篇介紹AES-Rijndael演算法的文章,此篇文章為AES作鋪墊,因為它的列混淆演算法的運算操作用到了有限域的概念。 

2.有限域的介紹

 Galois Field 在國內有兩個翻譯別名:伽羅華域、伽羅瓦域(我也不知道為什麼沒能統一一個翻譯)。在數學中,有限域是一個包含有限元素的域。通過GF(2^M)來表示域中含有2^M個元素。每一個域中有多個本原多項式,當M=8時,常見的本原多項式為P(x)=x^8+x^4+x^3+x^2+1,AES中的本原多項式為不可約多項式(irreducible polynomial):P(x)=x^8+x^4+x^3+x^1+1。 

3. 有限域的加法與乘法運算 

有限域中的運算方式需要先把數值轉化成多項式的形式,然後再進行相關運算。其中加法操作可以直接進行運算。 加法:計算機異或運算 加法例子:4+3 展開多項式為: x^2 + (x^1 + x^0) => 2^2 + 2^1 + 1 => 4 + 2 + 1 => 4 ^ 2 ^ 1 = 7 直接計算:4^3 = 7 乘法:通過乘法結合律展開多項式,如果x的最高指數大於7,那麼需要對本原多項式取餘數(初二多項式除法運算),否則就是展開後的多項式做加法操作。 取餘操作有一個演算法,參考《密碼編碼學與網路安全學原理》一書中提到的公式: 已知GF(2^8)最長的多項式f(x)=b7*x^7+b6*x^6+b5*x^5+b4*x^4+b3*x^3+b2*x^2+b1*x^1+b0*x^0 x*f(x)= b7*x^8+b6*x^7+b5*x^6+b4*x^5+b3*x^4+b2*x^3+b1*x^2+b0*x^1 x*f(x)的情況如下: 當b7=0時,最高指數為7不需要取餘,直接做異或加法運算。 當b7=1時,最高指數為8,大於7,則需要取m(x)的餘數,推導如下: f(x) = ( b7*x^8+b6*x^7+b5*x^6+b4*x^5+b3*x^4+b2*x^3+b1*x^2+b0*x^1 )mod m(x); 這裡有另外一個等式:x^8 mod m(x) = [m(x) – x^8] = (x^4 + x^3 + x + 1) 所以根據上面的等式可以化簡f(x) = b6*x^7+b5*x^6+b4*x^5+b3*x^4+b2*x^3+b1*x^2+b0*x^1 + x^4 + x^3 + x +1 而x^4 + x^3 + x + 1 就是十六程序的0x1B(因為2^4+2^3+2+1=27),因此本原多項式取餘的操作就等價於 異或本原多項式。公式如下: 圖片來源: 《密碼編碼學與網路安全學原理》 第88頁  高階的x可以重複使用這個公式,在程式實現部分會詳細介紹下。 乘法例子:7*4 展開多項式為:(x^2+x^1+x^0)*(x^2) => x^4 + x^2 + x^2 => 2^4 + 2^2 + 2^2 = 16 ^ 4 ^ 4 = 16 乘法例子:130*3 展開多項式為:(x^7+x^1)*(x^1+x^0) => x^8 + x^7 + x^1 + x^1 => x^8 + x^7 +( x^8+x^4+x^3+x^1+x^0 ) ( 不可約多項式 ) => x^7 + x^4+x^3+x^1+x^0 => 2^7 + 2^4 + 2^3 + 2^1 + 1 = 128 ^ 16 ^ 8 ^ 2 ^ 1 = 155

4. 程式的實現 

對於任意兩個數a和b做乘法運算,基於 GF(2^8) 的條件下,我們知道最後展開的多項式都是2^N之和,N=[0,8]。因此,多項式可能值為2^0=1,2^1=2,2^2=4,2^3=8,2^4=16,2^5=32,2^6=64,2^7=128,2^8=256。換算成二進位制就是: 00000001 => 0x01 00000010 => 0x02 00000100 => 0x04 00001000 => 0x08 00010000 => 0x10 00100000 => 0x20 01000000 => 0x40 10000000 => 0x80 所以對於任意位元組數a,和它相乘的所有可能為:a*x = a*(0x80+0x40+0x20+0x10+0x08+0x04+0x02+0x01) 比如:0x57 * 0x13 0x57 * 0x13 = 0x57*(0x01+0x02+0x10) 因為0x13 = 2^4+2^1+2^0 = 0x10 + 0x02 + 0x01 所以我們只要分別對a進行0x10、0x02、0x01的乘積再進行累加操作。(如果遇到高階,還是把0x01-0x80所有乘積都算出來,因為高階的乘法依賴低階乘積的結果) 0x57 * 0x01 = (x^6 + x^4 + x^2 + x^1 + x^0 )*x^0 = x^6 + x^4 +x^2 + x^1 + x^0 = 64 ^ 16 ^ 4 ^ 2 ^ 1 = 0x57 0x57*0x02 = (x^6 + x^4 + x^2 + x^1 + x^0 ) *x^1 = x^7 + x^5 + x^3 +x^2 + x^1 = 128 ^ 32 ^ 8 ^ 4 ^ 2 = 0xAE 0x57*0x10 = (x^6 + x^4 + x^2 + x^1 + x^0 ) *x^4 = x^10 + x^8 + x^6 + x^5 + x^4 = (這裡我們遇到了x的高階情況,高階的計算方式參考第二部分圖片中的x*f(x)的那個公式) 所以將0x57*0x10 轉成多項式為:(x^4)*0x57 = ((x^3)*0x57)*(x^1) = (((x^2)*0x57)*x^1)*(x^1)=( (((x^1)*0x57)*x^1)*(x^1) )*x^1即((0x57*0x02)*x^1)*(x^1)*(x^1) 因為上面的0x57*0x02=0xAE,即多項式:x^7+x^5+x^3+x^2+x^1,所以我們接著計算(x*( x^7+x^5+x^3+x^2+x^1 ))*x*x 就可以了,繼續往下推算: (x^8+x^6+x^4+x^3+x^2)*x*x 因為最高指數8大於7,所以進行取餘操作,取本原多項式的模為:(x^8+x^6+x^4+x^3+x^2+x^8+x^4+x^3+x^1+1)*x*x (x^6+x^2+x+1)*x*x (x^7+x^3+x^2+x)*x x^8+x^4+x^3+x^2 因為最高指數8大於7,所以進行取餘操作,取本原多項式的模為: (x^8+x^4+x^3+x^2+ x^8+x^4+x^3+x^1+1 ) x^2+x+1 4 ^ 2 ^ 1 = 0x07 所以 0x57 * 0x13 = 0x57*(0x01+0x02+0x10) = 0x57*0x01 + 0x57*0x02 + 0x57*0x10 = 0x57 + 0xAE + 0x07 = 0xFE 根據上面的推算我們就可以設計出演算法了,對於x*f(x)實質上,x=2的時候,乘以2在計算機裡就是左移的操作,我們將上面的計算用二進位制表示為: 0x57 * 0x01 = ‭01010111‬ * ‭00000001 = 01010111 = 0x57 0x57 * 0x02 = (0x57*0x01)*0x02 = (0x57*0x01) << 1 = 10101110 = 0xAE 0x57 * 0x04 = (0x57*0x02) << 1 =‭ 101011100‬‬ (這裡b7=1,所以進行異或操作,先去掉第9位的1) = (101011100 & 0xFF) ^ 0x1B = 0x47 0x57 * 0x08 = (0x57*0x04) << 1 = ‭10001110‬ = 0x8E 0x57*0x10 = (0x57*0x08) << 1 = ‭100011100‬ (b7=1,同上進行異或操作) = ‭(100011100 &0xFF) ^ 0x1B = 7 演算法描述: 第一步:計算0x01-0x10的所有結果集 if (($a << 1) & 0x100) $res[i] = (($a << 1) & 0xFF) ^ 0x1B else $res[i] = $a << 1 第二步:計算$b由哪幾個結果集組成 第三步:異或求和

4.1  PHP實現AES列混合有限域乘積計算 

PHP實現AES列混合有限域乘積計算

function aes_multi($a, $b = 0)
{
    if ($a >= 2 ** 8 || 2 ** 8 <= $b) {
        exit('只支援GF(2^8)域的計算');
    }
    $binMulti = [];
    $result = 0;
    for ($i = 0; $i < 8; $i++) {
        $a = ($a & 0x100) ? (($a & 0xFF) ^ 0x1B) : $a;
        $binMulti[] = $a;
        $a = $a << 1;
    }

    $binString = str_pad(base_convert($b, 10, 2), 8, 0, STR_PAD_LEFT);

    for ($i = 0; $i < 8; $i++) {
        if ($binString{$i} === '1') {
            $pos = strlen(substr($binString, $i)) - 1;
            $result ^= $binMulti[$pos];
        }
    }
    return $result;
}

var_dump(aes_multi(0x57, 0x13));

5.參考

官方文件   https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf