├── matrix_test.go ├── row.go ├── field.go ├── number_test.go ├── msp_test.go ├── matrix.go ├── raw_test.go ├── elem.go ├── formatted_test.go ├── README.md ├── formatted.go ├── raw.go └── msp.go /matrix_test.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestRecovery(t *testing.T) { 9 | for _, field := range Fields { 10 | // Generate the matrix. 11 | height, width := 10, 10 12 | M := field.Matrix(height, width) 13 | 14 | for i := range M.m { 15 | for j := range M.m[i].r { 16 | M.m[i].r[j].e[0] = byte(i + 1) 17 | M.m[i].r[j] = M.m[i].r[j].Exp(j) 18 | } 19 | } 20 | 21 | // Find the recovery vector. 22 | r, ok := M.Recovery() 23 | if !ok { 24 | t.Fatalf("Failed to find the recovery vector!") 25 | } 26 | 27 | // Find the output vector. 28 | out := field.Row(width) 29 | for i := range M.m { 30 | out.AddM(M.m[i].Mul(r.r[i])) 31 | } 32 | 33 | // Check that it is the target vector. 34 | if !bytes.Equal(out.r[0].Bytes(), field.One().Bytes()) { 35 | t.Errorf("Output is not the target vector!") 36 | continue 37 | } 38 | 39 | for i := 1; i < width; i++ { 40 | if !bytes.Equal(out.r[i].Bytes(), field.Zero().Bytes()) { 41 | t.Errorf("Output is not the target vector!") 42 | continue 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /row.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | type Row struct { 4 | Field 5 | 6 | r []Elem 7 | } 8 | 9 | func (r Row) ByteSlices() [][]byte { 10 | bs := make([][]byte, 0, r.Width()) 11 | for i := range r.r { 12 | bs = append(bs, r.r[i].Bytes()) 13 | } 14 | return bs 15 | } 16 | 17 | func (r Row) Width() int { 18 | return len(r.r) 19 | } 20 | 21 | // AddM adds two vectors. 22 | func (r Row) AddM(s Row) { 23 | if r.Width() != s.Width() { 24 | panic("Can't add rows that are different sizes!") 25 | } 26 | 27 | for i := range s.r { 28 | r.r[i].AddM(s.r[i]) 29 | } 30 | } 31 | 32 | // MulM multiplies the row by a scalar. 33 | func (r Row) MulM(e Elem) { 34 | for i := range r.r { 35 | r.r[i] = r.r[i].Mul(e) 36 | } 37 | } 38 | 39 | func (r Row) Mul(e Elem) Row { 40 | elem := r.Row(r.Width()) 41 | for i := range r.r { 42 | elem.r[i] = r.r[i].Mul(e) 43 | } 44 | return elem 45 | } 46 | 47 | func (r Row) DotProduct(s Row) Elem { 48 | if r.Width() != s.Width() { 49 | panic("Can't add rows that are different sizes!") 50 | } 51 | 52 | elem := r.Zero() 53 | for i := range r.r { 54 | elem.AddM(r.r[i].Mul(s.r[i])) 55 | } 56 | return elem 57 | } 58 | -------------------------------------------------------------------------------- /field.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | type Field []byte 4 | 5 | var Fields = map[int]Field{ 6 | 16: BuildField(16, 135), // x^128 + x^7 + x^2 + x + 1 7 | 32: BuildField(32, 37, 4), // x^256 + x^10 + x^5 + x^2 + 1 8 | } 9 | 10 | func BuildField(size int, modulus ...byte) []byte { 11 | field := make([]byte, size) 12 | copy(field, modulus) 13 | 14 | return field 15 | } 16 | 17 | func (f Field) Elem(val []byte) Elem { 18 | elem := Elem{ 19 | Field: f, 20 | e: make([]byte, len(f)), 21 | } 22 | copy(elem.e, val) 23 | 24 | return elem 25 | } 26 | 27 | func (f Field) Row(width int) Row { 28 | row := Row{ 29 | Field: f, 30 | r: make([]Elem, width), 31 | } 32 | 33 | for i := range row.r { 34 | row.r[i] = f.Zero() 35 | } 36 | 37 | return row 38 | } 39 | 40 | func (f Field) Matrix(height, width int) Matrix { 41 | matrix := Matrix{ 42 | Field: f, 43 | m: make([]Row, height), 44 | } 45 | 46 | for i := range matrix.m { 47 | matrix.m[i] = f.Row(width) 48 | } 49 | 50 | return matrix 51 | } 52 | 53 | func (f Field) Zero() Elem { 54 | return f.Elem(nil) 55 | } 56 | 57 | func (f Field) One() Elem { 58 | return f.Elem([]byte{1}) 59 | } 60 | 61 | func (f Field) Size() int { 62 | return len(f) 63 | } 64 | 65 | func (f Field) BitSize() int { 66 | return f.Size() * 8 67 | } 68 | -------------------------------------------------------------------------------- /number_test.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "testing" 7 | ) 8 | 9 | func TestFieldElemMultiplicationOne(t *testing.T) { 10 | for _, field := range Fields { 11 | x := field.Zero() 12 | rand.Read(x.e) 13 | 14 | xy, yx := x.Mul(field.One()), field.One().Mul(x) 15 | 16 | one := make([]byte, field.Size()) 17 | one[0] = 1 18 | 19 | if !bytes.Equal(field.One().Bytes(), one) { 20 | t.Errorf("One is not one?") 21 | continue 22 | } 23 | 24 | if !bytes.Equal(xy.Bytes(), x.Bytes()) || !bytes.Equal(yx.Bytes(), x.Bytes()) { 25 | t.Fatalf("Multiplication by 1 failed!\nx = %x\n1*x = %x\nx*1 = %x", x, yx, xy) 26 | } 27 | } 28 | } 29 | 30 | func TestFieldElemMultiplicationZero(t *testing.T) { 31 | for _, field := range Fields { 32 | x := field.Zero() 33 | rand.Read(x.e) 34 | 35 | xy, yx := x.Mul(field.Zero()), field.Zero().Mul(x) 36 | 37 | if !bytes.Equal(field.Zero().Bytes(), make([]byte, field.Size())) { 38 | t.Fatalf("Zero is not zero?") 39 | } 40 | 41 | if !bytes.Equal(xy.Bytes(), field.Zero().Bytes()) || !bytes.Equal(yx.Bytes(), field.Zero().Bytes()) { 42 | t.Fatalf("Multiplication by 0 failed!\nx = %x\n0*x = %x\nx*0 = %x", x, yx, xy) 43 | } 44 | } 45 | } 46 | 47 | func TestFieldElemInvert(t *testing.T) { 48 | for _, field := range Fields { 49 | x := field.Zero() 50 | rand.Read(x.e) 51 | 52 | xInv := x.Invert() 53 | 54 | xy, yx := x.Mul(xInv), xInv.Mul(x) 55 | 56 | if !bytes.Equal(xy.Bytes(), field.One().Bytes()) || !bytes.Equal(yx.Bytes(), field.One().Bytes()) { 57 | t.Fatalf("Multiplication by inverse failed!\nx = %x\nxInv = %x\nxInv*x = %x\nx*xInv = %x", x, xInv, yx, xy) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /msp_test.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "errors" 7 | "testing" 8 | ) 9 | 10 | type Database map[string][][]byte 11 | 12 | func (d *Database) ValidUser(name string) bool { 13 | _, ok := (*d)[name] 14 | return ok 15 | } 16 | 17 | func (d *Database) CanGetShare(name string) bool { 18 | _, ok := (*d)[name] 19 | return ok 20 | } 21 | 22 | func (d *Database) GetShare(name string) ([][]byte, error) { 23 | out, ok := (*d)[name] 24 | 25 | if ok { 26 | return out, nil 27 | } else { 28 | return nil, errors.New("Not found!") 29 | } 30 | } 31 | 32 | func TestMSP(t *testing.T) { 33 | for _, field := range Fields { 34 | db := &Database{ 35 | "Alice": [][]byte{}, 36 | "Bob": [][]byte{}, 37 | "Carl": [][]byte{}, 38 | } 39 | 40 | sec := make([]byte, field.Size()) 41 | rand.Read(sec) 42 | sec[0] &= 63 // Removes first 2 bits of key. 43 | 44 | predicate, _ := StringToMSP("(2, (1, Alice, Bob), Carl)") 45 | 46 | shares1, err := predicate.DistributeShares(sec, db) 47 | if err != nil { 48 | t.Error(err) 49 | continue 50 | } 51 | shares2, _ := predicate.DistributeShares(sec, db) 52 | if err != nil { 53 | t.Error(err) 54 | continue 55 | } 56 | 57 | alice := bytes.Compare(shares1["Alice"][0], shares2["Alice"][0]) 58 | bob := bytes.Compare(shares1["Bob"][0], shares2["Bob"][0]) 59 | carl := bytes.Compare(shares1["Carl"][0], shares2["Carl"][0]) 60 | 61 | if alice == 0 && bob == 0 && carl == 0 { 62 | t.Fatalf("Key splitting isn't random! %v %v", shares1, shares2) 63 | } 64 | 65 | db1 := Database(shares1) 66 | db2 := Database(shares2) 67 | 68 | sec1, err := predicate.RecoverSecret(&db1) 69 | if err != nil { 70 | t.Fatalf("#1: %v", err) 71 | } 72 | 73 | sec2, err := predicate.RecoverSecret(&db2) 74 | if err != nil { 75 | t.Fatalf("#2: %v", err) 76 | } 77 | 78 | if !(bytes.Compare(sec, sec1) == 0 && bytes.Compare(sec, sec2) == 0) { 79 | t.Fatalf("Secrets derived differed: %v %v %v", sec, sec1, sec2) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /matrix.go: -------------------------------------------------------------------------------- 1 | // Matrix operations for elements in GF(2^128). 2 | package msp 3 | 4 | import "bytes" 5 | 6 | type Matrix struct { 7 | Field 8 | 9 | m []Row 10 | } 11 | 12 | func (m Matrix) Height() int { 13 | return len(m.m) 14 | } 15 | 16 | func (m Matrix) Width() int { 17 | return m.m[0].Width() 18 | } 19 | 20 | // Mul right-multiplies a matrix by a row. 21 | func (m Matrix) Mul(r Row) Row { 22 | row := m.Row(m.Height()) 23 | for i := range m.m { 24 | row.r[i] = m.m[i].DotProduct(r) 25 | } 26 | return row 27 | } 28 | 29 | // Recovery returns the row vector that takes this matrix to the target vector [1 0 0 ... 0]. 30 | func (m Matrix) Recovery() (Row, bool) { 31 | a, b := m.Height(), m.Width() 32 | zero := m.Zero() 33 | 34 | // aug is the target vector. 35 | aug := m.Row(a) 36 | aug.r[0] = zero.One() 37 | 38 | // Duplicate e away so we don't mutate it; transpose it at the same time. 39 | f := m.Matrix(a, b) 40 | 41 | for i := range m.m { 42 | for j := range m.m[i].r { 43 | f.m[j].r[i] = m.m[i].r[j].Dup() 44 | } 45 | } 46 | 47 | for i := range f.m { 48 | if i >= b { // The matrix is tall and thin--we've finished before exhausting all the rows. 49 | break 50 | } 51 | 52 | // Find a row with a non-zero entry in the (row)th position 53 | candId := -1 54 | for j := range f.m[i:] { 55 | if !bytes.Equal(f.m[j].r[i].e, zero.e) { 56 | candId = j + i 57 | break 58 | } 59 | } 60 | 61 | if candId == -1 { // If we can't find one, fail and return our partial work. 62 | return aug, false 63 | } 64 | 65 | // Move it to the top 66 | f.m[i], f.m[candId] = f.m[candId], f.m[candId] 67 | aug.r[i], aug.r[candId] = aug.r[candId], aug.r[i] 68 | 69 | // Make the pivot 1. 70 | fInv := f.m[i].r[i].Invert() 71 | 72 | f.m[i].MulM(fInv) 73 | aug.r[i] = aug.r[i].Mul(fInv) 74 | 75 | // Cancel out the (row)th position for every row above and below it. 76 | for j := range f.m { 77 | if j != i && !bytes.Equal(f.m[j].r[i].e, zero.e) { 78 | c := f.m[j].r[i].Dup() 79 | 80 | f.m[j].AddM(f.m[i].Mul(c)) 81 | aug.r[j].AddM(aug.r[i].Mul(c)) 82 | } 83 | } 84 | } 85 | 86 | return aug, true 87 | } 88 | -------------------------------------------------------------------------------- /raw_test.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestRaw(t *testing.T) { 8 | alice := Condition(Name{"Alice", 0}) 9 | bob := Condition(Name{"Bob", 0}) 10 | carl := Condition(Name{"Carl", 0}) 11 | 12 | query1 := Raw{ 13 | NodeType: NodeAnd, 14 | Left: alice, 15 | Right: bob, 16 | } 17 | 18 | aliceOrBob := Condition(Raw{ 19 | NodeType: NodeOr, 20 | Left: alice, 21 | Right: bob, 22 | }) 23 | 24 | query2 := Raw{ 25 | NodeType: NodeAnd, 26 | Left: aliceOrBob, 27 | Right: carl, 28 | } 29 | 30 | db := &Database{ 31 | "Alice": [][]byte{[]byte("blah")}, 32 | "Carl": [][]byte{[]byte("herp")}, 33 | } 34 | 35 | if query1.Ok(db) != false { 36 | t.Fatalf("Query #1 was wrong.") 37 | } 38 | 39 | if query2.Ok(db) != true { 40 | t.Fatalf("Query #2 was wrong.") 41 | } 42 | 43 | query1String := "Alice & Bob" 44 | query2String := "(Alice | Bob) & Carl" 45 | 46 | if query1.String() != query1String { 47 | t.Fatalf("Query #1 String was wrong; %v", query1.String()) 48 | } 49 | 50 | if query2.String() != query2String { 51 | t.Fatalf("Query #2 String was wrong; %v", query2.String()) 52 | } 53 | 54 | decQuery1, err := StringToRaw(query1String) 55 | if err != nil || decQuery1.String() != query1String { 56 | t.Fatalf("Query #1 decoded wrong: %v %v", decQuery1.String(), err) 57 | } 58 | 59 | decQuery2, err := StringToRaw(query2String) 60 | if err != nil || decQuery2.String() != query2String { 61 | t.Fatalf("Query #2 decoded wrong: %v %v", decQuery2.String(), err) 62 | } 63 | 64 | formattedQuery1String := "(2, Alice, Bob)" 65 | formattedQuery2String := "(2, (1, Alice, Bob), Carl)" 66 | 67 | if query1.Formatted().String() != formattedQuery1String { 68 | t.Fatalf("Query #1 formatted wrong: %v", query1.Formatted().String()) 69 | } 70 | 71 | if query2.Formatted().String() != formattedQuery2String { 72 | t.Fatalf("Query #2 formatted wrong: %v", query2.Formatted().String()) 73 | } 74 | } 75 | 76 | func TestOneCondition(t *testing.T) { 77 | _, err := StringToRaw("(Alice or Bob)") 78 | 79 | if err == nil { 80 | t.Fatalf("A predicate with only one condition should fail to parse!") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /elem.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | type Elem struct { 4 | Field 5 | 6 | e []byte 7 | } 8 | 9 | func (e Elem) Bytes() []byte { 10 | b := make([]byte, len(e.e)) 11 | copy(b, e.e) 12 | return b 13 | } 14 | 15 | // AddM mutates e into e+f. 16 | func (e Elem) AddM(f Elem) { 17 | for i := 0; i < e.Size(); i++ { 18 | e.e[i] ^= f.e[i] 19 | } 20 | } 21 | 22 | // Add returns e+f. 23 | func (e Elem) Add(f Elem) Elem { 24 | elem := e.Dup() 25 | elem.AddM(f) 26 | 27 | return elem 28 | } 29 | 30 | // Mul returns e*f. 31 | func (e Elem) Mul(f Elem) Elem { 32 | elem := e.Zero() 33 | 34 | for i := 0; i < e.BitSize(); i++ { // Foreach bit e_i in e: 35 | if e.getCoeff(i) == 1 { // where e_i equals 1: 36 | temp := f.Dup() // Multiply f * x^i mod M(x): 37 | 38 | for j := 0; j < i; j++ { // Multiply f by x mod M(x), i times. 39 | carry := temp.shift() 40 | 41 | if carry { 42 | for k := range e.Field { 43 | temp.e[k] ^= e.Field[k] 44 | } 45 | } 46 | } 47 | 48 | elem.AddM(temp) // Add f * x^i to the output 49 | } 50 | } 51 | 52 | return elem 53 | } 54 | 55 | // Exp returns e^i. 56 | func (e Elem) Exp(i int) Elem { 57 | elem := e.One() 58 | 59 | for j := 0; j < i; j++ { 60 | elem = elem.Mul(e) 61 | } 62 | 63 | return elem 64 | } 65 | 66 | // Invert returns the multiplicative inverse of e. 67 | func (e Elem) Invert() Elem { 68 | elem, temp := e.Dup(), e.Dup() 69 | 70 | rounds := e.BitSize() - 2 71 | for i := 0; i < rounds; i++ { 72 | temp = temp.Mul(temp) 73 | elem = elem.Mul(temp) 74 | } 75 | 76 | return elem.Mul(elem) 77 | } 78 | 79 | // getCoeff returns the ith coefficient of the field element: either 0 or 1. 80 | func (e Elem) getCoeff(i int) byte { 81 | return (e.e[i/8] >> (uint(i) % 8)) & 1 82 | } 83 | 84 | // shift multiplies e by 2 and returns true if there was overflow and false if there wasn't. 85 | func (e Elem) shift() bool { 86 | carry := false 87 | 88 | for i := 0; i < e.Size(); i++ { 89 | nextCarry := (e.e[i] >= 128) 90 | 91 | e.e[i] = (e.e[i] << 1) 92 | if carry { 93 | e.e[i]++ 94 | } 95 | carry = nextCarry 96 | } 97 | 98 | return carry 99 | } 100 | 101 | // Dup returns a duplicate of e. 102 | func (e Elem) Dup() Elem { 103 | return e.Field.Elem(e.e) 104 | } 105 | -------------------------------------------------------------------------------- /formatted_test.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFormatted(t *testing.T) { 8 | query1 := Formatted{ 9 | Min: 2, 10 | Conds: []Condition{ 11 | Name{"Alice", 0}, Name{"Bob", 0}, Name{"Carl", 0}, 12 | }, 13 | } 14 | 15 | query2 := Formatted{ 16 | Min: 3, 17 | Conds: []Condition{ 18 | Name{"Alice", 0}, Name{"Bob", 0}, Name{"Carl", 0}, 19 | }, 20 | } 21 | 22 | query3 := Formatted{ 23 | Min: 2, 24 | Conds: []Condition{ 25 | Formatted{ 26 | Min: 1, 27 | Conds: []Condition{ 28 | Name{"Alice", 0}, Name{"Bob", 0}, 29 | }, 30 | }, 31 | Name{"Carl", 0}, 32 | }, 33 | } 34 | 35 | query4 := Formatted{ 36 | Min: 2, 37 | Conds: []Condition{ 38 | Formatted{ 39 | Min: 1, 40 | Conds: []Condition{ 41 | Name{"Alice", 0}, Name{"Carl", 0}, 42 | }, 43 | }, 44 | Name{"Bob", 0}, 45 | }, 46 | } 47 | 48 | db := &Database{ 49 | "Alice": [][]byte{[]byte("blah")}, 50 | "Carl": [][]byte{[]byte("herp")}, 51 | } 52 | 53 | if query1.Ok(db) != true { 54 | t.Fatalf("Query #1 was wrong.") 55 | } 56 | 57 | if query2.Ok(db) != false { 58 | t.Fatalf("Query #2 was wrong.") 59 | } 60 | 61 | if query3.Ok(db) != true { 62 | t.Fatalf("Query #3 was wrong.") 63 | } 64 | 65 | if query4.Ok(db) != false { 66 | t.Fatalf("Query #4 was wrong.") 67 | } 68 | 69 | query1String := "(2, Alice, Bob, Carl)" 70 | query3String := "(2, (1, Alice, Bob), Carl)" 71 | 72 | if query1.String() != query1String { 73 | t.Fatalf("Query #1 String was wrong; %v", query1.String()) 74 | } 75 | 76 | if query3.String() != query3String { 77 | t.Fatalf("Query #3 String was wrong; %v", query3.String()) 78 | } 79 | 80 | decQuery1, err := StringToFormatted(query1String) 81 | if err != nil || decQuery1.String() != query1String { 82 | t.Fatalf("Query #1 decoded wrong: %v %v", decQuery1.String(), err) 83 | } 84 | 85 | decQuery3, err := StringToFormatted(query3String) 86 | if err != nil || decQuery3.String() != query3String { 87 | t.Fatalf("Query #3 decoded wrong: %v %v", decQuery3.String(), err) 88 | } 89 | } 90 | 91 | func TestBugs(t *testing.T) { 92 | bugs := []string{ 93 | "(),)", 94 | "((2, Alice, Bob), Bob, Carl)", 95 | } 96 | 97 | for _, bug := range bugs { 98 | _, err := StringToFormatted(bug) 99 | if err == nil { 100 | t.Fatalf("Didn't panic or error on a malformed string: %v", bug) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Monotone Span Programs 2 | ====================== 3 | 4 | - [Introduction](#monotone-span-programs) 5 | - [Types of Predicates](#types-of-predicates) 6 | - [Documentation](#documentation) 7 | - [User Databases](#user-databases) 8 | - [Building Predicates](#building-predicates) 9 | - [Splitting & Reconstructing Secrets](#splitting--reconstructing-secrets) 10 | 11 | A *Monotone Span Program* (or *MSP*) is a cryptographic technique for splitting 12 | a secret into several *shares* that are then distributed to *parties* or 13 | *users*. (Have you heard of [Shamir's Secret Sharing](http://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing)? It's like that.) 14 | 15 | Unlike Shamir's Secret Sharing, MSPs allow *arbitrary monotone access 16 | structures*. An access structure is just a boolean predicate on a set of users 17 | that tells us whether or not that set is allowed to recover the secret. A 18 | monotone access structure is the same thing, but with the invariant that adding 19 | a user to a set will never turn the predicate's output from `true` to 20 | `false`--negations or boolean `nots` are disallowed. 21 | 22 | **Example:** `(Alice or Bob) and Carl` is good, but `(Alice or Bob) and !Carl` 23 | is not because excluding people is rude. 24 | 25 | MSPs are fundamental and powerful primitives. They're well-suited for 26 | distributed commitments (DC), verifiable secret sharing (VSS) and multi-party 27 | computation (MPC). 28 | 29 | 30 | #### Types of Predicates 31 | 32 | An MSP itself is a type of predicate and the reader is probably familiar with 33 | raw boolean predicates like in the example above, but another important type is 34 | a *formatted boolean predicate*. 35 | 36 | Formatted boolean predicates are isomorphic to all MSPs and therefore all 37 | monotone raw boolean predicates. They're built by nesting threshold gates. 38 | 39 | **Example:** Let `(2, Alice, Bob, Carl)` denote that at least 2 members of the 40 | set `{Alice, Bob, Carl}` must be present to recover the secret. Then, 41 | `(2, (1, Alice, Bob), Carl)` is the formatted version of 42 | `(Alice or Bob) and Carl`. 43 | 44 | It is possible to convert between different types of predicates (and its one of 45 | the fundamental operations of splitting secrets with an MSP), but circuit 46 | minimization is a non-trivial and computationally complex problem. The code can 47 | do a small amount of compression, but the onus is on the user to design 48 | efficiently computable predicates. 49 | 50 | 51 | #### To Do 52 | 53 | 1. Anonymous secret generation / secret homomorphisms 54 | 2. Non-interactive verifiable secret sharing / distributed commitments 55 | 56 | 57 | Documentation 58 | ------------- 59 | 60 | ### User Databases 61 | 62 | ```go 63 | type UserDatabase interface { 64 | ValidUser(string) bool // Is this the name of an existing user? 65 | CanGetShare(string) bool // Can I get this user's share? 66 | GetShare(string) ([][]byte, error) // Retrieves a user's shares. 67 | } 68 | ``` 69 | 70 | User databases are an abstraction over the primitive name -> share map that 71 | hopefully offer a bit more flexibility in implementing secret sharing schemes. 72 | `CanGetShare(name)` should be faster and less binding than `GetShare(name)`. 73 | `CanGetShare(name)` may be called a large number of times, but `GetShare(name)` 74 | will be called the absolute minimum number of times possible. 75 | 76 | Depending on the predicate used, a name may be associated to multiple shares of 77 | a secret, hence `[][]byte` as opposed to one share (`[]byte`). 78 | 79 | For test/play purposes there's a cheaty implementation of `UserDatabase` in 80 | `msp_test.go` that just wraps the `map[string][][]byte` returned by 81 | `DistributeShares(...)` 82 | 83 | ### Building Predicates 84 | 85 | ```go 86 | type Raw struct { ... } 87 | 88 | func StringToRaw(string) (Raw, error) { ... } 89 | func (r Raw) String() string { .. .} 90 | func (r Raw) Formatted() Formatted { ... } 91 | 92 | 93 | type Formatted struct { ... } 94 | 95 | func StringToFormatted(string) (Formatted, error) 96 | func (f Formatted) String() string 97 | ``` 98 | 99 | Building predicates is extremely easy--just write it out in a string and have 100 | one of the package methods parse it. 101 | 102 | Raw predicates take the `&` (logical AND) and `|` (logical OR) operators, but 103 | are otherwise the same as discussed above. Formatted predicates are exactly the 104 | same as above--just nested threshold gates. 105 | 106 | ```go 107 | r1, _ := msp.StringToRaw("(Alice | Bob) & Carl") 108 | r2, _ := msp.StringToRaw("Alice & Bob & Carl") 109 | 110 | fmt.Printf("%v\n", r1.Formatted()) // (2, (1, Alice, Bob), Carl) 111 | fmt.Printf("%v\n", r2.Formatted()) // (3, Alice, Bob, Carl) 112 | ``` 113 | 114 | ### Splitting & Reconstructing Secrets 115 | 116 | ```go 117 | type MSP Formatted 118 | 119 | func (m MSP) DistributeShares(sec []byte, db *UserDatabase) (map[string][][]byte, error) {} 120 | func (m MSP) RecoverSecret(db *UserDatabase) ([]byte, error) {} 121 | ``` 122 | 123 | To switch from predicate-mode to secret-sharing-mode, just cast your formatted 124 | predicate into an MSP something like this: 125 | ```go 126 | predicate := msp.StringToFormatted("(3, Alice, Bob, Carl)") 127 | sss := msp.MSP(predicate) 128 | ``` 129 | 130 | Calling `DistributeShares` on it returns a map from a party's name to their set 131 | of shares which should be given to that party and integrated into the 132 | `UserDatabase` somehow. When you're ready to reconstruct the secret, you call 133 | `RecoverSecret`, which does some prodding about and hopefully gives you back 134 | what you put in. 135 | -------------------------------------------------------------------------------- /formatted.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type Formatted struct { // Represents threshold gate (also type of condition) 11 | Min int 12 | Conds []Condition 13 | } 14 | 15 | func StringToFormatted(f string) (out Formatted, err error) { 16 | // Automaton. Modification of Dijkstra's Two-Stack Algorithm for parsing 17 | // infix notation. Running time linear in the size of the predicate? 18 | // 19 | // Steps either to the next comma or the next unparenthesis. 20 | // ( -> Push new queue onto staging stack 21 | // value -> Push onto back of queue at top of staging stack. 22 | // ) -> Pop queue off top of staging stack, build threshold gate, 23 | // and push gate onto the back of the top queue. 24 | // 25 | // Staging stack is empty on initialization and should have exactly 1 built 26 | // threshold gate at the end of the string. 27 | if len(f) == 0 || f[0] != '(' || f[len(f)-1] != ')' { 28 | return out, errors.New("Invalid string: Needs to begin and end with parentheses.") 29 | } 30 | 31 | getNext := func(f string) (string, string) { // f -> (next, rest) 32 | f = strings.TrimSpace(f) 33 | 34 | if f[0] == '(' { 35 | return f[0:1], f[1:] 36 | } 37 | 38 | nextComma := strings.Index(f, ",") 39 | if f[0] == ')' { 40 | if nextComma == -1 { 41 | return f[0:1], "" 42 | } 43 | return f[0:1], f[nextComma+1:] 44 | } else if nextComma == -1 { 45 | return f[0 : len(f)-1], f[len(f)-1:] 46 | } 47 | 48 | nextUnParen := strings.Index(f, ")") 49 | if nextComma < nextUnParen { 50 | return strings.TrimSpace(f[0:nextComma]), f[nextComma+1:] 51 | } 52 | 53 | return strings.TrimSpace(f[0:nextUnParen]), f[nextUnParen:] 54 | } 55 | 56 | staging := [][]Condition{} 57 | indices := make(map[string]int, 0) 58 | 59 | var nxt string 60 | for len(f) > 0 { 61 | nxt, f = getNext(f) 62 | 63 | switch nxt { 64 | case "(": 65 | staging = append([][]Condition{[]Condition{}}, staging...) 66 | case ")": 67 | if len(staging) < 1 || len(staging[0]) < 1 { // Check 1 68 | return out, errors.New("Invalid string: Illegal close parenthesis.") 69 | } 70 | 71 | top := staging[0] // Legal because of check 1. 72 | staging = staging[1:] 73 | 74 | var min int 75 | minStr, ok := top[0].(Name) // Legal because of check 1. 76 | if !ok { 77 | return out, errors.New("Invalid string: First argument wasn't a threshold!") 78 | } 79 | 80 | min, err = strconv.Atoi(minStr.string) 81 | if err != nil { 82 | return 83 | } 84 | 85 | built := Formatted{ 86 | Min: min, 87 | Conds: []Condition{}, 88 | } 89 | 90 | for _, cond := range top[1:] { 91 | built.Conds = append(built.Conds, cond) 92 | } 93 | 94 | if len(staging) == 0 { // Check 2 95 | if len(f) == 0 { 96 | return built, nil 97 | } 98 | return out, errors.New("Invalid string: Can't parse anymore, but there's still data. Too many closing parentheses or too few opening parentheses?") 99 | } 100 | 101 | staging[0] = append(staging[0], built) // Legal because of check 2. 102 | 103 | default: 104 | if len(staging) < 1 { 105 | return out, errors.New("Invalid string: Name is not encapsulated!") 106 | } 107 | 108 | if _, there := indices[nxt]; !there { 109 | indices[nxt] = 0 110 | } 111 | 112 | staging[0] = append(staging[0], Name{nxt, indices[nxt]}) // Legal because of check above. 113 | indices[nxt]++ 114 | } 115 | } 116 | 117 | return out, errors.New("Invalid string: Not finished parsing, but out of data. Too many opening parentheses or too few closing parentheses?") 118 | } 119 | 120 | func (f Formatted) String() string { 121 | out := fmt.Sprintf("(%v", f.Min) 122 | 123 | for _, cond := range f.Conds { 124 | switch cond := cond.(type) { 125 | case Name: 126 | out += fmt.Sprintf(", %v", cond.string) 127 | case Formatted: 128 | out += fmt.Sprintf(", %v", cond.String()) 129 | } 130 | } 131 | 132 | return out + ")" 133 | } 134 | 135 | func (f Formatted) Ok(db UserDatabase) bool { 136 | // Goes through the smallest number of conditions possible to check if the 137 | // threshold gate returns true. Sometimes requires recursing down to check 138 | // nested threshold gates. 139 | rest := f.Min 140 | 141 | for _, cond := range f.Conds { 142 | if cond.Ok(db) { 143 | rest-- 144 | } 145 | 146 | if rest == 0 { 147 | return true 148 | } 149 | } 150 | 151 | return false 152 | } 153 | 154 | func (f *Formatted) Compress() { 155 | if f.Min == len(f.Conds) { 156 | // AND Compression: (n, ..., (m, ...), ...) = (n + m, ...) 157 | skip := 0 158 | for i, cond := range f.Conds { 159 | if skip > 0 { 160 | skip-- 161 | continue 162 | } 163 | 164 | switch cond := cond.(type) { 165 | case Formatted: 166 | cond.Compress() 167 | f.Conds[i] = cond 168 | 169 | if cond.Min == len(cond.Conds) { 170 | f.Min += cond.Min - 1 171 | f.Conds = append(f.Conds[0:i], 172 | append(cond.Conds, f.Conds[i+1:]...)...) 173 | skip = len(cond.Conds) - 1 174 | } 175 | } 176 | } 177 | } else if f.Min == 1 { 178 | // OR Compression: (1, ..., (1, ...), ...) = (1, ...) 179 | skip := 0 180 | for i, cond := range f.Conds { 181 | if skip > 0 { 182 | skip-- 183 | continue 184 | } 185 | 186 | switch cond := cond.(type) { 187 | case Formatted: 188 | cond.Compress() 189 | f.Conds[i] = cond 190 | 191 | if cond.Min == 1 { 192 | f.Conds = append(f.Conds[0:i], 193 | append(cond.Conds, f.Conds[i+1:]...)...) 194 | skip = len(cond.Conds) - 1 195 | } 196 | } 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /raw.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | type NodeType int // Types of node in the binary expression tree. 9 | 10 | const ( 11 | NodeAnd NodeType = iota 12 | NodeOr 13 | ) 14 | 15 | func (t NodeType) Type() NodeType { 16 | return t 17 | } 18 | 19 | type Layer struct { 20 | Conditions []Condition 21 | Operators []NodeType 22 | } 23 | 24 | type Raw struct { // Represents one node in the tree. 25 | NodeType 26 | 27 | Left Condition 28 | Right Condition 29 | } 30 | 31 | func StringToRaw(r string) (out Raw, err error) { 32 | // Automaton. Modification of Dijkstra's Two-Stack Algorithm for parsing 33 | // infix notation. Reads one long unbroken expression (several operators and 34 | // operands with no parentheses) at a time and parses it into a binary 35 | // expression tree (giving AND operators precedence). Running time linear in 36 | // the size of the predicate? 37 | // 38 | // Steps to the next (un)parenthesis. 39 | // ( -> Push new queue onto staging stack 40 | // value -> Push onto back of queue at top of staging stack. 41 | // ) -> Pop queue off top of staging stack, build BET, and push tree 42 | // onto the back of the top queue. 43 | // 44 | // To build the binary expression tree, for each type of operation we iterate 45 | // through the (Condition, operator) lists compacting where that operation 46 | // occurs into tree nodes. 47 | // 48 | // Staging stack is empty on initialization and should have exactly 1 node 49 | // (the root node) at the end of the string. 50 | r = "(" + r + ")" 51 | 52 | min := func(a, b, c int) int { // Return smallest non-negative argument. 53 | if a > b { // Sort {a, b, c} 54 | a, b = b, a 55 | } 56 | if b > c { 57 | b, c = c, b 58 | } 59 | if a > b { 60 | a, b = b, a 61 | } 62 | 63 | if a != -1 { 64 | return a 65 | } else if b != -1 { 66 | return b 67 | } else { 68 | return c 69 | } 70 | } 71 | 72 | getNext := func(r string) (string, string) { // r -> (next, rest) 73 | r = strings.TrimSpace(r) 74 | 75 | if r[0] == '(' || r[0] == ')' || r[0] == '&' || r[0] == '|' { 76 | return r[0:1], r[1:] 77 | } 78 | 79 | nextOper := min( 80 | strings.Index(r, "&"), 81 | strings.Index(r, "|"), 82 | strings.Index(r, ")"), 83 | ) 84 | 85 | if nextOper == -1 { 86 | return r, "" 87 | } 88 | return strings.TrimSpace(r[0:nextOper]), r[nextOper:] 89 | } 90 | 91 | staging := []Layer{} // Stack of (Condition list, operator list) 92 | indices := make(map[string]int, 0) 93 | 94 | var nxt string 95 | for len(r) > 0 { 96 | nxt, r = getNext(r) 97 | 98 | switch nxt { 99 | case "(": 100 | staging = append([]Layer{Layer{}}, staging...) 101 | case ")": 102 | if len(staging) < 1 { // Check 1 103 | return out, errors.New("Invalid string: Illegal close parenthesis.") 104 | } 105 | 106 | top := staging[0] // Legal because of check 1. 107 | staging = staging[1:] 108 | 109 | if len(top.Conditions) != (len(top.Operators) + 1) { // Check 2 110 | return out, errors.New("Invalid string: There needs to be an operator (& or |) for every pair of operands.") 111 | } 112 | 113 | for typ := NodeAnd; typ <= NodeOr; typ++ { 114 | i := 0 115 | for i < len(top.Operators) { 116 | oper := top.Operators[i] // Legal because for loop condition. 117 | 118 | // Copy left and right out of slice and THEN give a pointer for them! 119 | left, right := top.Conditions[i], top.Conditions[i+1] // Legal because of check 2. 120 | if oper == typ { 121 | built := Raw{typ, left, right} 122 | 123 | top.Conditions = append( 124 | top.Conditions[:i], 125 | append([]Condition{built}, top.Conditions[i+2:]...)..., 126 | ) 127 | 128 | top.Operators = append(top.Operators[:i], top.Operators[i+1:]...) // Legal because for loop condition. 129 | } else { 130 | i++ 131 | } 132 | } 133 | } 134 | 135 | if len(top.Conditions) != 1 || len(top.Operators) != 0 { // Check 3 136 | return out, errors.New("Invalid string: Couldn't evaluate all of the operators.") 137 | } 138 | 139 | if len(staging) == 0 { // Check 4 140 | if len(r) == 0 { 141 | res, ok := top.Conditions[0].(Raw) // Legal because of check 3. 142 | if !ok { 143 | return out, errors.New("Invalid string: Only one condition was found?") 144 | } 145 | return res, nil 146 | } 147 | return out, errors.New("Invalid string: Can't parse anymore, but there's still data. Too many closing parentheses or too few opening parentheses?") 148 | } 149 | staging[0].Conditions = append(staging[0].Conditions, top.Conditions[0]) // Legal because of checks 3 and 4. 150 | 151 | case "&": 152 | // Legal because first operation is to add an empty layer to the stack. 153 | // If the stack is ever empty again, the function tries to return or error. 154 | staging[0].Operators = append(staging[0].Operators, NodeAnd) 155 | case "|": 156 | staging[0].Operators = append(staging[0].Operators, NodeOr) // Legal for same reason as case &. 157 | default: 158 | if _, there := indices[nxt]; !there { 159 | indices[nxt] = 0 160 | } 161 | 162 | staging[0].Conditions = append(staging[0].Conditions, Name{nxt, indices[nxt]}) // Legal for same reason as case &. 163 | indices[nxt]++ 164 | } 165 | } 166 | 167 | return out, errors.New("Invalid string: Not finished parsing, but out of data. Too many opening parentheses or too few closing parentheses?") 168 | } 169 | 170 | func (r Raw) String() string { 171 | out := "" 172 | 173 | switch left := r.Left.(type) { 174 | case Name: 175 | out += left.string 176 | case Raw: 177 | out += "(" + left.String() + ")" 178 | } 179 | 180 | if r.Type() == NodeAnd { 181 | out += " & " 182 | } else { 183 | out += " | " 184 | } 185 | 186 | switch right := r.Right.(type) { 187 | case Name: 188 | out += right.string 189 | case Raw: 190 | out += "(" + right.String() + ")" 191 | } 192 | 193 | return out 194 | } 195 | 196 | func (r Raw) Formatted() (out Formatted) { 197 | // Recursively maps a raw predicate to a formatted predicate by mapping AND 198 | // gates to (2, A, B) treshold gates and OR gates to (1, A, B) gates. 199 | if r.Type() == NodeAnd { 200 | out.Min = 2 201 | } else { 202 | out.Min = 1 203 | } 204 | 205 | switch left := r.Left.(type) { 206 | case Name: 207 | out.Conds = []Condition{left} 208 | case Raw: 209 | out.Conds = []Condition{left.Formatted()} 210 | } 211 | 212 | switch right := r.Right.(type) { 213 | case Name: 214 | out.Conds = append(out.Conds, right) 215 | case Raw: 216 | out.Conds = append(out.Conds, right.Formatted()) 217 | } 218 | 219 | out.Compress() // Small amount of predicate compression. 220 | return 221 | } 222 | 223 | func (r Raw) Ok(db UserDatabase) bool { 224 | if r.Type() == NodeAnd { 225 | return r.Left.Ok(db) && r.Right.Ok(db) 226 | } else { 227 | return r.Left.Ok(db) || r.Right.Ok(db) 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /msp.go: -------------------------------------------------------------------------------- 1 | package msp 2 | 3 | import ( 4 | "container/heap" 5 | "crypto/rand" 6 | "errors" 7 | "strings" 8 | ) 9 | 10 | // A UserDatabase is an abstraction over the name -> share map returned by the 11 | // secret splitter that allows an application to only decrypt or request shares 12 | // when needed, rather than re-build a partial map of known data. 13 | type UserDatabase interface { 14 | ValidUser(name string) bool 15 | CanGetShare(name string) bool 16 | GetShare(name string) ([][]byte, error) 17 | } 18 | 19 | type Condition interface { // Represents one condition in a predicate 20 | Ok(UserDatabase) bool 21 | } 22 | 23 | type Name struct { // Type of condition 24 | string 25 | index int 26 | } 27 | 28 | func (n Name) Ok(db UserDatabase) bool { 29 | return db.CanGetShare(n.string) 30 | } 31 | 32 | type TraceElem struct { 33 | loc int 34 | names []string 35 | trace []string 36 | } 37 | 38 | type TraceSlice []TraceElem 39 | 40 | func (ts TraceSlice) Len() int { return len(ts) } 41 | func (ts TraceSlice) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] } 42 | 43 | func (ts TraceSlice) Less(i, j int) bool { 44 | return len(ts[i].trace) > len(ts[j].trace) 45 | } 46 | 47 | func (ts *TraceSlice) Push(te interface{}) { *ts = append(*ts, te.(TraceElem)) } 48 | func (ts *TraceSlice) Pop() interface{} { 49 | old := *ts 50 | n := len(old) 51 | 52 | *ts = old[0 : n-1] 53 | out := old[n-1] 54 | 55 | return out 56 | } 57 | 58 | // Compact takes a trace slice and merges all of its fields. 59 | // 60 | // index: Union of all locations in the slice. 61 | // names: Union of all names in the slice. 62 | // trace: Union of all the traces in the slice. 63 | func (ts TraceSlice) Compact() (index []int, names []string, trace []string) { 64 | for _, te := range ts { 65 | index = append(index, te.loc) 66 | names = append(names, te.names...) 67 | trace = append(trace, te.trace...) 68 | } 69 | 70 | // This is a QuickSort related algorithm. It makes all the names in the trace unique so we don't double-count people. 71 | // 72 | // Invariant: There are no duplicates in trace[0:ptr] 73 | // Algorithm: Advance ptr by 1 and enforce the invariant. 74 | ptr, cutoff := 0, len(trace) 75 | 76 | TopLoop: 77 | for ptr < cutoff { // Choose the next un-checked element of the slice. 78 | for i := 0; i < ptr; i++ { // Compare it to all elements before it. 79 | if trace[i] == trace[ptr] { // If we find a duplicate... 80 | trace[ptr], trace[cutoff-1] = trace[cutoff-1], trace[ptr] // Push the dup to the end of the surviving slice. 81 | cutoff-- // Mark it for removal. 82 | 83 | continue TopLoop // Because trace[ptr] has been mutated, try to verify the invariant again w/o advancing ptr. 84 | } 85 | } 86 | 87 | ptr++ // There are no duplicates; move the ptr forward and start again. 88 | } 89 | trace = trace[0:cutoff] 90 | 91 | return 92 | } 93 | 94 | type MSP Formatted 95 | 96 | func StringToMSP(pred string) (m MSP, err error) { 97 | var f Formatted 98 | 99 | if -1 == strings.Index(pred, ",") { 100 | var r Raw 101 | r, err = StringToRaw(pred) 102 | if err != nil { 103 | return 104 | } 105 | 106 | f = r.Formatted() 107 | } else { 108 | f, err = StringToFormatted(pred) 109 | if err != nil { 110 | return 111 | } 112 | } 113 | 114 | return MSP(f), nil 115 | } 116 | 117 | // DerivePath returns the cheapest way to satisfy the MSP (the one with the minimal number of delegations). 118 | // 119 | // ok: True if the MSP can be satisfied with current delegations; false if not. 120 | // names: The names in the top-level threshold gate that need to be delegated. 121 | // locs: The index in the treshold gate for each name. 122 | // trace: All names that must be delegated for for this gate to be satisfied. 123 | func (m MSP) DerivePath(db UserDatabase) (ok bool, names []string, locs []int, trace []string) { 124 | ts := &TraceSlice{} 125 | 126 | for i, cond := range m.Conds { 127 | switch cond := cond.(type) { 128 | case Name: 129 | if db.CanGetShare(cond.string) { 130 | heap.Push(ts, TraceElem{ 131 | i, 132 | []string{cond.string}, 133 | []string{cond.string}, 134 | }) 135 | } 136 | 137 | case Formatted: 138 | sok, _, _, strace := MSP(cond).DerivePath(db) 139 | if sok { 140 | heap.Push(ts, TraceElem{i, []string{}, strace}) 141 | } 142 | } 143 | 144 | if (*ts).Len() > m.Min { // If we can otherwise satisfy the threshold gate 145 | heap.Pop(ts) // Drop the TraceElem with the heaviest trace (the one that requires the most delegations). 146 | } 147 | } 148 | 149 | ok = (*ts).Len() >= m.Min 150 | locs, names, trace = ts.Compact() 151 | return 152 | } 153 | 154 | // DistributeShares takes as input a secret and a user database and returns secret shares according to access structure 155 | // described by the MSP. 156 | func (m MSP) DistributeShares(sec []byte, db UserDatabase) (map[string][][]byte, error) { 157 | out := make(map[string][][]byte) 158 | 159 | field, ok := Fields[len(sec)] 160 | if !ok { 161 | return nil, errors.New("No field for secret length") 162 | } 163 | 164 | // Generate a Vandermonde matrix. 165 | height, width := len(m.Conds), m.Min 166 | M := field.Matrix(height, width) 167 | 168 | for i := range M.m { 169 | for j := range M.m[i].r { 170 | M.m[i].r[j].e[0] = byte(i + 1) 171 | M.m[i].r[j] = M.m[i].r[j].Exp(j) 172 | } 173 | } 174 | 175 | // Convert secret vector. 176 | s, buf := field.Row(width), make([]byte, len(sec)) 177 | for i := range s.r { 178 | rand.Read(buf) 179 | if i == 0 { 180 | copy(buf, sec) 181 | } 182 | 183 | s.r[i] = field.Elem(buf) 184 | } 185 | 186 | // Calculate shares. 187 | shares := M.Mul(s) 188 | 189 | // Distribute the shares. 190 | for i, cond := range m.Conds { 191 | share := shares.r[i] 192 | 193 | switch cond := cond.(type) { 194 | case Name: 195 | name := cond.string 196 | if !db.ValidUser(name) { 197 | return nil, errors.New("Unknown user in predicate.") 198 | } 199 | 200 | out[name] = append(out[name], share.Bytes()) 201 | case Formatted: 202 | below := MSP(cond) 203 | subOut, err := below.DistributeShares(share.Bytes(), db) 204 | if err != nil { 205 | return out, err 206 | } 207 | 208 | for name, shares := range subOut { 209 | out[name] = append(out[name], shares...) 210 | } 211 | } 212 | } 213 | 214 | return out, nil 215 | } 216 | 217 | // RecoverSecret takes a user database storing secret shares as input and returns the original secret. 218 | func (m MSP) RecoverSecret(db UserDatabase) ([]byte, error) { 219 | cache := make(map[string][][]byte, 0) // Caches un-used shares for a user. 220 | return m.recoverSecret(db, cache) 221 | } 222 | 223 | func (m MSP) recoverSecret(db UserDatabase, cache map[string][][]byte) ([]byte, error) { 224 | var ( 225 | index = []int{} // Indexes where given shares were in the matrix. 226 | shares = []Elem{} // Contains shares that will be used in reconstruction. 227 | ) 228 | 229 | ok, names, locs, _ := m.DerivePath(db) 230 | if !ok { 231 | return nil, errors.New("Not enough shares to recover.") 232 | } 233 | 234 | var field Field 235 | for _, name := range names { 236 | if _, cached := cache[name]; !cached { 237 | out, err := db.GetShare(name) 238 | if err != nil { 239 | return nil, err 240 | } 241 | 242 | cache[name] = out 243 | 244 | var ok bool 245 | if field == nil { 246 | field, ok = Fields[len(out[0])] 247 | if !ok { 248 | return nil, errors.New("No field for secret length") 249 | } 250 | } 251 | } 252 | } 253 | 254 | for _, loc := range locs { 255 | gate := m.Conds[loc] 256 | index = append(index, loc+1) 257 | 258 | switch gate := gate.(type) { 259 | case Name: 260 | if len(cache[gate.string]) <= gate.index { 261 | return nil, errors.New("Predicate / database mismatch!") 262 | } 263 | 264 | shares = append(shares, field.Elem(cache[gate.string][gate.index])) 265 | 266 | case Formatted: 267 | share, err := MSP(gate).recoverSecret(db, cache) 268 | if err != nil { 269 | return nil, err 270 | } 271 | 272 | shares = append(shares, field.Elem(share)) 273 | } 274 | } 275 | 276 | // Generate the Vandermonde matrix specific to whichever users' shares we're using. 277 | MSub := field.Matrix(m.Min, m.Min) 278 | 279 | for i := range MSub.m { 280 | for j := range MSub.m[i].r { 281 | MSub.m[i].r[j].e[0] = byte(index[i]) 282 | MSub.m[i].r[j] = MSub.m[i].r[j].Exp(j) 283 | } 284 | } 285 | 286 | // Calculate the reconstruction vector and use it to recover the secret. 287 | r, ok := MSub.Recovery() 288 | if !ok { 289 | return nil, errors.New("Unable to find a reconstruction vector!") 290 | } 291 | 292 | // Compute dot product of the shares vector and the reconstruction vector to 293 | // recover the secret. 294 | s := Row{Field: field, r: shares}.DotProduct(r) 295 | 296 | return s.Bytes(), nil 297 | } 298 | --------------------------------------------------------------------------------