59 lines
1.4 KiB
Python
59 lines
1.4 KiB
Python
# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
|
#
|
|
# Seed XOR helpers
|
|
#
|
|
import os, hashlib
|
|
from mnemonic import Mnemonic
|
|
|
|
|
|
def numwords_to_len(num_words):
|
|
return (num_words * 8) // 6
|
|
|
|
|
|
def xor(*args):
|
|
# bit-wise xor between all args
|
|
vlen = len(args[0])
|
|
|
|
# all have to be same length
|
|
assert all(len(e) == vlen for e in args)
|
|
rv = bytearray(vlen)
|
|
|
|
for i in range(vlen):
|
|
for a in args:
|
|
rv[i] ^= a[i]
|
|
return rv
|
|
|
|
|
|
def xor_split(secret, num_parts, deterministic=False):
|
|
vlen = len(secret)
|
|
parts = []
|
|
|
|
for i in range(num_parts - 1):
|
|
if deterministic:
|
|
msg = b'Batshitoshi ' + secret + b'%d of %d parts' % (i, num_parts)
|
|
part = hashlib.sha256(msg).digest()[:vlen]
|
|
else:
|
|
part = hashlib.sha256(os.urandom(vlen)).digest()[:vlen]
|
|
|
|
parts.append(part)
|
|
|
|
parts.append(xor(secret, *parts))
|
|
|
|
assert xor(*parts) == secret # selftest
|
|
|
|
return parts
|
|
|
|
|
|
def prepare_test_pairs(num_parts, num_words=24, deterministic=False, mnemonic=None):
|
|
if mnemonic is None:
|
|
seed = os.urandom(numwords_to_len(num_words))
|
|
mnemonic = Mnemonic('english').to_mnemonic(seed)
|
|
else:
|
|
seed = Mnemonic.to_seed(mnemonic=mnemonic)
|
|
|
|
parts = xor_split(seed, num_parts=num_parts, deterministic=deterministic)
|
|
|
|
return [Mnemonic('english').to_mnemonic(s) for s in parts], mnemonic
|
|
|
|
# EOF
|