From ccaa6313370dddc0923271bd1844baa4750672ba Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 23 Mar 2021 17:24:13 +0100 Subject: [PATCH] Add Signature Compression & Decompression --- .github/workflows/lint.yml | 2 +- README.md | 17 +++++++++----- blindsecp256k1.go | 26 +++++++++++++++++++++- blindsecp256k1_test.go | 45 ++++++++++++++++++++++++++++++++++++++ parsers.go | 9 ++++---- 5 files changed, 88 insertions(+), 11 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0e2fc88..a4a85fb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,4 +14,4 @@ jobs: run: | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0 rm -r wasm - $(go env GOPATH)/bin/golangci-lint run --timeout=5m -c .golangci.yml + go list -f '{{.Dir}}' ./... | fgrep -v wasm | xargs realpath --relative-to=. | xargs $(go env GOPATH)/bin/golangci-lint run --timeout=5m -c .golangci.yml diff --git a/README.md b/README.md index c1bdd96..1f5930f 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,24 @@ Blind signature over [secp256k1](https://en.bitcoin.it/wiki/Secp256k1), based on ## Usage ```go +import ( + [...] + "github.com/arnaucube/go-blindsecp256k1" +) + +[...] + // signer: create new signer key pair -sk := NewPrivateKey() +sk := blindsecp256k1.NewPrivateKey() signerPubK := sk.Public() // signer: when user requests new R parameter to blind a new msg, // create new signerR (public) with its secret k -k, signerR := NewRequestParameters() +k, signerR := blindsecp256k1.NewRequestParameters() // user: blinds the msg using signer's R msg := new(big.Int).SetBytes([]byte("test")) -msgBlinded, userSecretData, err := Blind(msg, signerR) +msgBlinded, userSecretData, err := blindsecp256k1.Blind(msg, signerR) require.Nil(t, err) // signer: signs the blinded message using its private key & secret k @@ -25,10 +32,10 @@ sBlind, err := sk.BlindSign(msgBlinded, k) require.Nil(t, err) // user: unblinds the blinded signature -sig := Unblind(sBlind, userSecretData) +sig := blindsecp256k1.Unblind(sBlind, userSecretData) // signature can be verified with signer PublicKey -verified := Verify(msg, sig, signerPubK) +verified := blindsecp256k1.Verify(msg, sig, signerPubK) assert.True(t, verified) ``` diff --git a/blindsecp256k1.go b/blindsecp256k1.go index 05c6342..587dffb 100644 --- a/blindsecp256k1.go +++ b/blindsecp256k1.go @@ -86,7 +86,7 @@ func (p *Point) isValid() error { return nil } -// Compress packs a Point to a byte array of 33 bytes +// Compress packs a Point to a byte array of 33 bytes, encoded in little-endian. func (p *Point) Compress() [33]byte { xBytes := p.X.Bytes() odd := byte(0) @@ -264,6 +264,30 @@ type Signature struct { F *Point } +// Compress packs a Signature to a byte array of 65 bytes, encoded in +// little-endian. +func (s *Signature) Compress() [65]byte { + var b [65]byte + sBytes := s.S.Bytes() + fBytes := s.F.Compress() + copy(b[:32], swapEndianness(sBytes)) + copy(b[32:], fBytes[:]) + return b +} + +// DecompressSignature unpacks a Signature from the given byte array of 65 bytes +func DecompressSignature(b [65]byte) (*Signature, error) { + s := new(big.Int).SetBytes(swapEndianness(b[:32])) + var fBytes [33]byte + copy(fBytes[:], b[32:]) + f, err := DecompressPoint(fBytes) + if err != nil { + return nil, err + } + sig := &Signature{S: s, F: f} + return sig, nil +} + // Unblind performs the unblinding operation of the blinded signature for the // given the UserSecretData func Unblind(sBlind *big.Int, u *UserSecretData) *Signature { diff --git a/blindsecp256k1_test.go b/blindsecp256k1_test.go index 44cba52..49aedef 100644 --- a/blindsecp256k1_test.go +++ b/blindsecp256k1_test.go @@ -107,6 +107,51 @@ func TestPointCompressDecompress(t *testing.T) { } } +func TestSignatureCompressDecompress(t *testing.T) { + f := G + sig := &Signature{ + S: big.NewInt(1), + F: f, + } + b := sig.Compress() + assert.Equal(t, + "01000000000000000000000000000000000000000000000000000000000000007"+ + "9be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800", + hex.EncodeToString(b[:])) + sig2, err := DecompressSignature(b) + require.Nil(t, err) + assert.Equal(t, sig, sig2) + + f = G + sig = &Signature{ + S: Q, + F: f, + } + b = sig.Compress() + assert.Equal(t, + "0cffffbfffffffffffffffffffffffffffffffffffffffffffffffffffffff3f7"+ + "9be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800", + hex.EncodeToString(b[:])) + sig2, err = DecompressSignature(b) + require.Nil(t, err) + require.Equal(t, sig, sig2) + + for i := 2; i < 10; i++ { + s := new(big.Int).Mod(new(big.Int).Mul(Q, big.NewInt(int64(i))), P) + f := G.Mul(big.NewInt(int64(i))) + sig := &Signature{ + S: s, + F: f, + } + b := sig.Compress() + assert.Equal(t, 65, len(b)) + + sig2, err := DecompressSignature(b) + require.Nil(t, err) + assert.Equal(t, sig, sig2) + } +} + func BenchmarkCompressDecompress(b *testing.B) { const n = 256 var points [n]*Point diff --git a/parsers.go b/parsers.go index 33014fc..9d34435 100644 --- a/parsers.go +++ b/parsers.go @@ -65,9 +65,9 @@ func (p *Point) Bytes() []byte { // little-endian. func NewPointFromBytes(b []byte) (*Point, error) { if len(b) != 64 { //nolint:gomnd - return nil, - fmt.Errorf("Can not parse bytes to Point, expected byte array of length %d, current %d", - 64, len(b)) + return nil, fmt.Errorf("Can not parse bytes to Point,"+ + " expected byte array of length %d, current %d", + 64, len(b)) } p := &Point{} p.X = new(big.Int).SetBytes(swapEndianness(b[:32])) @@ -193,7 +193,8 @@ func (sig *Signature) Bytes() []byte { func NewSignatureFromBytes(b []byte) (*Signature, error) { if len(b) != 96 { //nolint:gomnd return nil, - fmt.Errorf("Can not parse bytes to Signature, expected byte array of length %d, current %d", + fmt.Errorf("Can not parse bytes to Signature,"+ + " expected byte array of length %d, current %d", 96, len(b)) } s := new(big.Int).SetBytes(swapEndianness(b[:32]))