変更されたAES-256の破壊

最近、研究所で、コミュニティと共有したい奇妙な暗号化タスクに出会いました。 1つの文で、タスクを「 プレーンテキストに基づいて非線形コンポーネントを含まないLSX暗号に対する攻撃 」と指定できます。 このような教育的な「パズル」を解決するロシア語の例はほとんどなく、タスク自体は初心者に推奨されているため、このような記事は若い暗号解読者にとって興味深いものになると思います。 猫の下に来てください。

画像










状態



バイト置換  pi8 AES-256暗号は、フィールド内の特定の操作シーケンスによって決定されます Gf28 また、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加算 Kii=\上1r どこで r -暗号のラウンド数);

S -固定置換の使用  pi ブロックの各バイト;

L -線形変換。



概略的に、LSX変換は次のように表すことができます

画像






LSX暗号の各ラウンドは、個別のラウンドキーを使用します Ki 何らかの方法で主キーから生成された K 。 主キーのビット長 K 通常、反復キーの長さ以上です。 プライマリからの反復キーのキースキャン手順は、暗号によって大きく異なります。



一般的な暗号化変換式 EncKa ラウンド数が等しいLSX暗号の場合 r 次のように記述できます。





b=EncKa=X[Kr]LSX[Kr1]...LSX[K2]LSX[K1]a







概略的に、LSX暗号の一般的な構造は次のように表すことができます



画像






復号化プロセスは、逆変換を使用して実行されます。



X -反復キーを持つ入力ブロックのモジュロ2加算。

S1 -逆を適用する S ルックアップ;

L1 -逆の適用 L -変換( SS1=I -単一置換)。



復号式 DecKbr ラウンドLSX暗号:





a=DecKb=X[K1]S1L1X[K2]...S1L1X[Kr]b







LSX暗号の置換



LSXアーキテクチャ暗号では、置換が大きな役割を果たします-全単射マッピング  pinVn2\右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))
      
      





そして、結果に驚くことはありません。テーブルが縮退した形をしていることがわかります。各行には256/256の確率のレコードがあります(残りのレコードはそれぞれ0です)。



さらに、この種の差分は推奨されます。 変更された置換のテーブルは、構造上の特徴により、つまり人為的に生成されます。 この場合、そのような順列を生成する可能な方法を見つけるといいでしょう。



暗号化の基本的なコースから、ブロック暗号におけるSボックスの主な役割は、暗号文に非線形性を導入すること(言い換えれば、プレーンテキストと暗号テキスト間の統計的関係の隠蔽を最大化すること)であり、私たちに与えられたSボックスはAES脆弱性であることが知られています-256-M、ターゲット機能を満たさないと想定できます。 したがって、おそらく、このSボックスは、ある範囲の数値に線形関数を適用した結果です。 \上0255



実験方法を使用して、次の形式の単純なアフィン関数 fx=Mx oplusv どこで



M= 0010001100001101001111000010110110101111pmatrix011100011101111101111011\端pmatrix -8x8バイナリマトリックス、



v= beginpmatrix00101011 endpmatrixT バイナリ8ビット列ベクトルです。



したがって、仮定が真実であることが判明しました:バイナリバイトの形式で置換バイトを表す場合、変更された置換は、入力ビットと出力ビットの間に線形関係を持つ上記のアフィン変換に変わります。



これで、このような置換を生成する小さなスクリプトを作成することもできます。

長さ2 ^ nの縮退順列を生成します
 #!/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 SBA oplusV どこで

A -128ビットブロック表現 a ;



SB= beginpmatrixM00...00M0...000M...0...............000...M endpmatrix ; V= beginpmatrixvvv...v endpmatrix



Shiftift



[1]を参照して、ShiftRows操作の原因となるマトリックスを決定します。 このマトリックスの形式は次のとおりです。



SR= beginpmatrixI32x320000R20000R30000R4 endpmatrix どこで R= beginpmatrix0I8x80000I8x80000I8x8I8x8000 endpmatrix



そして、ShiftRows変換は次の形式を取ります。 A SRA



ミックスカラム







MDS= beginpmatrix02030101010203010101020303010102 endpmatrix







ここでは難しいです。フィールドの上に元のMDSマトリックスが必要です Gf28 同等のフォームにつながる Gf2 。 これを行うには、フィールドの代数構造 Gf28 多項式環(変数 x )からの係数 Gf2 多項式を法とする fx 次数8( 別名因子リング GF2[x]/fx ) AESの場合 fx=x8+x4+x3+x+1 -既約 Gf2 8次の多項式。



次:からのアイテム Gf28 基本の合計として表す \ {1、x、x ^ 2、\ドット、x ^ 7 \} 。 次に、要素を乗算した結果を覚えておいてください 02=x すべての基本的に:





x1=x;xx=x2;xx2=x3;xx3=x4;xx4=x5;xx5=x6;xx6=x7;xx7=x8modfx=x4+x3+x+1.







加算と乗算の分配性が満たされているため、上記の推論と組み合わせて、要素 02 からの係数を持つ行列として表すことができます Gf2





C2= 100000011000000000010000100010001000010000000010pmatrix0100000000100000\端pmatrix







次に、要素が 01 恒等行列に入ります C1=I8x8 、および要素 03 に行きます C3=C2 oplusC1



私たちの推論を確認するために、あなたは[2]を見ることができます。そこでは同様の行列が提案されます C2 ビット指向の要素乗算用 02 から Gf28



その結果、MixColumnsの元の変換中にMDSマトリックスの行に stateのstate が乗算されるという事実を考慮して、マトリックスを構成します。 MC この変換のビット指向の表現(ネタバレの下)。

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 MCA



攻撃



分析はほぼ完了しました。攻撃に必要なすべてを準備します。



一回



AES-256-M暗号の1ラウンドを検討してください。



標準形式:





a AddRoundKeyMixColumnsShiftRowsSubBytesa







行列形式で Gf2





A MCSRSBA oplusV oplusKi=MCSRSBA oplusMCSRV oplusKi







しかし、以来 MCV=SRV=V それから





A MCSRSBA oplusV oplusKi=LA oplusV oplusKi







どこで L=MCSRSB -AES-256-Mの線形散乱層全体を表すマトリックス。



数ラウンド



マトリックス形式のAES-256-M暗号の15ラウンド(14 +キー初期化ラウンド)を検討してください。





LL...L...LP0 oplusK0 oplusK1 oplusV oplusK2 oplusV oplus dots oplusK13 oplusV oplusK14 oplusV=C0







括弧を展開します。





L14P0 oplusL14K0 oplusL13K1 oplusL13V oplus dots oplusLK13 oplusLV oplusK14 oplusV=C0;









L14P0 oplusL13 oplusL12 oplus dots oplusL oplusIV oplusmkey=C0







どこで mkey=L14K0 oplusL13K1 oplusL12K2 oplus dots oplusLK13 oplusK14 -条件付きの「マスターキー」。これは、他のブロックの暗号化解除に使用できます(暗号化はECBモードで実行されるため)。





mkey=C0 oplusL14P0 oplusL13 oplusL12 oplus dots oplusL oplusIV;









Pi=L14Ci oplusmkey oplusL13 oplusL12 oplus dots oplusL oplusIV=









=L14Ci oplusC0 oplusL14P0=P0 oplusL14Ci oplusC0;







そして、ここに暗号文の残りのブロックを解読するための非常に「魔法の」公式があります。





Pi=P0 oplusL14C0 oplusCi







ご注意



AES Rijndaelの元の実装では、 MixColumns操作は、この時点での無駄のため、最後の暗号ラウンドでは省略されています。 このソリューションでは、結果のデモンストレーションを簡素化するために、これは考慮されていません(つまり、最後のラウンドキーでの追加後に MixColumns 使用さます)。



結論、コード、文献



L14



まず、マトリックス L14 セージ代数装置を使用して簡単に取得できます。

方法
 sage: L = matrix(ZZ, [ <__L> ]).base_extend(GF(2)) sage: invL = L.inverse() sage: invL14 = invL^(14) sage: invL14.str()
      
      







ソースコード



ハッキングの進行状況を「ライブ」で示す小さなソフトウェアデモがGithubに投稿されています。



おわりに



この種のタスクはどうですか? このようなタスクは、「カスタム」置換(さらには擬似ランダムに見えるように見える、さらにはNIST統計テストの一部に合格する、 以下のネタバレ参照 )など、元のアルゴリズムの見かけ上重要でない修正がどのように縮退するかを示す優れた例ですよく知られたマトリックスを使用した、単純なアフィン変換への暗号化の国際標準。

NIST統計的テストスイート
渡された変更されたルックアップ:

  • モノビット周波数テスト;
  • ブロック周波数テスト;
  • テストを実行します。
  • シリアルテスト
  • 近似エントロピーテスト。
  • 累積合計テスト。




ご清聴ありがとうございました!



参照資料



  1. Carlos Cid、Sean Murphy、Matthew Robshawによる高度暗号化標準の代数的側面
  2. Advances in Cryptology-CRYPTO 2015-35th Annual Cryptology Conference、Santa Barbara、CA、USA、August 16-20、2015、Proceedings、Part 1



All Articles