Files
next.orly.dev/pkg/crypto/ec/secp256k1/seckey_test.go

180 lines
5.4 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2023 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package secp256k1
import (
"bytes"
"crypto/rand"
"errors"
"math/big"
"testing"
"utils.orly"
)
// TestGenerateSecretKey ensures the key generation works as expected.
func TestGenerateSecretKey(t *testing.T) {
sec, err := GenerateSecretKey()
if err != nil {
t.Errorf("failed to generate secret key: %s", err)
return
}
pub := sec.PubKey()
if !isOnCurve(&pub.x, &pub.y) {
t.Error("public key is not on the curve")
}
}
// TestGenerateSecretKeyFromRand ensures generating a secret key from a random
// entropy source works as expected.
func TestGenerateSecretKeyFromRand(t *testing.T) {
sec, err := GenerateSecretKeyFromRand(rand.Reader)
if err != nil {
t.Errorf("failed to generate secret key: %s", err)
return
}
pub := sec.PubKey()
if !isOnCurve(&pub.x, &pub.y) {
t.Error("public key is not on the curve")
}
}
// mockSecretKeyReaderFunc is an adapter to allow the use of an ordinary
// function as an io.Reader.
type mockSecretKeyReaderFunc func([]byte) (int, error)
// Read calls the function with the provided parameter and returns the result.
func (f mockSecretKeyReaderFunc) Read(p []byte) (int, error) {
return f(p)
}
// TestGenerateSecretKeyCorners ensures random values that secret key
// generation correctly handles entropy values that are invalid for use as
// secret keys by creating a fake source of randomness to inject known bad
// values.
func TestGenerateSecretKeyCorners(t *testing.T) {
// Create a mock reader that returns the following sequence of values:
// 1st invocation: 0
// 2nd invocation: The curve order
// 3rd invocation: The curve order + 1
// 4th invocation: 1 (32-byte big endian)
oneModN := hexToModNScalar("01")
var numReads int
mockReader := mockSecretKeyReaderFunc(
func(p []byte) (int, error) {
numReads++
switch numReads {
case 1:
return copy(p, bytes.Repeat([]byte{0x00}, len(p))), nil
case 2:
return copy(p, curveParams.N.Bytes()), nil
case 3:
nPlusOne := new(big.Int).Add(curveParams.N, big.NewInt(1))
return copy(p, nPlusOne.Bytes()), nil
}
oneModNBytes := oneModN.Bytes()
return copy(p, oneModNBytes[:]), nil
},
)
// Generate a secret key using the mock reader and ensure the resulting key
// is the expected one. It should be the value "1" since the other values
// the sequence produces are invalid and thus should be rejected.
sec, err := GenerateSecretKeyFromRand(mockReader)
if err != nil {
t.Errorf("failed to generate secret key: %s", err)
return
}
if !sec.Key.Equals(oneModN) {
t.Fatalf(
"unexpected secret key -- got: %x, want %x", sec.Serialize(),
oneModN.Bytes(),
)
}
}
// TestGenerateSecretKeyError ensures the secret key generation properly
// handles errors when attempting to read from the source of randomness.
func TestGenerateSecretKeyError(t *testing.T) {
// Create a mock reader that returns an error.
errDisabled := errors.New("disabled")
mockReader := mockSecretKeyReaderFunc(
func(p []byte) (int, error) {
return 0, errDisabled
},
)
// Generate a secret key using the mock reader and ensure the expected
// error is returned.
_, err := GenerateSecretKeyFromRand(mockReader)
if !errors.Is(err, errDisabled) {
t.Fatalf("mismatched err -- got %v, want %v", err, errDisabled)
return
}
}
// TestSecKeys ensures a secret key created from bytes produces both the
// correct associated public key as well serializes back to the original bytes.
func TestSecKeys(t *testing.T) {
tests := []struct {
name string
sec string // hex encoded secret key to test
pub string // expected hex encoded serialized compressed public key
}{
{
name: "random secret key 1",
sec: "eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694",
pub: "025ceeba2ab4a635df2c0301a3d773da06ac5a18a7c3e0d09a795d7e57d233edf1",
}, {
name: "random secret key 2",
sec: "24b860d0651db83feba821e7a94ba8b87162665509cefef0cbde6a8fbbedfe7c",
pub: "032a6e51bf218085647d330eac2fafaeee07617a777ad9e8e7141b4cdae92cb637",
},
}
for _, test := range tests {
// Parse test data.
secKeyBytes := hexToBytes(test.sec)
wantPubKeyBytes := hexToBytes(test.pub)
sec := SecKeyFromBytes(secKeyBytes)
pub := sec.PubKey()
serializedPubKey := pub.SerializeCompressed()
if !utils.FastEqual(serializedPubKey, wantPubKeyBytes) {
t.Errorf(
"%s unexpected serialized public key - got: %x, want: %x",
test.name, serializedPubKey, wantPubKeyBytes,
)
}
serializedSecKey := sec.Serialize()
if !utils.FastEqual(serializedSecKey, secKeyBytes) {
t.Errorf(
"%s unexpected serialized secret key - got: %x, want: %x",
test.name, serializedSecKey, secKeyBytes,
)
}
}
}
// TestSecretKeyZero ensures that zeroing a secret key clears the memory
// associated with it.
func TestSecretKeyZero(t *testing.T) {
// Create a new secret key and zero the initial key material that is now
// copied into the secret key.
key := new(ModNScalar).SetHex("eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694")
secKey := NewSecretKey(key)
key.Zero()
// Ensure the secret key is non zero.
if secKey.Key.IsZero() {
t.Fatal("secret key is zero when it should be non zero")
}
// Zero the secret key and ensure it was properly zeroed.
secKey.Zero()
if !secKey.Key.IsZero() {
t.Fatal("secret key is non zero when it should be zero")
}
}