kryptografia-semestr-8/gcm.py

267 lines
8.9 KiB
Python

import math
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import GCM, ECB
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
def aes_gcm_authenticated_decryption(key, iv, auth_tag, associated_data, ciphertext):
aes_gcm_decryptor = Cipher(AES(key), GCM(iv, auth_tag)).decryptor()
aes_gcm_decryptor.authenticate_additional_data(associated_data)
recovered_plaintext = aes_gcm_decryptor.update(ciphertext) + aes_gcm_decryptor.finalize()
return recovered_plaintext, auth_tag
def xor_bytes(bytes_a: bytes, bytes_b: bytes) -> bytes:
return bytes([a ^ b for (a, b) in zip(bytes_a, bytes_b)])
def MUL(X_bytes, Y_bytes):
X = int.from_bytes(X_bytes, 'big')
Y = int.from_bytes(Y_bytes, 'big')
# Constant R defined for algorithm
R = 0xe1 << 120
# Step 1
x = [1 if X & (1 << i) else 0 for i in range(127, -1, -1)]
# Steps 2 and 3
Z_i = 0
V_i = Y
for i in range(128):
if x[i] == 0:
Z_i_1 = Z_i
else:
Z_i_1 = Z_i ^ V_i
if V_i % 2 == 0:
V_i_1 = V_i >> 1
else:
V_i_1 = (V_i >> 1) ^ R
Z_i = Z_i_1
V_i = V_i_1
# Step 4
return Z_i.to_bytes(16, 'big')
def GHASH(H, X):
# Input constraint: len(X) = 128m
# 128/8=16 bo bajty
m = len(X) // 16
# Step 1
X_blocks = [X[i * 16:(i + 1) * 16] for i in range(m)]
# Step 2
# b'\x00' = 00000000
Y_0 = b'\x00' * 16
# Step 3
Y_i_1 = Y_0
for i in range(m):
X_i = X_blocks[i]
Y_i = MUL(xor_bytes(Y_i_1, X_i), H)
Y_i_1 = Y_i
# Step 4
return Y_i_1
def INC_32(Y_bytes):
Y = int.from_bytes(Y_bytes, 'big')
Y_inc = ((Y >> 32) << 32) ^ (((Y & 0xffffffff) + 1) & 0xffffffff)
return Y_inc.to_bytes(16, 'big')
def GCTR(K, ICB, X):
# Step 1
if not X:
return b''
# Step 2
n = math.ceil(len(X) / 16)
# Step 3
X_blocks = [X[i * 16:(i + 1) * 16] for i in range(n)]
# Step 4
CB = [ICB]
# Step 5
for i in range(1, n):
CB_i = INC_32(CB[i - 1])
CB.append(CB_i)
# Steps 6 and 7
Y_blocks = []
for i in range(n):
X_i = X_blocks[i]
CB_i = CB[i]
aes_encryptor = Cipher(AES(K), ECB()).encryptor()
ciphertext = aes_encryptor.update(CB_i) + aes_encryptor.finalize()
# Y_i = xor_bytes(X_i, aes_encryption(CB_i, K))
Y_i = xor_bytes(X_i, ciphertext)
Y_blocks.append(Y_i)
# Step 8
Y = b''.join(Y_blocks)
# Step 9
return Y
def aes_gcm_encrypt(P, K, IV, A, t):
# Step 1
data = (b'\x00' * 16)
aes_encryptor = Cipher(AES(K), ECB()).encryptor()
H = aes_encryptor.update(data) + aes_encryptor.finalize()
# Step 2
len_IV = len(IV) * 8
if len_IV == 96:
J_0 = IV + b'\x00\x00\x00\x01'
else:
s = 128 * math.ceil(len_IV / 128) - len_IV
O_s_64 = b'\x00' * ((s + 64) // 8)
len_IV_64 = int.to_bytes(len_IV, 8, 'big')
J_0 = GHASH(H, IV + O_s_64 + len_IV_64)
# Step 3
C = GCTR(K, INC_32(J_0), P)
# Step 4
len_C, len_A = len(C) * 8, len(A) * 8
u = 128 * math.ceil(len_C / 128) - len_C
v = 128 * math.ceil(len_A / 128) - len_A
# Step 5
O_v = b'\x00' * (v // 8)
O_u = b'\x00' * (u // 8)
len_A_64 = int.to_bytes(len_A, 8, 'big')
len_C_64 = int.to_bytes(len_C, 8, 'big')
S = GHASH(H, A + O_v + C + O_u + len_A_64 + len_C_64)
# Step 6
T = GCTR(K, J_0, S)[:t // 8] # Assumes tag length multiple of 8
# Step 7
return C, T
if __name__ == "__main__":
# NIST Special Publication 800-38D
# NIST test vector 1
key = bytearray.fromhex('11754cd72aec309bf52f7687212e8957')
iv = bytearray.fromhex('3c819d9a9bed087615030b65')
plaintext = bytearray.fromhex('')
associated_data = bytearray.fromhex('')
expected_ciphertext = bytearray.fromhex('')
expected_tag = bytearray.fromhex('250327c674aaf477aef2675748cf6971')
tag_length = 128
ciphertext, auth_tag = aes_gcm_encrypt(plaintext, key, iv, associated_data, tag_length)
assert (ciphertext == expected_ciphertext)
assert (auth_tag == expected_tag)
message, _ = aes_gcm_authenticated_decryption(key, iv, auth_tag, associated_data, ciphertext)
print("wiadomosc:")
print(plaintext)
print("otrzymana wiadomosc:")
print(message)
# NIST test vector 2
key = bytearray.fromhex('fe47fcce5fc32665d2ae399e4eec72ba')
iv = bytearray.fromhex('5adb9609dbaeb58cbd6e7275')
plaintext = bytearray.fromhex('7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1b840382c4bccaf'
'3bafb4ca8429bea063')
associated_data = bytearray.fromhex('88319d6e1d3ffa5f987199166c8a9b56c2aeba5a')
expected_ciphertext = bytearray.fromhex('98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf5393043736365253ddb'
'c5db8778371495da76d269e5db3e')
expected_tag = bytearray.fromhex('291ef1982e4defedaa2249f898556b47')
tag_length = 128
ciphertext, auth_tag = aes_gcm_encrypt(plaintext, key, iv, associated_data, tag_length)
assert (ciphertext == expected_ciphertext)
assert (auth_tag == expected_tag)
message, _ = aes_gcm_authenticated_decryption(key, iv, auth_tag, associated_data, ciphertext)
print("wiadomosc:")
print(plaintext)
print("otrzymana wiadomosc:")
print(message)
# NIST test vector 3
key = bytearray.fromhex('c7d9358af0fd737b118dbf4347fd252a')
iv = bytearray.fromhex('83de9fa52280522b55290ebe3b067286d87690560179554153cb3341a04e15c5f35390602fa07e5b5f16dc38cf0'
'82b11ad6dd3fab8552d2bf8d9c8981bbfc5f3b57e5e3066e3df23f078fa25bce63d3d6f86ce9fbc2c679655b958'
'b09a991392eb93b453ba6e7bf8242f8f61329e3afe75d0f8536aa7e507d75891e540fb1d7e')
plaintext = bytearray.fromhex('422f46223fddff25fc7a6a897d20dc8af6cc8a37828c90bd95fa9b943f460eb0a26f29ffc483592efb64'
'835774160a1bb5c0cd')
associated_data = bytearray.fromhex('5d2b9a4f994ffaa03000149956c8932e85b1a167294514e388b73b10808f509ea73c075ecbf43c'
'ecfec13c202afed62110dabf8026d237f4e765853bc078f3afe081d0a1f8d8f7556b8e42acc3cc'
'e888262185048d67c55b2df1')
expected_ciphertext = bytearray.fromhex('86eba4911578ac72ac30c25fe424da9ab625f29b5c00e36d2c24a2733dc40123dc57a8c9f1'
'7a24a26c09c73ad4efbcba3bab5b')
expected_tag = bytearray.fromhex('492305190344618cab8b40f006a57186')
tag_length = 128
ciphertext, auth_tag = aes_gcm_encrypt(plaintext, key, iv, associated_data, tag_length)
assert (ciphertext == expected_ciphertext)
assert (auth_tag == expected_tag)
message, _ = aes_gcm_authenticated_decryption(key, iv, auth_tag, associated_data, ciphertext)
print("wiadomosc:")
print(plaintext)
print("otrzymana wiadomosc:")
print(message)
print("\n" * 10)
# wlasne
key = AESGCM.generate_key(bit_length=128) # bytearray.fromhex('11754cd72aec309bf52f7687212e8957')
iv = bytearray.fromhex('3c819d9a9bed087615030b65')
plaintext = "wiadomosc1".encode()
associated_data = "potwierdzenie".encode()
ciphertext, auth_tag = aes_gcm_encrypt(plaintext, key, iv, associated_data, tag_length)
print(ciphertext)
print(auth_tag)
message, _ = aes_gcm_authenticated_decryption(key, iv, auth_tag, associated_data, ciphertext)
print(message)
data = b"wiadomosc1"
aad = b"potwierdzenie"
aesgcm = AESGCM(key)
nonce = iv # os.urandom(12)
ct = aesgcm.encrypt(nonce, data, aad)
print(ct)
msg = aesgcm.decrypt(nonce, ct, aad)
print(msg)
# przykladowe dane 10
print("\n" * 5)
# wlasne
key = bytearray.fromhex('feffe9928665731c6d6a8f9467308308feffe9928665731c')
iv = bytearray.fromhex('cafebabefacedbaddecaf888')
plaintext = bytearray.fromhex(
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
associated_data = bytearray.fromhex("feedfacedeadbeeffeedfacedeadbeefabaddad2")
# wlasne
ciphertext, auth_tag = aes_gcm_encrypt(plaintext, key, iv, associated_data, tag_length)
print(ciphertext.hex(), auth_tag.hex(), sep="")
message, _ = aes_gcm_authenticated_decryption(key, iv, auth_tag, associated_data, ciphertext)
print(message.hex())
# gotowe
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(iv, plaintext, associated_data)
print(ciphertext.hex())
message = aesgcm.decrypt(iv, ciphertext, associated_data)
print(message.hex())