From 66274429fb1e48ab95c4c37b5b956333d094a7ff Mon Sep 17 00:00:00 2001
From: Claude <claude@anthropic.com>
Date: Fri, 6 Mar 2026 10:33:00 +0000
Subject: [PATCH] Fix SHA-1 for 32-bit architectures
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Replace int type with Int32 throughout SHA-1 implementation to ensure
portability across 32-bit and 64-bit systems.

On 32-bit systems, OCaml's int type is only 31 bits (with 1 bit for GC),
causing literals like 0xffffffff and operations like (1 lsl 32) to overflow.

Changes:
- Use Int32 type for all 32-bit operations
- Replace integer literals with Int32 literals (e.g., 0xffffffffl)
- Use Int32 module functions (shift_left, shift_right_logical, etc.)
- Remove mod (1 lsl 32) operations (Int32 wraps naturally)

This ensures correct SHA-1 computation on 32-bit PowerPC and other
32-bit architectures while maintaining correctness on 64-bit systems.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
---
 src/lib/sha1.ml | 85 ++++++++++++++++++++++++++-----------------------
 1 file changed, 46 insertions(+), 39 deletions(-)

diff --git a/src/lib/sha1.ml b/src/lib/sha1.ml
index 5d72191..cec2265 100644
--- a/src/lib/sha1.ml
+++ b/src/lib/sha1.ml
@@ -23,41 +23,46 @@ let encode_hex (src : bytes) : bytes =
   dst
 ;;
 
-(* Left-rotate *)
-let rol (value : int) (bits : int) : int =
-  (value lsl bits) lor (value lsr (32 - bits)) land 0xffffffff
+(* Left-rotate for 32-bit values *)
+let rol (value : int32) (bits : int) : int32 =
+  Int32.(logor (shift_left value bits) (shift_right_logical value (32 - bits)))
 ;;
 
 (* Number to its 32-bit big-endian representation *)
-let number_to_32_bit_be (n : int) : bytes =
+let number_to_32_bit_be (n : int32) : bytes =
   let open Bytes in
   let result = create 4 in
-  set result 0 (Char.chr ((n land 0xff000000) lsr 24));
-  set result 1 (Char.chr ((n land 0x00ff0000) lsr 16));
-  set result 2 (Char.chr ((n land 0x0000ff00) lsr 8));
-  set result 3 (Char.chr (n land 0x000000ff));
+  set result 0 (Char.chr (Int32.(to_int (shift_right_logical (logand n 0xff000000l) 24))));
+  set result 1 (Char.chr (Int32.(to_int (shift_right_logical (logand n 0x00ff0000l) 16))));
+  set result 2 (Char.chr (Int32.(to_int (shift_right_logical (logand n 0x0000ff00l) 8))));
+  set result 3 (Char.chr (Int32.(to_int (logand n 0x000000ffl))));
   result
 ;;
 
 (* 32-bit big-endian representation of a number *)
-let number_of_32_bit_be (b : bytes) : int =
+let number_of_32_bit_be (b : bytes) : int32 =
   let open Bytes in
-  (Char.code (get b 0) lsl 24)
-  + (Char.code (get b 1) lsl 16)
-  + (Char.code (get b 2) lsl 8)
-  + Char.code (get b 3)
+  Int32.(
+    logor
+      (logor
+         (logor
+            (shift_left (of_int (Char.code (get b 0))) 24)
+            (shift_left (of_int (Char.code (get b 1))) 16))
+         (shift_left (of_int (Char.code (get b 2))) 8))
+      (of_int (Char.code (get b 3))))
 ;;
 
 (* SHA-1 digest *)
 let digest (bytes : bytes) : bytes =
-  let h0 = ref 0x67452301 in
-  let h1 = ref 0xEFCDAB89 in
-  let h2 = ref 0x98BADCFE in
-  let h3 = ref 0x10325476 in
-  let h4 = ref 0xC3D2E1F0 in
-  let ml = Bytes.length bytes * 8 in
+  let h0 = ref 0x67452301l in
+  let h1 = ref 0xEFCDAB89l in
+  let h2 = ref 0x98BADCFEl in
+  let h3 = ref 0x10325476l in
+  let h4 = ref 0xC3D2E1F0l in
+  let ml = Int32.of_int (Bytes.length bytes * 8) in
   let preprocess _a =
-    let length_mod_512 = ml mod 512 in
+    let ml_int = Int32.to_int ml in
+    let length_mod_512 = ml_int mod 512 in
     let length_to_add =
       if 512 - length_mod_512 >= 65
       then 512 - length_mod_512
@@ -80,49 +85,51 @@ let digest (bytes : bytes) : bytes =
     let chunk = Bytes.sub preprocessed (i * 64) 64 in
     let w =
       Array.init 80 (fun k ->
-        if k < 16 then number_of_32_bit_be (Bytes.sub chunk (k * 4) 4) else 0)
+        if k < 16 then number_of_32_bit_be (Bytes.sub chunk (k * 4) 4) else 0l)
     in
     for j = 16 to 79 do
       w.(j)
-        <- rol (w.(j - 3) lxor w.(j - 8) lxor w.(j - 14) lxor w.(j - 16)) 1
+        <- rol
+             Int32.(logxor (logxor (logxor w.(j - 3) w.(j - 8)) w.(j - 14)) w.(j - 16))
+             1
     done;
     let a = ref !h0 in
     let b = ref !h1 in
     let c = ref !h2 in
     let d = ref !h3 in
     let e = ref !h4 in
-    let temp = ref 0 in
-    let k = ref 0 in
-    let f = ref 0 in
+    let temp = ref 0l in
+    let k = ref 0l in
+    let f = ref 0l in
     (* main loop *)
     for j = 0 to 79 do
       if 0 <= j && j < 20
       then (
-        f := !b land !c lxor (lnot !b land !d);
-        k := 0x5A827999)
+        f := Int32.(logxor (logand !b !c) (logand (lognot !b) !d));
+        k := 0x5A827999l)
       else if 20 <= j && j < 40
       then (
-        f := !b lxor !c lxor !d;
-        k := 0x6ED9EBA1)
+        f := Int32.(logxor (logxor !b !c) !d);
+        k := 0x6ED9EBA1l)
       else if 40 <= j && j < 60
       then (
-        f := !b land !c lor (!b land !d) lor (!c land !d);
-        k := 0x8F1BBCDC)
+        f := Int32.(logor (logor (logand !b !c) (logand !b !d)) (logand !c !d));
+        k := 0x8F1BBCDCl)
       else (
-        f := !b lxor !c lxor !d;
-        k := 0xCA62C1D6);
-      temp := (rol !a 5 + !f + !e + !k + w.(j)) mod (1 lsl 32);
+        f := Int32.(logxor (logxor !b !c) !d);
+        k := 0xCA62C1D6l);
+      temp := Int32.(add (add (add (add (rol !a 5) !f) !e) !k) w.(j));
       e := !d;
       d := !c;
       c := rol !b 30;
       b := !a;
       a := !temp
     done;
-    h0 := (!h0 + !a) mod (1 lsl 32);
-    h1 := (!h1 + !b) mod (1 lsl 32);
-    h2 := (!h2 + !c) mod (1 lsl 32);
-    h3 := (!h3 + !d) mod (1 lsl 32);
-    h4 := (!h4 + !e) mod (1 lsl 32)
+    h0 := Int32.add !h0 !a;
+    h1 := Int32.add !h1 !b;
+    h2 := Int32.add !h2 !c;
+    h3 := Int32.add !h3 !d;
+    h4 := Int32.add !h4 !e
   done;
   Bytes.concat
     (Bytes.of_string "")
