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.
This exposes a new function on the FieldVal type named IsOneBit which
operates similarly to IsOne 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.
This exposes a new function on the FieldVal type named IsOddBit which
operates similarly to IsOdd 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.
This exports the fieldVal type as FieldVal in order to allow external
callers to perform optimized field math. The intent is ultimately to
expose the group operations as well so callers are not forced to round
trip through big ints.
This updates the documentation for the field value type in preparation
for exporting the type as follows:
- Add a primer to the main field value type which warns loudly about the
requirements for the caller to ensure proper preconditions and
explains the concepts of normalization and magnitude accordingly
- Update all methods to explicitly call out their preconditions
- Update all methods to explicitly call out the resulting normalization
and magnitude
- Update all methods that are constant time to explicitly state it
This moves the SetHex method on the field value type to the associated
test file so it is only avilable during the tests which is its intended
purpose. It is not constant time and therefore shouldn't be available
to callers. This is in preparation for exporting the type.
This modifies the MulInt method on a field value to take a uint8 instead
of a uint in order to make it harder to misuse it since it has a
precondition which requires to the caller to ensure internal overflows
are avoided. In practice, the code currently only ever calls it with a
max value of 8, but in order to prepare for exporting the type, it's
nicer to make it harder to misuse.
This modifies the AddInt method on a field value to take a uint16
instead of a uint in order to make it impossible to misuse it since it
has a precondition of a max value of 2^26 - 1. In practice, the code
currently only ever calls it with 7, but in order to prepare for
exporting the type, it's nicer to prevent the potential misuse.
This modifies the SetInt method on a field value to take a uint16
instead of a uint in order to make it impossible to misuse it since it
has a precondition of a max value of 2^26 - 1. In practice, the code
currently only ever calls it with 0 and 1, but in order to prepare for
exporting the type, it's nicer to prevent the potential misuse.
This modifies the CurveParams struct that is exported to independently
define the params instead of directly embedding elliptic.CurveParams and
updates KoblitzCurve to directly embed elliptic.CurveParams instead to
prevent possible misuse.
This is desirable because elliptic.CurveParams also defines methods
which provide a non-constant and generic implementation of the curve
based on big ints that does not work properly with the secp256k1 curve
due to its use of a different a value in the curve equation. The
adaptor code works around that limitation by embedding the
elliptic.CurveParams and overriding the method implementations with the
correct calculations via the KoblitzCurve type.
In other words, prior to the change, the caller could have easily
misused the params to perform incorrect math by calling the
non-overridden methods on the type returned by the Params method instead
of the overridden version obtained via the S256 method.
That is no longer possible with this change since the Params method now
only contains the parameters and does not embed elliptic.CurveParams.
This modifies the various EC operations to reduce the overall number of
normalizations that are needed by requiring the caller to provide
normalized points instead of blindly normalizing the inputs. It also
updates the operations to ensure the result is normalized along with
updating the comments to call out the new semantics and updating all of
the callers accordingly to ensure the new requirements are met.
This results in less overall normalizations because the caller has more
information about whether or not normalization is needed and can
therefore avoid it in many cases, particularly when it is using the
result that is already normalized as part of a chain of operations.
The following benchmarks show a before and after comparison of the
affected operations:
benchmark old ns/op new ns/op delta
-----------------------------------------------------------------
BenchmarkAddJacobian 569 442 -22.32%
BenchmarkAddJacobianNotZOne 1087 1080 -0.64%
BenchmarkScalarBaseMult 46966 46164 -1.71%
BenchmarkScalarBaseMultJacobian 34218 34165 -0.15%
BenchmarkScalarBaseMultLarge 47077 46819 -0.55%
BenchmarkScalarMult 155318 148019 -4.70%
BenchmarkSigVerify 176007 168422 -4.31%
BenchmarkSign 57636 57561 -0.13%
BenchmarkSignCompact 57883 57653 -0.40%
BenchmarkRecoverCompact 206361 197419 -4.33%
This modifies signature verification to perform the final equality check
in Jacobian projective space to avoid an extra expensive field inversion
by taking advantage of the fact the R component of the signature can be
converted to Jacobian projective space and compared directly so long as
the two possible cases that arise from R being mod N and the original x
coordinate used to create it being mod P.
The following is a before and after comparison of verification of a
typical signature:
benchmark old ns/op new ns/op delta
-----------------------------------------------------
BenchmarkSigVerify 189505 177147 -6.52%
benchmark old allocs new allocs delta
-----------------------------------------------------
BenchmarkSigVerify 34 34 +0.00%
benchmark old bytes new bytes delta
-----------------------------------------------------
BenchmarkSigVerify 1633 1633 +0.00%
The MaybeAcceptDependents function was added as a part of the logic to
track tickets with non-approved inputs separately in a staging pool.
Given it modifies the contents of the mempool, it needs to hold a write
mutex as opposed to a read mutex as otherwise it can modify the mempool
out from under readers.
While here, comment the required locking semantics on the other
functions that were added as a part of the staging logic as well.
This removes the GenerateKey function from the schnorr package since
nothing uses it and it is expected that the caller will use
secp256k1.GeneartePrivateKey instead since the package works with that
type as private keys.