From 2ee2ebeb678398d3f9333a2cfa937378efe27cfb Mon Sep 17 00:00:00 2001 From: SeedHammer <93251679+seedhammer@users.noreply.github.com> Date: Tue, 19 Mar 2024 17:02:43 +0000 Subject: [PATCH] secp256k1: Add TinyGo support. The pre-computed table for speeding up ScalarBaseMultNonConst is several hundred kilobytes in the binary and even more when unpacked into working memory. Special-case ScalarBaseMultNonConst to fall back to ScalarMultNonConst when the 'tinygo' tag is specified, which is true when building a Go program with TinyGo. --- dcrec/secp256k1/curve.go | 13 +++++++++++++ dcrec/secp256k1/curve_embedded.go | 14 ++++++++++++++ dcrec/secp256k1/curve_precompute.go | 14 ++++++++++++++ dcrec/secp256k1/curve_test.go | 21 +++++++++++++++++++-- 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 dcrec/secp256k1/curve_embedded.go create mode 100644 dcrec/secp256k1/curve_precompute.go diff --git a/dcrec/secp256k1/curve.go b/dcrec/secp256k1/curve.go index c9d47f3078..86765ade42 100644 --- a/dcrec/secp256k1/curve.go +++ b/dcrec/secp256k1/curve.go @@ -1221,6 +1221,19 @@ func ScalarMultNonConst(k *ModNScalar, point, result *JacobianPoint) { // // NOTE: The resulting point will be normalized. func ScalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) { + scalarBaseMultNonConst(k, result) +} + +// scalarBaseMultNonConstSlow computes k*G through ScalarMultNonConst. +func scalarBaseMultNonConstSlow(k *ModNScalar, result *JacobianPoint) { + var G JacobianPoint + bigAffineToJacobian(curveParams.Gx, curveParams.Gy, &G) + ScalarMultNonConst(k, &G, result) +} + +// scalarBaseMultNonConstFast computes k*G through the precomputed lookup +// tables. +func scalarBaseMultNonConstFast(k *ModNScalar, result *JacobianPoint) { bytePoints := s256BytePoints() // Start with the point at infinity. diff --git a/dcrec/secp256k1/curve_embedded.go b/dcrec/secp256k1/curve_embedded.go new file mode 100644 index 0000000000..16288318c1 --- /dev/null +++ b/dcrec/secp256k1/curve_embedded.go @@ -0,0 +1,14 @@ +// Copyright (c) 2024 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +//go:build tinygo + +package secp256k1 + +// This file contains the variants suitable for +// memory or storage constrained environments. + +func scalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) { + scalarBaseMultNonConstSlow(k, result) +} diff --git a/dcrec/secp256k1/curve_precompute.go b/dcrec/secp256k1/curve_precompute.go new file mode 100644 index 0000000000..cf84f770ed --- /dev/null +++ b/dcrec/secp256k1/curve_precompute.go @@ -0,0 +1,14 @@ +// Copyright (c) 2024 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +//go:build !tinygo + +package secp256k1 + +// This file contains the variants that don't fit in +// memory or storage constrained environments. + +func scalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) { + scalarBaseMultNonConstFast(k, result) +} diff --git a/dcrec/secp256k1/curve_test.go b/dcrec/secp256k1/curve_test.go index a669a8f9a0..7c74c46cf6 100644 --- a/dcrec/secp256k1/curve_test.go +++ b/dcrec/secp256k1/curve_test.go @@ -565,7 +565,7 @@ func TestScalarBaseMultJacobian(t *testing.T) { // Ensure the result matches the expected value in Jacobian coordinates. var r JacobianPoint - ScalarBaseMultNonConst(k, &r) + scalarBaseMultNonConstFast(k, &r) if !r.IsStrictlyEqual(&want) { t.Errorf("%q: wrong result:\ngot: (%s, %s, %s)\nwant: (%s, %s, %s)", test.name, r.X, r.Y, r.Z, want.X, want.Y, want.Z) @@ -579,6 +579,16 @@ func TestScalarBaseMultJacobian(t *testing.T) { test.name, r.X, r.Y, wantAffine.X, wantAffine.Y) continue } + + // The slow fallback doesn't return identical Jacobian coordinates, + // but the affine coordinates should match. + scalarBaseMultNonConstSlow(k, &r) + r.ToAffine() + if !r.IsStrictlyEqual(&wantAffine) { + t.Errorf("%q: wrong affine result:\ngot: (%s, %s)\nwant: (%s, %s)", + test.name, r.X, r.Y, wantAffine.X, wantAffine.Y) + continue + } } } @@ -827,7 +837,14 @@ func TestScalarMultJacobianRandom(t *testing.T) { // Ensure the point calculated above matches the product of the scalars // times the base point. - ScalarBaseMultNonConst(product, &result) + scalarBaseMultNonConstFast(product, &result) + if !isSamePoint(&chained, &result) { + t.Fatalf("unexpected result \ngot (%v, %v, %v)\n"+ + "want (%v, %v, %v)", chained.X, chained.Y, chained.Z, result.X, + result.Y, result.Z) + } + + scalarBaseMultNonConstSlow(product, &result) if !isSamePoint(&chained, &result) { t.Fatalf("unexpected result \ngot (%v, %v, %v)\n"+ "want (%v, %v, %v)", chained.X, chained.Y, chained.Z, result.X,