seckey_test.go raw

   1  // Copyright (c) 2013-2016 The btcsuite developers
   2  // Copyright (c) 2015-2023 The Decred developers
   3  // Use of this source code is governed by an ISC
   4  // license that can be found in the LICENSE file.
   5  
   6  package secp256k1
   7  
   8  import (
   9  	"bytes"
  10  	"crypto/rand"
  11  	"errors"
  12  	"math/big"
  13  	"testing"
  14  
  15  	"next.orly.dev/pkg/nostr/utils"
  16  )
  17  
  18  // TestGenerateSecretKey ensures the key generation works as expected.
  19  func TestGenerateSecretKey(t *testing.T) {
  20  	sec, err := GenerateSecretKey()
  21  	if err != nil {
  22  		t.Errorf("failed to generate secret key: %s", err)
  23  		return
  24  	}
  25  	pub := sec.PubKey()
  26  	if !isOnCurve(&pub.x, &pub.y) {
  27  		t.Error("public key is not on the curve")
  28  	}
  29  }
  30  
  31  // TestGenerateSecretKeyFromRand ensures generating a secret key from a random
  32  // entropy source works as expected.
  33  func TestGenerateSecretKeyFromRand(t *testing.T) {
  34  	sec, err := GenerateSecretKeyFromRand(rand.Reader)
  35  	if err != nil {
  36  		t.Errorf("failed to generate secret key: %s", err)
  37  		return
  38  	}
  39  	pub := sec.PubKey()
  40  	if !isOnCurve(&pub.x, &pub.y) {
  41  		t.Error("public key is not on the curve")
  42  	}
  43  }
  44  
  45  // mockSecretKeyReaderFunc is an adapter to allow the use of an ordinary
  46  // function as an io.Reader.
  47  type mockSecretKeyReaderFunc func([]byte) (int, error)
  48  
  49  // Read calls the function with the provided parameter and returns the result.
  50  func (f mockSecretKeyReaderFunc) Read(p []byte) (int, error) {
  51  	return f(p)
  52  }
  53  
  54  // TestGenerateSecretKeyCorners ensures random values that secret key
  55  // generation correctly handles entropy values that are invalid for use as
  56  // secret keys by creating a fake source of randomness to inject known bad
  57  // values.
  58  func TestGenerateSecretKeyCorners(t *testing.T) {
  59  	// Create a mock reader that returns the following sequence of values:
  60  	// 1st invocation: 0
  61  	// 2nd invocation: The curve order
  62  	// 3rd invocation: The curve order + 1
  63  	// 4th invocation: 1 (32-byte big endian)
  64  	oneModN := hexToModNScalar("01")
  65  	var numReads int
  66  	mockReader := mockSecretKeyReaderFunc(
  67  		func(p []byte) (int, error) {
  68  			numReads++
  69  			switch numReads {
  70  			case 1:
  71  				return copy(p, bytes.Repeat([]byte{0x00}, len(p))), nil
  72  			case 2:
  73  				return copy(p, curveParams.N.Bytes()), nil
  74  			case 3:
  75  				nPlusOne := new(big.Int).Add(curveParams.N, big.NewInt(1))
  76  				return copy(p, nPlusOne.Bytes()), nil
  77  			}
  78  			oneModNBytes := oneModN.Bytes()
  79  			return copy(p, oneModNBytes[:]), nil
  80  		},
  81  	)
  82  	// Generate a secret key using the mock reader and ensure the resulting key
  83  	// is the expected one.  It should be the value "1" since the other values
  84  	// the sequence produces are invalid and thus should be rejected.
  85  	sec, err := GenerateSecretKeyFromRand(mockReader)
  86  	if err != nil {
  87  		t.Errorf("failed to generate secret key: %s", err)
  88  		return
  89  	}
  90  	if !sec.Key.Equals(oneModN) {
  91  		t.Fatalf(
  92  			"unexpected secret key -- got: %x, want %x", sec.Serialize(),
  93  			oneModN.Bytes(),
  94  		)
  95  	}
  96  }
  97  
  98  // TestGenerateSecretKeyError ensures the secret key generation properly
  99  // handles errors when attempting to read from the source of randomness.
 100  func TestGenerateSecretKeyError(t *testing.T) {
 101  	// Create a mock reader that returns an error.
 102  	errDisabled := errors.New("disabled")
 103  	mockReader := mockSecretKeyReaderFunc(
 104  		func(p []byte) (int, error) {
 105  			return 0, errDisabled
 106  		},
 107  	)
 108  	// Generate a secret key using the mock reader and ensure the expected
 109  	// error is returned.
 110  	_, err := GenerateSecretKeyFromRand(mockReader)
 111  	if !errors.Is(err, errDisabled) {
 112  		t.Fatalf("mismatched err -- got %v, want %v", err, errDisabled)
 113  		return
 114  	}
 115  }
 116  
 117  // TestSecKeys ensures a secret key created from bytes produces both the
 118  // correct associated public key as well serializes back to the original bytes.
 119  func TestSecKeys(t *testing.T) {
 120  	tests := []struct {
 121  		name string
 122  		sec  string // hex encoded secret key to test
 123  		pub  string // expected hex encoded serialized compressed public key
 124  	}{
 125  		{
 126  			name: "random secret key 1",
 127  			sec:  "eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694",
 128  			pub:  "025ceeba2ab4a635df2c0301a3d773da06ac5a18a7c3e0d09a795d7e57d233edf1",
 129  		}, {
 130  			name: "random secret key 2",
 131  			sec:  "24b860d0651db83feba821e7a94ba8b87162665509cefef0cbde6a8fbbedfe7c",
 132  			pub:  "032a6e51bf218085647d330eac2fafaeee07617a777ad9e8e7141b4cdae92cb637",
 133  		},
 134  	}
 135  
 136  	for _, test := range tests {
 137  		// Parse test data.
 138  		secKeyBytes := hexToBytes(test.sec)
 139  		wantPubKeyBytes := hexToBytes(test.pub)
 140  
 141  		sec := SecKeyFromBytes(secKeyBytes)
 142  		pub := sec.PubKey()
 143  
 144  		serializedPubKey := pub.SerializeCompressed()
 145  		if !utils.FastEqual(serializedPubKey, wantPubKeyBytes) {
 146  			t.Errorf(
 147  				"%s unexpected serialized public key - got: %x, want: %x",
 148  				test.name, serializedPubKey, wantPubKeyBytes,
 149  			)
 150  		}
 151  
 152  		serializedSecKey := sec.Serialize()
 153  		if !utils.FastEqual(serializedSecKey, secKeyBytes) {
 154  			t.Errorf(
 155  				"%s unexpected serialized secret key - got: %x, want: %x",
 156  				test.name, serializedSecKey, secKeyBytes,
 157  			)
 158  		}
 159  	}
 160  }
 161  
 162  // TestSecretKeyZero ensures that zeroing a secret key clears the memory
 163  // associated with it.
 164  func TestSecretKeyZero(t *testing.T) {
 165  	// Create a new secret key and zero the initial key material that is now
 166  	// copied into the secret key.
 167  	key := new(ModNScalar).SetHex("eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694")
 168  	secKey := NewSecretKey(key)
 169  	key.Zero()
 170  	// Ensure the secret key is non zero.
 171  	if secKey.Key.IsZero() {
 172  		t.Fatal("secret key is zero when it should be non zero")
 173  	}
 174  	// Zero the secret key and ensure it was properly zeroed.
 175  	secKey.Zero()
 176  	if !secKey.Key.IsZero() {
 177  		t.Fatal("secret key is non zero when it should be zero")
 178  	}
 179  }
 180