状態
バイト置換 pi8 AES-256暗号は、フィールド内の特定の操作シーケンスによって決定されます Gf(28) また、256バイトの配列で指定することもできます。
63 7c 77 7b f2 6b 6f c5 30 01 67 2b fe d7 ab 76
ca 82 c9 7d fa 59 47 f0 ad d4 a2 af 9c a4 72 c0
b7 fd 93 26 36 3f f7 cc 34 a5 e5 f1 71 d8 31 15
04 c7 23 c3 18 96 05 9a 07 12 80 e2 eb 27 b2 75
09 83 2c 1a 1b 6e 5a a0 52 3b d6 b3 29 e3 2f 84
53 d1 00 ed 20 fc b1 5b 6a cb be 39 4a 4c 58 cf
d0 ef aa fb 43 4d 33 85 45 f9 02 7f 50 3c 9f a8
51 a3 40 8f 92 9d 38 f5 bc b6 da 21 10 ff f3 d2
cd 0c 13 ec 5f 97 44 17 c4 a7 7e 3d 64 5d 19 73
60 81 4f dc 22 2a 90 88 46 ee b8 14 de 5e 0b db
e0 32 3a 0a 49 06 24 5c c2 d3 ac 62 91 95 e4 79
e7 c8 37 6d 8d d5 4e a9 6c 56 f4 ea 65 7a ae 08
ba 78 25 2e 1c a6 b4 c6 e8 dd 74 1f 4b bd 8b 8a
70 3e b5 66 48 03 f6 0e 61 35 57 b9 86 c1 1d 9e
e1 f8 98 11 69 d9 8e 94 9b 1e 87 e9 ce 55 28 df
8c a1 89 0d bf e6 42 68 41 99 2d 0f b0 54 bb 16
暗号の修正バージョン-「AES-256-M」を定義します。これは、置換のみが元のものと異なります。
2b c4 4d a2 76 99 10 ff 56 b9 30 df 0b e4 6d 82
db 34 bd 52 86 69 e0 0f a6 49 c0 2f fb 14 9d 72
95 7a f3 1c c8 27 ae 41 e8 07 8e 61 b5 5a d3 3c
65 8a 03 ec 38 d7 5e b1 18 f7 7e 91 45 aa 23 cc
cb 24 ad 42 96 79 f0 1f b6 59 d0 3f eb 04 8d 62
3b d4 5d b2 66 89 00 ef 46 a9 20 cf 1b f4 7d 92
75 9a 13 fc 28 c7 4e a1 08 e7 6e 81 55 ba 33 dc
85 6a e3 0c d8 37 be 51 f8 17 9e 71 a5 4a c3 2c
6f 80 09 e6 32 dd 54 bb 12 fd 74 9b 4f a0 29 c6
9f 70 f9 16 c2 2d a4 4b e2 0d 84 6b bf 50 d9 36
d1 3e b7 58 8c 63 ea 05 ac 43 ca 25 f1 1e 97 78
21 ce 47 a8 7c 93 1a f5 5c b3 3a d5 01 ee 67 88
8f 60 e9 06 d2 3d b4 5b f2 1d 94 7b af 40 c9 26
7f 90 19 f6 22 cd 44 ab 02 ed 64 8b 5f b0 39 d6
31 de 57 b8 6c 83 0a e5 4c a3 2a c5 11 fe 77 98
c1 2e a7 48 9c 73 fa 15 bc 53 da 35 e1 0e 87 68
チャレンジ。 5120ビットの暗号文(40ブロック)があります。これは、不明なキーを使用した単純な置換モードでAES-256-M暗号を使用してプレーンテキストを暗号化することによって取得されました。 平文の最初のブロックは既知です。 暗号文の未知の部分を開く実用的な方法を提案する。
理論的紹介
LSX暗号の一般的な構造を考えてみましょう。そのうちの1つはAES Rijndaelです。
LSX暗号構造
LSXアーキテクチャ暗号は、ラウンド関数の反復適用に基づいて変換されたテキストをブロックします(最新の暗号のブロック長aは128ビットです)。
ラウンド関数は、3つの変換で構成されます。
X -反復キーを持つ入力ブロックのモジュロ2加算 Ki ( i=\上線1、r どこで r -暗号のラウンド数);
S -固定置換の使用 pi ブロックの各バイト;
L -線形変換。
概略的に、LSX変換は次のように表すことができます
LSX暗号の各ラウンドは、個別のラウンドキーを使用します Ki 何らかの方法で主キーから生成された K 。 主キーのビット長 K 通常、反復キーの長さ以上です。 プライマリからの反復キーのキースキャン手順は、暗号によって大きく異なります。
一般的な暗号化変換式 Enc(K、a) ラウンド数が等しいLSX暗号の場合 r 次のように記述できます。
b=Enc(K、a)=X[Kr]LSX[Kr−1]...LSX[K2]LSX[K1](a)。
概略的に、LSX暗号の一般的な構造は次のように表すことができます
復号化プロセスは、逆変換を使用して実行されます。
X -反復キーを持つ入力ブロックのモジュロ2加算。
S−1 -逆を適用する S ルックアップ;
L−1 -逆の適用 L -変換( SS−1=I -単一置換)。
復号式 Dec(K、b)r ラウンドLSX暗号:
a=Dec(K、b)=X[K1]S−1L−1X[K2]...S−1L−1X[Kr](b)。
LSX暗号の置換
LSXアーキテクチャ暗号では、置換が大きな役割を果たします-全単射マッピング pin:Vn2\右矢印V2n どこで V_2 = \ {0、1 \}、V ^ n_2 = \ {0、1 \} ^ nV_2 = \ {0、1 \}、V ^ n_2 = \ {0、1 \} ^ n 。 不適切に選択された置換は、暗号全体の強度の低下につながり、最悪の場合、暗号に対する実際の攻撃につながる可能性があります。
AESアルゴリズム自体を段階的に分析することはしません。このリソースは2回以上実行されています。たとえば、 この記事、 この記事、そしてもちろん公式の暗号文書などのすばらしい記事を読むことができます 。
解決策
最初に何をするべきか全くわからない場合に、反射の論理を示すために、私は経験の浅い暗号作成者の観点からこの問題を解決する過程を再現しようとします。
知能
明らかに、このタスクの重要な特徴は脆弱な置換(Sボックス)であるため、この問題の調査は、微分特性のテーブルの構築から始まりました。 AESのような暗号に関しては古典的な微分法は実用的ではないという事実(ラウンド数が大きすぎる)にもかかわらず、修正されたSボックスに関するいくつかの有用な情報を抽出することができます。
そこで、テーブルを作成します。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Usage: python3 dc.py from itertools import product def dc(sbox): length = len(sbox) diff_table = [[0] * length for _ in range(length)] for c, d in product( *([list(range(length))]*2) ): diff_table[c ^ d][sbox[c] ^ sbox[d]] += 1 count_prob = 0 for c, d in product( *([list(range(length))]*2) ): if diff_table[c][d] == length: count_prob += 1 print('{} : {} -> {}'.format(diff_table[c][d], c, d)) return count_prob == length if __name__ == '__main__': # sboxm = [ <_> ] print(dc(sboxm))
そして、結果に驚くことはあり
さらに、この種の差分は推奨されます。 変更された置換のテーブルは、構造上の特徴により、つまり人為的に生成されます。 この場合、そのような順列を生成する可能な方法を見つけるといいでしょう。
暗号化の基本的なコースから、ブロック暗号におけるSボックスの主な役割は、暗号文に非線形性を導入すること(言い換えれば、プレーンテキストと暗号テキスト間の統計的関係の隠蔽を最大化すること)であり、私たちに与えられたSボックスはAES脆弱性であることが知られています-256-M、ターゲット機能を満たさないと想定できます。 したがって、おそらく、このSボックスは、ある範囲の数値に線形関数を適用した結果です。 \上線0、255 。
実験方法を使用して、次の形式の単純なアフィン関数 f(x)=Mx oplusv どこで
M= 001・0001&100001・1・01001・1・1・1・00001&0&1&1&0&11&0&1&0&1・1・1・1pmatrixの0・1・1・1・00011・1・0・1・1・1・1・10・1・1・1・1・0・1・1を開始\端pmatrixの -8x8バイナリマトリックス、
v= beginpmatrix0&0&1&0&1&0&1&1 endpmatrixT バイナリ8ビット列ベクトルです。
したがって、仮定が真実であることが判明しました:バイナリバイトの形式で置換バイトを表す場合、変更された置換は、入力ビットと出力ビットの間に線形関係を持つ上記のアフィン変換に変わります。
これで、このような置換を生成する小さなスクリプトを作成することもできます。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Usage: python3 generate_affine_sbox.py <sbox_length> import numpy as np import random import math import sys from itertools import product # ---------------------------------------------------------- # ---------------------- AFFINE SBOX ----------------------- # ---------------------------------------------------------- """ Affine function: y(x) = M*x + v, where M is 8x8 boolean matrix, v is 8-bit constant vector column, * is bitwise AND (&), + is bitwise XOR (^). """ def affine_function(x, M, v): Mx = (np.dot(M, x) % 2).T y = Mx ^ v return y def S(x, M, v, bin_length): raw_value = list(affine_function(to_bin(x, bin_length), M, v).flat) return int(''.join([ str(b) for b in raw_value ]), 2) def generate_affine_sbox(length): bin_length = len(bin(length-1)[2:]) np.random.seed() while True: M = np.random.randint(0, 2, size=(bin_length, bin_length)) if np.linalg.det(M).astype(int) % 2: break v = np.random.randint(0, 2, size=(1, bin_length)) sbox = [ S(i, M, v, bin_length) for i in range(length) ] if not any_duplicates(sbox) and is_sbox_degenerate(sbox): return (sbox, M, v) return (None, None, None) # ---------------------------------------------------------- # ----------------------- UTILITIES ------------------------ # ---------------------------------------------------------- def to_bin(number, bin_length): return np.array([ [int(b)] for b in bin(number)[2:].zfill(bin_length) ]) def print_sbox(name, sbox, length): dim = math.sqrt(length) print('{} = {{'.format(name), end='') if dim.is_integer(): print('\n\t', end='') for i in range(length): if not (i % dim) and i: print('\n\t', end='') print('{: >5}'.format(sbox[i]), end=', ') print('.\n}') else: for item in sbox: print(item, end=', ') print('.}') def any_duplicates(sbox): seen = set() for item in sbox: if item not in seen: seen.add(item) else: return True return False def is_sbox_degenerate(sbox): length = len(sbox) diff_table = [[0] * length for _ in range(length)] for c, d in product( *([list(range(length))]*2) ): diff_table[c ^ d][sbox[c] ^ sbox[d]] += 1 count_prob = 0 for c, d in product( *([list(range(length))]*2) ): if diff_table[c][d] == length: count_prob += 1 #print('{} : {} -> {}'.format(diff_table[c][d], c, d)) return count_prob == length # ---------------------------------------------------------- # -------------------------- MAIN -------------------------- # ---------------------------------------------------------- if __name__ == '__main__': if len(sys.argv) != 2: print('Usage: python3 {} <sbox_length>'.format(sys.argv[0])) sys.exit(1) try: length = int(sys.argv[1]) except ValueError: print("Invalid input type") sys.exit(1) if length & (length-1) != 0 or length < 1: print("Sbox length must be a power of two and > 1") sys.exit(1) while True: sbox, M, v = generate_affine_sbox(length) if sbox: print('M = \n{}\n'.format(repr(M))) print('v = \n{}\n'.format(repr(v))) print_sbox('Affine Sbox', sbox, length) break
攻撃準備
そのため、非線形であると想定されていた暗号の唯一のコンポーネントは非常に線形であることが判明したため、さらに移動する方向を推測するのは簡単です。
AES Rijndael暗号のすべての線形変換の代替ビット指向の表現を見つけるので、1つの大きな線形変換でブロックの各バイトに置換を適用する線形操作を「埋め込む」ことができます。
サブバイト
SubBytes操作、変換のみを表す S LSX暗号では、次のようになります a mapstoMa oplusv どこで a -暗号化されたブロック、 M そして v 上記のとおり。
したがって、SubBytes操作のビット指向表現は次の形式の例です。
A SBへマップ∗A oplusV どこで
A -128ビットブロック表現 a ;
SB= beginpmatrixM&0&0&...&00&M&0&...&00&0&M&...&0...&...&...&...&...0&0&0&...&M endpmatrix ; V= beginpmatrixvvv...v endpmatrix
Shiftift
[1]を参照して、ShiftRows操作の原因となるマトリックスを決定します。 このマトリックスの形式は次のとおりです。
SR= beginpmatrixI32x32&0&0&00&R2&0&00&0&R3&00&0&0&R4 endpmatrix、 どこで R= beginpmatrix0&I8x8&0&00&0&I8x8&00&0&0&I8x8I8x8&0&0&0 endpmatrix
そして、ShiftRows変換は次の形式を取ります。 A SRにマップ∗A 。
ミックスカラム
MDS= beginpmatrix02&03&01&0101&02&03&0101&01&02&0303&01&01&02 endpmatrix
ここでは難しいです。フィールドの上に元のMDSマトリックスが必要です Gf(28) 同等のフォームにつながる Gf(2) 。 これを行うには、フィールドの代数構造 Gf(28) 多項式環(変数 x )からの係数 Gf(2) 多項式を法とする f(x) 次数8( 別名因子リング GF(2)[x]/f(x) ) AESの場合 f(x)=x8+x4+x3+x+1 -既約 Gf(2) 8次の多項式。
次:からのアイテム Gf(28) 基本の合計として表す \ {1、x、x ^ 2、\ドット、x ^ 7 \} 。 次に、要素を乗算した結果を覚えておいてください 02=x すべての基本的に:
x∗1=x;x∗x=x2;x∗x2=x3;x∗x3=x4;x∗x4=x5;x∗x5=x6;x∗x6=x7;x∗x7=x8(modf(x))=x4+x3+x+1.
加算と乗算の分配性が満たされているため、上記の推論と組み合わせて、要素 02 からの係数を持つ行列として表すことができます Gf(2) :
C2= 1&00000011&00000000001−00001&0001・0001&00001&0&00000001&0pmatrixの0&1&000000001・00000を開始\端pmatrixの
次に、要素が 01 恒等行列に入ります C1=I8x8 、および要素 03 に行きます C3=C2 oplusC1 。
私たちの推論を確認するために、あなたは[2]を見ることができます。そこでは同様の行列が提案されます C2 ビット指向の要素乗算用 02 から Gf(28) 。
その結果、MixColumnsの元の変換中にMDSマトリックスの行に stateのstate 列が乗算されるという事実を考慮して、マトリックスを構成します。 MC この変換のビット指向の表現(ネタバレの下)。
( C2 00 00 00 C3 00 00 00 C1 00 00 00 C1 00 00 00 )
( 00 C2 00 00 00 C3 00 00 00 C1 00 00 00 C1 00 00 )
( 00 00 C2 00 00 00 C3 00 00 00 C1 00 00 00 C1 00 )
( 00 00 00 C2 00 00 00 C3 00 00 00 C1 00 00 00 C1 )
( C1 00 00 00 C2 00 00 00 C3 00 00 00 C1 00 00 00 )
( 00 C1 00 00 00 C2 00 00 00 C3 00 00 00 C1 00 00 )
( 00 00 C1 00 00 00 C2 00 00 00 C3 00 00 00 C1 00 )
( 00 00 00 C1 00 00 00 C2 00 00 00 C3 00 00 00 C1 )
( C1 00 00 00 C1 00 00 00 C2 00 00 00 C3 00 00 00 )
( 00 C1 00 00 00 C1 00 00 00 C2 00 00 00 C3 00 00 )
( 00 00 C1 00 00 00 C1 00 00 00 C2 00 00 00 C3 00 )
( 00 00 00 C1 00 00 00 C1 00 00 00 C2 00 00 00 C3 )
( C3 00 00 00 C1 00 00 00 C1 00 00 00 C2 00 00 00 )
( 00 C3 00 00 00 C1 00 00 00 C1 00 00 00 C2 00 00 )
( 00 00 C3 00 00 00 C1 00 00 00 C1 00 00 00 C2 00 )
( 00 00 00 C3 00 00 00 C1 00 00 00 C1 00 00 00 C2 )
そして、対応する変換は次の形式を取ります。 A MCにマッピング∗A 。
攻撃
分析はほぼ完了しました。攻撃に必要なすべてを準備します。
一回
AES-256-M暗号の1ラウンドを検討してください。
標準形式:
a AddRoundKey(MixColumns(ShiftRows(SubBytes(a))))にマップします
行列形式で Gf(2) :
A MCにマップ∗SR∗(SB∗A oplusV) oplusKi=MC∗SR∗SB∗A oplusMC∗SR∗V oplusKi、
しかし、以来 MC∗V=SR∗V=V それから
A MCにマップ∗SR∗SB∗A oplusV oplusKi=L∗A oplusV oplusKi、
どこで L=MC∗SR∗SB -AES-256-Mの線形散乱層全体を表すマトリックス。
数ラウンド
マトリックス形式のAES-256-M暗号の15ラウンド(14 +キー初期化ラウンド)を検討してください。
L(L(...L(...L(P0 oplusK0) oplusK1 oplusV) oplusK2 oplusV) oplus dots oplusK13 oplusV) oplusK14 oplusV)=C0。
括弧を展開します。
L14P0 oplusL14K0 oplusL13K1 oplusL13V oplus dots oplusLK13 oplusLV oplusK14 oplusV=C0;
L14P0 oplus(L13 oplusL12 oplus dots oplusL oplusI)V oplusmkey=C0、
どこで mkey=L14K0 oplusL13K1 oplusL12K2 oplus dots oplusLK13 oplusK14 -条件付きの「マスターキー」。これは、他のブロックの暗号化解除に使用できます(暗号化はECBモードで実行されるため)。
mkey=C0 oplusL14P0 oplus(L13 oplusL12 oplus dots oplusL oplusI)V;
Pi=L−14(Ci oplusmkey oplus(L13 oplusL12 oplus dots oplusL oplusI)V)=
=L−14(Ci oplusC0 oplusL14P0)=P0 oplusL−14(Ci oplusC0);
そして、ここに暗号文の残りのブロックを解読するための非常に「魔法の」公式があります。
Pi=P0 oplusL−14(C0 oplusCi)。
ご注意
AES Rijndaelの元の実装では、 MixColumns操作は、この時点での無駄のため、最後の暗号ラウンドでは省略されています。 このソリューションでは、結果のデモンストレーションを簡素化するために、これは考慮されていません(つまり、最後のラウンドキーでの追加後に MixColumns も使用されます)。
結論、コード、文献
L−14
まず、マトリックス L−14 セージ代数装置を使用して簡単に取得できます。
sage: L = matrix(ZZ, [ <__L> ]).base_extend(GF(2)) sage: invL = L.inverse() sage: invL14 = invL^(14) sage: invL14.str()
ソースコード
ハッキングの進行状況を「ライブ」で示す小さなソフトウェアデモがGithubに投稿されています。
おわりに
この種のタスクはどうですか? このようなタスクは、「カスタム」置換(さらには擬似ランダムに見えるように見える、さらにはNIST統計テストの一部に合格する、 以下のネタバレ参照 )など、元のアルゴリズムの見かけ上重要でない修正がどのように縮退するかを示す優れた例ですよく知られたマトリックスを使用した、単純なアフィン変換への暗号化の国際標準。
- モノビット周波数テスト;
- ブロック周波数テスト;
- テストを実行します。
- シリアルテスト
- 近似エントロピーテスト。
- 累積合計テスト。
ご清聴ありがとうございました!
参照資料
- Carlos Cid、Sean Murphy、Matthew Robshawによる高度暗号化標準の代数的側面
- Advances in Cryptology-CRYPTO 2015-35th Annual Cryptology Conference、Santa Barbara、CA、USA、August 16-20、2015、Proceedings、Part 1