Skip to content

Commit

Permalink
errors, new api replacing string input by []byte, algorithm to split …
Browse files Browse the repository at this point in the history
…the message in chunks based on prime number provided, new tests
  • Loading branch information
lucasmenendez committed Mar 24, 2024
1 parent 1e3fb59 commit fe0ce08
Show file tree
Hide file tree
Showing 13 changed files with 599 additions and 178 deletions.
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ package main

import (
"log"
"math/rand"

"github.com/lucasmenendez/gosss"
)
Expand All @@ -37,29 +38,36 @@ func main() {
// create a configuration with 8 shares and 7 minimum shares to recover the
// message
config := &gosss.Config{
Shares: 8,
Min: 7,
Shares: 4,
Min: 3,
}
// hide a message with the defined configuration
shares, err := gosss.HideMessage("secret", config)
msg := "688641b753f1c97526d6a767058a80fd6c6519f5bdb0a08098986b0478c8502b"
log.Printf("message to hide: %s", msg)
totalShares, err := gosss.HideMessage([]byte(msg), config)
if err != nil {
log.Fatalf("error hiding message: %v", err)
}
// print every share and exclude one share to test the recovery
excluded := 3
requiredShares := []string{}
for i, s := range shares {
log.Printf("share: %s", s)
if i != excluded {
requiredShares = append(requiredShares, s)
requiredShares := [][]string{}
for _, secretShares := range totalShares {
log.Printf("shares: %v", secretShares)
// choose a random share to exclude
index := rand.Intn(len(secretShares))
shares := []string{}
for i, share := range secretShares {
if i == index {
continue
}
shares = append(shares, share)
}
requiredShares = append(requiredShares, shares)
}
// recover the message with the required shares
message, err := gosss.RecoverMessage(requiredShares, nil)
if err != nil {
log.Fatalf("error recovering message: %v", err)
}
log.Printf("recovered message: %s", message)
log.Printf("recovered message: %s", string(message))
}

```
18 changes: 11 additions & 7 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package gosss

import (
"fmt"
"math/big"
)
import "math/big"

// 12th Mersenne Prime (2^127 - 1)
var DefaultPrime *big.Int = new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(127), nil), big.NewInt(1))
Expand Down Expand Up @@ -40,22 +37,29 @@ func (c *Config) prepare(op operation) error {
// and the minimum number of shares is greater than 1 and lower than
// the number of shares
if c.Shares <= 0 || c.Shares > maxShares {
return fmt.Errorf("number of shares must be between 1 and %d", maxShares)
return ErrConfigShares
}
if c.Min <= 1 || c.Min >= c.Shares {
return fmt.Errorf("minimum number of shares must be between 2 and %d", c.Shares-1)
return ErrConfigMin
}
case recoverOp:
// for recover a message no checks are needed unless the prime number is
// not defined so break the switch and set the default prime number if
// it is needed
break
default:
return fmt.Errorf("unknown operation")
return ErrConfigOp
}
// if the prime number is not defined it will use the default prime number
if c.Prime == nil {
c.Prime = DefaultPrime
}
return nil
}

// maxSecretPartSize returns the maximum size of the secret part that can be
// hidden in a share, it is the size of the prime number in bytes minus 1, to
// ensure the secret part is smaller than the prime number.
func (c *Config) maxSecretPartSize() int {
return len(c.Prime.Bytes()) - 1
}
69 changes: 69 additions & 0 deletions encode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package gosss

import (
"encoding/hex"
"fmt"
"math/big"
)

// shareToStr converts a big.Int to a string. It uses the bytes of the big.Int
func shareToStr(index, share *big.Int) (string, error) {
if index.Cmp(big.NewInt(255)) > 0 || index.Cmp(big.NewInt(0)) < 0 {
return "", ErrShareIndex
}
bShare := share.Bytes()
// encode the index in a byte and append it at the end of the share
bIndex := index.Bytes()
if len(bIndex) == 0 {
bIndex = []byte{0}
}
fullShare := append(bShare, bIndex[0])
return hex.EncodeToString(fullShare), nil
}

// strToShare converts a string to a big.Int. It uses the bytes of the string
func strToShare(s string) (*big.Int, *big.Int, error) {
b, err := hex.DecodeString(s)
if err != nil {
return nil, nil, fmt.Errorf("%w: %w", ErrDecodeShare, err)
}
bIndex := b[len(b)-1]
bShare := b[:len(b)-1]
return new(big.Int).SetBytes([]byte{bIndex}), new(big.Int).SetBytes(bShare), nil
}

// encodeShares function converts the x and y coordinates of the shares to
// strings. It returns the shares as strings. It uses the shareToStr function to
// encode the shares. It returns an error if the shares cannot be encoded.
func encodeShares(xs, ys []*big.Int) ([]string, error) {
if len(xs) == 0 || len(ys) == 0 || len(xs) != len(ys) {
return nil, ErrInvalidShares
}
// convert the shares to strings and append them to the result
shares := []string{}
for i := 0; i < len(xs); i++ {
share, err := shareToStr(xs[i], ys[i])
if err != nil {
return nil, err
}
shares = append(shares, share)
}
return shares, nil
}

// decodeShares function converts the strings of the shares to x and y
// coordinates of the shares. It uses the strToShare function to decode the
// shares. It returns an error if the shares cannot be decoded.
func decodeShares(shares []string) ([]*big.Int, []*big.Int, error) {
xs := []*big.Int{}
ys := []*big.Int{}
for _, strShare := range shares {
index, share, err := strToShare(strShare)
if err != nil {
return nil, nil, err
}
xs = append(xs, index)
ys = append(ys, share)
}
return xs, ys, nil
}
72 changes: 72 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package gosss

import (
"math/big"
"testing"
)

func Test_shareToStrStrToShare(t *testing.T) {
// generate 10 random big.Int and convert them to string
for i := 0; i < 10; i++ {
idx := big.NewInt(int64(i))
rand, err := randBigInt()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
shareStr, err := shareToStr(idx, rand)
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
index, shareBack, err := strToShare(shareStr)
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
if index.Cmp(idx) != 0 {
t.Errorf("unexpected index: %d", index)
}
if rand.Cmp(shareBack) != 0 {
t.Errorf("unexpected share: %s", shareStr)
}
}
}

func Test_encodeDecodeShares(t *testing.T) {
xs := []*big.Int{
big.NewInt(1),
big.NewInt(2),
big.NewInt(3),
big.NewInt(4),
}
ys := []*big.Int{
big.NewInt(100),
big.NewInt(200),
big.NewInt(300),
big.NewInt(400),
}
encodedShares, err := encodeShares(xs, ys)
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
decodedXs, decodedYs, err := decodeShares(encodedShares)
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
if len(xs) != len(decodedXs) || len(ys) != len(decodedYs) {
t.Errorf("unexpected shares length")
return
}
for i := 0; i < len(xs); i++ {
if xs[i].Cmp(decodedXs[i]) != 0 {
t.Errorf("unexpected x coordinate")
return
}
if ys[i].Cmp(decodedYs[i]) != 0 {
t.Errorf("unexpected y coordinate")
return
}
}
}
19 changes: 19 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package gosss

import "fmt"

var (
// config
ErrConfigShares = fmt.Errorf("wrong number of shares, it must be between 1 and the maximum number of shares")
ErrConfigMin = fmt.Errorf("wrong minimum number of shares, it must be between 2 and the number of shares minus 1")
ErrConfigOp = fmt.Errorf("unknown operation")
// encode
ErrShareIndex = fmt.Errorf("the index must fit in a byte (0-255)")
ErrDecodeShare = fmt.Errorf("error decoding share")
ErrInvalidShares = fmt.Errorf("invalid shares")
// math
ErrReadingRandom = fmt.Errorf("error reading random number")
// sss
ErrRequiredConfig = fmt.Errorf("configuration is required")
ErrEncodeMessage = fmt.Errorf("error encoding message")
)
46 changes: 0 additions & 46 deletions helpers_test.go

This file was deleted.

Loading

0 comments on commit fe0ce08

Please sign in to comment.