From fa3fb94df02309b7e0c8e34e8cde03e1f3b89389 Mon Sep 17 00:00:00 2001 From: Tim Hilliard Date: Tue, 7 Nov 2017 08:55:31 -0800 Subject: [PATCH 1/6] Allow shares to be created and combined as bytes rather than strings so that binary files can be shared --- sssa.go | 32 ++++++++++++++++++++++++++++---- sssa_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/sssa.go b/sssa.go index ac2c798..d0d54fc 100644 --- a/sssa.go +++ b/sssa.go @@ -20,6 +20,16 @@ const ( * share to recreate, of length shares, from the input secret raw as a string **/ func Create(minimum int, shares int, raw string) ([]string, error) { + return CreateFromBytes(minimum, shares, []byte(raw)) +} + +/** + * Returns a new arary of secret shares (encoding x,y pairs as base64 strings) + * created by Shamir's Secret Sharing Algorithm requring a minimum number of + * share to recreate, of length shares, from the input secret raw as an array + * bytes +**/ +func CreateFromBytes(minimum int, shares int, raw []byte) ([]string, error) { // Verify minimum isn't greater than shares; there is no way to recreate // the original polynomial in our current setup, therefore it doesn't make // sense to generate fewer shares than are needed to reconstruct the secret. @@ -28,7 +38,7 @@ func Create(minimum int, shares int, raw string) ([]string, error) { } // Convert the secret to its respective 256-bit big.Int representation - var secret []*big.Int = splitByteToInt([]byte(raw)) + var secret []*big.Int = splitByteToInt(raw) // Set constant prime across the package prime, _ = big.NewInt(0).SetString(DefaultPrimeStr, 10) @@ -110,7 +120,7 @@ func Create(minimum int, shares int, raw string) ([]string, error) { * or more are passed to this function. Passing thus does not affect it * Passing fewer however, simply means that the returned secret is wrong. **/ -func Combine(shares []string) (string, error) { +func CombineAsBytes(shares []string) ([]byte, error) { // Recreate the original object of x, y points, based upon number of shares // and size of each share (number of parts in the secret). var secrets [][][]*big.Int = make([][][]*big.Int, len(shares)) @@ -122,7 +132,7 @@ func Combine(shares []string) (string, error) { for i := range shares { // ...ensure that it is valid... if IsValidShare(shares[i]) == false { - return "", ErrOneOfTheSharesIsInvalid + return []byte(""), ErrOneOfTheSharesIsInvalid } // ...find the number of parts it represents... @@ -183,7 +193,21 @@ func Combine(shares []string) (string, error) { } // ...and return the result! - return string(mergeIntToByte(secret)), nil + return mergeIntToByte(secret), nil +} + +/** + * Takes a string array of shares encoded in base64 created via Shamir's + * Algorithm; each string must be of equal length of a multiple of 88 characters + * as a single 88 character share is a pair of 256-bit numbers (x, y). + * + * Note: the polynomial will converge if the specified minimum number of shares + * or more are passed to this function. Passing thus does not affect it + * Passing fewer however, simply means that the returned secret is wrong. +**/ +func Combine(shares []string) (string, error) { + res, err := CombineAsBytes(shares) + return string(res), err } /** diff --git a/sssa_test.go b/sssa_test.go index 389f7a9..1f12d33 100644 --- a/sssa_test.go +++ b/sssa_test.go @@ -2,6 +2,7 @@ package sssa import ( "testing" + "bytes" ) func TestCreateCombine(t *testing.T) { @@ -30,6 +31,32 @@ func TestCreateCombine(t *testing.T) { } } +func TestCreateCombineBytes(t *testing.T) { + // Short, medium, and long tests + testvals := [][]byte{ + []byte("N17FigASkL6p1EOgJhRaIquQLGvYV0"), + []byte("0y10VAfmyH7GLQY6QccCSLKJi8iFgpcSBTLyYOGbiYPqOpStAf1OYuzEBzZR"), + []byte("KjRHO1nHmIDidf6fKvsiXWcTqNYo2U9U8juO94EHXVqgearRISTQe0zAjkeUYYBvtcB8VWzZHYm6ktMlhOXXCfRFhbJzBUsXaHb5UDQAvs2GKy6yq0mnp8gCj98ksDlUultqygybYyHvjqR7D7EAWIKPKUVz4of8OzSjZlYg7YtCUMYhwQDryESiYabFID1PKBfKn5WSGgJBIsDw5g2HB2AqC1r3K8GboDN616Swo6qjvSFbseeETCYDB3ikS7uiK67ErIULNqVjf7IKoOaooEhQACmZ5HdWpr34tstg18rO"), + } + + minimum := []int{4, 6, 20} + shares := []int{5, 100, 100} + + for i := range testvals { + created, err := CreateFromBytes(minimum[i], shares[i], testvals[i]) + if err != nil { + t.Fatal("Fatal: creating: ", err) + } + combined, err := CombineAsBytes(created) + if err != nil { + t.Fatal("Fatal: combining: ", err) + } + if !bytes.Equal(combined, testvals[i]) { + t.Fatal("Fatal: combining returned invalid data") + } + } +} + func TestLibraryCombine(t *testing.T) { shares := []string{ "U1k9koNN67-og3ZY3Mmikeyj4gEFwK4HXDSglM8i_xc=yA3eU4_XYcJP0ijD63Tvqu1gklhBV32tu8cHPZXP-bk=", From cef85b2399729ba3eafcfaaf3c17e98ccb22ea7f Mon Sep 17 00:00:00 2001 From: Tim Hilliard Date: Fri, 10 Nov 2017 22:25:29 -0800 Subject: [PATCH 2/6] fix linting errors --- .gitlab-ci.yml | 4 +++ .travis.yml | 6 ++++ sssa.go | 84 +++++++++++++++++++++++--------------------------- utils.go | 18 +++++------ 4 files changed, 58 insertions(+), 54 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2f85956..67c993b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,9 @@ image: golang:latest +before_script: + - go get -u github.com/golang/lint/golint + tests: script: + - golint - go test diff --git a/.travis.yml b/.travis.yml index adbef37..9202465 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,3 +5,9 @@ go: - 1.4 - 1.5 - tip +install: + - go get -t ./... + - go get -u github.com/golang/lint/golint +script: + - golint + - go test \ No newline at end of file diff --git a/sssa.go b/sssa.go index d0d54fc..b8b8dab 100644 --- a/sssa.go +++ b/sssa.go @@ -5,30 +5,29 @@ import ( "math/big" ) +// Error codes when creating and combining secrets var ( ErrCannotRequireMoreShares = errors.New("cannot require more shares then existing") ErrOneOfTheSharesIsInvalid = errors.New("one of the shares is invalid") ) +// Default Prime String used to create the shares const ( DefaultPrimeStr = "115792089237316195423570985008687907853269984665640564039457584007913129639747" ) -/** - * Returns a new arary of secret shares (encoding x,y pairs as base64 strings) - * created by Shamir's Secret Sharing Algorithm requring a minimum number of - * share to recreate, of length shares, from the input secret raw as a string -**/ +// Create returns a new arary of secret shares (encoding x,y pairs as base64 +// strings) created by Shamir's Secret Sharing Algorithm requring a minimum +// number of share to recreate, of length shares, from the input secret raw +// as a string func Create(minimum int, shares int, raw string) ([]string, error) { return CreateFromBytes(minimum, shares, []byte(raw)) } -/** - * Returns a new arary of secret shares (encoding x,y pairs as base64 strings) - * created by Shamir's Secret Sharing Algorithm requring a minimum number of - * share to recreate, of length shares, from the input secret raw as an array - * bytes -**/ +// CreateFromBytes returns a new arary of secret shares (encoding x,y pair +// as base64 strings) created by Shamir's Secret Sharing Algorithm requring +// a minimum number of share to recreate, of length shares, from the input +// secret raw as an array bytes func CreateFromBytes(minimum int, shares int, raw []byte) ([]string, error) { // Verify minimum isn't greater than shares; there is no way to recreate // the original polynomial in our current setup, therefore it doesn't make @@ -38,13 +37,13 @@ func CreateFromBytes(minimum int, shares int, raw []byte) ([]string, error) { } // Convert the secret to its respective 256-bit big.Int representation - var secret []*big.Int = splitByteToInt(raw) + var secret = splitByteToInt(raw) // Set constant prime across the package prime, _ = big.NewInt(0).SetString(DefaultPrimeStr, 10) // List of currently used numbers in the polynomial - var numbers []*big.Int = make([]*big.Int, 0) + var numbers = make([]*big.Int, 0) numbers = append(numbers, big.NewInt(0)) // Create the polynomial of degree (minimum - 1); that is, the highest @@ -54,7 +53,7 @@ func CreateFromBytes(minimum int, shares int, raw []byte) ([]string, error) { // However, the polynomial object is a 2d array, because we are constructing // a different polynomial for each part of the secret // polynomial[parts][minimum] - var polynomial [][]*big.Int = make([][]*big.Int, len(secret)) + var polynomial = make([][]*big.Int, len(secret)) for i := range polynomial { polynomial[i] = make([]*big.Int, minimum) polynomial[i][0] = secret[i] @@ -80,8 +79,8 @@ func CreateFromBytes(minimum int, shares int, raw []byte) ([]string, error) { // in the inner loop. Can disappear later if desired. [TODO] // // secrets[shares][parts][2] - var secrets [][][]*big.Int = make([][][]*big.Int, shares) - var result []string = make([]string, shares) + var secrets = make([][][]*big.Int, shares) + var result = make([]string, shares) // For every share... for i := range secrets { @@ -111,19 +110,18 @@ func CreateFromBytes(minimum int, shares int, raw []byte) ([]string, error) { return result, nil } -/** - * Takes a string array of shares encoded in base64 created via Shamir's - * Algorithm; each string must be of equal length of a multiple of 88 characters - * as a single 88 character share is a pair of 256-bit numbers (x, y). - * - * Note: the polynomial will converge if the specified minimum number of shares - * or more are passed to this function. Passing thus does not affect it - * Passing fewer however, simply means that the returned secret is wrong. -**/ +// CombineAsBytes takes a string array of shares encoded in base64 created via +// Shamir's Algorithm; each string must be of equal length of a multiple of 88 +// characters as a single 88 character share is a pair of 256-bit +// numbers (x, y). +// +// Note: the polynomial will converge if the specified minimum number of shares +// or more are passed to this function. Passing thus does not affect it +// Passing fewer however, simply means that the returned secret is wrong. func CombineAsBytes(shares []string) ([]byte, error) { // Recreate the original object of x, y points, based upon number of shares // and size of each share (number of parts in the secret). - var secrets [][][]*big.Int = make([][][]*big.Int, len(shares)) + var secrets = make([][][]*big.Int, len(shares)) // Set constant prime prime, _ = big.NewInt(0).SetString(DefaultPrimeStr, 10) @@ -152,7 +150,7 @@ func CombineAsBytes(shares []string) ([]byte, error) { // Use Lagrange Polynomial Interpolation (LPI) to reconstruct the secret. // For each part of the secert (clearest to iterate over)... - var secret []*big.Int = make([]*big.Int, len(secrets[0])) + var secret = make([]*big.Int, len(secrets[0])) for j := range secret { secret[j] = big.NewInt(0) // ...and every share... @@ -196,29 +194,25 @@ func CombineAsBytes(shares []string) ([]byte, error) { return mergeIntToByte(secret), nil } -/** - * Takes a string array of shares encoded in base64 created via Shamir's - * Algorithm; each string must be of equal length of a multiple of 88 characters - * as a single 88 character share is a pair of 256-bit numbers (x, y). - * - * Note: the polynomial will converge if the specified minimum number of shares - * or more are passed to this function. Passing thus does not affect it - * Passing fewer however, simply means that the returned secret is wrong. -**/ +// Combine takes a string array of shares encoded in base64 created via Shamir's +// Algorithm; each string must be of equal length of a multiple of 88 characters +// as a single 88 character share is a pair of 256-bit numbers (x, y). +// +// Note: the polynomial will converge if the specified minimum number of shares +// or more are passed to this function. Passing thus does not affect it +// Passing fewer however, simply means that the returned secret is wrong. func Combine(shares []string) (string, error) { res, err := CombineAsBytes(shares) return string(res), err } -/** - * Takes in a given string to check if it is a valid secret - * - * Requirements: - * Length multiple of 88 - * Can decode each 44 character block as base64 - * - * Returns only success/failure (bool) -**/ +// IsValidShare takes in a given string to check if it is a valid secret +// +// Requirements: +// Length multiple of 88 +// Can decode each 44 character block as base64 +// +// Returns only success/failure (bool) func IsValidShare(candidate string) bool { // Set constant prime across the package prime, _ = big.NewInt(0).SetString(DefaultPrimeStr, 10) diff --git a/utils.go b/utils.go index 477e7c4..1a9505a 100644 --- a/utils.go +++ b/utils.go @@ -29,16 +29,16 @@ func random() *big.Int { * significant bit is zero. **/ func splitByteToInt(secret []byte) []*big.Int { - hex_data := hex.EncodeToString(secret) - count := int(math.Ceil(float64(len(hex_data)) / 64.0)) + hexData := hex.EncodeToString(secret) + count := int(math.Ceil(float64(len(hexData)) / 64.0)) result := make([]*big.Int, count) for i := 0; i < count; i++ { - if (i+1)*64 < len(hex_data) { - result[i], _ = big.NewInt(0).SetString(hex_data[i*64:(i+1)*64], 16) + if (i+1)*64 < len(hexData) { + result[i], _ = big.NewInt(0).SetString(hexData[i*64:(i+1)*64], 16) } else { - data := strings.Join([]string{hex_data[i*64:], strings.Repeat("0", 64-(len(hex_data)-i*64))}, "") + data := strings.Join([]string{hexData[i*64:], strings.Repeat("0", 64-(len(hexData)-i*64))}, "") result[i], _ = big.NewInt(0).SetString(data, 16) } } @@ -51,13 +51,13 @@ func splitByteToInt(secret []byte) []*big.Int { * least significant nulls **/ func mergeIntToByte(secret []*big.Int) []byte { - var hex_data = "" + var hexData = "" for i := range secret { tmp := fmt.Sprintf("%x", secret[i]) - hex_data += strings.Join([]string{strings.Repeat("0", (64 - len(tmp))), tmp}, "") + hexData += strings.Join([]string{strings.Repeat("0", (64 - len(tmp))), tmp}, "") } - result, _ := hex.DecodeString(hex_data) + result, _ := hex.DecodeString(hexData) result = bytes.TrimRight(result, "\x00") return result @@ -70,7 +70,7 @@ func mergeIntToByte(secret []*big.Int) []byte { **/ func evaluatePolynomial(polynomial []*big.Int, value *big.Int) *big.Int { last := len(polynomial) - 1 - var result *big.Int = big.NewInt(0).Set(polynomial[last]) + var result = big.NewInt(0).Set(polynomial[last]) for s := last - 1; s >= 0; s-- { result = result.Mul(result, value) From 5f3bc5df3dc795a431b72cb69849a57a02531eea Mon Sep 17 00:00:00 2001 From: Tim Hilliard Date: Fri, 10 Nov 2017 22:32:54 -0800 Subject: [PATCH 3/6] attempt to get travis working --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9202465..2b87c2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ go: - 1.5 - tip install: - - go get -t ./... - go get -u github.com/golang/lint/golint script: - golint From 4af1ca60cdbba2152746df41e2229bf13a711085 Mon Sep 17 00:00:00 2001 From: Tim Hilliard Date: Fri, 10 Nov 2017 22:34:49 -0800 Subject: [PATCH 4/6] attempt to get travis working --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2b87c2f..dae8ff5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,6 @@ go: - 1.4 - 1.5 - tip -install: - - go get -u github.com/golang/lint/golint script: - golint - go test \ No newline at end of file From f5f6b2d47fae3e5547244e408f5b3739848e1753 Mon Sep 17 00:00:00 2001 From: Tim Hilliard Date: Fri, 10 Nov 2017 22:37:36 -0800 Subject: [PATCH 5/6] attempt to get travis working --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index dae8ff5..4c1be52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,9 @@ go: - 1.4 - 1.5 - tip +install: + - go get -t ./... + - go get github.com/golang/lint/golint script: - golint - go test \ No newline at end of file From cba6d19fde8a6a660571929ea1ebd5f5b8ce863c Mon Sep 17 00:00:00 2001 From: Tim Hilliard Date: Fri, 10 Nov 2017 22:40:30 -0800 Subject: [PATCH 6/6] attempt to get travis working --- .travis.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c1be52..367ed8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,20 @@ go: - 1.4 - 1.5 - tip -install: - - go get -t ./... +matrix: + fast_finish: true +before_install: + - go get golang.org/x/tools/cmd/vet + - go get golang.org/x/tools/cmd/cover - go get github.com/golang/lint/golint + - go get github.com/mattn/goveralls +install: + - go get -d -v ./... && go build -v ./... script: - - golint - - go test \ No newline at end of file + - go vet -x ./... + - $HOME/gopath/bin/golint ./... + - go test -v ./... + - go test -covermode=count -coverprofile=profile.cov . + +after_script: + - $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci \ No newline at end of file