diff --git a/dcrec/secp256k1/curve.go b/dcrec/secp256k1/curve.go index 6d6d669f19..2df9f6b4fd 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. @@ -26,27 +26,49 @@ 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 } -// 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 { +// 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) +} + +// 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 @@ -59,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 { @@ -69,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: @@ -80,21 +115,21 @@ var ( // // Additionally, see the scalar multiplication function in this file for // details on how they are used. - endoNegLambda = hexToModNScalar("-5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72") - endoBeta = hexToFieldVal("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee") - endoNegB1 = hexToModNScalar("e4437ed6010e88286f547fa90abfe4c3") - endoNegB2 = hexToModNScalar("-3086d221a7d46bcde86c90e49284eb15") - endoZ1 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7f") - endoZ2 = hexToModNScalar("e4437ed6010e88286f547fa90abfe4c4221208ac9df506c6") + endoNegLambda = mustModNScalar("-5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72") + endoBeta = mustFieldVal("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee") + 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") - // endoBeta = hexToFieldVal("851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40") - // endoNegB1 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb15") - // endoNegB2 = hexToModNScalar("-114ca50f7a8e2f3f657c1108d9d44cfd8") - // endoZ1 = hexToModNScalar("114ca50f7a8e2f3f657c1108d9d44cfd95fbc92c10fddd145") - // endoZ2 = hexToModNScalar("3086d221a7d46bcde86c90e49284eb153daa8a1471e8ca7f") + // endoNegLambda = mustModNScalar("-ac9c52b33fa3cf1f5ad9e3fd77ed9ba4a880b9fc8ec739c2e0cfc810b51283ce") + // endoBeta = mustFieldVal("851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40") + // 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 @@ -1246,7 +1281,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/curve_bench_test.go b/dcrec/secp256k1/curve_bench_test.go index 5f0ed4df23..dc7101b5a8 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() @@ -55,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() @@ -68,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() @@ -81,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() @@ -99,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) @@ -139,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 8868674226..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 @@ -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 } @@ -828,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). @@ -941,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) @@ -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/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/bench_test.go b/dcrec/secp256k1/ecdsa/bench_test.go index ed0c99d67a..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) @@ -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,15 +50,15 @@ 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} 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}. @@ -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/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/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/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/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..674a9894c8 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, @@ -538,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", @@ -577,8 +602,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 := mustFieldVal64(test.expected) // Ensure negating another value produces the expected result. result := new(FieldVal64).NegateVal(f) @@ -654,8 +679,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 +761,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 +814,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 +895,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 +982,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. @@ -996,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", @@ -1027,8 +1048,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 := mustFieldVal64(test.expected) // Ensure squaring the value with the result going to another variable // produces the expected result. @@ -1068,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", @@ -1126,8 +1142,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 := mustFieldVal64(test.want) // Calculate the square root and ensure the validity flag matches the // expected value. @@ -1207,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", @@ -1238,8 +1250,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 := mustFieldVal64(test.expected) result := f.Inverse() if !result.Equals(expected) { t.Errorf("%s: wrong result\ngot: %x\nwant: %x", test.name, @@ -1308,7 +1320,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) diff --git a/dcrec/secp256k1/field_bench_test.go b/dcrec/secp256k1/field_bench_test.go index f89c420b72..91e37ab528 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{ @@ -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 5ae7604732..f8dabc35ee 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. @@ -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 { @@ -383,8 +376,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 +413,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 @@ -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, @@ -851,8 +832,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 @@ -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, @@ -907,7 +888,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 @@ -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, @@ -963,8 +944,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 @@ -978,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", @@ -1017,8 +994,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 := mustFieldVal(test.expected).Normalize() // Ensure negating another value produces the expected result. result := new(FieldVal).NegateVal(f, 1).Normalize() @@ -1038,8 +1015,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 @@ -1094,8 +1071,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, @@ -1105,8 +1082,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 @@ -1177,9 +1154,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. @@ -1202,7 +1179,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 @@ -1270,8 +1247,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, @@ -1281,8 +1258,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 @@ -1352,9 +1329,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. @@ -1376,8 +1353,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 @@ -1391,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", @@ -1422,8 +1395,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 := mustFieldVal(test.expected).Normalize() // Ensure squaring the value with the result going to another variable // produces the expected result. @@ -1446,7 +1419,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 @@ -1463,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", @@ -1521,8 +1489,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 := mustFieldVal(test.want).Normalize() // Calculate the square root and enusre the validity flag matches the // expected value. @@ -1588,8 +1556,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 @@ -1603,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", @@ -1634,8 +1598,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 := 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, @@ -1712,7 +1676,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/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/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/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) 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..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) @@ -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) @@ -58,7 +58,7 @@ func hexToFieldVal(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,11 +74,11 @@ 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( - hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), - hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), + mustFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), + mustFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), ) // blake256 of []byte{0x01, 0x02, 0x03, 0x04}. @@ -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 fb252cf0b0..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 := hexToFieldVal(test.expected[:64]) - wantSigS := hexToModNScalar(test.expected[64:]) + wantSigR := mustFieldVal(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) @@ -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