owenrusk.dev

tally

shamir's secret sharing over gf(256).

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

last commit split: -o DIR writes each share to its own file,

tally

Split a secret into n shares. Any k of them rebuild it. Fewer than k tell you nothing.

shamir's secret sharing over gf(256), one byte at a time. standard library only, randomness from secrets.

install

python 3.12 or newer. from a checkout:

pip install .

or run it in place with python -m tally.

split

tally split -k 3 -n 5 secret.txt
tally split -k 2 -n 3 < secret.txt
tally split -k 3 -n 5 -o shares/ secret.txt

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.

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.

the secret is never an argument. arguments end up in shell history and in ps.

join

tally join SHARE SHARE SHARE
tally join < shares.txt

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.

on stdin, blank lines and lines starting with # are skipped, so a file of shares can carry notes.

share format

t1-xxxx-xxxx-xxxx-...

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:

id     4 bytes, random, the same on every share of one split
k      1 byte, how many shares it takes
x      1 byte, this share's number, 1 to n
data   as long as the secret
check  4 bytes, crc32 of "t1" and everything above

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.

a share gives away k and the secret's length. nothing else.

t1 won't change. a different format would get a different number.

example

$ printf 'hello' | tally split -k 2 -n 3
t1-37bm-w6r2-05ak-qdek-eg8b-0mzr
t1-37bm-w6r2-089d-kh89-b5n7-gd8c
t1-37bm-w6r2-0cqr-e75p-8aw2-f5sb

$ tally join t1-37bm-w6r2-0cqr-e75p-8aw2-f5sb t1-37bm-w6r2-05ak-qdek-eg8b-0mzr
hello

$ tally join t1-37bm-w6r2-0cqr-e75p-8aw2-f5sb
tally: need 2 shares, got 1

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.