def encrypt(self, plaintext: bytes) -> Bytes: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Bytes-like object to be encrypted. Returns: Bytes: Resulting ciphertext. """ plaintext = Bytes.wrap(plaintext).zfill(self.block_size // 4) A = plaintext[self.block_size // 8:].int() B = plaintext[:self.block_size // 8].int() A = (A + self.S[0]) % self.mod B = (B + self.S[1]) % self.mod for i in range(1, self.num_rounds + 1): A = (left_rotate(A ^ B, B % self.block_size, bits=self.block_size) + self.S[2 * i]) % self.mod B = (left_rotate(B ^ A, A % self.block_size, bits=self.block_size) + self.S[2 * i + 1]) % self.mod return (Bytes(A, 'little').zfill(self.block_size // 8) + Bytes(B, 'little').zfill(self.block_size // 8))
def round_func(self, A, rc): C = [0] * 5 for x in range(5): C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4] D = [0] * 5 for x in range(5): D[x] = C[x - 1] ^ left_rotate(C[(x + 1) % 5], 1, 64) for x in range(5): for y in range(5): A[x][y] = A[x][y] ^ D[x] B = [[0] * 5 for _ in range(5)] for x in range(5): for y in range(5): B[y][(2 * x + 3 * y) % 5] = left_rotate(A[x][y], R[y][x], 64) for x in range(5): for y in range(5): A[x][y] = B[x][y] ^ ((~B[(x + 1) % 5][y]) & B[(x + 2) % 5][y]) A[0][0] ^= rc return A
def EXPAND_KEY(M_e, M_o, k): K = [] for i in range(ROUNDS + 4): A_i = G(2 * i * RHO, M_e, k) B_i = G((2 * i + 1) * RHO, M_o, k) B_i = left_rotate(B_i, 8) K.append((A_i + B_i) & 0xFFFFFFFF) K.append(left_rotate((A_i + 2 * B_i) & 0xFFFFFFFF, 9)) return K
def compression_func(chunk, state): """Process a chunk of data and return the new digest variables.""" assert len(chunk) == 64 w = [0] * 80 # Break chunk into sixteen 4-byte big-endian words w[i] for i in range(16): w[i] = struct.unpack(b'>I', chunk[i * 4:i * 4 + 4])[0] # Extend the sixteen 4-byte words into eighty 4-byte words for i in range(16, 80): w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1) # Initialize hash value for this chunk h0, h1, h2, h3, h4 = bytes_to_state(state) a = h0 b = h1 c = h2 d = h3 e = h4 for i in range(80): if 0 <= i <= 19: # Use alternative 1 for f from FIPS PB 180-1 to avoid bitwise not f = d ^ (b & (c ^ d)) k = 0x5A827999 elif 20 <= i <= 39: f = b ^ c ^ d k = 0x6ED9EBA1 elif 40 <= i <= 59: f = (b & c) | (b & d) | (c & d) k = 0x8F1BBCDC elif 60 <= i <= 79: f = b ^ c ^ d k = 0xCA62C1D6 a, b, c, d, e = ((left_rotate(a, 5) + f + e + k + w[i]) & 0xffffffff, a, left_rotate(b, 30), c, d) # Add this chunk's hash to result so far h0 = (h0 + a) & 0xffffffff h1 = (h1 + b) & 0xffffffff h2 = (h2 + c) & 0xffffffff h3 = (h3 + d) & 0xffffffff h4 = (h4 + e) & 0xffffffff state = [h0, h1, h2, h3, h4] return Bytes(state_to_bytes(state))
def decrypt(self, ciphertext: bytes) -> Bytes: """ Decrypts `ciphertext`. Parameters: ciphertext (bytes): Bytes-like object to be decrypted. Returns: Bytes: Resulting plaintext. """ ciphertext = Bytes.wrap(ciphertext) ct_chunks = [ chunk.zfill(4)[::-1].int() for chunk in ciphertext.chunk(4) ] # Dewhitening R = [ct_chunks[i] ^ self.K[i + 4] for i in range(len(ct_chunks))] for i in range(ROUNDS - 1, -1, -1): FR_0, FR_1 = self.F(R[0], R[1], i) R = [ R[0], R[1], left_rotate(R[2], 1) ^ FR_0, right_rotate(R[3] ^ FR_1, 1) ] R[0], R[2] = R[2], R[0] R[1], R[3] = R[3], R[1] R = [R[(i + 2) % 4] ^ self.K[i] for i in range(len(ct_chunks))] return Bytes(b''.join([int.to_bytes(r, 4, 'little') for r in R]), 'little')[::-1]
def encrypt(self, plaintext: bytes) -> Bytes: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Bytes-like object to be encrypted. Returns: Bytes: Resulting ciphertext. """ plaintext = Bytes.wrap(plaintext)[::-1] pt_chunks = [chunk.zfill(4).int() for chunk in plaintext.chunk(4)] # Whitening R = [pt_chunks[i] ^ self.K[i] for i in range(len(pt_chunks))] for i in range(ROUNDS): FR_0, FR_1 = self.F(R[0], R[1], i) R = [ R[0], R[1], right_rotate(R[2] ^ FR_0, 1), left_rotate(R[3], 1) ^ FR_1 ] R[0], R[2] = R[2], R[0] R[1], R[3] = R[3], R[1] R = [R[(i + 2) % 4] ^ self.K[i + 4] for i in range(len(pt_chunks))] return Bytes(b''.join([int.to_bytes(r, 4, 'little') for r in R]))
def FL(FL_IN, KE): x1 = (FL_IN >> 32) & MASK32 x2 = FL_IN & MASK32 k1 = (KE >> 32) & MASK32 k2 = KE & MASK32 x2 = x2 ^ left_rotate((x1 & k1), 1, bits=32) x1 = x1 ^ (x2 | k2) return (x1 << 32) | x2
def QUARTER_ROUND(a: int, b: int, c: int, d: int) -> (int, int, int, int): """ Performs a quarter round of ChaCha. Parameters: a (int): ChaCha state variable. b (int): ChaCha state variable. c (int): ChaCha state variable. d (int): ChaCha state variable. Returns: (int, int, int, int): New values for (a, b, c, d). """ a = (a + b) & 0xFFFFFFFF; d ^= a; d = left_rotate(d, 16) c = (c + d) & 0xFFFFFFFF; b ^= c; b = left_rotate(b, 12) a = (a + b) & 0xFFFFFFFF; d ^= a; d = left_rotate(d, 8) c = (c + d) & 0xFFFFFFFF; b ^= c; b = left_rotate(b, 7) return a, b, c, d
def FLINV(FLINV_IN, KE): y1 = (FLINV_IN) >> 32 & MASK32 y2 = FLINV_IN & MASK32 k1 = (KE >> 32) & MASK32 k2 = KE & MASK32 y1 = y1 ^ (y2 | k2) y2 = y2 ^ left_rotate((y1 & k1), 1, bits=32) return (y1 << 32) | y2
def COMPRESS(message, state): # The authors of RIPEMD160 couldn't decide on whether to use big or little endian, so they used both! # RIPEMD160 takes in bytes as big endian but operates and outputs bytes of little endian. Man, was this 'fun.' h = [chunk.to_int() for chunk in state.chunk(4)] msg_chunks = [ chunk[::-1].to_int() for chunk in Bytes.wrap(message, byteorder='big').chunk(4) ] AL = AR = h[0] BL = BR = h[1] CL = CR = h[2] DL = DR = h[3] EL = ER = h[4] for curr_round in range(5): for w in range(16): T = left_rotate( AL + FL[curr_round](BL, CL, DL) + msg_chunks[RL[curr_round][w]] + KL[curr_round], SL[curr_round][w]) + EL AL = EL & 0xFFFFFFFF EL = DL & 0xFFFFFFFF DL = left_rotate(CL, 10) CL = BL & 0xFFFFFFFF BL = T & 0xFFFFFFFF T = left_rotate( AR + FR[curr_round](BR, CR, DR) + msg_chunks[RR[curr_round][w]] + KR[curr_round], SR[curr_round][w]) + ER AR = ER & 0xFFFFFFFF ER = DR & 0xFFFFFFFF DR = left_rotate(CR, 10) CR = BR & 0xFFFFFFFF BR = T & 0xFFFFFFFF T = (h[1] + CL + DR) & 0xFFFFFFFF h[1] = (h[2] + DL + ER) & 0xFFFFFFFF h[2] = (h[3] + EL + AR) & 0xFFFFFFFF h[3] = (h[4] + AL + BR) & 0xFFFFFFFF h[4] = (h[0] + BL + CR) & 0xFFFFFFFF h[0] = T return sum([Bytes(state, 'little').zfill(4) for state in h])
def F(self, R_0, R_1, i): R_0_little = int.from_bytes(int.to_bytes(R_0, 4, 'big'), 'little') R_1_little = int.from_bytes( int.to_bytes(left_rotate(R_1, 8), 4, 'big'), 'little') T_0 = G(R_0_little, self.S, self.k) T_1 = G(R_1_little, self.S, self.k) F_0 = (T_0 + T_1 + self.K[2 * i + 8]) & 0xFFFFFFFF F_1 = (T_0 + 2 * T_1 + self.K[2 * i + 9]) & 0xFFFFFFFF return F_0, F_1
def key_schedule(key): key_bits = bytes_to_bitstring(key) left_key = ''.join([key_bits[PC_1_left[i] - 1] for i in range(28)]) right_key = ''.join([key_bits[PC_1_right[i] - 1] for i in range(28)]) for i in range(16): # Rotate rotation = rotation_round_map[i] left_key = bin(left_rotate(int(left_key, 2), rotation, bits=28))[2:].zfill(28) right_key = bin(left_rotate(int(right_key, 2), rotation, bits=28))[2:].zfill(28) # Permutate combined_keys = left_key + right_key sub_key = int.to_bytes( int(''.join([combined_keys[PC_2[j] - 1] for j in range(48)]), 2), 7, 'big') yield sub_key
def _key_expansion(self): b = len(self.key) u = self.block_size // 8 t = 2 * (self.num_rounds + 1) if b == 0: c = 1 elif b % u: self.key = self.key.zfill(u - b % u + b) b = len(self.key) c = b // u else: c = b // u const_idx = int(math.log(self.block_size, 2) - 4) if b == 0: L = [0] else: L = self.key.chunk(b // c) for i in range(b - 1, -1, -1): L[i // u] = Bytes.wrap(L[i // u] << 8).int() + self.key[i] S = [(P_w[const_idx] + (Q_w[const_idx] * i)) % self.mod for i in range(t)] i = j = 0 A = B = 0 for _ in range(3 * max(t, c)): A = S[i] = left_rotate((S[i] + A + B), 3, bits=self.block_size) B = L[j] = left_rotate((L[j] + A + B), (A + B) % self.block_size, bits=self.block_size) i = (i + 1) % t j = (j + 1) % c return S
def compression_func(message, state): X = list(struct.unpack("<16I", message) + (None, ) * (80 - 16)) h = bytes_to_state(state) last_state = [x for x in h] # Round 1 s = (3, 7, 11, 19) for r in range(16): i = (16 - r) % 4 k = r h[i] = left_rotate( (h[i] + F(h[(i + 1) % 4], h[(i + 2) % 4], h[(i + 3) % 4]) + X[k]) % 2**32, s[r % 4]) # Round 2 s = (3, 5, 9, 13) for r in range(16): i = (16 - r) % 4 k = 4 * (r % 4) + r // 4 h[i] = left_rotate( (h[i] + G(h[(i + 1) % 4], h[(i + 2) % 4], h[(i + 3) % 4]) + X[k] + 0x5a827999) % 2**32, s[r % 4]) # Round 3 s = (3, 9, 11, 15) k = (0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15) for r in range(16): i = (16 - r) % 4 h[i] = left_rotate( (h[i] + H(h[(i + 1) % 4], h[(i + 2) % 4], h[(i + 3) % 4]) + X[k[r]] + 0x6ed9eba1) % 2**32, s[r % 4]) new_state = [] for i, v in enumerate(h): new_state.append((v + last_state[i]) % 2**32) return Bytes(state_to_bytes(new_state))
def run_lfsr(self, u: int = None): """ Used internally. Runs the LFSR one iteration. """ f = self.lfsr_states[0] rotations = [(0, 8), (4, 20), (10, 21), (13, 17), (15, 15)] for state_idx, rot_amount in rotations: v = left_rotate(self.lfsr_states[state_idx], rot_amount, bits=31) f = ADD_M(f, v) if u: f = ADD_M(f, u) self.shift_states(f)
def lrot(self, amount: int, bits: int=None): """ Performs a left-rotate. Parameters: amount (int): Amount to rotate by. bits (int): Bitspace to rotate over. Returns: Bytes: A new instance of Bytes with the transformation applied. """ if not bits: bits = len(self) * 8 back_to_bytes = int.to_bytes(left_rotate(self.to_int(), amount % bits, bits), bits // 8, self.byteorder) return Bytes(back_to_bytes, self.byteorder)
def gen_func( sym_s0, sym_s1, SHFT_L=lambda x, n: (x << n) & MASK64, SHFT_R=DEFAULT_SHFT_R, RotateLeft=lambda x, n: left_rotate(x, n, bits=64) ) -> (list, int): """ Internal function compatible with Python and symbolic execution. """ s0, s1 = sym_s0, sym_s1 result = (s0 + s1) & MASK64 s1 ^= s0 sym_s0 = RotateLeft(s0, 24) ^ s1 ^ SHFT_L(s1, 16) sym_s1 = RotateLeft(s1, 37) return [sym_s0, sym_s1], result
def make_subkeys(self): w = {} for i, chunk in enumerate(self.key.chunk(4)[::-1]): w[i - 8] = chunk[::-1].int() for i in range(132): w[i] = (left_rotate( w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11)) w = { i: Bitstring(val, 'little', auto_fill=False)[::-1] for i, val in w.items() } k = {} for i in range(ROUNDS + 1): sbox = (ROUNDS + 3 - i) % ROUNDS for h in range(4): k[h + 4 * i] = Bitstring("", 'little', auto_fill=False) for j in range(ROUNDS): s_in = ''.join([ str( Bitstring(w[h + 4 * i], 'little', auto_fill=False).zfill(32)[j]) for h in range(4) ]) s_out = Bitstring(SBOX[sbox % len(SBOX)][s_in], 'little', auto_fill=False) for h in range(4): k[h + 4 * i] += s_out[h] K = [] for i in range(ROUNDS + 1): K.append(k[4 * i] + k[4 * i + 1] + k[4 * i + 2] + k[4 * i + 3]) K_hat = [] for i in range(ROUNDS + 1): K_hat.append(self.IP(K[i])) return K, K_hat
def compression_func(message, state): new_state = bytes_to_state(state) for chunk_ofst in range(0, len(message), 64): a, b, c, d = new_state chunk = message[chunk_ofst:chunk_ofst+64] for i in range(64): f = functions[i](b, c, d) g = index_functions[i](i) to_rotate = a + f + constants[i] + int.from_bytes(chunk[4*g:4*g+4], byteorder='little') new_b = (b + left_rotate(to_rotate, rotate_amounts[i])) & 0xFFFFFFFF a, b, c, d = d, new_b, b, c for i, val in enumerate([a, b, c, d]): new_state[i] += val new_state[i] &= 0xFFFFFFFF return Bytes(state_to_bytes(new_state))
def L2(X): return (X ^ left_rotate(X, 8) ^ left_rotate(X, 14) ^ left_rotate(X, 22) ^ left_rotate(X, 30)) & 0xFFFFFFFF
def L1(X): return (X ^ left_rotate(X, 2) ^ left_rotate(X, 10) ^ left_rotate(X, 18) ^ left_rotate(X, 24)) & 0xFFFFFFFF
def ROTL128(x, amount): return left_rotate(x, amount, bits=128)