owenrusk.dev

tally

shamir's secret sharing over gf(256).

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

tally / README.md -rw-r--r-- · 2401 bytes

 1 # tally
 2 
 3 Split a secret into n shares. Any k of them rebuild it. Fewer than k tell you nothing.
 4 
 5 shamir's secret sharing over gf(256), one byte at a time. standard library only, randomness from `secrets`.
 6 
 7 ## install
 8 
 9 python 3.12 or newer. from a checkout:
10 
11     pip install .
12 
13 or run it in place with `python -m tally`.
14 
15 ## split
16 
17     tally split -k 3 -n 5 secret.txt
18     tally split -k 2 -n 3 < secret.txt
19     tally split -k 3 -n 5 -o shares/ secret.txt
20 
21 reads the secret from the file, or from stdin if there's no file, and prints n shares, one per line. 2 <= k <= n <= 255.
22 
23 with `-o DIR` each share goes to its own file instead, `DIR/share-1.txt` and on, mode 600. it won't overwrite a share that's already there.
24 
25 the secret is never an argument. arguments end up in shell history and in `ps`.
26 
27 ## join
28 
29     tally join SHARE SHARE SHARE
30     tally join < shares.txt
31 
32 takes the shares as arguments, or one per line on stdin, and prints the secret. it refuses fewer than k, the same share twice, and shares from different splits. shares past the first k are checked against them.
33 
34 on stdin, blank lines and lines starting with `#` are skipped, so a file of shares can carry notes.
35 
36 ## share format
37 
38     t1-xxxx-xxxx-xxxx-...
39 
40 `t1` is the format version. the rest is crockford's base32 (digits and lowercase letters, no i, l, o or u) in groups of four, carrying:
41 
42     id     4 bytes, random, the same on every share of one split
43     k      1 byte, how many shares it takes
44     x      1 byte, this share's number, 1 to n
45     data   as long as the secret
46     check  4 bytes, crc32 of "t1" and everything above
47 
48 case, spaces and dashes don't matter when reading a share back, and o reads as 0, i and l as 1. a typo fails the check instead of rebuilding the wrong secret.
49 
50 a share gives away k and the secret's length. nothing else.
51 
52 `t1` won't change. a different format would get a different number.
53 
54 ## example
55 
56     $ printf 'hello' | tally split -k 2 -n 3
57     t1-37bm-w6r2-05ak-qdek-eg8b-0mzr
58     t1-37bm-w6r2-089d-kh89-b5n7-gd8c
59     t1-37bm-w6r2-0cqr-e75p-8aw2-f5sb
60 
61     $ tally join t1-37bm-w6r2-0cqr-e75p-8aw2-f5sb t1-37bm-w6r2-05ak-qdek-eg8b-0mzr
62     hello
63 
64     $ tally join t1-37bm-w6r2-0cqr-e75p-8aw2-f5sb
65     tally: need 2 shares, got 1
66 
67 shares from one split start the same way: that's the id. the secret comes back byte for byte, so `echo` would have split a trailing newline along with it.