Skip to content

Commit

Permalink
secp256k1: Add GeneratePrivateKeyFromRand tests.
Browse files Browse the repository at this point in the history
This adds tests for the newly added GeneratePrivateKeyFromRand func to
restore test coverage.

It also takes advantage of the ability to specify the random source to
inject faults to improve the test coverage of private key generation in
general to achieve 100% branch coverage.
  • Loading branch information
davecgh committed Apr 13, 2023
1 parent bd0b82d commit 6c5b953
Showing 1 changed file with 85 additions and 0 deletions.
85 changes: 85 additions & 0 deletions dcrec/secp256k1/privkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ package secp256k1

import (
"bytes"
cryptorand "crypto/rand"
"errors"
"math/big"
"testing"
)

Expand All @@ -23,6 +26,88 @@ func TestGeneratePrivateKey(t *testing.T) {
}
}

// TestGeneratePrivateKeyFromRand ensures generating a private key from a random
// entropy source works as expected.
func TestGeneratePrivateKeyFromRand(t *testing.T) {
priv, err := GeneratePrivateKeyFromRand(cryptorand.Reader)
if err != nil {
t.Errorf("failed to generate private key: %s", err)
return
}
pub := priv.PubKey()
if !isOnCurve(&pub.x, &pub.y) {
t.Error("public key is not on the curve")
}
}

// mockPrivateKeyReaderFunc is an adapter to allow the use of an ordinary
// function as an io.Reader.
type mockPrivateKeyReaderFunc func([]byte) (int, error)

// Read calls the function with the provided parameter and returns the result.
func (f mockPrivateKeyReaderFunc) Read(p []byte) (int, error) {
return f(p)
}

// TestGeneratePrivateKeyCorners ensures random values that private key
// generation correctly handles entropy values that are invalid for use as
// private keys by creating a fake source of randomness to inject known bad
// values.
func TestGeneratePrivateKeyCorners(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 := mockPrivateKeyReaderFunc(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 private 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.
priv, err := GeneratePrivateKeyFromRand(mockReader)
if err != nil {
t.Errorf("failed to generate private key: %s", err)
return
}
if !priv.Key.Equals(oneModN) {
t.Fatalf("unexpected private key -- got: %x, want %x", priv.Serialize(),
oneModN.Bytes())
}
}

// TestGeneratePrivateKeyError ensures the private key generation properly
// handles errors when attempting to read from the source of randomness.
func TestGeneratePrivateKeyError(t *testing.T) {
// Create a mock reader that returns an error.
errDisabled := errors.New("disabled")
mockReader := mockPrivateKeyReaderFunc(func(p []byte) (int, error) {
return 0, errDisabled
})

// Generate a private key using the mock reader and ensure the expected
// error is returned.
_, err := GeneratePrivateKeyFromRand(mockReader)
if !errors.Is(err, errDisabled) {
t.Fatalf("mismatched err -- got %v, want %v", err, errDisabled)
return
}
}

// TestPrivKeys ensures a private key created from bytes produces both the
// correct associated public key as well serializes back to the original bytes.
func TestPrivKeys(t *testing.T) {
Expand Down

0 comments on commit 6c5b953

Please sign in to comment.