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."""