From dcaf7215757f1aa025efff847e8d78cbedefc80f Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 28 Jun 2026 04:21:36 -0500 Subject: [PATCH 1/6] secp256k1: Add doc links for symbols. This updates the code to use the more modern documentation links when referencing other methods and symbols. --- dcrec/secp256k1/curve.go | 4 +-- dcrec/secp256k1/doc.go | 16 ++++----- dcrec/secp256k1/ecdsa/doc.go | 12 +++---- dcrec/secp256k1/ellipticadaptor.go | 4 +-- dcrec/secp256k1/error.go | 12 +++---- dcrec/secp256k1/example_test.go | 8 ++--- dcrec/secp256k1/field.go | 56 ++++++++++++++--------------- dcrec/secp256k1/field_bench_test.go | 2 +- dcrec/secp256k1/field_test.go | 46 ++++++++++++------------ dcrec/secp256k1/modnscalar.go | 27 +++++++------- dcrec/secp256k1/pubkey.go | 8 ++--- 11 files changed, 98 insertions(+), 97 deletions(-) diff --git a/dcrec/secp256k1/curve.go b/dcrec/secp256k1/curve.go index 6d6d669f19..4280921c96 100644 --- a/dcrec/secp256k1/curve.go +++ b/dcrec/secp256k1/curve.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2024 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Copyright 2013-2014 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -1246,7 +1246,7 @@ var jacobianG = func() JacobianPoint { return G }() -// scalarBaseMultNonConstSlow computes k*G through ScalarMultNonConst. +// scalarBaseMultNonConstSlow computes k*G through [ScalarMultNonConst]. func scalarBaseMultNonConstSlow(k *ModNScalar, result *JacobianPoint) { ScalarMultNonConst(k, &jacobianG, result) } diff --git a/dcrec/secp256k1/doc.go b/dcrec/secp256k1/doc.go index ac01e2343c..d946650d73 100644 --- a/dcrec/secp256k1/doc.go +++ b/dcrec/secp256k1/doc.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2014 The btcsuite developers -// Copyright (c) 2015-2022 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -24,8 +24,8 @@ An overview of the features provided by this package are as follows: - Parses uncompressed, compressed, and hybrid public keys - Serializes uncompressed and compressed public keys - Specialized types for performing optimized and constant time field operations - - FieldVal type for working modulo the secp256k1 field prime - - ModNScalar type for working modulo the secp256k1 group order + - [FieldVal] type for working modulo the secp256k1 field prime + - [ModNScalar] type for working modulo the secp256k1 group order - Elliptic curve operations in Jacobian projective coordinates - Point addition - Point doubling @@ -37,11 +37,11 @@ An overview of the features provided by this package are as follows: algorithms It also provides an implementation of the Go standard library crypto/elliptic -Curve interface via the S256 function so that it may be used with other packages -in the standard library such as crypto/tls, crypto/x509, and crypto/ecdsa. -However, in the case of ECDSA, it is highly recommended to use the ecdsa sub -package of this package instead since it is optimized specifically for secp256k1 -and is significantly faster as a result. +Curve interface via the [S256] function so that it may be used with other +packages in the standard library such as crypto/tls, crypto/x509, and +crypto/ecdsa. However, in the case of ECDSA, it is highly recommended to use +the ecdsa sub package of this package instead since it is optimized specifically +for secp256k1 and is significantly faster as a result. Although this package was primarily written for dcrd, it has intentionally been designed so it can be used as a standalone package for any projects needing to diff --git a/dcrec/secp256k1/ecdsa/doc.go b/dcrec/secp256k1/ecdsa/doc.go index 18d12c5266..b3765231a3 100644 --- a/dcrec/secp256k1/ecdsa/doc.go +++ b/dcrec/secp256k1/ecdsa/doc.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 The Decred developers +// Copyright (c) 2020-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -33,10 +33,10 @@ key without actually revealing it. # Errors The errors returned by this package are of type ecdsa.Error and fully support -the standard library errors.Is and errors.As functions. This allows the caller -to programmatically determine the specific error by examining the ErrorKind -field of the type asserted ecdsa.Error while still providing rich error messages -with contextual information. See ErrorKind in the package documentation for a -full list. +the standard library [errors.Is] and [errors.As] functions. This allows the +caller to programmatically determine the specific error by examining the +ErrorKind field of the type asserted ecdsa.Error while still providing rich +error messages with contextual information. See [ErrorKind] in the package +documentation for a full list. */ package ecdsa diff --git a/dcrec/secp256k1/ellipticadaptor.go b/dcrec/secp256k1/ellipticadaptor.go index 1d22f0a891..1b7719fc5c 100644 --- a/dcrec/secp256k1/ellipticadaptor.go +++ b/dcrec/secp256k1/ellipticadaptor.go @@ -286,9 +286,9 @@ var secp256k1 = &KoblitzCurve{ }, } -// S256 returns an elliptic.Curve which implements secp256k1. +// S256 returns an [elliptic.Curve] which implements secp256k1. // -// Deprecated: The standard library elliptic.Curve interface is now deprecated +// Deprecated: The standard library [elliptic.Curve] interface is now deprecated // and callers should interact with the safer, and much faster, specialized // methods instead. func S256() *KoblitzCurve { diff --git a/dcrec/secp256k1/error.go b/dcrec/secp256k1/error.go index ac8c45127e..9093576229 100644 --- a/dcrec/secp256k1/error.go +++ b/dcrec/secp256k1/error.go @@ -1,15 +1,15 @@ -// Copyright (c) 2020 The Decred developers +// Copyright (c) 2020-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package secp256k1 -// ErrorKind identifies a kind of error. It has full support for errors.Is and -// errors.As, so the caller can directly check against an error kind when +// ErrorKind identifies a kind of error. It has full support for [errors.Is] +// and [errors.As], so the caller can directly check against an error kind when // determining the reason for an error. type ErrorKind string -// These constants are used to identify a specific RuleError. +// These constants are used to identify a specific [Error]. const ( // ErrPubKeyInvalidLen indicates that the length of a serialized public // key is not one of the allowed lengths. @@ -43,7 +43,7 @@ func (e ErrorKind) Error() string { } // Error identifies an error related to public key cryptography using a -// sec256k1 curve. It has full support for errors.Is and errors.As, so the +// sec256k1 curve. It has full support for [errors.Is] and [errors.As], so the // caller can ascertain the specific reason for the error by checking // the underlying error. type Error struct { @@ -61,7 +61,7 @@ func (e Error) Unwrap() error { return e.Err } -// makeError creates an Error given a set of arguments. +// makeError creates an [Error] given a set of arguments. func makeError(kind ErrorKind, desc string) Error { return Error{Err: kind, Description: desc} } diff --git a/dcrec/secp256k1/example_test.go b/dcrec/secp256k1/example_test.go index f74b4421e3..337e91c2dd 100644 --- a/dcrec/secp256k1/example_test.go +++ b/dcrec/secp256k1/example_test.go @@ -1,5 +1,5 @@ // Copyright (c) 2014 The btcsuite developers -// Copyright (c) 2015-2020 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -16,9 +16,9 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" ) -// This example demonstrates use of GenerateSharedSecret to encrypt a message -// for a recipient's public key, and subsequently decrypt the message using the -// recipient's private key. +// This example demonstrates use of [secp256k1.GenerateSharedSecret] to encrypt +// a message for a recipient's public key, and subsequently decrypt the message +// using the recipient's private key. func Example_encryptDecryptMessage() { newAEAD := func(key []byte) (cipher.AEAD, error) { block, err := aes.NewCipher(key) diff --git a/dcrec/secp256k1/field.go b/dcrec/secp256k1/field.go index f979bb2efe..e877264771 100644 --- a/dcrec/secp256k1/field.go +++ b/dcrec/secp256k1/field.go @@ -1,6 +1,6 @@ // Copyright (c) 2013-2014 The btcsuite developers -// Copyright (c) 2015-2024 The Decred developers -// Copyright (c) 2013-2024 Dave Collins +// Copyright (c) 2015-2026 The Decred developers +// Copyright (c) 2013-2026 Dave Collins // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -49,7 +49,7 @@ package secp256k1 // // Since it is so important that the field arithmetic is extremely fast for high // performance crypto, this type does not perform any validation where it -// ordinarily would. See the documentation for FieldVal for more details. +// ordinarily would. See the documentation for [FieldVal] for more details. import ( "encoding/hex" @@ -234,10 +234,10 @@ func (f *FieldVal) SetInt(ui uint16) *FieldVal { } // SetBytes packs the passed 32-byte big-endian value into the internal field -// value representation in constant time. SetBytes interprets the provided -// array as a 256-bit big-endian unsigned integer, packs it into the internal -// field value representation, and returns either 1 if it is greater than or -// equal to the field prime (aka it overflowed) or 0 otherwise in constant time. +// value representation in constant time. It interprets the provided array as a +// 256-bit big-endian unsigned integer, packs it into the internal field value +// representation, and returns either 1 if it is greater than or equal to the +// field prime (aka it overflowed) or 0 otherwise in constant time. // // Note that a bool is not used here because it is not possible in Go to convert // from a bool to numeric value in constant time and many constant-time @@ -437,10 +437,10 @@ func (f *FieldVal) Normalize() *FieldVal { // directly into the passed byte slice in constant time. The target slice must // have at least 32 bytes available or it will panic. // -// There is a similar function, PutBytes, which unpacks the field value into a -// 32-byte array directly. This version is provided since it can be useful -// to write directly into part of a larger buffer without needing a separate -// allocation. +// There is a similar function, [FieldVal.PutBytes], which unpacks the field +// value into a 32-byte array directly. This version is provided since it can +// be useful to write directly into part of a larger buffer without needing a +// separate allocation. // // Preconditions: // - The field value MUST be normalized @@ -487,14 +487,14 @@ func (f *FieldVal) PutBytesUnchecked(b []byte) { // PutBytes unpacks the field value to a 32-byte big-endian value using the // passed byte array in constant time. // -// There is a similar function, PutBytesUnchecked, which unpacks the field value -// into a slice that must have at least 32 bytes available. This version is -// provided since it can be useful to write directly into an array that is type -// checked. +// There is a similar function, [FieldVal.PutBytesUnchecked], which unpacks the +// field value into a slice that must have at least 32 bytes available. This +// version is provided since it can be useful to write directly into an array +// that is type checked. // -// Alternatively, there is also Bytes, which unpacks the field value into a new -// array and returns that which can sometimes be more ergonomic in applications -// that aren't concerned about an additional copy. +// Alternatively, there is also [FieldVal.Bytes], which unpacks the field value +// into a new array and returns that which can sometimes be more ergonomic in +// applications that aren't concerned about an additional copy. // // Preconditions: // - The field value MUST be normalized @@ -504,10 +504,10 @@ func (f *FieldVal) PutBytes(b *[32]byte) { // Bytes unpacks the field value to a 32-byte big-endian value in constant time. // -// See PutBytes and PutBytesUnchecked for variants that allow an array or slice -// to be passed which can be useful to cut down on the number of allocations by -// allowing the caller to reuse a buffer or write directly into part of a larger -// buffer. +// See [FieldVal.PutBytes] and [FieldVal.PutBytesUnchecked] for variants that +// allow an array or slice to be passed which can be useful to cut down on the +// number of allocations by allowing the caller to reuse a buffer or write +// directly into part of a larger buffer. // // Preconditions: // - The field value MUST be normalized @@ -522,8 +522,8 @@ func (f *FieldVal) Bytes() *[32]byte { // // Note that a bool is not used here because it is not possible in Go to convert // from a bool to numeric value in constant time and many constant-time -// operations require a numeric value. See IsZero for the version that returns -// a bool. +// operations require a numeric value. See [FieldVal.IsZero] for the version +// that returns a bool. // // Preconditions: // - The field value MUST be normalized @@ -555,8 +555,8 @@ func (f *FieldVal) IsZero() bool { // // Note that a bool is not used here because it is not possible in Go to convert // from a bool to numeric value in constant time and many constant-time -// operations require a numeric value. See IsOne for the version that returns a -// bool. +// operations require a numeric value. See [FieldVal.IsOne] for the version +// that returns a bool. // // Preconditions: // - The field value MUST be normalized @@ -590,8 +590,8 @@ func (f *FieldVal) IsOne() bool { // // Note that a bool is not used here because it is not possible in Go to convert // from a bool to numeric value in constant time and many constant-time -// operations require a numeric value. See IsOdd for the version that returns a -// bool. +// operations require a numeric value. See [FieldVal.IsOdd] for the version +// that returns a bool. // // Preconditions: // - The field value MUST be normalized diff --git a/dcrec/secp256k1/field_bench_test.go b/dcrec/secp256k1/field_bench_test.go index f89c420b72..46e595611c 100644 --- a/dcrec/secp256k1/field_bench_test.go +++ b/dcrec/secp256k1/field_bench_test.go @@ -10,7 +10,7 @@ import ( ) // BenchmarkFieldNormalize benchmarks how long it takes the internal field -// to perform normalization (which includes modular reduction). +// to perform normalization (which includes modular reduction) with [FieldVal]. func BenchmarkFieldNormalize(b *testing.B) { // The function is constant time so any value is fine. f := &FieldVal{n: [10]uint32{ diff --git a/dcrec/secp256k1/field_test.go b/dcrec/secp256k1/field_test.go index 5ae7604732..0dae64d7d7 100644 --- a/dcrec/secp256k1/field_test.go +++ b/dcrec/secp256k1/field_test.go @@ -1,6 +1,6 @@ // Copyright (c) 2013-2016 The btcsuite developers -// Copyright (c) 2015-2024 The Decred developers -// Copyright (c) 2013-2024 Dave Collins +// Copyright (c) 2015-2026 The Decred developers +// Copyright (c) 2013-2026 Dave Collins // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -383,8 +383,8 @@ func TestFieldZero(t *testing.T) { } } -// TestFieldIsZero ensures that checking if a field is zero via IsZero and -// IsZeroBit works as expected. +// TestFieldIsZero ensures that checking if a field is zero via +// [FieldVal.IsZero] and [FieldVal.IsZeroBit] works as expected. func TestFieldIsZero(t *testing.T) { f := new(FieldVal) if !f.IsZero() { @@ -420,8 +420,8 @@ func TestFieldIsZero(t *testing.T) { } } -// TestFieldIsOne ensures that checking if a field is one via IsOne and IsOneBit -// works as expected. +// TestFieldIsOne ensures that checking if a field is one via [FieldVal.IsOne] +// and [FieldVal.IsOneBit] works as expected. func TestFieldIsOne(t *testing.T) { tests := []struct { name string // test description @@ -851,8 +851,8 @@ func TestFieldNormalize(t *testing.T) { } } -// TestFieldIsOdd ensures that checking if a field value is odd via IsOdd and -// IsOddBit works as expected. +// TestFieldIsOdd ensures that checking if a field value is odd via +// [FieldVal.IsOdd] and [FieldVal.IsOddBit] works as expected. func TestFieldIsOdd(t *testing.T) { tests := []struct { name string // test description @@ -907,7 +907,7 @@ func TestFieldIsOdd(t *testing.T) { } // TestFieldEquals ensures that checking two field values for equality via -// Equals works as expected. +// [FieldVal.Equals] works as expected. func TestFieldEquals(t *testing.T) { tests := []struct { name string // test description @@ -963,8 +963,8 @@ func TestFieldEquals(t *testing.T) { } } -// TestFieldNegate ensures that negating field values via Negate works as -// expected. +// TestFieldNegate ensures that negating field values via [FieldVal.Negate] +// works as expected. func TestFieldNegate(t *testing.T) { tests := []struct { name string // test description @@ -1038,8 +1038,8 @@ func TestFieldNegate(t *testing.T) { } } -// TestFieldAddInt ensures that adding an integer to field values via AddInt -// works as expected. +// TestFieldAddInt ensures that adding an integer to field values via +// [FieldVal.AddInt] works as expected. func TestFieldAddInt(t *testing.T) { tests := []struct { name string // test description @@ -1105,8 +1105,8 @@ func TestFieldAddInt(t *testing.T) { } } -// TestFieldAdd ensures that adding two field values together via Add and Add2 -// works as expected. +// TestFieldAdd ensures that adding two field values together via [FieldVal.Add] +// and [FieldVal.Add2] works as expected. func TestFieldAdd(t *testing.T) { tests := []struct { name string // test description @@ -1202,7 +1202,7 @@ func TestFieldAdd(t *testing.T) { } // TestFieldMulInt ensures that multiplying an integer to field values via -// MulInt works as expected. +// [FieldVal.MulInt] works as expected. func TestFieldMulInt(t *testing.T) { tests := []struct { name string // test description @@ -1281,8 +1281,8 @@ func TestFieldMulInt(t *testing.T) { } } -// TestFieldMul ensures that multiplying two field values via Mul and Mul2 works -// as expected. +// TestFieldMul ensures that multiplying two field values via [FieldVal.Mul] and +// [FieldVal.Mul2] works as expected. func TestFieldMul(t *testing.T) { tests := []struct { name string // test description @@ -1376,8 +1376,8 @@ func TestFieldMul(t *testing.T) { } } -// TestFieldSquare ensures that squaring field values via Square and SqualVal -// works as expected. +// TestFieldSquare ensures that squaring field values via [FieldVal.Square] and +// [FieldVal.SqualVal] works as expected. func TestFieldSquare(t *testing.T) { tests := []struct { name string // test description @@ -1446,7 +1446,7 @@ func TestFieldSquare(t *testing.T) { } // TestFieldSquareRoot ensures that calculating the square root of field values -// via SquareRootVal works as expected for edge cases. +// via [FieldVal.SquareRootVal] works as expected for edge cases. func TestFieldSquareRoot(t *testing.T) { tests := []struct { name string // test description @@ -1588,8 +1588,8 @@ func TestFieldSquareRootRandom(t *testing.T) { } } -// TestFieldInverse ensures that finding the multiplicative inverse via Inverse -// works as expected. +// TestFieldInverse ensures that finding the multiplicative inverse via +// [FieldVal.Inverse] works as expected. func TestFieldInverse(t *testing.T) { tests := []struct { name string // test description diff --git a/dcrec/secp256k1/modnscalar.go b/dcrec/secp256k1/modnscalar.go index a3f8998fa4..4bf30a678f 100644 --- a/dcrec/secp256k1/modnscalar.go +++ b/dcrec/secp256k1/modnscalar.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 The Decred developers +// Copyright (c) 2020-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -184,8 +184,8 @@ func (s *ModNScalar) Zero() { // // Note that a bool is not used here because it is not possible in Go to convert // from a bool to numeric value in constant time and many constant-time -// operations require a numeric value. See IsZero for the version that returns -// a bool. +// operations require a numeric value. See [ModNScalar.IsZero] for the version +// that returns a bool. func (s *ModNScalar) IsZeroBit() uint32 { // The scalar can only be zero if no bits are set in any of the words. bits := s.n[0] | s.n[1] | s.n[2] | s.n[3] | s.n[4] | s.n[5] | s.n[6] | s.n[7] @@ -385,23 +385,24 @@ func (s *ModNScalar) PutBytesUnchecked(b []byte) { // PutBytes unpacks the scalar to a 32-byte big-endian value using the passed // byte array in constant time. // -// There is a similar function, PutBytesUnchecked, which unpacks the scalar into -// a slice that must have at least 32 bytes available. This version is provided -// since it can be useful to write directly into an array that is type checked. +// There is a similar function, [ModNScalar.PutBytesUnchecked], which unpacks +// the scalar into a slice that must have at least 32 bytes available. This +// version is provided since it can be useful to write directly into an array +// that is type checked. // -// Alternatively, there is also Bytes, which unpacks the scalar into a new array -// and returns that which can sometimes be more ergonomic in applications that -// aren't concerned about an additional copy. +// Alternatively, there is also [ModNScalar.Bytes], which unpacks the scalar +// into a new array and returns that which can sometimes be more ergonomic in +// applications that aren't concerned about an additional copy. func (s *ModNScalar) PutBytes(b *[32]byte) { s.PutBytesUnchecked(b[:]) } // Bytes unpacks the scalar to a 32-byte big-endian value in constant time. // -// See PutBytes and PutBytesUnchecked for variants that allow an array or slice -// to be passed which can be useful to cut down on the number of allocations -// by allowing the caller to reuse a buffer or write directly into part of a -// larger buffer. +// See [ModNScalar.PutBytes] and [ModNScalar.PutBytesUnchecked] for variants +// that allow an array or slice to be passed which can be useful to cut down on +// the number of allocations by allowing the caller to reuse a buffer or write +// directly into part of a larger buffer. func (s *ModNScalar) Bytes() [32]byte { var b [32]byte s.PutBytesUnchecked(b[:]) diff --git a/dcrec/secp256k1/pubkey.go b/dcrec/secp256k1/pubkey.go index a52b158245..3ab8d33a1a 100644 --- a/dcrec/secp256k1/pubkey.go +++ b/dcrec/secp256k1/pubkey.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2014 The btcsuite developers -// Copyright (c) 2015-2024 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -74,10 +74,10 @@ type PublicKey struct { // NewPublicKey instantiates a new public key with the given x and y // coordinates. // -// It should be noted that, unlike ParsePubKey, since this accepts arbitrary x +// It should be noted that, unlike [ParsePubKey], since this accepts arbitrary x // and y coordinates, it allows creation of public keys that are not valid -// points on the secp256k1 curve. The IsOnCurve method of the returned instance -// can be used to determine validity. +// points on the secp256k1 curve. The [PublicKey.IsOnCurve] method of the +// returned instance can be used to determine validity. func NewPublicKey(x, y *FieldVal) *PublicKey { var pubKey PublicKey pubKey.x.Set(x) From 2936cfee752ce2c60848d5e8973153363d643fc4 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 28 Jun 2026 04:21:44 -0500 Subject: [PATCH 2/6] secp256k1: Field test consistency and assertions. This updates the tests to consistently make use of the helper methods that parse hex to 10x26 field vals and panic if they represent invalid values. Since some of the tests intentionally involve values that overflow the max allowed value, the helpers are reworked slightly to either allow overflow or not and all tests are updated accordingly. Finally, it renames the helpers that panic to the more canonical mustX scheme. --- dcrec/secp256k1/curve.go | 37 ++++++-- dcrec/secp256k1/curve_bench_test.go | 18 ++-- dcrec/secp256k1/curve_test.go | 16 ++-- dcrec/secp256k1/ecdsa/bench_test.go | 14 +-- dcrec/secp256k1/field_bench_test.go | 19 ++-- dcrec/secp256k1/field_test.go | 87 ++++++++----------- dcrec/secp256k1/pubkey_bench_test.go | 2 +- dcrec/secp256k1/pubkey_test.go | 22 ++--- .../secp256k1/schnorr/signature_bench_test.go | 10 +-- dcrec/secp256k1/schnorr/signature_test.go | 4 +- 10 files changed, 117 insertions(+), 112 deletions(-) diff --git a/dcrec/secp256k1/curve.go b/dcrec/secp256k1/curve.go index 4280921c96..ba9ec9ae1b 100644 --- a/dcrec/secp256k1/curve.go +++ b/dcrec/secp256k1/curve.go @@ -26,22 +26,41 @@ import ( // (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1) // where x = x1/z1^2 and y = y1/z1^3. -// hexToFieldVal converts the passed hex string into a FieldVal and will panic -// if there is an error. This is only provided for the hard-coded constants so -// errors in the source code can be detected. It will only (and must only) be -// called with hard-coded values. -func hexToFieldVal(s string) *FieldVal { +// mustFieldValInternal converts the passed hex string into a [FieldVal] and +// will panic if there is an error. Values that overflow are treated as an +// error unless the allow overflow flag is set. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustFieldValInternal(s string, allowOverflow bool) *FieldVal { + if len(s)%2 != 0 { + s = "0" + s + } b, err := hex.DecodeString(s) if err != nil { panic("invalid hex in source file: " + s) } + if len(b) > 32 { + panic("hex in source file overflows uint256: " + s) + } var f FieldVal - if overflow := f.SetByteSlice(b); overflow { - panic("hex in source file overflows mod P: " + s) + if overflow := f.SetByteSlice(b); overflow && !allowOverflow { + panic("hex in source file overflows mod N scalar: " + s) } return &f } +// mustFieldVal converts the passed hex string into a [FieldVal] and will panic +// if there is an error. Values that overflow are treated as an error. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustFieldVal(s string) *FieldVal { + return mustFieldValInternal(s, false) +} + // hexToModNScalar converts the passed hex string into a ModNScalar and will // panic if there is an error. This is only provided for the hard-coded // constants so errors in the source code can be detected. It will only (and @@ -81,7 +100,7 @@ var ( // Additionally, see the scalar multiplication function in this file for // details on how they are used. endoNegLambda = hexToModNScalar("-5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72") - endoBeta = hexToFieldVal("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee") + endoBeta = mustFieldVal("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee") endoNegB1 = hexToModNScalar("e4437ed6010e88286f547fa90abfe4c3") endoNegB2 = hexToModNScalar("-3086d221a7d46bcde86c90e49284eb15") endoZ1 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7f") @@ -90,7 +109,7 @@ var ( // Alternatively, the following parameters are valid as well, however, // benchmarks show them to be about 2% slower in practice. // endoNegLambda = hexToModNScalar("-ac9c52b33fa3cf1f5ad9e3fd77ed9ba4a880b9fc8ec739c2e0cfc810b51283ce") - // endoBeta = hexToFieldVal("851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40") + // endoBeta = mustFieldVal("851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40") // endoNegB1 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb15") // endoNegB2 = hexToModNScalar("-114ca50f7a8e2f3f657c1108d9d44cfd8") // endoZ1 = hexToModNScalar("114ca50f7a8e2f3f657c1108d9d44cfd95fbc92c10fddd145") diff --git a/dcrec/secp256k1/curve_bench_test.go b/dcrec/secp256k1/curve_bench_test.go index 5f0ed4df23..0d8983acce 100644 --- a/dcrec/secp256k1/curve_bench_test.go +++ b/dcrec/secp256k1/curve_bench_test.go @@ -35,14 +35,16 @@ func BenchmarkAddNonConst(b *testing.B) { // function with Z values other than one so the optimizations associated with // Z=1 aren't used. func BenchmarkAddNonConstNotZOne(b *testing.B) { - x1 := new(FieldVal).SetHex("d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718") - y1 := new(FieldVal).SetHex("5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190") - z1 := new(FieldVal).SetHex("2") - x2 := new(FieldVal).SetHex("91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4") - y2 := new(FieldVal).SetHex("03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1") - z2 := new(FieldVal).SetHex("3") - p1 := MakeJacobianPoint(x1, y1, z1) - p2 := MakeJacobianPoint(x2, y2, z2) + p1 := jacobianPointFromHex( + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + ) + p2 := jacobianPointFromHex( + "91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4", + "03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1", + "3", + ) b.ReportAllocs() b.ResetTimer() diff --git a/dcrec/secp256k1/curve_test.go b/dcrec/secp256k1/curve_test.go index 8868674226..4834fe9fcb 100644 --- a/dcrec/secp256k1/curve_test.go +++ b/dcrec/secp256k1/curve_test.go @@ -93,11 +93,15 @@ func randJacobian(t *testing.T, rng *rand.Rand) *JacobianPoint { // jacobianPointFromHex decodes the passed big-endian hex strings into a // Jacobian point with its internal fields set to the resulting values. Only // the first 32-bytes are used. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. func jacobianPointFromHex(x, y, z string) JacobianPoint { var p JacobianPoint - p.X.SetHex(x) - p.Y.SetHex(y) - p.Z.SetHex(z) + p.X = *mustFieldVal(x) + p.Y = *mustFieldVal(y) + p.Z = *mustFieldVal(z) return p } @@ -1170,7 +1174,7 @@ func TestDecompressY(t *testing.T) { // Decompress the test odd y coordinate for the given test x coordinate // and ensure the returned validity flag matches the expected result. var oddY FieldVal - fx := new(FieldVal).SetHex(test.x) + fx := mustFieldValWithOverflow(test.x) valid := DecompressY(fx, true, &oddY) if valid != test.valid { t.Errorf("%s: unexpected valid flag -- got: %v, want: %v", @@ -1194,7 +1198,7 @@ func TestDecompressY(t *testing.T) { } // Ensure the decompressed odd Y coordinate is the expected value. - wantOddY := new(FieldVal).SetHex(test.wantOddY) + wantOddY := mustFieldVal(test.wantOddY) if !wantOddY.Equals(&oddY) { t.Errorf("%s: mismatched odd y\ngot: %v, want: %v", test.name, oddY, wantOddY) @@ -1202,7 +1206,7 @@ func TestDecompressY(t *testing.T) { } // Ensure the decompressed even Y coordinate is the expected value. - wantEvenY := new(FieldVal).SetHex(test.wantEvenY) + wantEvenY := mustFieldVal(test.wantEvenY) if !wantEvenY.Equals(&evenY) { t.Errorf("%s: mismatched even y\ngot: %v, want: %v", test.name, evenY, wantEvenY) diff --git a/dcrec/secp256k1/ecdsa/bench_test.go b/dcrec/secp256k1/ecdsa/bench_test.go index ed0c99d67a..476a70008a 100644 --- a/dcrec/secp256k1/ecdsa/bench_test.go +++ b/dcrec/secp256k1/ecdsa/bench_test.go @@ -28,11 +28,11 @@ func hexToModNScalar(s string) *secp256k1.ModNScalar { return &scalar } -// hexToFieldVal converts the passed hex string into a FieldVal and will panic -// if there is an error. This is only provided for the hard-coded constants so +// mustFieldVal converts the passed hex string into a FieldVal and will panic if +// there is an error. This is only provided for the hard-coded constants so // errors in the source code can be detected. It will only (and must only) be // called with hard-coded values. -func hexToFieldVal(s string) *secp256k1.FieldVal { +func mustFieldVal(s string) *secp256k1.FieldVal { b, err := hex.DecodeString(s) if err != nil { panic("invalid hex in source file: " + s) @@ -50,8 +50,8 @@ func BenchmarkSigVerify(b *testing.B) { // Randomly generated keypair. // Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d pubKey := secp256k1.NewPublicKey( - hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), - hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), + mustFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), + mustFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), ) // Double sha256 of []byte{0x01, 0x02, 0x03, 0x04} @@ -150,8 +150,8 @@ func BenchmarkSignCompact(b *testing.B) { func BenchmarkRecoverCompact(b *testing.B) { // Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d wantPubKey := secp256k1.NewPublicKey( - hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), - hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), + mustFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), + mustFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), ) compactSig := hexToBytes("205978b7896bc71676ba2e459882a8f52e1299449596c4f" + diff --git a/dcrec/secp256k1/field_bench_test.go b/dcrec/secp256k1/field_bench_test.go index 46e595611c..91e37ab528 100644 --- a/dcrec/secp256k1/field_bench_test.go +++ b/dcrec/secp256k1/field_bench_test.go @@ -37,7 +37,6 @@ func BenchmarkBigIntNegateModP(b *testing.B) { for i := 0; i < b.N; i++ { result := new(big.Int).Neg(v1) result.Mod(result, curveParams.P) - } } @@ -46,7 +45,7 @@ func BenchmarkBigIntNegateModP(b *testing.B) { func BenchmarkFieldNegate(b *testing.B) { // The function is constant time so any value is fine. valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal).SetHex(valHex) + f := mustFieldVal(valHex) b.ReportAllocs() b.ResetTimer() @@ -78,8 +77,8 @@ func BenchmarkFieldAdd(b *testing.B) { // The function is constant time so any values are fine. f1Hex := "d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab" f2Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f1 := new(FieldVal).SetHex(f1Hex) - f2 := new(FieldVal).SetHex(f2Hex) + f1 := mustFieldVal(f1Hex) + f2 := mustFieldVal(f2Hex) b.ReportAllocs() b.ResetTimer() @@ -111,8 +110,8 @@ func BenchmarkFieldMul(b *testing.B) { // The function is constant time so any values are fine. f1Hex := "d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab" f2Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f1 := new(FieldVal).SetHex(f1Hex) - f2 := new(FieldVal).SetHex(f2Hex) + f1 := mustFieldVal(f1Hex) + f2 := mustFieldVal(f2Hex) b.ReportAllocs() b.ResetTimer() @@ -140,7 +139,7 @@ func BenchmarkBigIntSqrtModP(b *testing.B) { func BenchmarkFieldSqrt(b *testing.B) { // The function is constant time so any value is fine. valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal).SetHex(valHex).Normalize() + f := mustFieldVal(valHex) b.ReportAllocs() b.ResetTimer() @@ -169,7 +168,7 @@ func BenchmarkBigIntSquareModP(b *testing.B) { func BenchmarkFieldSquare(b *testing.B) { // The function is constant time so any values are fine. fHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal).SetHex(fHex) + f := mustFieldVal(fHex) b.ReportAllocs() b.ResetTimer() @@ -198,7 +197,7 @@ func BenchmarkBigIntInverseModP(b *testing.B) { func BenchmarkFieldInverse(b *testing.B) { // The function is constant time so any value is fine. valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal).SetHex(valHex).Normalize() + f := mustFieldVal(valHex) b.ReportAllocs() b.ResetTimer() @@ -232,7 +231,7 @@ func BenchmarkBigIntIsGtOrEqPrimeMinusOrder(b *testing.B) { func BenchmarkFieldIsGtOrEqPrimeMinusOrder(b *testing.B) { // The function is constant time so any value is fine. valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal).SetHex(valHex).Normalize() + f := mustFieldVal(valHex) b.ReportAllocs() b.ResetTimer() diff --git a/dcrec/secp256k1/field_test.go b/dcrec/secp256k1/field_test.go index 0dae64d7d7..8370095fae 100644 --- a/dcrec/secp256k1/field_test.go +++ b/dcrec/secp256k1/field_test.go @@ -8,7 +8,6 @@ package secp256k1 import ( "bytes" - "encoding/hex" "fmt" "math/big" "math/rand" @@ -17,20 +16,15 @@ import ( "time" ) -// SetHex decodes the passed big-endian hex string into the internal field value -// representation. Only the first 32-bytes are used. +// mustFieldValWithOverflow converts the passed hex string into a [FieldVal] and +// will panic if there is an error. Values that overflow are NOT treated as an +// error. // -// This is NOT constant time. -// -// The field value is returned to support chaining. This enables syntax like: -// f := new(FieldVal).SetHex("0abc").Add(1) so that f = 0x0abc + 1 -func (f *FieldVal) SetHex(hexString string) *FieldVal { - if len(hexString)%2 != 0 { - hexString = "0" + hexString - } - bytes, _ := hex.DecodeString(hexString) - f.SetByteSlice(bytes) - return f +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustFieldValWithOverflow(s string) *FieldVal { + return mustFieldValInternal(s, true) } // randFieldVal returns a field value created from a random value generated by @@ -339,7 +333,7 @@ func TestFieldBytes(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in).Normalize() + f := mustFieldValWithOverflow(test.in).Normalize() expected := hexToBytes(test.expected) // Ensure getting the bytes works as expected. @@ -372,8 +366,7 @@ func TestFieldBytes(t *testing.T) { // TestFieldZero ensures that zeroing a field value works as expected. func TestFieldZero(t *testing.T) { - var f FieldVal - f.SetHex("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") + f := mustFieldVal("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") f.Zero() for idx, rawInt := range f.n { if rawInt != 0 { @@ -506,7 +499,7 @@ func TestFieldIsOne(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in) + f := mustFieldValWithOverflow(test.in) if test.normalize { f.Normalize() } @@ -608,22 +601,10 @@ func TestFieldStringer(t *testing.T) { name: "2^256-4294968274 (the secp256k1 prime+1, so should result in 1)", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", expected: "0000000000000000000000000000000000000000000000000000000000000001", - }, { - name: "invalid hex g", - in: "g", - expected: "0000000000000000000000000000000000000000000000000000000000000000", - }, { - name: "invalid hex 1h", - in: "1h", - expected: "0000000000000000000000000000000000000000000000000000000000000000", - }, { - name: "invalid hex i1", - in: "i1", - expected: "0000000000000000000000000000000000000000000000000000000000000000", }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in) + f := mustFieldValWithOverflow(test.in) result := f.String() if result != test.expected { t.Errorf("%s: wrong result\ngot: %v\nwant: %v", test.name, result, @@ -889,7 +870,7 @@ func TestFieldIsOdd(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in) + f := mustFieldValWithOverflow(test.in) result := f.IsOdd() if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, @@ -952,8 +933,8 @@ func TestFieldEquals(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in1).Normalize() - f2 := new(FieldVal).SetHex(test.in2).Normalize() + f := mustFieldValWithOverflow(test.in1).Normalize() + f2 := mustFieldValWithOverflow(test.in2).Normalize() result := f.Equals(f2) if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, result, @@ -1017,8 +998,8 @@ func TestFieldNegate(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in).Normalize() - expected := new(FieldVal).SetHex(test.expected).Normalize() + f := mustFieldValWithOverflow(test.in).Normalize() + expected := mustFieldValWithOverflow(test.expected).Normalize() // Ensure negating another value produces the expected result. result := new(FieldVal).NegateVal(f, 1).Normalize() @@ -1094,8 +1075,8 @@ func TestFieldAddInt(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in1).Normalize() - expected := new(FieldVal).SetHex(test.expected).Normalize() + f := mustFieldValWithOverflow(test.in1).Normalize() + expected := mustFieldVal(test.expected).Normalize() result := f.AddInt(test.in2).Normalize() if !result.Equals(expected) { t.Errorf("%s: wrong result -- got: %v -- want: %v", test.name, @@ -1177,9 +1158,9 @@ func TestFieldAdd(t *testing.T) { for _, test := range tests { // Parse test hex. - f1 := new(FieldVal).SetHex(test.in1).Normalize() - f2 := new(FieldVal).SetHex(test.in2).Normalize() - expected := new(FieldVal).SetHex(test.expected).Normalize() + f1 := mustFieldValWithOverflow(test.in1).Normalize() + f2 := mustFieldValWithOverflow(test.in2).Normalize() + expected := mustFieldVal(test.expected).Normalize() // Ensure adding the two values with the result going to another // variable produces the expected result. @@ -1270,8 +1251,8 @@ func TestFieldMulInt(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in1).Normalize() - expected := new(FieldVal).SetHex(test.expected).Normalize() + f := mustFieldValWithOverflow(test.in1).Normalize() + expected := mustFieldVal(test.expected).Normalize() result := f.MulInt(test.in2).Normalize() if !result.Equals(expected) { t.Errorf("%s: wrong result -- got: %v -- want: %v", test.name, @@ -1352,9 +1333,9 @@ func TestFieldMul(t *testing.T) { }} for _, test := range tests { - f1 := new(FieldVal).SetHex(test.in1).Normalize() - f2 := new(FieldVal).SetHex(test.in2).Normalize() - expected := new(FieldVal).SetHex(test.expected).Normalize() + f1 := mustFieldValWithOverflow(test.in1).Normalize() + f2 := mustFieldValWithOverflow(test.in2).Normalize() + expected := mustFieldVal(test.expected).Normalize() // Ensure multiplying the two values with the result going to another // variable produces the expected result. @@ -1422,8 +1403,8 @@ func TestFieldSquare(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in).Normalize() - expected := new(FieldVal).SetHex(test.expected).Normalize() + f := mustFieldValWithOverflow(test.in).Normalize() + expected := mustFieldValWithOverflow(test.expected).Normalize() // Ensure squaring the value with the result going to another variable // produces the expected result. @@ -1521,8 +1502,8 @@ func TestFieldSquareRoot(t *testing.T) { }} for _, test := range tests { - input := new(FieldVal).SetHex(test.in).Normalize() - want := new(FieldVal).SetHex(test.want).Normalize() + input := mustFieldValWithOverflow(test.in).Normalize() + want := mustFieldValWithOverflow(test.want).Normalize() // Calculate the square root and enusre the validity flag matches the // expected value. @@ -1634,8 +1615,8 @@ func TestFieldInverse(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal).SetHex(test.in).Normalize() - expected := new(FieldVal).SetHex(test.expected).Normalize() + f := mustFieldValWithOverflow(test.in).Normalize() + expected := mustFieldValWithOverflow(test.expected).Normalize() result := f.Inverse().Normalize() if !result.Equals(expected) { t.Errorf("%s: d wrong result\ngot: %v\nwant: %v", test.name, result, @@ -1712,7 +1693,7 @@ func TestFieldIsGtOrEqPrimeMinusOrder(t *testing.T) { }} for _, test := range tests { - result := new(FieldVal).SetHex(test.in).IsGtOrEqPrimeMinusOrder() + result := mustFieldVal(test.in).IsGtOrEqPrimeMinusOrder() if result != test.expected { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result, test.expected) diff --git a/dcrec/secp256k1/pubkey_bench_test.go b/dcrec/secp256k1/pubkey_bench_test.go index 9dce1d0245..09b7da24ab 100644 --- a/dcrec/secp256k1/pubkey_bench_test.go +++ b/dcrec/secp256k1/pubkey_bench_test.go @@ -14,7 +14,7 @@ import ( func BenchmarkPubKeyDecompress(b *testing.B) { // Randomly generated keypair. // Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d - pubKeyX := new(FieldVal).SetHex("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab") + pubKeyX := mustFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab") b.ReportAllocs() b.ResetTimer() diff --git a/dcrec/secp256k1/pubkey_test.go b/dcrec/secp256k1/pubkey_test.go index b52a84dab1..d984af86d9 100644 --- a/dcrec/secp256k1/pubkey_test.go +++ b/dcrec/secp256k1/pubkey_test.go @@ -216,7 +216,7 @@ func TestParsePubKey(t *testing.T) { // Ensure the x and y coordinates match the expected values upon // successful parse. - wantX, wantY := hexToFieldVal(test.wantX), hexToFieldVal(test.wantY) + wantX, wantY := mustFieldVal(test.wantX), mustFieldVal(test.wantY) if !pubKey.x.Equals(wantX) { t.Errorf("%s: mismatched x coordinate -- got %v, want %v", test.name, pubKey.x, wantX) @@ -315,7 +315,7 @@ func TestPubKeySerialize(t *testing.T) { for _, test := range tests { // Parse the test data. - x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY) + x, y := mustFieldVal(test.pubX), mustFieldVal(test.pubY) pubKey := NewPublicKey(x, y) // Serialize with the correct method and ensure the result matches the @@ -339,16 +339,16 @@ func TestPubKeySerialize(t *testing.T) { // works as expected. func TestPublicKeyIsEqual(t *testing.T) { pubKey1 := &PublicKey{ - x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"), - y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"), + x: *mustFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"), + y: *mustFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"), } pubKey1Copy := &PublicKey{ - x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"), - y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"), + x: *mustFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"), + y: *mustFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"), } pubKey2 := &PublicKey{ - x: *hexToFieldVal("ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"), - y: *hexToFieldVal("0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032"), + x: *mustFieldVal("ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"), + y: *mustFieldVal("0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032"), } if !pubKey1.IsEqual(pubKey1) { @@ -395,8 +395,8 @@ func TestPublicKeyAsJacobian(t *testing.T) { for _, test := range tests { // Parse the test data. pubKeyBytes := hexToBytes(test.pubKey) - wantX := hexToFieldVal(test.wantX) - wantY := hexToFieldVal(test.wantY) + wantX := mustFieldVal(test.wantX) + wantY := mustFieldVal(test.wantY) pubKey, err := ParsePubKey(pubKeyBytes) if err != nil { t.Errorf("%s: failed to parse public key: %v", test.name, err) @@ -457,7 +457,7 @@ func TestPublicKeyIsOnCurve(t *testing.T) { for _, test := range tests { // Parse the test data. - x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY) + x, y := mustFieldVal(test.pubX), mustFieldVal(test.pubY) pubKey := NewPublicKey(x, y) result := pubKey.IsOnCurve() diff --git a/dcrec/secp256k1/schnorr/signature_bench_test.go b/dcrec/secp256k1/schnorr/signature_bench_test.go index 4584b44979..72a042b432 100644 --- a/dcrec/secp256k1/schnorr/signature_bench_test.go +++ b/dcrec/secp256k1/schnorr/signature_bench_test.go @@ -39,11 +39,11 @@ func hexToBytes(s string) []byte { return b } -// hexToFieldVal converts the passed hex string into a FieldVal and will panic -// if there is an error. This is only provided for the hard-coded constants so +// mustFieldVal converts the passed hex string into a FieldVal and will panic if +// there is an error. This is only provided for the hard-coded constants so // errors in the source code can be detected. It will only (and must only) be // called with hard-coded values. -func hexToFieldVal(s string) *secp256k1.FieldVal { +func mustFieldVal(s string) *secp256k1.FieldVal { b, err := hex.DecodeString(s) if err != nil { panic("invalid hex in source file: " + s) @@ -77,8 +77,8 @@ func BenchmarkSigVerify(b *testing.B) { d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") privKey := secp256k1.NewPrivateKey(d) pubKey := secp256k1.NewPublicKey( - hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), - hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), + mustFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), + mustFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), ) // blake256 of []byte{0x01, 0x02, 0x03, 0x04}. diff --git a/dcrec/secp256k1/schnorr/signature_test.go b/dcrec/secp256k1/schnorr/signature_test.go index fb252cf0b0..7cc99412df 100644 --- a/dcrec/secp256k1/schnorr/signature_test.go +++ b/dcrec/secp256k1/schnorr/signature_test.go @@ -261,7 +261,7 @@ func TestSchnorrSignAndVerify(t *testing.T) { hash := hexToBytes(test.hash) nonce := hexToModNScalar(test.nonce) wantSig := hexToBytes(test.expected) - wantSigR := hexToFieldVal(test.expected[:64]) + wantSigR := mustFieldVal(test.expected[:64]) wantSigS := hexToModNScalar(test.expected[64:]) // Ensure the test data is sane by comparing the provided hashed message @@ -540,7 +540,7 @@ func TestVerifyErrors(t *testing.T) { for _, test := range tests { // Parse test data into types. hash := hexToBytes(test.hash) - pubX, pubY := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY) + pubX, pubY := mustFieldVal(test.pubX), mustFieldVal(test.pubY) pubKey := secp256k1.NewPublicKey(pubX, pubY) // Create the serialized signature from the bytes and attempt to parse From 1bdfaa175e0a317d3741bf04da00498f90c07e0b Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 28 Jun 2026 04:21:47 -0500 Subject: [PATCH 3/6] secp256k1: Field64 test consistency and assertions. This updates the tests to consistently make use of the helper methods that parse hex to 4x64 field vals and panic if they represent invalid values. Since some of the tests intentionally involve values that overflow the max allowed value, the helpers are reworked slightly to either allow overflow or not and all tests are updated accordingly. Finally, it renames the helpers that panic to the more canonical mustX scheme. --- dcrec/secp256k1/field64_bench_test.go | 16 ++-- dcrec/secp256k1/field64_test.go | 109 ++++++++++++++++---------- 2 files changed, 77 insertions(+), 48 deletions(-) diff --git a/dcrec/secp256k1/field64_bench_test.go b/dcrec/secp256k1/field64_bench_test.go index 6b63e3cb05..274253eb4e 100644 --- a/dcrec/secp256k1/field64_bench_test.go +++ b/dcrec/secp256k1/field64_bench_test.go @@ -14,7 +14,7 @@ import "testing" func BenchmarkField64Sqrt(b *testing.B) { // The function is constant time so any value is fine. valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal64).SetHex(valHex) + f := mustFieldVal64(valHex) b.ReportAllocs() b.ResetTimer() @@ -30,7 +30,7 @@ func BenchmarkField64Sqrt(b *testing.B) { func BenchmarkField64Inverse(b *testing.B) { // The function is constant time so any value is fine. valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal64).SetHex(valHex) + f := mustFieldVal64(valHex) b.ReportAllocs() b.ResetTimer() @@ -45,7 +45,7 @@ func BenchmarkField64Inverse(b *testing.B) { func BenchmarkField64IsGtOrEqPrimeMinusOrder(b *testing.B) { // The function is constant time so any value is fine. valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" - f := new(FieldVal64).SetHex(valHex) + f := mustFieldVal64(valHex) b.ReportAllocs() b.ResetTimer() @@ -56,8 +56,8 @@ func BenchmarkField64IsGtOrEqPrimeMinusOrder(b *testing.B) { // BenchmarkField64Add benchmarks adding two field values. func BenchmarkField64Add(b *testing.B) { - a := new(FieldVal64).SetHex("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab") - c := new(FieldVal64).SetHex("16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca") + a := mustFieldVal64("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab") + c := mustFieldVal64("16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca") b.ReportAllocs() b.ResetTimer() @@ -69,8 +69,8 @@ func BenchmarkField64Add(b *testing.B) { // BenchmarkField64Mul benchmarks multiplying two field values. func BenchmarkField64Mul(b *testing.B) { - a := new(FieldVal64).SetHex("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab") - c := new(FieldVal64).SetHex("16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca") + a := mustFieldVal64("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab") + c := mustFieldVal64("16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca") b.ReportAllocs() b.ResetTimer() @@ -82,7 +82,7 @@ func BenchmarkField64Mul(b *testing.B) { // BenchmarkField64Square benchmarks squaring a field value. func BenchmarkField64Square(b *testing.B) { - a := new(FieldVal64).SetHex("16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca") + a := mustFieldVal64("16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca") b.ReportAllocs() b.ResetTimer() diff --git a/dcrec/secp256k1/field64_test.go b/dcrec/secp256k1/field64_test.go index e8e5c611f4..7d384d0336 100644 --- a/dcrec/secp256k1/field64_test.go +++ b/dcrec/secp256k1/field64_test.go @@ -15,20 +15,50 @@ import ( "time" ) -// These tests mirror the FieldVal (field_test.go) suite for FieldVal64. -// FieldVal64 is always kept fully reduced, so the few FieldVal cases -// that depend on a denormalized internal representation are adjusted to reflect -// the reduced value instead. - -// SetHex decodes the passed big-endian hex string into the field value. Only the -// first 32 bytes are used. This is NOT constant time and is only used by tests. -func (f *FieldVal64) SetHex(hexString string) *FieldVal64 { - if len(hexString)%2 != 0 { - hexString = "0" + hexString +// mustFieldVal64Internal converts the passed hex string into a [FieldVal64] and +// will panic if there is an error. Values that overflow are treated as an +// error unless the allow overflow flag is set. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustFieldVal64Internal(s string, allowOverflow bool) *FieldVal64 { + if len(s)%2 != 0 { + s = "0" + s } - decoded, _ := hex.DecodeString(hexString) - f.SetByteSlice(decoded) - return f + b, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + if len(b) > 32 { + panic("hex in source file overflows uint256: " + s) + } + var f FieldVal64 + if overflow := f.SetByteSlice(b); overflow && !allowOverflow { + panic("hex in source file overflows mod N scalar: " + s) + } + return &f +} + +// mustFieldVal64 converts the passed hex string into a [FieldVal64] and will +// panic if there is an error. Values that overflow are treated as an error. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustFieldVal64(s string) *FieldVal64 { + return mustFieldVal64Internal(s, false) +} + +// mustFieldVal64WithOverflow converts the passed hex string into a [FieldVal64] +// and will panic if there is an error. Values that overflow are NOT treated as +// an error. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustFieldVal64WithOverflow(s string) *FieldVal64 { + return mustFieldVal64Internal(s, true) } // randIntAndFieldVal64 returns a big integer and a field value both created from @@ -271,7 +301,7 @@ func TestField64Bytes(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in) + f := mustFieldVal64WithOverflow(test.in) expected := hexToBytes(test.expected) // Ensure getting the bytes works as expected. @@ -304,8 +334,7 @@ func TestField64Bytes(t *testing.T) { // TestField64Zero ensures that zeroing a field value works as expected. func TestField64Zero(t *testing.T) { - var f FieldVal64 - f.SetHex("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") + f := mustFieldVal64("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") f.Zero() for idx, limb := range f.n { if limb != 0 { @@ -393,7 +422,7 @@ func TestField64IsOne(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in) + f := mustFieldVal64WithOverflow(test.in) result := f.IsOne() if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, result, @@ -449,7 +478,7 @@ func TestField64IsOdd(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in) + f := mustFieldVal64WithOverflow(test.in) result := f.IsOdd() if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, @@ -512,8 +541,8 @@ func TestField64Equals(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in1) - f2 := new(FieldVal64).SetHex(test.in2) + f := mustFieldVal64WithOverflow(test.in1) + f2 := mustFieldVal64WithOverflow(test.in2) result := f.Equals(f2) if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, result, @@ -577,8 +606,8 @@ func TestField64Negate(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in) - expected := new(FieldVal64).SetHex(test.expected) + f := mustFieldVal64WithOverflow(test.in) + expected := mustFieldVal64WithOverflow(test.expected) // Ensure negating another value produces the expected result. result := new(FieldVal64).NegateVal(f) @@ -654,8 +683,8 @@ func TestField64AddInt(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in1) - expected := new(FieldVal64).SetHex(test.expected) + f := mustFieldVal64WithOverflow(test.in1) + expected := mustFieldVal64(test.expected) result := f.AddInt(test.in2) if !result.Equals(expected) { t.Errorf("%s: wrong result -- got: %x -- want: %x", test.name, @@ -736,9 +765,9 @@ func TestField64Add(t *testing.T) { }} for _, test := range tests { - f1 := new(FieldVal64).SetHex(test.in1) - f2 := new(FieldVal64).SetHex(test.in2) - expected := new(FieldVal64).SetHex(test.expected) + f1 := mustFieldVal64WithOverflow(test.in1) + f2 := mustFieldVal64WithOverflow(test.in2) + expected := mustFieldVal64(test.expected) // Ensure adding the two values with the result going to another // variable produces the expected result. @@ -789,7 +818,7 @@ func TestField64MulByX(t *testing.T) { "e3cbe002cc93029190181a906ed41af401c9726546dc19389a06290efdf563f1", // random sampling } for _, in := range testCases { - in := new(FieldVal64).SetHex(in) + in := mustFieldVal64(in) for _, m := range mulByFuncs { var want FieldVal64 want.Set(in).MulInt(m.factor) @@ -870,8 +899,8 @@ func TestField64MulInt(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in1) - expected := new(FieldVal64).SetHex(test.expected) + f := mustFieldVal64WithOverflow(test.in1) + expected := mustFieldVal64(test.expected) result := f.MulInt(test.in2) if !result.Equals(expected) { t.Errorf("%s: wrong result -- got: %x -- want: %x", test.name, @@ -957,9 +986,9 @@ func TestField64Mul(t *testing.T) { }} for _, test := range tests { - f1 := new(FieldVal64).SetHex(test.in1) - f2 := new(FieldVal64).SetHex(test.in2) - expected := new(FieldVal64).SetHex(test.expected) + f1 := mustFieldVal64WithOverflow(test.in1) + f2 := mustFieldVal64WithOverflow(test.in2) + expected := mustFieldVal64(test.expected) // Ensure multiplying the two values with the result going to another // variable produces the expected result. @@ -1027,8 +1056,8 @@ func TestField64Square(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in) - expected := new(FieldVal64).SetHex(test.expected) + f := mustFieldVal64WithOverflow(test.in) + expected := mustFieldVal64WithOverflow(test.expected) // Ensure squaring the value with the result going to another variable // produces the expected result. @@ -1126,8 +1155,8 @@ func TestField64SquareRoot(t *testing.T) { }} for _, test := range tests { - input := new(FieldVal64).SetHex(test.in) - want := new(FieldVal64).SetHex(test.want) + input := mustFieldVal64WithOverflow(test.in) + want := mustFieldVal64WithOverflow(test.want) // Calculate the square root and ensure the validity flag matches the // expected value. @@ -1238,8 +1267,8 @@ func TestField64Inverse(t *testing.T) { }} for _, test := range tests { - f := new(FieldVal64).SetHex(test.in) - expected := new(FieldVal64).SetHex(test.expected) + f := mustFieldVal64WithOverflow(test.in) + expected := mustFieldVal64WithOverflow(test.expected) result := f.Inverse() if !result.Equals(expected) { t.Errorf("%s: wrong result\ngot: %x\nwant: %x", test.name, @@ -1308,7 +1337,7 @@ func TestField64IsGtOrEqPrimeMinusOrder(t *testing.T) { }} for _, test := range tests { - result := new(FieldVal64).SetHex(test.in).IsGtOrEqPrimeMinusOrder() + result := mustFieldVal64(test.in).IsGtOrEqPrimeMinusOrder() if result != test.expected { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result, test.expected) From ea494ddddf377e2d5866646fb18947d7b1344fbb Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 28 Jun 2026 04:21:51 -0500 Subject: [PATCH 4/6] secp256k1: Scalar test consistency and assertions. This updates the tests to consistently make use of the helper methods that parse hex to mod n scalars and panic if they represent invalid values. Since some of the tests intentionally involve values that overflow the max allowed value, the helpers are reworked slightly to either allow overflow or not and all tests are updated accordingly. --- dcrec/secp256k1/curve.go | 48 +++++++++----- dcrec/secp256k1/curve_bench_test.go | 10 +-- dcrec/secp256k1/curve_test.go | 6 +- dcrec/secp256k1/ecdsa/bench_test.go | 16 ++--- dcrec/secp256k1/ecdsa/signature_test.go | 44 ++++++------- dcrec/secp256k1/modnscalar_test.go | 65 +++++++++---------- dcrec/secp256k1/privkey_test.go | 4 +- .../secp256k1/schnorr/signature_bench_test.go | 10 +-- dcrec/secp256k1/schnorr/signature_test.go | 8 +-- 9 files changed, 112 insertions(+), 99 deletions(-) diff --git a/dcrec/secp256k1/curve.go b/dcrec/secp256k1/curve.go index ba9ec9ae1b..2df9f6b4fd 100644 --- a/dcrec/secp256k1/curve.go +++ b/dcrec/secp256k1/curve.go @@ -61,11 +61,14 @@ func mustFieldVal(s string) *FieldVal { return mustFieldValInternal(s, false) } -// hexToModNScalar converts the passed hex string into a ModNScalar and will -// panic if there is an error. This is only provided for the hard-coded -// constants so errors in the source code can be detected. It will only (and -// must only) be called with hard-coded values. -func hexToModNScalar(s string) *ModNScalar { +// mustModNScalarInternal converts the passed hex string into a [ModNScalar] and +// will panic if there is an error. Values that overflow are treated as an +// error unless the allow overflow flag is set. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustModNScalarInternal(s string, allowOverflow bool) *ModNScalar { var isNegative bool if len(s) > 0 && s[0] == '-' { isNegative = true @@ -78,8 +81,11 @@ func hexToModNScalar(s string) *ModNScalar { if err != nil { panic("invalid hex in source file: " + s) } + if len(b) > 32 { + panic("hex in source file overflows uint256: " + s) + } var scalar ModNScalar - if overflow := scalar.SetByteSlice(b); overflow { + if overflow := scalar.SetByteSlice(b); overflow && !allowOverflow { panic("hex in source file overflows mod N scalar: " + s) } if isNegative { @@ -88,6 +94,16 @@ func hexToModNScalar(s string) *ModNScalar { return &scalar } +// mustModNScalar converts the passed hex string into a [ModNScalar] and will +// panic if there is an error. Values that overflow are treated as an error. +// +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustModNScalar(s string) *ModNScalar { + return mustModNScalarInternal(s, false) +} + var ( // The following constants are used to accelerate scalar point // multiplication through the use of the endomorphism: @@ -99,21 +115,21 @@ var ( // // Additionally, see the scalar multiplication function in this file for // details on how they are used. - endoNegLambda = hexToModNScalar("-5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72") + endoNegLambda = mustModNScalar("-5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72") endoBeta = mustFieldVal("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee") - endoNegB1 = hexToModNScalar("e4437ed6010e88286f547fa90abfe4c3") - endoNegB2 = hexToModNScalar("-3086d221a7d46bcde86c90e49284eb15") - endoZ1 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7f") - endoZ2 = hexToModNScalar("e4437ed6010e88286f547fa90abfe4c4221208ac9df506c6") + endoNegB1 = mustModNScalar("e4437ed6010e88286f547fa90abfe4c3") + endoNegB2 = mustModNScalar("-3086d221a7d46bcde86c90e49284eb15") + endoZ1 = mustModNScalar("3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7f") + endoZ2 = mustModNScalar("e4437ed6010e88286f547fa90abfe4c4221208ac9df506c6") // Alternatively, the following parameters are valid as well, however, // benchmarks show them to be about 2% slower in practice. - // endoNegLambda = hexToModNScalar("-ac9c52b33fa3cf1f5ad9e3fd77ed9ba4a880b9fc8ec739c2e0cfc810b51283ce") + // endoNegLambda = mustModNScalar("-ac9c52b33fa3cf1f5ad9e3fd77ed9ba4a880b9fc8ec739c2e0cfc810b51283ce") // endoBeta = mustFieldVal("851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40") - // endoNegB1 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb15") - // endoNegB2 = hexToModNScalar("-114ca50f7a8e2f3f657c1108d9d44cfd8") - // endoZ1 = hexToModNScalar("114ca50f7a8e2f3f657c1108d9d44cfd95fbc92c10fddd145") - // endoZ2 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7f") + // endoNegB1 = mustModNScalar("3086d221a7d46bcde86c90e49284eb15") + // endoNegB2 = mustModNScalar("-114ca50f7a8e2f3f657c1108d9d44cfd8") + // endoZ1 = mustModNScalar("114ca50f7a8e2f3f657c1108d9d44cfd95fbc92c10fddd145") + // endoZ2 = mustModNScalar("3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7f") ) // JacobianPoint is an element of the group formed by the secp256k1 curve in diff --git a/dcrec/secp256k1/curve_bench_test.go b/dcrec/secp256k1/curve_bench_test.go index 0d8983acce..dc7101b5a8 100644 --- a/dcrec/secp256k1/curve_bench_test.go +++ b/dcrec/secp256k1/curve_bench_test.go @@ -57,7 +57,7 @@ func BenchmarkAddNonConstNotZOne(b *testing.B) { // BenchmarkScalarBaseMultNonConst benchmarks multiplying a scalar by the base // point of the curve using whichever variant is active. func BenchmarkScalarBaseMultNonConst(b *testing.B) { - k := hexToModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + k := mustModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") b.ReportAllocs() b.ResetTimer() @@ -70,7 +70,7 @@ func BenchmarkScalarBaseMultNonConst(b *testing.B) { // BenchmarkScalarBaseMultNonConstFast benchmarks multiplying a scalar by the // base point of the curve using the fast variant. func BenchmarkScalarBaseMultNonConstFast(b *testing.B) { - k := hexToModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + k := mustModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") b.ReportAllocs() b.ResetTimer() @@ -83,7 +83,7 @@ func BenchmarkScalarBaseMultNonConstFast(b *testing.B) { // BenchmarkScalarBaseMultNonConstSlow benchmarks multiplying a scalar by the // base point of the curve using the resource-constrained slow variant. func BenchmarkScalarBaseMultNonConstSlow(b *testing.B) { - k := hexToModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + k := mustModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") b.ReportAllocs() b.ResetTimer() @@ -101,7 +101,7 @@ func BenchmarkSplitK(b *testing.B) { // produced scalars. h := "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0" negOne := new(ModNScalar).NegateVal(oneModN) - halfOrder := hexToModNScalar(h) + halfOrder := mustModNScalar(h) halfOrderMOne := new(ModNScalar).Add2(halfOrder, negOne) halfOrderPOne := new(ModNScalar).Add2(halfOrder, oneModN) lambdaMOne := new(ModNScalar).Add2(endoLambda, negOne) @@ -141,7 +141,7 @@ func BenchmarkSplitK(b *testing.B) { // BenchmarkScalarMultNonConst benchmarks multiplying a scalar by an arbitrary // point on the curve. func BenchmarkScalarMultNonConst(b *testing.B) { - k := hexToModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + k := mustModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") point := jacobianPointFromHex( "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", diff --git a/dcrec/secp256k1/curve_test.go b/dcrec/secp256k1/curve_test.go index 4834fe9fcb..b1b4bec009 100644 --- a/dcrec/secp256k1/curve_test.go +++ b/dcrec/secp256k1/curve_test.go @@ -17,7 +17,7 @@ import ( var ( // oneModN is simply the number 1 as a mod n scalar. - oneModN = hexToModNScalar("1") + oneModN = mustModNScalar("1") // endoLambda is the positive version of the lambda constant used in the // endomorphism. It is stored here for convenience and to avoid recomputing @@ -832,7 +832,7 @@ func TestScalarBaseMultJacobian(t *testing.T) { // Parse test data. want := jacobianPointFromHex(test.x1, test.y1, test.z1) wantAffine := jacobianPointFromHex(test.x2, test.y2, "01") - k := hexToModNScalar(test.k) + k := mustModNScalar(test.k) // Ensure the test data is using points that are actually on the curve // (or the point at infinity). @@ -945,7 +945,7 @@ func TestSplitK(t *testing.T) { // produced scalars. h := "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0" negOne := new(ModNScalar).NegateVal(oneModN) - halfOrder := hexToModNScalar(h) + halfOrder := mustModNScalar(h) halfOrderMOne := new(ModNScalar).Add2(halfOrder, negOne) halfOrderPOne := new(ModNScalar).Add2(halfOrder, oneModN) lambdaMOne := new(ModNScalar).Add2(endoLambda, negOne) diff --git a/dcrec/secp256k1/ecdsa/bench_test.go b/dcrec/secp256k1/ecdsa/bench_test.go index 476a70008a..0daaaea044 100644 --- a/dcrec/secp256k1/ecdsa/bench_test.go +++ b/dcrec/secp256k1/ecdsa/bench_test.go @@ -12,11 +12,11 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" ) -// hexToModNScalar converts the passed hex string into a ModNScalar and will +// mustModNScalar converts the passed hex string into a ModNScalar and will // panic if there is an error. This is only provided for the hard-coded // constants so errors in the source code can be detected. It will only (and // must only) be called with hard-coded values. -func hexToModNScalar(s string) *secp256k1.ModNScalar { +func mustModNScalar(s string) *secp256k1.ModNScalar { b, err := hex.DecodeString(s) if err != nil { panic("invalid hex in source file: " + s) @@ -57,8 +57,8 @@ func BenchmarkSigVerify(b *testing.B) { // Double sha256 of []byte{0x01, 0x02, 0x03, 0x04} msgHash := hexToBytes("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0") sig := NewSignature( - hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), - hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), + mustModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), + mustModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), ) if !sig.Verify(msgHash, pubKey) { @@ -76,7 +76,7 @@ func BenchmarkSigVerify(b *testing.B) { // BenchmarkSign benchmarks how long it takes to sign a message. func BenchmarkSign(b *testing.B) { // Randomly generated keypair. - d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + d := mustModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") privKey := secp256k1.NewPrivateKey(d) // blake256 of []byte{0x01, 0x02, 0x03, 0x04}. @@ -96,8 +96,8 @@ func BenchmarkSigSerialize(b *testing.B) { // Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d // Signature for double sha256 of []byte{0x01, 0x02, 0x03, 0x04}. sig := NewSignature( - hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), - hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), + mustModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), + mustModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), ) b.ReportAllocs() @@ -132,7 +132,7 @@ func BenchmarkNonceRFC6979(b *testing.B) { // BenchmarkSignCompact benchmarks how long it takes to produce a compact // signature for a message. func BenchmarkSignCompact(b *testing.B) { - d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + d := mustModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") privKey := secp256k1.NewPrivateKey(d) // blake256 of []byte{0x01, 0x02, 0x03, 0x04}. diff --git a/dcrec/secp256k1/ecdsa/signature_test.go b/dcrec/secp256k1/ecdsa/signature_test.go index c5aa93096a..7663c7952c 100644 --- a/dcrec/secp256k1/ecdsa/signature_test.go +++ b/dcrec/secp256k1/ecdsa/signature_test.go @@ -221,8 +221,8 @@ func TestSignatureSerialize(t *testing.T) { // 0437cd7f8525ceed2324359c2d0ba26006d92d85 "valid 1 - r and s most significant bits are zero", &Signature{ - r: *hexToModNScalar("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), - s: *hexToModNScalar("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), + r: *mustModNScalar("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), + s: *mustModNScalar("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), }, hexToBytes("304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d62" + "4c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc" + @@ -232,8 +232,8 @@ func TestSignatureSerialize(t *testing.T) { // cb00f8a0573b18faa8c4f467b049f5d202bf1101d9ef2633bc611be70376a4b4 "valid 2 - r most significant bit is one", &Signature{ - r: *hexToModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), - s: *hexToModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + r: *mustModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + s: *mustModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), }, hexToBytes("304502210082235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c" + "30a23b0afbb8d178abcf3022024bf68e256c534ddfaf966bf908deb94430" + @@ -247,8 +247,8 @@ func TestSignatureSerialize(t *testing.T) { // modified to expect the equally valid low S signature variant. "valid 3 - s most significant bit is one", &Signature{ - r: *hexToModNScalar("1cadddc2838598fee7dc35a12b340c6bde8b389f7bfd19a1252a17c4b5ed2d71"), - s: *hexToModNScalar("c1a251bbecb14b058a8bd77f65de87e51c47e95904f4c0e9d52eddc21c1415ac"), + r: *mustModNScalar("1cadddc2838598fee7dc35a12b340c6bde8b389f7bfd19a1252a17c4b5ed2d71"), + s: *mustModNScalar("c1a251bbecb14b058a8bd77f65de87e51c47e95904f4c0e9d52eddc21c1415ac"), }, hexToBytes("304402201cadddc2838598fee7dc35a12b340c6bde8b389f7bfd1" + "9a1252a17c4b5ed2d7102203e5dae44134eb4fa757428809a2178199e66f" + @@ -519,11 +519,11 @@ func TestSignAndVerify(t *testing.T) { tests := signTests(t) for _, test := range tests { - privKey := secp256k1.NewPrivateKey(hexToModNScalar(test.key)) + privKey := secp256k1.NewPrivateKey(mustModNScalar(test.key)) hash := hexToBytes(test.hash) - nonce := hexToModNScalar(test.nonce) - wantSigR := hexToModNScalar(test.wantSigR) - wantSigS := hexToModNScalar(test.wantSigS) + nonce := mustModNScalar(test.nonce) + wantSigR := mustModNScalar(test.wantSigR) + wantSigS := mustModNScalar(test.wantSigS) wantSig := NewSignature(wantSigR, wantSigS).Serialize() // Sign the hash of the message with the given private key and nonce. @@ -685,9 +685,9 @@ func TestSignFailures(t *testing.T) { }} for _, test := range tests { - privKey := hexToModNScalar(test.key) + privKey := mustModNScalar(test.key) hash := hexToBytes(test.hash) - nonce := hexToModNScalar(test.nonce) + nonce := mustModNScalar(test.nonce) // Ensure the signing is NOT successful. sig, _, success := sign(privKey, nonce, hash) @@ -736,10 +736,10 @@ func TestVerifyFailures(t *testing.T) { }} for _, test := range tests { - privKey := hexToModNScalar(test.key) + privKey := mustModNScalar(test.key) hash := hexToBytes(test.hash) - r := hexToModNScalar(test.r) - s := hexToModNScalar(test.s) + r := mustModNScalar(test.r) + s := mustModNScalar(test.s) sig := NewSignature(r, s) // Ensure the verification is NOT successful. @@ -756,16 +756,16 @@ func TestVerifyFailures(t *testing.T) { // works as expected. func TestSignatureIsEqual(t *testing.T) { sig1 := &Signature{ - r: *hexToModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), - s: *hexToModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + r: *mustModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + s: *mustModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), } sig1Copy := &Signature{ - r: *hexToModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), - s: *hexToModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + r: *mustModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + s: *mustModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), } sig2 := &Signature{ - r: *hexToModNScalar("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), - s: *hexToModNScalar("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), + r: *mustModNScalar("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), + s: *mustModNScalar("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), } if !sig1.IsEqual(sig1) { @@ -795,7 +795,7 @@ func TestSignAndRecoverCompact(t *testing.T) { } // Parse test data. - privKey := secp256k1.NewPrivateKey(hexToModNScalar(test.key)) + privKey := secp256k1.NewPrivateKey(mustModNScalar(test.key)) pubKey := privKey.PubKey() hash := hexToBytes(test.hash) wantSig := hexToBytes("00" + test.wantSigR + test.wantSigS) diff --git a/dcrec/secp256k1/modnscalar_test.go b/dcrec/secp256k1/modnscalar_test.go index 1500f8accf..251708a6ff 100644 --- a/dcrec/secp256k1/modnscalar_test.go +++ b/dcrec/secp256k1/modnscalar_test.go @@ -6,7 +6,6 @@ package secp256k1 import ( "bytes" - "encoding/hex" "fmt" "math/big" "math/rand" @@ -15,21 +14,15 @@ import ( "time" ) -// SetHex interprets the provided hex string as a 256-bit big-endian unsigned -// integer (meaning it is truncated to the first 32 bytes), reduces it modulo -// the group order and sets the scalar to the result. +// mustModNScalarWithOverflow converts the passed hex string into a [ModNScalar] +// and will panic if there is an error. Values that overflow are NOT treated as +// an error. // -// This is NOT constant time. -// -// The scalar is returned to support chaining. This enables syntax like: -// s := new(ModNScalar).SetHex("0abc").Add(1) so that s = 0x0abc + 1 -func (s *ModNScalar) SetHex(hexString string) *ModNScalar { - if len(hexString)%2 != 0 { - hexString = "0" + hexString - } - bytes, _ := hex.DecodeString(hexString) - s.SetByteSlice(bytes) - return s +// This is only provided for the hard-coded constants so errors in the source +// code can be detected. It will only (and must only) be called with hard-coded +// values. +func mustModNScalarWithOverflow(s string) *ModNScalar { + return mustModNScalarInternal(s, true) } // randModNScalar returns a mod N scalar created from a random value generated @@ -69,8 +62,8 @@ func randIntAndModNScalar(t *testing.T, rng *rand.Rand) (*big.Int, *ModNScalar) // TestModNScalarZero ensures that zeroing a scalar modulo the group order works // as expected. func TestModNScalarZero(t *testing.T) { - var s ModNScalar - s.SetHex("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") + valHex := "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + s := mustModNScalar(valHex) s.Zero() for idx, rawInt := range s.n { if rawInt != 0 { @@ -354,7 +347,7 @@ func TestModNScalarBytes(t *testing.T) { }} for _, test := range tests { - s := new(ModNScalar).SetHex(test.in) + s := mustModNScalarWithOverflow(test.in) expected := hexToBytes(test.expected) // Ensure getting the bytes works as expected. @@ -423,7 +416,7 @@ func TestModNScalarIsOdd(t *testing.T) { }} for _, test := range tests { - result := new(ModNScalar).SetHex(test.in).IsOdd() + result := mustModNScalarWithOverflow(test.in).IsOdd() if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, result, test.expected) @@ -478,8 +471,8 @@ func TestModNScalarEquals(t *testing.T) { }} for _, test := range tests { - s1 := new(ModNScalar).SetHex(test.in1) - s2 := new(ModNScalar).SetHex(test.in2) + s1 := mustModNScalarWithOverflow(test.in1) + s2 := mustModNScalarWithOverflow(test.in2) result := s1.Equals(s2) if result != test.expected { t.Errorf("%s: wrong result -- got: %v, want: %v", test.name, result, @@ -601,9 +594,9 @@ func TestModNScalarAdd(t *testing.T) { for _, test := range tests { // Parse test hex. - s1 := new(ModNScalar).SetHex(test.in1) - s2 := new(ModNScalar).SetHex(test.in2) - expected := new(ModNScalar).SetHex(test.expected) + s1 := mustModNScalarWithOverflow(test.in1) + s2 := mustModNScalarWithOverflow(test.in2) + expected := mustModNScalar(test.expected) // Ensure the result has the expected value. s1.Add(s2) @@ -800,9 +793,9 @@ func TestModNScalarMul(t *testing.T) { }} for _, test := range tests { - v1 := new(ModNScalar).SetHex(test.in1) - v2 := new(ModNScalar).SetHex(test.in2) - expected := new(ModNScalar).SetHex(test.expected) + v1 := mustModNScalarWithOverflow(test.in1) + v2 := mustModNScalarWithOverflow(test.in2) + expected := mustModNScalar(test.expected) // Ensure multiplying two other values produces the expected result. result := new(ModNScalar).Mul2(v1, v2) @@ -883,6 +876,10 @@ func TestModNScalarSquare(t *testing.T) { name: "group order - 1", in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", expected: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "group order + 1", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + expected: "1", }, { name: "overflow in word eight", in: "100000000000000000000000000000000", @@ -926,8 +923,8 @@ func TestModNScalarSquare(t *testing.T) { }} for _, test := range tests { - v := new(ModNScalar).SetHex(test.in) - expected := new(ModNScalar).SetHex(test.expected) + v := mustModNScalarWithOverflow(test.in) + expected := mustModNScalar(test.expected) // Ensure squaring another value produces the expected result. result := new(ModNScalar).SquareVal(v) @@ -1036,8 +1033,8 @@ func TestModNScalarNegate(t *testing.T) { }} for _, test := range tests { - s := new(ModNScalar).SetHex(test.in) - expected := new(ModNScalar).SetHex(test.expected) + s := mustModNScalarWithOverflow(test.in) + expected := mustModNScalar(test.expected) // Ensure negating another value produces the expected result. result := new(ModNScalar).NegateVal(s) @@ -1146,8 +1143,8 @@ func TestModNScalarInverseNonConst(t *testing.T) { }} for _, test := range tests { - s := new(ModNScalar).SetHex(test.in) - expected := new(ModNScalar).SetHex(test.expected) + s := mustModNScalarWithOverflow(test.in) + expected := mustModNScalar(test.expected) // Ensure calculating the multiplicative inverse of another value // produces the expected result. @@ -1250,7 +1247,7 @@ func TestModNScalarIsOverHalfOrder(t *testing.T) { }} for _, test := range tests { - result := new(ModNScalar).SetHex(test.in).IsOverHalfOrder() + result := mustModNScalarWithOverflow(test.in).IsOverHalfOrder() if result != test.expected { t.Errorf("%s: unexpected result -- got: %v, want: %v", test.name, result, test.expected) diff --git a/dcrec/secp256k1/privkey_test.go b/dcrec/secp256k1/privkey_test.go index 9d4b98bc2a..2050e12d42 100644 --- a/dcrec/secp256k1/privkey_test.go +++ b/dcrec/secp256k1/privkey_test.go @@ -59,7 +59,7 @@ func TestGeneratePrivateKeyCorners(t *testing.T) { // 2nd invocation: The curve order // 3rd invocation: The curve order + 1 // 4th invocation: 1 (32-byte big endian) - oneModN := hexToModNScalar("01") + oneModN := mustModNScalar("01") var numReads int mockReader := mockPrivateKeyReaderFunc(func(p []byte) (int, error) { numReads++ @@ -152,7 +152,7 @@ func TestPrivKeys(t *testing.T) { func TestPrivateKeyZero(t *testing.T) { // Create a new private key and zero the initial key material that is now // copied into the private key. - key := new(ModNScalar).SetHex("eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694") + key := mustModNScalar("eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694") privKey := NewPrivateKey(key) key.Zero() diff --git a/dcrec/secp256k1/schnorr/signature_bench_test.go b/dcrec/secp256k1/schnorr/signature_bench_test.go index 72a042b432..8b12212a25 100644 --- a/dcrec/secp256k1/schnorr/signature_bench_test.go +++ b/dcrec/secp256k1/schnorr/signature_bench_test.go @@ -11,11 +11,11 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" ) -// hexToModNScalar converts the passed hex string into a ModNScalar and will +// mustModNScalar converts the passed hex string into a ModNScalar and will // panic if there is an error. This is only provided for the hard-coded // constants so errors in the source code can be detected. It will only (and // must only) be called with hard-coded values. -func hexToModNScalar(s string) *secp256k1.ModNScalar { +func mustModNScalar(s string) *secp256k1.ModNScalar { b, err := hex.DecodeString(s) if err != nil { panic("invalid hex in source file: " + s) @@ -58,7 +58,7 @@ func mustFieldVal(s string) *secp256k1.FieldVal { // BenchmarkSign benchmarks how long it takes to sign a message. func BenchmarkSign(b *testing.B) { // From randomly generated keypair. - d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + d := mustModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") privKey := secp256k1.NewPrivateKey(d) // blake256 of []byte{0x01, 0x02, 0x03, 0x04}. @@ -74,7 +74,7 @@ func BenchmarkSign(b *testing.B) { // BenchmarkSigVerify benchmarks how long it takes to verify Schnorr signatures. func BenchmarkSigVerify(b *testing.B) { // From randomly generated keypair. - d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + d := mustModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") privKey := secp256k1.NewPrivateKey(d) pubKey := secp256k1.NewPublicKey( mustFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), @@ -98,7 +98,7 @@ func BenchmarkSigVerify(b *testing.B) { // signatures. func BenchmarkSigSerialize(b *testing.B) { // From randomly generated keypair. - d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + d := mustModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") privKey := secp256k1.NewPrivateKey(d) // blake256 of []byte{0x01, 0x02, 0x03, 0x04}. diff --git a/dcrec/secp256k1/schnorr/signature_test.go b/dcrec/secp256k1/schnorr/signature_test.go index 7cc99412df..efbce1aef5 100644 --- a/dcrec/secp256k1/schnorr/signature_test.go +++ b/dcrec/secp256k1/schnorr/signature_test.go @@ -256,13 +256,13 @@ func TestSchnorrSignAndVerify(t *testing.T) { }} for _, test := range tests { - privKey := hexToModNScalar(test.key) + privKey := mustModNScalar(test.key) msg := hexToBytes(test.msg) hash := hexToBytes(test.hash) - nonce := hexToModNScalar(test.nonce) + nonce := mustModNScalar(test.nonce) wantSig := hexToBytes(test.expected) wantSigR := mustFieldVal(test.expected[:64]) - wantSigS := hexToModNScalar(test.expected[64:]) + wantSigS := mustModNScalar(test.expected[64:]) // Ensure the test data is sane by comparing the provided hashed message // and nonce, in the case rfc6979 was used, to their calculated values. @@ -321,7 +321,7 @@ func TestSchnorrSignAndVerify(t *testing.T) { } // Ensure the produced signature verifies as well. - pubKey := secp256k1.NewPrivateKey(hexToModNScalar(test.key)).PubKey() + pubKey := secp256k1.NewPrivateKey(mustModNScalar(test.key)).PubKey() err = schnorrVerify(gotSig, hash, pubKey) if err != nil { t.Errorf("%s: signature failed to verify: %v", test.name, err) From c793c94097f2c97c0a22f410af79ddb35688a4c8 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 28 Jun 2026 04:21:53 -0500 Subject: [PATCH 5/6] secp256k1: Remove inert field tests. This removes a few 10x26 field tests that aren't actually testing anything related to the associated methods because they involve unreduced outputs which are impossible. The outputs are necessarily reduced when they are parsed by the test code which mean, at best, it's testing a few isolated cases of parsing reduction, but that is explicitly tested much more thoroughly elsewhere. --- dcrec/secp256k1/field_test.go | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/dcrec/secp256k1/field_test.go b/dcrec/secp256k1/field_test.go index 8370095fae..f8dabc35ee 100644 --- a/dcrec/secp256k1/field_test.go +++ b/dcrec/secp256k1/field_test.go @@ -959,10 +959,6 @@ func TestFieldNegate(t *testing.T) { name: "secp256k1 prime (direct val in with 0 out)", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", expected: "0", - }, { - name: "secp256k1 prime (0 in with direct val out)", - in: "0", - expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "1 -> secp256k1 prime - 1", in: "1", @@ -999,7 +995,7 @@ func TestFieldNegate(t *testing.T) { for _, test := range tests { f := mustFieldValWithOverflow(test.in).Normalize() - expected := mustFieldValWithOverflow(test.expected).Normalize() + expected := mustFieldVal(test.expected).Normalize() // Ensure negating another value produces the expected result. result := new(FieldVal).NegateVal(f, 1).Normalize() @@ -1372,10 +1368,6 @@ func TestFieldSquare(t *testing.T) { name: "secp256k1 prime (direct val in with 0 out)", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", expected: "0", - }, { - name: "secp256k1 prime (0 in with direct val out)", - in: "0", - expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "secp256k1 prime - 1", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", @@ -1404,7 +1396,7 @@ func TestFieldSquare(t *testing.T) { for _, test := range tests { f := mustFieldValWithOverflow(test.in).Normalize() - expected := mustFieldValWithOverflow(test.expected).Normalize() + expected := mustFieldVal(test.expected).Normalize() // Ensure squaring the value with the result going to another variable // produces the expected result. @@ -1444,11 +1436,6 @@ func TestFieldSquareRoot(t *testing.T) { in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", valid: true, want: "0", - }, { - name: "secp256k1 prime (as 0 in direct val out)", - in: "0", - valid: true, - want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "secp256k1 prime-1", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", @@ -1503,7 +1490,7 @@ func TestFieldSquareRoot(t *testing.T) { for _, test := range tests { input := mustFieldValWithOverflow(test.in).Normalize() - want := mustFieldValWithOverflow(test.want).Normalize() + want := mustFieldVal(test.want).Normalize() // Calculate the square root and enusre the validity flag matches the // expected value. @@ -1584,10 +1571,6 @@ func TestFieldInverse(t *testing.T) { name: "secp256k1 prime (direct val in with 0 out)", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", expected: "0", - }, { - name: "secp256k1 prime (0 in with direct val out)", - in: "0", - expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "secp256k1 prime - 1", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", @@ -1616,7 +1599,7 @@ func TestFieldInverse(t *testing.T) { for _, test := range tests { f := mustFieldValWithOverflow(test.in).Normalize() - expected := mustFieldValWithOverflow(test.expected).Normalize() + expected := mustFieldVal(test.expected).Normalize() result := f.Inverse().Normalize() if !result.Equals(expected) { t.Errorf("%s: d wrong result\ngot: %v\nwant: %v", test.name, result, From c2d2b545340943071bf4e943ad6b2121f52d9b67 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 28 Jun 2026 04:21:56 -0500 Subject: [PATCH 6/6] secp256k1: Remove inert field64 tests. This removes a few 4x64 field tests that aren't actually testing anything related to the associated methods because they involve unreduced outputs which are impossible. The outputs are necessarily reduced when they are parsed by the test code which mean, at best, it's testing a few isolated cases of parsing reduction, but that is explicitly tested much more thoroughly elsewhere. --- dcrec/secp256k1/field64_test.go | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/dcrec/secp256k1/field64_test.go b/dcrec/secp256k1/field64_test.go index 7d384d0336..674a9894c8 100644 --- a/dcrec/secp256k1/field64_test.go +++ b/dcrec/secp256k1/field64_test.go @@ -567,10 +567,6 @@ func TestField64Negate(t *testing.T) { name: "secp256k1 prime (direct val in with 0 out)", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", expected: "0", - }, { - name: "secp256k1 prime (0 in with direct val out)", - in: "0", - expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "1 -> secp256k1 prime - 1", in: "1", @@ -607,7 +603,7 @@ func TestField64Negate(t *testing.T) { for _, test := range tests { f := mustFieldVal64WithOverflow(test.in) - expected := mustFieldVal64WithOverflow(test.expected) + expected := mustFieldVal64(test.expected) // Ensure negating another value produces the expected result. result := new(FieldVal64).NegateVal(f) @@ -1025,10 +1021,6 @@ func TestField64Square(t *testing.T) { name: "secp256k1 prime (direct val in with 0 out)", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", expected: "0", - }, { - name: "secp256k1 prime (0 in with direct val out)", - in: "0", - expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "secp256k1 prime - 1", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", @@ -1057,7 +1049,7 @@ func TestField64Square(t *testing.T) { for _, test := range tests { f := mustFieldVal64WithOverflow(test.in) - expected := mustFieldVal64WithOverflow(test.expected) + expected := mustFieldVal64(test.expected) // Ensure squaring the value with the result going to another variable // produces the expected result. @@ -1097,11 +1089,6 @@ func TestField64SquareRoot(t *testing.T) { in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", valid: true, want: "0", - }, { - name: "secp256k1 prime (as 0 in direct val out)", - in: "0", - valid: true, - want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "secp256k1 prime-1", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", @@ -1156,7 +1143,7 @@ func TestField64SquareRoot(t *testing.T) { for _, test := range tests { input := mustFieldVal64WithOverflow(test.in) - want := mustFieldVal64WithOverflow(test.want) + want := mustFieldVal64(test.want) // Calculate the square root and ensure the validity flag matches the // expected value. @@ -1236,10 +1223,6 @@ func TestField64Inverse(t *testing.T) { name: "secp256k1 prime (direct val in with 0 out)", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", expected: "0", - }, { - name: "secp256k1 prime (0 in with direct val out)", - in: "0", - expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", }, { name: "secp256k1 prime - 1", in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", @@ -1268,7 +1251,7 @@ func TestField64Inverse(t *testing.T) { for _, test := range tests { f := mustFieldVal64WithOverflow(test.in) - expected := mustFieldVal64WithOverflow(test.expected) + expected := mustFieldVal64(test.expected) result := f.Inverse() if !result.Equals(expected) { t.Errorf("%s: wrong result\ngot: %x\nwant: %x", test.name,