In this commit, we optimize our signature implementation slightly, by
defining pre-computed sha256(tag) variables for the commonly used
values. If a tag matches this, then we'll use that hash value to avoid
an extra round of hashing.
In this commit, we add an initial implementation of BIP-340. Mirroring
the recently added `ecsda` package, we create a new `schnorr` package
with a unique `Signature` type and `ParsePubkey` function. The new
`Signature` type implements the fixed-sized 64-byte signatures, and the
`ParsePubkey` method only accepts pubkeys that are 32-bytes in length,
with an implicit sign byte.
The signing implementation by default, deviates from BIP-340 as it opts
to use rfc6979 deterministic signatures by default, which means callers
don't need to always pass in their own `auxNonce` randomness. A set of
functional arguments allows callers to pass in their own value, which is
the way all the included test vectors function.
The other optional functional argument added is the `FastSign` option
that allows callers to skip the final step of verifying each signature
they generate.
In this commit, we create a new package to house the ECDSA-specific
logic in the new `btcec/v2` pacakge. Thsi c hange is meant to mirror the
structure of the `dcrec` package, as we'll soon slot in our own custom
BIP-340 implementation.
In this commit, we turn the package into a new Go module (version 2),
and then port over the current set of types and functions to mainly
alias to the more optimized and maintained dcrec variant.
Taking a look at the benchmarks, most operations other than
normalization (which IIRC is a bit slower now due to constant time
fixes) enjoy some nice speeds up:
```
benchcmp is deprecated in favor of benchstat: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat
benchmark old ns/op new ns/op delta
BenchmarkAddJacobian-8 464 328 -29.20%
BenchmarkAddJacobianNotZOne-8 1138 372 -67.27%
BenchmarkScalarBaseMult-8 47336 31531 -33.39%
BenchmarkScalarBaseMultLarge-8 42465 32057 -24.51%
BenchmarkScalarMult-8 123355 117579 -4.68%
BenchmarkNAF-8 582 168 -71.12%
BenchmarkSigVerify-8 175414 120794 -31.14%
BenchmarkFieldNormalize-8 23.8 24.4 +2.39%
BenchmarkParseCompressedPubKey-8 24282 10907 -55.08%
```
The implementation has been adapted from the dcrec module in dcrd. The
bug was initially fixed in decred/dcrd@3d9cda1 while transitioning to a
constant time algorithm. A large set of test vectors were subsequently
added in decred/dcrd@8c6b52d.
The function signature has been preserved for backwards compatibility.
This means that returning whether the value has overflowed, and the
corresponding test vectors have not been backported.
This fixes#1170 and closes a previous attempt to fix the bug in #1178.
This commit fixes an issue introduced in the recent #1429, where
the output of SqrtVal is not normalized before using IsOdd() to compare
with the expected parity of the y-coordinate. The IsOdd() is only
guaranteed to work if the value has been denormalized, so a denormalized
sqrt >= p would report the opposite parity. We fix this by normalizing
both after compute sqrt(x^3) and when negating the root as directed by
the ybit.
The previous naming suggested that the value ((P+1)/4+1)/4 was being
returned, when in fact the returned value is simply (P+1)/4. The old
method is superseded by Q().
This commit optimizes the decompressPoint subroutine, used in extracting
compressed pubkeys and performing pubkey recovery. We do so by replacing
the use of big.Int.Exp with with square-and-multiply exponentiation of
btcec's more optimized fieldVals, reducing the overall latency and
memory requirements of decompressPoint.
Instead of operating on bits of Q = (P+1)/4, the exponentiation applies
the square-and-multiply operations on full bytes of Q. Compared to the
original speedup. Compared the bit-wise version, the improvement is
roughly 10%.
A new pair fieldVal methods called Sqrt and SqrtVal are added, which
applies the square-and-multiply exponentiation using precomputed
byte-slice of the value Q.
Comparison against big.Int sqrt and SAM sqrt over bytes of Q:
benchmark old ns/op new ns/op delta
BenchmarkParseCompressedPubKey-8 35545 23119 -34.96%
benchmark old allocs new allocs delta
BenchmarkParseCompressedPubKey-8 35 6 -82.86%
benchmark old bytes new bytes delta
BenchmarkParseCompressedPubKey-8 2777 256 -90.78%
As of https://github.com/btcsuite/btcd/pull/1193, decompressPoint now
validates that the point is on the curve. The x and y cooordinates are
also implicitly <= P, since the modular reduction is applied to both
before the method returns. The checks are moved so that they are still
applied when parsing an uncompressed pubkey, as the checks are not
redundant in that path.
This regenerates the precomputed secp256k1 byte points used to optimize
scalar multiplication. This should have been done as part of the
normalization correction.
This commit adds a new function to btcec: IsCompressedPubKey. This
function returns true iff the passed serialized public key is encoded
in compressed format.
This slightly optimizes the NAF function by avoiding returning the
unused bit when there is not a carry.
It also adds a bunch of additional unit tests which I made while
debugging.
This modifies the normalize function of the internal field value to
both optimize it and address an issue where the reduction could
lead to an incorrect result with a small range of values. It also adds
tests to ensure the behavior is correct.
The following benchmark shows the relative speedups as a result of the
optimization on my system. In particular, the changes result in
approximately a 14% speedup in Normalize, which ultimately translates to
a 2% speedup in signature verifies.
benchmark old ns/op new ns/op delta
--------------------------------------------------------------------
BenchmarkAddJacobian 1364 1289 -5.50%
BenchmarkAddJacobianNotZOne 3150 3091 -1.87%
BenchmarkScalarBaseMult 134117 132816 -0.97%
BenchmarkScalarBaseMultLarge 135067 132966 -1.56%
BenchmarkScalarMult 411218 402217 -2.19%
BenchmarkSigVerify 671585 657833 -2.05%
BenchmarkFieldNormalize 36.0 31.0 -13.89%
The github markdown interpreter has been changed such that it no longer
allows spaces in between the brackets and parenthesis of links and now
requires a newline in between anchors and other formatting. This
updates all of the markdown files accordingly.
While here, it also corrects a couple of inconsistencies in some of the
README.md files.
This simplifies the code based on the recommendations of the gosimple
lint tool.
Also, it increases the deadline for the linters to run to 10 minutes and
reduces the number of threads that is uses. This is being done because
the Travis environment has become increasingly slower and it also seems
to be hampered by too many threads running concurrently.
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.
Also, remove the exception from the lint checks about returning the
unexported type since it is no longer required.
This adds new tests to the TestNormalize, TestMul, TestAdd2 functions
which trigger an issue with modular reduction that was fixed in the
prevous commit to prevent regressions.
As noted in issue #706, the existing code had an issue where the
normalized result was > P when both the first and second words of the
field representation being normalized were BOTH greater than or equal to
the first and second words of P. Although this condition is rare in
practice, it needs to be handled properly.
This resolves the issue by comparing the low words in the final
reduction step against the normalized low order prime bits to ensure the
final subtraction occurs correctly any time they're > P. This approach
retains the constant time property as well.
This is mostly a backport of some of the same modifications made in
Decred along with a few additional things cleaned up. In particular,
this updates the code to make use of the new chainhash package.
Also, since this required API changes anyways and the hash algorithm is
no longer tied specifically to SHA, all other functions throughout the
code base which had "Sha" in their name have been changed to Hash so
they are not incorrectly implying the hash algorithm.
The following is an overview of the changes:
- Remove the wire.ShaHash type
- Update all references to wire.ShaHash to the new chainhash.Hash type
- Rename the following functions and update all references:
- wire.BlockHeader.BlockSha -> BlockHash
- wire.MsgBlock.BlockSha -> BlockHash
- wire.MsgBlock.TxShas -> TxHashes
- wire.MsgTx.TxSha -> TxHash
- blockchain.ShaHashToBig -> HashToBig
- peer.ShaFunc -> peer.HashFunc
- Rename all variables that included sha in their name to include hash
instead
- Update for function name changes in other dependent packages such as
btcutil
- Update copyright dates on all modified files
- Update glide.lock file to use the required version of btcutil