pythonで暗号演算 ~AES-GCMで認証付き暗号文生成~

python,暗号演算,python,暗号演算

今回はpythonのPyCryptodomeを使用して、AES-GCM認証付き暗号文生成を実施します。

初めに

AES-GCM(Galois/Counter Mode)は、AESの暗号利用モードのうちの一つです。

以前各種AESの暗号利用モード(ECB,CBC,CMAC,CTR)を取り上げてきましたが、AES-GCMは暗号化と同時に認証子(MAC)を生成するという点でほかの暗号利用モードとは異なります。

ブロックサイズは128bit(16bytes)であり、生成するMAC値のサイズも同サイズとなります。

また、GCMのCMがカウンタモードを意味するように、AES-GCMの暗号化はCTRを使用しています。

一方でMACの生成については、ガロアモードと呼ばれる暗号処理を用いて行われます。

TLS通信においては、メッセージの暗号化と、MAC生成を並行して行えることからパフォーマンス面で優れています。

詳細については以下RFC 5288を参照してみてください。

ノンスについて

AES-GCMは暗復号にてノンスと呼ばれる12byteの初期値を与える必要があります。

使用されるノンスはメッセージと鍵の組み合わせに対して一意である必要があります。

ノンス(Nonce)とはNumber used onceの略称であり、決して再利用してはならない値です。

上記前提を守らなかった場合、暗号演算における安全性は大きく損なわれます。

TLS通信時のノンス

TLSにおいてAES-GCMで使用されるノンスは、4byteのソルトと8byteのノンスの連結となります。

4byteのソルトは、ハンドシェイク毎の固定値を利用します。

8byteのノンス送信者が任意の値を割り当てます。

これらの値は通信において暗号化せずに通信相手に渡されます。

そのため、同じ値を使用してしまうと簡単に第三者に知られてしまう危険性があります。

AES-GMAC

暗号化はせず、ガロアモードでMAC生成のみを実施する暗号利用モードとしてAES-GMACがあります。

実装

以下参考にPyCryptodomeライブラリを使用して、実際にAES-GCMによる暗号演算を実施します。

ソース

macの扱いが増えている以外は、ほかのAES処理と変わらないですね。

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes


key = get_random_bytes(16)
nonce = get_random_bytes(12)
data = b"secret"

def main():
    # AES_GCM暗号化
    ciphertext, mac = aes_gcm_encrypt(key,nonce,data)
    print(ciphertext, mac)
    # AES_GCM復号化
    plaintext = aes_gcm_decrypt(key,nonce,ciphertext,mac)
    print(plaintext)


# AES_GCM暗号化
def aes_gcm_encrypt(key,iv,text):
    cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
    ciphertext, mac = cipher.encrypt_and_digest(text)
    return ciphertext, mac


# AES_GCM復号化
def aes_gcm_decrypt(key,iv,ciphertext,mac):
    plaintext = 0
    cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
    try:
        plaintext = cipher.decrypt_and_verify(ciphertext,mac)
    except (ValueError, KeyError):
        print("Incorrect decryption")
    return plaintext


if __name__ == '__main__':
	main()

出力結果

b'\xfb\xe9G\x19\x90\xc5' b'\xd25%\xed/\xc4\x9b\xbb\xfe0\x9f\xb1\x9a~%\x8d'
b'secret'

MAC値を適当な値にするとエラーとなります。

b'\xf5\x12>;\xb5v' b'\xd9_(\xe7y\xe1"(\xf1\xbf:\x18\'\x8d\x97\xa3'
Incorrect decryption
0