Simplified scrambling function

This commit is contained in:
puddly
2018-08-05 17:27:47 -04:00
parent c57920d857
commit aa53527f67

119
xld.py
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import sys
import copy
import base64
import struct
import argparse
@@ -15,21 +15,24 @@ DIGEST_LENGTH = 64 + len('\nVersion=0001')
def bit_concat32(high, low):
return ((high & 0xFFFFFFFF) << 32) | (low & 0xFFFFFFFF)
def byte_swap(bits, n):
n = n & (1 << bits) - 1
return int.from_bytes(n.to_bytes(bits // 8, 'little')[::-1], 'little')
def reverse_bytes32(n):
as_bytes = n.to_bytes(4, 'little')[::-1]
return int.from_bytes(as_bytes, 'little')
def LODWORD(n):
def concat_reverse_bytes32(high, low):
return bit_concat32(reverse_bytes32(high), reverse_bytes32(low))
def LOW32(n):
return n & 0x00000000FFFFFFFF
def HIDWORD(n):
def HIGH32(n):
return (n & 0xFFFFFFFF00000000) >> 32
def set_LODWORD(n, v):
def set_LOW32(n, v):
return (n & 0xFFFFFFFF00000000) | (v & 0xFFFFFFFF)
def set_HIDWORD(n, v):
return (n & 0x00000000FFFFFFFF) | ((v & 0xFFFFFFFF) << 32)
def set_HIGH32(n, v):
return ((v & 0xFFFFFFFF) << 32) | (n & 0x00000000FFFFFFFF)
def rotate_left(n, k):
return ((n << k) & 0xFFFFFFFF) | (n >> (32 - k))
@@ -38,9 +41,8 @@ def rotate_right(n, k):
return ((n >> k) | (n << (32 - k))) & 0xFFFFFFFF
def almost_sha256(data):
# Non-standard initial state
state = (0x1D95E3A4, 0x06520EF5, 0x3A9CFB75, 0x6104BCAE, 0x09CEDA82, 0xBA55E60B, 0xEAEC16C6, 0xEB19AF15)
def sha256(data, initial_state):
state = initial_state
# Standard round constants
round_constants = (0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2)
@@ -52,11 +54,13 @@ def almost_sha256(data):
data += b'\x80' + (b'\x00' * ((K - 7) // 8)) + L.to_bytes(8, 'big')
for start in range(0, len(data), 64):
# Process chunks of 64 bytes
chunk = data[start:start + 64]
round_state = [0] * 64
round_state[0:16] = struct.unpack('!16L', chunk)
for i in range(0, len(chunk), 4):
round_state[i // 4] = int.from_bytes(chunk[i:i + 4], 'big')
for i in range(16, 64):
s0 = rotate_right(round_state[i - 15], 7) ^ rotate_right(round_state[i - 15], 18) ^ (round_state[i - 15] >> 3)
s1 = rotate_right(round_state[i - 2], 17) ^ rotate_right(round_state[i - 2], 19) ^ (round_state[i - 2] >> 10)
@@ -89,80 +93,57 @@ def almost_sha256(data):
def scramble(data):
previous = MAGIC_INITIAL_STATE
mod_current = 0
X = LOW32(MAGIC_INITIAL_STATE)
Y = HIGH32(MAGIC_INITIAL_STATE)
output = b''
output = []
for size in range(DIGEST_LENGTH, 0, -8):
current = 0
for offset in range(0, len(data), 8):
size = len(data) - offset
needs_padding = (size < 8) # We will always need padding in the end
# If we can read off two 32-bit integers, do it. Otherwise, reuse the last state
if size >= 8:
a = int.from_bytes(data[offset:offset + 4], 'little')
b = int.from_bytes(data[offset + 4:offset + 8], 'little')
if not needs_padding:
offset = DIGEST_LENGTH - size
chunk1 = int.from_bytes(data[offset:offset + 4], 'little')
chunk2 = int.from_bytes(data[offset + 4:offset + 8], 'little')
current = previous ^ bit_concat32(byte_swap(32, chunk2), byte_swap(32, chunk1))
else:
current = byte_swap(64, bit_concat32(mod_current, HIDWORD(mod_current)))
X ^= reverse_bytes32(a)
Y ^= reverse_bytes32(b)
# Do some kind of 8-round scramble on the state four times
for i in range(4):
for j in range(2):
current = set_HIDWORD(current, HIDWORD(current) ^ current)
Y ^= X
a = (MAGIC_CONSTANTS[4*j + 0] + HIDWORD(current)) & 0xFFFFFFFF
b = a
a = rotate_left(a, 1)
c = (b - 1 + a) & 0xFFFFFFFF
d = c
c = rotate_left(c, 4)
current = set_LODWORD(current, d ^ c ^ current)
a = (MAGIC_CONSTANTS[4*j + 0] + Y) & 0xFFFFFFFF
b = (a - 1 + rotate_left(a, 1)) & 0xFFFFFFFF
e = (MAGIC_CONSTANTS[4*j + 1] + current) & 0xFFFFFFFF
f = e
e = rotate_left(e, 2)
g = (f + 1 + e) & 0xFFFFFFFF
h = g
g = rotate_left(g, 8)
i = (MAGIC_CONSTANTS[4*j + 2] + (h ^ g)) & 0xFFFFFFFF
p = i
i = rotate_left(i, 1)
k = (i - p) & 0xFFFFFFFF
l = k
k = rotate_left(k, 16)
X ^= b ^ rotate_left(b, 4)
current = set_HIDWORD(current, HIDWORD(current) ^ (current | l) ^ k)
m = (MAGIC_CONSTANTS[4*j + 3] + HIDWORD(current)) & 0xFFFFFFFF
n = m
m = rotate_left(m, 2)
c = (MAGIC_CONSTANTS[4*j + 1] + X) & 0xFFFFFFFF
d = (c + 1 + rotate_left(c, 2)) & 0xFFFFFFFF
current = set_LODWORD(current, ((n + 1 + m) ^ current) & 0xFFFFFFFF)
e = (MAGIC_CONSTANTS[4*j + 2] + (d ^ rotate_left(d, 8))) & 0xFFFFFFFF
f = (rotate_left(e, 1) - e) & 0xFFFFFFFF
previous = current
mod_current = byte_swap(64, (current << 32) | HIDWORD(current))
if needs_padding:
remaining = bytearray(data[len(output):])
Y ^= (X | f) ^ rotate_left(f, 16)
for i in range(size):
remaining[i] ^= mod_current & 0xFF
mod_current >>= 8
output += remaining
break
g = (MAGIC_CONSTANTS[4*j + 3] + Y) & 0xFFFFFFFF
X ^= (g + 1 + rotate_left(g, 2)) & 0xFFFFFFFF
output += mod_current.to_bytes(8, 'little')
output.append(concat_reverse_bytes32(Y, X).to_bytes(8, 'little'))
return output
if size > 0:
last_chunk = output.pop()
output.append(bytearray(data[8 * len(output) + i] ^ last_chunk[i] for i in range(size)))
return b''.join(output)
def encode(data):
import base64
# Non-standard base64 alphabet
mapping = str.maketrans(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
@@ -193,7 +174,9 @@ def extract_info(data):
def xld_verify(data):
data, version, old_signature = extract_info(data)
hashed_data = (almost_sha256(data.encode('utf-8')) + '\nVersion=0001').encode('ascii')
initial_state = (0x1D95E3A4, 0x06520EF5, 0x3A9CFB75, 0x6104BCAE, 0x09CEDA82, 0xBA55E60B, 0xEAEC16C6, 0xEB19AF15)
hashed_data = (sha256(data.encode('utf-8'), initial_state) + '\nVersion=0001').encode('ascii')
scrambled_data = scramble(hashed_data)
signature = encode(scrambled_data)