Add hash.py
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
MASK64 = (1 << 64) - 1
|
||||
LANES = 32
|
||||
STRIDE = 128
|
||||
ROWBITS = LANES * STRIDE
|
||||
ROWFULL = (1 << ROWBITS) - 1
|
||||
LANEMASK = sum(MASK64 << (i * STRIDE) for i in range(LANES))
|
||||
|
||||
A_ROUNDS = 12
|
||||
A_BLOCK = 768
|
||||
|
||||
B_N = 12288
|
||||
B_FULL = (1 << B_N) - 1
|
||||
B_ROUNDS = 12
|
||||
B_BLOCK = 1024
|
||||
|
||||
|
||||
def _constants(count):
|
||||
out = []
|
||||
x = 0x243F6A8885A308D3
|
||||
for _ in range(count):
|
||||
x = (x + 0x9E3779B97F4A7C15) & MASK64
|
||||
z = x
|
||||
z = ((z ^ (z >> 30)) * 0xBF58476D1CE4E5B9) & MASK64
|
||||
z = ((z ^ (z >> 27)) * 0x94D049BB133111EB) & MASK64
|
||||
out.append(z ^ (z >> 31))
|
||||
return out
|
||||
|
||||
|
||||
def _pack_lanes(words):
|
||||
v = 0
|
||||
for i, w in enumerate(words):
|
||||
v |= w << (i * STRIDE)
|
||||
return v
|
||||
|
||||
|
||||
def _pack_dense(words):
|
||||
v = 0
|
||||
for i, w in enumerate(words):
|
||||
v |= w << (i * 64)
|
||||
return v
|
||||
|
||||
|
||||
_TABLE = _constants(A_ROUNDS * 4 * LANES + 4 * LANES + B_ROUNDS * 192 + 192 + 2 * B_ROUNDS)
|
||||
_pos = 0
|
||||
_A_RC = []
|
||||
for _ in range(A_ROUNDS * 4):
|
||||
_A_RC.append(_pack_lanes(_TABLE[_pos : _pos + LANES]))
|
||||
_pos += LANES
|
||||
_A_IV = []
|
||||
for _ in range(4):
|
||||
_A_IV.append(_pack_lanes(_TABLE[_pos : _pos + LANES]))
|
||||
_pos += LANES
|
||||
_B_RC = []
|
||||
for _ in range(B_ROUNDS):
|
||||
_B_RC.append(_pack_dense(_TABLE[_pos : _pos + 192]))
|
||||
_pos += 192
|
||||
_B_IV = _pack_dense(_TABLE[_pos : _pos + 192])
|
||||
_pos += 192
|
||||
_B_SHIFT = [(w % (B_N // 2 - 64)) + 32 for w in _TABLE[_pos : _pos + B_ROUNDS]]
|
||||
_pos += B_ROUNDS
|
||||
_B_ROT = [(w % (B_N - 128)) + 64 for w in _TABLE[_pos : _pos + B_ROUNDS]]
|
||||
|
||||
_ZERO8 = b"\x00" * 8
|
||||
|
||||
|
||||
def _permute_a(rows):
|
||||
r0, r1, r2, r3 = rows
|
||||
LM = LANEMASK
|
||||
RF = ROWFULL
|
||||
for rnd in range(A_ROUNDS):
|
||||
base = rnd * 4
|
||||
r0 ^= _A_RC[base]
|
||||
r1 ^= _A_RC[base + 1]
|
||||
r2 ^= _A_RC[base + 2]
|
||||
r3 ^= _A_RC[base + 3]
|
||||
|
||||
r0 = (r0 + r1) & LM
|
||||
r3 ^= r0
|
||||
r3 = ((r3 << 32) | (r3 >> 32)) & LM
|
||||
r2 = (r2 + r3) & LM
|
||||
r1 ^= r2
|
||||
r1 = ((r1 << 24) | (r1 >> 40)) & LM
|
||||
r0 = (r0 + r1) & LM
|
||||
r3 ^= r0
|
||||
r3 = ((r3 << 16) | (r3 >> 48)) & LM
|
||||
r2 = (r2 + r3) & LM
|
||||
r1 ^= r2
|
||||
r1 = ((r1 << 63) | (r1 >> 1)) & LM
|
||||
|
||||
r1 = ((r1 << 128) | (r1 >> (ROWBITS - 128))) & RF
|
||||
r2 = ((r2 << 640) | (r2 >> (ROWBITS - 640))) & RF
|
||||
r3 = ((r3 << 1664) | (r3 >> (ROWBITS - 1664))) & RF
|
||||
|
||||
r0 = (r0 + r1) & LM
|
||||
r3 ^= r0
|
||||
r3 = ((r3 << 32) | (r3 >> 32)) & LM
|
||||
r2 = (r2 + r3) & LM
|
||||
r1 ^= r2
|
||||
r1 = ((r1 << 24) | (r1 >> 40)) & LM
|
||||
r0 = (r0 + r1) & LM
|
||||
r3 ^= r0
|
||||
r3 = ((r3 << 16) | (r3 >> 48)) & LM
|
||||
r2 = (r2 + r3) & LM
|
||||
r1 ^= r2
|
||||
r1 = ((r1 << 63) | (r1 >> 1)) & LM
|
||||
|
||||
k = ((r2 >> (((rnd * 3 + 1) & 31) * STRIDE + 58)) & 63) | 1
|
||||
r0 = ((r0 << k) | (r0 >> (64 - k))) & LM
|
||||
k = ((r3 >> (((rnd * 3 + 2) & 31) * STRIDE + 58)) & 63) | 1
|
||||
r1 = ((r1 << k) | (r1 >> (64 - k))) & LM
|
||||
k = ((r0 >> (((rnd * 5 + 1) & 31) * STRIDE + 58)) & 63) | 1
|
||||
r2 = ((r2 << k) | (r2 >> (64 - k))) & LM
|
||||
k = ((r1 >> (((rnd * 5 + 2) & 31) * STRIDE + 58)) & 63) | 1
|
||||
r3 = ((r3 << k) | (r3 >> (64 - k))) & LM
|
||||
|
||||
r0 = (r0 * 0xD1B54A32D192ED03) & LM
|
||||
r0 ^= (r0 >> 29) & LM
|
||||
r1 = (r1 * 0xD1B54A32D192ED03) & LM
|
||||
r1 ^= (r1 >> 29) & LM
|
||||
r2 = (r2 * 0xD1B54A32D192ED03) & LM
|
||||
r2 ^= (r2 >> 29) & LM
|
||||
r3 = (r3 * 0xD1B54A32D192ED03) & LM
|
||||
r3 ^= (r3 >> 29) & LM
|
||||
|
||||
rows[0] = r0
|
||||
rows[1] = r1
|
||||
rows[2] = r2
|
||||
rows[3] = r3
|
||||
|
||||
|
||||
def _permute_b(x):
|
||||
for rnd in range(B_ROUNDS):
|
||||
x = (x + _B_RC[rnd]) & B_FULL
|
||||
x = (x + 2 * x * x) & B_FULL
|
||||
x ^= x >> _B_SHIFT[rnd]
|
||||
r = _B_ROT[rnd]
|
||||
x = ((x << r) | (x >> (B_N - r))) & B_FULL
|
||||
return x
|
||||
|
||||
|
||||
def _absorb_a(rows, block):
|
||||
spread = b"".join(block[i : i + 8] + _ZERO8 for i in range(0, A_BLOCK, 8))
|
||||
m = int.from_bytes(spread, "little")
|
||||
rows[0] ^= m & ROWFULL
|
||||
rows[1] ^= (m >> ROWBITS) & ROWFULL
|
||||
rows[2] ^= (m >> (2 * ROWBITS)) & ROWFULL
|
||||
_permute_a(rows)
|
||||
|
||||
|
||||
def _squeeze_a(rows):
|
||||
out = bytearray()
|
||||
for lane in range(8):
|
||||
w = (rows[0] >> (lane * STRIDE)) & MASK64
|
||||
out += w.to_bytes(8, "little")
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def _pad_tail(tail, total_bits, block):
|
||||
padded = bytearray(tail)
|
||||
padded.append(0x80)
|
||||
while (len(padded) + 16) % block != 0:
|
||||
padded.append(0)
|
||||
padded += total_bits.to_bytes(16, "little")
|
||||
return bytes(padded)
|
||||
|
||||
|
||||
class NJLHash:
|
||||
def __init__(self, data=b""):
|
||||
self._rows = list(_A_IV)
|
||||
self._bx = _B_IV
|
||||
self._tail_a = bytearray()
|
||||
self._tail_b = bytearray()
|
||||
self._bits = 0
|
||||
if data:
|
||||
self.update(data)
|
||||
|
||||
def update(self, data):
|
||||
if isinstance(data, str):
|
||||
data = data.encode("utf-8")
|
||||
self._bits += len(data) * 8
|
||||
ta = self._tail_a
|
||||
ta += data
|
||||
while len(ta) >= A_BLOCK:
|
||||
_absorb_a(self._rows, bytes(ta[:A_BLOCK]))
|
||||
del ta[:A_BLOCK]
|
||||
tb = self._tail_b
|
||||
tb += data
|
||||
while len(tb) >= B_BLOCK:
|
||||
self._bx = _permute_b(self._bx ^ int.from_bytes(tb[:B_BLOCK], "little"))
|
||||
del tb[:B_BLOCK]
|
||||
return self
|
||||
|
||||
def digest(self):
|
||||
rows = list(self._rows)
|
||||
padded = _pad_tail(self._tail_a, self._bits, A_BLOCK)
|
||||
for offset in range(0, len(padded), A_BLOCK):
|
||||
_absorb_a(rows, padded[offset : offset + A_BLOCK])
|
||||
part_a = _squeeze_a(rows)
|
||||
x = self._bx
|
||||
padded = _pad_tail(self._tail_b, self._bits, B_BLOCK)
|
||||
for offset in range(0, len(padded), B_BLOCK):
|
||||
x = _permute_b(x ^ int.from_bytes(padded[offset : offset + B_BLOCK], "little"))
|
||||
part_b = (x & ((1 << 512) - 1)).to_bytes(64, "little")
|
||||
return part_a + part_b
|
||||
|
||||
def hexdigest(self):
|
||||
return self.digest().hex()
|
||||
|
||||
|
||||
def _track_a(data):
|
||||
rows = list(_A_IV)
|
||||
padded = _pad_tail(data, len(data) * 8, A_BLOCK)
|
||||
for offset in range(0, len(padded), A_BLOCK):
|
||||
_absorb_a(rows, padded[offset : offset + A_BLOCK])
|
||||
return _squeeze_a(rows)
|
||||
|
||||
|
||||
def _track_b(data):
|
||||
x = _B_IV
|
||||
padded = _pad_tail(data, len(data) * 8, B_BLOCK)
|
||||
for offset in range(0, len(padded), B_BLOCK):
|
||||
x = _permute_b(x ^ int.from_bytes(padded[offset : offset + B_BLOCK], "little"))
|
||||
return (x & ((1 << 512) - 1)).to_bytes(64, "little")
|
||||
|
||||
|
||||
def njlhash(data):
|
||||
if isinstance(data, str):
|
||||
data = data.encode("utf-8")
|
||||
return _track_a(data) + _track_b(data)
|
||||
|
||||
|
||||
def njlhash_hex(data):
|
||||
return njlhash(data).hex()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
live = NJLHash()
|
||||
for ch in "EXAMPLE":
|
||||
live.update(ch)
|
||||
print("+" + ch + " live: " + live.hexdigest()[:48] + "...")
|
||||
print("EXAMPLE -> " + live.hexdigest())
|
||||
Reference in New Issue
Block a user