This updates the internal implementation of the PublicKey type to use
the FieldVal type instead of big.Ints and modifies NewPublicKey to
accept the FieldVals and updates all callers and tests in the repository
accordingly.
Public key parsing and serialization are now completely free of
big.Ints.
Finally, it moves the X and Y methods to the ellipticadaptor.go file
since they are only intended to provide interop with the standard
library elliptic curve adaptor code.
This reduces the number of allocations needed in the ecdsa compact
signature recovery as well as schnorr signature verification and results
in a slight speedup for both.
The following benchmarks show the before and after comparison:
benchmark old ns/op new ns/op delta
----------------------------------------------------------
BenchmarkRecoverCompact 198012 196173 -0.93%
BenchmarkSigVerify 179170 177805 -0.76%
benchmark old allocs new allocs delta
-----------------------------------------------------------
BenchmarkRecoverCompact 36 32 -11.11%
BenchmarkSigVerify 19 15 -21.05%
benchmark old bytes new bytes delta
-----------------------------------------------------------
BenchmarkRecoverCompact 1745 1617 -7.34%
BenchmarkSigVerify 977 848 -13.20%
This adds a new method to the PublicKey type which can be used to
determine if the public key is on the secp256k1 curve directly.
It also adds tests to ensure proper functionality.
This updates the public key parsing tests to test for the explicit
reason for the failure to ensure that each test is actually testing the
intended condition.
This introduces a new error type and associated error code for public
key errors which can be used with the errors.Is and errors.As interfaces
to programmatically detect the failure reason and updates the public key
parsing code to return appropriate error codes.
It includes full tests for the new error infrastructure.
This reworks the tests related to public keys to them more consistent
with the rest of the package and improve their coverage to include all
of the possible error paths.
It also brings back the hybrid pubkey parsing to match what the function
comment claims and corrects the tests accordingly. The restriction as
to which specific types of pubkeys are allowed must be (and is) enforced
by the opcode(s) that interpret them as opposed to the lib that
implements the more generic specification.
The following is an overview of the changes:
- Convert all tests to use hex strings
- Add and verify the expected x and y coordinates from a parsed pubkey
- Bring back support for parsing hybrid keys
- Correct remaining existing tests for hybrid pubkeys
- Add negative tests for all combinations of encoding what would
otherwise be valid pubkey with the wrong format byte and y coord
oddness
- Add negative tests for x and y coordinates == p for the uncompressed,
compressed, and hybrid formats
- Add negative tests for all formats such that either the x or y
coordinate are > p and are constructed in such a way that they would
be valid if the coordinate is taken mod p to ensure implementations
don't incorrectly ignore the range check
This updates the code to use the recently-added PutBytesUnchecked of
the FieldVal and ModNScalar types to serialize directly into larger
buffers where possible to avoid the need for additional copies.
This updates the code to use the recently-added PutBytesUnchecked of
the FieldVal and ModNScalar types to serialize directly into larger
buffers where possible to avoid the need for additional copies.
The following benchmarks show a before and after comparison of the
affected operations:
benchmark old ns/op new ns/op delta
--------------------------------------------------------
BenchmarkSign 52682 52284 -0.76%
BenchmarkSigVerify 178050 176977 -0.60%
BenchmarkSigSerialize 87.5 73.3 -16.23%
benchmark old allocs new allocs delta
--------------------------------------------------------
BenchmarkSign 16 16 +0.00%
BenchmarkSigVerify 21 21 +0.00%
BenchmarkSigSerialize 1 1 +0.00%
benchmark old bytes new bytes delta
--------------------------------------------------------
BenchmarkSign 960 960 +0.00%
BenchmarkSigVerify 1105 1105 +0.00%
This optimizes the various internal implementations that make use of
secp256k1 to use the specialized ModNScalar and JacobianPoint types
instead of big.Ints.
The following benchmarks show a before and after comparison:
benchmark old ns/op new ns/op delta
-----------------------------------------------------------
BenchmarkDeriveHardened 3702 3547 -4.19%
BenchmarkDeriveNormal 4053 3587 -11.50%
BenchmarkPrivToPub 94.7 94.0 -0.74%
BenchmarkDeserialize 5825 5797 -0.48%
BenchmarkSerialize 9509 9402 -1.13%
benchmark old allocs new allocs delta
-----------------------------------------------------------
BenchmarkDeriveHardened 14 12 -14.29%
BenchmarkDeriveNormal 15 12 -20.00%
BenchmarkPrivToPub 1 1 +0.00%
BenchmarkDeserialize 4 3 -25.00%
BenchmarkSerialize 3 3 +0.00%
benchmark old bytes new bytes delta
-----------------------------------------------------------
BenchmarkDeriveHardened 1360 1232 -9.41%
BenchmarkDeriveNormal 1409 1232 -12.56%
BenchmarkPrivToPub 112 112 +0.00%
BenchmarkDeserialize 320 256 -20.00%
BenchmarkSerialize 272 272 +0.00%
This adds a new method to ModNScalar named PutBytesUnchecked which can
be used by callers to serialize the scalar directly into a target byte
slice with the caveat that the caller must ensure the target has at
least 32 bytes available or it will panic.
This is useful in cases where the caller wishes to write into a larger
buffer without having to first serialize into a 32-byte array and copy
it.
It also updates the tests to ensure it works as expected.
This adds a new method to FieldVal named PutBytesUnchecked which can be
used by callers to serialize the field value directly into a target byte
slice with the caveat that the caller must ensure the target has at
least 32 bytes available or it will panic.
This is useful in cases where the caller wishes to write into a larger
buffer without having to first serialize into a 32-byte array and copy
it.
It also updates the tests to ensure it works as expected.
This renames some error codes to more closely match the failure as
follows:
- ErrBadInputSize -> ErrInvalidHashLen
- ErrInputValue -> ErrPrivateKeyIsZero
- ErrPointNotOnCurve -> ErrPubKeyNotOnCurve
- ErrBadSigRYValue -> ErrSigRYIsOdd
- ErrBadSigRNotOnCurve -> ErrSigRNotOnCurve
This updates the internal implementation of the Signature type to use
the ModNScalar and FieldVal types instead of big.Ints and modifies
NewSignature to accept the types accordingly.
Signature parsing and signing operations are now completely free of
big.Ints. Verification still relies on them due to their use in public
keys.
It also removes the now unused primitives.go file.
This modifies the signature verification function use the
ModNScalar and FieldVal types instead of big ints.
This is work towards eventually using the specialized types throughout.
The following benchmark shows a before and after comparison of typical
signature verification:
benchmark old ns/op new ns/op delta
------------------------------------------------------
BenchmarkSigVerify 203206 182142 -10.37%
benchmark old allocs new allocs delta
------------------------------------------------------
BenchmarkSigVerify 45 21 -53.33%
benchmark old bytes new bytes delta
------------------------------------------------------
BenchmarkSigVerify 2066 1041 -49.61%
This modifies the function that produces signatures to use the
ModNScalar and FieldVal types instead of big ints and also fleshes out
the comments to include a reference to the signing algorithm specified
in README.md.
This is work towards eventually using the specialized types throughout.
While the primary focus of this change is not optimization since signing
happens infrequently enough that it is not in a performance hot path, as
can be seen from the benchmark below, it does have the effect of making
signing a bit faster.
The following benchmark shows a before and after comparison of producing
a typical signature:
benchmark old ns/op new ns/op delta
-------------------------------------------------
BenchmarkSign 54270 52622 -3.04%
benchmark old allocs new allocs delta
-------------------------------------------------
BenchmarkSign 34 20 -41.18%
benchmark old bytes new bytes delta
-------------------------------------------------
BenchmarkSign 1809 1088 -39.86%
This adds a README.md file for the package which includes the custom
EC-Schnorr-DCRv0 specification along with the design details and
rationale for why its various characteristics were chosen.
This reworks the Schnorr signature parsing code to be more consistent
with the ecdsa parsing code and pave the way for eventually being able
to remove the reliance on big ints.
It also adds a full set of signature parsing tests that include 100%
coverage of all branches in the parsing code and test for the explicit
reason for the failure to ensure that each test is actually testing the
intended condition.
In particular, the parsing code has been changed as follows:
- Reject signatures that have the r and/or s components out of
the allowed ranges for their respective type
- Make use of the specialized mod N scalar and field val types
- Introduce new error codes for signature errors and return them
This modifies the SetBytes and SetByteSlice methods of the FieldVal type
to return whether or not the provided value was greater than or equal to
the field prime and adds tests to ensure it works as expected.
This allows callers to easily check if a given uint256 value will fit
within the field element without modular reduction and makes it more
consistent with the ModNScalar type.
Finally, it updates all of the callers in the repository accordingly.
This makes use of the PubKey method on secp256k1.PrivateKey to convert a
private key to a public key instead of manually performing the
conversion.
This is desirable since secp256k1 provides the method specifically for
this purpose and it also has the benefit of allowing secp256k1 to
perform the task without round tripping through big integers which
results in slightly faster derivation.
The following benchmarks show a before and after comparison of the
affected operations:
benchmark old ns/op new ns/op delta
----------------------------------------------------------
BenchmarkDeriveHardened 3702 3671 -0.84%
BenchmarkDeriveNormal 4053 3999 -1.33%
benchmark old allocs new allocs delta
----------------------------------------------------------
BenchmarkDeriveHardened 14 14 +0.00%
BenchmarkDeriveNormal 15 15 +0.00%
benchmark old bytes new bytes delta
----------------------------------------------------------
BenchmarkDeriveHardened 1360 1360 +0.00%
BenchmarkDeriveNormal 1409 1409 +0.00%
This adds a modification of a new test vector added to BIP32 which tests
for the case of a child key with leading zeros. It was modified to
ensure it tests the intended condition with the new hash function Decred
uses.
This adds several tests to help ensure the signature verification code
works as intended with invalid signatures by crafting signatures that
are specifically designed to hit the various failure cases and checking
the verification failed for that explicit reason.
This updates the Error and ErrorCode types to provide support for
errors.Is and errors.As and adds a full set of tests for the error
infrastructure so the error handling is more consistent with the rest of
the secp256k1 module.
While here, it removes the unused ErrPubKeyOffCurve error and renames
the error creation func to signatureError to be more consistent.
This modifies the signing code to use extra data that specifically
identifies the custom schnorr signature scheme used when generating
RFC6979 nonces to ensure the same nonce is not generated for the same
message and key as for other signing algorithms such as ECDSA.
While nobody realistically should ever be signing the same message with
the same private key with both Schnorr and ECDSA at the same time, it's
safer to ensure that even if they do, the same nonce value isn't used
since that could lead to revealing the private key.
It also updates the relevant tests to use the new deterministic nonces
and signatures which have been independently verified with the Sage
computer algebra system.
Currently, the secp256k1 package is rather tightly bound to ECDSA
signatures which makes them the only real first-class citizen in the
code. In an effort to make it clear the ECDSA is only one possible
digital signature algorithms and to enable Schnorr signatures to also be
made first class citizens, this decouples all code related to producing
and parsing ECDSA signatures into a separate package under secp256k1
named ecdsa.
This is a fairly large change in terms of the number of lines changed,
however, the vast majority of the changes are mechanical to deal with
the package name changes and moved code. The only real new code is
documentation for the new package.
The following is a high level overview of the changes:
- Rename signature.go to ecdsa/signature.go
- Rename signature_test.go to ecdsa/signature_test.go
- Rename sigerror.go to ecdsa/error.go
- Rename SignatureErrorCode type to ErrorCode
- Rename SignatureError type to Error
- Rename sigerror_test.go to ecdsa/error_test.go
- Rename signature_bench_test.go to ecdsa/bench_test.go
- Move signing-related examples from example_test.go to
ecdsa/example_test.go
- Update all moved code to reference secp256k1 types and funcs
- Remove Sign from the secp256k1.PrivateKey type in favor of ecdsa.Sign
- Add ecdsa/README.md to describe the new package
- Add ecdsa/doc.go to provide package documentation via godoc
- Move signing examples from README.md to new ecdsa/README.md
- Update txscript and rpcserver to use the new package methods
This moves the code for generating an RFC6979 nonce and the related
tests into separate files named nonce.go and nonce_test.go,
respectively. The nonce is not specific to ecdsa and therefore it is
being split out so it remains accessible to multiple signing algorithms.
This exports the internal scalar that is used to house the private key
so that it can be used by external callers without having to round trip
through serializing the bytes.
This add a new method to the PublicKey type named AsJacobian which
converts the public key into a Jacobian point with Z=1. This allows the
public key to be treated a Jacobian point in the secp256k1 group in
calculations.
It also adds an associated test to ensure proper functionality.
This exports the decompressY func as DecompressY in order to allow
external callers to perform optimized elliptic curve point decompression
for a given X coordinate when possible.
It also moves the function and related tests from pubkey.go and
pubkey_test.go to curve.go and curve_test.go, respectively, since it
more correctly belongs there given it is not specific to public keys.
This is part of exposing the ec group operations so callers are not
forced to round trip through big ints.
This exports the scalarBaseMultJacobian func as ScalarBaseMultNonConst
in order to allow external callers to perform optimized elliptic curve
point multiplication of the group base point with a scalar in Jacobian
project coordinates in addition to more clearly indentifying that the
function is not constant time.
This is part of exposing the ec group operations so callers are not
forced to round trip through big ints in affine space.
This exports the scalarMultJacobian func as SclarMultNonConst in order
to allow external callers to perform optimized elliptic curve point
multiplication with a scalar in Jacobian project coordinates in addition
to more clearly indentifying that the function is not constant time.
This is part of exposing the ec group operations so callers are not
forced to round trip through big ints in affine space.
This exports the doubleJacobian func as DoubleNonConst in order to allow
external callers to perform optimized elliptic curve point doubling in
Jacobian project coordinates in addition to more clearly indentifying
that the function is not constant time.
This is part of exposing the ec group operations so callers are not
forced to round trip through big ints in affine space.
This exports the addJacobian func as AddNonConst in order to allow
external callers to perform optimized elliptic curve point addition in
Jacobian project coordinates in addition to more clearly indentifying
that the function is not constant time.
This is part of exposing the ec group operations so callers are not
forced to round trip through big ints in affine space.
This exports the jacobianPoint type as JacobianPoint in order to allow
external callers to perform optimized elliptic curve math in Jacobian
projective coordinates.
This is part of exposing the ec group operations so callers are not
forced to round trip through big ints in affine space.
This removes the bool return from the internal schnorrVerify function
since it is entirely unnecessary given an error is already returned if
the signature fails to verify. It is also more consistent with the rest
of the code.
This exposes a new function on the FieldVal type named IsZeroBit which
operates similarly to IsZero except it returns a 0 or 1.
This is being provided 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.
Since the type is now exported for external use, consumers would
otherwise have no way to perform the conversion.