Request: This isn't exactly secret, but I didn't plan to release it yet. So don't go telling all your friends, yet... let people stumble on this thread on their own
You're ridiculous! I was going to keep this a secret until I released the new wallets, but that may still be a while, and the feature is technically already done. If you hadn't
specifically requested exactly what I just finished a few days ago, I was going to leave it to be part of mega-release with the new wallets
However, it's not integrated into the GUI, because the current wallets use a ton of data to represent a wallet, and splitting them results in each part being even 2x bigger. The new wallets will use 160 bits instead of 512, making it a lot more "friendly" to apply this technique. However, if you don't mind that it will be a ridiculous amount of data and you have some patience with the command line, you can do this right now! (It's so fresh though, you have to switch to the "armoryd" branch which is where I've been doing my latest development)
If you're into math, you might enjoy the description of how this works, at the bottom of this post. For now, here's some sample output from my unit-test:
Splitting secret into 3-of-5: secret=9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
Fragments:
Fragment 1: [a272f36702ea5db5d89d9c41931faba0, ab273c91fef6de0d5cc3dc5e87712255]
Fragment 2: [3b5090051d3684c2ca8aa241b353ae0e, 56cfeec3a5c2dc64c05c4649bcc851a2]
Fragment 3: [49bc409989e5b8dbb9f3241bec05f5bc, 27e8cf636660f8819112fdc58d8f0352]
Fragment 4: [46822a24fc0243e438779d28e29799e4, 312166ef08a03608a8083123445d44e7]
Fragment 5: [630744fd7c3c59d632b93949901ea8be, b18f33873447825b8ab52f424e7caf28]
Reconstructing secret from various subsets of fragments...
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
The reconstructed secret is: 9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f
Splitting secret took: 0.00063 sec
Reconstructing takes: 0.00290 sec
The code that generated what is above is in unittest.py:
from armoryengine import *
M,N = 3,5
nbytes=16
secretHex = '9f'*16
secret = hex_to_binary(secretHex)
print '\nSplitting secret into %d-of-%d: secret=%s' % (M,N,secretHex)
tstart = RightNow()
out = SplitSecret(secret, M, N)
tsplit = RightNow() - tstart
print 'Fragments:'
for i in range(len(out)):
x = binary_to_hex(out[i][0])
y = binary_to_hex(out[i][1])
print ' Fragment %d: [%s, %s]' % (i+1,x,y)
trecon = 0
print 'Reconstructing secret from various subsets of fragments...'
for i in range(10):
shuffle(out)
tstart = RightNow()
reconstruct = ReconstructSecret(out, M, nbytes)
trecon += RightNow() - tstart
print ' The reconstructed secret is:', binary_to_hex(reconstruct)
print 'Splitting secret took: %0.5f sec' % tsplit
print 'Reconstructing takes: %0.5f sec' % (trecon/10)
I'm not sure I can cram it into the GUI and subject users to typing in 1kB/fragment. That's a lot of data! I think I'll skip that for now, and just focus on finishing the new wallets and release it with that...
Enjoy!
The Math: It's a
super-elegant solution: encode your secret into the coefficients of a polynomial, and then distribute points on that polynomial. If it's a first-degree polynomial (line), two points are needed to solve the coefficients (and thus recover the secret). If you want to require 3 points, use a second-degree poly (parabola). Use higher order if you want to require more pieces.
The cooler part about this is that you have to use finite fields if you want any chance of this working with 256-bit numbers: pick a large prime number, N, and do all operations modulo N. This creates a cyclic group of size N. Things like division are simply A/B = A*(B^(N-1)). You can't do it with floats or doubles, because there's not enough precision to track all the significant digits in the variables and you'd die from the rounding errors.
And the
coolest part is how little code was required to implement all the finite-field matrix operations in python! There's nothing efficient about it, but it doesn't need to be efficient to be useful here. It is still usable up to 8-of-N secret splitting -- it takes a couple seconds to reconstruct, but who cares!