owenrusk.dev

tally

shamir's secret sharing over gf(256).

git clone https://owenrusk.dev/tally.git

commit 07d5b0d9de5ea454e03e1defe9b668cb5e4ce68d
parent eed86d5b4ffb6b8bded85b168a45f131cf16dc27
author Owen Rusk <owen@papermothgames.com>
date   2025-11-17 21:25:48 -0600
shamir: lagrange weights once per join, not once per byte

joining a few kilobytes at k=10 went from seconds to a blink.
tally/shamir.py+13-11
diff --git a/tally/shamir.py b/tally/shamir.py
index 30d44b4..368be83 100644
--- a/tally/shamir.py
+++ b/tally/shamir.py
@@ -28,17 +28,19 @@ def interpolate(shares: list[tuple[int, bytes]], x: int = 0) -> bytes:
     xs = [xj for xj, _ in shares]
     if len(set(xs)) != len(xs):
         raise ValueError("two shares with the same x")
-    out = bytearray()
-    for i in range(len(shares[0][1])):
-        acc = 0
-        for j, (xj, ys) in enumerate(shares):
-            num = den = 1
-            for m, xm in enumerate(xs):
-                if m != j:
-                    num = gf256.mul(num, xm ^ x)
-                    den = gf256.mul(den, xm ^ xj)
-            acc ^= gf256.mul(ys[i], gf256.div(num, den))
-        out.append(acc)
+    # the weights depend only on the xs, so work them out once, not once per byte
+    weights = []
+    for j, xj in enumerate(xs):
+        num = den = 1
+        for m, xm in enumerate(xs):
+            if m != j:
+                num = gf256.mul(num, xm ^ x)
+                den = gf256.mul(den, xm ^ xj)
+        weights.append(gf256.div(num, den))
+    out = bytearray(len(shares[0][1]))
+    for w, (_, ys) in zip(weights, shares):
+        for i, y in enumerate(ys):
+            out[i] ^= gf256.mul(w, y)
     return bytes(out)