—Mike Speciner
I’m an engineer tasked with building a system to launch nuclear missiles, and I want to make sure that the missiles aren’t launched by mistake. But I also want to make sure that they will be launched when properly authorized, so that they can serve as a credible deterrent. To do this, I want to give out launch codes to a number of trusted individuals (for redundancy) but require the cooperation of a few of them in order to launch (so one or two of them can’t go rogue).
I have a b-bit secret s that I want to break into n shares so that any k of those shares can be combined to reconstruct the secret, but no k−1 of them can give any information about the secret.
k = 1: Each share is the secret itself:
si = s
k = n: n−1 shares are random b-bit numbers, and the remaining share is the xor of those numbers and the secret s:
si = ri for 0 < i < k
s0 = s ⊕ r1 ⊕ ⋯ ⊕ rk−1
Observation: A line is determined by 2 points. More generally a polynomial curve of degree k−1 is determined by k points on that curve.
So, if we give a point on a k−1 degree curve to each of our n sharers, any k of them can together determine the curve. If we make y at x = 0 be our secret s, and choose the other k−1 coefficients randomly, we’ve mostly solved our problem.
| = |
|
|
To compute the secret s from k of the shares si, take the corresponding k rows from the matrix (leaving a k×k Vandermonde matrix), invert it and then multiply by the vector of k shares. The first element of the resulting vector is the secret s, the remaining elements are the random numbers ri.
So far, I’ve described the scheme using “real” numbers, but those are problematic on a computer. We could use rational numbers, but those get expensive to compute. Also, the notion of a random number gets a bit dicey. Integers don’t work, because they’re mostly hard to invert. The numbers we really want to use are those from a finite field.
| Real numbers (int, float) | BAD |
| Finite field numbers | GOOD |
In order for numbers to be usable for our secret sharing scheme, they have to satisfy certain properties:
+ − × / 0 1 ∞
Numbers with these properties are called finite fields, and lucky for us, there are lots of them. For each prime p and each positive integer n, there is exactly one finite field, written GF(pn); it has pn numbers in it.
I’ve written a finite field module, ffield.py, for computing with finite fields. It has a single class, called ffield, which takes three arguments: a prime p, a positive integer n, and an irreducible polynomial encoded as a number or tuple, and returns a class implementing the specified finite field. The reason for the third argument is that, while there is only a single finite field for a given p and n, its numbers can be represented by nonegative integers less than pn in many different ways, depending on that third argument. But 0 and 1 are always represented as themselves.
| + | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f |
| 1 | 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6 | 9 | 8 | b | a | d | c | f | e |
| 2 | 2 | 3 | 0 | 1 | 6 | 7 | 4 | 5 | a | b | 8 | 9 | e | f | c | d |
| 3 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | b | a | 9 | 8 | f | e | d | c |
| 4 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | c | d | e | f | 8 | 9 | a | b |
| 5 | 5 | 4 | 7 | 6 | 1 | 0 | 3 | 2 | d | c | f | e | 9 | 8 | b | a |
| 6 | 6 | 7 | 4 | 5 | 2 | 3 | 0 | 1 | e | f | c | d | a | b | 8 | 9 |
| 7 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | f | e | d | c | b | a | 9 | 8 |
| 8 | 8 | 9 | a | b | c | d | e | f | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 9 | 9 | 8 | b | a | d | c | f | e | 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6 |
| a | a | b | 8 | 9 | e | f | c | d | 2 | 3 | 0 | 1 | 6 | 7 | 4 | 5 |
| b | b | a | 9 | 8 | f | e | d | c | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 |
| c | c | d | e | f | 8 | 9 | a | b | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 |
| d | d | c | f | e | 9 | 8 | b | a | 5 | 4 | 7 | 6 | 1 | 0 | 3 | 2 |
| e | e | f | c | d | a | b | 8 | 9 | 6 | 7 | 4 | 5 | 2 | 3 | 0 | 1 |
| f | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| × | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f |
| 2 | 0 | 2 | 4 | 6 | 8 | a | c | e | f | d | b | 9 | 7 | 5 | 3 | 1 |
| 3 | 0 | 3 | 6 | 5 | c | f | a | 9 | 7 | 4 | 1 | 2 | b | 8 | d | e |
| 4 | 0 | 4 | 8 | c | f | b | 7 | 3 | 1 | 5 | 9 | d | e | a | 6 | 2 |
| 5 | 0 | 5 | a | f | b | e | 1 | 4 | 9 | c | 3 | 6 | 2 | 7 | 8 | d |
| 6 | 0 | 6 | c | a | 7 | 1 | b | d | e | 8 | 2 | 4 | 9 | f | 5 | 3 |
| 7 | 0 | 7 | e | 9 | 3 | 4 | d | a | 6 | 1 | 8 | f | 5 | 2 | b | c |
| 8 | 0 | 8 | f | 7 | 1 | 9 | e | 6 | 2 | a | d | 5 | 3 | b | c | 4 |
| 9 | 0 | 9 | d | 4 | 5 | c | 8 | 1 | a | 3 | 7 | e | f | 6 | 2 | b |
| a | 0 | a | b | 1 | 9 | 3 | 2 | 8 | d | 7 | 6 | c | 4 | e | f | 5 |
| b | 0 | b | 9 | 2 | d | 6 | 4 | f | 5 | e | c | 7 | 8 | 3 | 1 | a |
| c | 0 | c | 7 | b | e | 2 | 9 | 5 | 3 | f | 4 | 8 | d | 1 | a | 6 |
| d | 0 | d | 5 | 8 | a | 7 | f | 2 | b | 6 | e | 3 | 1 | c | 4 | 9 |
| e | 0 | e | 3 | d | 6 | 8 | 5 | b | c | 2 | f | 1 | a | 4 | 9 | 7 |
| f | 0 | f | 1 | e | 2 | d | 3 | c | 4 | b | 5 | a | 6 | 9 | 7 | 8 |
I’ve written a matrix module, matrix.py, for computing with matrices whose elements may be regular Python numbers or Python number-like class instances. In particular, it works with ffield.py and rational.py (an implementation of the field of rational numbers). It has a single class, called matrix, which takes arguments specifying the dimensions of the matrix and its contents, and returns the corresponding vector or matrix, or higher-dimensional array (but we can ignore that). It provides the usual matrix operations, such as addition, subtraction, multiplication, division, negation, inverse, transpose, trace, determinant.
I’ve written a little module, share.py, for demonstrating the use of finite fields for secret sharing. If we have a b-bit secret, GF(2b) is a natural choice. By default, share.py uses b = 256, and in particular F=ffield(2,256,1061).
All of the python modules mentioned, as well as this presentation, are available on https://github.com/ms0/. The python modules can be imported from the msmath package (pip install msmath).