def permuted(self, a): """Takes 16 integers or 64 bytes, returns the ChaCha-permuted bytes Note that this is more elaborate than in the reference code. This should make it easier to use the ChaCha-permutation on its own. """ # TODO find a nice way to split this without duplication assert (len(a) == 16 and all(type(i) is int for i in a) or len(a) == 64 and type(a) in [bytes, bytearray]) if len(a) == 64: x = list(ints_from_4bytes(a)) else: x = list(a) def ROL32(x, n): return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n)) def quarterround(x, a, b, c, d): x[a] = (x[a] + x[b] & 0xFFFFFFFF); x[d] = ROL32(x[d] ^ x[a], 16) x[c] = (x[c] + x[d] & 0xFFFFFFFF); x[b] = ROL32(x[b] ^ x[c], 12) x[a] = (x[a] + x[b] & 0xFFFFFFFF); x[d] = ROL32(x[d] ^ x[a], 8) x[c] = (x[c] + x[d] & 0xFFFFFFFF); x[b] = ROL32(x[b] ^ x[c], 7) for i in range(0, self.rounds, 2): quarterround(x, 0, 4, 8, 12) quarterround(x, 1, 5, 9, 13) quarterround(x, 2, 6, 10, 14) quarterround(x, 3, 7, 11, 15) quarterround(x, 0, 5, 10, 15) quarterround(x, 1, 6, 11, 12) quarterround(x, 2, 7, 8, 13) quarterround(x, 3, 4, 9, 14) if len(a) == 16: for i in range(16): x[i] = (x[i] + a[i] & 0xFFFFFFFF) return b''.join(ints_to_4bytes(x))
def __init__(self, key=None, iv=None, rounds=12): assert rounds & 1 == 0 self.rounds = rounds if key is None: key = bytes(32) if iv is None: iv = bytes(8) assert len(key) in [16, 32] assert len(iv) == 8 self.state = [] if len(key) == 32: c = bytes(sigma, 'latin-1') self.state += ints_from_4bytes(c) self.state += ints_from_4bytes(key) elif len(key) == 16: c = bytes(tau, 'latin-1') self.state += ints_from_4bytes(c) self.state += ints_from_4bytes(key) self.state += ints_from_4bytes(key) self.state += [0, 0] self.state += ints_from_4bytes(iv)