Skip to content

Commit

Permalink
Add Hex serde for keys (#44)
Browse files Browse the repository at this point in the history
* Add Hex serde for keys

---------

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>
  • Loading branch information
bytemare authored Sep 20, 2024
1 parent e969657 commit 50035fd
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 31 deletions.
67 changes: 51 additions & 16 deletions keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package secretsharing

import (
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
Expand All @@ -26,8 +27,12 @@ var (
errEncodingInvalidLength = errors.New("invalid encoding length")
errEncodingInvalidJSONEncoding = errors.New("invalid JSON encoding")
errInvalidPolynomialLength = errors.New("invalid polynomial length (exceeds uint16 limit 65535)")
errPublicKeyShareDecodePrefix = errors.New("failed to decode PublicKeyShare")
errKeyShareDecodePrefix = errors.New("failed to decode KeyShare")
)

const errFmt = "%w: %w"

// The Share interface enables to use functions in this package with compatible key shares.
type Share interface {
// Identifier returns the identity for this share.
Expand Down Expand Up @@ -78,13 +83,18 @@ func (p *PublicKeyShare) Encode() []byte {
return out
}

// Hex returns the hexadecimal representation of the byte encoding returned by Encode().
func (p *PublicKeyShare) Hex() string {
return hex.EncodeToString(p.Encode())
}

func (p *PublicKeyShare) decode(g group.Group, cLen int, data []byte) error {
eLen := g.ElementLength()
id := binary.LittleEndian.Uint16(data[1:3])

pk := g.NewElement()
if err := pk.Decode(data[7 : 7+eLen]); err != nil {
return fmt.Errorf("failed to decode public key: %w", err)
return fmt.Errorf("%w: failed to decode public key: %w", errPublicKeyShareDecodePrefix, err)
}

i := 0
Expand All @@ -93,7 +103,7 @@ func (p *PublicKeyShare) decode(g group.Group, cLen int, data []byte) error {
for j := 7 + eLen; j < len(data); j += eLen {
c := g.NewElement()
if err := c.Decode(data[j : j+eLen]); err != nil {
return fmt.Errorf("failed to decode commitment %d: %w", i+1, err)
return fmt.Errorf("%w: failed to decode commitment %d: %w", errPublicKeyShareDecodePrefix, i+1, err)
}

commitment[i] = c
Expand All @@ -112,21 +122,31 @@ func (p *PublicKeyShare) decode(g group.Group, cLen int, data []byte) error {
func (p *PublicKeyShare) Decode(data []byte) error {
g, expectedLength, cLen, err := decodeKeyShareHeader(data)
if err != nil {
return err
return fmt.Errorf(errFmt, errPublicKeyShareDecodePrefix, err)
}

if len(data) != expectedLength {
return errEncodingInvalidLength
return fmt.Errorf(errFmt, errPublicKeyShareDecodePrefix, errEncodingInvalidLength)
}

return p.decode(g, cLen, data)
}

// DecodeHex sets p to the decoding of the hex encoded representation returned by Hex().
func (p *PublicKeyShare) DecodeHex(h string) error {
b, err := hex.DecodeString(h)
if err != nil {
return fmt.Errorf(errFmt, errPublicKeyShareDecodePrefix, err)
}

return p.Decode(b)
}

// UnmarshalJSON decodes data into p, or returns an error.
func (p *PublicKeyShare) UnmarshalJSON(data []byte) error {
ps := new(publicKeyShareShadow)
if err := unmarshalJSON(data, ps); err != nil {
return err
return fmt.Errorf(errFmt, errPublicKeyShareDecodePrefix, err)
}

*p = PublicKeyShare(*ps)
Expand Down Expand Up @@ -168,38 +188,53 @@ func (k *KeyShare) Encode() []byte {
return out
}

// Hex returns the hexadecimal representation of the byte encoding returned by Encode().
func (k *KeyShare) Hex() string {
return hex.EncodeToString(k.Encode())
}

// Decode deserializes the compact encoding obtained from Encode(), or returns an error.
func (k *KeyShare) Decode(data []byte) error {
g, pkLen, cLen, _err := decodeKeyShareHeader(data)
if _err != nil {
return _err
g, pkLen, cLen, err := decodeKeyShareHeader(data)
if err != nil {
return fmt.Errorf(errFmt, errKeyShareDecodePrefix, err)
}

expectedLength := pkLen + g.ScalarLength() + g.ElementLength()
if len(data) != expectedLength {
return errEncodingInvalidLength
return fmt.Errorf(errFmt, errKeyShareDecodePrefix, errEncodingInvalidLength)
}

pk := new(PublicKeyShare)
if err := pk.decode(g, cLen, data[:pkLen]); err != nil {
return err
if err = pk.decode(g, cLen, data[:pkLen]); err != nil {
return fmt.Errorf(errFmt, errKeyShareDecodePrefix, err)
}

s := g.NewScalar()
if err := s.Decode(data[pkLen : pkLen+g.ScalarLength()]); err != nil {
return fmt.Errorf("failed to decode Secret in KeyShare: %w", err)
if err = s.Decode(data[pkLen : pkLen+g.ScalarLength()]); err != nil {
return fmt.Errorf("%w: failed to decode secret key: %w", errKeyShareDecodePrefix, err)
}

e := g.NewElement()
if err := e.Decode(data[pkLen+g.ScalarLength():]); err != nil {
return fmt.Errorf("failed to decode GroupPublicKey in KeyShare: %w", err)
if err = e.Decode(data[pkLen+g.ScalarLength():]); err != nil {
return fmt.Errorf("%w: failed to decode GroupPublicKey: %w", errKeyShareDecodePrefix, err)
}

k.populate(s, e, pk)

return nil
}

// DecodeHex sets k to the decoding of the hex encoded representation returned by Hex().
func (k *KeyShare) DecodeHex(h string) error {
b, err := hex.DecodeString(h)
if err != nil {
return fmt.Errorf(errFmt, errKeyShareDecodePrefix, err)
}

return k.Decode(b)
}

func (k *KeyShare) populate(s *group.Scalar, gpk *group.Element, pks *PublicKeyShare) {
k.Secret = s
k.GroupPublicKey = gpk
Expand All @@ -210,7 +245,7 @@ func (k *KeyShare) populate(s *group.Scalar, gpk *group.Element, pks *PublicKeyS
func (k *KeyShare) UnmarshalJSON(data []byte) error {
ks := new(keyShareShadow)
if err := unmarshalJSON(data, ks); err != nil {
return err
return fmt.Errorf(errFmt, errKeyShareDecodePrefix, err)
}

k.populate(ks.Secret, ks.GroupPublicKey, (*PublicKeyShare)(ks.publicKeyShareShadow))
Expand Down
95 changes: 80 additions & 15 deletions tests/ss_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ func compareKeyShares(s1, s2 *secretsharing.KeyShare) error {
return comparePublicKeyShare(&s1.PublicKeyShare, &s2.PublicKeyShare)
}

func TestEncoding(t *testing.T) {
func TestEncoding_Bytes(t *testing.T) {
threshold := uint16(3) // threshold is the minimum amount of necessary shares to recombine the secret
max := uint16(7) // the max amount of key share-holders

Expand Down Expand Up @@ -746,7 +746,49 @@ func TestEncoding(t *testing.T) {
}
}

func TestJSONEncoding(t *testing.T) {
func TestEncoding_Hex(t *testing.T) {
threshold := uint16(3) // threshold is the minimum amount of necessary shares to recombine the secret
max := uint16(7) // the max amount of key share-holders

for _, g := range groups {
t.Run(g.String(), func(t *testing.T) {
// This is the global secret to be shared
secret := g.NewScalar().Random()

// Shard the secret into shares
shares, err := secretsharing.ShardAndCommit(g, secret, threshold, max)
if err != nil {
t.Fatal(err)
}

// PublicKeyShare
h := shares[0].Public().Hex()

decodedPKS := new(secretsharing.PublicKeyShare)
if err = decodedPKS.DecodeHex(h); err != nil {
t.Fatal(err)
}

if err = comparePublicKeyShare(&shares[0].PublicKeyShare, decodedPKS); err != nil {
t.Fatal(err)
}

// KeyShare
h = shares[0].Hex()

decodedKS := &secretsharing.KeyShare{}
if err = decodedKS.DecodeHex(h); err != nil {
t.Fatal(err)
}

if err = compareKeyShares(shares[0], decodedKS); err != nil {
t.Fatal(err)
}
})
}
}

func TestEncoding_JSON(t *testing.T) {
threshold := uint16(3) // threshold is the minimum amount of necessary shares to recombine the secret
max := uint16(7) // the max amount of key share-holders

Expand Down Expand Up @@ -796,6 +838,7 @@ func TestJSONEncoding(t *testing.T) {

type serde interface {
Decode([]byte) error
DecodeHex(h string) error
UnmarshalJSON(data []byte) error
}

Expand All @@ -812,6 +855,12 @@ func testDecodeErrorPrefix(t *testing.T, s serde, data []byte, expectedPrefix er
}
}

func testDecodeHexError(t *testing.T, s serde, data string, expectedError error) {
if err := s.DecodeHex(data); err == nil || err.Error() != expectedError.Error() {
t.Fatalf("expected error %q, got %q", expectedError, err)
}
}

func testUnmarshalJSONError(t *testing.T, s serde, data []byte, expectedError error) {
if err := json.Unmarshal(data, s); err == nil || err.Error() != expectedError.Error() {
t.Fatalf("expected error %q, got %q", expectedError, err)
Expand Down Expand Up @@ -883,9 +932,9 @@ func TestEncoding_PublicKeyShare_Bad(t *testing.T) {
threshold := uint16(3)
max := uint16(4)

errEncodingInvalidLength := errors.New("invalid encoding length")
errEncodingInvalidGroup := errors.New("invalid group identifier")
errEncodingInvalidJSONEncoding := errors.New("invalid JSON encoding")
errEncodingInvalidLength := errors.New("failed to decode PublicKeyShare: invalid encoding length")
errEncodingInvalidGroup := errors.New("failed to decode PublicKeyShare: invalid group identifier")
errEncodingInvalidJSONEncoding := errors.New("failed to decode PublicKeyShare: invalid JSON encoding")

for _, g := range groups {
t.Run(g.String(), func(t *testing.T) {
Expand Down Expand Up @@ -917,16 +966,21 @@ func TestEncoding_PublicKeyShare_Bad(t *testing.T) {

// Decode: Bad public key
encoded = slices.Replace(encoded, 7, 7+g.ElementLength(), badElement...)
expectedErrorPrefix := errors.New("failed to decode public key")
expectedErrorPrefix := errors.New("failed to decode PublicKeyShare: failed to decode public key")
testDecodeErrorPrefix(t, new(secretsharing.PublicKeyShare), encoded, expectedErrorPrefix)

// Decode: bad commitment
encoded = shares[0].Public().Encode()
offset := 7 + 2*g.ElementLength()
encoded = slices.Replace(encoded, offset, offset+g.ElementLength(), badElement...)
expectedErrorPrefix = errors.New("failed to decode commitment 2")
expectedErrorPrefix = errors.New("failed to decode PublicKeyShare: failed to decode commitment 2")
testDecodeErrorPrefix(t, new(secretsharing.PublicKeyShare), encoded, expectedErrorPrefix)

// Bad Hex
h := shares[0].Public().Hex()
expectedErrorPrefix = errors.New("failed to decode PublicKeyShare: encoding/hex: odd length hex string")
testDecodeHexError(t, new(secretsharing.PublicKeyShare), h[:len(h)-1], expectedErrorPrefix)

// UnmarshallJSON: bad json
data, err := json.Marshal(shares[0])
if err != nil {
Expand Down Expand Up @@ -974,7 +1028,7 @@ func TestEncoding_PublicKeyShare_Bad(t *testing.T) {
data = replaceStringInBytes(data, fmt.Sprintf("\"group\":%d", g), "\"group\":"+overflow)

expectedErrorPrefix = errors.New(
"failed to read Group: strconv.Atoi: parsing \"9223372036854775808\": value out of range",
"failed to decode PublicKeyShare: failed to read Group: strconv.Atoi: parsing \"9223372036854775808\": value out of range",
)

testUnmarshalJSONErrorPrefix(t, new(secretsharing.PublicKeyShare), data, expectedErrorPrefix)
Expand Down Expand Up @@ -1026,7 +1080,9 @@ func TestEncoding_PublicKeyShare_Bad(t *testing.T) {
t.Fatal(err)
}

errInvalidPolynomialLength := errors.New("invalid polynomial length (exceeds uint16 limit 65535)")
errInvalidPolynomialLength := errors.New(
"failed to decode PublicKeyShare: invalid polynomial length (exceeds uint16 limit 65535)",
)
testUnmarshalJSONError(t, new(secretsharing.PublicKeyShare), data, errInvalidPolynomialLength)
})
}
Expand All @@ -1036,9 +1092,9 @@ func TestEncoding_KeyShare_Bad(t *testing.T) {
threshold := uint16(1)
max := uint16(2)

errEncodingInvalidLength := errors.New("invalid encoding length")
errEncodingInvalidGroup := errors.New("invalid group identifier")
errEncodingInvalidJSONEncoding := errors.New("invalid JSON encoding")
errEncodingInvalidLength := errors.New("failed to decode KeyShare: invalid encoding length")
errEncodingInvalidGroup := errors.New("failed to decode KeyShare: invalid group identifier")
errEncodingInvalidJSONEncoding := errors.New("failed to decode KeyShare: invalid JSON encoding")

for _, g := range groups {
t.Run(g.String(), func(t *testing.T) {
Expand Down Expand Up @@ -1074,25 +1130,34 @@ func TestEncoding_KeyShare_Bad(t *testing.T) {
encoded = shares[0].Encode()
encoded = slices.Replace(encoded, offset, offset+g.ElementLength(), badElement...)

expectedErrorPrefix := errors.New("failed to decode public key: ")
expectedErrorPrefix := errors.New(
"failed to decode KeyShare: failed to decode PublicKeyShare: failed to decode public key: element Decode: ",
)
testDecodeErrorPrefix(t, new(secretsharing.KeyShare), encoded, expectedErrorPrefix)

// Decode: Bad scalar
offset += g.ElementLength() + len(shares[0].Commitment)*g.ElementLength()
encoded = shares[0].Encode()
encoded = slices.Replace(encoded, offset, offset+g.ScalarLength(), badScalar...)
expectedErrorPrefix = errors.New("failed to decode Secret in KeyShare")
expectedErrorPrefix = errors.New("failed to decode KeyShare: failed to decode secret key: scalar Decode: ")

testDecodeErrorPrefix(t, new(secretsharing.KeyShare), encoded, expectedErrorPrefix)

// Decode: bad group public key
offset += g.ScalarLength()
encoded = shares[0].Encode()
encoded = slices.Replace(encoded, offset, offset+g.ElementLength(), badElement...)
expectedErrorPrefix = errors.New("failed to decode GroupPublicKey in KeyShare")
expectedErrorPrefix = errors.New(
"failed to decode KeyShare: failed to decode GroupPublicKey: element Decode: ",
)

testDecodeErrorPrefix(t, new(secretsharing.KeyShare), encoded, expectedErrorPrefix)

// Bad Hex
h := shares[0].Hex()
expectedErrorPrefix = errors.New("failed to decode KeyShare: encoding/hex: odd length hex string")
testDecodeHexError(t, new(secretsharing.KeyShare), h[:len(h)-1], expectedErrorPrefix)

// UnmarshallJSON: bad json
data, err := json.Marshal(shares[0])
if err != nil {
Expand Down

0 comments on commit 50035fd

Please sign in to comment.