From ad4166fb03ace2f247113b1eb03d893d927f8111 Mon Sep 17 00:00:00 2001 From: Martin Tranberg Date: Sun, 29 Mar 2026 17:36:13 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20QuickXorHash:=20XOR=20l=C3=A6ngde=20ind?= =?UTF-8?q?=20i=20de=20sidste=2064=20bit=20(bits=2096-159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Korrigerer finaliseringslogikken så filstørrelsen XOR'es ind i de mest betydende 64 bit af 160-bit staten - Tidligere version XOR'ede i de mindst betydende bit, hvilket gav forkerte hashes - Dette matcher nu præcis Microsofts specifikation og fjerner falske hash-mismatches --- download_sharepoint.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/download_sharepoint.py b/download_sharepoint.py index d0cd55c..e3ce989 100644 --- a/download_sharepoint.py +++ b/download_sharepoint.py @@ -80,8 +80,8 @@ 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. - Follows the official Microsoft/Rclone implementation: - 160-bit circular XOR with a final length XOR.""" + Follows the official Microsoft implementation: 160-bit circular XOR + with the file length XORed into the LAST 64 bits.""" h = 0 length = 0 mask = (1 << 160) - 1 @@ -93,16 +93,14 @@ def quickxorhash(file_path): break for b in chunk: shift = (length * 11) % 160 - # Circular shift left: the byte is XORed into the 160-bit state - # at a position that rotates 11 bits for every byte. shifted = b << shift wrapped = (shifted & mask) | (shifted >> 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 + # Finalize: XOR the 64-bit length into the LAST 64 bits of the 160-bit state. + # Bits 96 to 159. + h ^= (length << (160 - 64)) # Convert to 20 bytes (160 bits) in little-endian format result = h.to_bytes(20, byteorder='little')