mirror of
https://github.com/OPSnet/xld_logchecker.py.git
synced 2026-01-16 15:04:17 -05:00
Simplified scrambling function
This commit is contained in:
119
xld.py
119
xld.py
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user