Vendored in Bren2010/MSP

This commit is contained in:
Brendan McMillion
2015-11-11 15:20:46 -08:00
committed by Brendan McMillion
parent becabb40e9
commit e652300f43
8 changed files with 1173 additions and 9 deletions
+9 -9
View File
@@ -15,8 +15,8 @@ import (
"sort"
"strconv"
"github.com/Bren2010/msp"
"github.com/cloudflare/redoctober/keycache"
"github.com/cloudflare/redoctober/msp"
"github.com/cloudflare/redoctober/padding"
"github.com/cloudflare/redoctober/passvault"
"github.com/cloudflare/redoctober/symcrypt"
@@ -52,11 +52,11 @@ type AccessStructure struct {
// Implements msp.UserDatabase
type UserDatabase struct {
records *passvault.Records
cache *keycache.Cache
cache *keycache.Cache
user string
labels []string
keySet map[string]SingleWrappedKey
user string
labels []string
keySet map[string]SingleWrappedKey
shareSet map[string][][]byte
}
@@ -433,10 +433,10 @@ func (encrypted *EncryptedData) unwrapKey(cache *keycache.Cache, user string) (u
}
db := msp.UserDatabase(UserDatabase{
cache: cache,
user: user,
labels: encrypted.Labels,
keySet: encrypted.KeySetRSA,
cache: cache,
user: user,
labels: encrypted.Labels,
keySet: encrypted.KeySetRSA,
shareSet: encrypted.ShareSet,
})
unwrappedKey, err = sss.RecoverSecret(msp.Modulus(127), &db)
+153
View File
@@ -0,0 +1,153 @@
Monotone Span Programs
======================
- [Introduction](#monotone-span-programs)
- [Types of Predicates](#types-of-predicates)
- [Documentation](#documentation)
- [User Databases](#user-databases)
- [Building Predicates](#building-predicates)
- [Splitting & Reconstructing Secrets](#splitting--reconstructing-secrets)
A *Monotone Span Program* (or *MSP*) is a cryptographic technique for splitting
a secret into several *shares* that are then distributed to *parties* or
*users*. (Have you heard of [Shamir's Secret Sharing](http://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing)? It's like that.)
Unlike Shamir's Secret Sharing, MSPs allow *arbitrary monotone access
structures*. An access structure is just a boolean predicate on a set of users
that tells us whether or not that set is allowed to recover the secret. A
monotone access structure is the same thing, but with the invariant that adding
a user to a set will never turn the predicate's output from `true` to
`false`--negations or boolean `nots` are disallowed.
**Example:** `(Alice or Bob) and Carl` is good, but `(Alice or Bob) and !Carl`
is not because excluding people is rude.
MSPs are fundamental and powerful primitives. They're well-suited for
distributed commitments (DC), verifiable secret sharing (VSS) and multi-party
computation (MPC).
#### Types of Predicates
An MSP itself is a type of predicate and the reader is probably familiar with
raw boolean predicates like in the example above, but another important type is
a *formatted boolean predicate*.
Formatted boolean predicates are isomorphic to all MSPs and therefore all
monotone raw boolean predicates. They're built by nesting threshold gates.
**Example:** Let `(2, Alice, Bob, Carl)` denote that at least 2 members of the
set `{Alice, Bob, Carl}` must be present to recover the secret. Then,
`(2, (1, Alice, Bob), Carl)` is the formatted version of
`(Alice or Bob) and Carl`.
It is possible to convert between different types of predicates (and its one of
the fundamental operations of splitting secrets with an MSP), but circuit
minimization is a non-trivial and computationally complex problem. The code can
do a small amount of compression, but the onus is on the user to design
efficiently computable predicates.
#### To Do
1. Anonymous secret generation / secret homomorphisms
2. Non-interactive verifiable secret sharing / distributed commitments
Documentation
-------------
### User Databases
```go
type UserDatabase interface {
ValidUser(string) bool // Is this the name of an existing user?
CanGetShare(string) bool // Can I get this user's share?
GetShare(string) ([][]byte, error) // Retrieves a user's shares.
}
```
User databases are an abstraction over the primitive name -> share map that
hopefully offer a bit more flexibility in implementing secret sharing schemes.
`CanGetShare(name)` should be faster and less binding than `GetShare(name)`.
`CanGetShare(name)` may be called a large number of times, but `GetShare(name)`
will be called the absolute minimum number of times possible.
Depending on the predicate used, a name may be associated to multiple shares of
a secret, hence `[][]byte` as opposed to one share (`[]byte`).
For test/play purposes there's a cheaty implementation of `UserDatabase` in
`msp_test.go` that just wraps the `map[string][][]byte` returned by
`DistributeShares(...)`
### Building Predicates
```go
type Raw struct { ... }
func StringToRaw(string) (Raw, error) { ... }
func (r Raw) String() string { .. .}
func (r Raw) Formatted() Formatted { ... }
type Formatted struct { ... }
func StringToFormatted(string) (Formatted, error)
func (f Formatted) String() string
```
Building predicates is extremely easy--just write it out in a string and have
one of the package methods parse it.
Raw predicates take the `&` (logical AND) and `|` (logical OR) operators, but
are otherwise the same as discussed above. Formatted predicates are exactly the
same as above--just nested threshold gates.
```go
r1, _ := msp.StringToRaw("(Alice | Bob) & Carl")
r2, _ := msp.StringToRaw("Alice & Bob & Carl")
fmt.Printf("%v\n", r1.Formatted()) // (2, (1, Alice, Bob), Carl)
fmt.Printf("%v\n", r2.Formatted()) // (3, Alice, Bob, Carl)
```
### Splitting & Reconstructing Secrets
```go
type MSP Formatted
func Modulus(n int) *big.Int {}
func (m MSP) DistributeShares(sec []byte, modulus *big.Int, db *UserDatabase) (map[string][][]byte, error) {}
func (m MSP) RecoverSecret(modulus *big.Int, db *UserDatabase) ([]byte, error) {}
```
To switch from predicate-mode to secret-sharing-mode, just cast your formatted
predicate into an MSP something like this:
```go
predicate := msp.StringToFormatted("(3, Alice, Bob, Carl)")
sss := msp.MSP(predicate)
```
Calling `DistributeShares` on it returns a map from a party's name to their set
of shares which should be given to that party and integrated into the
`UserDatabase` somehow. When you're ready to reconstruct the secret, you call
`RecoverSecret`, which does some prodding about and hopefully gives you back
what you put in.
The modulus determines the size of the secret shares. It must be prime, larger
than the secret, and larger than n<sup>k</sup> where `n` is the number of
parties and `k` is the depth of the circuit. (For each path from the root to a
gate with only leaf nodes, sum the thresholds of all the gates along that path.
`k` is the maximum of these values--a safe overestimation is to sum the
thresholds of all gates in the circuit) Because this form of secret sharing
is *information-theoretically secure*, as long as the modulus satisfies those
requirements, it can be as small or as large as you'd like (there's no
bonus/reduction in security). An excessively small modulus is restrictive, but
saves a *lot* of bandwidth. However, there's no sensible reason for a modulus
over about 256 bits--by that point, it's easier to generate a random AES key,
encrypt your secret with *that*, and distribute shares of the decryption key
instead.
For convenience, the `Modulus` function has some hard coded moduli that are
useful. The choices are currently: `127`, `224`, and `256` for a 127-bit,
224-bit, or 256-bit modulus, respectively.
+189
View File
@@ -0,0 +1,189 @@
package msp
import (
"container/list"
"errors"
"fmt"
"strconv"
"strings"
)
type Formatted struct { // Represents threshold gate (also type of condition)
Min int
Conds []Condition
}
func StringToFormatted(f string) (out Formatted, err error) {
// Automaton. Modification of Dijkstra's Two-Stack Algorithm for parsing
// infix notation. Running time linear in the size of the predicate?
//
// Steps either to the next comma or the next unparenthesis.
// ( -> Push new queue onto staging stack
// value -> Push onto back of queue at top of staging stack.
// ) -> Pop queue off top of staging stack, build threshold gate,
// and push gate onto the back of the top queue.
//
// Staging stack is empty on initialization and should have exactly 1 built
// threshold gate at the end of the string.
if f[0] != '(' || f[len(f)-1] != ')' {
return out, errors.New("Invalid string--wrong format.")
}
getNext := func(f string) (string, string) { // f -> (next, rest)
f = strings.TrimSpace(f)
if f[0] == '(' {
return f[0:1], f[1:]
}
nextComma := strings.Index(f, ",")
if f[0] == ')' {
if nextComma == -1 {
return f[0:1], ""
}
return f[0:1], f[nextComma+1:]
} else if nextComma == -1 {
return f[0 : len(f)-1], f[len(f)-1:]
}
nextUnParen := strings.Index(f, ")")
if nextComma < nextUnParen {
return strings.TrimSpace(f[0:nextComma]), f[nextComma+1:]
}
return strings.TrimSpace(f[0:nextUnParen]), f[nextUnParen:]
}
staging := list.New()
indices := make(map[string]int, 0)
var nxt string
for len(f) > 0 {
nxt, f = getNext(f)
switch nxt {
case "(":
staging.PushFront(list.New())
case ")":
top := staging.Remove(staging.Front()).(*list.List)
var min int
minStr := top.Front()
min, err = strconv.Atoi(minStr.Value.(String).string)
if err != nil {
return
}
built := Formatted{
Min: min,
Conds: []Condition{},
}
for cond := minStr.Next(); cond != nil; cond = cond.Next() {
built.Conds = append(built.Conds, cond.Value.(Condition))
}
if staging.Len() == 0 {
if len(f) == 0 {
return built, nil
}
return built, errors.New("Invalid string--terminated early.")
}
staging.Front().Value.(*list.List).PushBack(built)
default:
if _, there := indices[nxt]; !there {
indices[nxt] = 0
}
staging.Front().Value.(*list.List).PushBack(String{nxt, indices[nxt]})
indices[nxt]++
}
}
return out, errors.New("Invalid string--never terminated.")
}
func (f Formatted) String() string {
out := fmt.Sprintf("(%v", f.Min)
for _, cond := range f.Conds {
switch cond.(type) {
case String:
out += fmt.Sprintf(", %v", cond.(String).string)
case Formatted:
out += fmt.Sprintf(", %v", (cond.(Formatted)).String())
}
}
return out + ")"
}
func (f Formatted) Ok(db *UserDatabase) bool {
// Goes through the smallest number of conditions possible to check if the
// threshold gate returns true. Sometimes requires recursing down to check
// nested threshold gates.
rest := f.Min
for _, cond := range f.Conds {
if cond.Ok(db) {
rest--
}
if rest == 0 {
return true
}
}
return false
}
func (f *Formatted) Compress() {
if f.Min == len(f.Conds) {
// AND Compression: (n, ..., (m, ...), ...) = (n + m, ...)
skip := 0
for i, cond := range f.Conds {
if skip > 0 {
skip--
continue
}
switch cond.(type) {
case Formatted:
cond := cond.(Formatted)
cond.Compress()
f.Conds[i] = cond
if cond.Min == len(cond.Conds) {
f.Min += cond.Min - 1
f.Conds = append(f.Conds[0:i],
append(cond.Conds, f.Conds[i+1:]...)...)
skip = len(cond.Conds) - 1
}
}
}
} else if f.Min == 1 {
// OR Compression: (1, ..., (1, ...), ...) = (1, ...)
skip := 0
for i, cond := range f.Conds {
if skip > 0 {
skip--
continue
}
switch cond.(type) {
case Formatted:
cond := cond.(Formatted)
cond.Compress()
f.Conds[i] = cond
if cond.Min == 1 {
f.Conds = append(f.Conds[0:i],
append(cond.Conds, f.Conds[i+1:]...)...)
skip = len(cond.Conds) - 1
}
}
}
}
}
+89
View File
@@ -0,0 +1,89 @@
package msp
import (
"testing"
)
func TestFormatted(t *testing.T) {
query1 := Formatted{
Min: 2,
Conds: []Condition{
String{"Alice", 0}, String{"Bob", 0}, String{"Carl", 0},
},
}
query2 := Formatted{
Min: 3,
Conds: []Condition{
String{"Alice", 0}, String{"Bob", 0}, String{"Carl", 0},
},
}
query3 := Formatted{
Min: 2,
Conds: []Condition{
Formatted{
Min: 1,
Conds: []Condition{
String{"Alice", 0}, String{"Bob", 0},
},
},
String{"Carl", 0},
},
}
query4 := Formatted{
Min: 2,
Conds: []Condition{
Formatted{
Min: 1,
Conds: []Condition{
String{"Alice", 0}, String{"Carl", 0},
},
},
String{"Bob", 0},
},
}
db := UserDatabase(Database(map[string][][]byte{
"Alice": [][]byte{[]byte("blah")},
"Carl": [][]byte{[]byte("herp")},
}))
if query1.Ok(&db) != true {
t.Fatalf("Query #1 was wrong.")
}
if query2.Ok(&db) != false {
t.Fatalf("Query #2 was wrong.")
}
if query3.Ok(&db) != true {
t.Fatalf("Query #3 was wrong.")
}
if query4.Ok(&db) != false {
t.Fatalf("Query #4 was wrong.")
}
query1String := "(2, Alice, Bob, Carl)"
query3String := "(2, (1, Alice, Bob), Carl)"
if query1.String() != query1String {
t.Fatalf("Query #1 String was wrong; %v", query1.String())
}
if query3.String() != query3String {
t.Fatalf("Query #3 String was wrong; %v", query3.String())
}
decQuery1, err := StringToFormatted(query1String)
if err != nil || decQuery1.String() != query1String {
t.Fatalf("Query #1 decoded wrong: %v %v", decQuery1.String(), err)
}
decQuery3, err := StringToFormatted(query3String)
if err != nil || decQuery3.String() != query3String {
t.Fatalf("Query #3 decoded wrong: %v %v", decQuery3.String(), err)
}
}
+369
View File
@@ -0,0 +1,369 @@
package msp
import (
"container/heap"
"crypto/rand"
"errors"
"math/big"
"strings"
)
// A UserDatabase is an abstraction over the name -> share map returned by the
// secret splitter that allows an application to only decrypt or request shares
// when needed, rather than re-build a partial map of known data.
type UserDatabase interface {
ValidUser(name string) bool
CanGetShare(string) bool
GetShare(string) ([][]byte, error)
}
type Condition interface { // Represents one condition in a predicate
Ok(*UserDatabase) bool
}
type String struct { // Type of condition
string
index int
}
func (s String) Ok(db *UserDatabase) bool {
return (*db).CanGetShare(s.string)
}
type TraceElem struct {
loc int
names []string
trace []string
}
type TraceSlice []TraceElem
func (ts TraceSlice) Len() int { return len(ts) }
func (ts TraceSlice) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] }
func (ts TraceSlice) Less(i, j int) bool {
return len(ts[i].trace) < len(ts[j].trace)
}
func (ts *TraceSlice) Push(te interface{}) { *ts = append(*ts, te.(TraceElem)) }
func (ts *TraceSlice) Pop() interface{} {
out := (*ts)[0]
*ts = (*ts)[1 : len(*ts)-1]
return out
}
func (ts TraceSlice) Compact() (index []int, names []string, trace []string) {
for _, te := range ts {
index = append(index, te.loc)
names = append(names, te.names...)
trace = append(trace, te.trace...)
}
ptr, cutoff := 0, len(trace)
TopLoop:
for ptr < cutoff {
for i := 0; i < ptr; i++ {
if trace[i] == trace[ptr] {
trace[ptr], trace[cutoff-1] = trace[cutoff-1], trace[ptr]
cutoff--
continue TopLoop
}
}
ptr++
}
trace = trace[0:cutoff]
return
}
type MSP Formatted
func Modulus(n int) (modulus *big.Int) {
switch n {
case 256:
modulus = big.NewInt(1) // 2^256 - 2^224 + 2^192 + 2^96 - 1
modulus.Lsh(modulus, 256)
modulus.Sub(modulus, big.NewInt(0).Lsh(big.NewInt(1), 224))
modulus.Add(modulus, big.NewInt(0).Lsh(big.NewInt(1), 192))
modulus.Add(modulus, big.NewInt(0).Lsh(big.NewInt(1), 96))
modulus.Sub(modulus, big.NewInt(1))
case 224:
modulus = big.NewInt(1) // 2^224 - 2^96 + 1
modulus.Lsh(modulus, 224)
modulus.Sub(modulus, big.NewInt(0).Lsh(big.NewInt(1), 96))
modulus.Add(modulus, big.NewInt(1))
default: // Silent fail.
modulus = big.NewInt(1) // 2^127 - 1
modulus.Lsh(modulus, 127)
modulus.Sub(modulus, big.NewInt(1))
}
return
}
func StringToMSP(pred string) (m MSP, err error) {
var f Formatted
if -1 == strings.Index(pred, ",") {
var r Raw
r, err = StringToRaw(pred)
if err != nil {
return
}
f = r.Formatted()
} else {
f, err = StringToFormatted(pred)
if err != nil {
return
}
}
return MSP(f), nil
}
func (m MSP) DerivePath(db *UserDatabase) (ok bool, names []string, locs []int, trace []string) {
ts := &TraceSlice{}
for i, cond := range m.Conds {
switch cond.(type) {
case String:
if (*db).CanGetShare(cond.(String).string) {
heap.Push(ts, TraceElem{
i,
[]string{cond.(String).string},
[]string{cond.(String).string},
})
}
case Formatted:
sok, _, _, strace := MSP(cond.(Formatted)).DerivePath(db)
if !sok {
continue
}
heap.Push(ts, TraceElem{i, []string{}, strace})
}
if (*ts).Len() > m.Min {
*ts = (*ts)[0:m.Min]
}
}
ok = (*ts).Len() >= m.Min
locs, names, trace = ts.Compact()
return
}
func (m MSP) DistributeShares(sec []byte, modulus *big.Int, db *UserDatabase) (map[string][][]byte, error) {
out := make(map[string][][]byte)
// Math to distribute shares.
secInt := big.NewInt(0).SetBytes(sec) // Convert secret to number.
secInt.Mod(secInt, modulus)
var junk []*big.Int // Generate junk numbers.
for i := 1; i < m.Min; i++ {
r := make([]byte, (modulus.BitLen()/8)+1)
_, err := rand.Read(r)
if err != nil {
return out, err
}
s := big.NewInt(0).SetBytes(r)
s.Mod(s, modulus)
junk = append(junk, s)
}
for i, cond := range m.Conds { // Calculate shares.
share := big.NewInt(1)
share.Mul(share, secInt)
for j := 2; j <= m.Min; j++ {
cell := big.NewInt(int64(i + 1))
cell.Exp(cell, big.NewInt(int64(j-1)), modulus)
// CELL SHOULD ALWAYS BE LESS THAN MODULUS
share.Add(share, cell.Mul(cell, junk[j-2])).Mod(share, modulus)
}
switch cond.(type) {
case String:
name := cond.(String).string
if _, ok := out[name]; ok {
out[name] = append(out[name], share.Bytes())
} else if (*db).ValidUser(name) {
out[name] = [][]byte{share.Bytes()}
} else {
return out, errors.New("Unknown user in predicate.")
}
default:
below := MSP(cond.(Formatted))
subOut, err := below.DistributeShares(share.Bytes(), modulus, db)
if err != nil {
return out, err
}
for name, shares := range subOut {
if _, ok := out[name]; ok {
out[name] = append(out[name], shares...)
} else {
out[name] = shares
}
}
}
}
return out, nil
}
func (m MSP) RecoverSecret(modulus *big.Int, db *UserDatabase) ([]byte, error) {
cache := make(map[string][][]byte, 0) // Caches un-used shares for a user.
return m.recoverSecret(modulus, db, cache)
}
func (m MSP) recoverSecret(modulus *big.Int, db *UserDatabase, cache map[string][][]byte) ([]byte, error) {
var (
index = []int{} // Indexes where given shares were in the matrix.
shares = [][]byte{} // Contains shares that will be used in reconstruction.
)
ok, names, locs, _ := m.DerivePath(db)
if !ok {
return nil, errors.New("Not enough shares to recover.")
}
for _, name := range names {
if _, cached := cache[name]; !cached {
out, err := (*db).GetShare(name)
if err != nil {
return nil, err
}
cache[name] = out
}
}
for _, loc := range locs {
gate := m.Conds[loc]
index = append(index, loc+1)
switch gate.(type) {
case String:
if len(cache[gate.(String).string]) <= gate.(String).index {
return nil, errors.New("Predicate / database mismatch!")
}
shares = append(shares, cache[gate.(String).string][gate.(String).index])
case Formatted:
share, err := MSP(gate.(Formatted)).recoverSecret(modulus, db, cache)
if err != nil {
return nil, err
}
shares = append(shares, share)
}
}
// Calculate the reconstruction vector. We only need the top row of the
// matrix's inverse, so we augment M transposed with u1 transposed and
// eliminate Gauss-Jordan style.
matrix := [][][2]int{} // 2d grid of (numerator, denominator)
matrix = append(matrix, [][2]int{}) // Create first row of all 1s
for j := 0; j < m.Min; j++ {
matrix[0] = append(matrix[0], [2]int{1, 1})
}
for j := 1; j < m.Min; j++ { // Fill in rest of matrix.
row := [][2]int{}
for k, idx := range index {
row = append(row, [2]int{int(idx) * matrix[j-1][k][0], matrix[j-1][k][1]})
}
matrix = append(matrix, row)
}
matrix[0] = append(matrix[0], [2]int{1, 1}) // Stick on last column.
for j := 1; j < m.Min; j++ {
matrix[j] = append(matrix[j], [2]int{0, 1})
}
// Reduce matrix.
for i := 0; i < len(matrix); i++ {
for j := 0; j < len(matrix[i]); j++ { // Make row unary.
if i == j {
continue
}
matrix[i][j][0] *= matrix[i][i][1]
matrix[i][j][1] *= matrix[i][i][0]
}
matrix[i][i] = [2]int{1, 1}
for j := 0; j < len(matrix); j++ { // Remove this row from the others.
if i == j {
continue
}
top := matrix[j][i][0]
bot := matrix[j][i][1]
for k := 0; k < len(matrix[j]); k++ {
// matrix[j][k] = matrix[j][k] - matrix[j][i] * matrix[i][k]
temp := [2]int{0, 0}
temp[0] = top * matrix[i][k][0]
temp[1] = bot * matrix[i][k][1]
if matrix[j][k][0] == 0 {
matrix[j][k][0] = -temp[0]
matrix[j][k][1] = temp[1]
} else {
matrix[j][k][0] = (matrix[j][k][0] * temp[1]) - (temp[0] * matrix[j][k][1])
matrix[j][k][1] *= temp[1]
}
if matrix[j][k][0] == 0 {
matrix[j][k][1] = 1
}
}
}
}
// Compute dot product of the shares vector and the reconstruction vector to
// reconstruct the secret.
size := len(modulus.Bytes())
out := make([]byte, size)
secInt := big.NewInt(0)
for i, share := range shares {
lst := len(matrix[i]) - 1
num := big.NewInt(int64(matrix[i][lst][0]))
den := big.NewInt(int64(matrix[i][lst][1]))
num.Mod(num, modulus)
den.Mod(den, modulus)
coeff := big.NewInt(0).ModInverse(den, modulus)
coeff.Mul(coeff, num).Mod(coeff, modulus)
shareInt := big.NewInt(0).SetBytes(share)
shareInt.Mul(shareInt, coeff).Mod(shareInt, modulus)
secInt.Add(secInt, shareInt).Mod(secInt, modulus)
}
out = append(out, secInt.Bytes()...)
return out[len(out)-size:], nil
}
+72
View File
@@ -0,0 +1,72 @@
package msp
import (
"bytes"
"crypto/rand"
"errors"
"testing"
)
type Database map[string][][]byte
func (d Database) ValidUser(name string) bool {
_, ok := d[name]
return ok
}
func (d Database) CanGetShare(name string) bool {
_, ok := d[name]
return ok
}
func (d Database) GetShare(name string) ([][]byte, error) {
out, ok := d[name]
if ok {
return out, nil
} else {
return nil, errors.New("Not found!")
}
}
func TestMSP(t *testing.T) {
db := UserDatabase(Database(map[string][][]byte{
"Alice": [][]byte{},
"Bob": [][]byte{},
"Carl": [][]byte{},
}))
sec := make([]byte, 16)
rand.Read(sec)
sec[0] &= 63 // Removes first 2 bits of key.
predicate, _ := StringToMSP("(2, (1, Alice, Bob), Carl)")
shares1, _ := predicate.DistributeShares(sec, Modulus(127), &db)
shares2, _ := predicate.DistributeShares(sec, Modulus(127), &db)
alice := bytes.Compare(shares1["Alice"][0], shares2["Alice"][0])
bob := bytes.Compare(shares1["Bob"][0], shares2["Bob"][0])
carl := bytes.Compare(shares1["Carl"][0], shares2["Carl"][0])
if alice == 0 && bob == 0 && carl == 0 {
t.Fatalf("Key splitting isn't random! %v %v", shares1, shares2)
}
db1 := UserDatabase(Database(shares1))
db2 := UserDatabase(Database(shares2))
sec1, err := predicate.RecoverSecret(Modulus(127), &db1)
if err != nil {
t.Fatalf("#1: %v", err)
}
sec2, err := predicate.RecoverSecret(Modulus(127), &db2)
if err != nil {
t.Fatalf("#2: %v", err)
}
if !(bytes.Compare(sec, sec1) == 0 && bytes.Compare(sec, sec2) == 0) {
t.Fatalf("Secrets derived differed: %v %v %v", sec, sec1, sec2)
}
}
+218
View File
@@ -0,0 +1,218 @@
package msp
import (
"container/list"
"errors"
"strings"
)
type NodeType int // Types of node in the binary expression tree.
const (
NodeAnd NodeType = iota
NodeOr
)
func (t NodeType) Type() NodeType {
return t
}
type Raw struct { // Represents one node in the tree.
NodeType
Left *Condition
Right *Condition
}
func StringToRaw(r string) (out Raw, err error) {
// Automaton. Modification of Dijkstra's Two-Stack Algorithm for parsing
// infix notation. Reads one long unbroken expression (several operators and
// operands with no parentheses) at a time and parses it into a binary
// expression tree (giving AND operators precedence). Running time linear in
// the size of the predicate?
//
// Steps to the next (un)parenthesis.
// ( -> Push new queue onto staging stack
// value -> Push onto back of queue at top of staging stack.
// ) -> Pop queue off top of staging stack, build BET, and push tree
// onto the back of the top queue.
//
// To build the binary expression tree, for each type of operation we iterate
// through the (Condition, operator) lists compacting where that operation
// occurs into tree nodes.
//
// Staging stack is empty on initialization and should have exactly 1 node
// (the root node) at the end of the string.
r = "(" + r + ")"
min := func(a, b, c int) int { // Return smallest non-negative argument.
if a > b { // Sort {a, b, c}
a, b = b, a
}
if b > c {
b, c = c, b
}
if a > b {
a, b = b, a
}
if a != -1 {
return a
} else if b != -1 {
return b
} else {
return c
}
}
getNext := func(r string) (string, string) { // r -> (next, rest)
r = strings.TrimSpace(r)
if r[0] == '(' || r[0] == ')' || r[0] == '&' || r[0] == '|' {
return r[0:1], r[1:]
}
nextOper := min(
strings.Index(r, "&"),
strings.Index(r, "|"),
strings.Index(r, ")"),
)
if nextOper == -1 {
return r, ""
}
return strings.TrimSpace(r[0:nextOper]), r[nextOper:]
}
staging := list.New() // Stack of (Condition list, operator list)
indices := make(map[string]int, 0)
var nxt string
for len(r) > 0 {
nxt, r = getNext(r)
switch nxt {
case "(":
staging.PushFront([2]*list.List{list.New(), list.New()})
case ")":
top := staging.Remove(staging.Front()).([2]*list.List)
if top[0].Len() != (top[1].Len() + 1) {
return out, errors.New("Stacks are invalid size.")
}
for typ := NodeAnd; typ <= NodeOr; typ++ {
var step *list.Element
leftOperand := top[0].Front()
for oper := top[1].Front(); oper != nil; oper = step {
step = oper.Next()
if oper.Value.(NodeType) == typ {
left := leftOperand.Value.(Condition)
right := leftOperand.Next().Value.(Condition)
leftOperand.Next().Value = Raw{
NodeType: typ,
Left: &left,
Right: &right,
}
leftOperand = leftOperand.Next()
top[0].Remove(leftOperand.Prev())
top[1].Remove(oper)
} else {
leftOperand = leftOperand.Next()
}
}
}
if top[0].Len() != 1 || top[1].Len() != 0 {
return out, errors.New("Invalid expression--couldn't evaluate.")
}
if staging.Len() == 0 {
if len(r) == 0 {
return top[0].Front().Value.(Raw), nil
}
return out, errors.New("Invalid string--terminated early.")
}
staging.Front().Value.([2]*list.List)[0].PushBack(top[0].Front().Value)
case "&":
staging.Front().Value.([2]*list.List)[1].PushBack(NodeAnd)
case "|":
staging.Front().Value.([2]*list.List)[1].PushBack(NodeOr)
default:
if _, there := indices[nxt]; !there {
indices[nxt] = 0
}
staging.Front().Value.([2]*list.List)[0].PushBack(String{nxt, indices[nxt]})
indices[nxt]++
}
}
return out, errors.New("Invalid string--never terminated.")
}
func (r Raw) String() string {
out := ""
switch (*r.Left).(type) {
case String:
out += (*r.Left).(String).string
default:
out += "(" + (*r.Left).(Raw).String() + ")"
}
if r.Type() == NodeAnd {
out += " & "
} else {
out += " | "
}
switch (*r.Right).(type) {
case String:
out += (*r.Right).(String).string
default:
out += "(" + (*r.Right).(Raw).String() + ")"
}
return out
}
func (r Raw) Formatted() (out Formatted) {
// Recursively maps a raw predicate to a formatted predicate by mapping AND
// gates to (2, A, B) treshold gates and OR gates to (1, A, B) gates.
if r.Type() == NodeAnd {
out.Min = 2
} else {
out.Min = 1
}
switch (*r.Left).(type) {
case String:
out.Conds = []Condition{(*r.Left).(String)}
default:
out.Conds = []Condition{(*r.Left).(Raw).Formatted()}
}
switch (*r.Right).(type) {
case String:
out.Conds = append(out.Conds, (*r.Right).(String))
default:
out.Conds = append(out.Conds, (*r.Right).(Raw).Formatted())
}
out.Compress() // Small amount of predicate compression.
return
}
func (r Raw) Ok(db *UserDatabase) bool {
if r.Type() == NodeAnd {
return (*r.Left).Ok(db) && (*r.Right).Ok(db)
} else {
return (*r.Left).Ok(db) || (*r.Right).Ok(db)
}
}
+74
View File
@@ -0,0 +1,74 @@
package msp
import (
"testing"
)
func TestRaw(t *testing.T) {
alice := Condition(String{"Alice", 0})
bob := Condition(String{"Bob", 0})
carl := Condition(String{"Carl", 0})
query1 := Raw{
NodeType: NodeAnd,
Left: &alice,
Right: &bob,
}
aliceOrBob := Condition(Raw{
NodeType: NodeOr,
Left: &alice,
Right: &bob,
})
query2 := Raw{
NodeType: NodeAnd,
Left: &aliceOrBob,
Right: &carl,
}
db := UserDatabase(Database(map[string][][]byte{
"Alice": [][]byte{[]byte("blah")},
"Carl": [][]byte{[]byte("herp")},
}))
if query1.Ok(&db) != false {
t.Fatalf("Query #1 was wrong.")
}
if query2.Ok(&db) != true {
t.Fatalf("Query #2 was wrong.")
}
query1String := "Alice & Bob"
query2String := "(Alice | Bob) & Carl"
if query1.String() != query1String {
t.Fatalf("Query #1 String was wrong; %v", query1.String())
}
if query2.String() != query2String {
t.Fatalf("Query #2 String was wrong; %v", query2.String())
}
decQuery1, err := StringToRaw(query1String)
if err != nil || decQuery1.String() != query1String {
t.Fatalf("Query #1 decoded wrong: %v %v", decQuery1.String(), err)
}
decQuery2, err := StringToRaw(query2String)
if err != nil || decQuery2.String() != query2String {
t.Fatalf("Query #2 decoded wrong: %v %v", decQuery2.String(), err)
}
formattedQuery1String := "(2, Alice, Bob)"
formattedQuery2String := "(2, (1, Alice, Bob), Carl)"
if query1.Formatted().String() != formattedQuery1String {
t.Fatalf("Query #1 formatted wrong: %v", query1.Formatted().String())
}
if query2.Formatted().String() != formattedQuery2String {
t.Fatalf("Query #2 formatted wrong: %v", query2.Formatted().String())
}
}