def __init__(self, cipher: EncryptionAlg, nonce: bytes): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. nonce (bytes): Bytes-like nonce. """ Primitive.__init__(self) self.cipher = cipher self.nonce = nonce self.ctr = CTR(self.cipher, b'') self.cmac = CMAC(self.cipher)
def __init__(self, cipher: EncryptionAlg, mac_len: int): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. mac_len (int): Length of MAC to generate. """ Primitive.__init__(self) self.cipher = cipher self.cmac = CBCMAC(self.cipher) self.mac_len = mac_len self.ctr = CTR(self.cipher, b'\x00' * 16)
def __init__(self, cipher: EncryptionAlg): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. """ Primitive.__init__(self) self.cipher = cipher self.H = self.cipher.encrypt(b'\x00' * 16).int() self.ctr = CTR(self.cipher, b'\x00' * 8) # Precompute the product table self.product_table = [0] * 16 self.product_table[reverse_bits(1)] = self.H for i in range(2, 16, 2): self.product_table[reverse_bits(i)] = self.gcm_shift( self.product_table[reverse_bits(i // 2)]) self.product_table[reverse_bits( i + 1)] = self.product_table[reverse_bits(i)] ^ self.H
def test_all_vecs(self): for unparsed_vec in TEST_VECS: _alg, key, nonce, plaintext, ciphertext, _what = unparsed_vec.split(":") key_bytes, nonce, plaintext, expected_ciphertext = [codecs.decode(item.encode('utf-8'), 'hex_codec') for item in [key, nonce, plaintext, ciphertext]] key = Bytes(key_bytes).zfill(ceil(len(key_bytes) / 16) * 16) ctr = CTR(Rijndael(key), nonce[:15]) ctr.counter = 1 ciphertext = ctr.encrypt(plaintext) ctr = CTR(Rijndael(key), nonce[:15]) ctr.counter = 1 self.assertEqual(ciphertext, expected_ciphertext) self.assertEqual(plaintext, ctr.decrypt(ciphertext))
def init_aes256_ctr(key_iv): key, iv = key_iv[:32], key_iv[32:] ctr = CTR(Rijndael(key), nonce=b'') ctr.counter = iv.int() return ctr
class GCM(StreamingBlockCipherMode): """Galois counter mode (GCM) block cipher mode""" EPHEMERAL = EphemeralSpec(ephemeral_type=EphemeralType.NONCE, size=SizeSpec(size_type=SizeType.SINGLE, sizes=96)) AUTH_TAG_SIZE = SizeSpec(size_type=SizeType.SINGLE, sizes=128) USAGE_FREQUENCY = FrequencyType.PROLIFIC def __init__(self, cipher: EncryptionAlg): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. """ Primitive.__init__(self) self.cipher = cipher self.H = self.cipher.encrypt(b'\x00' * 16).int() self.ctr = CTR(self.cipher, b'\x00' * 8) # Precompute the product table self.product_table = [0] * 16 self.product_table[reverse_bits(1)] = self.H for i in range(2, 16, 2): self.product_table[reverse_bits(i)] = self.gcm_shift( self.product_table[reverse_bits(i // 2)]) self.product_table[reverse_bits( i + 1)] = self.product_table[reverse_bits(i)] ^ self.H def __repr__(self): return f"<GCM: cipher={self.cipher}, H={self.H}, ctr={self.ctr}>" def __str__(self): return self.__repr__() def clock_ctr(self, nonce: bytes) -> Bytes: nonce = Bytes.wrap(nonce) if len(nonce) == 12: self.ctr.nonce = nonce self.ctr.counter = 1 else: payload = nonce.pad_congruent_right(16) + (b'\x00' * 8) + Bytes( len(nonce) * 8).zfill(8) J_0 = Bytes(self.update(0, payload)).zfill(16) self.ctr.nonce = J_0[:15] self.ctr.counter = J_0[-1] return self.ctr.encrypt(Bytes(b'').zfill(16)) def encrypt(self, nonce: bytes, plaintext: bytes, data: bytes = b'') -> Bytes: """ Encrypts `plaintext`. Parameters: nonce (bytes): Bytes-like nonce. plaintext (bytes): Bytes-like object to be encrypted. data (bytes): Bytes-like additional data to be authenticated but not encrypted. Returns: Bytes: Resulting ciphertext. """ tag_mask = self.clock_ctr(nonce) data = Bytes.wrap(data) ciphertext = self.ctr.encrypt(plaintext) tag = self.auth(ciphertext, data, tag_mask) return ciphertext + tag def decrypt(self, nonce: bytes, authed_ciphertext: bytes, data: bytes = b'') -> Bytes: """ Decrypts `ciphertext`. Parameters: nonce (bytes): Bytes-like nonce. authed_ciphertext (bytes): Bytes-like object to be decrypted. data (bytes): Bytes-like additional data to be authenticated. Returns: Bytes: Resulting plaintext. """ from samson.utilities.runtime import RUNTIME authed_ciphertext = Bytes.wrap(authed_ciphertext) ciphertext, orig_tag = authed_ciphertext[:-16], authed_ciphertext[-16:] tag_mask = self.clock_ctr(nonce) data = Bytes.wrap(data) tag = self.auth(ciphertext, data, tag_mask) if not RUNTIME.compare_bytes(tag, orig_tag): raise Exception('Tag mismatch: authentication failed!') return self.ctr.decrypt(ciphertext) def gcm_shift(self, x: int) -> int: high_bit_set = x & 1 x >>= 1 if high_bit_set: x ^= 0xe1 << (128 - 8) return x def mul(self, y): ret = 0 for _ in range(0, 128, 4): high_bit = ret & 0xF ret >>= 4 ret ^= GCM_REDUCTION_TABLE[high_bit] << (128 - 16) ret ^= self.product_table[y & 0xF] y >>= 4 return ret def auth(self, ciphertext: Bytes, ad: Bytes, tag_mask: Bytes) -> Bytes: y = 0 y = self.update(y, ad) y = self.update(y, ciphertext) y ^= (len(ad) << (3 + 64)) | (len(ciphertext) << 3) y = self.mul(y) y ^= tag_mask.int() return Bytes(int.to_bytes(y, 16, 'big')) def update(self, y: int, data: Bytes) -> int: for chunk in data.chunk(16): y ^= chunk.int() y = self.mul(y) extra = len(data) % 16 if extra != 0: block = bytearray(16) block[:extra] = data[-extra:] y ^= int.from_bytes(block, 'big') y = self.mul(y) return y
class CCM(StreamingBlockCipherMode): """ Counter with CBC-MAC block cipher mode. CCM is only defined for ciphers with 128 bit block size. """ USAGE_FREQUENCY = FrequencyType.UNUSUAL AUTH_TAG_SIZE = SizeSpec(size_type=SizeType.RANGE, sizes=range(32, 129, 16), typical=[128]) EPHEMERAL = EphemeralSpec(ephemeral_type=EphemeralType.NONCE, size=SizeSpec(size_type=SizeType.RANGE, sizes=range(8, 97, 8), typical=[96])) def __init__(self, cipher: EncryptionAlg, mac_len: int): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. mac_len (int): Length of MAC to generate. """ Primitive.__init__(self) self.cipher = cipher self.cmac = CBCMAC(self.cipher) self.mac_len = mac_len self.ctr = CTR(self.cipher, b'\x00' * 16) def __repr__(self): return f"<CCM: cipher={self.cipher}, ctr={self.ctr}>" def __str__(self): return self.__repr__() def _calculate_formatting_params(self, nonce: bytes, plaintext: bytes, data: bytes): data_len = len(data) q = 15 - len(nonce) flags = (64 * (data_len > 0)) + 8 * (((self.mac_len) - 2) // 2) + (q - 1) b_0 = Bytes(flags) + nonce + int.to_bytes(len(plaintext), q, 'big') return data_len, q, flags, b_0 # https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38c.pdf def _generate_mac(self, nonce: bytes, plaintext: bytes, data: bytes) -> bytes: data_len, _q, _flags, b_0 = self._calculate_formatting_params(nonce, plaintext, data) data_len_encoded = b'' if data_len > 0: if data_len < ((2 ** 16) - (2 ** 8)): size = 2 elif data_len < (2 ** 32): data_len_encoded = b'\xFF\xFE' size = 4 else: data_len_encoded = b'\xFF\xFF' size = 8 data_len_encoded += int.to_bytes(data_len, size, 'big') padded_data = Bytes.wrap(data_len_encoded + data).pad_congruent_right(16) padded_plaintext = Bytes.wrap(plaintext).pad_congruent_right(16) T = self.cmac.generate(b_0 + padded_data + padded_plaintext, pad=False) return T def _generate_keystream(self, nonce: bytes, q: int, length: int) -> Bytes: formatted_nonce = Bytes(q - 1) + nonce self.ctr.nonce = formatted_nonce self.ctr.counter = 0 keystream = self.ctr.encrypt(Bytes(b'').zfill(length)) return keystream def encrypt(self, nonce: bytes, plaintext: bytes, data: bytes) -> Bytes: """ Encrypts `plaintext`. Parameters: nonce (bytes): Bytes-like nonce. plaintext (bytes): Bytes-like object to be encrypted. data (bytes): Bytes-like additional data to be authenticated but not encrypted. Returns: Bytes: Resulting ciphertext. """ T = self._generate_mac(nonce, plaintext, data) _data_len, q, _flags, _b_0 = self._calculate_formatting_params(nonce, plaintext, data) keystream = self._generate_keystream(nonce, q, len(plaintext) + 16) return (keystream[len(T):] ^ (plaintext)) + (T ^ keystream[:len(T)])[:self.mac_len] def decrypt(self, nonce: bytes, ciphertext: bytes, data: bytes) -> Bytes: """ Decrypts `ciphertext`. Parameters: nonce (bytes): Bytes-like nonce. plaintext (bytes): Bytes-like object to be decrypted. data (bytes): Bytes-like additional data to be authenticated. Returns: Bytes: Resulting plaintext. """ _data_len, q, _flags, _b_0 = self._calculate_formatting_params(nonce, ciphertext, data) keystream = self._generate_keystream(nonce, q, len(ciphertext) + (16 - self.mac_len)) total_plaintext = (keystream[16:] + keystream[:self.mac_len]) ^ ciphertext plaintext, mac = total_plaintext[:-self.mac_len], total_plaintext[-self.mac_len:] T = self._generate_mac(nonce, plaintext, data)[:self.mac_len] if not hmac.compare_digest(T, mac): raise Exception("Authentication of data failed: MACs not equal") return plaintext
def login_ctr(ciphertext): return b';admin=true;' in CTR(aes, nonce).encrypt(ciphertext)
def encrypt_data_ctr(data): return CTR(aes, nonce).encrypt(format_data(data))
def aes_ctr_oracle(message): aes = Rijndael(rand_bytes(key_size)) ciphertext = CTR(aes, rand_bytes(block_size // 2)).encrypt( zlib.compress(message)) return len(ciphertext)
def encrypt_ctr(secret): return CTR(Rijndael(key), int.to_bytes(0, block_size // 2, 'big')).encrypt(secret)
class EAX(StreamingBlockCipherMode): """ EAX block cipher mode http://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf """ EPHEMERAL = EphemeralSpec(ephemeral_type=EphemeralType.NONCE, size=SizeSpec(size_type=SizeType.DEPENDENT, selector=lambda block_mode: block_mode.cipher.BLOCK_SIZE)) AUTH_TAG_SIZE = SizeSpec(size_type=SizeType.DEPENDENT, selector=lambda block_mode: block_mode.cipher.BLOCK_SIZE) def __init__(self, cipher: EncryptionAlg, nonce: bytes): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. nonce (bytes): Bytes-like nonce. """ Primitive.__init__(self) self.cipher = cipher self.nonce = nonce self.ctr = CTR(self.cipher, b'') self.cmac = CMAC(self.cipher) def __repr__(self): return f"<EAX: cipher={self.cipher}, nonce={self.nonce}>" def __str__(self): return self.__repr__() def generate_tag(self, ciphertext: bytes, auth_data: bytes) -> Bytes: """ Internal function. Generates a valid tag for the `ciphertext` and `auth_data`. """ cipher_mac = self.cmac.generate(Bytes(2).zfill(self.cipher.block_size) + ciphertext) tag = cipher_mac ^ self.cmac.generate(Bytes(0).zfill(self.cipher.block_size) + self.nonce) ^ self.cmac.generate(Bytes(1).zfill(self.cipher.block_size) + Bytes.wrap(auth_data)) return tag def encrypt(self, plaintext: bytes, auth_data: bytes) -> Bytes: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Bytes-like object to be encrypted. auth_data (bytes): Bytes-like additional data to be authenticated but not encrypted. Returns: Bytes: Resulting ciphertext. """ self.ctr.counter = self.cmac.generate(Bytes(0).zfill(self.cipher.block_size) + self.nonce).int() ciphertext = self.ctr.encrypt(plaintext) tag = self.generate_tag(ciphertext, auth_data) return ciphertext + tag[:self.cipher.block_size] def decrypt(self, ciphertext: bytes, auth_data: bytes, verify: bool=True) -> Bytes: """ Decrypts `ciphertext`. Parameters: ciphertext (bytes): Bytes-like object to be decrypted. auth_data (bytes): Bytes-like additional data to be authenticated but not encrypted. verify (bool): Whether or not to verify the authentication tag. Returns: Bytes: Resulting plaintext. """ ciphertext, given_tag = ciphertext[:-16], ciphertext[-16:] tag = self.generate_tag(ciphertext, auth_data) if verify: assert tag == given_tag self.ctr.counter = self.cmac.generate(Bytes(0).zfill(self.cipher.block_size) + self.nonce).int() plaintext = self.ctr.decrypt(ciphertext) return plaintext