Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/cmw 587/resolve vulnerabilities #61

Merged
merged 3 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/make-software/casper-go-sdk
go 1.19

require (
github.com/btcsuite/btcd v0.22.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/shopspring/decimal v1.3.1
github.com/stretchr/testify v1.8.2
golang.org/x/crypto v0.9.0
Expand Down
7 changes: 4 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand Down
20 changes: 20 additions & 0 deletions tests/types/keypair/private_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,23 @@ func Test_SECPKey_CreateAndValidateRawSignature(t *testing.T) {
err = privateKeyData.PublicKey().VerifyRawSignature(secretMessage, signature)
assert.NoError(t, err)
}

func Test_ED25PKey_CreateAndValidateSignature(t *testing.T) {
secretMessage := []byte("Enigmatic Shadows Concealing Ancient Whispers")
privateKeyData, err := keypair.GeneratePrivateKey(keypair.ED25519)
require.NoError(t, err)
signature, err := privateKeyData.Sign(secretMessage)
require.NoError(t, err)
err = privateKeyData.PublicKey().VerifySignature(secretMessage, signature)
assert.NoError(t, err)
}

func Test_ED25Key_CreateAndValidateRawSignature(t *testing.T) {
secretMessage := []byte("Enigmatic Shadows Concealing Ancient Whispers")
privateKeyData, err := keypair.GeneratePrivateKey(keypair.ED25519)
require.NoError(t, err)
signature, err := privateKeyData.RawSign(secretMessage)
require.NoError(t, err)
err = privateKeyData.PublicKey().VerifyRawSignature(secretMessage, signature)
assert.NoError(t, err)
}
9 changes: 3 additions & 6 deletions types/keypair/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func (v PrivateKey) PublicKey() PublicKey {
}

// Sign creates a Casper compatible cryptographic signature, including the algorithm tag prefix
func (v PrivateKey) Sign(mes []byte) ([]byte, error) {
sign, err := v.priv.Sign(mes)
func (v PrivateKey) Sign(msg []byte) ([]byte, error) {
sign, err := v.priv.Sign(msg)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -83,13 +83,10 @@ func GeneratePrivateKey(algorithm keyAlgorithm) (PrivateKey, error) {
return PrivateKey{}, err
}
case SECP256K1:
data, _, err := secp256k1.NewPemPair()
priv, err = secp256k1.GeneratePrivateKey()
if err != nil {
return PrivateKey{}, err
}
if priv, err = secp256k1.NewPrivateKeyFromPem(data); err != nil {
return PrivateKey{}, err
}
}
publicKey, err := NewPublicKeyFromBytes(append([]byte{byte(algorithm)}, priv.PublicKeyBytes()...))
if err != nil {
Expand Down
76 changes: 15 additions & 61 deletions types/keypair/secp256k1/pem_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package secp256k1

import (
"crypto/elliptic"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"

"github.com/btcsuite/btcd/btcec"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
)

var oid = asn1.ObjectIdentifier{1, 3, 132, 0, 10}
Expand All @@ -20,13 +18,8 @@ type ecPrivateKey struct {
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
}

type pkixPublicKey struct {
Algo pkix.AlgorithmIdentifier
BitString asn1.BitString
}

func NewPemPair() ([]byte, []byte, error) {
priv, err := btcec.NewPrivateKey(btcec.S256())
priv, err := secp256k1.GeneratePrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("creating new S256 private key")
}
Expand All @@ -44,7 +37,15 @@ func NewPemPair() ([]byte, []byte, error) {
return privKeyPem, pubKeyPem, nil
}

func PrivateKeyToPem(priv *btcec.PrivateKey) ([]byte, error) {
func GeneratePrivateKey() (PrivateKey, error) {
priv, err := secp256k1.GeneratePrivateKey()
if err != nil {
return PrivateKey{}, fmt.Errorf("creating new S256 private key")
}
return PrivateKey{key: priv}, nil
}

func PrivateKeyToPem(priv *secp256k1.PrivateKey) ([]byte, error) {
key := priv.ToECDSA()

privateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8)
Expand All @@ -66,7 +67,7 @@ func PrivateKeyToPem(priv *btcec.PrivateKey) ([]byte, error) {
), nil
}

func PemToPrivateKey(priv []byte) (*btcec.PrivateKey, error) {
func PemToPrivateKey(priv []byte) (*secp256k1.PrivateKey, error) {
block, _ := pem.Decode(priv)
if block == nil {
return nil, fmt.Errorf("key not found")
Expand All @@ -80,61 +81,14 @@ func PemToPrivateKey(priv []byte) (*btcec.PrivateKey, error) {
return nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
}

curve := btcec.S256()

k := new(big.Int).SetBytes(privKey.PrivateKey)
curveOrder := curve.Params().N
if k.Cmp(curveOrder) >= 0 {
return nil, fmt.Errorf("x509: invalid elliptic curve private key value")
}

key := new(btcec.PrivateKey)
key.Curve = curve
key.D = k

privateKey := make([]byte, (curveOrder.BitLen()+7)/8)

for len(privKey.PrivateKey) > len(privateKey) {
if privKey.PrivateKey[0] != 0 {
return nil, fmt.Errorf("x509: invalid private key length")
}
privKey.PrivateKey = privKey.PrivateKey[1:]
}

copy(privateKey[len(privateKey)-len(privKey.PrivateKey):], privKey.PrivateKey)
key.X, key.Y = curve.ScalarBaseMult(privateKey)

return key, nil
return secp256k1.PrivKeyFromBytes(privKey.PrivateKey), nil
}

func PublicKeyToPem(pub *btcec.PublicKey) ([]byte, error) {
pubEDSA := pub.ToECDSA()

var publicKeyAlgorithm pkix.AlgorithmIdentifier

publicKeyBytes := elliptic.Marshal(pubEDSA.Curve, pubEDSA.X, pubEDSA.Y)

publicKeyAlgorithm.Algorithm = oid
var paramBytes []byte
paramBytes, err := asn1.Marshal(oid)
if err != nil {
return nil, err
}

publicKeyAlgorithm.Parameters.FullBytes = paramBytes

pubBytes, _ := asn1.Marshal(pkixPublicKey{
Algo: publicKeyAlgorithm,
BitString: asn1.BitString{
Bytes: publicKeyBytes,
BitLength: 8 * len(publicKeyBytes),
},
})

func PublicKeyToPem(pub *secp256k1.PublicKey) ([]byte, error) {
return pem.EncodeToMemory(
&pem.Block{
Type: "EC PUBLIC KEY",
Bytes: pubBytes,
Bytes: pub.SerializeUncompressed(),
},
), nil
}
25 changes: 5 additions & 20 deletions types/keypair/secp256k1/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import (
"errors"
"os"

"github.com/btcsuite/btcd/btcec"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
)

type PrivateKey struct {
key *btcec.PrivateKey
key *secp256k1.PrivateKey
}

func (v PrivateKey) PublicKeyBytes() []byte {
Expand All @@ -18,12 +19,8 @@ func (v PrivateKey) PublicKeyBytes() []byte {

func (v PrivateKey) Sign(mes []byte) ([]byte, error) {
hash := sha256.Sum256(mes)
sig, err := v.key.Sign(hash[:])
if err != nil {
return nil, err
}

return serializeSig(sig), nil
// Return the signature as a concatenation of the R and S values in big-endian to match the old signature format.
return ecdsa.SignCompact(v.key, hash[:], false)[1:], nil
}

func NewPrivateKeyFromPemFile(path string) (PrivateKey, error) {
Expand All @@ -44,15 +41,3 @@ func NewPrivateKeyFromPem(content []byte) (PrivateKey, error) {
key: private,
}, nil
}

// Serialize signature to R || S.
// R, S are padded to 32 bytes respectively.
func serializeSig(sig *btcec.Signature) []byte {
rBytes := sig.R.Bytes()
sBytes := sig.S.Bytes()
sigBytes := make([]byte, 64)
// 0 pad the byte arrays from the left if they aren't big enough.
copy(sigBytes[32-len(rBytes):32], rBytes)
copy(sigBytes[64-len(sBytes):64], sBytes)
return sigBytes
}
71 changes: 31 additions & 40 deletions types/keypair/secp256k1/public_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,66 +3,57 @@ package secp256k1
import (
"crypto/sha256"
"fmt"
"math/big"
"log"

"github.com/btcsuite/btcd/btcec"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
)

const PublicKeySize = 33

// used to reject malleable signatures
// see:
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39
var secp256k1halfN = new(big.Int).Rsh(btcec.S256().N, 1)

type PublicKey btcec.PublicKey
type PublicKey struct {
key *secp256k1.PublicKey
Volodymyr-Kuchinskyi marked this conversation as resolved.
Show resolved Hide resolved
}

func (v PublicKey) Bytes() []byte {
key := btcec.PublicKey(v)
return key.SerializeCompressed()
return v.key.SerializeCompressed()
}

// VerifySignature verifies a signature of the form R || S.
// It rejects signatures which are not in lower-S form.
func (v PublicKey) VerifySignature(msg []byte, sigStr []byte) bool {
if len(sigStr) != 64 {
return false
}

pub, err := btcec.ParsePubKey(v.Bytes(), btcec.S256())
if err != nil {
return false
}

// parse the signature:
signature := signatureFromBytes(sigStr)
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
// see: https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93
if signature.S.Cmp(secp256k1halfN) > 0 {
return false
}

sum256 := sha256.Sum256(msg)
return signature.Verify(sum256[:], pub)
}

// Read Signature struct from R || S. Caller needs to ensure
// that len(sigStr) == 64.
func signatureFromBytes(sigStr []byte) *btcec.Signature {
return &btcec.Signature{
R: new(big.Int).SetBytes(sigStr[:32]),
S: new(big.Int).SetBytes(sigStr[32:64]),
var signature *ecdsa.Signature
var err error
// if old signature len = 64, parse it as raw signature
if len(sigStr) == 64 {
// Split the signature bytes into r and s values and parse them into ModNScalar
var r, s secp256k1.ModNScalar
var bytesR [32]byte
var bytesS [32]byte
copy(bytesR[:], sigStr[:32])
copy(bytesS[:], sigStr[32:])
r.SetBytes(&bytesR)
s.SetBytes(&bytesS)

signature = ecdsa.NewSignature(&r, &s)
} else {
signature, err = ecdsa.ParseDERSignature(sigStr)
if err != nil {
log.Println(err)
return false
}
}
hash := sha256.Sum256(msg)
return signature.Verify(hash[:], v.key)
}

func NewPublicKey(data []byte) (PublicKey, error) {
if len(data) != PublicKeySize {
return PublicKey{}, fmt.Errorf("can't parse wrong size of public key: %d", len(data))
}
key, err := btcec.ParsePubKey(data, btcec.S256())
key, err := secp256k1.ParsePubKey(data)
if err != nil {
return PublicKey{}, err
}
return PublicKey(*key), err
return PublicKey{key: key}, err
}