From 39a3aff495605353d9ba5e833fa561196c901121 Mon Sep 17 00:00:00 2001 From: Martin Tranberg Date: Sun, 29 Mar 2026 14:52:13 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20QuickXorHash-implementering=20og=20tilf?= =?UTF-8?q?=C3=B8j=20manglende=20l=C3=A6ngde-XOR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Opdaterer quickxorhash til at bruge en 160-bit heltalsbuffer for korrekt cirkulær rotation - Tilføjer det obligatoriske XOR-trin med filens længde, som manglede tidligere - Sikrer korrekt 20-byte little-endian format ved base64-encoding - Dette løser problemet med konstante hash-mismatch på ellers korrekte filer --- download_sharepoint.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/download_sharepoint.py b/download_sharepoint.py index 54a29cc..d0cd55c 100644 --- a/download_sharepoint.py +++ b/download_sharepoint.py @@ -80,24 +80,33 @@ def safe_get(url, headers, stream=False, timeout=60, params=None): # --- Punkt 4: Integrity Validation (QuickXorHash) --- def quickxorhash(file_path): """Compute Microsoft QuickXorHash for a file. Returns base64-encoded string. - Uses 3 × uint64 cells matching Microsoft's C# reference implementation.""" - SHIFT = 11 - WIDTH = 160 - data = [0, 0, 0] # 3 × 64-bit unsigned integers - i = 0 + Follows the official Microsoft/Rclone implementation: + 160-bit circular XOR with a final length XOR.""" + h = 0 + length = 0 + mask = (1 << 160) - 1 + with open(file_path, 'rb') as f: while True: chunk = f.read(CHUNK_SIZE) if not chunk: break - for byte in chunk: - bit_idx = (i * SHIFT) % WIDTH - cell = bit_idx // 64 - shift = bit_idx % 64 - data[cell] = (data[cell] ^ (byte << shift)) & 0xFFFFFFFFFFFFFFFF - i += 1 - result = struct.pack('> 160) + h ^= wrapped + length += 1 + + # Finalize: XOR the 64-bit length into the 160-bit state. + # This affects the first 8 bytes of the little-endian representation. + h ^= length + + # Convert to 20 bytes (160 bits) in little-endian format + result = h.to_bytes(20, byteorder='little') + return base64.b64encode(result).decode('ascii') def verify_integrity(local_path, remote_hash): """Verifies file integrity using Microsoft QuickXorHash."""