Skip to content

Commit

Permalink
btcec: add deep copy method for public keys
Browse files Browse the repository at this point in the history
  • Loading branch information
ziggie1984 committed Dec 11, 2024
1 parent 67b8efd commit d0c01bb
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
4 changes: 3 additions & 1 deletion btcec/field.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package btcec

import secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
import (
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
)

// FieldVal implements optimized fixed-precision arithmetic over the secp256k1
// finite field. This means all arithmetic is performed modulo
Expand Down
9 changes: 9 additions & 0 deletions btcec/pubkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ func NewPublicKey(x, y *FieldVal) *PublicKey {
return secp.NewPublicKey(x, y)
}

// CopyPublicKey returns a deep copy of the public key.
func CopyPublicKey(pk *PublicKey) *PublicKey {
var result secp.JacobianPoint
pk.AsJacobian(&result)
result.ToAffine()

return NewPublicKey(&result.X, &result.Y)
}

// SerializedKey is a type for representing a public key in its compressed
// serialized form.
//
Expand Down
60 changes: 60 additions & 0 deletions btcec/pubkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ package btcec

import (
"bytes"
"math/rand"
"testing"
"testing/quick"

"github.com/davecgh/go-spew/spew"
)
Expand Down Expand Up @@ -292,3 +294,61 @@ func TestIsCompressed(t *testing.T) {
}
}
}

// TestCopyPublicKeyProperties tests that the CopyPublicKey function copies the
// original public key and that the copy is equal to the original.
func TestCopyPublicKeyProperties(t *testing.T) {
f := func(x, y [32]byte) bool {
// Convert byte slices to FieldVals.
xf := new(FieldVal)
yf := new(FieldVal)
if overflow := xf.SetByteSlice(x[:]); overflow {
// Only check the function for valid coordinates.
return true
}

if overflow := xf.SetByteSlice(y[:]); overflow {
// Only check the function for valid coordinates.
return true
}

// Create an original public key.
original := NewPublicKey(xf, yf)
if original == nil {
// Skip invalid inputs.
return true
}

// Make a copy of the original public key.
copied := CopyPublicKey(original)
if copied == nil {
// copy should succeed if original was valid.
return false
}

// The copy should not be the same instance.
if original == copied {
return false
}

// The copy should be equal to the original
// First check the serialized forms.
originalBytes := original.SerializeCompressed()
copiedBytes := copied.SerializeCompressed()
if !bytes.Equal(originalBytes, copiedBytes) {
return false
}

return original.IsEqual(copied)
}

// Create a deterministic random source using a fixed seed.
rng := rand.New(rand.NewSource(42))
config := &quick.Config{
Rand: rng,
}

if err := quick.Check(f, config); err != nil {
t.Error(err)
}
}

0 comments on commit d0c01bb

Please sign in to comment.