""" Implementation of the SHAKE-256 algorithm for Ed448 """ try: import hashlib hashlib.new("shake256").digest(64) def shake_256(msg, outlen): return hashlib.new("shake256", msg).digest(outlen) except (TypeError, ValueError): from ._compat import bytes_to_int, int_to_bytes # From little endian. def _from_le(s): return bytes_to_int(s, byteorder="little") # Rotate a word x by b places to the left. def _rol(x, b): return ((x << b) | (x >> (64 - b))) & (2**64 - 1) # Do the SHA-3 state transform on state s. def _sha3_transform(s): ROTATIONS = [ 0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14, ] PERMUTATION = [ 1, 6, 9, 22, 14, 20, 2, 12, 13, 19, 23, 15, 4, 24, 21, 8, 16, 5, 3, 18, 17, 11, 7, 10, ] RC = [ 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, ] for rnd in range(0, 24): # AddColumnParity (Theta) c = [0] * 5 d = [0] * 5 for i in range(0, 25): c[i % 5] ^= s[i] for i in range(0, 5): d[i] = c[(i + 4) % 5] ^ _rol(c[(i + 1) % 5], 1) for i in range(0, 25): s[i] ^= d[i % 5] # RotateWords (Rho) for i in range(0, 25): s[i] = _rol(s[i], ROTATIONS[i]) # PermuteWords (Pi) t = s[PERMUTATION[0]] for i in range(0, len(PERMUTATION) - 1): s[PERMUTATION[i]] = s[PERMUTATION[i + 1]] s[PERMUTATION[-1]] = t # NonlinearMixRows (Chi) for i in range(0, 25, 5): t = [ s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i], s[i + 1], ] for j in range(0, 5): s[i + j] = t[j] ^ ((~t[j + 1]) & (t[j + 2])) # AddRoundConstant (Iota) s[0] ^= RC[rnd] # Reinterpret octet array b to word array and XOR it to state s. def _reinterpret_to_words_and_xor(s, b): for j in range(0, len(b) // 8): s[j] ^= _from_le(b[8 * j : 8 * j + 8]) # Reinterpret word array w to octet array and return it. def _reinterpret_to_octets(w): mp = bytearray() for j in range(0, len(w)): mp += int_to_bytes(w[j], 8, byteorder="little") return mp def _sha3_raw(msg, r_w, o_p, e_b): """Semi-generic SHA-3 implementation""" r_b = 8 * r_w s = [0] * 25 # Handle whole blocks. idx = 0 blocks = len(msg) // r_b for i in range(0, blocks): _reinterpret_to_words_and_xor(s, msg[idx : idx + r_b]) idx += r_b _sha3_transform(s) # Handle last block padding. m = bytearray(msg[idx:]) m.append(o_p) while len(m) < r_b: m.append(0) m[len(m) - 1] |= 128 # Handle padded last block. _reinterpret_to_words_and_xor(s, m) _sha3_transform(s) # Output. out = bytearray() while len(out) < e_b: out += _reinterpret_to_octets(s[:r_w]) _sha3_transform(s) return out[:e_b] def shake_256(msg, outlen): return _sha3_raw(msg, 17, 31, outlen)