pythonで暗号演算 ~AESまとめとGUIアプリ作成~
今回はこれまで取り上げたpythonによるAESの暗号演算処理(ECB、CBC、CTR、CMAC)のまとめと、任意の暗号演算を行えるようなアプリ作成を行ってみたいと思います。
暗号演算はpycryptodome、GUIではPySimpleGUIのライブラリを使用します。
各暗号利用モードごとのIF
以下にざっとIFをまとめてみました。
CMACのpaddingを「△」としているのは、RFC 4493で明示されているパディング処理をライブラリで実施してくれるからです。
ECBとCBCはPKCS#5、PKCS#7のパディングを使用します。
AESで扱える鍵長は128ビット、192ビット、256ビットの3種類があります。またいずれの鍵長の場合も、暗号処理のブロック単位が128ビットであることは変わりありません。
ソースコード
aes関連の処理は以下にまとめました。
import Crypto.Cipher.AES as AES
from Crypto.Util import Counter
from Crypto.Hash import CMAC
# ECB暗号化
def aes_ecb_enc_fnc( key , txt ):
cipher = AES.new( key, AES.MODE_ECB )
padding_txt = padding_fnc(txt)
enc_txt = cipher.encrypt(padding_txt)
return enc_txt
# ECB復号化
def aes_ecb_dec_fnc( key , txt ):
decipher = AES.new( key, AES.MODE_ECB )
dec_txt = decipher.decrypt(txt)
return dec_txt
# CBC暗号化
def aes_cbc_enc_fnc( key , txt , iv ):
cipher = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
padding_txt = padding_fnc(txt)
enc_txt = cipher.encrypt(padding_txt)
return enc_txt
# CBC復号化
def aes_cbc_dec_fnc( key , txt , iv ):
decipher = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
dec_txt = decipher.decrypt(txt)
return dec_txt
# CTR暗号化
def aes_ctr_enc_fnc( key , txt , nonce , counter ):
counter_size = 128 - len(nonce)*8
ctr = Counter.new(counter_size, prefix=nonce, little_endian=False, initial_value=counter)
cipher = AES.new(key=key, mode=AES.MODE_CTR , counter=ctr)
enc_txt = cipher.encrypt(txt)
return enc_txt
# CTR復号化
def aes_ctr_dec_fnc( key , txt , nonce , counter ):
counter_size = 128 - len(nonce)*8
ctr = Counter.new(counter_size, prefix=nonce, little_endian=False, initial_value=counter)
decipher = AES.new(key=key, mode=AES.MODE_CTR , counter=ctr)
dec_txt = decipher.decrypt(txt)
return dec_txt
# MAC生成
def aes_cmac_fnc( key , txt ):
h = CMAC.new(key, txt, ciphermod=AES)
mac = h.digest()
return mac
# パディング(PKCS#5、PKCS#7)
def padding_fnc( data ):
padding_data = data
if 0 != (len(data) % 16):
padding_size = 16 - (len(data) % 16)
for i in range(padding_size):
padding_data += padding_size.to_bytes(1, 'little')
return padding_data
GUI周りは以下。今回もPySimpleGUIを使用しました。
# coding: utf -8
import PySimpleGUI as sg # ライブラリの読み込み
import re
import aes
# テーマの設定
# sg.theme("Dark Blue 3 ")
# ドメイン設定
L1 = [
# 暗号利用モード
[sg.Text("・暗号利用モード ", size=(20, 1)),
sg.OptionMenu(["ECB","CBC", "CTR", "CMAC"],
background_color="#ffffff",
default_value="ECB",
size=(5, 1),
key="-MODE-"),
sg.OptionMenu(["暗号化", "復号化"],
background_color="#ffffff",
default_value="暗号化",
size=(5, 1),
key="-TYPE-")],
# 鍵値
[sg.Text("・鍵値 ", size=(20, 1)),
sg.InputText(default_text="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
text_color="#000000",
background_color="#ffffff",
size=(45, 1),
key="-KEY-")],
# IV
[sg.Text("・IV ", size=(20, 1)),
sg.InputText(default_text="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
text_color="#000000",
background_color="#ffffff",
key="-IV-",
size=(45, 1))],
# Nonce
[sg.Text("・Nonce ", size=(20, 1)),
sg.InputText(default_text="FFFFFFFFFFFFFFFF",
text_color="#000000",
background_color="#ffffff",
key="-NONCE-",
size=(45, 1))],
# カウンタ
[sg.Text("・カウンタ ", size=(20, 1)),
sg.InputText(default_text="0",
text_color="#000000",
background_color="#ffffff",
key="-COUNTER-",
size=(45, 1))],
# 入力値(平文 0r 暗号)
[sg.Text("・入力値 (平文 or 暗号文)", size=(40, 1))],
[sg.Multiline(default_text="",
text_color="#000000",
background_color="#ffffff",
size=(100, 10),
key="-INPUT_TXT-")],
# 出力値
[sg.Text("・出力値 (平文 or 暗号文 or MAC)", size=(40, 1))],
[sg.Multiline(default_text="",
text_color="#000000",
# background_color="#ffff00",
size=(100, 10),
key="-OUTPUT_TXT-")],
[sg.Button("実行", border_width=4, size=(15, 1), key="aes_start")]]
# ウィンドウ作成
window = sg.Window("AES_TOOL ", L1)
def main():
# イベントループ
while True:
# イベントの読み取り(イベント待ち)
event, values = window.read()
if event == "aes_start":
# 不要要素の削除
input_txt = re.sub('[^0123456789abcdefABCDEF]', '', values["-INPUT_TXT-"])
# テキストサイズチェック
if 0 == ( len(input_txt) % 2 ):
input_txt = bytes.fromhex(input_txt)
key = bytes.fromhex(re.sub('[^0123456789abcdefABCDEF]', '', values["-KEY-"]))
output_txt = ""
if "ECB" == values["-MODE-"]:
if "暗号化" == values["-TYPE-"]:
output_txt = aes.aes_ecb_enc_fnc( key , input_txt )
else:
output_txt = aes.aes_ecb_dec_fnc( key , input_txt )
elif "CBC" == values["-MODE-"]:
if "暗号化" == values["-TYPE-"]:
output_txt = aes.aes_cbc_enc_fnc( key , input_txt , bytes.fromhex(re.sub('[^0123456789abcdefABCDEF]', '', values["-IV-"]) ))
else:
output_txt = aes.aes_cbc_dec_fnc( key , input_txt , bytes.fromhex(re.sub('[^0123456789abcdefABCDEF]', '', values["-IV-"]) ))
elif "CTR" == values["-MODE-"]:
if "暗号化" == values["-TYPE-"]:
output_txt = aes.aes_ctr_enc_fnc( key , input_txt , bytes.fromhex(re.sub('[^0123456789abcdefABCDEF]', '', values["-NONCE-"])) , int(values["-COUNTER-"]) )
else:
output_txt = aes.aes_ctr_dec_fnc( key , input_txt , bytes.fromhex(re.sub('[^0123456789abcdefABCDEF]', '', values["-NONCE-"])) , int(values["-COUNTER-"]) )
elif "CMAC" == values["-MODE-"]:
output_txt = aes.aes_cmac_fnc( key , input_txt )
window["-OUTPUT_TXT-"].Update(output_txt.hex())
else:
window["-OUTPUT_TXT-"].Update("入力値不正")
# 終了条件( None: クローズボタン)
elif event is None:
break
# 終了処理
window.close()
if __name__ == '__main__':
main()
以下に同コードを上げました。
出力
実行すると次のGUIが起動します。HEX値で入出力を実施するため、入力領域に外れ値(スペース等)があった場合は置換して暗号処理を実施します。
テストベクター
CBC、CTR、CMACについて各RFCからテストベクターを引っ張ってきて動作確認を実施しました。
AES-CBCテストベクター
RFC 3602 – The AES-CBC Cipher Algorithm and Its Use with IPsec
Case #1
Key : 0x06a9214036b8a15b512e03d534120006
IV : 0x3dafba429d9eb430b422da802c9fac41
Plaintext : 0x53696e676c6520626c6f636b206d7367 (“Single block msg")
Ciphertext: 0xe353779c1079aeb82708942dbe77181a
Case #2
Key : 0xc286696d887c9aa0611bbb3e2025a45a
IV : 0x562e17996d093d28ddb3ba695a2e6f58
Plaintext : 0x000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f
Ciphertext: 0xd296cd94c2cccf8a3a863028b5e1dc0a 7586602d253cfff91b8266bea6d61ab1
AES-CTRテストベクター
Test Vector #1
AES Key : AE 68 52 F8 12 10 67 CC 4B F7 A5 76 55 77 F3 9E
Plaintext : 53 69 6E 67 6C 65 20 62 6C 6F 63 6B 20 6D 73 67
Nonce、カウンタ: 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 01
Ciphertext : E4 09 5D 4F B7 A7 B3 79 2D 61 75 A3 26 13 11 B8
Test Vector #2
Key : 7E 24 06 78 17 FA E0 D7 43 D6 CE 1F 32 53 91 63
Plaintext : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
Nonce、カウンタ: 00 6C B6 DB C0 54 3B 59 DA 48 D9 0B 00 00 00 01
Ciphertext : 51 04 A1 06 16 8A 72 D9 79 0D 41 EE 8E DA D3 88 : EB 2E 1E FC 46 DA 57 C8 FC E6 30 DF 91 41 BE 28
AES-CMACテストベクター
RFC 4493 – The AES-CMAC Algorithm
Key:2b7e1516 28aed2a6 abf71588 09cf4f3c
Example 1: len = 0
AES-CMAC:bb1d6929 e9593728 7fa37d12 9b75674
Example 2: len = 16
M:6bc1bee2 2e409f96 e93d7e11 7393172a
AES-CMAC:070a16b4 6b4d4144 f79bdd9d d04a287c
Example 3: len = 40
M:6bc1bee2 2e409f96 e93d7e11 7393172aae2d8a57 1e03ac9c 9eb76fac 45af8e5130c81c46 a35ce411
AES-CMAC:dfa66747 de9ae630 30ca3261 1497c827
その他
今回重要視していなかったECBやAES以外のテストベクターについて、NISTの公式からもいろいろなテストベクターが公開されています。
機会があれば参考にしてみてください。