Skip to content

Commit

Permalink
Add utils for compact RSV encoding/decoding
Browse files Browse the repository at this point in the history
Signed-off-by: Peter Broadhurst <peter.broadhurst@kaleido.io>
  • Loading branch information
peterbroadhurst committed Aug 14, 2024
1 parent c2aa0ba commit 7c2d912
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 2 deletions.
1 change: 1 addition & 0 deletions internal/signermsgs/en_error_messges.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,5 @@ var (
MsgInvalidEIP1559Transaction = ffe("FF22084", "Transaction payload invalid (EIP-1559): %v")
MsgInvalidEIP155TransactionV = ffe("FF22085", "Invalid V value from EIP-155 transaction (chainId=%d)")
MsgInvalidChainID = ffe("FF22086", "Invalid chainId expected=%d actual=%d")
MsgSigningInvalidCompactRSV = ffe("FF22087", "Invalid signature data (compact R,S,V) length=%d (expected=65)")
)
3 changes: 1 addition & 2 deletions pkg/secp256k1/keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ func GenerateSecp256k1KeyPair() (*KeyPair, error) {

// Deprecated: Note there is no error condition returned by this function (use KeyPairFromBytes)
func NewSecp256k1KeyPair(b []byte) (*KeyPair, error) {
key, pubKey := btcec.PrivKeyFromBytes(b)
return wrapSecp256k1Key(key, pubKey), nil
return KeyPairFromBytes(b), nil
}

func KeyPairFromBytes(b []byte) *KeyPair {
Expand Down
11 changes: 11 additions & 0 deletions pkg/secp256k1/keypair_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package secp256k1

import (
"context"
"math/big"
"testing"

Expand Down Expand Up @@ -57,6 +58,16 @@ func TestGeneratedKeyRoundTrip(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, keypair.Address, *addr)

sigRSV := sig.CompactRSV()
sig2, err := DecodeCompactRSV(context.Background(), sigRSV)
assert.NoError(t, err)
addr, err = sig2.Recover(data, 1001)
assert.NoError(t, err)
assert.Equal(t, keypair.Address, *addr)

_, err = DecodeCompactRSV(context.Background(), []byte("wrong"))
assert.Regexp(t, "FF22087", err)

_, err = sig.Recover(data, 42)
assert.Regexp(t, "invalid V value in signature", err)

Expand Down
23 changes: 23 additions & 0 deletions pkg/secp256k1/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
package secp256k1

import (
"context"
"fmt"
"math/big"

ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/hyperledger/firefly-common/pkg/i18n"
"github.com/hyperledger/firefly-signer/internal/signermsgs"
"github.com/hyperledger/firefly-signer/pkg/ethtypes"
"golang.org/x/crypto/sha3"
)
Expand Down Expand Up @@ -99,6 +102,26 @@ func (s *SignatureData) RecoverDirect(message []byte, chainID int64) (a *ethtype
return PublicKeyToAddress(pubKey), nil
}

// We use the ethereum convention of R,S,V for compact packing (mentioned because Golang tends to prefer V,R,S)
func (s *SignatureData) CompactRSV() []byte {
signatureBytes := make([]byte, 65)
s.R.FillBytes(signatureBytes[0:32])
s.S.FillBytes(signatureBytes[32:64])
signatureBytes[64] = byte(s.V.Int64())
return signatureBytes
}

func DecodeCompactRSV(ctx context.Context, compactRSV []byte) (*SignatureData, error) {
if len(compactRSV) != 65 {
return nil, i18n.NewError(ctx, signermsgs.MsgSigningInvalidCompactRSV, len(compactRSV))
}
var sig SignatureData
sig.R = new(big.Int).SetBytes(compactRSV[0:32])
sig.S = new(big.Int).SetBytes(compactRSV[32:64])
sig.V = new(big.Int).SetBytes(compactRSV[64:65])
return &sig, nil
}

// Sign hashes the input then signs it
func (k *KeyPair) Sign(message []byte) (ethSig *SignatureData, err error) {
msgHash := sha3.NewLegacyKeccak256()
Expand Down

0 comments on commit 7c2d912

Please sign in to comment.