diff --git a/docs/doc-comments-prompt.txt b/.aiassistant/rules/rules.md similarity index 58% rename from docs/doc-comments-prompt.txt rename to .aiassistant/rules/rules.md index d283c09..6038815 100644 --- a/docs/doc-comments-prompt.txt +++ b/.aiassistant/rules/rules.md @@ -1,4 +1,23 @@ -Always start documentation comments with the symbol name verbatim, and then use this to start a sentence summarizing the symbol's function +--- +apply: always +--- + +Always use named returns when generating code + +Always use `if ; chk.E(err) {}` and declare any new variables used +in the statement above the if statement with a var declaration. + +When returning from functions, assign the return values in statements above the +return statement. Always make returns "naked" as the code editor shows the +function signature above the current function being edited. + +Use short 2-3 letter acronyms for variables that are only used within for, +select and switch blocks, make them relate to the names of the variables that +are referred to such as the subject of the range in a for loop or the channel +name in a select or the condition variable in a switch. + +Always start documentation comments with the symbol name verbatim, and then use +this to start a sentence summarizing the symbol's function. For documentation comments on functions and methods: @@ -6,30 +25,41 @@ For documentation comments on functions and methods: - use the format `# Header` for headings of sections. -- Follow by a description of the parameters and then return values, with a series of bullet points describing each item, each with an empty line in between. +- Follow by a description of the parameters and then return values, with a + series of bullet points describing each item, each with an empty line in + between. -- Last, describe the expected behaviour of the function or method, keep this with one space apart from the comment start token +- Last, describe the expected behaviour of the function or method, keep this + with one space apart from the comment start token -For documentation on types, variables and comments, write 1-2 sentences describing how the item is used. +For documentation on types, variables and comments, write 1-2 sentences +describing how the item is used. -For documentation on package, summarise in up to 3 sentences the functions and purpose of the package +For documentation on package, summarise in up to 3 sentences the functions and +purpose of the package -Do not use markdown ** or __ or any similar things in initial words of a bullet point, instead use standard godoc style # prefix for header sections +Do not use markdown ** or __ or any similar things in initial words of a bullet +point, instead use standard godoc style # prefix for header sections -ALWAYS separate each bullet point with an empty line, and ALWAYS indent them three spaces after the // +ALWAYS separate each bullet point with an empty line, and ALWAYS indent them +three spaces after the // NEVER put a colon after the first word of the first line of a document comment Use British English spelling and Oxford commas -Always break lines before 80 columns, and flow under bullet points two columns right of the bullet point hyphen. +Always break lines before 80 columns, and flow under bullet points two columns +right of the bullet point hyphen. Do not write a section for parameters or return values when there is none -In the `# Expected behavior` section always add an empty line after this title before the description, and don't indent this section as this makes it appear as preformatted monospace. +In the `# Expected behavior` section always add an empty line after this title +before the description, and don't indent this section as this makes it appear as +preformatted monospace. A good typical example: +```go // NewServer initializes and returns a new Server instance based on the provided // ServerParams and optional settings. It sets up storage, initializes the // relay, and configures necessary components for server operation. @@ -57,3 +87,5 @@ A good typical example: // - Sets up a ServeMux for handling HTTP requests. // // - Initializes the relay, starting its operation in a separate goroutine. + +``` diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..8768db4 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,108 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go +# +# Release Process: +# 1. Update the version in the pkg/version/version file (e.g. v1.2.3) +# 2. Create and push a tag matching the version: +# git tag v1.2.3 +# git push origin v1.2.3 +# 3. The workflow will automatically: +# - Build binaries for multiple platforms (Linux, macOS, Windows) +# - Create a GitHub release with the binaries +# - Generate release notes + +name: Go + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.25' + + - name: Install libsecp256k1 + run: ./scripts/ubuntu_install_libsecp256k1.sh + + - name: Build with cgo + run: go build -v ./... + + - name: Test with cgo + run: go test -v ./... + + - name: Set CGO off + run: echo "CGO_ENABLED=0" >> $GITHUB_ENV + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... + +# release: +# needs: build +# runs-on: ubuntu-latest +# permissions: +# contents: write +# packages: write +# +# steps: +# - uses: actions/checkout@v4 +# +# - name: Set up Go +# uses: actions/setup-go@v4 +# with: +# go-version: '1.25' +# +# - name: Install libsecp256k1 +# run: ./scripts/ubuntu_install_libsecp256k1.sh +# +# - name: Build Release Binaries +# if: startsWith(github.ref, 'refs/tags/v') +# run: | +# # Extract version from tag (e.g., v1.2.3 -> 1.2.3) +# VERSION=${GITHUB_REF#refs/tags/v} +# echo "Building release binaries for version $VERSION" +# +# # Create directory for binaries +# mkdir -p release-binaries +# +# # Build for different platforms +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o release-binaries/orly-${VERSION}-linux-amd64 . +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o release-binaries/orly-${VERSION}-linux-arm64 . +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o release-binaries/orly-${VERSION}-darwin-amd64 . +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -o release-binaries/orly-${VERSION}-darwin-arm64 . +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o release-binaries/orly-${VERSION}-windows-amd64.exe . +# +# # Build cmd executables +# for cmd in lerproxy nauth nurl vainstr walletcli; do +# echo "Building $cmd" +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o release-binaries/${cmd}-${VERSION}-linux-amd64 ./cmd/${cmd} +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o release-binaries/${cmd}-${VERSION}-linux-arm64 ./cmd/${cmd} +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o release-binaries/${cmd}-${VERSION}-darwin-amd64 ./cmd/${cmd} +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -o release-binaries/${cmd}-${VERSION}-darwin-arm64 ./cmd/${cmd} +# GOEXPERIMENT=greenteagc,jsonv2 GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o release-binaries/${cmd}-${VERSION}-windows-amd64.exe ./cmd/${cmd} +# done +# +# # Create checksums +# cd release-binaries +# sha256sum * > SHA256SUMS.txt +# cd .. +# +# - name: Create GitHub Release +# if: startsWith(github.ref, 'refs/tags/v') +# uses: softprops/action-gh-release@v1 +# with: +# files: release-binaries/* +# draft: false +# prerelease: false +# generate_release_notes: true diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0f7af07 --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module next.orly.dev + +go 1.25.0 + +require ( + github.com/davecgh/go-spew v1.1.1 + github.com/klauspost/cpuid/v2 v2.3.0 + github.com/stretchr/testify v1.10.0 + github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b + lol.mleku.dev v1.0.2 +) + +require ( + github.com/fatih/color v1.18.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/templexxx/cpu v0.0.1 // indirect + golang.org/x/sys v0.35.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f3ef074 --- /dev/null +++ b/go.sum @@ -0,0 +1,27 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY= +github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= +github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b h1:XeDLE6c9mzHpdv3Wb1+pWBaWv/BlHK0ZYIu/KaL6eHg= +github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b/go.mod h1:7rwmCH0wC2fQvNEvPZ3sKXukhyCTyiaZ5VTZMQYpZKQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +lol.mleku.dev v1.0.2 h1:bSV1hHnkmt1hq+9nSvRwN6wgcI7itbM3XRZ4dMB438c= +lol.mleku.dev v1.0.2/go.mod h1:DQ0WnmkntA9dPLCXgvtIgYt5G0HSqx3wSTLolHgWeLA= diff --git a/pkg/crypto/ec/LICENSE b/pkg/crypto/ec/LICENSE new file mode 100644 index 0000000..d2d1dd9 --- /dev/null +++ b/pkg/crypto/ec/LICENSE @@ -0,0 +1,17 @@ +ISC License + +Copyright (c) 2013-2017 The btcsuite developers +Copyright (c) 2015-2020 The Decred developers +Copyright (c) 2017 The Lightning Network Developers + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/pkg/crypto/ec/README.md b/pkg/crypto/ec/README.md new file mode 100644 index 0000000..2c0ae89 --- /dev/null +++ b/pkg/crypto/ec/README.md @@ -0,0 +1,39 @@ +realy.lol/pkg/ec +===== + +This is a full drop-in replacement for +[github.com/btcsuite/btcd/btcec](https://github.com/btcsuite/btcd/tree/master/btcec) +eliminating the import from the Decred repository, and including the chainhash +helper functions, needed for hashing messages for signatures. + +The decred specific tests also have been removed, as well as all tests that use +blake256 hashes as these are irrelevant to bitcoin and nostr. Some of them +remain present, commented out, in case it is worth regenerating the vectors +based on sha256 hashes, but on first blush it seems unlikely to be any benefit. + +This includes the old style compact secp256k1 ECDSA signatures, that recover the +public key rather than take a key as a parameter as used in Bitcoin +transactions, the new style Schnorr signatures, and the Musig2 implementation. + +BIP 340 Schnorr signatures are implemented including the variable length +message signing with the extra test vectors present and passing. + +The remainder of this document is from the original README.md. + +------------------------------------------------------------------------------ + +Package `ec` implements elliptic curve cryptography needed for working with +Bitcoin. It is designed so that it may be used with the standard +crypto/ecdsa packages provided with Go. + +A comprehensive suite of test is provided to ensure proper functionality. + +Package btcec was originally based on work from ThePiachu which is licensed +underthe same terms as Go, but it has signficantly diverged since then. The +btcsuite developers original is licensed under the liberal ISC license. + +## Installation and Updating + +```bash +$ go get mleku.dev/pkg/ec@latest +``` diff --git a/pkg/crypto/ec/base58/LICENSE b/pkg/crypto/ec/base58/LICENSE new file mode 100644 index 0000000..eba799a --- /dev/null +++ b/pkg/crypto/ec/base58/LICENSE @@ -0,0 +1,14 @@ +Copyright © 2004-2011 []byte Internet Systems Consortium, Inc. ("ISC") +Copyright © 1995-2003 []byte Internet Software Consortium + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. \ No newline at end of file diff --git a/pkg/crypto/ec/base58/README.adoc b/pkg/crypto/ec/base58/README.adoc new file mode 100644 index 0000000..09ec6f9 --- /dev/null +++ b/pkg/crypto/ec/base58/README.adoc @@ -0,0 +1,12 @@ += base58 + +image:http://img.shields.io/badge/license-ISC-blue.svg[ISC License,link=http://copyfree.org] + +Package base58 provides an API for encoding and decoding to and from the modified base58 encoding. +It also provides an API to do Base58Check encoding, as described https://en.bitcoin.it/wiki/Base58Check_encoding[here]. + +A comprehensive suite of tests is provided to ensure proper functionality. + +== License + +Package base58 is licensed under the http://copyfree.org[copyfree] ISC License. \ No newline at end of file diff --git a/pkg/crypto/ec/base58/alphabet.go b/pkg/crypto/ec/base58/alphabet.go new file mode 100644 index 0000000..0e1201d --- /dev/null +++ b/pkg/crypto/ec/base58/alphabet.go @@ -0,0 +1,49 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// AUTOGENERATED by genalphabet.go; do not edit. + +package base58 + +const ( + // Ciphers is the modified base58 Ciphers used by Bitcoin. + Ciphers = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + alphabetIdx0 = '1' +) + +var b58 = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 255, 255, 255, 255, 255, 255, + 255, 9, 10, 11, 12, 13, 14, 15, + 16, 255, 17, 18, 19, 20, 21, 255, + 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 255, 255, 255, 255, 255, + 255, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 255, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, +} diff --git a/pkg/crypto/ec/base58/base58.go b/pkg/crypto/ec/base58/base58.go new file mode 100644 index 0000000..31e4309 --- /dev/null +++ b/pkg/crypto/ec/base58/base58.go @@ -0,0 +1,142 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58 + +import ( + "math/big" +) + +//go:generate go run genalphabet.go + +var bigRadix = [...]*big.Int{ + big.NewInt(0), + big.NewInt(58), + big.NewInt(58 * 58), + big.NewInt(58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), + big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), + bigRadix10, +} + +var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10 + +// Decode decodes a modified base58 string to a byte slice. +func Decode(b string) []byte { + answer := big.NewInt(0) + scratch := new(big.Int) + + // Calculating with big.Int is slow for each iteration. + // x += b58[b[i]] * j + // j *= 58 + // + // Instead we can try to do as much calculations on int64. + // We can represent a 10 digit base58 number using an int64. + // + // Hence we'll try to convert 10, base58 digits at a time. + // The rough idea is to calculate `t`, such that: + // + // t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0 + // x *= 58^10 + // x += t + // + // Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10. + // In that case we'll use the bigRadix[n] lookup for the appropriate power. + for t := b; len(t) > 0; { + n := len(t) + if n > 10 { + n = 10 + } + + total := uint64(0) + for _, v := range t[:n] { + if v > 255 { + return []byte("") + } + + tmp := b58[v] + if tmp == 255 { + return []byte("") + } + total = total*58 + uint64(tmp) + } + + answer.Mul(answer, bigRadix[n]) + scratch.SetUint64(total) + answer.Add(answer, scratch) + + t = t[n:] + } + + tmpval := answer.Bytes() + + var numZeros int + for numZeros = 0; numZeros < len(b); numZeros++ { + if b[numZeros] != alphabetIdx0 { + break + } + } + flen := numZeros + len(tmpval) + val := make([]byte, flen) + copy(val[numZeros:], tmpval) + + return val +} + +// Encode encodes a byte slice to a modified base58 string. +func Encode(b []byte) string { + x := new(big.Int) + x.SetBytes(b) + + // maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58) + maxlen := int(float64(len(b))*1.365658237309761) + 1 + answer := make([]byte, 0, maxlen) + mod := new(big.Int) + for x.Sign() > 0 { + // Calculating with big.Int is slow for each iteration. + // x, mod = x / 58, x % 58 + // + // Instead we can try to do as much calculations on int64. + // x, mod = x / 58^10, x % 58^10 + // + // Which will give us mod, which is 10 digit base58 number. + // We'll loop that 10 times to convert to the answer. + + x.DivMod(x, bigRadix10, mod) + if x.Sign() == 0 { + // When x = 0, we need to ensure we don't add any extra zeros. + m := mod.Int64() + for m > 0 { + answer = append(answer, Ciphers[m%58]) + m /= 58 + } + } else { + m := mod.Int64() + for i := 0; i < 10; i++ { + answer = append(answer, Ciphers[m%58]) + m /= 58 + } + } + } + + // leading zero bytes + for _, i := range b { + if i != 0 { + break + } + answer = append(answer, alphabetIdx0) + } + + // reverse + alen := len(answer) + for i := 0; i < alen/2; i++ { + answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] + } + + return string(answer) +} diff --git a/pkg/crypto/ec/base58/base58_test.go b/pkg/crypto/ec/base58/base58_test.go new file mode 100644 index 0000000..f7ad243 --- /dev/null +++ b/pkg/crypto/ec/base58/base58_test.go @@ -0,0 +1,124 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58_test + +import ( + "encoding/hex" + "testing" + + "next.orly.dev/pkg/crypto/ec/base58" + "next.orly.dev/pkg/utils" +) + +var stringTests = []struct { + in string + out string +}{ + {"", ""}, + {" ", "Z"}, + {"-", "n"}, + {"0", "q"}, + {"1", "r"}, + {"-1", "4SU"}, + {"11", "4k8"}, + {"abc", "ZiCa"}, + {"1234598760", "3mJr7AoUXx2Wqd"}, + {"abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f"}, + { + "00000000000000000000000000000000000000000000000000000000000000", + "3sN2THZeE9Eh9eYrwkvZqNstbHGvrxSAM7gXUXvyFQP8XvQLUqNCS27icwUeDT7ckHm4FUHM2mTVh1vbLmk7y", + }, +} + +var invalidStringTests = []struct { + in string + out string +}{ + {"0", ""}, + {"O", ""}, + {"I", ""}, + {"l", ""}, + {"3mJr0", ""}, + {"O3yxU", ""}, + {"3sNI", ""}, + {"4kl8", ""}, + {"0OIl", ""}, + {"!@#$%^&*()-_=+~`", ""}, + {"abcd\xd80", ""}, + {"abcd\U000020BF", ""}, +} + +var hexTests = []struct { + in string + out string +}{ + {"", ""}, + {"61", "2g"}, + {"626262", "a3gV"}, + {"636363", "aPEr"}, + { + "73696d706c792061206c6f6e6720737472696e67", + "2cFupjhnEsSn59qHXstmK2ffpLv2", + }, + { + "00eb15231dfceb60925886b67d065299925915aeb172c06647", + "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L", + }, + {"516b6fcd0f", "ABnLTmg"}, + {"bf4f89001e670274dd", "3SEo3LWLoPntC"}, + {"572e4794", "3EFU7m"}, + {"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, + {"10c8511e", "Rt5zm"}, + {"00000000000000000000", "1111111111"}, + { + "000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5", + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY", + }, +} + +func TestBase58(t *testing.T) { + // Encode tests + for x, test := range stringTests { + tmp := []byte(test.in) + if res := base58.Encode(tmp); res != test.out { + t.Errorf( + "Encode test #%d failed: got: %s want: %s", + x, res, test.out, + ) + continue + } + } + + // Decode tests + for x, test := range hexTests { + b, err := hex.DecodeString(test.in) + if err != nil { + t.Errorf("hex.DecodeString failed failed #%d: got: %s", x, test.in) + continue + } + if res := base58.Decode(test.out); !utils.FastEqual(res, b) { + t.Errorf( + "Decode test #%d failed: got: %q want: %q", + x, res, test.in, + ) + continue + } + } + + // Decode with invalid input + for x, test := range invalidStringTests { + if res := base58.Decode(test.in); string(res) != test.out { + t.Errorf( + "Decode invalidString test #%d failed: got: %q want: %q", + x, res, test.out, + ) + continue + } + } +} diff --git a/pkg/crypto/ec/base58/base58bench_test.go b/pkg/crypto/ec/base58/base58bench_test.go new file mode 100644 index 0000000..1240fba --- /dev/null +++ b/pkg/crypto/ec/base58/base58bench_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58_test + +import ( + "bytes" + "testing" + + "next.orly.dev/pkg/crypto/ec/base58" +) + +var ( + raw5k = bytes.Repeat([]byte{0xff}, 5000) + raw100k = bytes.Repeat([]byte{0xff}, 100*1000) + encoded5k = base58.Encode(raw5k) + encoded100k = base58.Encode(raw100k) +) + +func BenchmarkBase58Encode_5K(b *testing.B) { + b.SetBytes(int64(len(raw5k))) + for i := 0; i < b.N; i++ { + base58.Encode(raw5k) + } +} + +func BenchmarkBase58Encode_100K(b *testing.B) { + b.SetBytes(int64(len(raw100k))) + for i := 0; i < b.N; i++ { + base58.Encode(raw100k) + } +} + +func BenchmarkBase58Decode_5K(b *testing.B) { + b.SetBytes(int64(len(encoded5k))) + for i := 0; i < b.N; i++ { + base58.Decode(encoded5k) + } +} + +func BenchmarkBase58Decode_100K(b *testing.B) { + b.SetBytes(int64(len(encoded100k))) + for i := 0; i < b.N; i++ { + base58.Decode(encoded100k) + } +} diff --git a/pkg/crypto/ec/base58/base58check.go b/pkg/crypto/ec/base58/base58check.go new file mode 100644 index 0000000..8d8b3d6 --- /dev/null +++ b/pkg/crypto/ec/base58/base58check.go @@ -0,0 +1,53 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58 + +import ( + "errors" + + "next.orly.dev/pkg/crypto/sha256" +) + +// ErrChecksum indicates that the checksum of a check-encoded string does not verify against +// the checksum. +var ErrChecksum = errors.New("checksum error") + +// ErrInvalidFormat indicates that the check-encoded string has an invalid format. +var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing") + +// checksum: first four bytes of sha256^2 +func checksum(input []byte) (cksum [4]byte) { + h := sha256.Sum256(input) + h2 := sha256.Sum256(h[:]) + copy(cksum[:], h2[:4]) + return +} + +// CheckEncode prepends a version byte and appends a four byte checksum. +func CheckEncode(input []byte, version byte) string { + b := make([]byte, 0, 1+len(input)+4) + b = append(b, version) + b = append(b, input...) + cksum := checksum(b) + b = append(b, cksum[:]...) + return Encode(b) +} + +// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum. +func CheckDecode(input string) (result []byte, version byte, err error) { + decoded := Decode(input) + if len(decoded) < 5 { + return nil, 0, ErrInvalidFormat + } + version = decoded[0] + var cksum [4]byte + copy(cksum[:], decoded[len(decoded)-4:]) + if checksum(decoded[:len(decoded)-4]) != cksum { + return nil, 0, ErrChecksum + } + payload := decoded[1 : len(decoded)-4] + result = append(result, payload...) + return +} diff --git a/pkg/crypto/ec/base58/base58check_test.go b/pkg/crypto/ec/base58/base58check_test.go new file mode 100644 index 0000000..8f894d1 --- /dev/null +++ b/pkg/crypto/ec/base58/base58check_test.go @@ -0,0 +1,87 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58_test + +import ( + "testing" + + "next.orly.dev/pkg/crypto/ec/base58" +) + +var checkEncodingStringTests = []struct { + version byte + in string + out string +}{ + {20, "", "3MNQE1X"}, + {20, " ", "B2Kr6dBE"}, + {20, "-", "B3jv1Aft"}, + {20, "0", "B482yuaX"}, + {20, "1", "B4CmeGAC"}, + {20, "-1", "mM7eUf6kB"}, + {20, "11", "mP7BMTDVH"}, + {20, "abc", "4QiVtDjUdeq"}, + {20, "1234598760", "ZmNb8uQn5zvnUohNCEPP"}, + { + 20, "abcdefghijklmnopqrstuvwxyz", + "K2RYDcKfupxwXdWhSAxQPCeiULntKm63UXyx5MvEH2", + }, + { + 20, "00000000000000000000000000000000000000000000000000000000000000", + "bi1EWXwJay2udZVxLJozuTb8Meg4W9c6xnmJaRDjg6pri5MBAxb9XwrpQXbtnqEoRV5U2pixnFfwyXC8tRAVC8XxnjK", + }, +} + +func TestBase58Check(t *testing.T) { + for x, test := range checkEncodingStringTests { + // test encoding + if res := base58.CheckEncode( + []byte(test.in), + test.version, + ); res != test.out { + t.Errorf( + "CheckEncode test #%d failed: got %s, want: %s", x, res, + test.out, + ) + } + + // test decoding + res, version, err := base58.CheckDecode(test.out) + switch { + case err != nil: + t.Errorf("CheckDecode test #%d failed with err: %v", x, err) + + case version != test.version: + t.Errorf( + "CheckDecode test #%d failed: got version: %d want: %d", x, + version, test.version, + ) + + case string(res) != test.in: + t.Errorf( + "CheckDecode test #%d failed: got: %s want: %s", x, res, + test.in, + ) + } + } + + // test the two decoding failure cases + // case 1: checksum error + _, _, err := base58.CheckDecode("3MNQE1Y") + if err != base58.ErrChecksum { + t.Error("Checkdecode test failed, expected ErrChecksum") + } + // case 2: invalid formats (string lengths below 5 mean the version byte and/or the checksum + // bytes are missing). + testString := "" + for len := 0; len < 4; len++ { + testString += "x" + _, _, err = base58.CheckDecode(testString) + if err != base58.ErrInvalidFormat { + t.Error("Checkdecode test failed, expected ErrInvalidFormat") + } + } + +} diff --git a/pkg/crypto/ec/base58/cov_report.sh b/pkg/crypto/ec/base58/cov_report.sh new file mode 100644 index 0000000..307f05b --- /dev/null +++ b/pkg/crypto/ec/base58/cov_report.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# This script uses gocov to generate a test coverage report. +# The gocov tool my be obtained with the following command: +# go get github.com/axw/gocov/gocov +# +# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. + +# Check for gocov. +type gocov >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo >&2 "This script requires the gocov tool." + echo >&2 "You may obtain it with the following command:" + echo >&2 "go get github.com/axw/gocov/gocov" + exit 1 +fi +gocov test | gocov report diff --git a/pkg/crypto/ec/base58/doc.go b/pkg/crypto/ec/base58/doc.go new file mode 100644 index 0000000..f3c0a39 --- /dev/null +++ b/pkg/crypto/ec/base58/doc.go @@ -0,0 +1,29 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package base58 provides an API for working with modified base58 and Base58Check +encodings. + +# Modified Base58 Encoding + +Standard base58 encoding is similar to standard base64 encoding except, as the +name implies, it uses a 58 character Ciphers which results in an alphanumeric +string and allows some characters which are problematic for humans to be +excluded. Due to this, there can be various base58 alphabets. + +The modified base58 Ciphers used by Bitcoin, and hence this package, omits the +0, O, I, and l characters that look the same in many fonts and are therefore +hard to humans to distinguish. + +# Base58Check Encoding Scheme + +The Base58Check encoding scheme is primarily used for Bitcoin addresses at the +time of this writing, however it can be used to generically encode arbitrary +byte arrays into human-readable strings along with a version byte that can be +used to differentiate the same payload. For Bitcoin addresses, the extra +version is used to differentiate the network of otherwise identical public keys +which helps prevent using an address intended for one network on another. +*/ +package base58 diff --git a/pkg/crypto/ec/base58/example_test.go b/pkg/crypto/ec/base58/example_test.go new file mode 100644 index 0000000..8646c20 --- /dev/null +++ b/pkg/crypto/ec/base58/example_test.go @@ -0,0 +1,71 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package base58_test + +import ( + "fmt" + + "next.orly.dev/pkg/crypto/ec/base58" +) + +// This example demonstrates how to decode modified base58 encoded data. +func ExampleDecode() { + // Decode example modified base58 encoded data. + encoded := "25JnwSn7XKfNQ" + decoded := base58.Decode(encoded) + + // Show the decoded data. + fmt.Println("Decoded Data:", string(decoded)) + + // Output: + // Decoded Data: Test data +} + +// This example demonstrates how to encode data using the modified base58 +// encoding scheme. +func ExampleEncode() { + // Encode example data with the modified base58 encoding scheme. + data := []byte("Test data") + encoded := base58.Encode(data) + + // Show the encoded data. + fmt.Println("Encoded Data:", encoded) + + // Output: + // Encoded Data: 25JnwSn7XKfNQ +} + +// This example demonstrates how to decode Base58Check encoded data. +func ExampleCheckDecode() { + // Decode an example Base58Check encoded data. + encoded := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + decoded, version, err := base58.CheckDecode(encoded) + if err != nil { + fmt.Println(err) + return + } + + // Show the decoded data. + fmt.Printf("Decoded data: %x\n", decoded) + fmt.Println("Version Byte:", version) + + // Output: + // Decoded data: 62e907b15cbf27d5425399ebf6f0fb50ebb88f18 + // Version Byte: 0 +} + +// This example demonstrates how to encode data using the Base58Check encoding +// scheme. +func ExampleCheckEncode() { + // Encode example data with the Base58Check encoding scheme. + data := []byte("Test data") + encoded := base58.CheckEncode(data, 0) + + // Show the encoded data. + fmt.Println("Encoded Data:", encoded) + + // Output: + // Encoded Data: 182iP79GRURMp7oMHDU +} diff --git a/pkg/crypto/ec/base58/gen/genalphabet.go b/pkg/crypto/ec/base58/gen/genalphabet.go new file mode 100644 index 0000000..1b11b34 --- /dev/null +++ b/pkg/crypto/ec/base58/gen/genalphabet.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "io" + "log" + "os" + "strconv" +) + +var ( + start = []byte(`// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// AUTOGENERATED by genalphabet.go; do not edit. + +package base58 + +const ( + // Ciphers is the modified base58 alphabet used by Bitcoin. + Ciphers = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + alphabetIdx0 = '1' +) + +var b58 = [256]byte{`) + + end = []byte(`}`) + + alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + tab = []byte("\t") + invalid = []byte("255") + comma = []byte(",") + space = []byte(" ") + nl = []byte("\n") +) + +func write(w io.Writer, b []byte) { + _, err := w.Write(b) + if err != nil { + log.Fatal(err) + } +} + +func main() { + fi, err := os.Create("alphabet.go") + if err != nil { + log.Fatal(err) + } + defer fi.Close() + + write(fi, start) + write(fi, nl) + for i := byte(0); i < 32; i++ { + write(fi, tab) + for j := byte(0); j < 8; j++ { + idx := bytes.IndexByte(alphabet, i*8+j) + if idx == -1 { + write(fi, invalid) + } else { + write(fi, strconv.AppendInt(nil, int64(idx), 10)) + } + write(fi, comma) + if j != 7 { + write(fi, space) + } + } + write(fi, nl) + } + write(fi, end) + write(fi, nl) +} diff --git a/pkg/crypto/ec/bech32/README.adoc b/pkg/crypto/ec/bech32/README.adoc new file mode 100644 index 0000000..0ce06f1 --- /dev/null +++ b/pkg/crypto/ec/bech32/README.adoc @@ -0,0 +1,27 @@ += bech32 + +image:http://img.shields.io/badge/license-ISC-blue.svg[ISC License,link=http://copyfree.org] +image:https://godoc.org/realy.lol/pkg/ec/bech32?status.png[GoDoc,link=http://godoc.org/realy.lol/pkg/ec/bech32] + +Package bech32 provides a Go implementation of the bech32 format specified in +https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki[BIP 173]. + +Test vectors from BIP 173 are added to ensure compatibility with the BIP. + +== Installation and Updating + +[source,bash] +---- +$ go get -u mleku.dev/pkg/ec/bech32 +---- + +== Examples + +* http://godoc.org/realy.lol/pkg/ec/bech32#example-Bech32Decode[Bech32 decode Example] +Demonstrates how to decode a bech32 encoded string. +* http://godoc.org/realy.lol/pkg/ec/bech32#example-BechEncode[Bech32 encode Example] +Demonstrates how to encode data into a bech32 string. + +== License + +Package bech32 is licensed under the http://copyfree.org[copyfree] ISC License. \ No newline at end of file diff --git a/pkg/crypto/ec/bech32/bech32.go b/pkg/crypto/ec/bech32/bech32.go new file mode 100644 index 0000000..f3f1705 --- /dev/null +++ b/pkg/crypto/ec/bech32/bech32.go @@ -0,0 +1,411 @@ +// Copyright (c) 2017 The btcsuite developers +// Copyright (c) 2019 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "bytes" + "strings" +) + +// Charset is the set of characters used in the data section of bech32 strings. +// Note that this is ordered, such that for a given charset[i], i is the binary +// value of the character. +// +// This wasn't exported in the original lol. +const Charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + +// gen encodes the generator polynomial for the bech32 BCH checksum. +var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + +// toBytes converts each character in the string 'chars' to the value of the +// index of the corresponding character in 'charset'. +func toBytes(chars []byte) ([]byte, error) { + decoded := make([]byte, 0, len(chars)) + for i := 0; i < len(chars); i++ { + index := strings.IndexByte(Charset, chars[i]) + if index < 0 { + return nil, ErrNonCharsetChar(chars[i]) + } + decoded = append(decoded, byte(index)) + } + return decoded, nil +} + +// bech32Polymod calculates the BCH checksum for a given hrp, values and +// checksum data. Checksum is optional, and if nil a 0 checksum is assumed. +// +// Values and checksum (if provided) MUST be encoded as 5 bits per element (base +// 32), otherwise the results are undefined. +// +// For more details on the polymod calculation, please refer to BIP 173. +func bech32Polymod(hrp []byte, values, checksum []byte) int { + check := 1 + // Account for the high bits of the HRP in the checksum. + for i := 0; i < len(hrp); i++ { + b := check >> 25 + hiBits := int(hrp[i]) >> 5 + check = (check&0x1ffffff)<<5 ^ hiBits + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + check ^= gen[i] + } + } + } + // Account for the separator (0) between high and low bits of the HRP. + // x^0 == x, so we eliminate the redundant xor used in the other rounds. + b := check >> 25 + check = (check & 0x1ffffff) << 5 + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + check ^= gen[i] + } + } + // Account for the low bits of the HRP. + for i := 0; i < len(hrp); i++ { + b := check >> 25 + loBits := int(hrp[i]) & 31 + check = (check&0x1ffffff)<<5 ^ loBits + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + check ^= gen[i] + } + } + } + // Account for the values. + for _, v := range values { + b := check >> 25 + check = (check&0x1ffffff)<<5 ^ int(v) + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + check ^= gen[i] + } + } + } + if checksum == nil { + // A nil checksum is used during encoding, so assume all bytes are zero. + // x^0 == x, so we eliminate the redundant xor used in the other rounds. + for v := 0; v < 6; v++ { + b := check >> 25 + check = (check & 0x1ffffff) << 5 + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + check ^= gen[i] + } + } + } + } else { + // Checksum is provided during decoding, so use it. + for _, v := range checksum { + b := check >> 25 + check = (check&0x1ffffff)<<5 ^ int(v) + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + check ^= gen[i] + } + } + } + } + return check +} + +// writeBech32Checksum calculates the checksum data expected for a string that +// will have the given hrp and payload data and writes it to the provided string +// builder. +// +// The payload data MUST be encoded as a base 32 (5 bits per element) byte slice +// and the hrp MUST only use the allowed character set (ascii chars between 33 +// and 126), otherwise the results are undefined. +// +// For more details on the checksum calculation, please refer to BIP 173. +func writeBech32Checksum( + hrp []byte, data []byte, bldr *bytes.Buffer, + version Version, +) { + + bech32Const := int(VersionToConsts[version]) + polymod := bech32Polymod(hrp, data, nil) ^ bech32Const + for i := 0; i < 6; i++ { + b := byte((polymod >> uint(5*(5-i))) & 31) + // This can't fail, given we explicitly cap the previous b byte by the + // first 31 bits. + c := Charset[b] + bldr.WriteByte(c) + } +} + +// bech32VerifyChecksum verifies whether the bech32 string specified by the +// provided hrp and payload data (encoded as 5 bits per element byte slice) has +// the correct checksum suffix. The version of bech32 used (bech32 OG, or +// bech32m) is also returned to allow the caller to perform proper address +// validation (segwitv0 should use bech32, v1+ should use bech32m). +// +// Data MUST have more than 6 elements, otherwise this function panics. +// +// For more details on the checksum verification, please refer to BIP 173. +func bech32VerifyChecksum(hrp []byte, data []byte) (Version, bool) { + checksum := data[len(data)-6:] + values := data[:len(data)-6] + polymod := bech32Polymod(hrp, values, checksum) + // Before BIP-350, we'd always check this against a static constant of + // 1 to know if the checksum was computed properly. As we want to + // generically support decoding for bech32m as well as bech32, we'll + // look up the returned value and compare it to the set of defined + // constants. + bech32Version, ok := ConstsToVersion[ChecksumConst(polymod)] + if ok { + return bech32Version, true + } + return VersionUnknown, false +} + +// DecodeNoLimit is a bech32 checksum version aware arbitrary string length +// decoder. This function will return the version of the decoded checksum +// constant so higher level validation can be performed to ensure the correct +// version of bech32 was used when encoding. +func decodeNoLimit(bech []byte) ([]byte, []byte, Version, error) { + // The minimum allowed size of a bech32 string is 8 characters, since it + // needs a non-empty HRP, a separator, and a 6 character checksum. + if len(bech) < 8 { + return nil, nil, VersionUnknown, ErrInvalidLength(len(bech)) + } + // Only ASCII characters between 33 and 126 are allowed. + var hasLower, hasUpper bool + for i := 0; i < len(bech); i++ { + if bech[i] < 33 || bech[i] > 126 { + return nil, nil, VersionUnknown, ErrInvalidCharacter(bech[i]) + } + // The characters must be either all lowercase or all uppercase. Testing + // directly with ascii codes is safe here, given the previous test. + hasLower = hasLower || (bech[i] >= 97 && bech[i] <= 122) + hasUpper = hasUpper || (bech[i] >= 65 && bech[i] <= 90) + if hasLower && hasUpper { + return nil, nil, VersionUnknown, ErrMixedCase{} + } + } + // Bech32 standard uses only the lowercase for of strings for checksum + // calculation. + if hasUpper { + bech = bytes.ToLower(bech) + } + // The string is invalid if the last '1' is non-existent, it is the + // first character of the string (no human-readable part) or one of the + // last 6 characters of the string (since checksum cannot contain '1'). + one := bytes.LastIndexByte(bech, '1') + if one < 1 || one+7 > len(bech) { + return nil, nil, VersionUnknown, ErrInvalidSeparatorIndex(one) + } + // The human-readable part is everything before the last '1'. + hrp := bech[:one] + data := bech[one+1:] + // Each character corresponds to the byte with value of the index in + // 'charset'. + decoded, err := toBytes(data) + if err != nil { + return nil, nil, VersionUnknown, err + } + // Verify if the checksum (stored inside decoded[:]) is valid, given the + // previously decoded hrp. + bech32Version, ok := bech32VerifyChecksum(hrp, decoded) + if !ok { + // Invalid checksum. Calculate what it should have been, so that the + // error contains this information. + // + // Extract the payload bytes and actual checksum in the string. + actual := bech[len(bech)-6:] + payload := decoded[:len(decoded)-6] + // Calculate the expected checksum, given the hrp and payload + // data. We'll actually compute _both_ possibly valid checksum + // to further aide in debugging. + var expectedBldr bytes.Buffer + expectedBldr.Grow(6) + writeBech32Checksum(hrp, payload, &expectedBldr, Version0) + expectedVersion0 := expectedBldr.String() + var b strings.Builder + b.Grow(6) + writeBech32Checksum(hrp, payload, &expectedBldr, VersionM) + expectedVersionM := expectedBldr.String() + err = ErrInvalidChecksum{ + Expected: expectedVersion0, + ExpectedM: expectedVersionM, + Actual: string(actual), + } + return nil, nil, VersionUnknown, err + } + // We exclude the last 6 bytes, which is the checksum. + return hrp, decoded[:len(decoded)-6], bech32Version, nil +} + +// DecodeNoLimit decodes a bech32 encoded string, returning the human-readable +// part and the data part excluding the checksum. This function does NOT +// validate against the BIP-173 maximum length allowed for bech32 strings and +// is meant for use in custom applications (such as lightning network payment +// requests), NOT on-chain addresses. +// +// Note that the returned data is 5-bit (base32) encoded and the human-readable +// part will be lowercase. +func DecodeNoLimit(bech []byte) ([]byte, []byte, error) { + hrp, data, _, err := decodeNoLimit(bech) + return hrp, data, err +} + +// Decode decodes a bech32 encoded string, returning the human-readable part and +// the data part excluding the checksum. +// +// Note that the returned data is 5-bit (base32) encoded and the human-readable +// part will be lowercase. +func Decode(bech []byte) ([]byte, []byte, error) { + // The maximum allowed length for a bech32 string is 90. + if len(bech) > 90 { + return nil, nil, ErrInvalidLength(len(bech)) + } + hrp, data, _, err := decodeNoLimit(bech) + return hrp, data, err +} + +// DecodeGeneric is identical to the existing Decode method, but will also +// return bech32 version that matches the decoded checksum. This method should +// be used when decoding segwit addresses, as it enables additional +// verification to ensure the proper checksum is used. +func DecodeGeneric(bech []byte) ([]byte, []byte, Version, error) { + // The maximum allowed length for a bech32 string is 90. + if len(bech) > 90 { + return nil, nil, VersionUnknown, ErrInvalidLength(len(bech)) + } + return decodeNoLimit(bech) +} + +// encodeGeneric is the base bech32 encoding function that is aware of the +// existence of the checksum versions. This method is private, as the Encode +// and EncodeM methods are intended to be used instead. +func encodeGeneric(hrp []byte, data []byte, version Version) ([]byte, error) { + // The resulting bech32 string is the concatenation of the lowercase + // hrp, the separator 1, data and the 6-byte checksum. + hrp = bytes.ToLower(hrp) + var bldr bytes.Buffer + bldr.Grow(len(hrp) + 1 + len(data) + 6) + bldr.Write(hrp) + bldr.WriteString("1") + // Write the data part, using the bech32 charset. + for _, b := range data { + if int(b) >= len(Charset) { + return nil, ErrInvalidDataByte(b) + } + bldr.WriteByte(Charset[b]) + } + // Calculate and write the checksum of the data. + writeBech32Checksum(hrp, data, &bldr, version) + return bldr.Bytes(), nil +} + +// Encode encodes a byte slice into a bech32 string with the given +// human-readable part (HRP). The HRP will be converted to lowercase if needed +// since mixed cased encodings are not permitted and lowercase is used for +// checksum purposes. Note that the bytes must each encode 5 bits (base32). +func Encode(hrp, data []byte) ([]byte, error) { + return encodeGeneric(hrp, data, Version0) +} + +// EncodeM is the exactly same as the Encode method, but it uses the new +// bech32m constant instead of the original one. It should be used whenever one +// attempts to encode a segwit address of v1 and beyond. +func EncodeM(hrp, data []byte) ([]byte, error) { + return encodeGeneric(hrp, data, VersionM) +} + +// ConvertBits converts a byte slice where each byte is encoding fromBits bits, +// to a byte slice where each byte is encoding toBits bits. +func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ( + []byte, + error, +) { + + if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { + return nil, ErrInvalidBitGroups{} + } + // Determine the maximum size the resulting array can have after base + // conversion, so that we can size it a single time. This might be off + // by a byte depending on whether padding is used or not and if the input + // data is a multiple of both fromBits and toBits, but we ignore that and + // just size it to the maximum possible. + maxSize := len(data)*int(fromBits)/int(toBits) + 1 + // The final bytes, each byte encoding toBits bits. + regrouped := make([]byte, 0, maxSize) + // Keep track of the next byte we create and how many bits we have + // added to it out of the toBits goal. + nextByte := byte(0) + filledBits := uint8(0) + for _, b := range data { + // Discard unused bits. + b <<= 8 - fromBits + // How many bits remaining to extract from the input data. + remFromBits := fromBits + for remFromBits > 0 { + // How many bits remaining to be added to the next byte. + remToBits := toBits - filledBits + // The number of bytes to next extract is the minimum of + // remFromBits and remToBits. + toExtract := remFromBits + if remToBits < toExtract { + toExtract = remToBits + } + // Add the next bits to nextByte, shifting the already + // added bits to the left. + nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) + // Discard the bits we just extracted and get ready for + // next iteration. + b <<= toExtract + remFromBits -= toExtract + filledBits += toExtract + // If the nextByte is completely filled, we add it to + // our regrouped bytes and start on the next byte. + if filledBits == toBits { + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + } + } + // We pad any unfinished group if specified. + if pad && filledBits > 0 { + nextByte <<= toBits - filledBits + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + // Any incomplete group must be <= 4 bits, and all zeroes. + if filledBits > 0 && (filledBits > 4 || nextByte != 0) { + return nil, ErrInvalidIncompleteGroup{} + } + return regrouped, nil +} + +// EncodeFromBase256 converts a base256-encoded byte slice into a base32-encoded +// byte slice and then encodes it into a bech32 string with the given +// human-readable part (HRP). The HRP will be converted to lowercase if needed +// since mixed cased encodings are not permitted and lowercase is used for +// checksum purposes. +func EncodeFromBase256(hrp, data []byte) ([]byte, error) { + converted, err := ConvertBits(data, 8, 5, true) + if err != nil { + return nil, err + } + return Encode(hrp, converted) +} + +// DecodeToBase256 decodes a bech32-encoded string into its associated +// human-readable part (HRP) and base32-encoded data, converts that data to a +// base256-encoded byte slice and returns it along with the lowercase HRP. +func DecodeToBase256(bech []byte) ([]byte, []byte, error) { + hrp, data, err := Decode(bech) + if err != nil { + return nil, nil, err + } + converted, err := ConvertBits(data, 5, 8, false) + if err != nil { + return nil, nil, err + } + return hrp, converted, nil +} diff --git a/pkg/crypto/ec/bech32/bech32_test.go b/pkg/crypto/ec/bech32/bech32_test.go new file mode 100644 index 0000000..347fd0b --- /dev/null +++ b/pkg/crypto/ec/bech32/bech32_test.go @@ -0,0 +1,776 @@ +// Copyright (c) 2017-2020 The btcsuite developers +// Copyright (c) 2019 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "strings" + "testing" + + "next.orly.dev/pkg/utils" +) + +// TestBech32 tests whether decoding and re-encoding the valid BIP-173 test +// vectors works and if decoding invalid test vectors fails for the correct +// reason. +func TestBech32(t *testing.T) { + tests := []struct { + str string + expectedError error + }{ + {"A12UEL5L", nil}, + {"a12uel5l", nil}, + { + "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + nil, + }, + {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", nil}, + { + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + nil, + }, + {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", nil}, + { + "split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", + ErrInvalidChecksum{ + "2y9e3w", "2y9e3wlc445v", + "2y9e2w", + }, + }, // invalid checksum + { + "s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", + ErrInvalidCharacter(' '), + }, // invalid character (space) in hrp + { + "spl\x7Ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + ErrInvalidCharacter(127), + }, // invalid character (DEL) in hrp + { + "split1cheo2y9e2w", + ErrNonCharsetChar('o'), + }, // invalid character (o) in data part + {"split1a2y9w", ErrInvalidSeparatorIndex(5)}, // too short data part + { + "1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + ErrInvalidSeparatorIndex(0), + }, // empty hrp + { + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + ErrInvalidLength(91), + }, // too long + // Additional test vectors used in bitcoin core + {" 1nwldj5", ErrInvalidCharacter(' ')}, + {"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, + {"\x801eym55h", ErrInvalidCharacter(0x80)}, + { + "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", + ErrInvalidLength(91), + }, + {"pzry9x0s0muk", ErrInvalidSeparatorIndex(-1)}, + {"1pzry9x0s0muk", ErrInvalidSeparatorIndex(0)}, + {"x1b4n0q5v", ErrNonCharsetChar(98)}, + {"li1dgmt3", ErrInvalidSeparatorIndex(2)}, + {"de1lg7wt\xff", ErrInvalidCharacter(0xff)}, + {"A1G7SGD8", ErrInvalidChecksum{"2uel5l", "2uel5llqfn3a", "g7sgd8"}}, + {"10a06t8", ErrInvalidLength(7)}, + {"1qzzfhee", ErrInvalidSeparatorIndex(0)}, + {"a12UEL5L", ErrMixedCase{}}, + {"A12uEL5L", ErrMixedCase{}}, + } + for i, test := range tests { + str := []byte(test.str) + hrp, decoded, err := Decode([]byte(str)) + if !errors.Is(err, test.expectedError) { + t.Errorf( + "%d: expected decoding error %v "+ + "instead got %v", i, test.expectedError, err, + ) + continue + } + if err != nil { + // End test case here if a decoding error was expected. + continue + } + // Check that it encodes to the same string + encoded, err := Encode(hrp, decoded) + if err != nil { + t.Errorf("encoding failed: %v", err) + } + if !utils.FastEqual(encoded, bytes.ToLower([]byte(str))) { + t.Errorf( + "expected data to encode to %v, but got %v", + str, encoded, + ) + } + // Flip a bit in the string an make sure it is caught. + pos := bytes.LastIndexAny(str, "1") + flipped := []byte(string(str[:pos+1]) + string(str[pos+1]^1) + string(str[pos+2:])) + _, _, err = Decode(flipped) + if err == nil { + t.Error("expected decoding to fail") + } + } +} + +// TestBech32M tests that the following set of strings, based on the test +// vectors in BIP-350 are either valid or invalid using the new bech32m +// checksum algo. Some of these strings are similar to the set of above test +// vectors, but end up with different checksums. +func TestBech32M(t *testing.T) { + tests := []struct { + str string + expectedError error + }{ + {"A1LQFN3A", nil}, + {"a1lqfn3a", nil}, + { + "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", + nil, + }, + {"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", nil}, + { + "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", + nil, + }, + {"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", nil}, + {"?1v759aa", nil}, + // Additional test vectors used in bitcoin core + {"\x201xj0phk", ErrInvalidCharacter('\x20')}, + {"\x7f1g6xzxy", ErrInvalidCharacter('\x7f')}, + {"\x801vctc34", ErrInvalidCharacter('\x80')}, + { + "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", + ErrInvalidLength(91), + }, + {"qyrz8wqd2c9m", ErrInvalidSeparatorIndex(-1)}, + {"1qyrz8wqd2c9m", ErrInvalidSeparatorIndex(0)}, + {"y1b0jsk6g", ErrNonCharsetChar(98)}, + {"lt1igcx5c0", ErrNonCharsetChar(105)}, + {"in1muywd", ErrInvalidSeparatorIndex(2)}, + {"mm1crxm3i", ErrNonCharsetChar(105)}, + {"au1s5cgom", ErrNonCharsetChar(111)}, + {"M1VUXWEZ", ErrInvalidChecksum{"mzl49c", "mzl49cw70eq6", "vuxwez"}}, + {"16plkw9", ErrInvalidLength(7)}, + {"1p2gdwpf", ErrInvalidSeparatorIndex(0)}, + + {" 1nwldj5", ErrInvalidCharacter(' ')}, + {"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, + {"\x801eym55h", ErrInvalidCharacter(0x80)}, + } + for i, test := range tests { + str := []byte(test.str) + hrp, decoded, err := Decode(str) + if test.expectedError != err { + t.Errorf( + "%d: (%v) expected decoding error %v "+ + "instead got %v", i, str, test.expectedError, + err, + ) + continue + } + if err != nil { + // End test case here if a decoding error was expected. + continue + } + // Check that it encodes to the same string, using bech32 m. + encoded, err := EncodeM(hrp, decoded) + if err != nil { + t.Errorf("encoding failed: %v", err) + } + + if !utils.FastEqual(encoded, bytes.ToLower(str)) { + t.Errorf( + "expected data to encode to %v, but got %v", + str, encoded, + ) + } + // Flip a bit in the string an make sure it is caught. + pos := bytes.LastIndexAny(str, "1") + flipped := []byte(string(str[:pos+1]) + string(str[pos+1]^1) + string(str[pos+2:])) + _, _, err = Decode(flipped) + if err == nil { + t.Error("expected decoding to fail") + } + } +} + +// TestBech32DecodeGeneric tests that given a bech32 string, or a bech32m +// string, the proper checksum version is returned so that callers can perform +// segwit addr validation. +func TestBech32DecodeGeneric(t *testing.T) { + tests := []struct { + str string + version Version + }{ + {"A1LQFN3A", VersionM}, + {"a1lqfn3a", VersionM}, + { + "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", + VersionM, + }, + {"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", VersionM}, + { + "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", + VersionM, + }, + { + "split1checkupstagehandshakeupstreamerranterredcaperredlc445v", + VersionM, + }, + {"?1v759aa", VersionM}, + {"A12UEL5L", Version0}, + {"a12uel5l", Version0}, + { + "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + Version0, + }, + {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", Version0}, + { + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + Version0, + }, + { + "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + Version0, + }, + {"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", Version0}, + { + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + Version0, + }, + { + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", + VersionM, + }, + {"BC1SW50QGDZ25J", VersionM}, + {"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", VersionM}, + { + "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + Version0, + }, + { + "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", + VersionM, + }, + { + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", + VersionM, + }, + } + for i, test := range tests { + _, _, version, err := DecodeGeneric([]byte(test.str)) + if err != nil { + t.Errorf( + "%d: (%v) unexpected error during "+ + "decoding: %v", i, test.str, err, + ) + continue + } + if version != test.version { + t.Errorf( + "(%v): invalid version: expected %v, got %v", + test.str, test.version, version, + ) + } + } +} + +// TestMixedCaseEncode ensures mixed case HRPs are converted to lowercase as +// expected when encoding and that decoding the produced encoding when converted +// to all uppercase produces the lowercase HRP and original data. +func TestMixedCaseEncode(t *testing.T) { + tests := []struct { + name string + hrp string + data string + encoded string + }{ + { + name: "all uppercase HRP with no data", + hrp: "A", + data: "", + encoded: "a12uel5l", + }, { + name: "all uppercase HRP with data", + hrp: "UPPERCASE", + data: "787878", + encoded: "uppercase10pu8sss7kmp", + }, { + name: "mixed case HRP even offsets uppercase", + hrp: "AbCdEf", + data: "00443214c74254b635cf84653a56d7c675be77df", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + }, { + name: "mixed case HRP odd offsets uppercase ", + hrp: "aBcDeF", + data: "00443214c74254b635cf84653a56d7c675be77df", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + }, { + name: "all lowercase HRP", + hrp: "abcdef", + data: "00443214c74254b635cf84653a56d7c675be77df", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + }, + } + for _, test := range tests { + // Convert the text hex to bytes, convert those bytes from base256 to + // base32, then ensure the encoded result with the HRP provided in the + // test data is as expected. + data, err := hex.DecodeString(test.data) + if err != nil { + t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) + continue + } + convertedData, err := ConvertBits(data, 8, 5, true) + if err != nil { + t.Errorf( + "%q: unexpected convert bits error: %v", test.name, + err, + ) + continue + } + gotEncoded, err := Encode([]byte(test.hrp), convertedData) + if err != nil { + t.Errorf("%q: unexpected encode error: %v", test.name, err) + continue + } + if !utils.FastEqual(gotEncoded, []byte(test.encoded)) { + t.Errorf( + "%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, test.encoded, + ) + continue + } + // Ensure the decoding the expected lowercase encoding converted to all + // uppercase produces the lowercase HRP and original data. + gotHRP, gotData, err := Decode(bytes.ToUpper([]byte(test.encoded))) + if err != nil { + t.Errorf("%q: unexpected decode error: %v", test.name, err) + continue + } + wantHRP := strings.ToLower(test.hrp) + if !utils.FastEqual(gotHRP, []byte(wantHRP)) { + t.Errorf( + "%q: mismatched decoded HRP -- got %q, want %q", test.name, + gotHRP, wantHRP, + ) + continue + } + convertedGotData, err := ConvertBits(gotData, 5, 8, false) + if err != nil { + t.Errorf( + "%q: unexpected convert bits error: %v", test.name, + err, + ) + continue + } + if !utils.FastEqual(convertedGotData, data) { + t.Errorf( + "%q: mismatched data -- got %x, want %x", test.name, + convertedGotData, data, + ) + continue + } + } +} + +// TestCanDecodeUnlimtedBech32 tests whether decoding a large bech32 string works +// when using the DecodeNoLimit version +func TestCanDecodeUnlimtedBech32(t *testing.T) { + input := "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5kx0yd" + // Sanity check that an input of this length errors on regular Decode() + _, _, err := Decode([]byte(input)) + if err == nil { + t.Fatalf("Test vector not appropriate") + } + // Try and decode it. + hrp, data, err := DecodeNoLimit([]byte(input)) + if err != nil { + t.Fatalf( + "Expected decoding of large string to work. Got error: %v", + err, + ) + } + // Verify data for correctness. + if !utils.FastEqual(hrp, []byte("1")) { + t.Fatalf("Unexpected hrp: %v", hrp) + } + decodedHex := fmt.Sprintf("%x", data) + expected := "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000" + if decodedHex != expected { + t.Fatalf("Unexpected decoded data: %s", decodedHex) + } +} + +// TestBech32Base256 ensures decoding and encoding various bech32, HRPs, and +// data produces the expected results when using EncodeFromBase256 and +// DecodeToBase256. It includes tests for proper handling of case +// manipulations. +func TestBech32Base256(t *testing.T) { + tests := []struct { + name string // test name + encoded string // bech32 string to decode + hrp string // expected human-readable part + data string // expected hex-encoded data + err error // expected error + }{ + { + name: "all uppercase, no data", + encoded: "A12UEL5L", + hrp: "a", + data: "", + }, { + name: "long hrp with separator and excluded chars, no data", + encoded: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + hrp: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio", + data: "", + }, { + name: "6 char hrp with data with leading zero", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + hrp: "abcdef", + data: "00443214c74254b635cf84653a56d7c675be77df", + }, { + name: "hrp same as separator and max length encoded string", + encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + hrp: "1", + data: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "5 char hrp with data chosen to produce human-readable data part", + encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + hrp: "split", + data: "c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d", + }, { + name: "same as previous but with checksum invalidated", + encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", + err: ErrInvalidChecksum{"2y9e3w", "2y9e3wlc445v", "2y9e2w"}, + }, { + name: "hrp with invalid character (space)", + encoded: "s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", + err: ErrInvalidCharacter(' '), + }, { + name: "hrp with invalid character (DEL)", + encoded: "spl\x7ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + err: ErrInvalidCharacter(127), + }, { + name: "data part with invalid character (o)", + encoded: "split1cheo2y9e2w", + err: ErrNonCharsetChar('o'), + }, { + name: "data part too short", + encoded: "split1a2y9w", + err: ErrInvalidSeparatorIndex(5), + }, { + name: "empty hrp", + encoded: "1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + err: ErrInvalidSeparatorIndex(0), + }, { + name: "no separator", + encoded: "pzry9x0s0muk", + err: ErrInvalidSeparatorIndex(-1), + }, { + name: "too long by one char", + encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + err: ErrInvalidLength(91), + }, { + name: "invalid due to mixed case in hrp", + encoded: "aBcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + err: ErrMixedCase{}, + }, { + name: "invalid due to mixed case in data part", + encoded: "abcdef1Qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + err: ErrMixedCase{}, + }, + } + for _, test := range tests { + // Ensure the decode either produces an error or not as expected. + str := test.encoded + gotHRP, gotData, err := DecodeToBase256([]byte(str)) + if test.err != err { + t.Errorf( + "%q: unexpected decode error -- got %v, want %v", + test.name, err, test.err, + ) + continue + } + if err != nil { + // End test case here if a decoding error was expected. + continue + } + // Ensure the expected HRP and original data are as expected. + if !utils.FastEqual(gotHRP, []byte(test.hrp)) { + t.Errorf( + "%q: mismatched decoded HRP -- got %q, want %q", test.name, + gotHRP, test.hrp, + ) + continue + } + data, err := hex.DecodeString(test.data) + if err != nil { + t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) + continue + } + if !utils.FastEqual(gotData, data) { + t.Errorf( + "%q: mismatched data -- got %x, want %x", test.name, + gotData, data, + ) + continue + } + // Encode the same data with the HRP converted to all uppercase and + // ensure the result is the lowercase version of the original encoded + // bech32 string. + gotEncoded, err := EncodeFromBase256( + bytes.ToUpper([]byte(test.hrp)), data, + ) + if err != nil { + t.Errorf( + "%q: unexpected uppercase HRP encode error: %v", test.name, + err, + ) + } + wantEncoded := bytes.ToLower([]byte(str)) + if !utils.FastEqual(gotEncoded, wantEncoded) { + t.Errorf( + "%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, wantEncoded, + ) + } + // Encode the same data with the HRP converted to all lowercase and + // ensure the result is the lowercase version of the original encoded + // bech32 string. + gotEncoded, err = EncodeFromBase256( + bytes.ToLower([]byte(test.hrp)), data, + ) + if err != nil { + t.Errorf( + "%q: unexpected lowercase HRP encode error: %v", test.name, + err, + ) + } + if !utils.FastEqual(gotEncoded, wantEncoded) { + t.Errorf( + "%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, wantEncoded, + ) + } + // Encode the same data with the HRP converted to mixed upper and + // lowercase and ensure the result is the lowercase version of the + // original encoded bech32 string. + var mixedHRPBuilder bytes.Buffer + for i, r := range test.hrp { + if i%2 == 0 { + mixedHRPBuilder.WriteString(strings.ToUpper(string(r))) + continue + } + mixedHRPBuilder.WriteRune(r) + } + gotEncoded, err = EncodeFromBase256(mixedHRPBuilder.Bytes(), data) + if err != nil { + t.Errorf( + "%q: unexpected lowercase HRP encode error: %v", test.name, + err, + ) + } + if !utils.FastEqual(gotEncoded, wantEncoded) { + t.Errorf( + "%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, wantEncoded, + ) + } + // Ensure a bit flip in the string is caught. + pos := strings.LastIndexAny(test.encoded, "1") + flipped := str[:pos+1] + string(str[pos+1]^1) + str[pos+2:] + _, _, err = DecodeToBase256([]byte(flipped)) + if err == nil { + t.Error("expected decoding to fail") + } + } +} + +// BenchmarkEncodeDecodeCycle performs a benchmark for a full encode/decode +// cycle of a bech32 string. It also reports the allocation count, which we +// expect to be 2 for a fully optimized cycle. +func BenchmarkEncodeDecodeCycle(b *testing.B) { + // Use a fixed, 49-byte raw data for testing. + inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") + if err != nil { + b.Fatalf("failed to initialize input data: %v", err) + } + // Convert this into a 79-byte, base 32 byte slice. + base32Input, err := ConvertBits(inputData, 8, 5, true) + if err != nil { + b.Fatalf("failed to convert input to 32 bits-per-element: %v", err) + } + // Use a fixed hrp for the tests. This should generate an encoded bech32 + // string of size 90 (the maximum allowed by BIP-173). + hrp := "bc" + // Begin the benchmark. Given that we test one roundtrip per iteration + // (that is, one Encode() and one Decode() operation), we expect at most + // 2 allocations per reported test op. + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + str, err := Encode([]byte(hrp), base32Input) + if err != nil { + b.Fatalf("failed to encode input: %v", err) + } + _, _, err = Decode(str) + if err != nil { + b.Fatalf("failed to decode string: %v", err) + } + } +} + +// TestConvertBits tests whether base conversion works using TestConvertBits(). +func TestConvertBits(t *testing.T) { + tests := []struct { + input string + output string + fromBits uint8 + toBits uint8 + pad bool + }{ + // Trivial empty conversions. + {"", "", 8, 5, false}, + {"", "", 8, 5, true}, + {"", "", 5, 8, false}, + {"", "", 5, 8, true}, + // Conversions of 0 value with/without padding. + {"00", "00", 8, 5, false}, + {"00", "0000", 8, 5, true}, + {"0000", "00", 5, 8, false}, + {"0000", "0000", 5, 8, true}, + // Testing when conversion ends exactly at the byte edge. This makes + // both padded and unpadded versions the same. + {"0000000000", "0000000000000000", 8, 5, false}, + {"0000000000", "0000000000000000", 8, 5, true}, + {"0000000000000000", "0000000000", 5, 8, false}, + {"0000000000000000", "0000000000", 5, 8, true}, + // Conversions of full byte sequences. + {"ffffff", "1f1f1f1f1e", 8, 5, true}, + {"1f1f1f1f1e", "ffffff", 5, 8, false}, + {"1f1f1f1f1e", "ffffff00", 5, 8, true}, + // Sample random conversions. + {"c9ca", "190705", 8, 5, false}, + {"c9ca", "19070500", 8, 5, true}, + {"19070500", "c9ca", 5, 8, false}, + {"19070500", "c9ca00", 5, 8, true}, + // Test cases tested on TestConvertBitsFailures with their corresponding + // fixes. + {"ff", "1f1c", 8, 5, true}, + {"1f1c10", "ff20", 5, 8, true}, + // Large conversions. + { + "cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1", + "190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", + 8, 5, true, + }, + { + "190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", + "cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed100", + 5, 8, true, + }, + } + for i, tc := range tests { + input, err := hex.DecodeString(tc.input) + if err != nil { + t.Fatalf("invalid test input data: %v", err) + } + expected, err := hex.DecodeString(tc.output) + if err != nil { + t.Fatalf("invalid test output data: %v", err) + } + actual, err := ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) + if err != nil { + t.Fatalf("test case %d failed: %v", i, err) + } + if !utils.FastEqual(actual, expected) { + t.Fatalf( + "test case %d has wrong output; expected=%x actual=%x", + i, expected, actual, + ) + } + } +} + +// TestConvertBitsFailures tests for the expected conversion failures of +// ConvertBits(). +func TestConvertBitsFailures(t *testing.T) { + tests := []struct { + input string + fromBits uint8 + toBits uint8 + pad bool + err error + }{ + // Not enough output bytes when not using padding. + {"ff", 8, 5, false, ErrInvalidIncompleteGroup{}}, + {"1f1c10", 5, 8, false, ErrInvalidIncompleteGroup{}}, + // Unsupported bit conversions. + {"", 0, 5, false, ErrInvalidBitGroups{}}, + {"", 10, 5, false, ErrInvalidBitGroups{}}, + {"", 5, 0, false, ErrInvalidBitGroups{}}, + {"", 5, 10, false, ErrInvalidBitGroups{}}, + } + for i, tc := range tests { + input, err := hex.DecodeString(tc.input) + if err != nil { + t.Fatalf("invalid test input data: %v", err) + } + _, err = ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) + if err != tc.err { + t.Fatalf( + "test case %d failure: expected '%v' got '%v'", i, + tc.err, err, + ) + } + } +} + +// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior +// of ConvertBits when converting from a higher base into a lower base (e.g. 8 +// => 5). +// +// Only a single allocation is expected, which is used for the output array. +func BenchmarkConvertBitsDown(b *testing.B) { + // Use a fixed, 49-byte raw data for testing. + inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") + if err != nil { + b.Fatalf("failed to initialize input data: %v", err) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ConvertBits(inputData, 8, 5, true) + if err != nil { + b.Fatalf("error converting bits: %v", err) + } + } +} + +// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior +// of ConvertBits when converting from a lower base into a higher base (e.g. 5 +// => 8). +// +// Only a single allocation is expected, which is used for the output array. +func BenchmarkConvertBitsUp(b *testing.B) { + // Use a fixed, 79-byte raw data for testing. + inputData, err := hex.DecodeString("190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408") + if err != nil { + b.Fatalf("failed to initialize input data: %v", err) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ConvertBits(inputData, 8, 5, true) + if err != nil { + b.Fatalf("error converting bits: %v", err) + } + } +} diff --git a/pkg/crypto/ec/bech32/doc.go b/pkg/crypto/ec/bech32/doc.go new file mode 100644 index 0000000..73d4521 --- /dev/null +++ b/pkg/crypto/ec/bech32/doc.go @@ -0,0 +1,13 @@ +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package bech32 provides a Go implementation of the bech32 format specified in +// BIP 173. +// +// Bech32 strings consist of a human-readable part (hrp), followed by the +// separator 1, then a checksummed data part encoded using the 32 characters +// "qpzry9x8gf2tvdw0s3jn54khce6mua7l". +// +// More info: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki +package bech32 diff --git a/pkg/crypto/ec/bech32/error.go b/pkg/crypto/ec/bech32/error.go new file mode 100644 index 0000000..efcc4d2 --- /dev/null +++ b/pkg/crypto/ec/bech32/error.go @@ -0,0 +1,89 @@ +// Copyright (c) 2019 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "fmt" +) + +// ErrMixedCase is returned when the bech32 string has both lower and uppercase +// characters. +type ErrMixedCase struct{} + +func (err ErrMixedCase) Error() string { + return "string not all lowercase or all uppercase" +} + +// ErrInvalidBitGroups is returned when conversion is attempted between byte +// slices using bit-per-element of unsupported value. +type ErrInvalidBitGroups struct{} + +func (err ErrInvalidBitGroups) Error() string { + return "only bit groups between 1 and 8 allowed" +} + +// ErrInvalidIncompleteGroup is returned when then byte slice used as input has +// data of wrong length. +type ErrInvalidIncompleteGroup struct{} + +func (err ErrInvalidIncompleteGroup) Error() string { + return "invalid incomplete group" +} + +// ErrInvalidLength is returned when the bech32 string has an invalid length +// given the BIP-173 defined restrictions. +type ErrInvalidLength int + +func (err ErrInvalidLength) Error() string { + return fmt.Sprintf("invalid bech32 string length %d", int(err)) +} + +// ErrInvalidCharacter is returned when the bech32 string has a character +// outside the range of the supported charset. +type ErrInvalidCharacter rune + +func (err ErrInvalidCharacter) Error() string { + return fmt.Sprintf("invalid character in string: '%c'", rune(err)) +} + +// ErrInvalidSeparatorIndex is returned when the separator character '1' is +// in an invalid position in the bech32 string. +type ErrInvalidSeparatorIndex int + +func (err ErrInvalidSeparatorIndex) Error() string { + return fmt.Sprintf("invalid separator index %d", int(err)) +} + +// ErrNonCharsetChar is returned when a character outside of the specific +// bech32 charset is used in the string. +type ErrNonCharsetChar rune + +func (err ErrNonCharsetChar) Error() string { + return fmt.Sprintf("invalid character not part of charset: %v", int(err)) +} + +// ErrInvalidChecksum is returned when the extracted checksum of the string +// is different than what was expected. Both the original version, as well as +// the new bech32m checksum may be specified. +type ErrInvalidChecksum struct { + Expected string + ExpectedM string + Actual string +} + +func (err ErrInvalidChecksum) Error() string { + return fmt.Sprintf( + "invalid checksum (expected (bech32=%v, "+ + "bech32m=%v), got %v)", err.Expected, err.ExpectedM, err.Actual, + ) +} + +// ErrInvalidDataByte is returned when a byte outside the range required for +// conversion into a string was found. +type ErrInvalidDataByte byte + +func (err ErrInvalidDataByte) Error() string { + return fmt.Sprintf("invalid data byte: %v", byte(err)) +} diff --git a/pkg/crypto/ec/bech32/example_test.go b/pkg/crypto/ec/bech32/example_test.go new file mode 100644 index 0000000..ae15651 --- /dev/null +++ b/pkg/crypto/ec/bech32/example_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "encoding/hex" + "fmt" +) + +// This example demonstrates how to decode a bech32 encoded string. +func ExampleDecode() { + encoded := "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx" + hrp, decoded, err := Decode([]byte(encoded)) + if err != nil { + fmt.Println("Error:", err) + } + // Show the decoded data. + fmt.Printf("Decoded human-readable part: %s\n", hrp) + fmt.Println("Decoded Data:", hex.EncodeToString(decoded)) + // Output: + // Decoded human-readable part: bc + // Decoded Data: 010e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e160e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16 +} + +// This example demonstrates how to encode data into a bech32 string. +func ExampleEncode() { + data := []byte("Test data") + // Convert test data to base32: + conv, err := ConvertBits(data, 8, 5, true) + if err != nil { + fmt.Println("Error:", err) + } + encoded, err := Encode([]byte("customHrp!11111q"), conv) + if err != nil { + fmt.Println("Error:", err) + } + // Show the encoded data. + fmt.Printf("Encoded Data: %s", encoded) + // Output: + // Encoded Data: customhrp!11111q123jhxapqv3shgcgkxpuhe +} diff --git a/pkg/crypto/ec/bech32/version.go b/pkg/crypto/ec/bech32/version.go new file mode 100644 index 0000000..c1e75d2 --- /dev/null +++ b/pkg/crypto/ec/bech32/version.go @@ -0,0 +1,40 @@ +package bech32 + +// ChecksumConst is a type that represents the currently defined bech32 +// checksum constants. +type ChecksumConst int + +const ( + // Version0Const is the original constant used in the checksum + // verification for bech32. + Version0Const ChecksumConst = 1 + // VersionMConst is the new constant used for bech32m checksum + // verification. + VersionMConst ChecksumConst = 0x2bc830a3 +) + +// Version defines the current set of bech32 versions. +type Version uint8 + +const ( + // Version0 defines the original bech version. + Version0 Version = iota + // VersionM is the new bech32 version defined in BIP-350, also known as + // bech32m. + VersionM + // VersionUnknown denotes an unknown bech version. + VersionUnknown +) + +// VersionToConsts maps bech32 versions to the checksum constant to be used +// when encoding, and asserting a particular version when decoding. +var VersionToConsts = map[Version]ChecksumConst{ + Version0: Version0Const, + VersionM: VersionMConst, +} + +// ConstsToVersion maps a bech32 constant to the version it's associated with. +var ConstsToVersion = map[ChecksumConst]Version{ + Version0Const: Version0, + VersionMConst: VersionM, +} diff --git a/pkg/crypto/ec/bench_test.go b/pkg/crypto/ec/bench_test.go new file mode 100644 index 0000000..2674278 --- /dev/null +++ b/pkg/crypto/ec/bench_test.go @@ -0,0 +1,188 @@ +// Copyright 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "math/big" + "testing" + + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/encoders/hex" +) + +// setHex decodes the passed big-endian hex string into the internal field value +// representation. Only the first 32-bytes are used. +// +// 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 setHex(hexString string) *FieldVal { + if len(hexString)%2 != 0 { + hexString = "0" + hexString + } + bytes, _ := hex.Dec(hexString) + var f FieldVal + f.SetByteSlice(bytes) + return &f +} + +// 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 { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var f FieldVal + if overflow := f.SetByteSlice(b); overflow { + panic("hex in source file overflows mod P: " + s) + } + return &f +} + +// fromHex converts the passed hex string into a big integer pointer and will +// panic is there is an error. This is only provided for the hard-coded +// constants so errors in the source code can bet detected. It will only (and +// must only) be called for initialization purposes. +func fromHex(s string) *big.Int { + if s == "" { + return big.NewInt(0) + } + r, ok := new(big.Int).SetString(s, 16) + if !ok { + panic("invalid hex in source file: " + s) + } + return r +} + +// 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. +func jacobianPointFromHex(x, y, z string) JacobianPoint { + var p JacobianPoint + p.X = *setHex(x) + p.Y = *setHex(y) + p.Z = *setHex(z) + return p +} + +// BenchmarkAddNonConst benchmarks the secp256k1 curve AddNonConst function with +// Z values of 1 so that the associated optimizations are used. +func BenchmarkAddJacobian(b *testing.B) { + p1 := jacobianPointFromHex( + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + ) + p2 := jacobianPointFromHex( + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + ) + b.ReportAllocs() + b.ResetTimer() + var result JacobianPoint + for i := 0; i < b.N; i++ { + secp256k1.AddNonConst(&p1, &p2, &result) + } +} + +// BenchmarkAddNonConstNotZOne benchmarks the secp256k1 curve AddNonConst +// function with Z values other than one so the optimizations associated with +// Z=1 aren't used. +func BenchmarkAddJacobianNotZOne(b *testing.B) { + x1 := setHex("d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718") + y1 := setHex("5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190") + z1 := setHex("2") + x2 := setHex("91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4") + y2 := setHex("03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1") + z2 := setHex("3") + p1 := MakeJacobianPoint(x1, y1, z1) + p2 := MakeJacobianPoint(x2, y2, z2) + b.ReportAllocs() + b.ResetTimer() + var result JacobianPoint + for i := 0; i < b.N; i++ { + AddNonConst(&p1, &p2, &result) + } +} + +// BenchmarkScalarBaseMult benchmarks the secp256k1 curve ScalarBaseMult +// function. +func BenchmarkScalarBaseMult(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + curve := S256() + for i := 0; i < b.N; i++ { + curve.ScalarBaseMult(k.Bytes()) + } +} + +// BenchmarkScalarBaseMultLarge benchmarks the secp256k1 curve ScalarBaseMult +// function with abnormally large k values. +func BenchmarkScalarBaseMultLarge(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c005751111111011111110") + curve := S256() + for i := 0; i < b.N; i++ { + curve.ScalarBaseMult(k.Bytes()) + } +} + +// BenchmarkScalarMult benchmarks the secp256k1 curve ScalarMult function. +func BenchmarkScalarMult(b *testing.B) { + x := fromHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") + y := fromHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + curve := S256() + for i := 0; i < b.N; i++ { + curve.ScalarMult(x, y, k.Bytes()) + } +} + +// 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 { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var scalar ModNScalar + if overflow := scalar.SetByteSlice(b); overflow { + panic("hex in source file overflows mod N scalar: " + s) + } + return &scalar +} + +// BenchmarkFieldNormalize benchmarks how long it takes the internal field +// to perform normalization (which includes modular reduction). +func BenchmarkFieldNormalize(b *testing.B) { + // The normalize function is constant time so default value is fine. + var f FieldVal + for i := 0; i < b.N; i++ { + f.Normalize() + } +} + +// BenchmarkParseCompressedPubKey benchmarks how long it takes to decompress and +// validate a compressed public key from a byte array. +func BenchmarkParseCompressedPubKey(b *testing.B) { + rawPk, _ := hex.Dec("0234f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") + + var ( + pk *PublicKey + err error + ) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + pk, err = ParsePubKey(rawPk) + } + _ = pk + _ = err +} diff --git a/pkg/crypto/ec/btcec.go b/pkg/crypto/ec/btcec.go new file mode 100644 index 0000000..cfa0309 --- /dev/null +++ b/pkg/crypto/ec/btcec.go @@ -0,0 +1,53 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// 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. + +package btcec + +// References: +// [SECG]: Recommended Elliptic Curve Domain Parameters +// http://www.secg.org/sec2-v2.pdf +// +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) + +// This package operates, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1) +// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole +// calculation can be performed within the transform (as in ScalarMult and +// ScalarBaseMult). But even for Add and Double, it's faster to apply and +// reverse the transform than to operate in affine coordinates. + +import ( + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// KoblitzCurve provides an implementation for secp256k1 that fits the ECC +// Curve interface from crypto/elliptic. +type KoblitzCurve = secp256k1.KoblitzCurve + +// S256 returns a Curve which implements secp256k1. +func S256() *KoblitzCurve { + return secp256k1.S256() +} + +// CurveParams contains the parameters for the secp256k1 curve. +type CurveParams = secp256k1.CurveParams + +// Params returns the secp256k1 curve parameters for convenience. +func Params() *CurveParams { + return secp256k1.Params() +} + +// Generator returns the public key at the Generator Point. +func Generator() *PublicKey { + var ( + result JacobianPoint + k secp256k1.ModNScalar + ) + k.SetInt(1) + ScalarBaseMultNonConst(&k, &result) + result.ToAffine() + return NewPublicKey(&result.X, &result.Y) +} diff --git a/pkg/crypto/ec/btcec_test.go b/pkg/crypto/ec/btcec_test.go new file mode 100644 index 0000000..1832295 --- /dev/null +++ b/pkg/crypto/ec/btcec_test.go @@ -0,0 +1,918 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Copyright 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "crypto/rand" + "fmt" + "math/big" + "testing" +) + +// isJacobianOnS256Curve returns boolean if the point (x,y,z) is on the +// secp256k1 curve. +func isJacobianOnS256Curve(point *JacobianPoint) bool { + // Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7 + // In Jacobian coordinates, Y = y/z^3 and X = x/z^2 + // Thus: + // (y/z^3)^2 = (x/z^2)^3 + 7 + // y^2/z^6 = x^3/z^6 + 7 + // y^2 = x^3 + 7*z^6 + var y2, z2, x3, result FieldVal + y2.SquareVal(&point.Y).Normalize() + z2.SquareVal(&point.Z) + x3.SquareVal(&point.X).Mul(&point.X) + result.SquareVal(&z2).Mul(&z2).MulInt(7).Add(&x3).Normalize() + return y2.Equals(&result) +} + +// TestAddJacobian tests addition of points projected in Jacobian coordinates. +func TestAddJacobian(t *testing.T) { + tests := []struct { + x1, y1, z1 string // Coordinates (in hex) of first point to add + x2, y2, z2 string // Coordinates (in hex) of second point to add + x3, y3, z3 string // Coordinates (in hex) of expected point + }{ + // Addition with a point at infinity (left hand side). + // ∞ + P = P + { + "0", + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + }, + // Addition with a point at infinity (right hand side). + // P + ∞ = P + { + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "0", + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + }, + // Addition with z1=z2=1 different x values. + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "0cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a6", + "e205f79361bbe0346b037b4010985dbf4f9e1e955e7d0d14aca876bfa79aad87", + "44a5646b446e3877a648d6d381370d9ef55a83b666ebce9df1b1d7d65b817b2f", + }, + // Addition with z1=z2=1 same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + "1", + "0", + "0", + "0", + }, + // Addition with z1=z2=1 same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", + "b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", + "16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", + }, + + // Addition with z1=z2 (!=1) different x values. + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "5d2fe112c21891d440f65a98473cb626111f8a234d2cd82f22172e369f002147", + "98e3386a0a622a35c4561ffb32308d8e1c6758e10ebb1b4ebd3d04b4eb0ecbe8", + "2", + "cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a60", + "817de4d86ef80d1ac0ded00426176fd3e787a5579f43452b2a1db021e6ac3778", + "129591ad11b8e1de99235b4e04dc367bd56a0ed99baf3a77c6c75f5a6e05f08d", + }, + // Addition with z1=z2 (!=1) same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "a470ab21467813b6e0496d2c2b70c11446bab4fcbc9a52b7f225f30e869aea9f", + "2", + "0", + "0", + "0", + }, + // Addition with z1=z2 (!=1) same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + + // Addition with z1!=z2 and z2=1 different x values. + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "3ef1f68795a6ccd1181e23eab80a1b9a2cebdcde755413bf097936eb5b91b4f3", + "0bef26c377c068d606f6802130bb7e9f3c3d2abcfa1a295950ed81133561cb04", + "252b235a2371c3bd3246b69c09b86cf7aad41db3375e74ef8d8ebeb4dc0be11a", + }, + // Addition with z1!=z2 and z2=1 same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + "1", + "0", + "0", + "0", + }, + // Addition with z1!=z2 and z2=1 same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + + // Addition with z1!=z2 and z2!=1 different x values. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4", + "03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1", + "3", + "3f07081927fd3f6dadd4476614c89a09eba7f57c1c6c3b01fa2d64eac1eef31e", + "949166e04ebc7fd95a9d77e5dfd88d1492ecffd189792e3944eb2b765e09e031", + "eb8cba81bcffa4f44d75427506737e1f045f21e6d6f65543ee0e1d163540c931", + }, // Addition with z1!=z2 and z2!=1 same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", + "cafc41904dd5428934f7d075129c8ba46eb622d4fc88d72cd1401452664add18", + "3", + "0", + "0", + "0", + }, + // Addition with z1!=z2 and z2!=1 same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", + "3503be6fb22abd76cb082f8aed63745b9149dd2b037728d32ebfebac99b51f17", + "3", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to Jacobian points. + p1 := jacobianPointFromHex(test.x1, test.y1, test.z1) + p2 := jacobianPointFromHex(test.x2, test.y2, test.z2) + want := jacobianPointFromHex(test.x3, test.y3, test.z3) + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !p1.Z.IsZero() && !isJacobianOnS256Curve(&p1) { + t.Errorf( + "#%d first point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + if !p2.Z.IsZero() && !isJacobianOnS256Curve(&p2) { + t.Errorf( + "#%d second point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + if !want.Z.IsZero() && !isJacobianOnS256Curve(&want) { + t.Errorf( + "#%d expected point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + // Add the two points. + var r JacobianPoint + AddNonConst(&p1, &p2, &r) + + // Ensure result matches expected. + if !r.X.Equals(&want.X) || !r.Y.Equals(&want.Y) || !r.Z.Equals(&want.Z) { + t.Errorf( + "#%d wrong result\ngot: (%v, %v, %v)\n"+ + "want: (%v, %v, %v)", i, r.X, r.Y, r.Z, want.X, want.Y, + want.Z, + ) + continue + } + } +} + +// TestAddAffine tests addition of points in affine coordinates. +func TestAddAffine(t *testing.T) { + tests := []struct { + x1, y1 string // Coordinates (in hex) of first point to add + x2, y2 string // Coordinates (in hex) of second point to add + x3, y3 string // Coordinates (in hex) of expected point + }{ + // Addition with a point at infinity (left hand side). + // ∞ + P = P + { + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, + // Addition with a point at infinity (right hand side). + // P + ∞ = P + { + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, + + // Addition with different x values. + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "fd5b88c21d3143518d522cd2796f3d726793c88b3e05636bc829448e053fed69", + "21cf4f6a5be5ff6380234c50424a970b1f7e718f5eb58f68198c108d642a137f", + }, + // Addition with same x opposite y. + // P(x, y) + P(x, -y) = infinity + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + "0", + "0", + }, + // Addition with same point. + // P(x, y) + P(x, y) = 2P + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "59477d88ae64a104dbb8d31ec4ce2d91b2fe50fa628fb6a064e22582196b365b", + "938dc8c0f13d1e75c987cb1a220501bd614b0d3dd9eb5c639847e1240216e3b6", + }, + } + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + x1, y1 := fromHex(test.x1), fromHex(test.y1) + x2, y2 := fromHex(test.x2), fromHex(test.y2) + x3, y3 := fromHex(test.x3), fromHex(test.y3) + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !(x1.Sign() == 0 && y1.Sign() == 0) && !S256().IsOnCurve(x1, y1) { + t.Errorf( + "#%d first point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + if !(x2.Sign() == 0 && y2.Sign() == 0) && !S256().IsOnCurve(x2, y2) { + t.Errorf( + "#%d second point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + if !(x3.Sign() == 0 && y3.Sign() == 0) && !S256().IsOnCurve(x3, y3) { + t.Errorf( + "#%d expected point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + // Add the two points. + rx, ry := S256().Add(x1, y1, x2, y2) + + // Ensure result matches expected. + if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 { + t.Errorf( + "#%d wrong result\ngot: (%x, %x)\n"+ + "want: (%x, %x)", i, rx, ry, x3, y3, + ) + continue + } + } +} + +// isStrictlyEqual returns whether or not the two Jacobian points are strictly +// equal for use in the tests. Recall that several Jacobian points can be +// equal in affine coordinates, while not having the same coordinates in +// projective space, so the two points not being equal doesn't necessarily mean +// they aren't actually the same affine point. +func isStrictlyEqual(p, other *JacobianPoint) bool { + return p.X.Equals(&other.X) && p.Y.Equals(&other.Y) && p.Z.Equals(&other.Z) +} + +// TestDoubleJacobian tests doubling of points projected in Jacobian +// coordinates. +func TestDoubleJacobian(t *testing.T) { + tests := []struct { + x1, y1, z1 string // Coordinates (in hex) of point to double + x3, y3, z3 string // Coordinates (in hex) of expected point + }{ + // Doubling a point at infinity is still infinity. + { + "0", + "0", + "0", + "0", + "0", + "0", + }, + // Doubling with z1=1. + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", + "b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", + "16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", + }, + // Doubling with z1!=1. + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + // From btcd issue #709. + { + "201e3f75715136d2f93c4f4598f91826f94ca01f4233a5bd35de9708859ca50d", + "bdf18566445e7562c6ada68aef02d498d7301503de5b18c6aef6e2b1722412e1", + "0000000000000000000000000000000000000000000000000000000000000001", + "4a5e0559863ebb4e9ed85f5c4fa76003d05d9a7626616e614a1f738621e3c220", + "00000000000000000000000000000000000000000000000000000001b1388778", + "7be30acc88bceac58d5b4d15de05a931ae602a07bcb6318d5dedc563e4482993", + }, + } + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + p1 := jacobianPointFromHex(test.x1, test.y1, test.z1) + want := jacobianPointFromHex(test.x3, test.y3, test.z3) + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !p1.Z.IsZero() && !isJacobianOnS256Curve(&p1) { + t.Errorf( + "#%d first point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + if !want.Z.IsZero() && !isJacobianOnS256Curve(&want) { + t.Errorf( + "#%d expected point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + // Double the point. + var result JacobianPoint + DoubleNonConst(&p1, &result) + // Ensure result matches expected. + if !isStrictlyEqual(&result, &want) { + t.Errorf( + "#%d wrong result\ngot: (%v, %v, %v)\n"+ + "want: (%v, %v, %v)", i, result.X, result.Y, result.Z, + want.X, want.Y, want.Z, + ) + continue + } + } +} + +// TestDoubleAffine tests doubling of points in affine coordinates. +func TestDoubleAffine(t *testing.T) { + tests := []struct { + x1, y1 string // Coordinates (in hex) of point to double + x3, y3 string // Coordinates (in hex) of expected point + }{ + // Doubling a point at infinity is still infinity. + // 2*∞ = ∞ (point at infinity) + { + "0", + "0", + "0", + "0", + }, + // Random points. + { + "e41387ffd8baaeeb43c2faa44e141b19790e8ac1f7ff43d480dc132230536f86", + "1b88191d430f559896149c86cbcb703193105e3cf3213c0c3556399836a2b899", + "88da47a089d333371bd798c548ef7caae76e737c1980b452d367b3cfe3082c19", + "3b6f659b09a362821dfcfefdbfbc2e59b935ba081b6c249eb147b3c2100b1bc1", + }, + { + "b3589b5d984f03ef7c80aeae444f919374799edf18d375cab10489a3009cff0c", + "c26cf343875b3630e15bccc61202815b5d8f1fd11308934a584a5babe69db36a", + "e193860172998751e527bb12563855602a227fc1f612523394da53b746bb2fb1", + "2bfcf13d2f5ab8bb5c611fab5ebbed3dc2f057062b39a335224c22f090c04789", + }, + { + "2b31a40fbebe3440d43ac28dba23eee71c62762c3fe3dbd88b4ab82dc6a82340", + "9ba7deb02f5c010e217607fd49d58db78ec273371ea828b49891ce2fd74959a1", + "2c8d5ef0d343b1a1a48aa336078eadda8481cb048d9305dc4fdf7ee5f65973a2", + "bb4914ac729e26d3cd8f8dc8f702f3f4bb7e0e9c5ae43335f6e94c2de6c3dc95", + }, + { + "61c64b760b51981fab54716d5078ab7dffc93730b1d1823477e27c51f6904c7a", + "ef6eb16ea1a36af69d7f66524c75a3a5e84c13be8fbc2e811e0563c5405e49bd", + "5f0dcdd2595f5ad83318a0f9da481039e36f135005420393e72dfca985b482f4", + "a01c849b0837065c1cb481b0932c441f49d1cab1b4b9f355c35173d93f110ae0", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + x1, y1 := fromHex(test.x1), fromHex(test.y1) + x3, y3 := fromHex(test.x3), fromHex(test.y3) + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !(x1.Sign() == 0 && y1.Sign() == 0) && !S256().IsOnCurve(x1, y1) { + t.Errorf( + "#%d first point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + if !(x3.Sign() == 0 && y3.Sign() == 0) && !S256().IsOnCurve(x3, y3) { + t.Errorf( + "#%d expected point is not on the curve -- "+ + "invalid test data", i, + ) + continue + } + // Double the point. + rx, ry := S256().Double(x1, y1) + + // Ensure result matches expected. + if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 { + t.Errorf( + "#%d wrong result\ngot: (%x, %x)\n"+ + "want: (%x, %x)", i, rx, ry, x3, y3, + ) + continue + } + } +} + +func TestOnCurve(t *testing.T) { + s256 := S256() + if !s256.IsOnCurve(s256.Params().Gx, s256.Params().Gy) { + t.Errorf("FAIL S256") + } +} + +type baseMultTest struct { + k string + x, y string +} + +// TODO: add more test vectors +var s256BaseMultTests = []baseMultTest{ + { + "AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522", + "34F9460F0E4F08393D192B3C5133A6BA099AA0AD9FD54EBCCFACDFA239FF49C6", + "B71EA9BD730FD8923F6D25A7A91E7DD7728A960686CB5A901BB419E0F2CA232", + }, + { + "7E2B897B8CEBC6361663AD410835639826D590F393D90A9538881735256DFAE3", + "D74BF844B0862475103D96A611CF2D898447E288D34B360BC885CB8CE7C00575", + "131C670D414C4546B88AC3FF664611B1C38CEB1C21D76369D7A7A0969D61D97D", + }, + { + "6461E6DF0FE7DFD05329F41BF771B86578143D4DD1F7866FB4CA7E97C5FA945D", + "E8AECC370AEDD953483719A116711963CE201AC3EB21D3F3257BB48668C6A72F", + "C25CAF2F0EBA1DDB2F0F3F47866299EF907867B7D27E95B3873BF98397B24EE1", + }, + { + "376A3A2CDCD12581EFFF13EE4AD44C4044B8A0524C42422A7E1E181E4DEECCEC", + "14890E61FCD4B0BD92E5B36C81372CA6FED471EF3AA60A3E415EE4FE987DABA1", + "297B858D9F752AB42D3BCA67EE0EB6DCD1C2B7B0DBE23397E66ADC272263F982", + }, + { + "1B22644A7BE026548810C378D0B2994EEFA6D2B9881803CB02CEFF865287D1B9", + "F73C65EAD01C5126F28F442D087689BFA08E12763E0CEC1D35B01751FD735ED3", + "F449A8376906482A84ED01479BD18882B919C140D638307F0C0934BA12590BDE", + }, +} + +// TODO: test different curves as well? +func TestBaseMult(t *testing.T) { + s256 := S256() + for i, e := range s256BaseMultTests { + k, ok := new(big.Int).SetString(e.k, 16) + if !ok { + t.Errorf("%d: bad value for k: %s", i, e.k) + } + x, y := s256.ScalarBaseMult(k.Bytes()) + if fmt.Sprintf("%X", x) != e.x || fmt.Sprintf("%X", y) != e.y { + t.Errorf( + "%d: bad output for k=%s: got (%X, %X), want (%s, %s)", i, + e.k, x, y, e.x, e.y, + ) + } + if testing.Short() && i > 5 { + break + } + } +} + +func TestBaseMultVerify(t *testing.T) { + s256 := S256() + for bytes := 1; bytes < 40; bytes++ { + for i := 0; i < 30; i++ { + data := make([]byte, bytes) + _, err := rand.Read(data) + if err != nil { + t.Errorf("failed to read random data for %d", i) + continue + } + x, y := s256.ScalarBaseMult(data) + xWant, yWant := s256.ScalarMult(s256.Gx, s256.Gy, data) + if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 { + t.Errorf( + "%d: bad output for %X: got (%X, %X), want (%X, %X)", + i, data, x, y, xWant, yWant, + ) + } + if testing.Short() && i > 2 { + break + } + } + } +} + +func TestScalarMult(t *testing.T) { + tests := []struct { + x string + y string + k string + rx string + ry string + }{ + // base mult, essentially. + { + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725", + "50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", + "2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6", + }, + // From btcd issue #709. + { + "000000000000000000000000000000000000000000000000000000000000002c", + "420e7a99bba18a9d3952597510fd2b6728cfeafc21a4e73951091d4d8ddbe94e", + "a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58", + "a2112dcdfbcd10ae1133a358de7b82db68e0a3eb4b492cc8268d1e7118c98788", + "27fc7463b7bb3c5f98ecf2c84a6272bb1681ed553d92c69f2dfe25a9f9fd3836", + }, + } + s256 := S256() + for i, test := range tests { + x, _ := new(big.Int).SetString(test.x, 16) + y, _ := new(big.Int).SetString(test.y, 16) + k, _ := new(big.Int).SetString(test.k, 16) + xWant, _ := new(big.Int).SetString(test.rx, 16) + yWant, _ := new(big.Int).SetString(test.ry, 16) + xGot, yGot := s256.ScalarMult(x, y, k.Bytes()) + if xGot.Cmp(xWant) != 0 || yGot.Cmp(yWant) != 0 { + t.Fatalf( + "%d: bad output: got (%X, %X), want (%X, %X)", i, xGot, + yGot, xWant, yWant, + ) + } + } +} + +func TestScalarMultRand(t *testing.T) { + // Strategy for this test: + // Get a random exponent from the generator point at first + // This creates a new point which is used in the next iteration + // Use another random exponent on the new point. + // We use BaseMult to verify by multiplying the previous exponent + // and the new random exponent together (mod no) + s256 := S256() + x, y := s256.Gx, s256.Gy + exponent := big.NewInt(1) + for i := 0; i < 1024; i++ { + data := make([]byte, 32) + _, err := rand.Read(data) + if err != nil { + t.Fatalf("failed to read random data at %d", i) + break + } + x, y = s256.ScalarMult(x, y, data) + exponent.Mul(exponent, new(big.Int).SetBytes(data)) + xWant, yWant := s256.ScalarBaseMult(exponent.Bytes()) + if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 { + t.Fatalf( + "%d: bad output for %X: got (%X, %X), want (%X, %X)", i, + data, x, y, xWant, yWant, + ) + break + } + } +} + +var ( + // Next 6 constants are from Hal Finney's bitcointalk.org post: + // https://bitcointalk.org/index.php?topic=3238.msg45565#msg45565 + // May he rest in peace. + // + // They have also been independently derived from the code in the + // EndomorphismVectors function in genstatics.go. + endomorphismLambda = fromHex("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72") + endomorphismBeta = hexToFieldVal("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee") + endomorphismA1 = fromHex("3086d221a7d46bcde86c90e49284eb15") + endomorphismB1 = fromHex("-e4437ed6010e88286f547fa90abfe4c3") + endomorphismA2 = fromHex("114ca50f7a8e2f3f657c1108d9d44cfd8") + endomorphismB2 = fromHex("3086d221a7d46bcde86c90e49284eb15") +) + +// splitK returns a balanced length-two representation of k and their signs. +// This is algorithm 3.74 from [GECC]. +// +// One thing of note about this algorithm is that no matter what c1 and c2 are, +// the final equation of k = k1 + k2 * lambda (mod n) will hold. This is +// provable mathematically due to how a1/b1/a2/b2 are computed. +// +// c1 and c2 are chosen to minimize the max(k1,k2). +func splitK(k []byte) ([]byte, []byte, int, int) { + // All math here is done with big.Int, which is slow. + // At some point, it might be useful to write something similar to + // FieldVal but for no instead of P as the prime field if this ends up + // being a bottleneck. + bigIntK := new(big.Int) + c1, c2 := new(big.Int), new(big.Int) + tmp1, tmp2 := new(big.Int), new(big.Int) + k1, k2 := new(big.Int), new(big.Int) + bigIntK.SetBytes(k) + // c1 = round(b2 * k / n) from step 4. + // Rounding isn't really necessary and costs too much, hence skipped + c1.Mul(endomorphismB2, bigIntK) + c1.Div(c1, Params().N) + // c2 = round(b1 * k / n) from step 4 (sign reversed to optimize one step) + // Rounding isn't really necessary and costs too much, hence skipped + c2.Mul(endomorphismB1, bigIntK) + c2.Div(c2, Params().N) + // k1 = k - c1 * a1 - c2 * a2 from step 5 (note c2's sign is reversed) + tmp1.Mul(c1, endomorphismA1) + tmp2.Mul(c2, endomorphismA2) + k1.Sub(bigIntK, tmp1) + k1.Add(k1, tmp2) + // k2 = - c1 * b1 - c2 * b2 from step 5 (note c2's sign is reversed) + tmp1.Mul(c1, endomorphismB1) + tmp2.Mul(c2, endomorphismB2) + k2.Sub(tmp2, tmp1) + // Note Bytes() throws out the sign of k1 and k2. This matters + // since k1 and/or k2 can be negative. Hence, we pass that + // back separately. + return k1.Bytes(), k2.Bytes(), k1.Sign(), k2.Sign() +} + +func TestSplitK(t *testing.T) { + tests := []struct { + k string + k1, k2 string + s1, s2 int + }{ + { + "6df2b5d30854069ccdec40ae022f5c948936324a4e9ebed8eb82cfd5a6b6d766", + "00000000000000000000000000000000b776e53fb55f6b006a270d42d64ec2b1", + "00000000000000000000000000000000d6cc32c857f1174b604eefc544f0c7f7", + -1, -1, + }, + { + "6ca00a8f10632170accc1b3baf2a118fa5725f41473f8959f34b8f860c47d88d", + "0000000000000000000000000000000007b21976c1795723c1bfbfa511e95b84", + "00000000000000000000000000000000d8d2d5f9d20fc64fd2cf9bda09a5bf90", + 1, -1, + }, + { + "b2eda8ab31b259032d39cbc2a234af17fcee89c863a8917b2740b67568166289", + "00000000000000000000000000000000507d930fecda7414fc4a523b95ef3c8c", + "00000000000000000000000000000000f65ffb179df189675338c6185cb839be", + -1, -1, + }, + { + "f6f00e44f179936f2befc7442721b0633f6bafdf7161c167ffc6f7751980e3a0", + "0000000000000000000000000000000008d0264f10bcdcd97da3faa38f85308d", + "0000000000000000000000000000000065fed1506eb6605a899a54e155665f79", + -1, -1, + }, + { + "8679085ab081dc92cdd23091ce3ee998f6b320e419c3475fae6b5b7d3081996e", + "0000000000000000000000000000000089fbf24fbaa5c3c137b4f1cedc51d975", + "00000000000000000000000000000000d38aa615bd6754d6f4d51ccdaf529fea", + -1, -1, + }, + { + "6b1247bb7931dfcae5b5603c8b5ae22ce94d670138c51872225beae6bba8cdb3", + "000000000000000000000000000000008acc2a521b21b17cfb002c83be62f55d", + "0000000000000000000000000000000035f0eff4d7430950ecb2d94193dedc79", + -1, -1, + }, + { + "a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58", + "0000000000000000000000000000000045c53aa1bb56fcd68c011e2dad6758e4", + "00000000000000000000000000000000a2e79d200f27f2360fba57619936159b", + -1, -1, + }, + } + s256 := S256() + for i, test := range tests { + k, ok := new(big.Int).SetString(test.k, 16) + if !ok { + t.Errorf("%d: bad value for k: %s", i, test.k) + } + k1, k2, k1Sign, k2Sign := splitK(k.Bytes()) + k1str := fmt.Sprintf("%064x", k1) + if test.k1 != k1str { + t.Errorf("%d: bad k1: got %v, want %v", i, k1str, test.k1) + } + k2str := fmt.Sprintf("%064x", k2) + if test.k2 != k2str { + t.Errorf("%d: bad k2: got %v, want %v", i, k2str, test.k2) + } + if test.s1 != k1Sign { + t.Errorf("%d: bad k1 sign: got %d, want %d", i, k1Sign, test.s1) + } + if test.s2 != k2Sign { + t.Errorf("%d: bad k2 sign: got %d, want %d", i, k2Sign, test.s2) + } + k1Int := new(big.Int).SetBytes(k1) + k1SignInt := new(big.Int).SetInt64(int64(k1Sign)) + k1Int.Mul(k1Int, k1SignInt) + k2Int := new(big.Int).SetBytes(k2) + k2SignInt := new(big.Int).SetInt64(int64(k2Sign)) + k2Int.Mul(k2Int, k2SignInt) + gotK := new(big.Int).Mul(k2Int, endomorphismLambda) + gotK.Add(k1Int, gotK) + gotK.Mod(gotK, s256.N) + if k.Cmp(gotK) != 0 { + t.Errorf("%d: bad k: got %X, want %X", i, gotK.Bytes(), k.Bytes()) + } + } +} + +func TestSplitKRand(t *testing.T) { + s256 := S256() + for i := 0; i < 1024; i++ { + bytesK := make([]byte, 32) + _, err := rand.Read(bytesK) + if err != nil { + t.Fatalf("failed to read random data at %d", i) + break + } + k := new(big.Int).SetBytes(bytesK) + k1, k2, k1Sign, k2Sign := splitK(bytesK) + k1Int := new(big.Int).SetBytes(k1) + k1SignInt := new(big.Int).SetInt64(int64(k1Sign)) + k1Int.Mul(k1Int, k1SignInt) + k2Int := new(big.Int).SetBytes(k2) + k2SignInt := new(big.Int).SetInt64(int64(k2Sign)) + k2Int.Mul(k2Int, k2SignInt) + gotK := new(big.Int).Mul(k2Int, endomorphismLambda) + gotK.Add(k1Int, gotK) + gotK.Mod(gotK, s256.N) + if k.Cmp(gotK) != 0 { + t.Errorf("%d: bad k: got %X, want %X", i, gotK.Bytes(), k.Bytes()) + } + } +} + +// Test this curve's usage with the ecdsa package. + +func testKeyGeneration(t *testing.T, c *KoblitzCurve, tag string) { + priv, err := NewSecretKey() + if err != nil { + t.Errorf("%s: error: %s", tag, err) + return + } + pub := priv.PubKey() + if !c.IsOnCurve(pub.X(), pub.Y()) { + t.Errorf("%s: public key invalid: %s", tag, err) + } +} + +func TestKeyGeneration(t *testing.T) { + testKeyGeneration(t, S256(), "S256") +} + +// checkNAFEncoding returns an error if the provided positive and negative +// portions of an overall NAF encoding do not adhere to the requirements or they +// do not sum back to the provided original value. +func checkNAFEncoding(pos, neg []byte, origValue *big.Int) error { + // NAF must not have a leading zero byte and the number of negative + // bytes must not exceed the positive portion. + if len(pos) > 0 && pos[0] == 0 { + return fmt.Errorf("positive has leading zero -- got %x", pos) + } + if len(neg) > len(pos) { + return fmt.Errorf( + "negative has len %d > pos len %d", len(neg), + len(pos), + ) + } + // Ensure the result doesn't have any adjacent non-zero digits. + gotPos := new(big.Int).SetBytes(pos) + gotNeg := new(big.Int).SetBytes(neg) + posOrNeg := new(big.Int).Or(gotPos, gotNeg) + prevBit := posOrNeg.Bit(0) + for bit := 1; bit < posOrNeg.BitLen(); bit++ { + thisBit := posOrNeg.Bit(bit) + if prevBit == 1 && thisBit == 1 { + return fmt.Errorf( + "adjacent non-zero digits found at bit pos %d", + bit-1, + ) + } + prevBit = thisBit + } + // Ensure the resulting positive and negative portions of the overall + // NAF representation sum back to the original value. + gotValue := new(big.Int).Sub(gotPos, gotNeg) + if origValue.Cmp(gotValue) != 0 { + return fmt.Errorf( + "pos-neg is not original value: got %x, want %x", + gotValue, origValue, + ) + } + return nil +} diff --git a/pkg/crypto/ec/chaincfg/deployment_time_frame.go b/pkg/crypto/ec/chaincfg/deployment_time_frame.go new file mode 100644 index 0000000..d9adc02 --- /dev/null +++ b/pkg/crypto/ec/chaincfg/deployment_time_frame.go @@ -0,0 +1,153 @@ +package chaincfg + +import ( + "fmt" + "time" + + "next.orly.dev/pkg/crypto/ec/wire" +) + +var ( + // ErrNoBlockClock is returned when an operation fails due to lack of + // synchornization with the current up to date block clock. + ErrNoBlockClock = fmt.Errorf("no block clock synchronized") +) + +// ConsensusDeploymentStarter determines if a given consensus deployment has +// started. A deployment has started once according to the current "time", the +// deployment is eligible for activation once a perquisite condition has +// passed. +type ConsensusDeploymentStarter interface { + // HasStarted returns true if the consensus deployment has started. + HasStarted(*wire.BlockHeader) (bool, error) +} + +// ConsensusDeploymentEnder determines if a given consensus deployment has +// ended. A deployment has ended once according got eh current "time", the +// deployment is no longer eligible for activation. +type ConsensusDeploymentEnder interface { + // HasEnded returns true if the consensus deployment has ended. + HasEnded(*wire.BlockHeader) (bool, error) +} + +// BlockClock is an abstraction over the past median time computation. The past +// median time computation is used in several consensus checks such as CSV, and +// also BIP 9 version bits. This interface allows callers to abstract away the +// computation of the past median time from the perspective of a given block +// header. +type BlockClock interface { + // PastMedianTime returns the past median time from the PoV of the + // passed block header. The past median time is the median time of the + // 11 blocks prior to the passed block header. + PastMedianTime(*wire.BlockHeader) (time.Time, error) +} + +// ClockConsensusDeploymentEnder is a more specialized version of the +// ConsensusDeploymentEnder that uses a BlockClock in order to determine if a +// deployment has started or not. +// +// NOTE: Any calls to HasEnded will _fail_ with ErrNoBlockClock if they +// happen before SynchronizeClock is executed. +type ClockConsensusDeploymentEnder interface { + ConsensusDeploymentEnder + // SynchronizeClock synchronizes the target ConsensusDeploymentStarter + // with the current up-to date BlockClock. + SynchronizeClock(clock BlockClock) +} + +// MedianTimeDeploymentStarter is a ClockConsensusDeploymentStarter that uses +// the median time past of a target block node to determine if a deployment has +// started. +type MedianTimeDeploymentStarter struct { + blockClock BlockClock + startTime time.Time +} + +// NewMedianTimeDeploymentStarter returns a new instance of a +// MedianTimeDeploymentStarter for a given start time. Using a time.Time +// instance where IsZero() is true, indicates that a deployment should be +// considered to always have been started. +func NewMedianTimeDeploymentStarter(startTime time.Time) *MedianTimeDeploymentStarter { + return &MedianTimeDeploymentStarter{ + startTime: startTime, + } +} + +// HasStarted returns true if the consensus deployment has started. +func (m *MedianTimeDeploymentStarter) HasStarted(blkHeader *wire.BlockHeader) ( + bool, + error, +) { + switch { + // If we haven't yet been synchronized with a block clock, then we + // can't tell the time, so we'll fail. + case m.blockClock == nil: + return false, ErrNoBlockClock + // If the time is "zero", then the deployment has always started. + case m.startTime.IsZero(): + return true, nil + } + medianTime, err := m.blockClock.PastMedianTime(blkHeader) + if err != nil { + return false, err + } + // We check both after and equal here as after will fail for equivalent + // times, and we want to be inclusive. + return medianTime.After(m.startTime) || medianTime.Equal(m.startTime), nil +} + +// MedianTimeDeploymentEnder is a ClockConsensusDeploymentEnder that uses the +// median time past of a target block to determine if a deployment has ended. +type MedianTimeDeploymentEnder struct { + blockClock BlockClock + endTime time.Time +} + +// NewMedianTimeDeploymentEnder returns a new instance of the +// MedianTimeDeploymentEnder anchored around the passed endTime. Using a +// time.Time instance where IsZero() is true, indicates that a deployment +// should be considered to never end. +func NewMedianTimeDeploymentEnder(endTime time.Time) *MedianTimeDeploymentEnder { + return &MedianTimeDeploymentEnder{ + endTime: endTime, + } +} + +// HasEnded returns true if the deployment has ended. +func (m *MedianTimeDeploymentEnder) HasEnded(blkHeader *wire.BlockHeader) ( + bool, + error, +) { + switch { + // If we haven't yet been synchronized with a block clock, then we can't tell + // the time, so we'll we haven't yet been synchronized with a block + // clock, then w can't tell the time, so we'll fail. + case m.blockClock == nil: + return false, ErrNoBlockClock + // If the time is "zero", then the deployment never ends. + case m.endTime.IsZero(): + return false, nil + } + medianTime, err := m.blockClock.PastMedianTime(blkHeader) + if err != nil { + return false, err + } + // We check both after and equal here as after will fail for equivalent + // times, and we want to be inclusive. + return medianTime.After(m.endTime) || medianTime.Equal(m.endTime), nil +} + +// EndTime returns the raw end time of the deployment. +func (m *MedianTimeDeploymentEnder) EndTime() time.Time { + return m.endTime +} + +// SynchronizeClock synchronizes the target ConsensusDeploymentEnder with the +// current up-to date BlockClock. +func (m *MedianTimeDeploymentEnder) SynchronizeClock(clock BlockClock) { + m.blockClock = clock +} + +// A compile-time assertion to ensure MedianTimeDeploymentEnder implements the +// ClockConsensusDeploymentStarter interface. +var _ ClockConsensusDeploymentEnder = (*MedianTimeDeploymentEnder)(nil) diff --git a/pkg/crypto/ec/chaincfg/genesis.go b/pkg/crypto/ec/chaincfg/genesis.go new file mode 100644 index 0000000..c53e7cc --- /dev/null +++ b/pkg/crypto/ec/chaincfg/genesis.go @@ -0,0 +1,110 @@ +package chaincfg + +import ( + "time" + + "next.orly.dev/pkg/crypto/ec/chainhash" + "next.orly.dev/pkg/crypto/ec/wire" +) + +var ( + // genesisCoinbaseTx is the coinbase transaction for the genesis blocks for + // the main network, regression test network, and test network (version 3). + genesisCoinbaseTx = wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: chainhash.Hash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, + 0x45, /* |.......E| */ + 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, + 0x65, /* |The Time| */ + 0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, + 0x6e, /* |s 03/Jan| */ + 0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, + 0x68, /* |/2009 Ch| */ + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, + 0x72, /* |ancellor| */ + 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, + 0x6e, /* | on brin| */ + 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, + 0x63, /* |k of sec|*/ + 0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, + 0x6c, /* |ond bail| */ + 0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x20, /* |out for |*/ + 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |banks| */ + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0x12a05f200, + PkScript: []byte{ + 0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, + 0x55, /* |A.g....U| */ + 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, + 0x30, /* |H'.g..q0| */ + 0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, + 0x39, /* |..\..(.9| */ + 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, + 0x61, /* |..yb...a| */ + 0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, + 0xef, /* |..I..?L.| */ + 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, + 0xc1, /* |8..U....| */ + 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, + 0x0b, /* |..\8M...| */ + 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, + 0xf1, /* |.W.Lp+k.| */ + 0x1d, 0x5f, 0xac, /* |._.| */ + }, + }, + }, + LockTime: 0, + } + // genesisHash is the hash of the first block in the block chain for the main + // network (genesis block). + genesisHash = chainhash.Hash( + [chainhash.HashSize]byte{ + // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + ) + // genesisMerkleRoot is the hash of the first transaction in the genesis block + // for the main network. + genesisMerkleRoot = chainhash.Hash( + [chainhash.HashSize]byte{ + // Make go vet happy. + 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, + 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, + 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, + 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, + }, + ) + // genesisBlock defines + // genesisBlock defines the genesis block of the block chain which serves as the + // public transaction ledger for the main network. + genesisBlock = wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000 + MerkleRoot: genesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + Timestamp: time.Unix( + 0x495fab29, + 0, + ), // 2009-01-03 18:15:05 +0000 UTC + Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000] + Nonce: 0x7c2bac1d, // 2083236893 + }, + Transactions: []*wire.MsgTx{&genesisCoinbaseTx}, + } +) diff --git a/pkg/crypto/ec/chaincfg/params.go b/pkg/crypto/ec/chaincfg/params.go new file mode 100644 index 0000000..470541b --- /dev/null +++ b/pkg/crypto/ec/chaincfg/params.go @@ -0,0 +1,493 @@ +// Package chaincfg provides basic parameters for bitcoin chain and testnets. +package chaincfg + +import ( + "math/big" + "time" + + "next.orly.dev/pkg/crypto/ec/chainhash" + "next.orly.dev/pkg/crypto/ec/wire" +) + +var ( + // bigOne is 1 represented as a big.Int. It is defined here to avoid + // the overhead of creating it multiple times. + bigOne = big.NewInt(1) + + // mainPowLimit is the highest proof of work value a Bitcoin block can + // have for the main network. It is the value 2^224 - 1. + mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne) +) + +// Constants that define the deployment offset in the deployments field of the +// parameters for each deployment. This is useful to be able to get the details +// of a specific deployment by name. +const ( + // DeploymentTestDummy defines the rule change deployment ID for testing + // purposes. + DeploymentTestDummy = iota + + // DeploymentTestDummyMinActivation defines the rule change deployment + // ID for testing purposes. This differs from the DeploymentTestDummy + // in that it specifies the newer params the taproot fork used for + // activation: a custom threshold and a min activation height. + DeploymentTestDummyMinActivation + + // DeploymentCSV defines the rule change deployment ID for the CSV + // soft-fork package. The CSV package includes the deployment of BIPS + // 68, 112, and 113. + DeploymentCSV + + // DeploymentSegwit defines the rule change deployment ID for the + // Segregated Witness (segwit) soft-fork package. The segwit package + // includes the deployment of BIPS 141, 142, 144, 145, 147 and 173. + DeploymentSegwit + + // DeploymentTaproot defines the rule change deployment ID for the + // Taproot (+Schnorr) soft-fork package. The taproot package includes + // the deployment of BIPS 340, 341 and 342. + DeploymentTaproot + + // NOTE: DefinedDeployments must always come last since it is used to + // determine how many defined deployments there currently are. + + // DefinedDeployments is the number of currently defined deployments. + DefinedDeployments +) + +// ConsensusDeployment defines details related to a specific consensus rule +// change that is voted in. This is part of BIP0009. +type ConsensusDeployment struct { + // BitNumber defines the specific bit number within the block version + // this particular soft-fork deployment refers to. + BitNumber uint8 + + // MinActivationHeight is an optional field that when set (default + // value being zero), modifies the traditional BIP 9 state machine by + // only transitioning from LockedIn to Active once the block height is + // greater than (or equal to) thus specified height. + MinActivationHeight uint32 + + // CustomActivationThreshold if set (non-zero), will _override_ the + // existing RuleChangeActivationThreshold value set at the + // network/chain level. This value divided by the active + // MinerConfirmationWindow denotes the threshold required for + // activation. A value of 1815 block denotes a 90% threshold. + CustomActivationThreshold uint32 + + // DeploymentStarter is used to determine if the given + // ConsensusDeployment has started or not. + DeploymentStarter ConsensusDeploymentStarter + + // DeploymentEnder is used to determine if the given + // ConsensusDeployment has ended or not. + DeploymentEnder ConsensusDeploymentEnder +} + +// Checkpoint identifies a known good point in the block chain. Using +// checkpoints allows a few optimizations for old blocks during initial download +// and also prevents forks from old blocks. +// +// Each checkpoint is selected based upon several factors. See the +// documentation for blockchain.IsCheckpointCandidate for details on the +// selection criteria. +type Checkpoint struct { + Height int32 + Hash *chainhash.Hash +} + +// DNSSeed identifies a DNS seed. +type DNSSeed struct { + // Host defines the hostname of the seed. + Host string + + // HasFiltering defines whether the seed supports filtering + // by service flags (wire.ServiceFlag). + HasFiltering bool +} + +// Params defines a Bitcoin network by its parameters. These parameters may be +// used by Bitcoin applications to differentiate networks as well as addresses +// and keys for one network from those intended for use on another network. +type Params struct { + // Name defines a human-readable identifier for the network. + Name string + + // Net defines the magic bytes used to identify the network. + Net wire.BitcoinNet + + // DefaultPort defines the default peer-to-peer port for the network. + DefaultPort string + + // DNSSeeds defines a list of DNS seeds for the network that are used + // as one method to discover peers. + DNSSeeds []DNSSeed + + // GenesisBlock defines the first block of the chain. + GenesisBlock *wire.MsgBlock + + // GenesisHash is the starting block hash. + GenesisHash *chainhash.Hash + + // PowLimit defines the highest allowed proof of work value for a block + // as a uint256. + PowLimit *big.Int + + // PowLimitBits defines the highest allowed proof of work value for a + // block in compact form. + PowLimitBits uint32 + + // PoWNoRetargeting defines whether the network has difficulty + // retargeting enabled or not. This should only be set to true for + // regtest like networks. + PoWNoRetargeting bool + + // These fields define the block heights at which the specified softfork + // BIP became active. + BIP0034Height int32 + BIP0065Height int32 + BIP0066Height int32 + + // CoinbaseMaturity is the number of blocks required before newly mined + // coins (coinbase transactions) can be spent. + CoinbaseMaturity uint16 + + // SubsidyReductionInterval is the interval of blocks before the subsidy + // is reduced. + SubsidyReductionInterval int32 + + // TargetTimespan is the desired amount of time that should elapse + // before the block difficulty requirement is examined to determine how + // it should be changed in order to maintain the desired block + // generation rate. + TargetTimespan time.Duration + + // TargetTimePerBlock is the desired amount of time to generate each + // block. + TargetTimePerBlock time.Duration + + // RetargetAdjustmentFactor is the adjustment factor used to limit + // the minimum and maximum amount of adjustment that can occur between + // difficulty retargets. + RetargetAdjustmentFactor int64 + + // ReduceMinDifficulty defines whether the network should reduce the + // minimum required difficulty after a long enough period of time has + // passed without finding a block. This is really only useful for test + // networks and should not be set on a main network. + ReduceMinDifficulty bool + + // MinDiffReductionTime is the amount of time after which the minimum + // required difficulty should be reduced when a block hasn't been found. + // + // NOTE: This only applies if ReduceMinDifficulty is true. + MinDiffReductionTime time.Duration + + // GenerateSupported specifies whether or not CPU mining is allowed. + GenerateSupported bool + + // Checkpoints ordered from oldest to newest. + Checkpoints []Checkpoint + + // These fields are related to voting on consensus rule changes as + // defined by BIP0009. + // + // RuleChangeActivationThreshold is the number of blocks in a threshold + // state retarget window for which a positive vote for a rule change + // must be cast in order to lock in a rule change. It should typically + // be 95% for the main network and 75% for test networks. + // + // MinerConfirmationWindow is the number of blocks in each threshold + // state retarget window. + // + // Deployments define the specific consensus rule changes to be voted + // on. + RuleChangeActivationThreshold uint32 + MinerConfirmationWindow uint32 + Deployments [DefinedDeployments]ConsensusDeployment + + // Mempool parameters + RelayNonStdTxs bool + + // Human-readable part for Bech32 encoded segwit addresses, as defined + // in BIP 173. + Bech32HRPSegwit []byte + + // Address encoding magics + PubKeyHashAddrID byte // First byte of a P2PKH address + ScriptHashAddrID byte // First byte of a P2SH address + PrivateKeyID byte // First byte of a WIF private key + WitnessPubKeyHashAddrID byte // First byte of a P2WPKH address + WitnessScriptHashAddrID byte // First byte of a P2WSH address + + // BIP32 hierarchical deterministic extended key magics + HDPrivateKeyID [4]byte + HDPublicKeyID [4]byte + + // BIP44 coin type used in the hierarchical deterministic path for + // address generation. + HDCoinType uint32 +} + +// MainNetParams defines the network parameters for the main Bitcoin network. +var MainNetParams = Params{ + Name: "mainnet", + Net: wire.MainNet, + DefaultPort: "8333", + DNSSeeds: []DNSSeed{ + {"seed.bitcoin.sipa.be", true}, + {"dnsseed.bluematt.me", true}, + {"dnsseed.bitcoin.dashjr.org", false}, + {"seed.bitcoinstats.com", true}, + {"seed.bitnodes.io", false}, + {"seed.bitcoin.jonasschnelli.ch", true}, + }, + + // Chain parameters + GenesisBlock: &genesisBlock, + GenesisHash: &genesisHash, + PowLimit: mainPowLimit, + PowLimitBits: 0x1d00ffff, + BIP0034Height: 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8 + BIP0065Height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 + BIP0066Height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 + CoinbaseMaturity: 100, + SubsidyReductionInterval: 210000, + TargetTimespan: time.Hour * 24 * 14, // 14 days + TargetTimePerBlock: time.Minute * 10, // 10 minutes + RetargetAdjustmentFactor: 4, // 25% less, 400% more + ReduceMinDifficulty: false, + MinDiffReductionTime: 0, + GenerateSupported: false, + + // Checkpoints ordered from oldest to newest. + Checkpoints: []Checkpoint{ + { + 11111, + newHashFromStr("0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"), + }, + { + 33333, + newHashFromStr("000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"), + }, + { + 74000, + newHashFromStr("0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"), + }, + { + 105000, + newHashFromStr("00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"), + }, + { + 134444, + newHashFromStr("00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"), + }, + { + 168000, + newHashFromStr("000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"), + }, + { + 193000, + newHashFromStr("000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"), + }, + { + 210000, + newHashFromStr("000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"), + }, + { + 216116, + newHashFromStr("00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"), + }, + { + 225430, + newHashFromStr("00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"), + }, + { + 250000, + newHashFromStr("000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"), + }, + { + 267300, + newHashFromStr("000000000000000a83fbd660e918f218bf37edd92b748ad940483c7c116179ac"), + }, + { + 279000, + newHashFromStr("0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"), + }, + { + 300255, + newHashFromStr("0000000000000000162804527c6e9b9f0563a280525f9d08c12041def0a0f3b2"), + }, + { + 319400, + newHashFromStr("000000000000000021c6052e9becade189495d1c539aa37c58917305fd15f13b"), + }, + { + 343185, + newHashFromStr("0000000000000000072b8bf361d01a6ba7d445dd024203fafc78768ed4368554"), + }, + { + 352940, + newHashFromStr("000000000000000010755df42dba556bb72be6a32f3ce0b6941ce4430152c9ff"), + }, + { + 382320, + newHashFromStr("00000000000000000a8dc6ed5b133d0eb2fd6af56203e4159789b092defd8ab2"), + }, + { + 400000, + newHashFromStr("000000000000000004ec466ce4732fe6f1ed1cddc2ed4b328fff5224276e3f6f"), + }, + { + 430000, + newHashFromStr("000000000000000001868b2bb3a285f3cc6b33ea234eb70facf4dcdf22186b87"), + }, + { + 460000, + newHashFromStr("000000000000000000ef751bbce8e744ad303c47ece06c8d863e4d417efc258c"), + }, + { + 490000, + newHashFromStr("000000000000000000de069137b17b8d5a3dfbd5b145b2dcfb203f15d0c4de90"), + }, + { + 520000, + newHashFromStr("0000000000000000000d26984c0229c9f6962dc74db0a6d525f2f1640396f69c"), + }, + { + 550000, + newHashFromStr("000000000000000000223b7a2298fb1c6c75fb0efc28a4c56853ff4112ec6bc9"), + }, + { + 560000, + newHashFromStr("0000000000000000002c7b276daf6efb2b6aa68e2ce3be67ef925b3264ae7122"), + }, + { + 563378, + newHashFromStr("0000000000000000000f1c54590ee18d15ec70e68c8cd4cfbadb1b4f11697eee"), + }, + { + 597379, + newHashFromStr("00000000000000000005f8920febd3925f8272a6a71237563d78c2edfdd09ddf"), + }, + { + 623950, + newHashFromStr("0000000000000000000f2adce67e49b0b6bdeb9de8b7c3d7e93b21e7fc1e819d"), + }, + { + 654683, + newHashFromStr("0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72"), + }, + { + 691719, + newHashFromStr("00000000000000000008a89e854d57e5667df88f1cdef6fde2fbca1de5b639ad"), + }, + { + 724466, + newHashFromStr("000000000000000000052d314a259755ca65944e68df6b12a067ea8f1f5a7091"), + }, + { + 751565, + newHashFromStr("00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd"), + }, + }, + + // Consensus rule change deployments. + // + // The miner confirmation window is defined as: + // target proof of work timespan / target proof of work spacing + RuleChangeActivationThreshold: 1916, // 95% of MinerConfirmationWindow + MinerConfirmationWindow: 2016, // + Deployments: [DefinedDeployments]ConsensusDeployment{ + DeploymentTestDummy: { + BitNumber: 28, + DeploymentStarter: NewMedianTimeDeploymentStarter( + time.Unix(11991456010, 0), // January 1, 2008 UTC + ), + DeploymentEnder: NewMedianTimeDeploymentEnder( + time.Unix(1230767999, 0), // December 31, 2008 UTC + ), + }, + DeploymentTestDummyMinActivation: { + BitNumber: 22, + CustomActivationThreshold: 1815, // Only needs 90% hash rate. + MinActivationHeight: 10_0000, // Can only activate after height 10k. + DeploymentStarter: NewMedianTimeDeploymentStarter( + time.Time{}, // Always available for vote + ), + DeploymentEnder: NewMedianTimeDeploymentEnder( + time.Time{}, // Never expires + ), + }, + DeploymentCSV: { + BitNumber: 0, + DeploymentStarter: NewMedianTimeDeploymentStarter( + time.Unix(1462060800, 0), // May 1st, 2016 + ), + DeploymentEnder: NewMedianTimeDeploymentEnder( + time.Unix(1493596800, 0), // May 1st, 2017 + ), + }, + DeploymentSegwit: { + BitNumber: 1, + DeploymentStarter: NewMedianTimeDeploymentStarter( + time.Unix(1479168000, 0), // November 15, 2016 UTC + ), + DeploymentEnder: NewMedianTimeDeploymentEnder( + time.Unix(1510704000, 0), // November 15, 2017 UTC. + ), + }, + DeploymentTaproot: { + BitNumber: 2, + DeploymentStarter: NewMedianTimeDeploymentStarter( + time.Unix(1619222400, 0), // April 24th, 2021 UTC. + ), + DeploymentEnder: NewMedianTimeDeploymentEnder( + time.Unix(1628640000, 0), // August 11th, 2021 UTC. + ), + CustomActivationThreshold: 1815, // 90% + MinActivationHeight: 709_632, + }, + }, + + // Mempool parameters + RelayNonStdTxs: false, + + // Human-readable part for Bech32 encoded segwit addresses, as defined in + // BIP 173. + Bech32HRPSegwit: []byte("bc"), // always bc for main net + + // Address encoding magics + PubKeyHashAddrID: 0x00, // starts with 1 + ScriptHashAddrID: 0x05, // starts with 3 + PrivateKeyID: 0x80, // starts with 5 (uncompressed) or K (compressed) + WitnessPubKeyHashAddrID: 0x06, // starts with p2 + WitnessScriptHashAddrID: 0x0A, // starts with 7Xh + + // BIP32 hierarchical deterministic extended key magics + HDPrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv + HDPublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub + + // BIP44 coin type used in the hierarchical deterministic path for + // address generation. + HDCoinType: 0, +} + +// newHashFromStr converts the passed big-endian hex string into a +// chainhash.Hash. It only differs from the one available in chainhash in that +// it panics on an error since it will only (and must only) be called with +// hard-coded, and therefore known good, hashes. +func newHashFromStr(hexStr string) *chainhash.Hash { + hash, err := chainhash.NewHashFromStr(hexStr) + if err != nil { + // Ordinarily I don't like panics in library code since it + // can take applications down without them having a chance to + // recover which is extremely annoying, however an exception is + // being made in this case because the only way this can panic + // is if there is an error in the hard-coded hashes. Thus it + // will only ever potentially panic on init and therefore is + // 100% predictable. + panic(err) + } + return hash +} diff --git a/pkg/crypto/ec/chainhash/README.md b/pkg/crypto/ec/chainhash/README.md new file mode 100644 index 0000000..406f49c --- /dev/null +++ b/pkg/crypto/ec/chainhash/README.md @@ -0,0 +1,19 @@ +chainhash +========= + +[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +======= + +chainhash provides a generic hash type and associated functions that allows the +specific hash algorithm to be abstracted. + +## Installation and Updating + +```bash +$ go get -u mleku.online/git/ec/chainhash +``` + +## License + +Package chainhash is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/pkg/crypto/ec/chainhash/doc.go b/pkg/crypto/ec/chainhash/doc.go new file mode 100644 index 0000000..c3eb43d --- /dev/null +++ b/pkg/crypto/ec/chainhash/doc.go @@ -0,0 +1,5 @@ +// Package chainhash provides abstracted hash functionality. +// +// This package provides a generic hash type and associated functions that +// allows the specific hash algorithm to be abstracted. +package chainhash diff --git a/pkg/crypto/ec/chainhash/hash.go b/pkg/crypto/ec/chainhash/hash.go new file mode 100644 index 0000000..c24d180 --- /dev/null +++ b/pkg/crypto/ec/chainhash/hash.go @@ -0,0 +1,229 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2015 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chainhash + +import ( + "encoding/json" + "fmt" + + "next.orly.dev/pkg/crypto/sha256" + "next.orly.dev/pkg/encoders/hex" +) + +const ( + // HashSize of array used to store hashes. See Hash. + HashSize = 32 + // MaxHashStringSize is the maximum length of a Hash hash string. + MaxHashStringSize = HashSize * 2 +) + +var ( + // TagBIP0340Challenge is the BIP-0340 tag for challenges. + TagBIP0340Challenge = []byte("BIP0340/challenge") + // TagBIP0340Aux is the BIP-0340 tag for aux data. + TagBIP0340Aux = []byte("BIP0340/aux") + // TagBIP0340Nonce is the BIP-0340 tag for nonces. + TagBIP0340Nonce = []byte("BIP0340/nonce") + // TagTapSighash is the tag used by BIP 341 to generate the sighash + // flags. + TagTapSighash = []byte("TapSighash") + // TagTapLeaf is the message tag prefix used to compute the hash + // digest of a tapscript leaf. + TagTapLeaf = []byte("TapLeaf") + // TagTapBranch is the message tag prefix used to compute the + // hash digest of two tap leaves into a taproot branch node. + TagTapBranch = []byte("TapBranch") + // TagTapTweak is the message tag prefix used to compute the hash tweak + // used to enable a public key to commit to the taproot branch root + // for the witness program. + TagTapTweak = []byte("TapTweak") + // precomputedTags is a map containing the SHA-256 hash of the BIP-0340 + // tags. + precomputedTags = map[string]Hash{ + string(TagBIP0340Challenge): sha256.Sum256(TagBIP0340Challenge), + string(TagBIP0340Aux): sha256.Sum256(TagBIP0340Aux), + string(TagBIP0340Nonce): sha256.Sum256(TagBIP0340Nonce), + string(TagTapSighash): sha256.Sum256(TagTapSighash), + string(TagTapLeaf): sha256.Sum256(TagTapLeaf), + string(TagTapBranch): sha256.Sum256(TagTapBranch), + string(TagTapTweak): sha256.Sum256(TagTapTweak), + } +) + +// ErrHashStrSize describes an error that indicates the caller specified a hash +// string that has too many characters. +var ErrHashStrSize = fmt.Errorf( + "max hash string length is %v bytes", + MaxHashStringSize, +) + +// Hash is used in several of the bitcoin messages and common structures. It +// typically represents the double sha256 of data. +type Hash [HashSize]byte + +// String returns the Hash as the hexadecimal string of the byte-reversed +// hash. +func (hash Hash) String() string { + for i := 0; i < HashSize/2; i++ { + hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i] + } + return hex.Enc(hash[:]) +} + +// CloneBytes returns a copy of the bytes which represent the hash as a byte +// slice. +// +// NOTE: It is generally cheaper to just slice the hash directly thereby reusing +// the same bytes rather than calling this method. +func (hash *Hash) CloneBytes() []byte { + newHash := make([]byte, HashSize) + copy(newHash, hash[:]) + return newHash +} + +// SetBytes sets the bytes which represent the hash. An error is returned if +// the number of bytes passed in is not HashSize. +func (hash *Hash) SetBytes(newHash []byte) error { + nhlen := len(newHash) + if nhlen != HashSize { + return fmt.Errorf( + "invalid hash length of %v, want %v", nhlen, + HashSize, + ) + } + copy(hash[:], newHash) + return nil +} + +// IsEqual returns true if target is the same as hash. +func (hash *Hash) IsEqual(target *Hash) bool { + if hash == nil && target == nil { + return true + } + if hash == nil || target == nil { + return false + } + return *hash == *target +} + +// MarshalJSON serialises the hash as a JSON appropriate string value. +func (hash Hash) MarshalJSON() ([]byte, error) { + return json.Marshal(hash.String()) +} + +// UnmarshalJSON parses the hash with JSON appropriate string value. +func (hash *Hash) UnmarshalJSON(input []byte) error { + // If the first byte indicates an array, the hash could have been marshalled + // using the legacy method and e.g. persisted. + if len(input) > 0 && input[0] == '[' { + return decodeLegacy(hash, input) + } + var sh string + err := json.Unmarshal(input, &sh) + if err != nil { + return err + } + newHash, err := NewHashFromStr(sh) + if err != nil { + return err + } + return hash.SetBytes(newHash[:]) +} + +// NewHash returns a new Hash from a byte slice. An error is returned if +// the number of bytes passed in is not HashSize. +func NewHash(newHash []byte) (*Hash, error) { + var sh Hash + err := sh.SetBytes(newHash) + if err != nil { + return nil, err + } + return &sh, err +} + +// TaggedHash implements the tagged hash scheme described in BIP-340. We use +// sha-256 to bind a message hash to a specific context using a tag: +// sha256(sha256(tag) || sha256(tag) || msg). +func TaggedHash(tag []byte, msgs ...[]byte) *Hash { + // Check to see if we've already pre-computed the hash of the tag. If + // so then this'll save us an extra sha256 hash. + shaTag, ok := precomputedTags[string(tag)] + if !ok { + shaTag = sha256.Sum256(tag) + } + // h = sha256(sha256(tag) || sha256(tag) || msg) + h := sha256.New() + h.Write(shaTag[:]) + h.Write(shaTag[:]) + for _, msg := range msgs { + h.Write(msg) + } + taggedHash := h.Sum(nil) + // The function can't error out since the above hash is guaranteed to + // be 32 bytes. + hash, _ := NewHash(taggedHash) + return hash +} + +// NewHashFromStr creates a Hash from a hash string. The string should be +// the hexadecimal string of a byte-reversed hash, but any missing characters +// result in zero padding at the end of the Hash. +func NewHashFromStr(hash string) (*Hash, error) { + ret := new(Hash) + err := Decode(ret, hash) + if err != nil { + return nil, err + } + return ret, nil +} + +// Decode decodes the byte-reversed hexadecimal string encoding of a Hash to a +// destination. +func Decode(dst *Hash, src string) error { + // Return error if hash string is too long. + if len(src) > MaxHashStringSize { + return ErrHashStrSize + } + // Hex decoder expects the hash to be a multiple of two. When not, pad + // with a leading zero. + var srcBytes []byte + if len(src)%2 == 0 { + srcBytes = []byte(src) + } else { + srcBytes = make([]byte, 1+len(src)) + srcBytes[0] = '0' + copy(srcBytes[1:], src) + } + // Hex decode the source bytes to a temporary destination. + var reversedHash Hash + _, err := hex.DecAppend( + reversedHash[HashSize-hex.DecLen(len(srcBytes)):], + srcBytes, + ) + if err != nil { + return err + } + // Reverse copy from the temporary hash to destination. Because the + // temporary was zeroed, the written result will be correctly padded. + for i, b := range reversedHash[:HashSize/2] { + dst[i], dst[HashSize-1-i] = reversedHash[HashSize-1-i], b + } + return nil +} + +// decodeLegacy decodes an Hash that has been encoded with the legacy method +// (i.e. represented as a bytes array) to a destination. +func decodeLegacy(dst *Hash, src []byte) error { + var hashBytes []byte + err := json.Unmarshal(src, &hashBytes) + if err != nil { + return err + } + if len(hashBytes) != HashSize { + return ErrHashStrSize + } + return dst.SetBytes(hashBytes) +} diff --git a/pkg/crypto/ec/chainhash/hash_test.go b/pkg/crypto/ec/chainhash/hash_test.go new file mode 100644 index 0000000..7692d8b --- /dev/null +++ b/pkg/crypto/ec/chainhash/hash_test.go @@ -0,0 +1,228 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chainhash + +import ( + "testing" + + "next.orly.dev/pkg/utils" +) + +// mainNetGenesisHash is the hash of the first block in the block chain for the +// main network (genesis block). +var mainNetGenesisHash = Hash( + [HashSize]byte{ + // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + }, +) + +// TestHash tests the Hash API. +func TestHash(t *testing.T) { + // Hash of block 234439. + blockHashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef" + blockHash, err := NewHashFromStr(blockHashStr) + if err != nil { + t.Errorf("NewHashFromStr: %v", err) + } + // Hash of block 234440 as byte slice. + buf := []byte{ + 0x79, 0xa6, 0x1a, 0xdb, 0xc6, 0xe5, 0xa2, 0xe1, + 0x39, 0xd2, 0x71, 0x3a, 0x54, 0x6e, 0xc7, 0xc8, + 0x75, 0x63, 0x2e, 0x75, 0xf1, 0xdf, 0x9c, 0x3f, + 0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + hash, err := NewHash(buf) + if err != nil { + t.Errorf("NewHash: unexpected error %v", err) + } + // Ensure proper size. + if len(hash) != HashSize { + t.Errorf( + "NewHash: hash length mismatch - got: %v, want: %v", + len(hash), HashSize, + ) + } + // Ensure contents match. + if !utils.FastEqual(hash[:], buf) { + t.Errorf( + "NewHash: hash contents mismatch - got: %v, want: %v", + hash[:], buf, + ) + } + // Ensure contents of hash of block 234440 don't match 234439. + if hash.IsEqual(blockHash) { + t.Errorf( + "IsEqual: hash contents should not match - got: %v, want: %v", + hash, blockHash, + ) + } + // Set hash from byte slice and ensure contents match. + err = hash.SetBytes(blockHash.CloneBytes()) + if err != nil { + t.Errorf("SetBytes: %v", err) + } + if !hash.IsEqual(blockHash) { + t.Errorf( + "IsEqual: hash contents mismatch - got: %v, want: %v", + hash, blockHash, + ) + } + // Ensure nil hashes are handled properly. + if !(*Hash)(nil).IsEqual(nil) { + t.Error("IsEqual: nil hashes should match") + } + if hash.IsEqual(nil) { + t.Error("IsEqual: non-nil hash matches nil hash") + } + // Invalid size for SetBytes. + err = hash.SetBytes([]byte{0x00}) + if err == nil { + t.Errorf("SetBytes: failed to received expected err - got: nil") + } + // Invalid size for NewHash. + invalidHash := make([]byte, HashSize+1) + _, err = NewHash(invalidHash) + if err == nil { + t.Errorf("NewHash: failed to received expected err - got: nil") + } +} + +// TestHashString tests the stringized output for hashes. +func TestHashString(t *testing.T) { + // Block 100000 hash. + wantStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hash := Hash( + [HashSize]byte{ + // Make go vet happy. + 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, + 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, + 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, + 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + ) + hashStr := hash.String() + if hashStr != wantStr { + t.Errorf( + "String: wrong hash string - got %v, want %v", + hashStr, wantStr, + ) + } +} + +// todo: these fail for some reason +// // TestNewHashFromStr executes tests against the NewHashFromStr function. +// func TestNewHashFromStr(t *testing.T) { +// tests := []struct { +// in string +// want Hash +// err error +// }{ +// // Genesis hash. +// { +// "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", +// mainNetGenesisHash, +// nil, +// }, +// // Genesis hash with stripped leading zeros. +// { +// "19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", +// mainNetGenesisHash, +// nil, +// }, +// // Empty string. +// { +// "", +// Hash{}, +// nil, +// }, +// // Single digit hash. +// { +// "1", +// Hash([HashSize]byte{ // Make go vet happy. +// 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// }), +// nil, +// }, +// // Block 203707 with stripped leading zeros. +// { +// "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc", +// Hash([HashSize]byte{ // Make go vet happy. +// 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, +// 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, +// 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, +// 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// }), +// nil, +// }, +// // Hash string that is too long. +// { +// "01234567890123456789012345678901234567890123456789012345678912345", +// Hash{}, +// ErrHashStrSize, +// }, +// // Hash string that is contains non-hex chars. +// { +// "abcdefg", +// Hash{}, +// hex.InvalidByteError('g'), +// }, +// } +// +// unexpectedErrStr := "NewHashFromStr #%d failed to detect expected error - got: %v want: %v" +// unexpectedResultStr := "NewHashFromStr #%d got: %v want: %v" +// t.Logf("Running %d tests", len(tests)) +// for i, test := range tests { +// result, err := NewHashFromStr(test.in) +// if err != test.err { +// t.Errorf(unexpectedErrStr, i, err, test.err) +// continue +// } else if err != nil { +// // Got expected error. Move on to the next test. +// continue +// } +// if !test.want.IsEqual(result) { +// t.Errorf(unexpectedResultStr, i, result, &test.want) +// continue +// } +// } +// } +// +// // TestHashJsonMarshal tests json marshal and unmarshal. +// func TestHashJsonMarshal(t *testing.T) { +// hashStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" +// legacyHashStr := []byte("[6,229,51,253,26,218,134,57,31,63,108,52,50,4,176,210,120,212,170,236,28,11,32,170,39,186,3,0,0,0,0,0]") +// hash, err := NewHashFromStr(hashStr) +// if err != nil { +// t.Errorf("NewHashFromStr error:%v, hashStr:%s", err, hashStr) +// } +// hashBytes, err := json.Marshal(hash) +// if err != nil { +// t.Errorf("Marshal json error:%v, hash:%v", err, hashBytes) +// } +// var newHash Hash +// err = json.Unmarshal(hashBytes, &newHash) +// if err != nil { +// t.Errorf("Unmarshal json error:%v, hash:%v", err, hashBytes) +// } +// if !hash.IsEqual(&newHash) { +// t.Errorf("String: wrong hash string - got %v, want %v", +// newHash.String(), hashStr) +// } +// err = newHash.Unmarshal(legacyHashStr) +// if err != nil { +// t.Errorf("Unmarshal legacy json error:%v, hash:%v", err, legacyHashStr) +// } +// if !hash.IsEqual(&newHash) { +// t.Errorf("String: wrong hash string - got %v, want %v", +// newHash.String(), hashStr) +// } +// } diff --git a/pkg/crypto/ec/chainhash/hashfuncs.go b/pkg/crypto/ec/chainhash/hashfuncs.go new file mode 100644 index 0000000..a0556a1 --- /dev/null +++ b/pkg/crypto/ec/chainhash/hashfuncs.go @@ -0,0 +1,33 @@ +// Copyright (c) 2015 The Decred developers +// Copyright (c) 2016-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chainhash + +import ( + "next.orly.dev/pkg/crypto/sha256" +) + +// HashB calculates hash(b) and returns the resulting bytes. +func HashB(b []byte) []byte { + hash := sha256.Sum256(b) + return hash[:] +} + +// HashH calculates hash(b) and returns the resulting bytes as a Hash. +func HashH(b []byte) Hash { return Hash(sha256.Sum256(b)) } + +// DoubleHashB calculates hash(hash(b)) and returns the resulting bytes. +func DoubleHashB(b []byte) []byte { + first := sha256.Sum256(b) + second := sha256.Sum256(first[:]) + return second[:] +} + +// DoubleHashH calculates hash(hash(b)) and returns the resulting bytes as a +// Hash. +func DoubleHashH(b []byte) Hash { + first := sha256.Sum256(b) + return sha256.Sum256(first[:]) +} diff --git a/pkg/crypto/ec/chainhash/hashfuncs_test.go b/pkg/crypto/ec/chainhash/hashfuncs_test.go new file mode 100644 index 0000000..93f44c1 --- /dev/null +++ b/pkg/crypto/ec/chainhash/hashfuncs_test.go @@ -0,0 +1,323 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chainhash + +import ( + "fmt" + "testing" +) + +// TestHashFuncs ensures the hash functions which perform hash(b) work as +// expected. +func TestHashFuncs(t *testing.T) { + tests := []struct { + out string + in string + }{ + { + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "", + }, + { + "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", + "a", + }, + { + "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603", + "ab", + }, + { + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + "abc", + }, + { + "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", + "abcd", + }, + { + "36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c", + "abcde", + }, + { + "bef57ec7f53a6d40beb640a780a639c83bc29ac8a9816f1fc6c5c6dcd93c4721", + "abcdef", + }, + { + "7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a", + "abcdefg", + }, + { + "9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c02190ecf1e430635ab", + "abcdefgh", + }, + { + "19cc02f26df43cc571bc9ed7b0c4d29224a3ec229529221725ef76d021c8326f", + "abcdefghi", + }, + { + "72399361da6a7754fec986dca5b7cbaf1c810a28ded4abaf56b2106d06cb78b0", + "abcdefghij", + }, + { + "a144061c271f152da4d151034508fed1c138b8c976339de229c3bb6d4bbb4fce", + "Discard medicine more than two years old.", + }, + { + "6dae5caa713a10ad04b46028bf6dad68837c581616a1589a265a11288d4bb5c4", + "He who has a shady past knows that nice guys finish last.", + }, + { + "ae7a702a9509039ddbf29f0765e70d0001177914b86459284dab8b348c2dce3f", + "I wouldn't marry him with a ten foot pole.", + }, + { + "6748450b01c568586715291dfa3ee018da07d36bb7ea6f180c1af6270215c64f", + "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", + }, + { + "14b82014ad2b11f661b5ae6a99b75105c2ffac278cd071cd6c05832793635774", + "The days of the digital watch are numbered. -Tom Stoppard", + }, + { + "7102cfd76e2e324889eece5d6c41921b1e142a4ac5a2692be78803097f6a48d8", + "Nepal premier won't resign.", + }, + { + "23b1018cd81db1d67983c5f7417c44da9deb582459e378d7a068552ea649dc9f", + "For every action there is an equal and opposite government program.", + }, + { + "8001f190dfb527261c4cfcab70c98e8097a7a1922129bc4096950e57c7999a5a", + "His money is twice tainted: 'taint yours and 'taint mine.", + }, + { + "8c87deb65505c3993eb24b7a150c4155e82eee6960cf0c3a8114ff736d69cad5", + "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", + }, + { + "bfb0a67a19cdec3646498b2e0f751bddc41bba4b7f30081b0b932aad214d16d7", + "It's a tiny change to the code and not completely disgusting. - Bob Manchek", + }, + { + "7f9a0b9bf56332e19f5a0ec1ad9c1425a153da1c624868fda44561d6b74daf36", + "size: a.out: bad magic", + }, + { + "b13f81b8aad9e3666879af19886140904f7f429ef083286195982a7588858cfc", + "The major problem is with sendmail. -Mark Horton", + }, + { + "b26c38d61519e894480c70c8374ea35aa0ad05b2ae3d6674eec5f52a69305ed4", + "Give me a rock, paper and scissors and I will move the world. CCFestoon", + }, + { + "049d5e26d4f10222cd841a119e38bd8d2e0d1129728688449575d4ff42b842c1", + "If the enemy is within range, then so are you.", + }, + { + "0e116838e3cc1c1a14cd045397e29b4d087aa11b0853fc69ec82e90330d60949", + "It's well we cannot hear the screams/That we create in others' dreams.", + }, + { + "4f7d8eb5bcf11de2a56b971021a444aa4eafd6ecd0f307b5109e4e776cd0fe46", + "You remind me of a TV show, but that's all right: I watch it anyway.", + }, + { + "61c0cc4c4bd8406d5120b3fb4ebc31ce87667c162f29468b3c779675a85aebce", + "C is as portable as Stonehedge!!", + }, + { + "1fb2eb3688093c4a3f80cd87a5547e2ce940a4f923243a79a2a1e242220693ac", + "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", + }, + { + "395585ce30617b62c80b93e8208ce866d4edc811a177fdb4b82d3911d8696423", + "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", + }, + { + "4f9b189a13d030838269dce846b16a1ce9ce81fe63e65de2f636863336a98fe6", + "How can you write a big system without C++? -Paul Glick", + }, + } + + // Ensure the hash function which returns a byte slice returns the + // expected result. + for _, test := range tests { + h := fmt.Sprintf("%x", HashB([]byte(test.in))) + if h != test.out { + t.Errorf("HashB(%q) = %s, want %s", test.in, h, test.out) + continue + } + } + // Ensure the hash function which returns a Hash returns the expected + // result. + for _, test := range tests { + hash := HashH([]byte(test.in)) + h := fmt.Sprintf("%x", hash[:]) + if h != test.out { + t.Errorf("HashH(%q) = %s, want %s", test.in, h, test.out) + continue + } + } +} + +// TestDoubleHashFuncs ensures the hash functions which perform hash(hash(b)) +// work as expected. +func TestDoubleHashFuncs(t *testing.T) { + tests := []struct { + out string + in string + }{ + { + "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456", + "", + }, + { + "bf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8", + "a", + }, + { + "a1ff8f1856b5e24e32e3882edd4a021f48f28a8b21854b77fdef25a97601aace", + "ab", + }, + { + "4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", + "abc", + }, + { + "7e9c158ecd919fa439a7a214c9fc58b85c3177fb1613bdae41ee695060e11bc6", + "abcd", + }, + { + "1d72b6eb7ba8b9709c790b33b40d8c46211958e13cf85dbcda0ed201a99f2fb9", + "abcde", + }, + { + "ce65d4756128f0035cba4d8d7fae4e9fa93cf7fdf12c0f83ee4a0e84064bef8a", + "abcdef", + }, + { + "dad6b965ad86b880ceb6993f98ebeeb242de39f6b87a458c6510b5a15ff7bbf1", + "abcdefg", + }, + { + "b9b12e7125f73fda20b8c4161fb9b4b146c34cf88595a1e0503ca2cf44c86bc4", + "abcdefgh", + }, + { + "546db09160636e98405fbec8464a84b6464b32514db259e235eae0445346ffb7", + "abcdefghi", + }, + { + "27635cf23fdf8a10f4cb2c52ade13038c38718c6d7ca716bfe726111a57ad201", + "abcdefghij", + }, + { + "ae0d8e0e7c0336f0c3a72cefa4f24b625a6a460417a921d066058a0b81e23429", + "Discard medicine more than two years old.", + }, + { + "eeb56d02cf638f87ea8f11ebd5b0201afcece984d87be458578d3cfb51978f1b", + "He who has a shady past knows that nice guys finish last.", + }, + { + "dc640bf529608a381ea7065ecbcd0443b95f6e4c008de6e134aff1d36bd4b9d8", + "I wouldn't marry him with a ten foot pole.", + }, + { + "42e54375e60535eb07fc15c6350e10f2c22526f84db1d6f6bba925e154486f33", + "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", + }, + { + "4ed6aa9b88c84afbf928710b03714de69e2ad967c6a78586069adcb4c470d150", + "The days of the digital watch are numbered. -Tom Stoppard", + }, + { + "590c24d1877c1919fad12fe01a8796999e9d20cfbf9bc9bc72fa0bd69f0b04dd", + "Nepal premier won't resign.", + }, + { + "37d270687ee8ebafcd3c1a32f56e1e1304b3c93f252cb637d57a66d59c475eca", + "For every action there is an equal and opposite government program.", + }, + { + "306828fd89278838bb1c544c3032a1fd25ea65c40bba586437568828a5fbe944", + "His money is twice tainted: 'taint yours and 'taint mine.", + }, + { + "49965777eac71faf1e2fb0f6b239ba2fae770977940fd827bcbfe15def6ded53", + "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", + }, + { + "df99ee4e87dd3fb07922dee7735997bbae8f26db20c86137d4219fc4a37b77c3", + "It's a tiny change to the code and not completely disgusting. - Bob Manchek", + }, + { + "920667c84a15b5ee3df4620169f5c0ec930cea0c580858e50e68848871ed65b4", + "size: a.out: bad magic", + }, + { + "5e817fe20848a4a3932db68e90f8d54ec1b09603f0c99fdc051892b776acd462", + "The major problem is with sendmail. -Mark Horton", + }, + { + "6a9d47248ed38852f5f4b2e37e7dfad0ce8d1da86b280feef94ef267e468cff2", + "Give me a rock, paper and scissors and I will move the world. CCFestoon", + }, + { + "2e7aa1b362c94efdbff582a8bd3f7f61c8ce4c25bbde658ef1a7ae1010e2126f", + "If the enemy is within range, then so are you.", + }, + { + "e6729d51240b1e1da76d822fd0c55c75e409bcb525674af21acae1f11667c8ca", + "It's well we cannot hear the screams/That we create in others' dreams.", + }, + { + "09945e4d2743eb669f85e4097aa1cc39ea680a0b2ae2a65a42a5742b3b809610", + "You remind me of a TV show, but that's all right: I watch it anyway.", + }, + { + "1018d8b2870a974887c5174360f0fbaf27958eef15b24522a605c5dae4ae0845", + "C is as portable as Stonehedge!!", + }, + { + "97c76b83c6645c78c261dcdc55d44af02d9f1df8057f997fd08c310c903624d5", + "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", + }, + { + "6bcbf25469e9544c5b5806b24220554fedb6695ba9b1510a76837414f7adb113", + "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", + }, + { + "1041988b06835481f0845be2a54f4628e1da26145b2de7ad1be3bb643cef9d4f", + "How can you write a big system without C++? -Paul Glick", + }, + } + // Ensure the hash function which returns a byte slice returns the + // expected result. + for _, test := range tests { + h := fmt.Sprintf("%x", DoubleHashB([]byte(test.in))) + if h != test.out { + t.Errorf( + "DoubleHashB(%q) = %s, want %s", test.in, h, + test.out, + ) + continue + } + } + // Ensure the hash function which returns a Hash returns the expected + // result. + for _, test := range tests { + hash := DoubleHashH([]byte(test.in)) + h := fmt.Sprintf("%x", hash[:]) + if h != test.out { + t.Errorf( + "DoubleHashH(%q) = %s, want %s", test.in, h, + test.out, + ) + continue + } + } +} diff --git a/pkg/crypto/ec/ciphering.go b/pkg/crypto/ec/ciphering.go new file mode 100644 index 0000000..e7cc46c --- /dev/null +++ b/pkg/crypto/ec/ciphering.go @@ -0,0 +1,16 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// GenerateSharedSecret generates a shared secret based on a secret key and a +// public key using Diffie-Hellman key exchange (ECDH) (RFC 4753). +// RFC5903 Section 9 states we should only return x. +func GenerateSharedSecret(privkey *SecretKey, pubkey *PublicKey) []byte { + return secp256k1.GenerateSharedSecret(privkey, pubkey) +} diff --git a/pkg/crypto/ec/ciphering_test.go b/pkg/crypto/ec/ciphering_test.go new file mode 100644 index 0000000..357d99f --- /dev/null +++ b/pkg/crypto/ec/ciphering_test.go @@ -0,0 +1,32 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "testing" + + "next.orly.dev/pkg/utils" +) + +func TestGenerateSharedSecret(t *testing.T) { + privKey1, err := NewSecretKey() + if err != nil { + t.Errorf("secret key generation error: %s", err) + return + } + privKey2, err := NewSecretKey() + if err != nil { + t.Errorf("secret key generation error: %s", err) + return + } + secret1 := GenerateSharedSecret(privKey1, privKey2.PubKey()) + secret2 := GenerateSharedSecret(privKey2, privKey1.PubKey()) + if !utils.FastEqual(secret1, secret2) { + t.Errorf( + "ECDH failed, secrets mismatch - first: %x, second: %x", + secret1, secret2, + ) + } +} diff --git a/pkg/crypto/ec/curve.go b/pkg/crypto/ec/curve.go new file mode 100644 index 0000000..45de1fb --- /dev/null +++ b/pkg/crypto/ec/curve.go @@ -0,0 +1,111 @@ +// Copyright (c) 2015-2021 The btcsuite developers +// Copyright (c) 2015-2021 The Decred developers + +package btcec + +import ( + "fmt" + + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// JacobianPoint is an element of the group formed by the secp256k1 curve in +// Jacobian projective coordinates and thus represents a point on the curve. +type JacobianPoint = secp256k1.JacobianPoint + +// infinityPoint is the jacobian representation of the point at infinity. +var infinityPoint JacobianPoint + +// MakeJacobianPoint returns a Jacobian point with the provided X, Y, and Z +// coordinates. +func MakeJacobianPoint(x, y, z *FieldVal) JacobianPoint { + return secp256k1.MakeJacobianPoint(x, y, z) +} + +// AddNonConst adds the passed Jacobian points together and stores the result +// in the provided result param in *non-constant* time. +func AddNonConst(p1, p2, result *JacobianPoint) { + secp256k1.AddNonConst(p1, p2, result) +} + +// DecompressY attempts to calculate the Y coordinate for the given X +// coordinate such that the result pair is a point on the secp256k1 curve. It +// adjusts Y based on the desired oddness and returns whether or not it was +// successful since not all X coordinates are valid. +// +// The magnitude of the provided X coordinate field val must be a max of 8 for +// a correct result. The resulting Y field val will have a max magnitude of 2. +func DecompressY(x *FieldVal, odd bool, resultY *FieldVal) bool { + return secp256k1.DecompressY(x, odd, resultY) +} + +// DoubleNonConst doubles the passed Jacobian point and stores the result in +// the provided result parameter in *non-constant* time. +// +// NOTE: The point must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func DoubleNonConst(p, result *JacobianPoint) { + secp256k1.DoubleNonConst(p, result) +} + +// ScalarBaseMultNonConst multiplies k*G where G is the base point of the group +// and k is a big endian integer. The result is stored in Jacobian coordinates +// (x1, y1, z1). +// +// NOTE: The resulting point will be normalized. +func ScalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) { + secp256k1.ScalarBaseMultNonConst(k, result) +} + +// ScalarMultNonConst multiplies k*P where k is a big endian integer modulo the +// curve order and P is a point in Jacobian projective coordinates and stores +// the result in the provided Jacobian point. +// +// NOTE: The point must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func ScalarMultNonConst(k *ModNScalar, point, result *JacobianPoint) { + secp256k1.ScalarMultNonConst(k, point, result) +} + +// ParseJacobian parses a byte slice point as a secp256k1.Publickey and returns the +// pubkey as a JacobianPoint. If the nonce is a zero slice, the infinityPoint +// is returned. +func ParseJacobian(point []byte) (JacobianPoint, error) { + var result JacobianPoint + if len(point) != 33 { + str := fmt.Sprintf( + "invalid nonce: invalid length: %v", + len(point), + ) + return JacobianPoint{}, makeError(secp256k1.ErrPubKeyInvalidLen, str) + } + if point[0] == 0x00 { + return infinityPoint, nil + } + noncePk, err := secp256k1.ParsePubKey(point) + if err != nil { + return JacobianPoint{}, err + } + noncePk.AsJacobian(&result) + return result, nil +} + +// JacobianToByteSlice converts the passed JacobianPoint to a Pubkey +// and serializes that to a byte slice. If the JacobianPoint is the infinity +// point, a zero slice is returned. +func JacobianToByteSlice(point JacobianPoint) []byte { + if point.X == infinityPoint.X && point.Y == infinityPoint.Y { + return make([]byte, 33) + } + point.ToAffine() + return NewPublicKey( + &point.X, &point.Y, + ).SerializeCompressed() +} + +// GeneratorJacobian sets the passed JacobianPoint to the Generator Point. +func GeneratorJacobian(jacobian *JacobianPoint) { + var k ModNScalar + k.SetInt(1) + ScalarBaseMultNonConst(&k, jacobian) +} diff --git a/pkg/crypto/ec/doc.go b/pkg/crypto/ec/doc.go new file mode 100644 index 0000000..0fd494e --- /dev/null +++ b/pkg/crypto/ec/doc.go @@ -0,0 +1,19 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package btcec implements support for the elliptic curves needed for bitcoin. +// +// Bitcoin uses elliptic curve cryptography using koblitz curves +// (specifically secp256k1) for cryptographic functions. See +// http://www.secg.org/collateral/sec2_final.pdf for details on the +// standard. +// +// This package provides the data structures and functions implementing the +// crypto/elliptic Curve interface in order to permit using these curves +// with the standard crypto/ecdsa package provided with go. Helper +// functionality is provided to parse signatures and public keys from +// standard formats. It was designed for use with btcd, but should be +// general enough for other uses of elliptic curve crypto. It was originally based +// on some initial work by ThePiachu, but has significantly diverged since then. +package btcec diff --git a/pkg/crypto/ec/ecdsa/README.md b/pkg/crypto/ec/ecdsa/README.md new file mode 100644 index 0000000..3fa5623 --- /dev/null +++ b/pkg/crypto/ec/ecdsa/README.md @@ -0,0 +1,28 @@ +ecdsa +===== + +[![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/mleku.online/git/ec/secp/ecdsa) + +Package ecdsa provides secp256k1-optimized ECDSA signing and verification. + +This package provides data structures and functions necessary to produce and +verify deterministic canonical signatures in accordance with RFC6979 and +BIP0062, optimized specifically for the secp256k1 curve using the Elliptic Curve +Digital Signature Algorithm (ECDSA), as defined in FIPS 186-3. See +https://www.secg.org/sec2-v2.pdf (also found here +at [sec2-v2.pdf](../sec2-v2.pdf)) for details on the secp256k1 standard. + +It also provides functions to parse and serialize the ECDSA signatures with the +more strict Distinguished Encoding Rules (DER) of ISO/IEC 8825-1 and some +additional restrictions specific to secp256k1. + +In addition, it supports a custom "compact" signature format which allows +efficient recovery of the public key from a given valid signature and message +hash combination. + +A comprehensive suite of tests is provided to ensure proper functionality. + +## License + +Package ecdsa is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/pkg/crypto/ec/ecdsa/bench_test.go b/pkg/crypto/ec/ecdsa/bench_test.go new file mode 100644 index 0000000..1f6361b --- /dev/null +++ b/pkg/crypto/ec/ecdsa/bench_test.go @@ -0,0 +1,169 @@ +// Copyright 2013-2016 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ecdsa + +import ( + "testing" + + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/encoders/hex" +) + +// 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) *secp256k1.ModNScalar { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var scalar secp256k1.ModNScalar + if overflow := scalar.SetByteSlice(b); overflow { + panic("hex in source file overflows mod N scalar: " + s) + } + 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 +// 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 { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var f secp256k1.FieldVal + if overflow := f.SetByteSlice(b); overflow { + panic("hex in source file overflows mod P: " + s) + } + return &f +} + +// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to +// verify signatures. +func BenchmarkSigVerify(b *testing.B) { + // Randomly generated keypair. + // Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d + pubKey := secp256k1.NewPublicKey( + hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), + hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), + ) + // Double sha256 of by{0x01, 0x02, 0x03, 0x04} + msgHash := hexToBytes("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0") + sig := NewSignature( + hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), + hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), + ) + if !sig.Verify(msgHash, pubKey) { + b.Errorf("Signature failed to verify") + return + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + sig.Verify(msgHash, pubKey) + } +} + +// BenchmarkSign benchmarks how long it takes to sign a message. +func BenchmarkSign(b *testing.B) { + // Randomly generated keypair. + d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + secKey := secp256k1.NewSecretKey(d) + // blake256 of by{0x01, 0x02, 0x03, 0x04}. + msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + signRFC6979(secKey, msgHash) + } +} + +// BenchmarkSigSerialize benchmarks how long it takes to serialize a typical +// signature with the strict DER encoding. +func BenchmarkSigSerialize(b *testing.B) { + // Randomly generated keypair. + // Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d + // Signature for double sha256 of by{0x01, 0x02, 0x03, 0x04}. + sig := NewSignature( + hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), + hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), + ) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + sig.Serialize() + } +} + +// BenchmarkNonceRFC6979 benchmarks how long it takes to generate a +// deterministic nonce according to RFC6979. +func BenchmarkNonceRFC6979(b *testing.B) { + // Randomly generated keypair. + // Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d + // X: d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab + // Y: ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52 + secKeyStr := "9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d" + secKey := hexToBytes(secKeyStr) + // BLAKE-256 of by{0x01, 0x02, 0x03, 0x04}. + msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") + b.ReportAllocs() + b.ResetTimer() + var noElideNonce *secp256k1.ModNScalar + for i := 0; i < b.N; i++ { + noElideNonce = secp256k1.NonceRFC6979(secKey, msgHash, nil, nil, 0) + } + _ = noElideNonce +} + +// BenchmarkSignCompact benchmarks how long it takes to produce a compact +// signature for a message. +func BenchmarkSignCompact(b *testing.B) { + d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + secKey := secp256k1.NewSecretKey(d) + // blake256 of by{0x01, 0x02, 0x03, 0x04}. + msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = SignCompact(secKey, msgHash, true) + } +} + +// BenchmarkRecoverCompact benchmarks how long it takes to recover a public key +// given a compact signature and message. +func BenchmarkRecoverCompact(b *testing.B) { + // Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d + wantPubKey := secp256k1.NewPublicKey( + hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), + hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), + ) + compactSig := hexToBytes( + "205978b7896bc71676ba2e459882a8f52e1299449596c4f" + + "93c59bf1fbfa2f9d3b76ecd0c99406f61a6de2bb5a8937c061c176ecf381d0231e0d" + + "af73b922c8952c7", + ) + // blake256 of by{0x01, 0x02, 0x03, 0x04}. + msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") + // Ensure a valid compact signature is being benchmarked. + pubKey, wasCompressed, err := RecoverCompact(compactSig, msgHash) + if err != nil { + b.Fatalf("unexpected err: %v", err) + } + if !wasCompressed { + b.Fatal("recover claims uncompressed pubkey") + } + if !pubKey.IsEqual(wantPubKey) { + b.Fatal("recover returned unexpected pubkey") + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, _ = RecoverCompact(compactSig, msgHash) + } +} diff --git a/pkg/crypto/ec/ecdsa/doc.go b/pkg/crypto/ec/ecdsa/doc.go new file mode 100644 index 0000000..f47a29c --- /dev/null +++ b/pkg/crypto/ec/ecdsa/doc.go @@ -0,0 +1,40 @@ +// Copyright (c) 2020-2023 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package ecdsa provides secp256k1-optimized ECDSA signing and verification. +// +// This package provides data structures and functions necessary to produce and +// verify deterministic canonical signatures in accordance with RFC6979 and +// BIP0062, optimized specifically for the secp256k1 curve using the Elliptic Curve +// Digital Signature Algorithm (ECDSA), as defined in FIPS 186-3. See +// https://www.secg.org/sec2-v2.pdf for details on the secp256k1 standard. +// +// It also provides functions to parse and serialize the ECDSA signatures with the +// more strict Distinguished Encoding Rules (DER) of ISO/IEC 8825-1 and some +// additional restrictions specific to secp256k1. +// +// In addition, it supports a custom "compact" signature format which allows +// efficient recovery of the public key from a given valid signature and message +// hash combination. +// +// A comprehensive suite of tests is provided to ensure proper functionality. +// +// # ECDSA use in Decred +// +// At the time of this writing, ECDSA signatures are heavily used for proving coin +// ownership in Decred as the vast majority of transactions consist of what is +// effectively transferring ownership of coins to a public key associated with a +// secret key only known to the recipient of the coins along with an encumbrance +// that requires an ECDSA signature that proves the new owner possesses the secret +// 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. +package ecdsa diff --git a/pkg/crypto/ec/ecdsa/error.go b/pkg/crypto/ec/ecdsa/error.go new file mode 100644 index 0000000..08b8c6e --- /dev/null +++ b/pkg/crypto/ec/ecdsa/error.go @@ -0,0 +1,106 @@ +// Copyright (c) 2020-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ecdsa + +// 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 Error. +const ( + // ErrSigTooShort is returned when a signature that should be a DER + // signature is too short. + ErrSigTooShort = ErrorKind("ErrSigTooShort") + // ErrSigTooLong is returned when a signature that should be a DER signature + // is too long. + ErrSigTooLong = ErrorKind("ErrSigTooLong") + // ErrSigInvalidSeqID is returned when a signature that should be a DER + // signature does not have the expected ASN.1 sequence ID. + ErrSigInvalidSeqID = ErrorKind("ErrSigInvalidSeqID") + // ErrSigInvalidDataLen is returned when a signature that should be a DER + // signature does not specify the correct number of remaining bytes for the + // R and S portions. + ErrSigInvalidDataLen = ErrorKind("ErrSigInvalidDataLen") + // ErrSigMissingSTypeID is returned when a signature that should be a DER + // signature does not provide the ASN.1 type ID for S. + ErrSigMissingSTypeID = ErrorKind("ErrSigMissingSTypeID") + // ErrSigMissingSLen is returned when a signature that should be a DER + // signature does not provide the length of S. + ErrSigMissingSLen = ErrorKind("ErrSigMissingSLen") + // ErrSigInvalidSLen is returned when a signature that should be a DER + // signature does not specify the correct number of bytes for the S portion. + ErrSigInvalidSLen = ErrorKind("ErrSigInvalidSLen") + // ErrSigInvalidRIntID is returned when a signature that should be a DER + // signature does not have the expected ASN.1 integer ID for R. + ErrSigInvalidRIntID = ErrorKind("ErrSigInvalidRIntID") + // ErrSigZeroRLen is returned when a signature that should be a DER + // signature has an R length of zero. + ErrSigZeroRLen = ErrorKind("ErrSigZeroRLen") + // ErrSigNegativeR is returned when a signature that should be a DER + // signature has a negative value for R. + ErrSigNegativeR = ErrorKind("ErrSigNegativeR") + // ErrSigTooMuchRPadding is returned when a signature that should be a DER + // signature has too much padding for R. + ErrSigTooMuchRPadding = ErrorKind("ErrSigTooMuchRPadding") + // ErrSigRIsZero is returned when a signature has R set to the value zero. + ErrSigRIsZero = ErrorKind("ErrSigRIsZero") + // ErrSigRTooBig is returned when a signature has R with a value that is + // greater than or equal to the group order. + ErrSigRTooBig = ErrorKind("ErrSigRTooBig") + // ErrSigInvalidSIntID is returned when a signature that should be a DER + // signature does not have the expected ASN.1 integer ID for S. + ErrSigInvalidSIntID = ErrorKind("ErrSigInvalidSIntID") + // ErrSigZeroSLen is returned when a signature that should be a DER + // signature has an S length of zero. + ErrSigZeroSLen = ErrorKind("ErrSigZeroSLen") + // ErrSigNegativeS is returned when a signature that should be a DER + // signature has a negative value for S. + ErrSigNegativeS = ErrorKind("ErrSigNegativeS") + // ErrSigTooMuchSPadding is returned when a signature that should be a DER + // signature has too much padding for S. + ErrSigTooMuchSPadding = ErrorKind("ErrSigTooMuchSPadding") + // ErrSigSIsZero is returned when a signature has S set to the value zero. + ErrSigSIsZero = ErrorKind("ErrSigSIsZero") + // ErrSigSTooBig is returned when a signature has S with a value that is + // greater than or equal to the group order. + ErrSigSTooBig = ErrorKind("ErrSigSTooBig") + // ErrSigInvalidLen is returned when a signature that should be a compact + // signature is not the required length. + ErrSigInvalidLen = ErrorKind("ErrSigInvalidLen") + // ErrSigInvalidRecoveryCode is returned when a signature that should be a + // compact signature has an invalid value for the public key recovery code. + ErrSigInvalidRecoveryCode = ErrorKind("ErrSigInvalidRecoveryCode") + // ErrSigOverflowsPrime is returned when a signature that should be a + // compact signature has the overflow bit set but adding the order to it + // would overflow the underlying field prime. + ErrSigOverflowsPrime = ErrorKind("ErrSigOverflowsPrime") + // ErrPointNotOnCurve is returned when attempting to recover a public key + // from a compact signature results in a point that is not on the elliptic + // curve. + ErrPointNotOnCurve = ErrorKind("ErrPointNotOnCurve") +) + +// Error satisfies the error interface and prints human-readable errors. +func (e ErrorKind) Error() string { return string(e) } + +// Error identifies an error related to an ECDSA signature. 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 { + Err error + Description string +} + +// Error satisfies the error interface and prints human-readable errors. +func (e Error) Error() string { return e.Description } + +// Unwrap returns the underlying wrapped error. +func (e Error) Unwrap() error { return e.Err } + +// signatureError creates an Error given a set of arguments. +func signatureError(kind ErrorKind, desc string) Error { + return Error{Err: kind, Description: desc} +} diff --git a/pkg/crypto/ec/ecdsa/error_test.go b/pkg/crypto/ec/ecdsa/error_test.go new file mode 100644 index 0000000..0a6a64b --- /dev/null +++ b/pkg/crypto/ec/ecdsa/error_test.go @@ -0,0 +1,154 @@ +// Copyright (c) 2020-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ecdsa + +import ( + "errors" + "testing" +) + +// TestErrorKindStringer tests the stringized output for the ErrorKind type. +func TestErrorKindStringer(t *testing.T) { + tests := []struct { + in ErrorKind + want string + }{ + {ErrSigTooShort, "ErrSigTooShort"}, + {ErrSigTooLong, "ErrSigTooLong"}, + {ErrSigInvalidSeqID, "ErrSigInvalidSeqID"}, + {ErrSigInvalidDataLen, "ErrSigInvalidDataLen"}, + {ErrSigMissingSTypeID, "ErrSigMissingSTypeID"}, + {ErrSigMissingSLen, "ErrSigMissingSLen"}, + {ErrSigInvalidSLen, "ErrSigInvalidSLen"}, + {ErrSigInvalidRIntID, "ErrSigInvalidRIntID"}, + {ErrSigZeroRLen, "ErrSigZeroRLen"}, + {ErrSigNegativeR, "ErrSigNegativeR"}, + {ErrSigTooMuchRPadding, "ErrSigTooMuchRPadding"}, + {ErrSigRIsZero, "ErrSigRIsZero"}, + {ErrSigRTooBig, "ErrSigRTooBig"}, + {ErrSigInvalidSIntID, "ErrSigInvalidSIntID"}, + {ErrSigZeroSLen, "ErrSigZeroSLen"}, + {ErrSigNegativeS, "ErrSigNegativeS"}, + {ErrSigTooMuchSPadding, "ErrSigTooMuchSPadding"}, + {ErrSigSIsZero, "ErrSigSIsZero"}, + {ErrSigSTooBig, "ErrSigSTooBig"}, + {ErrSigInvalidLen, "ErrSigInvalidLen"}, + {ErrSigInvalidRecoveryCode, "ErrSigInvalidRecoveryCode"}, + {ErrSigOverflowsPrime, "ErrSigOverflowsPrime"}, + {ErrPointNotOnCurve, "ErrPointNotOnCurve"}, + } + + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("#%d: got: %s want: %s", i, result, test.want) + continue + } + } +} + +// TestError tests the error output for the Error type. +func TestError(t *testing.T) { + tests := []struct { + in Error + want string + }{ + { + Error{Description: "some error"}, + "some error", + }, { + Error{Description: "human-readable error"}, + "human-readable error", + }, + } + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("#%d: got: %s want: %s", i, result, test.want) + continue + } + } +} + +// TestErrorKindIsAs ensures both ErrorKind and Error can be identified as being +// a specific error kind via errors.Is and unwrapped via errors.As. +func TestErrorKindIsAs(t *testing.T) { + tests := []struct { + name string + err error + target error + wantMatch bool + wantAs ErrorKind + }{ + { + name: "ErrSigTooShort == ErrSigTooShort", + err: ErrSigTooShort, + target: ErrSigTooShort, + wantMatch: true, + wantAs: ErrSigTooShort, + }, { + name: "Error.ErrSigTooShort == ErrSigTooShort", + err: signatureError(ErrSigTooShort, ""), + target: ErrSigTooShort, + wantMatch: true, + wantAs: ErrSigTooShort, + }, { + name: "Error.ErrSigTooShort == Error.ErrSigTooShort", + err: signatureError(ErrSigTooShort, ""), + target: signatureError(ErrSigTooShort, ""), + wantMatch: true, + wantAs: ErrSigTooShort, + }, { + name: "ErrSigTooLong != ErrSigTooShort", + err: ErrSigTooLong, + target: ErrSigTooShort, + wantMatch: false, + wantAs: ErrSigTooLong, + }, { + name: "Error.ErrSigTooLong != ErrSigTooShort", + err: signatureError(ErrSigTooLong, ""), + target: ErrSigTooShort, + wantMatch: false, + wantAs: ErrSigTooLong, + }, { + name: "ErrSigTooLong != Error.ErrSigTooShort", + err: ErrSigTooLong, + target: signatureError(ErrSigTooShort, ""), + wantMatch: false, + wantAs: ErrSigTooLong, + }, { + name: "Error.ErrSigTooLong != Error.ErrSigTooShort", + err: signatureError(ErrSigTooLong, ""), + target: signatureError(ErrSigTooShort, ""), + wantMatch: false, + wantAs: ErrSigTooLong, + }, + } + for _, test := range tests { + // Ensure the error matches or not depending on the expected result. + result := errors.Is(test.err, test.target) + if result != test.wantMatch { + t.Errorf( + "%s: incorrect error identification -- got %v, want %v", + test.name, result, test.wantMatch, + ) + continue + } + // Ensure the underlying error kind can be unwrapped and is the + // expected code. + var kind ErrorKind + if !errors.As(test.err, &kind) { + t.Errorf("%s: unable to unwrap to error", test.name) + continue + } + if !errors.Is(kind, test.wantAs) { + t.Errorf( + "%s: unexpected unwrapped error -- got %v, want %v", + test.name, kind, test.wantAs, + ) + continue + } + } +} diff --git a/pkg/crypto/ec/ecdsa/example_test.go b/pkg/crypto/ec/ecdsa/example_test.go new file mode 100644 index 0000000..3b4ca8b --- /dev/null +++ b/pkg/crypto/ec/ecdsa/example_test.go @@ -0,0 +1,79 @@ +// Copyright (c) 2014 The btcsuite developers +// Copyright (c) 2015-2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// TODO: change this to work with sha256 + +package ecdsa_test + +// // This example demonstrates signing a message with a secp256k1 secret key that +// // is first parsed from raw bytes and serializing the generated signature. +// func ExampleSign() { +// // Decode a hex-encoded secret key. +// pkBytes, err := hex.Dec("22a47fa09a223f2aa079edf85a7c2d4f87" + +// "20ee63e502ee2869afab7de234b80c") +// if err != nil { +// fmt.Println(err) +// return +// } +// secKey := secp256k1.SecKeyFromBytes(pkBytes) +// +// // Sign a message using the secret key. +// message := "test message" +// messageHash := blake256.Sum256(by(message)) +// signature := ecdsa.Sign(secKey, messageHash[:]) +// +// // Serialize and display the signature. +// fmt.Printf("Serialized Signature: %x\n", signature.Serialize()) +// +// // Verify the signature for the message using the public key. +// pubKey := secKey.Pubkey() +// verified := signature.Verify(messageHash[:], pubKey) +// fmt.Printf("Signature Verified? %v\n", verified) +// +// // Output: +// // Serialized Signature: 3045022100fcc0a8768cfbcefcf2cadd7cfb0fb18ed08dd2e2ae84bef1a474a3d351b26f0302200fc1a350b45f46fa00101391302818d748c2b22615511a3ffd5bb638bd777207 +// // Signature Verified? true +// } + +// // This example demonstrates verifying a secp256k1 signature against a public +// // key that is first parsed from raw bytes. The signature is also parsed from +// // raw bytes. +// func ExampleSignature_Verify() { +// // Decode hex-encoded serialized public key. +// pubKeyBytes, err := hex.Dec("02a673638cb9587cb68ea08dbef685c" + +// "6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5") +// if err != nil { +// fmt.Println(err) +// return +// } +// pubKey, err := secp256k1.ParsePubKey(pubKeyBytes) +// if err != nil { +// fmt.Println(err) +// return +// } +// +// // Decode hex-encoded serialized signature. +// sigBytes, err := hex.Dec("3045022100fcc0a8768cfbcefcf2cadd7cfb0" + +// "fb18ed08dd2e2ae84bef1a474a3d351b26f0302200fc1a350b45f46fa0010139130" + +// "2818d748c2b22615511a3ffd5bb638bd777207") +// if err != nil { +// fmt.Println(err) +// return +// } +// signature, err := ecdsa.ParseDERSignature(sigBytes) +// if err != nil { +// fmt.Println(err) +// return +// } +// +// // Verify the signature for the message using the public key. +// message := "test message" +// messageHash := blake256.Sum256(by(message)) +// verified := signature.Verify(messageHash[:], pubKey) +// fmt.Println("Signature Verified?", verified) +// +// // Output: +// // Signature Verified? true +// } diff --git a/pkg/crypto/ec/ecdsa/signature.go b/pkg/crypto/ec/ecdsa/signature.go new file mode 100644 index 0000000..46d700b --- /dev/null +++ b/pkg/crypto/ec/ecdsa/signature.go @@ -0,0 +1,954 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ecdsa + +import ( + "fmt" + + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// References: +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) +// +// [ISO/IEC 8825-1]: Information technology — ASN.1 encoding rules: +// Specification of Basic Encoding Rules (BER), Canonical Encoding Rules +// (CER) and Distinguished Encoding Rules (DER) +// +// [SEC1]: Elliptic Curve Cryptography (May 31, 2009, Version 2.0) +// https://www.secg.org/sec1-v2.pdf + +var ( + // zero32 is an array of 32 bytes used for the purposes of zeroing and is + // defined here to avoid extra allocations. + zero32 = [32]byte{} + // orderAsFieldVal is the order of the secp256k1 curve group stored as a + // field value. It is provided here to avoid the need to create it multiple + // times. + orderAsFieldVal = func() secp256k1.FieldVal { + var f secp256k1.FieldVal + f.SetByteSlice(secp256k1.Params().N.Bytes()) + return f + }() +) + +const ( + // asn1SequenceID is the ASN.1 identifier for a sequence and is used when + // parsing and serializing signatures encoded with the Distinguished + // Encoding Rules (DER) format per section 10 of [ISO/IEC 8825-1]. + asn1SequenceID = 0x30 + // asn1IntegerID is the ASN.1 identifier for an integer and is used when + // parsing and serializing signatures encoded with the Distinguished + // Encoding Rules (DER) format per section 10 of [ISO/IEC 8825-1]. + asn1IntegerID = 0x02 +) + +// Signature is a type representing an ECDSA signature. +type Signature struct { + r secp256k1.ModNScalar + s secp256k1.ModNScalar +} + +// NewSignature instantiates a new signature given some r and s values. +func NewSignature(r, s *secp256k1.ModNScalar) *Signature { + return &Signature{*r, *s} +} + +// Serialize returns the ECDSA signature in the Distinguished Encoding Rules +// (DER) format per section 10 of [ISO/IEC 8825-1] and such that the S component +// of the signature is less than or equal to the half order of the group. +// +// Note that the serialized bytes returned do not include the appended hash type +// used in Decred signature scripts. +func (sig *Signature) Serialize() []byte { + // The format of a DER encoded signature is as follows: + // + // 0x30 0x02 0x02 + // - 0x30 is the ASN.1 identifier for a sequence. + // - Total length is 1 byte and specifies length of all remaining data. + // - 0x02 is the ASN.1 identifier that specifies an integer follows. + // - Length of R is 1 byte and specifies how many bytes R occupies. + // - R is the arbitrary length big-endian encoded number which + // represents the R value of the signature. DER encoding dictates + // that the value must be encoded using the minimum possible number + // of bytes. This implies the first byte can only be null if the + // highest bit of the next byte is set in order to prevent it from + // being interpreted as a negative number. + // - 0x02 is once again the ASN.1 integer identifier. + // - Length of S is 1 byte and specifies how many bytes S occupies. + // - S is the arbitrary length big-endian encoded number which + // represents the S value of the signature. The encoding rules are + // identical as those for R. + + // Ensure the S component of the signature is less than or equal to the half + // order of the group because both S and its negation are valid signatures + // modulo the order, so this forces a consistent choice to reduce signature + // malleability. + sigS := new(secp256k1.ModNScalar).Set(&sig.s) + if sigS.IsOverHalfOrder() { + sigS.Negate() + } + + // Serialize the R and S components of the signature into their fixed + // 32-byte big-endian encoding. Note that the extra leading zero byte is + // used to ensure it is canonical per DER and will be stripped if needed + // below. + var rBuf, sBuf [33]byte + sig.r.PutBytesUnchecked(rBuf[1:33]) + sigS.PutBytesUnchecked(sBuf[1:33]) + // Ensure the encoded bytes for the R and S components are canonical per DER + // by trimming all leading zero bytes so long as the next byte does not have + // the high bit set and it's not the final byte. + canonR, canonS := rBuf[:], sBuf[:] + for len(canonR) > 1 && canonR[0] == 0x00 && canonR[1]&0x80 == 0 { + canonR = canonR[1:] + } + for len(canonS) > 1 && canonS[0] == 0x00 && canonS[1]&0x80 == 0 { + canonS = canonS[1:] + } + // Total length of returned signature is 1 byte for each magic and length + // (6 total), plus lengths of R and S. + totalLen := 6 + len(canonR) + len(canonS) + b := make([]byte, 0, totalLen) + b = append(b, asn1SequenceID) + b = append(b, byte(totalLen-2)) + b = append(b, asn1IntegerID) + b = append(b, byte(len(canonR))) + b = append(b, canonR...) + b = append(b, asn1IntegerID) + b = append(b, byte(len(canonS))) + b = append(b, canonS...) + return b +} + +// zeroArray32 zeroes the provided 32-byte buffer. +func zeroArray32(b *[32]byte) { + copy(b[:], zero32[:]) +} + +// fieldToModNScalar converts a field value to scalar modulo the group order and +// returns the scalar along with either 1 if it was reduced (aka it overflowed) +// or 0 otherwise. +// +// 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. +func fieldToModNScalar(v *secp256k1.FieldVal) (secp256k1.ModNScalar, uint32) { + var buf [32]byte + v.PutBytes(&buf) + var s secp256k1.ModNScalar + overflow := s.SetBytes(&buf) + zeroArray32(&buf) + return s, overflow +} + +// modNScalarToField converts a scalar modulo the group order to a field value. +func modNScalarToField(v *secp256k1.ModNScalar) secp256k1.FieldVal { + var buf [32]byte + v.PutBytes(&buf) + var fv secp256k1.FieldVal + fv.SetBytes(&buf) + return fv +} + +// Verify returns whether the signature is valid for the provided hash +// and secp256k1 public key. +func (sig *Signature) Verify(hash []byte, pubKey *secp256k1.PublicKey) bool { + // The algorithm for verifying an ECDSA signature is given as algorithm 4.30 + // in [GECC]. + // + // The following is a paraphrased version for reference: + // + // G = curve generator + // N = curve order + // Q = public key + // m = message + // R, S = signature + // + // 1. Fail if R and S are not in [1, N-1] + // 2. e = H(m) + // 3. w = S^-1 mod N + // 4. u1 = e * w mod N + // u2 = R * w mod N + // 5. X = u1G + u2Q + // 6. Fail if X is the point at infinity + // 7. x = X.x mod N (X.x is the x coordinate of X) + // 8. Verified if x == R + // + // However, since all group operations are done internally in Jacobian + // projective space, the algorithm is modified slightly here in order to + // avoid an expensive inversion back into affine coordinates at step 7. + // Credits to Greg Maxwell for originally suggesting this optimization. + // + // Ordinarily, step 7 involves converting the x coordinate to affine by + // calculating x = x / z^2 (mod P) and then calculating the remainder as + // x = x (mod N). Then step 8 compares it to R. + // + // Note that since R is the x coordinate mod N from a random point that was + // originally mod P, and the cofactor of the secp256k1 curve is 1, there are + // only two possible x coordinates that the original random point could have + // been to produce R: x, where x < N, and x+N, where x+N < P. + // + // This implies that the signature is valid if either: + // a) R == X.x / X.z^2 (mod P) + // => R * X.z^2 == X.x (mod P) + // --or-- + // b) R + N < P && R + N == X.x / X.z^2 (mod P) + // => R + N < P && (R + N) * X.z^2 == X.x (mod P) + // + // Therefore the following modified algorithm is used: + // + // 1. Fail if R and S are not in [1, N-1] + // 2. e = H(m) + // 3. w = S^-1 mod N + // 4. u1 = e * w mod N + // u2 = R * w mod N + // 5. X = u1G + u2Q + // 6. Fail if X is the point at infinity + // 7. z = (X.z)^2 mod P (X.z is the z coordinate of X) + // 8. Verified if R * z == X.x (mod P) + // 9. Fail if R + N >= P + // 10. Verified if (R + N) * z == X.x (mod P) + // + // Step 1. + // + // Fail if R and S are not in [1, N-1]. + if sig.r.IsZero() || sig.s.IsZero() { + return false + } + // Step 2. + // + // e = H(m) + var e secp256k1.ModNScalar + e.SetByteSlice(hash) + // Step 3. + // + // w = S^-1 mod N + w := new(secp256k1.ModNScalar).InverseValNonConst(&sig.s) + // Step 4. + // + // u1 = e * w mod N + // u2 = R * w mod N + u1 := new(secp256k1.ModNScalar).Mul2(&e, w) + u2 := new(secp256k1.ModNScalar).Mul2(&sig.r, w) + // Step 5. + // + // X = u1G + u2Q + var X, Q, u1G, u2Q secp256k1.JacobianPoint + pubKey.AsJacobian(&Q) + secp256k1.ScalarBaseMultNonConst(u1, &u1G) + secp256k1.ScalarMultNonConst(u2, &Q, &u2Q) + secp256k1.AddNonConst(&u1G, &u2Q, &X) + // Step 6. + // + // Fail if X is the point at infinity + if (X.X.IsZero() && X.Y.IsZero()) || X.Z.IsZero() { + return false + } + // Step 7. + // + // z = (X.z)^2 mod P (X.z is the z coordinate of X) + z := new(secp256k1.FieldVal).SquareVal(&X.Z) + // Step 8. + // + // Verified if R * z == X.x (mod P) + sigRModP := modNScalarToField(&sig.r) + result := new(secp256k1.FieldVal).Mul2(&sigRModP, z).Normalize() + if result.Equals(&X.X) { + return true + } + // Step 9. + // + // Fail if R + N >= P + if sigRModP.IsGtOrEqPrimeMinusOrder() { + return false + } + // Step 10. + // + // Verified if (R + N) * z == X.x (mod P) + sigRModP.Add(&orderAsFieldVal) + result.Mul2(&sigRModP, z).Normalize() + return result.Equals(&X.X) +} + +// IsEqual compares this Signature instance to the one passed, returning true if +// both Signatures are equivalent. A signature is equivalent to another, if +// they both have the same scalar value for R and S. +func (sig *Signature) IsEqual(otherSig *Signature) bool { + return sig.r.Equals(&otherSig.r) && sig.s.Equals(&otherSig.s) +} + +// ParseDERSignature parses a signature in the Distinguished Encoding Rules +// (DER) format per section 10 of [ISO/IEC 8825-1] and enforces the following +// additional restrictions specific to secp256k1: +// +// - The R and S values must be in the valid range for secp256k1 scalars: +// - Negative values are rejected +// - Zero is rejected +// - Values greater than or equal to the secp256k1 group order are rejected +func ParseDERSignature(sig []byte) (*Signature, error) { + // The format of a DER encoded signature for secp256k1 is as follows: + // + // 0x30 0x02 0x02 + // - 0x30 is the ASN.1 identifier for a sequence + // - Total length is 1 byte and specifies length of all remaining data + // - 0x02 is the ASN.1 identifier that specifies an integer follows + // - Length of R is 1 byte and specifies how many bytes R occupies + // - R is the arbitrary length big-endian encoded number which + // represents the R value of the signature. DER encoding dictates + // that the value must be encoded using the minimum possible number + // of bytes. This implies the first byte can only be null if the + // highest bit of the next byte is set in order to prevent it from + // being interpreted as a negative number. + // - 0x02 is once again the ASN.1 integer identifier + // - Length of S is 1 byte and specifies how many bytes S occupies + // - S is the arbitrary length big-endian encoded number which + // represents the S value of the signature. The encoding rules are + // identical as those for R. + // + // NOTE: The DER specification supports specifying lengths that can occupy + // more than 1 byte, however, since this is specific to secp256k1 + // signatures, all lengths will be a single byte. + const ( + // minSigLen is the minimum length of a DER encoded signature and is + // when both R and S are 1 byte each. + // + // 0x30 + <1-byte> + 0x02 + 0x01 + + 0x2 + 0x01 + + minSigLen = 8 + // maxSigLen is the maximum length of a DER encoded signature and is + // when both R and S are 33 bytes each. It is 33 bytes because a + // 256-bit integer requires 32 bytes and an additional leading null byte + // might be required if the high bit is set in the value. + // + // 0x30 + <1-byte> + 0x02 + 0x21 + <33 bytes> + 0x2 + 0x21 + <33 bytes> + maxSigLen = 72 + // sequenceOffset is the byte offset within the signature of the + // expected ASN.1 sequence identifier. + sequenceOffset = 0 + // dataLenOffset is the byte offset within the signature of the expected + // total length of all remaining data in the signature. + dataLenOffset = 1 + // rTypeOffset is the byte offset within the signature of the ASN.1 + // identifier for R and is expected to indicate an ASN.1 integer. + rTypeOffset = 2 + // rLenOffset is the byte offset within the signature of the length of + // R. + rLenOffset = 3 + // rOffset is the byte offset within the signature of R. + rOffset = 4 + ) + // The signature must adhere to the minimum and maximum allowed length. + sigLen := len(sig) + if sigLen < minSigLen { + str := fmt.Sprintf( + "malformed signature: too short: %d < %d", sigLen, + minSigLen, + ) + return nil, signatureError(ErrSigTooShort, str) + } + if sigLen > maxSigLen { + str := fmt.Sprintf( + "malformed signature: too long: %d > %d", sigLen, + maxSigLen, + ) + return nil, signatureError(ErrSigTooLong, str) + } + // The signature must start with the ASN.1 sequence identifier. + if sig[sequenceOffset] != asn1SequenceID { + str := fmt.Sprintf( + "malformed signature: format has wrong type: %#x", + sig[sequenceOffset], + ) + return nil, signatureError(ErrSigInvalidSeqID, str) + } + // The signature must indicate the correct amount of data for all elements + // related to R and S. + if int(sig[dataLenOffset]) != sigLen-2 { + str := fmt.Sprintf( + "malformed signature: bad length: %d != %d", + sig[dataLenOffset], sigLen-2, + ) + return nil, signatureError(ErrSigInvalidDataLen, str) + } + // Calculate the offsets of the elements related to S and ensure S is inside + // the signature. + // + // rLen specifies the length of the big-endian encoded number which + // represents the R value of the signature. + // + // sTypeOffset is the offset of the ASN.1 identifier for S and, like its R + // counterpart, is expected to indicate an ASN.1 integer. + // + // sLenOffset and sOffset are the byte offsets within the signature of the + // length of S and S itself, respectively. + rLen := int(sig[rLenOffset]) + sTypeOffset := rOffset + rLen + sLenOffset := sTypeOffset + 1 + if sTypeOffset >= sigLen { + str := "malformed signature: S type indicator missing" + return nil, signatureError(ErrSigMissingSTypeID, str) + } + if sLenOffset >= sigLen { + str := "malformed signature: S length missing" + return nil, signatureError(ErrSigMissingSLen, str) + } + // The lengths of R and S must match the overall length of the signature. + // + // sLen specifies the length of the big-endian encoded number which + // represents the S value of the signature. + sOffset := sLenOffset + 1 + sLen := int(sig[sLenOffset]) + if sOffset+sLen != sigLen { + str := "malformed signature: invalid S length" + return nil, signatureError(ErrSigInvalidSLen, str) + } + // R elements must be ASN.1 integers. + if sig[rTypeOffset] != asn1IntegerID { + str := fmt.Sprintf( + "malformed signature: R integer marker: %#x != %#x", + sig[rTypeOffset], asn1IntegerID, + ) + return nil, signatureError(ErrSigInvalidRIntID, str) + } + // Zero-length integers are not allowed for R. + if rLen == 0 { + str := "malformed signature: R length is zero" + return nil, signatureError(ErrSigZeroRLen, str) + } + // R must not be negative. + if sig[rOffset]&0x80 != 0 { + str := "malformed signature: R is negative" + return nil, signatureError(ErrSigNegativeR, str) + } + // Null bytes at the start of R are not allowed, unless R would otherwise be + // interpreted as a negative number. + if rLen > 1 && sig[rOffset] == 0x00 && sig[rOffset+1]&0x80 == 0 { + str := "malformed signature: R value has too much padding" + return nil, signatureError(ErrSigTooMuchRPadding, str) + } + // S elements must be ASN.1 integers. + if sig[sTypeOffset] != asn1IntegerID { + str := fmt.Sprintf( + "malformed signature: S integer marker: %#x != %#x", + sig[sTypeOffset], asn1IntegerID, + ) + return nil, signatureError(ErrSigInvalidSIntID, str) + } + // Zero-length integers are not allowed for S. + if sLen == 0 { + str := "malformed signature: S length is zero" + return nil, signatureError(ErrSigZeroSLen, str) + } + // S must not be negative. + if sig[sOffset]&0x80 != 0 { + str := "malformed signature: S is negative" + return nil, signatureError(ErrSigNegativeS, str) + } + // Null bytes at the start of S are not allowed, unless S would otherwise be + // interpreted as a negative number. + if sLen > 1 && sig[sOffset] == 0x00 && sig[sOffset+1]&0x80 == 0 { + str := "malformed signature: S value has too much padding" + return nil, signatureError(ErrSigTooMuchSPadding, str) + } + // The signature is validly encoded per DER at this point, however, enforce + // additional restrictions to ensure R and S are in the range [1, N-1] since + // valid ECDSA signatures are required to be in that range per spec. + // + // Also note that while the overflow checks are required to make use of the + // specialized mod N scalar type, rejecting zero here is not strictly + // required because it is also checked when verifying the signature, but + // there really isn't a good reason not to fail early here on signatures + // that do not conform to the ECDSA spec. + // + // Strip leading zeroes from R. + rBytes := sig[rOffset : rOffset+rLen] + for len(rBytes) > 0 && rBytes[0] == 0x00 { + rBytes = rBytes[1:] + } + // R must be in the range [1, N-1]. Notice the check for the maximum number + // of bytes is required because SetByteSlice truncates as noted in its + // comment so it could otherwise fail to detect the overflow. + var r secp256k1.ModNScalar + if len(rBytes) > 32 { + str := "invalid signature: R is larger than 256 bits" + return nil, signatureError(ErrSigRTooBig, str) + } + if overflow := r.SetByteSlice(rBytes); overflow { + str := "invalid signature: R >= group order" + return nil, signatureError(ErrSigRTooBig, str) + } + if r.IsZero() { + str := "invalid signature: R is 0" + return nil, signatureError(ErrSigRIsZero, str) + } + // Strip leading zeroes from S. + sBytes := sig[sOffset : sOffset+sLen] + for len(sBytes) > 0 && sBytes[0] == 0x00 { + sBytes = sBytes[1:] + } + // S must be in the range [1, N-1]. Notice the check for the maximum number + // of bytes is required because SetByteSlice truncates as noted in its + // comment so it could otherwise fail to detect the overflow. + var s secp256k1.ModNScalar + if len(sBytes) > 32 { + str := "invalid signature: S is larger than 256 bits" + return nil, signatureError(ErrSigSTooBig, str) + } + if overflow := s.SetByteSlice(sBytes); overflow { + str := "invalid signature: S >= group order" + return nil, signatureError(ErrSigSTooBig, str) + } + if s.IsZero() { + str := "invalid signature: S is 0" + return nil, signatureError(ErrSigSIsZero, str) + } + // Create and return the signature. + return NewSignature(&r, &s), nil +} + +// sign generates an ECDSA signature over the secp256k1 curve for the provided +// hash (which should be the result of hashing a larger message) using the given +// nonce and secret key and returns it along with an additional public key +// recovery code and success indicator. Upon success, the produced signature is +// deterministic (same message, nonce, and key yield the same signature) and +// canonical in accordance with BIP0062. +// +// Note that signRFC6979 makes use of this function as it is the primary ECDSA +// signing logic. It differs in that it accepts a nonce to use when signing and +// may not successfully produce a valid signature for the given nonce. It is +// primarily separated for testing purposes. +func sign(secKey, nonce *secp256k1.ModNScalar, hash []byte) ( + *Signature, byte, + bool, +) { + // The algorithm for producing an ECDSA signature is given as algorithm 4.29 + // in [GECC]. + // + // The following is a paraphrased version for reference: + // + // G = curve generator + // N = curve order + // d = secret key + // m = message + // r, s = signature + // + // 1. Select random nonce k in [1, N-1] + // 2. Compute kG + // 3. r = kG.x mod N (kG.x is the x coordinate of the point kG) + // Repeat from step 1 if r = 0 + // 4. e = H(m) + // 5. s = k^-1(e + dr) mod N + // Repeat from step 1 if s = 0 + // 6. Return (r,s) + // + // This is slightly modified here to conform to RFC6979 and BIP 62 as + // follows: + // + // A. Instead of selecting a random nonce in step 1, use RFC6979 to generate + // a deterministic nonce in [1, N-1] parameterized by the secret key, + // message being signed, and an iteration count for the repeat cases + // B. Negate s calculated in step 5 if it is > N/2 + // This is done because both s and its negation are valid signatures + // modulo the curve order N, so it forces a consistent choice to reduce + // signature malleability + // + // NOTE: Step 1 is performed by the caller. + // + // Step 2. + // + // Compute kG + // + // Note that the point must be in affine coordinates. + k := nonce + var kG secp256k1.JacobianPoint + secp256k1.ScalarBaseMultNonConst(k, &kG) + kG.ToAffine() + // Step 3. + // + // r = kG.x mod N + // Repeat from step 1 if r = 0 + r, overflow := fieldToModNScalar(&kG.X) + if r.IsZero() { + return nil, 0, false + } + // Since the secp256k1 curve has a cofactor of 1, when recovering a + // public key from an ECDSA signature over it, there are four possible + // candidates corresponding to the following cases: + // + // 1) The X coord of the random point is < N and its Y coord even + // 2) The X coord of the random point is < N and its Y coord is odd + // 3) The X coord of the random point is >= N and its Y coord is even + // 4) The X coord of the random point is >= N and its Y coord is odd + // + // Rather than forcing the recovery procedure to check all possible + // cases, this creates a recovery code that uniquely identifies which of + // the cases apply by making use of 2 bits. Bit 0 identifies the + // oddness case and Bit 1 identifies the overflow case (aka when the X + // coord >= N). + // + // It is also worth noting that making use of Hasse's theorem shows + // there are around log_2((p-n)/p) ~= -127.65 ~= 1 in 2^127 points where + // the X coordinate is >= N. It is not possible to calculate these + // points since that would require breaking the ECDLP, but, in practice + // this strongly implies with extremely high probability that there are + // only a few actual points for which this case is true. + pubKeyRecoveryCode := byte(overflow<<1) | byte(kG.Y.IsOddBit()) + // Step 4. + // + // e = H(m) + // + // Note that this actually sets e = H(m) mod N which is correct since + // it is only used in step 5 which itself is mod N. + var e secp256k1.ModNScalar + e.SetByteSlice(hash) + // Step 5 with modification B. + // + // s = k^-1(e + dr) mod N + // Repeat from step 1 if s = 0 + // s = -s if s > N/2 + kinv := new(secp256k1.ModNScalar).InverseValNonConst(k) + s := new(secp256k1.ModNScalar).Mul2(secKey, &r).Add(&e).Mul(kinv) + if s.IsZero() { + return nil, 0, false + } + if s.IsOverHalfOrder() { + s.Negate() + // Negating s corresponds to the random point that would have been + // generated by -k (mod N), which necessarily has the opposite + // oddness since N is prime, thus flip the pubkey recovery code + // oddness bit accordingly. + pubKeyRecoveryCode ^= 0x01 + } + // Step 6. + // + // Return (r,s) + return NewSignature(&r, s), pubKeyRecoveryCode, true +} + +// signRFC6979 generates a deterministic ECDSA signature according to RFC 6979 +// and BIP0062 and returns it along with an additional public key recovery code +// for efficiently recovering the public key from the signature. +func signRFC6979(secKey *secp256k1.SecretKey, hash []byte) ( + *Signature, + byte, +) { + // The algorithm for producing an ECDSA signature is given as algorithm 4.29 + // in [GECC]. + // + // The following is a paraphrased version for reference: + // + // G = curve generator + // N = curve order + // d = secret key + // m = message + // r, s = signature + // + // 1. Select random nonce k in [1, N-1] + // 2. Compute kG + // 3. r = kG.x mod N (kG.x is the x coordinate of the point kG) + // Repeat from step 1 if r = 0 + // 4. e = H(m) + // 5. s = k^-1(e + dr) mod N + // Repeat from step 1 if s = 0 + // 6. Return (r,s) + // + // This is slightly modified here to conform to RFC6979 and BIP 62 as + // follows: + // + // A. Instead of selecting a random nonce in step 1, use RFC6979 to generate + // a deterministic nonce in [1, N-1] parameterized by the secret key, + // message being signed, and an iteration count for the repeat cases + // B. Negate s calculated in step 5 if it is > N/2 + // This is done because both s and its negation are valid signatures + // modulo the curve order N, so it forces a consistent choice to reduce + // signature malleability + secKeyScalar := &secKey.Key + var secKeyBytes [32]byte + secKeyScalar.PutBytes(&secKeyBytes) + defer zeroArray32(&secKeyBytes) + for iteration := uint32(0); ; iteration++ { + // Step 1 with modification A. + // + // Generate a deterministic nonce in [1, N-1] parameterized by the + // secret key, message being signed, and iteration count. + k := secp256k1.NonceRFC6979(secKeyBytes[:], hash, nil, nil, iteration) + // Steps 2-6. + sig, pubKeyRecoveryCode, success := sign(secKeyScalar, k, hash) + k.Zero() + if !success { + continue + } + return sig, pubKeyRecoveryCode + } +} + +// Sign generates an ECDSA signature over the secp256k1 curve for the provided +// hash (which should be the result of hashing a larger message) using the given +// secret key. The produced signature is deterministic (same message and same +// key yield the same signature) and canonical in accordance with RFC6979 and +// BIP0062. +func Sign(key *secp256k1.SecretKey, hash []byte) *Signature { + signature, _ := signRFC6979(key, hash) + return signature +} + +const ( + // compactSigSize is the size of a compact signature. It consists of a + // compact signature recovery code byte followed by the R and S components + // serialized as 32-byte big-endian values. 1+32*2 = 65. + // for the R and S components. 1+32+32=65. + compactSigSize = 65 + // compactSigMagicOffset is a value used when creating the compact signature + // recovery code inherited from Bitcoin and has no meaning, but has been + // retained for compatibility. For historical purposes, it was originally + // picked to avoid a binary representation that would allow compact + // signatures to be mistaken for other components. + compactSigMagicOffset = 27 + // compactSigCompPubKey is a value used when creating the compact signature + // recovery code to indicate the original public key was compressed. + compactSigCompPubKey = 4 + // pubKeyRecoveryCodeOddnessBit specifies the bit that indicates the oddess + // of the Y coordinate of the random point calculated when creating a + // signature. + pubKeyRecoveryCodeOddnessBit = 1 << 0 + // pubKeyRecoveryCodeOverflowBit specifies the bit that indicates the X + // coordinate of the random point calculated when creating a signature was + // >= N, where N is the order of the group. + pubKeyRecoveryCodeOverflowBit = 1 << 1 +) + +// SignCompact produces a compact ECDSA signature over the secp256k1 curve for +// the provided hash (which should be the result of hashing a larger message) +// using the given secret key. The isCompressedKey parameter specifies if the +// produced signature should reference a compressed public key or not. +// +// Compact signature format: +// <1-byte compact sig recovery code><32-byte R><32-byte S> +// +// The compact sig recovery code is the value 27 + public key recovery code + 4 +// if the compact signature was created with a compressed public key. +func SignCompact( + key *secp256k1.SecretKey, hash []byte, + isCompressedKey bool, +) []byte { + // Create the signature and associated pubkey recovery code and calculate + // the compact signature recovery code. + sig, pubKeyRecoveryCode := signRFC6979(key, hash) + compactSigRecoveryCode := compactSigMagicOffset + pubKeyRecoveryCode + if isCompressedKey { + compactSigRecoveryCode += compactSigCompPubKey + } + // Output <32-byte R><32-byte S>. + var b [compactSigSize]byte + b[0] = compactSigRecoveryCode + sig.r.PutBytesUnchecked(b[1:33]) + sig.s.PutBytesUnchecked(b[33:65]) + return b[:] +} + +// RecoverCompact attempts to recover the secp256k1 public key from the provided +// compact signature and message hash. It first verifies the signature, and, if +// the signature matches then the recovered public key will be returned as well +// as a boolean indicating whether or not the original key was compressed. +func RecoverCompact(signature, hash []byte) ( + *secp256k1.PublicKey, bool, error, +) { + // The following is very loosely based on the information and algorithm that + // describes recovering a public key from and ECDSA signature in section + // 4.1.6 of [SEC1]. + // + // Given the following parameters: + // + // G = curve generator + // N = group order + // P = field prime + // Q = public key + // m = message + // e = hash of the message + // r, s = signature + // X = random point used when creating signature whose x coordinate is r + // + // The equation to recover a public key candidate from an ECDSA signature + // is: + // Q = r^-1(sX - eG). + // + // This can be verified by plugging it in for Q in the sig verification + // equation: + // X = s^-1(eG + rQ) (mod N) + // => s^-1(eG + r(r^-1(sX - eG))) (mod N) + // => s^-1(eG + sX - eG) (mod N) + // => s^-1(sX) (mod N) + // => X (mod N) + // + // However, note that since r is the x coordinate mod N from a random point + // that was originally mod P, and the cofactor of the secp256k1 curve is 1, + // there are four possible points that the original random point could have + // been to produce r: (r,y), (r,-y), (r+N,y), and (r+N,-y). At least 2 of + // those points will successfully verify, and all 4 will successfully verify + // when the original x coordinate was in the range [N+1, P-1], but in any + // case, only one of them corresponds to the original secret key used. + // + // The method described by section 4.1.6 of [SEC1] to determine which one is + // the correct one involves calculating each possibility as a candidate + // public key and comparing the candidate to the authentic public key. It + // also hints that it is possible to generate the signature in a such a + // way that only one of the candidate public keys is viable. + // + // A more efficient approach that is specific to the secp256k1 curve is used + // here instead which is to produce a "pubkey recovery code" when signing + // that uniquely identifies which of the 4 possibilities is correct for the + // original random point and using that to recover the pubkey directly as + // follows: + // + // 1. Fail if r and s are not in [1, N-1] + // 2. Convert r to integer mod P + // 3. If pubkey recovery code overflow bit is set: + // 3.1 Fail if r + N >= P + // 3.2 r = r + N (mod P) + // 4. y = +sqrt(r^3 + 7) (mod P) + // 4.1 Fail if y does not exist + // 4.2 y = -y if needed to match pubkey recovery code oddness bit + // 5. X = (r, y) + // 6. e = H(m) mod N + // 7. w = r^-1 mod N + // 8. u1 = -(e * w) mod N + // u2 = s * w mod N + // 9. Q = u1G + u2X + // 10. Fail if Q is the point at infinity + // + // A compact signature consists of a recovery byte followed by the R and + // S components serialized as 32-byte big-endian values. + if len(signature) != compactSigSize { + str := fmt.Sprintf( + "malformed signature: wrong size: %d != %d", + len(signature), compactSigSize, + ) + return nil, false, signatureError(ErrSigInvalidLen, str) + } + // Parse and validate the compact signature recovery code. + const ( + minValidCode = compactSigMagicOffset + maxValidCode = compactSigMagicOffset + compactSigCompPubKey + 3 + ) + sigRecoveryCode := signature[0] + if sigRecoveryCode < minValidCode || sigRecoveryCode > maxValidCode { + str := fmt.Sprintf( + "invalid signature: public key recovery code %d is "+ + "not in the valid range [%d, %d]", sigRecoveryCode, + minValidCode, + maxValidCode, + ) + return nil, false, signatureError(ErrSigInvalidRecoveryCode, str) + } + sigRecoveryCode -= compactSigMagicOffset + wasCompressed := sigRecoveryCode&compactSigCompPubKey != 0 + pubKeyRecoveryCode := sigRecoveryCode & 3 + // Step 1. + // + // Parse and validate the R and S signature components. + // + // Fail if r and s are not in [1, N-1]. + var r, s secp256k1.ModNScalar + if overflow := r.SetByteSlice(signature[1:33]); overflow { + str := "invalid signature: R >= group order" + return nil, false, signatureError(ErrSigRTooBig, str) + } + if r.IsZero() { + str := "invalid signature: R is 0" + return nil, false, signatureError(ErrSigRIsZero, str) + } + if overflow := s.SetByteSlice(signature[33:]); overflow { + str := "invalid signature: S >= group order" + return nil, false, signatureError(ErrSigSTooBig, str) + } + if s.IsZero() { + str := "invalid signature: S is 0" + return nil, false, signatureError(ErrSigSIsZero, str) + } + // Step 2. + // + // Convert r to integer mod P. + fieldR := modNScalarToField(&r) + // Step 3. + // + // If pubkey recovery code overflow bit is set: + if pubKeyRecoveryCode&pubKeyRecoveryCodeOverflowBit != 0 { + // Step 3.1. + // + // Fail if r + N >= P + // + // Either the signature or the recovery code must be invalid if the + // recovery code overflow bit is set and adding N to the R component + // would exceed the field prime since R originally came from the X + // coordinate of a random point on the curve. + if fieldR.IsGtOrEqPrimeMinusOrder() { + str := "invalid signature: signature R + N >= P" + return nil, false, signatureError(ErrSigOverflowsPrime, str) + } + // Step 3.2. + // + // r = r + N (mod P) + fieldR.Add(&orderAsFieldVal) + } + // Step 4. + // + // y = +sqrt(r^3 + 7) (mod P) + // Fail if y does not exist. + // y = -y if needed to match pubkey recovery code oddness bit + // + // The signature must be invalid if the calculation fails because the X + // coord originally came from a random point on the curve which means there + // must be a Y coord that satisfies the equation for a valid signature. + oddY := pubKeyRecoveryCode&pubKeyRecoveryCodeOddnessBit != 0 + var y secp256k1.FieldVal + if valid := secp256k1.DecompressY(&fieldR, oddY, &y); !valid { + str := "invalid signature: not for a valid curve point" + return nil, false, signatureError(ErrPointNotOnCurve, str) + } + // Step 5. + // + // X = (r, y) + var X secp256k1.JacobianPoint + X.X.Set(fieldR.Normalize()) + X.Y.Set(y.Normalize()) + X.Z.SetInt(1) + // Step 6. + // + // e = H(m) mod N + var e secp256k1.ModNScalar + e.SetByteSlice(hash) + // Step 7. + // + // w = r^-1 mod N + w := new(secp256k1.ModNScalar).InverseValNonConst(&r) + // Step 8. + // + // u1 = -(e * w) mod N + // u2 = s * w mod N + u1 := new(secp256k1.ModNScalar).Mul2(&e, w).Negate() + u2 := new(secp256k1.ModNScalar).Mul2(&s, w) + // Step 9. + // + // Q = u1G + u2X + var Q, u1G, u2X secp256k1.JacobianPoint + secp256k1.ScalarBaseMultNonConst(u1, &u1G) + secp256k1.ScalarMultNonConst(u2, &X, &u2X) + secp256k1.AddNonConst(&u1G, &u2X, &Q) + // Step 10. + // + // Fail if Q is the point at infinity. + // + // Either the signature or the pubkey recovery code must be invalid if the + // recovered pubkey is the point at infinity. + if (Q.X.IsZero() && Q.Y.IsZero()) || Q.Z.IsZero() { + str := "invalid signature: recovered pubkey is the point at infinity" + return nil, false, signatureError(ErrPointNotOnCurve, str) + } + // Notice that the public key is in affine coordinates. + Q.ToAffine() + pubKey := secp256k1.NewPublicKey(&Q.X, &Q.Y) + return pubKey, wasCompressed, nil +} diff --git a/pkg/crypto/ec/ecdsa/signature_test.go b/pkg/crypto/ec/ecdsa/signature_test.go new file mode 100644 index 0000000..5e00d74 --- /dev/null +++ b/pkg/crypto/ec/ecdsa/signature_test.go @@ -0,0 +1,1146 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// TODO: change this test to use sha256 (tests are still valid only the +// generated hash differs, of course) + +package ecdsa + +import ( + "errors" + "math/rand" + "testing" + "time" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/encoders/hex" + "next.orly.dev/pkg/utils" +) + +// hexToBytes converts the passed hex string into bytes 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 hexToBytes(s string) []byte { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} + +// TestSignatureParsing ensures that signatures are properly parsed according +// to DER rules. The error paths are tested as well. +func TestSignatureParsing(t *testing.T) { + tests := []struct { + name string + sig []byte + err error + }{ + { + // signature from Decred blockchain tx + // 76634e947f49dfc6228c3e8a09cd3e9e15893439fc06df7df0fc6f08d659856c:0 + name: "valid signature 1", + sig: hexToBytes( + "3045022100cd496f2ab4fe124f977ffe3caa09f7576d8a34156" + + "b4e55d326b4dffc0399a094022013500a0510b5094bff220c74656879b8ca03" + + "69d3da78004004c970790862fc03", + ), + err: nil, + }, { + // signature from Decred blockchain tx + // 76634e947f49dfc6228c3e8a09cd3e9e15893439fc06df7df0fc6f08d659856c:1 + name: "valid signature 2", + sig: hexToBytes( + "3044022036334e598e51879d10bf9ce3171666bc2d1bbba6164" + + "cf46dd1d882896ba35d5d022056c39af9ea265c1b6d7eab5bc977f06f81e35c" + + "dcac16f3ec0fd218e30f2bad2a", + ), + err: nil, + }, { + name: "empty", + sig: nil, + err: ErrSigTooShort, + }, { + name: "too short", + sig: hexToBytes("30050201000200"), + err: ErrSigTooShort, + }, { + name: "too long", + sig: hexToBytes( + "3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" + + "90556b5140c63e0ef8481352480101", + ), + err: ErrSigTooLong, + }, { + name: "bad ASN.1 sequence id", + sig: hexToBytes( + "3145022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" + + "90556b5140c63e0ef848135248", + ), + err: ErrSigInvalidSeqID, + }, { + name: "mismatched data length (short one byte)", + sig: hexToBytes( + "3044022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" + + "90556b5140c63e0ef848135248", + ), + err: ErrSigInvalidDataLen, + }, { + name: "mismatched data length (long one byte)", + sig: hexToBytes( + "3046022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" + + "90556b5140c63e0ef848135248", + ), + err: ErrSigInvalidDataLen, + }, { + name: "bad R ASN.1 int marker", + sig: hexToBytes( + "304403204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" + + "24c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56c" + + "bbac4622082221a8768d1d09", + ), + err: ErrSigInvalidRIntID, + }, { + name: "zero R length", + sig: hexToBytes( + "30240200022030e09575e7a1541aa018876a4003cefe1b061a90" + + "556b5140c63e0ef848135248", + ), + err: ErrSigZeroRLen, + }, { + name: "negative R (too little padding)", + sig: hexToBytes( + "30440220b2ec8d34d473c3aa2ab5eb7cc4a0783977e5db8c8daf" + + "777e0b6d7bfa6b6623f302207df6f09af2c40460da2c2c5778f636d3b2e27e20" + + "d10d90f5a5afb45231454700", + ), + err: ErrSigNegativeR, + }, { + name: "too much R padding", + sig: hexToBytes( + "304402200077f6e93de5ed43cf1dfddaa79fca4b766e1a8fc879" + + "b0333d377f62538d7eb5022054fed940d227ed06d6ef08f320976503848ed1f5" + + "2d0dd6d17f80c9c160b01d86", + ), + err: ErrSigTooMuchRPadding, + }, { + name: "bad S ASN.1 int marker", + sig: hexToBytes( + "3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074032030e09575e7a1541aa018876a4003cefe1b061a" + + "90556b5140c63e0ef848135248", + ), + err: ErrSigInvalidSIntID, + }, { + name: "missing S ASN.1 int marker", + sig: hexToBytes( + "3023022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074", + ), + err: ErrSigMissingSTypeID, + }, { + name: "S length missing", + sig: hexToBytes( + "3024022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef07402", + ), + err: ErrSigMissingSLen, + }, { + name: "invalid S length (short one byte)", + sig: hexToBytes( + "3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074021f30e09575e7a1541aa018876a4003cefe1b061a" + + "90556b5140c63e0ef848135248", + ), + err: ErrSigInvalidSLen, + }, { + name: "invalid S length (long one byte)", + sig: hexToBytes( + "3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef074022130e09575e7a1541aa018876a4003cefe1b061a" + + "90556b5140c63e0ef848135248", + ), + err: ErrSigInvalidSLen, + }, { + name: "zero S length", + sig: hexToBytes( + "3025022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" + + "49f2a1ac584fd546bef0740200", + ), + err: ErrSigZeroSLen, + }, { + name: "negative S (too little padding)", + sig: hexToBytes( + "304402204fc10344934662ca0a93a84d14d650d8a21cf2ab91f6" + + "08e8783d2999c955443202208441aacd6b17038ff3f6700b042934f9a6fea0ce" + + "c2051b51dc709e52a5bb7d61", + ), + err: ErrSigNegativeS, + }, { + name: "too much S padding", + sig: hexToBytes( + "304402206ad2fdaf8caba0f2cb2484e61b81ced77474b4c2aa06" + + "9c852df1351b3314fe20022000695ad175b09a4a41cd9433f6b2e8e83253d6a7" + + "402096ba313a7be1f086dde5", + ), + err: ErrSigTooMuchSPadding, + }, { + name: "R == 0", + sig: hexToBytes( + "30250201000220181522ec8eca07de4860a4acdd12909d831cc5" + + "6cbbac4622082221a8768d1d09", + ), + err: ErrSigRIsZero, + }, { + name: "R == N", + sig: hexToBytes( + "3045022100fffffffffffffffffffffffffffffffebaaedce6af" + + "48a03bbfd25e8cd03641410220181522ec8eca07de4860a4acdd12909d831cc5" + + "6cbbac4622082221a8768d1d09", + ), + err: ErrSigRTooBig, + }, { + name: "R > N (>32 bytes)", + sig: hexToBytes( + "3045022101cd496f2ab4fe124f977ffe3caa09f756283910fc1a" + + "96f60ee6873e88d3cfe1d50220181522ec8eca07de4860a4acdd12909d831cc5" + + "6cbbac4622082221a8768d1d09", + ), + err: ErrSigRTooBig, + }, { + name: "R > N", + sig: hexToBytes( + "3045022100fffffffffffffffffffffffffffffffebaaedce6af" + + "48a03bbfd25e8cd03641420220181522ec8eca07de4860a4acdd12909d831cc5" + + "6cbbac4622082221a8768d1d09", + ), + err: ErrSigRTooBig, + }, { + name: "S == 0", + sig: hexToBytes( + "302502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" + + "24c6c61548ab5fb8cd41020100", + ), + err: ErrSigSIsZero, + }, { + name: "S == N", + sig: hexToBytes( + "304502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" + + "24c6c61548ab5fb8cd41022100fffffffffffffffffffffffffffffffebaaedc" + + "e6af48a03bbfd25e8cd0364141", + ), + err: ErrSigSTooBig, + }, { + name: "S > N (>32 bytes)", + sig: hexToBytes( + "304502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" + + "24c6c61548ab5fb8cd4102210113500a0510b5094bff220c74656879b784b246" + + "ba89c0a07bc49bcf05d8993d44", + ), + err: ErrSigSTooBig, + }, { + name: "S > N", + sig: hexToBytes( + "304502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" + + "24c6c61548ab5fb8cd41022100fffffffffffffffffffffffffffffffebaaedc" + + "e6af48a03bbfd25e8cd0364142", + ), + err: ErrSigSTooBig, + }, + } + for _, test := range tests { + _, err := ParseDERSignature(test.sig) + if !errors.Is(err, test.err) { + t.Errorf( + "%s mismatched err -- got %v, want %v", test.name, err, + test.err, + ) + continue + } + } +} + +// TestSignatureSerialize ensures that serializing signatures works as expected. +func TestSignatureSerialize(t *testing.T) { + tests := []struct { + name string + ecsig *Signature + expected []byte + }{ + { + // signature from bitcoin blockchain tx + // 0437cd7f8525ceed2324359c2d0ba26006d92d85 + "valid 1 - r and s most significant bits are zero", + &Signature{ + r: *hexToModNScalar("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), + s: *hexToModNScalar("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), + }, + hexToBytes( + "304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d62" + + "4c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc" + + "56cbbac4622082221a8768d1d09", + ), + }, { + // signature from bitcoin blockchain tx + // cb00f8a0573b18faa8c4f467b049f5d202bf1101d9ef2633bc611be70376a4b4 + "valid 2 - r most significant bit is one", + &Signature{ + r: *hexToModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + s: *hexToModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + }, + hexToBytes( + "304502210082235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c" + + "30a23b0afbb8d178abcf3022024bf68e256c534ddfaf966bf908deb94430" + + "5596f7bdcc38d69acad7f9c868724", + ), + }, { + // signature from bitcoin blockchain tx + // fda204502a3345e08afd6af27377c052e77f1fefeaeb31bdd45f1e1237ca5470 + // + // Note that signatures with an S component that is > half the group + // order are neither allowed nor produced in Decred, so this has been + // 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"), + }, + hexToBytes( + "304402201cadddc2838598fee7dc35a12b340c6bde8b389f7bfd1" + + "9a1252a17c4b5ed2d7102203e5dae44134eb4fa757428809a2178199e66f" + + "38daa53df51eaa380cab4222b95", + ), + }, { + "zero signature", + &Signature{ + r: *new(secp256k1.ModNScalar).SetInt(0), + s: *new(secp256k1.ModNScalar).SetInt(0), + }, + hexToBytes("3006020100020100"), + }, + } + for i, test := range tests { + result := test.ecsig.Serialize() + if !utils.FastEqual(result, test.expected) { + t.Errorf( + "Serialize #%d (%s) unexpected result:\n"+ + "got: %x\nwant: %x", i, test.name, result, + test.expected, + ) + } + } +} + +// signTest describes tests for producing and verifying ECDSA signatures for a +// selected set of secret keys, messages, and nonces that have been verified +// independently with the Sage computer algebra system. It is defined +// separately since it is intended for use in both normal and compact signature +// tests. +type signTest struct { + name string // test description + key string // hex encoded secret key + msg string // hex encoded message to sign before hashing + hash string // hex encoded hash of the message to sign + nonce string // hex encoded nonce to use in the signature calculation + rfc6979 bool // whether the nonce is an RFC6979 nonce + wantSigR string // hex encoded expected signature R + wantSigS string // hex encoded expected signature S + wantCode byte // expected public key recovery code +} + +// // signTests returns several tests for ECDSA signatures that use a selected set +// // of secret keys, messages, and nonces that have been verified independently +// // with the Sage computer algebra system. It is defined here versus inside a +// // specific test function scope so it can be shared for both normal and compact +// // signature tests. +// func signTests(t *testing.T) []signTest { +// t.Helper() +// +// tests := []signTest{{ +// name: "key 0x1, blake256(0x01020304), rfc6979 nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000001", +// msg: "01020304", +// hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", +// nonce: "4154324ecd4158938f1df8b5b659aeb639c7fbc36005934096e514af7d64bcc2", +// rfc6979: true, +// wantSigR: "c6c4137b0e5fbfc88ae3f293d7e80c8566c43ae20340075d44f75b009c943d09", +// wantSigS: "00ba213513572e35943d5acdd17215561b03f11663192a7252196cc8b2a99560", +// wantCode: 0, +// }, { +// name: "key 0x1, blake256(0x01020304), random nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000001", +// msg: "01020304", +// hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", +// nonce: "a6df66500afeb7711d4c8e2220960855d940a5ed57260d2c98fbf6066cca283e", +// rfc6979: false, +// wantSigR: "b073759a96a835b09b79e7b93c37fdbe48fb82b000c4a0e1404ba5d1fbc15d0a", +// wantSigS: "7e34928a3e3832ec21e7711644d9388f7deb6340ead661d7056b0665974b87f3", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "key 0x2, blake256(0x01020304), rfc6979 nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000002", +// msg: "01020304", +// hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", +// nonce: "55f96f24cf7531f527edfe3b9222eca12d575367c32a7f593a828dc3651acf49", +// rfc6979: true, +// wantSigR: "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59", +// wantSigS: "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "key 0x2, blake256(0x01020304), random nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000002", +// msg: "01020304", +// hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", +// nonce: "679a6d36e7fe6c02d7668af86d78186e8f9ccc04371ac1c8c37939d1f5cae07a", +// rfc6979: false, +// wantSigR: "4a090d82f48ca12d9e7aa24b5dcc187ee0db2920496f671d63e86036aaa7997e", +// wantSigS: "261ffe8ba45007fc5fbbba6b4c6ed41beafb48b09fa8af1d6a3fbc6ccefbad", +// wantCode: 0, +// }, { +// name: "key 0x1, blake256(0x0102030405), rfc6979 nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000001", +// msg: "0102030405", +// hash: "dc063eba3c8d52a159e725c1a161506f6cb6b53478ad5ef3f08d534efa871d9f", +// nonce: "aa87a543c68f2568bb107c9946afa5233bf94fb6a7a063544505282621021629", +// rfc6979: true, +// wantSigR: "dda8308cdbda2edf51ccf598b42b42b19597e102eb2ed4a04a16dd57084d3b40", +// wantSigS: "0b6d67bab4929624e28f690407a15efc551354544fdc179970ff401eec2e5dc9", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "key 0x1, blake256(0x0102030405), random nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000001", +// msg: "0102030405", +// hash: "dc063eba3c8d52a159e725c1a161506f6cb6b53478ad5ef3f08d534efa871d9f", +// nonce: "65f880c892fdb6e7f74f76b18c7c942cfd037ef9cf97c39c36e08bbc36b41616", +// rfc6979: false, +// wantSigR: "72e5666f4e9d1099447b825cf737ee32112f17a67e2ca7017ae098da31dfbb8b", +// wantSigS: "1a7326da661a62f66358dcf53300afdc8e8407939dae1192b5b0899b0254311b", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "key 0x2, blake256(0x0102030405), rfc6979 nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000002", +// msg: "0102030405", +// hash: "dc063eba3c8d52a159e725c1a161506f6cb6b53478ad5ef3f08d534efa871d9f", +// nonce: "a13d652abd54b6e862548e5d12716df14dc192d93f3fa13536fdf4e56c54f233", +// rfc6979: true, +// wantSigR: "122663fd29e41a132d3c8329cf05d61ebcca9351074cc277dcd868faba58d87d", +// wantSigS: "353a44f2d949c04981e4e4d9c1f93a9e0644e63a5eaa188288c5ad68fd288d40", +// wantCode: 0, +// }, { +// name: "key 0x2, blake256(0x0102030405), random nonce", +// key: "0000000000000000000000000000000000000000000000000000000000000002", +// msg: "0102030405", +// hash: "dc063eba3c8d52a159e725c1a161506f6cb6b53478ad5ef3f08d534efa871d9f", +// nonce: "026ece4cfb704733dd5eef7898e44c33bd5a0d749eb043f48705e40fa9e9afa0", +// rfc6979: false, +// wantSigR: "3c4c5a2f217ea758113fd4e89eb756314dfad101a300f48e5bd764d3b6e0f8bf", +// wantSigS: "6513e82442f133cb892514926ed9158328ead488ff1b027a31827603a65009df", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "random key 1, blake256(0x01), rfc6979 nonce", +// key: "a1becef2069444a9dc6331c3247e113c3ee142edda683db8643f9cb0af7cbe33", +// msg: "01", +// hash: "4a6c419a1e25c85327115c4ace586decddfe2990ed8f3d4d801871158338501d", +// nonce: "edb3a01063a0c6ccfc0d77295077cbd322cf364bfa64b7eeea3b20305135d444", +// rfc6979: true, +// wantSigR: "ef392791d87afca8256c4c9c68d981248ee34a09069f50fa8dfc19ae34cd92ce", +// wantSigS: "0a2b9cb69fd794f7f204c272293b8585a294916a21a11fd94ec04acae2dc6d21", +// wantCode: 0, +// }, { +// name: "random key 2, blake256(0x02), rfc6979 nonce", +// key: "59930b76d4b15767ec0e8c8e5812aa2e57db30c6af7963e2a6295ba02af5416b", +// msg: "02", +// hash: "49af37ab5270015fe25276ea5a3bb159d852943df23919522a202205fb7d175c", +// nonce: "af2a59085976494567ef0fc2ecede587b2d1d8e9898cc46e72d7f3e33156e057", +// rfc6979: true, +// wantSigR: "886c9cccb356b3e1deafef2c276a4f8717ab73c1244c3f673cfbff5897de0e06", +// wantSigS: "609394185495f978ae84b69be90c69947e5dd8dcb4726da604fcbd139d81fc55", +// wantCode: 0, +// }, { +// name: "random key 3, blake256(0x03), rfc6979 nonce", +// key: "c5b205c36bb7497d242e96ec19a2a4f086d8daa919135cf490d2b7c0230f0e91", +// msg: "03", +// hash: "b706d561742ad3671703c247eb927ee8a386369c79644131cdeb2c5c26bf6c5d", +// nonce: "82d82b696a386d6d7a111c4cb943bfd39de8e5f6195e7eed9d3edb40fe1419fa", +// rfc6979: true, +// wantSigR: "6589d5950cec1fe2e7e20593b5ffa3556de20c176720a1796aa77a0cec1ec5a7", +// wantSigS: "2a26deba3241de852e786f5b4e2b98d3efb958d91fe9773b331dbcca9e8be800", +// wantCode: 0, +// }, { +// name: "random key 4, blake256(0x04), rfc6979 nonce", +// key: "65b46d4eb001c649a86309286aaf94b18386effe62c2e1586d9b1898ccf0099b", +// msg: "04", +// hash: "4c6eb9e38415034f4c93d3304d10bef38bf0ad420eefd0f72f940f11c5857786", +// nonce: "7afd696a9e770961d2b2eaec77ab7c22c734886fa57bc4a50a9f1946168cd06f", +// rfc6979: true, +// wantSigR: "81db1d6dca08819ad936d3284a359091e57c036648d477b96af9d8326965a7d1", +// wantSigS: "1bdf719c4be69351ba7617a187ac246912101aea4b5a7d6dfc234478622b43c6", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "random key 5, blake256(0x05), rfc6979 nonce", +// key: "915cb9ba4675de06a182088b182abcf79fa8ac989328212c6b866fa3ec2338f9", +// msg: "05", +// hash: "bdd15db13448905791a70b68137445e607cca06cc71c7a58b9b2e84a06c54d08", +// nonce: "2a6ae70ea5cf1b932331901d640ece54551f5f33bf9484d5f95c676b5612b527", +// rfc6979: true, +// wantSigR: "47fd51aecbc743477cb59aa29d18d11d75fb206ae1cdd044216e4f294e33d5b6", +// wantSigS: "3d50edc03066584d50b8d19d681865a23960b37502ede5bf452bdca56744334a", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "random key 6, blake256(0x06), rfc6979 nonce", +// key: "93e9d81d818f08ba1f850c6dfb82256b035b42f7d43c1fe090804fb009aca441", +// msg: "06", +// hash: "19b7506ad9c189a9f8b063d2aee15953d335f5c88480f8515d7d848e7771c4ae", +// nonce: "0b847a0ae0cbe84dfca66621f04f04b0f2ec190dce10d43ba8c3915c0fcd90ed", +// rfc6979: true, +// wantSigR: "c99800bc7ac7ea11afe5d7a264f4c26edd63ae9c7ecd6d0d19992980bcda1d34", +// wantSigS: "2844d4c9020ddf9e96b86c1a04788e0f371bd562291fd17ee017db46259d04fb", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "random key 7, blake256(0x07), rfc6979 nonce", +// key: "c249bbd5f533672b7dcd514eb1256854783531c2b85fe60bf4ce6ea1f26afc2b", +// msg: "07", +// hash: "53d661e71e47a0a7e416591200175122d83f8af31be6a70af7417ad6f54d0038", +// nonce: "0f8e20694fe766d7b79e5ac141e3542f2f3c3d2cc6d0f60e0ec263a46dbe6d49", +// rfc6979: true, +// wantSigR: "7a57a5222fb7d615eaa0041193f682262cebfa9b448f9c519d3644d0a3348521", +// wantSigS: "574923b7b5aec66b62f1589002db29342c9f5ed56d5e80f5361c0307ff1561fa", +// wantCode: 0, +// }, { +// name: "random key 8, blake256(0x08), rfc6979 nonce", +// key: "ec0be92fcec66cf1f97b5c39f83dfd4ddcad0dad468d3685b5eec556c6290bcc", +// msg: "08", +// hash: "9bff7982eab6f7883322edf7bdc86a23c87ca1c07906fbb1584f57b197dc6253", +// nonce: "ab7df49257d18f5f1b730cc7448f46bd82eb43e6e220f521fa7d23802310e24d", +// rfc6979: true, +// wantSigR: "64f90b09c8b1763a3eeefd156e5d312f80a98c24017811c0163b1c0b01323668", +// wantSigS: "7d7bf4ff295ecfc9578eadc8378b0eea0c0362ad083b0fd1c9b3c06f4537f6ff", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }, { +// name: "random key 9, blake256(0x09), rfc6979 nonce", +// key: "6847b071a7cba6a85099b26a9c3e57a964e4990620e1e1c346fecc4472c4d834", +// msg: "09", +// hash: "4c2231813064f8500edae05b40195416bd543fd3e76c16d6efb10c816d92e8b6", +// nonce: "48ea6c907e1cda596048d812439ccf416eece9a7de400c8a0e40bd48eb7e613a", +// rfc6979: true, +// wantSigR: "81fc600775d3cdcaa14f8629537299b8226a0c8bfce9320ce64a8d14e3f95bae", +// wantSigS: "3607997d36b48bce957ae9b3d450e0969f6269554312a82bf9499efc8280ea6d", +// wantCode: 0, +// }, { +// name: "random key 10, blake256(0x0a), rfc6979 nonce", +// key: "b7548540f52fe20c161a0d623097f827608c56023f50442cc00cc50ad674f6b5", +// msg: "0a", +// hash: "e81db4f0d76e02805155441f50c861a8f86374f3ae34c7a3ff4111d3a634ecb1", +// nonce: "95c07e315cd5457e84270ca01019563c8eeaffb18ab4f23e88a44a0ff01c5f6f", +// rfc6979: true, +// wantSigR: "0d4cbf2da84f7448b083fce9b9c4e1834b5e2e98defcec7ec87e87c739f5fe78", +// wantSigS: "0997db60683e12b4494702347fc7ae7f599e5a95c629c146e0fc615a1a2acac5", +// wantCode: pubKeyRecoveryCodeOddnessBit, +// }} +// +// // Ensure the test data is sane by comparing the provided hashed message and +// // nonce, in the case RFC6979 was used, to their calculated values. These +// // values could just be calculated instead of specified in the test data, +// // but it's nice to have all of the calculated values available in the test +// // data for cross implementation testing and verification. +// for _, test := range tests { +// msg := hexToBytes(test.msg) +// hash := hexToBytes(test.hash) +// +// calcHash := blake256.Sum256(msg) +// if !equals(calcHash[:], hash) { +// t.Errorf("%s: mismatched test hash -- expected: %x, given: %x", +// test.name, calcHash[:], hash) +// continue +// } +// if test.rfc6979 { +// secKeyBytes := hexToBytes(test.key) +// nonceBytes := hexToBytes(test.nonce) +// calcNonce := secp256k1.NonceRFC6979(secKeyBytes, hash, nil, nil, 0) +// calcNonceBytes := calcNonce.Bytes() +// if !equals(calcNonceBytes[:], nonceBytes) { +// t.Errorf("%s: mismatched test nonce -- expected: %x, given: %x", +// test.name, calcNonceBytes, nonceBytes) +// continue +// } +// } +// } +// +// return tests +// } + +// // TestSignAndVerify ensures the ECDSA signing function produces the expected +// // signatures for a selected set of secret keys, messages, and nonces that have +// // been verified independently with the Sage computer algebra system. It also +// // ensures verifying the signature works as expected. +// func TestSignAndVerify(t *testing.T) { +// t.Parallel() +// +// tests := signTests(t) +// for _, test := range tests { +// secKey := secp256k1.NewSecretKey(hexToModNScalar(test.key)) +// hash := hexToBytes(test.hash) +// nonce := hexToModNScalar(test.nonce) +// wantSigR := hexToModNScalar(test.wantSigR) +// wantSigS := hexToModNScalar(test.wantSigS) +// wantSig := NewSignature(wantSigR, wantSigS).Serialize() +// +// // Sign the hash of the message with the given secret key and nonce. +// gotSig, recoveryCode, success := sign(&secKey.Key, nonce, hash) +// if !success { +// t.Errorf("%s: unexpected error when signing", test.name) +// continue +// } +// +// // Ensure the generated signature is the expected value. +// gotSigBytes := gotSig.Serialize() +// if !equals(gotSigBytes, wantSig) { +// t.Errorf("%s: unexpected signature -- got %x, want %x", test.name, +// gotSigBytes, wantSig) +// continue +// } +// +// // Ensure the generated public key recovery code is the expected value. +// if recoveryCode != test.wantCode { +// t.Errorf("%s: unexpected recovery code -- got %x, want %x", +// test.name, recoveryCode, test.wantCode) +// continue +// } +// +// // Ensure the produced signature verifies. +// pubKey := secKey.Pubkey() +// if !gotSig.Verify(hash, pubKey) { +// t.Errorf("%s: signature failed to verify", test.name) +// continue +// } +// +// // Ensure the signature generated by the exported method is the expected +// // value as well in the case RFC6979 was used. +// if test.rfc6979 { +// gotSig = Sign(secKey, hash) +// gotSigBytes := gotSig.Serialize() +// if !equals(gotSigBytes, wantSig) { +// t.Errorf("%s: unexpected signature -- got %x, want %x", +// test.name, gotSigBytes, wantSig) +// continue +// } +// } +// } +// } + +// TestSignAndVerifyRandom ensures ECDSA signing and verification work as +// expected for randomly-generated secret keys and messages. It also ensures +// invalid signatures are not improperly verified by mutating the valid +// signature and changing the message the signature covers. +func TestSignAndVerifyRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate a random secret key. + var buf [32]byte + if _, err := rng.Read(buf[:]); chk.T(err) { + t.Fatalf("failed to read random secret key: %v", err) + } + var secKeyScalar secp256k1.ModNScalar + secKeyScalar.SetBytes(&buf) + secKey := secp256k1.NewSecretKey(&secKeyScalar) + // Generate a random hash to sign. + var hash [32]byte + if _, err := rng.Read(hash[:]); chk.T(err) { + t.Fatalf("failed to read random hash: %v", err) + } + // Sign the hash with the secret key and then ensure the produced + // signature is valid for the hash and public key associated with the + // secret key. + sig := Sign(secKey, hash[:]) + pubKey := secKey.PubKey() + if !sig.Verify(hash[:], pubKey) { + t.Fatalf( + "failed to verify signature\nsig: %x\nhash: %x\n"+ + "secret key: %x\npublic key: %x", sig.Serialize(), hash, + secKey.Serialize(), pubKey.SerializeCompressed(), + ) + } + // Change a random bit in the signature and ensure the bad signature + // fails to verify the original message. + badSig := *sig + randByte := rng.Intn(32) + randBit := rng.Intn(7) + if randComponent := rng.Intn(2); randComponent == 0 { + badSigBytes := badSig.r.Bytes() + badSigBytes[randByte] ^= 1 << randBit + badSig.r.SetBytes(&badSigBytes) + } else { + badSigBytes := badSig.s.Bytes() + badSigBytes[randByte] ^= 1 << randBit + badSig.s.SetBytes(&badSigBytes) + } + if badSig.Verify(hash[:], pubKey) { + t.Fatalf( + "verified bad signature\nsig: %x\nhash: %x\n"+ + "secret key: %x\npublic key: %x", badSig.Serialize(), hash, + secKey.Serialize(), pubKey.SerializeCompressed(), + ) + } + // Change a random bit in the hash that was originally signed and ensure + // the original good signature fails to verify the new bad message. + badHash := make([]byte, len(hash)) + copy(badHash, hash[:]) + randByte = rng.Intn(len(badHash)) + randBit = rng.Intn(7) + badHash[randByte] ^= 1 << randBit + if sig.Verify(badHash[:], pubKey) { + t.Fatalf( + "verified signature for bad hash\nsig: %x\nhash: %x\n"+ + "pubkey: %x", sig.Serialize(), badHash, + pubKey.SerializeCompressed(), + ) + } + } +} + +// TestSignFailures ensures the internal ECDSA signing function returns an +// unsuccessful result when particular combinations of values are unable to +// produce a valid signature. +func TestSignFailures(t *testing.T) { + t.Parallel() + tests := []struct { + name string // test description + key string // hex encoded secret key + hash string // hex encoded hash of the message to sign + nonce string // hex encoded nonce to use in the signature calculation + }{ + { + name: "zero R is invalid (forced by using zero nonce)", + key: "0000000000000000000000000000000000000000000000000000000000000001", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + nonce: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "zero S is invalid (forced by key/hash/nonce choice)", + key: "0000000000000000000000000000000000000000000000000000000000000001", + hash: "393bec84f1a04037751c0d6c2817f37953eaa204ac0898de7adb038c33a20438", + nonce: "4154324ecd4158938f1df8b5b659aeb639c7fbc36005934096e514af7d64bcc2", + }, + } + for _, test := range tests { + secKey := hexToModNScalar(test.key) + hash := hexToBytes(test.hash) + nonce := hexToModNScalar(test.nonce) + // Ensure the signing is NOT successful. + sig, _, success := sign(secKey, nonce, hash[:]) + if success { + t.Errorf( + "%s: unexpected success -- got sig %x", test.name, + sig.Serialize(), + ) + continue + } + } +} + +// TestVerifyFailures ensures the ECDSA verification function returns an +// unsuccessful result for edge conditions. +func TestVerifyFailures(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + key string // hex encoded secret key + hash string // hex encoded hash of the message to sign + r, s string // hex encoded r and s components of signature to verify + }{ + { + name: "signature R is 0", + key: "0000000000000000000000000000000000000000000000000000000000000001", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + r: "0000000000000000000000000000000000000000000000000000000000000000", + s: "00ba213513572e35943d5acdd17215561b03f11663192a7252196cc8b2a99560", + }, { + name: "signature S is 0", + key: "0000000000000000000000000000000000000000000000000000000000000001", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + r: "c6c4137b0e5fbfc88ae3f293d7e80c8566c43ae20340075d44f75b009c943d09", + s: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "u1G + u2Q is the point at infinity", + key: "0000000000000000000000000000000000000000000000000000000000000001", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + r: "3cfe45621a29fac355260a14b9adc0fe43ac2f13e918fc9ddfa117e964b61a8a", + s: "00ba213513572e35943d5acdd17215561b03f11663192a7252196cc8b2a99560", + }, { + name: "signature R < P-N, but invalid", + key: "0000000000000000000000000000000000000000000000000000000000000001", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + r: "000000000000000000000000000000014551231950b75fc4402da1722fc9baed", + s: "00ba213513572e35943d5acdd17215561b03f11663192a7252196cc8b2a99560", + }, + } + for _, test := range tests { + secKey := hexToModNScalar(test.key) + hash := hexToBytes(test.hash) + r := hexToModNScalar(test.r) + s := hexToModNScalar(test.s) + sig := NewSignature(r, s) + // Ensure the verification is NOT successful. + pubKey := secp256k1.NewSecretKey(secKey).PubKey() + if sig.Verify(hash, pubKey) { + t.Errorf( + "%s: unexpected success for invalid signature: %x", + test.name, sig.Serialize(), + ) + continue + } + } +} + +// TestSignatureIsEqual ensures that equality testing between two signatures +// works as expected. +func TestSignatureIsEqual(t *testing.T) { + sig1 := &Signature{ + r: *hexToModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + s: *hexToModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + } + sig1Copy := &Signature{ + r: *hexToModNScalar("82235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + s: *hexToModNScalar("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + } + sig2 := &Signature{ + r: *hexToModNScalar("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), + s: *hexToModNScalar("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), + } + if !sig1.IsEqual(sig1) { + t.Fatalf("bad self signature equality check: %v == %v", sig1, sig1Copy) + } + if !sig1.IsEqual(sig1Copy) { + t.Fatalf("bad signature equality check: %v == %v", sig1, sig1Copy) + } + + if sig1.IsEqual(sig2) { + t.Fatalf("bad signature equality check: %v != %v", sig1, sig2) + } +} + +// // TestSignAndRecoverCompact ensures compact (recoverable public key) ECDSA +// // signing and public key recovery works as expected for a selected set of +// // secret keys, messages, and nonces that have been verified independently with +// // the Sage computer algebra system. +// func TestSignAndRecoverCompact(t *testing.T) { +// t.Parallel() +// +// tests := signTests(t) +// for _, test := range tests { +// // Skip tests using nonces that are not RFC6979. +// if !test.rfc6979 { +// continue +// } +// +// // Parse test data. +// secKey := secp256k1.NewSecretKey(hexToModNScalar(test.key)) +// pubKey := secKey.Pubkey() +// hash := hexToBytes(test.hash) +// wantSig := hexToBytes("00" + test.wantSigR + test.wantSigS) +// +// // Test compact signatures for both the compressed and uncompressed +// // versions of the public key. +// for _, compressed := range []bool{true, false} { +// // Populate the expected compact signature recovery code. +// wantRecoveryCode := compactSigMagicOffset + test.wantCode +// if compressed { +// wantRecoveryCode += compactSigCompPubKey +// } +// wantSig[0] = wantRecoveryCode +// +// // Sign the hash of the message with the given secret key and +// // ensure the generated signature is the expected value per the +// // specified compressed flag. +// gotSig := SignCompact(secKey, hash, compressed) +// if !equals(gotSig, wantSig) { +// t.Errorf("%s: unexpected signature -- got %x, want %x", +// test.name, gotSig, wantSig) +// continue +// } +// +// // Ensure the recovered public key and flag that indicates whether +// // or not the signature was for a compressed public key are the +// // expected values. +// gotPubKey, gotCompressed, err := RecoverCompact(gotSig, hash) +// if err != nil { +// t.Errorf("%s: unexpected error when recovering: %v", test.name, +// err) +// continue +// } +// if gotCompressed != compressed { +// t.Errorf("%s: unexpected compressed flag -- got %v, want %v", +// test.name, gotCompressed, compressed) +// continue +// } +// if !gotPubKey.IsEqual(pubKey) { +// t.Errorf("%s: unexpected public key -- got %x, want %x", +// test.name, gotPubKey.SerializeUncompressed(), +// pubKey.SerializeUncompressed()) +// continue +// } +// } +// } +// } + +// TestRecoverCompactErrors ensures several error paths in compact signature recovery are +// detected as expected. When possible, the signatures are otherwise valid, except the specific +// failure to ensure it's robust against things like fault attacks. +func TestRecoverCompactErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + sig string // hex encoded signature to recover pubkey from + hash string // hex encoded hash of message + err error // expected error + }{ + { + name: "empty signature", + sig: "", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigInvalidLen, + }, { + // Signature created from secret key 0x02, blake256(0x01020304). + name: "no compact sig recovery code (otherwise valid sig)", + sig: "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59" + + "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigInvalidLen, + }, { + // Signature created from secret key 0x02, blake256(0x01020304). + name: "signature one byte too long (S padded with leading zero)", + sig: "1f" + + "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59" + + "0044b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigInvalidLen, + }, { + // Signature created from secret key 0x02, blake256(0x01020304). + name: "compact sig recovery code too low (otherwise valid sig)", + sig: "1a" + + "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59" + + "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigInvalidRecoveryCode, + }, { + // Signature created from secret key 0x02, blake256(0x01020304). + name: "compact sig recovery code too high (otherwise valid sig)", + sig: "23" + + "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59" + + "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigInvalidRecoveryCode, + }, { + // Signature invented since finding a signature with an r value that is + // exactly the group order prior to the modular reduction is not + // calculable without breaking the underlying crypto. + name: "R == group order", + sig: "1f" + + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" + + "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigRTooBig, + }, { + // Signature invented since finding a signature with an r value that + // would be valid modulo the group order and is still 32 bytes is not + // calculable without breaking the underlying crypto. + name: "R > group order and still 32 bytes (order + 1)", + sig: "1f" + + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142" + + "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigRTooBig, + }, { + // Signature invented since the only way a signature could have an r + // value of zero is if the nonce were zero which is invalid. + name: "R == 0", + sig: "1f" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigRIsZero, + }, { + // Signature invented since finding a signature with an s value that is + // exactly the group order prior to the modular reduction is not + // calculable without breaking the underlying crypto. + name: "S == group order", + sig: "1f" + + "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59" + + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigSTooBig, + }, { + // Signature invented since finding a signature with an s value that + // would be valid modulo the group order and is still 32 bytes is not + // calculable without breaking the underlying crypto. + name: "S > group order and still 32 bytes (order + 1)", + sig: "1f" + + "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59" + + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigSTooBig, + }, { + // Signature created by forcing the key/hash/nonce choices such that s + // is zero and is therefore invalid. The signing code will not produce + // such a signature in practice. + name: "S == 0", + sig: "1f" + + "e6f137b52377250760cc702e19b7aee3c63b0e7d95a91939b14ab3b5c4771e59" + + "0000000000000000000000000000000000000000000000000000000000000000", + hash: "393bec84f1a04037751c0d6c2817f37953eaa204ac0898de7adb038c33a20438", + err: ErrSigSIsZero, + }, { + // Signature invented since finding a secret key needed to create a + // valid signature with an r value that is >= group order prior to the + // modular reduction is not possible without breaking the underlying + // crypto. + name: "R >= field prime minus group order with overflow bit", + sig: "21" + + "000000000000000000000000000000014551231950b75fc4402da1722fc9baee" + + "44b9bc4620afa158b7efdfea5234ff2d5f2f78b42886f02cf581827ee55318ea", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrSigOverflowsPrime, + }, { + // Signature created from secret key 0x01, blake256(0x0102030407) over + // the secp256r1 curve (note the r1 instead of k1). + name: "pubkey not on the curve, signature valid for secp256r1 instead", + sig: "1f" + + "2a81d1b3facc22185267d3f8832c5104902591bc471253f1cfc5eb25f4f740f2" + + "72e65d019f9b09d769149e2be0b55de9b0224d34095bddc6a5dba90bfda33c45", + hash: "9165e957708bc95cf62d020769c150b2d7b08e7ab7981860815b1eaabd41d695", + err: ErrPointNotOnCurve, + }, { + // Signature created from secret key 0x01, blake256(0x01020304) and + // manually setting s = -e*k^-1. + name: "calculated pubkey point at infinity", + sig: "1f" + + "c6c4137b0e5fbfc88ae3f293d7e80c8566c43ae20340075d44f75b009c943d09" + + "1281d8d90a5774045abd57b453c7eadbc830dbadec89ae8dd7639b9cc55641d0", + hash: "c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7", + err: ErrPointNotOnCurve, + }, + } + for _, test := range tests { + // Parse test data. + hash := hexToBytes(test.hash) + sig := hexToBytes(test.sig) + // Ensure the expected error is hit. + _, _, err := RecoverCompact(sig, hash) + if !errors.Is(err, test.err) { + t.Errorf( + "%s: mismatched err -- got %v, want %v", test.name, err, + test.err, + ) + continue + } + } +} + +// TestSignAndRecoverCompactRandom ensures compact (recoverable public key) +// ECDSA signing and recovery work as expected for randomly-generated secret +// keys and messages. It also ensures mutated signatures and messages do not +// improperly recover the original public key. +func TestSignAndRecoverCompactRandom(t *testing.T) { + t.Parallel() + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate a random secret key. + var buf [32]byte + if _, err := rng.Read(buf[:]); chk.T(err) { + t.Fatalf("failed to read random secret key: %v", err) + } + var secKeyScalar secp256k1.ModNScalar + secKeyScalar.SetBytes(&buf) + secKey := secp256k1.NewSecretKey(&secKeyScalar) + wantPubKey := secKey.PubKey() + // Generate a random hash to sign. + var hash [32]byte + if _, err := rng.Read(hash[:]); chk.T(err) { + t.Fatalf("failed to read random hash: %v", err) + } + // Test compact signatures for both the compressed and uncompressed + // versions of the public key. + for _, compressed := range []bool{true, false} { + // Sign the hash with the secret key and then ensure the original + // public key and compressed flag is recovered from the produced + // signature. + gotSig := SignCompact(secKey, hash[:], compressed) + + gotPubKey, gotCompressed, err := RecoverCompact(gotSig, hash[:]) + if err != nil { + t.Fatalf( + "unexpected err: %v\nsig: %x\nhash: %x\nsecret key: %x", + err, gotSig, hash, secKey.Serialize(), + ) + } + if gotCompressed != compressed { + t.Fatalf( + "unexpected compressed flag: %v\nsig: %x\nhash: %x\n"+ + "secret key: %x", gotCompressed, gotSig, hash, + secKey.Serialize(), + ) + } + if !gotPubKey.IsEqual(wantPubKey) { + t.Fatalf( + "unexpected recovered public key: %x\nsig: %x\nhash: "+ + "%x\nsecret key: %x", gotPubKey.SerializeUncompressed(), + gotSig, hash, secKey.Serialize(), + ) + } + // Change a random bit in the signature and ensure the bad signature + // fails to recover the original public key. + badSig := make([]byte, len(gotSig)) + copy(badSig, gotSig) + randByte := rng.Intn(len(badSig)-1) + 1 + randBit := rng.Intn(7) + badSig[randByte] ^= 1 << randBit + badPubKey, _, err := RecoverCompact(badSig, hash[:]) + if err == nil && badPubKey.IsEqual(wantPubKey) { + t.Fatalf( + "recovered public key for bad sig: %x\nhash: %x\n"+ + "secret key: %x", badSig, hash, secKey.Serialize(), + ) + } + // Change a random bit in the hash that was originally signed and + // ensure the original good signature fails to recover the original + // public key. + badHash := make([]byte, len(hash)) + copy(badHash, hash[:]) + randByte = rng.Intn(len(badHash)) + randBit = rng.Intn(7) + badHash[randByte] ^= 1 << randBit + badPubKey, _, err = RecoverCompact(gotSig, badHash[:]) + if err == nil && badPubKey.IsEqual(wantPubKey) { + t.Fatalf( + "recovered public key for bad hash: %x\nsig: %x\n"+ + "secret key: %x", badHash, gotSig, secKey.Serialize(), + ) + } + } + } +} diff --git a/pkg/crypto/ec/error.go b/pkg/crypto/ec/error.go new file mode 100644 index 0000000..2e25fda --- /dev/null +++ b/pkg/crypto/ec/error.go @@ -0,0 +1,24 @@ +// Copyright (c) 2013-2021 The btcsuite developers +// Copyright (c) 2015-2021 The Decred developers + +package btcec + +import ( + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// 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 +// caller can ascertain the specific reason for the error by checking the +// underlying error. +type Error = secp256k1.Error + +// 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 = secp256k1.ErrorKind + +// makeError creates an secp256k1.Error given a set of arguments. +func makeError(kind ErrorKind, desc string) Error { + return Error{Err: kind, Description: desc} +} diff --git a/pkg/crypto/ec/field.go b/pkg/crypto/ec/field.go new file mode 100644 index 0000000..c94652f --- /dev/null +++ b/pkg/crypto/ec/field.go @@ -0,0 +1,45 @@ +package btcec + +import ( + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// FieldVal implements optimized fixed-precision arithmetic over the secp256k1 +// finite field. This means all arithmetic is performed modulo +// '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'. +// +// WARNING: Since it is so important for the field arithmetic to be extremely +// fast for high performance crypto, this type does not perform any validation +// of documented preconditions where it ordinarily would. As a result, it is +// IMPERATIVE for callers to understand some key concepts that are described +// below and ensure the methods are called with the necessary preconditions +// that each method is documented with. For example, some methods only give the +// correct result if the field value is normalized and others require the field +// values involved to have a maximum magnitude and THERE ARE NO EXPLICIT CHECKS +// TO ENSURE THOSE PRECONDITIONS ARE SATISFIED. This does, unfortunately, make +// the type more difficult to use correctly and while I typically prefer to +// ensure all state and input is valid for most code, this is a bit of an +// exception because those extra checks really add up in what ends up being +// critical hot paths. +// +// The first key concept when working with this type is normalization. In order +// to avoid the need to propagate a ton of carries, the internal representation +// provides additional overflow bits for each word of the overall 256-bit +// value. This means that there are multiple internal representations for the +// same value and, as a result, any methods that rely on comparison of the +// value, such as equality and oddness determination, require the caller to +// provide a normalized value. +// +// The second key concept when working with this type is magnitude. As +// previously mentioned, the internal representation provides additional +// overflow bits which means that the more math operations that are performed +// on the field value between normalizations, the more those overflow bits +// accumulate. The magnitude is effectively that maximum possible number of +// those overflow bits that could possibly be required as a result of a given +// operation. Since there are only a limited number of overflow bits available, +// this implies that the max possible magnitude MUST be tracked by the caller +// and the caller MUST normalize the field value if a given operation would +// cause the magnitude of the result to exceed the max allowed value. +// +// IMPORTANT: The max allowed magnitude of a field value is 64. +type FieldVal = secp256k1.FieldVal diff --git a/pkg/crypto/ec/field_test.go b/pkg/crypto/ec/field_test.go new file mode 100644 index 0000000..c70fed1 --- /dev/null +++ b/pkg/crypto/ec/field_test.go @@ -0,0 +1,1196 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2016 Dave Collins +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "math/rand" + "testing" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/encoders/hex" +) + +// TestIsZero ensures that checking if a field IsZero works as expected. +func TestIsZero(t *testing.T) { + f := new(FieldVal) + if !f.IsZero() { + t.Errorf( + "new field value is not zero - got %v (rawints %x)", f, + f.String(), + ) + } + f.SetInt(1) + if f.IsZero() { + t.Errorf( + "field claims it's zero when it's not - got %v "+ + "(raw rawints %x)", f, f.String(), + ) + } + f.Zero() + if !f.IsZero() { + t.Errorf( + "field claims it's not zero when it is - got %v "+ + "(raw rawints %x)", f, f.String(), + ) + } +} + +// TestStringer ensures the stringer returns the appropriate hex string. +func TestStringer(t *testing.T) { + tests := []struct { + in string + expected string + }{ + { + "0", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + { + "1", + "0000000000000000000000000000000000000000000000000000000000000001", + }, + { + "a", + "000000000000000000000000000000000000000000000000000000000000000a", + }, + { + "b", + "000000000000000000000000000000000000000000000000000000000000000b", + }, + { + "c", + "000000000000000000000000000000000000000000000000000000000000000c", + }, + { + "d", + "000000000000000000000000000000000000000000000000000000000000000d", + }, + { + "e", + "000000000000000000000000000000000000000000000000000000000000000e", + }, + { + "f", + "000000000000000000000000000000000000000000000000000000000000000f", + }, + { + "f0", + "00000000000000000000000000000000000000000000000000000000000000f0", + }, + // 2^26-1 + { + "3ffffff", + "0000000000000000000000000000000000000000000000000000000003ffffff", + }, + // 2^32-1 + { + "ffffffff", + "00000000000000000000000000000000000000000000000000000000ffffffff", + }, + // 2^64-1 + { + "ffffffffffffffff", + "000000000000000000000000000000000000000000000000ffffffffffffffff", + }, + // 2^96-1 + { + "ffffffffffffffffffffffff", + "0000000000000000000000000000000000000000ffffffffffffffffffffffff", + }, + // 2^128-1 + { + "ffffffffffffffffffffffffffffffff", + "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + }, + // 2^160-1 + { + "ffffffffffffffffffffffffffffffffffffffff", + "000000000000000000000000ffffffffffffffffffffffffffffffffffffffff", + }, + // 2^192-1 + { + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + }, + // 2^224-1 + { + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + // 2^256-4294968273 (the btcec prime, so should result in 0) + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + // 2^256-4294968274 (the secp256k1 prime+1, so should result in 1) + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "0000000000000000000000000000000000000000000000000000000000000001", + }, + + // Invalid hex + { + "g", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + { + "1h", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + { + "i1", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + } + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := setHex(test.in) + result := f.String() + if result != test.expected { + t.Errorf( + "FieldVal.String #%d wrong result\ngot: %v\n"+ + "want: %v", i, result, test.expected, + ) + continue + } + } +} + +// TestNormalize ensures that normalizing the internal field words works as +// expected. +func TestNormalize(t *testing.T) { + tests := []struct { + raw [10]uint32 // Intentionally denormalized value + normalized [10]uint32 // Normalized form of the raw value + }{ + { + [10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^26 + { + [10]uint32{0x04000000, 0x0, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x1, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^26 + 1 + { + [10]uint32{0x04000001, 0x0, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000001, 0x1, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^32 - 1 + { + [10]uint32{0xffffffff, 0x00, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x03ffffff, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^32 + { + [10]uint32{0x04000000, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x40, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^32 + 1 + { + [10]uint32{0x04000001, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000001, 0x40, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^64 - 1 + { + [10]uint32{0xffffffff, 0xffffffc0, 0xfc0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x03ffffff, 0x03ffffff, 0xfff, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^64 + { + [10]uint32{0x04000000, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^64 + 1 + { + [10]uint32{0x04000001, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000001, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^96 - 1 + { + [10]uint32{ + 0xffffffff, 0xffffffc0, 0xffffffc0, 0x3ffc0, 0, 0, 0, 0, + 0, 0, + }, + [10]uint32{ + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, 0, 0, 0, + 0, 0, + }, + }, + // 2^96 + { + [10]uint32{ + 0x04000000, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, 0, 0, 0, + 0, 0, + }, + [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x40000, 0, 0, 0, 0, + 0, 0, + }, + }, + // 2^128 - 1 + { + [10]uint32{ + 0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffc0, + 0, 0, 0, 0, 0, + }, + [10]uint32{ + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0xffffff, + 0, 0, 0, 0, 0, + }, + }, + // 2^128 + { + [10]uint32{ + 0x04000000, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x0ffffff, 0, 0, 0, 0, 0, + }, + [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x1000000, 0, 0, 0, 0, 0, + }, + }, + // 2^256 - 4294968273 (secp256k1 prime) + { + [10]uint32{ + 0xfffffc2f, 0xffffff80, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0x3fffc0, + }, + [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x000000, + }, + }, + // Prime larger than P where both first and second words are larger + // than P's first and second words + { + [10]uint32{ + 0xfffffc30, 0xffffff86, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0x3fffc0, + }, + [10]uint32{ + 0x00000001, 0x00000006, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x000000, + }, + }, + // Prime larger than P where only the second word is larger + // than P's second words. + { + [10]uint32{ + 0xfffffc2a, 0xffffff87, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0x3fffc0, + }, + [10]uint32{ + 0x03fffffb, 0x00000006, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x000000, + }, + }, + // 2^256 - 1 + { + [10]uint32{ + 0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0x3fffc0, + }, + [10]uint32{ + 0x000003d0, 0x00000040, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x000000, + }, + }, + // Prime with field representation such that the initial + // reduction does not result in a carry to bit 256. + // + // 2^256 - 4294968273 (secp256k1 prime) + { + [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x003fffff, + }, + [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, + }, + }, + // Prime larger than P that reduces to a value which is still + // larger than P when it has a magnitude of 1 due to its first + // word and does not result in a carry to bit 256. + // + // 2^256 - 4294968272 (secp256k1 prime + 1) + { + [10]uint32{ + 0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x003fffff, + }, + [10]uint32{ + 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, + }, + }, + // Prime larger than P that reduces to a value which is still + // larger than P when it has a magnitude of 1 due to its second + // word and does not result in a carry to bit 256. + // + // 2^256 - 4227859409 (secp256k1 prime + 0x4000000) + { + [10]uint32{ + 0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x003fffff, + }, + [10]uint32{ + 0x00000000, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, + }, + }, + // Prime larger than P that reduces to a value which is still + // larger than P when it has a magnitude of 1 due to a carry to + // bit 256, but would not be without the carry. These values + // come from the fact that P is 2^256 - 4294968273 and 977 is + // the low order word in the internal field representation. + // + // 2^256 * 5 - ((4294968273 - (977+1)) * 4) + { + [10]uint32{ + 0x03ffffff, 0x03fffeff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x0013fffff, + }, + [10]uint32{ + 0x00001314, 0x00000040, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x000000000, + }, + }, + // Prime larger than P that reduces to a value which is still + // larger than P when it has a magnitude of 1 due to both a + // carry to bit 256 and the first word. + { + [10]uint32{ + 0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x07ffffff, 0x003fffff, + }, + [10]uint32{ + 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, + }, + }, + // Prime larger than P that reduces to a value which is still + // larger than P when it has a magnitude of 1 due to both a + // carry to bit 256 and the second word. + // + { + [10]uint32{ + 0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x3ffffff, + 0x07ffffff, 0x003fffff, + }, + [10]uint32{ + 0x00000000, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x0000000, + 0x00000000, 0x00000001, + }, + }, + // Prime larger than P that reduces to a value which is still + // larger than P when it has a magnitude of 1 due to a carry to + // bit 256 and the first and second words. + // + { + [10]uint32{ + 0x03fffc30, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x07ffffff, 0x003fffff, + }, + [10]uint32{ + 0x00000001, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, + }, + }, + } + + t.Logf("Running %d tests", len(tests)) + for range tests { + // TODO(roasbeef): can't access internal state + /*f := new(FieldVal) + f.n = test.raw + f.Normalize() + if !reflect.DeepEqual(f.n, test.normalized) { + t.Errorf("FieldVal.Normalize #%d wrong result\n"+ + "got: %x\nwant: %x", i, f.n, test.normalized) + continue + }*/ + } +} + +// TestIsOdd ensures that checking if a field value IsOdd works as expected. +func TestIsOdd(t *testing.T) { + tests := []struct { + in string // hex encoded value + expected bool // expected oddness + }{ + {"0", false}, + {"1", true}, + {"2", false}, + // 2^32 - 1 + {"ffffffff", true}, + // 2^64 - 2 + {"fffffffffffffffe", false}, + // secp256k1 prime + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + true, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := setHex(test.in) + result := f.IsOdd() + if result != test.expected { + t.Errorf( + "FieldVal.IsOdd #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, test.expected, + ) + continue + } + } +} + +// TestEquals ensures that checking two field values for equality via Equals +// works as expected. +func TestEquals(t *testing.T) { + tests := []struct { + in1 string // hex encoded value + in2 string // hex encoded value + expected bool // expected equality + }{ + {"0", "0", true}, + {"0", "1", false}, + {"1", "0", false}, + // 2^32 - 1 == 2^32 - 1? + {"ffffffff", "ffffffff", true}, + // 2^64 - 1 == 2^64 - 2? + {"ffffffffffffffff", "fffffffffffffffe", false}, + // 0 == prime (mod prime)? + { + "0", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + true, + }, + // 1 == prime+1 (mod prime)? + { + "1", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + true, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := setHex(test.in1).Normalize() + f2 := setHex(test.in2).Normalize() + result := f.Equals(f2) + if result != test.expected { + t.Errorf( + "FieldVal.Equals #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, test.expected, + ) + continue + } + } +} + +// TestNegate ensures that negating field values via Negate works as expected. +func TestNegate(t *testing.T) { + tests := []struct { + in string // hex encoded value + expected string // expected hex encoded value + }{ + // secp256k1 prime (aka 0) + {"0", "0"}, + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "0", + }, + { + "0", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + }, + // secp256k1 prime-1 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + "1", + }, + { + "1", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + }, + // secp256k1 prime-2 + { + "2", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + "2", + }, + // Random sampling + { + "b3d9aac9c5e43910b4385b53c7e78c21d4cd5f8e683c633aed04c233efc2e120", + "4c2655363a1bc6ef4bc7a4ac381873de2b32a07197c39cc512fb3dcb103d1b0f", + }, + { + "f8a85984fee5a12a7c8dd08830d83423c937d77c379e4a958e447a25f407733f", + "757a67b011a5ed583722f77cf27cbdc36c82883c861b56a71bb85d90bf888f0", + }, + { + "45ee6142a7fda884211e93352ed6cb2807800e419533be723a9548823ece8312", + "ba119ebd5802577bdee16ccad12934d7f87ff1be6acc418dc56ab77cc131791d", + }, + { + "53c2a668f07e411a2e473e1c3b6dcb495dec1227af27673761d44afe5b43d22b", + "ac3d59970f81bee5d1b8c1e3c49234b6a213edd850d898c89e2bb500a4bc2a04", + }, + } + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := setHex(test.in).Normalize() + expected := setHex(test.expected).Normalize() + result := f.Negate(1).Normalize() + if !result.Equals(expected) { + t.Errorf( + "FieldVal.Negate #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected, + ) + continue + } + } +} + +// TestFieldAddInt ensures that adding an integer to field values via AddInt +// works as expected. +func TestFieldAddInt(t *testing.T) { + tests := []struct { + name string // test description + in1 string // hex encoded value + in2 uint16 // unsigned integer to add to the value above + expected string // expected hex encoded value + }{ + { + name: "zero + one", + in1: "0", + in2: 1, + expected: "1", + }, { + name: "one + zero", + in1: "1", + in2: 0, + expected: "1", + }, { + name: "one + one", + in1: "1", + in2: 1, + expected: "2", + }, { + name: "secp256k1 prime-1 + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: 1, + expected: "0", + }, { + name: "secp256k1 prime + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: 1, + expected: "1", + }, { + name: "random sampling #1", + in1: "ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d6c1", + in2: 0x10f, + expected: "ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d7d0", + }, { + name: "random sampling #2", + in1: "44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41deea9cecf", + in2: 0x3196, + expected: "44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41deeaa0065", + }, { + name: "random sampling #3", + in1: "88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f7105122c9c", + in2: 0x966f, + expected: "88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f710512c30b", + }, { + name: "random sampling #4", + in1: "8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee09a015e2a6", + in2: 0xc54, + expected: "8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee09a015eefa", + }, + } + for _, test := range tests { + f := setHex(test.in1).Normalize() + expected := setHex(test.expected).Normalize() + result := f.AddInt(test.in2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: wrong result -- got: %v -- want: %v", test.name, + result, expected, + ) + continue + } + } +} + +// TestFieldAdd ensures that adding two field values together via Add and Add2 +// works as expected. +func TestFieldAdd(t *testing.T) { + tests := []struct { + name string // test description + in1 string // first hex encoded value + in2 string // second hex encoded value to add + expected string // expected hex encoded value + }{ + { + name: "zero + one", + in1: "0", + in2: "1", + expected: "1", + }, { + name: "one + zero", + in1: "1", + in2: "0", + expected: "1", + }, { + name: "secp256k1 prime-1 + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: "1", + expected: "0", + }, { + name: "secp256k1 prime + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: "1", + expected: "1", + }, { + name: "random sampling #1", + in1: "2b2012f975404e5065b4292fb8bed0a5d315eacf24c74d8b27e73bcc5430edcc", + in2: "2c3cefa4e4753e8aeec6ac4c12d99da4d78accefda3b7885d4c6bab46c86db92", + expected: "575d029e59b58cdb547ad57bcb986e4aaaa0b7beff02c610fcadf680c0b7c95e", + }, { + name: "random sampling #2", + in1: "8131e8722fe59bb189692b96c9f38de92885730f1dd39ab025daffb94c97f79c", + in2: "ff5454b765f0aab5f0977dcc629becc84cabeb9def48e79c6aadb2622c490fa9", + expected: "80863d2995d646677a00a9632c8f7ab175315ead0d1c824c9088b21c78e10b16", + }, { + name: "random sampling #3", + in1: "c7c95e93d0892b2b2cdd77e80eb646ea61be7a30ac7e097e9f843af73fad5c22", + in2: "3afe6f91a74dfc1c7f15c34907ee981656c37236d946767dd53ccad9190e437c", + expected: "2c7ce2577d72747abf33b3116a4df00b881ec6785c47ffc74c105d158bba36f", + }, { + name: "random sampling #4", + in1: "fd1c26f6a23381e5d785ba889494ec059369b888ad8431cd67d8c934b580dbe1", + in2: "a475aa5a31dcca90ef5b53c097d9133d6b7117474b41e7877bb199590fc0489c", + expected: "a191d150d4104c76c6e10e492c6dff42fedacfcff8c61954e38a628ec541284e", + }, { + name: "random sampling #5", + in1: "ad82b8d1cc136e23e9fd77fe2c7db1fe5a2ecbfcbde59ab3529758334f862d28", + in2: "4d6a4e95d6d61f4f46b528bebe152d408fd741157a28f415639347a84f6f574b", + expected: "faed0767a2e98d7330b2a0bcea92df3eea060d12380e8ec8b62a9fdb9ef58473", + }, { + name: "random sampling #6", + in1: "f3f43a2540054a86e1df98547ec1c0e157b193e5350fb4a3c3ea214b228ac5e7", + in2: "25706572592690ea3ddc951a1b48b504a4c83dc253756e1b96d56fdfb3199522", + expected: "19649f97992bdb711fbc2d6e9a0a75e5fc79d1a7888522bf5abf912bd5a45eda", + }, { + name: "random sampling #7", + in1: "6915bb94eef13ff1bb9b2633d997e13b9b1157c713363cc0e891416d6734f5b8", + in2: "11f90d6ac6fe1c4e8900b1c85fb575c251ec31b9bc34b35ada0aea1c21eded22", + expected: "7b0ec8ffb5ef5c40449bd7fc394d56fdecfd8980cf6af01bc29c2b898922e2da", + }, { + name: "random sampling #8", + in1: "48b0c9eae622eed9335b747968544eb3e75cb2dc8128388f948aa30f88cabde4", + in2: "0989882b52f85f9d524a3a3061a0e01f46d597839d2ba637320f4b9510c8d2d5", + expected: "523a5216391b4e7685a5aea9c9f52ed32e324a601e53dec6c699eea4999390b9", + }, + } + for _, test := range tests { + // Parse test hex. + f1 := setHex(test.in1).Normalize() + f2 := setHex(test.in2).Normalize() + expected := setHex(test.expected).Normalize() + // Ensure adding the two values with the result going to another + // variable produces the expected result. + result := new(FieldVal).Add2(f1, f2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + result, expected, + ) + continue + } + // Ensure adding the value to an existing field value produces the + // expected result. + f1.Add(f2).Normalize() + if !f1.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + f1, expected, + ) + continue + } + } +} + +// TestFieldMulInt ensures that multiplying an integer to field values via +// MulInt works as expected. +func TestFieldMulInt(t *testing.T) { + tests := []struct { + name string // test description + in1 string // hex encoded value + in2 uint8 // unsigned integer to multiply with value above + expected string // expected hex encoded value + }{ + { + name: "zero * zero", + in1: "0", + in2: 0, + expected: "0", + }, { + name: "one * zero", + in1: "1", + in2: 0, + expected: "0", + }, { + name: "zero * one", + in1: "0", + in2: 1, + expected: "0", + }, { + name: "one * one", + in1: "1", + in2: 1, + expected: "1", + }, { + name: "secp256k1 prime-1 * 2", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: 2, + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, { + name: "secp256k1 prime * 3", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: 3, + expected: "0", + }, { + name: "secp256k1 prime-1 * 8", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: 8, + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27", + }, { + // Random samples for first value. The second value is limited + // to 8 since that is the maximum int used in the elliptic curve + // calculations. + name: "random sampling #1", + in1: "b75674dc9180d306c692163ac5e089f7cef166af99645c0c23568ab6d967288a", + in2: 6, + expected: "4c06bd2b6904f228a76c8560a3433bced9a8681d985a2848d407404d186b0280", + }, { + name: "random sampling #2", + in1: "54873298ac2b5ba8591c125ae54931f5ea72040aee07b208d6135476fb5b9c0e", + in2: 3, + expected: "fd9597ca048212f90b543710afdb95e1bf560c20ca17161a8239fd64f212d42a", + }, { + name: "random sampling #3", + in1: "7c30fbd363a74c17e1198f56b090b59bbb6c8755a74927a6cba7a54843506401", + in2: 5, + expected: "6cf4eb20f2447c77657fccb172d38c0aa91ea4ac446dc641fa463a6b5091fba7", + }, { + name: "random sampling #3", + in1: "fb4529be3e027a3d1587d8a500b72f2d312e3577340ef5175f96d113be4c2ceb", + in2: 8, + expected: "da294df1f013d1e8ac3ec52805b979698971abb9a077a8bafcb688a4f261820f", + }, + } + for _, test := range tests { + f := setHex(test.in1).Normalize() + expected := setHex(test.expected).Normalize() + result := f.MulInt(test.in2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: wrong result -- got: %v -- want: %v", test.name, + result, expected, + ) + continue + } + } +} + +// TestFieldMul ensures that multiplying two field values via Mul and Mul2 works +// as expected. +func TestFieldMul(t *testing.T) { + tests := []struct { + name string // test description + in1 string // first hex encoded value + in2 string // second hex encoded value to multiply with + expected string // expected hex encoded value + }{ + { + name: "zero * zero", + in1: "0", + in2: "0", + expected: "0", + }, { + name: "one * zero", + in1: "1", + in2: "0", + expected: "0", + }, { + name: "zero * one", + in1: "0", + in2: "1", + expected: "0", + }, { + name: "one * one", + in1: "1", + in2: "1", + expected: "1", + }, { + name: "slightly over prime", + in1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff1ffff", + in2: "1000", + expected: "1ffff3d1", + }, { + name: "secp256k1 prime-1 * 2", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: "2", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, { + name: "secp256k1 prime * 3", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: "3", + expected: "0", + }, { + name: "secp256k1 prime * 3", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: "8", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27", + }, { + name: "random sampling #1", + in1: "cfb81753d5ef499a98ecc04c62cb7768c2e4f1740032946db1c12e405248137e", + in2: "58f355ad27b4d75fb7db0442452e732c436c1f7c5a7c4e214fa9cc031426a7d3", + expected: "1018cd2d7c2535235b71e18db9cd98027386328d2fa6a14b36ec663c4c87282b", + }, { + name: "random sampling #2", + in1: "26e9d61d1cdf3920e9928e85fa3df3e7556ef9ab1d14ec56d8b4fc8ed37235bf", + in2: "2dfc4bbe537afee979c644f8c97b31e58be5296d6dbc460091eae630c98511cf", + expected: "da85f48da2dc371e223a1ae63bd30b7e7ee45ae9b189ac43ff357e9ef8cf107a", + }, { + name: "random sampling #3", + in1: "5db64ed5afb71646c8b231585d5b2bf7e628590154e0854c4c29920b999ff351", + in2: "279cfae5eea5d09ade8e6a7409182f9de40981bc31c84c3d3dfe1d933f152e9a", + expected: "2c78fbae91792dd0b157abe3054920049b1879a7cc9d98cfda927d83be411b37", + }, { + name: "random sampling #4", + in1: "b66dfc1f96820b07d2bdbd559c19319a3a73c97ceb7b3d662f4fe75ecb6819e6", + in2: "bf774aba43e3e49eb63a6e18037d1118152568f1a3ac4ec8b89aeb6ff8008ae1", + expected: "c4f016558ca8e950c21c3f7fc15f640293a979c7b01754ee7f8b3340d4902ebb", + }, + } + for _, test := range tests { + f1 := setHex(test.in1).Normalize() + f2 := setHex(test.in2).Normalize() + expected := setHex(test.expected).Normalize() + // Ensure multiplying the two values with the result going to another + // variable produces the expected result. + result := new(FieldVal).Mul2(f1, f2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + result, expected, + ) + continue + } + // Ensure multiplying the value to an existing field value produces the + // expected result. + f1.Mul(f2).Normalize() + if !f1.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + f1, expected, + ) + continue + } + } +} + +// TestFieldSquare ensures that squaring field values via Square and SqualVal +// works as expected. +func TestFieldSquare(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded value + expected string // expected hex encoded value + }{ + { + name: "zero", + in: "0", + expected: "0", + }, { + 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", + expected: "1", + }, { + name: "secp256k1 prime - 2", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + expected: "4", + }, { + name: "random sampling #1", + in: "b0ba920360ea8436a216128047aab9766d8faf468895eb5090fc8241ec758896", + expected: "133896b0b69fda8ce9f648b9a3af38f345290c9eea3cbd35bafcadf7c34653d3", + }, { + name: "random sampling #2", + in: "c55d0d730b1d0285a1599995938b042a756e6e8857d390165ffab480af61cbd5", + expected: "cd81758b3f5877cbe7e5b0a10cebfa73bcbf0957ca6453e63ee8954ab7780bee", + }, { + name: "random sampling #3", + in: "e89c1f9a70d93651a1ba4bca5b78658f00de65a66014a25544d3365b0ab82324", + expected: "39ffc7a43e5dbef78fd5d0354fb82c6d34f5a08735e34df29da14665b43aa1f", + }, { + name: "random sampling #4", + in: "7dc26186079d22bcbe1614aa20ae627e62d72f9be7ad1e99cac0feb438956f05", + expected: "bf86bcfc4edb3d81f916853adfda80c07c57745b008b60f560b1912f95bce8ae", + }, + } + for _, test := range tests { + f := setHex(test.in).Normalize() + expected := setHex(test.expected).Normalize() + // Ensure squaring the value with the result going to another variable + // produces the expected result. + result := new(FieldVal).SquareVal(f).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + result, expected, + ) + continue + } + // Ensure self squaring an existing field value produces the expected + // result. + f.Square().Normalize() + if !f.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + f, expected, + ) + continue + } + } +} + +// TestInverse ensures that finding the multiplicative inverse via Inverse works +// as expected. +func TestInverse(t *testing.T) { + tests := []struct { + in string // hex encoded value + expected string // expected hex encoded value + }{ + // secp256k1 prime (aka 0) + {"0", "0"}, + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "0", + }, + { + "0", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + }, + // secp256k1 prime-1 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + }, + // secp256k1 prime-2 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe17", + }, + // Random sampling + { + "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca", + "987aeb257b063df0c6d1334051c47092b6d8766c4bf10c463786d93f5bc54354", + }, + { + "69d1323ce9f1f7b3bd3c7320b0d6311408e30281e273e39a0d8c7ee1c8257919", + "49340981fa9b8d3dad72de470b34f547ed9179c3953797d0943af67806f4bb6", + }, + { + "e0debf988ae098ecda07d0b57713e97c6d213db19753e8c95aa12a2fc1cc5272", + "64f58077b68af5b656b413ea366863f7b2819f8d27375d9c4d9804135ca220c2", + }, + { + "dcd394f91f74c2ba16aad74a22bb0ed47fe857774b8f2d6c09e28bfb14642878", + "fb848ec64d0be572a63c38fe83df5e7f3d032f60bf8c969ef67d36bf4ada22a9", + }, + } + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := setHex(test.in).Normalize() + expected := setHex(test.expected).Normalize() + result := f.Inverse().Normalize() + if !result.Equals(expected) { + t.Errorf( + "FieldVal.Inverse #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected, + ) + continue + } + } +} + +// randFieldVal returns a field value created from a random value generated by +// the passed rng. +func randFieldVal(t *testing.T, rng *rand.Rand) *FieldVal { + t.Helper() + var buf [32]byte + if _, err := rng.Read(buf[:]); chk.T(err) { + t.Fatalf("failed to read random: %v", err) + } + // Create and return both a big integer and a field value. + var fv FieldVal + fv.SetBytes(&buf) + return &fv +} + +// TestFieldSquareRoot ensures that calculating the square root of field values +// via SquareRootVal works as expected for edge cases. +func TestFieldSquareRoot(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded value + valid bool // whether or not the value has a square root + want string // expected hex encoded value + }{ + { + name: "secp256k1 prime (as 0 in and out)", + in: "0", + valid: true, + want: "0", + }, { + name: "secp256k1 prime (direct val with 0 out)", + 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", + valid: false, + want: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "secp256k1 prime-2", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + valid: false, + want: "210c790573632359b1edb4302c117d8a132654692c3feeb7de3a86ac3f3b53f7", + }, { + name: "(secp256k1 prime-2)^2", + in: "0000000000000000000000000000000000000000000000000000000000000004", + valid: true, + want: "0000000000000000000000000000000000000000000000000000000000000002", + }, { + name: "value 1", + in: "0000000000000000000000000000000000000000000000000000000000000001", + valid: true, + want: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "value 2", + in: "0000000000000000000000000000000000000000000000000000000000000002", + valid: true, + want: "210c790573632359b1edb4302c117d8a132654692c3feeb7de3a86ac3f3b53f7", + }, { + name: "random sampling 1", + in: "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca", + valid: false, + want: "6a27dcfca440cf7930a967be533b9620e397f122787c53958aaa7da7ad3d89a4", + }, { + name: "square of random sampling 1", + in: "f4a8c3738ace0a1c3abf77737ae737f07687b5e24c07a643398298bd96893a18", + valid: true, + want: "e90468feb8565338c9ab2b41dcc33b7478a31df5dedd2db0f8c2d641d77fa165", + }, { + name: "random sampling 2", + in: "69d1323ce9f1f7b3bd3c7320b0d6311408e30281e273e39a0d8c7ee1c8257919", + valid: true, + want: "61f4a7348274a52d75dfe176b8e3aaff61c1c833b6678260ba73def0fb2ad148", + }, { + name: "random sampling 3", + in: "e0debf988ae098ecda07d0b57713e97c6d213db19753e8c95aa12a2fc1cc5272", + valid: false, + want: "6e1cc9c311d33d901670135244f994b1ea39501f38002269b34ce231750cfbac", + }, { + name: "random sampling 4", + in: "dcd394f91f74c2ba16aad74a22bb0ed47fe857774b8f2d6c09e28bfb14642878", + valid: true, + want: "72b22fe6f173f8bcb21898806142ed4c05428601256eafce5d36c1b08fb82bab", + }, + } + for _, test := range tests { + input := setHex(test.in).Normalize() + want := setHex(test.want).Normalize() + // Calculate the square root and enusre the validity flag matches the + // expected value. + var result FieldVal + isValid := result.SquareRootVal(input) + if isValid != test.valid { + t.Errorf( + "%s: mismatched validity -- got %v, want %v", test.name, + isValid, test.valid, + ) + continue + } + // Ensure the calculated result matches the expected value. + result.Normalize() + if !result.Equals(want) { + t.Errorf( + "%s: d wrong result\ngot: %v\nwant: %v", test.name, result, + want, + ) + continue + } + } +} + +// hexToBytes converts the passed hex string into bytes 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 hexToBytes(s string) []byte { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} diff --git a/pkg/crypto/ec/fuzz_test.go b/pkg/crypto/ec/fuzz_test.go new file mode 100644 index 0000000..94f88c3 --- /dev/null +++ b/pkg/crypto/ec/fuzz_test.go @@ -0,0 +1,48 @@ +//go:build gofuzz || go1.18 +// +build gofuzz go1.18 + +// Copyright (c) 2013-2017 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "testing" + + "next.orly.dev/pkg/encoders/hex" +) + +func FuzzParsePubKey(f *testing.F) { + // 1. Seeds from pubkey tests. + for _, test := range pubKeyTests { + if test.isValid { + f.Add(test.key) + } + } + // 2. Seeds from recovery tests. + var recoveryTestPubKeys = []string{ + "04E32DF42865E97135ACFB65F3BAE71BDC86F4D49150AD6A440B6F15878109880A0A2B2667F7E725CEEA70C673093BF67663E0312623C8E091B13CF2C0F11EF652", + "04A7640409AA2083FDAD38B2D8DE1263B2251799591D840653FB02DBBA503D7745FCB83D80E08A1E02896BE691EA6AFFB8A35939A646F1FC79052A744B1C82EDC3", + } + for _, pubKey := range recoveryTestPubKeys { + seed, err := hex.Dec(pubKey) + if err != nil { + f.Fatal(err) + } + f.Add(seed) + } + // Now run the fuzzer. + f.Fuzz( + func(t *testing.T, input []byte) { + key, err := ParsePubKey(input) + if key == nil && err == nil { + panic("key==nil && err==nil") + } + if key != nil && err != nil { + panic("key!=nil yet err!=nil") + } + }, + ) +} diff --git a/pkg/crypto/ec/modnscalar.go b/pkg/crypto/ec/modnscalar.go new file mode 100644 index 0000000..945d603 --- /dev/null +++ b/pkg/crypto/ec/modnscalar.go @@ -0,0 +1,50 @@ +// Copyright (c) 2013-2021 The btcsuite developers +// Copyright (c) 2015-2021 The Decred developers + +package btcec + +import ( + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// ModNScalar implements optimized 256-bit constant-time fixed-precision +// arithmetic over the secp256k1 group order. This means all arithmetic is +// performed modulo: +// +// 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 +// +// It only implements the arithmetic needed for elliptic curve operations, +// however, the operations that are not implemented can typically be worked +// around if absolutely needed. For example, subtraction can be performed by +// adding the negation. +// +// Should it be absolutely necessary, conversion to the standard library +// math/big.Int can be accomplished by using the Bytes method, slicing the +// resulting fixed-size array, and feeding it to big.Int.SetBytes. However, +// that should typically be avoided when possible as conversion to big.Ints +// requires allocations, is not constant time, and is slower when working modulo +// the group order. +type ModNScalar = secp256k1.ModNScalar + +// NonceRFC6979 generates a nonce deterministically according to RFC 6979 using +// HMAC-SHA256 for the hashing function. It takes a 32-byte hash as an input +// and returns a 32-byte nonce to be used for deterministic signing. The extra +// and version arguments are optional, but allow additional data to be added to +// the input of the HMAC. When provided, the extra data must be 32-bytes and +// version must be 16 bytes or they will be ignored. +// +// Finally, the extraIterations parameter provides a method to produce a stream +// of deterministic nonces to ensure the signing code is able to produce a nonce +// that results in a valid signature in the extremely unlikely event the +// original nonce produced results in an invalid signature (e.g. R == 0). +// Signing code should start with 0 and increment it if necessary. +func NonceRFC6979( + privKey []byte, hash []byte, extra []byte, version []byte, + extraIterations uint32, +) *ModNScalar { + + return secp256k1.NonceRFC6979( + privKey, hash, extra, version, + extraIterations, + ) +} diff --git a/pkg/crypto/ec/musig2/bench_test.go b/pkg/crypto/ec/musig2/bench_test.go new file mode 100644 index 0000000..19f2854 --- /dev/null +++ b/pkg/crypto/ec/musig2/bench_test.go @@ -0,0 +1,280 @@ +// Copyright 2013-2022 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package musig2 + +import ( + "fmt" + "testing" + + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/schnorr" + "next.orly.dev/pkg/encoders/hex" +) + +var ( + testPrivBytes = hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + testMsg = hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") +) + +func hexToBytes(s string) []byte { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} + +func hexToModNScalar(s string) *btcec.ModNScalar { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var scalar btcec.ModNScalar + if overflow := scalar.SetByteSlice(b); overflow { + panic("hex in source file overflows mod N scalar: " + s) + } + return &scalar +} + +func genSigner(t *testing.B) signer { + privKey, err := btcec.NewSecretKey() + if err != nil { + t.Fatalf("unable to gen priv key: %v", err) + } + pubKey := privKey.PubKey() + nonces, err := GenNonces(WithPublicKey(pubKey)) + if err != nil { + t.Fatalf("unable to gen nonces: %v", err) + } + return signer{ + privKey: privKey, + pubKey: pubKey, + nonces: nonces, + } +} + +var ( + testSig *PartialSignature + testErr error +) + +// BenchmarkPartialSign benchmarks how long it takes to generate a partial +// signature factoring in if the keys are sorted and also if we're in fast sign +// mode. +func BenchmarkPartialSign(b *testing.B) { + for _, numSigners := range []int{10, 100} { + for _, fastSign := range []bool{true, false} { + for _, sortKeys := range []bool{true, false} { + name := fmt.Sprintf( + "num_signers=%v/fast_sign=%v/sort=%v", + numSigners, fastSign, sortKeys, + ) + signers := make(signerSet, numSigners) + for i := 0; i < numSigners; i++ { + signers[i] = genSigner(b) + } + combinedNonce, err := AggregateNonces(signers.pubNonces()) + if err != nil { + b.Fatalf("unable to generate combined nonce: %v", err) + } + var sig *PartialSignature + var msg [32]byte + copy(msg[:], testMsg[:]) + keys := signers.keys() + b.Run( + name, func(b *testing.B) { + var signOpts []SignOption + if fastSign { + signOpts = append(signOpts, WithFastSign()) + } + if sortKeys { + signOpts = append(signOpts, WithSortedKeys()) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sig, err = Sign( + signers[0].nonces.SecNonce, signers[0].privKey, + combinedNonce, keys, msg, signOpts..., + ) + if err != nil { + b.Fatalf("unable to generate sig: %v", err) + } + } + testSig = sig + testErr = err + }, + ) + } + } + } +} + +// TODO: this fails +// // TODO(roasbeef): add impact of sorting ^ +// +// var sigOk bo +// +// // BenchmarkPartialVerify benchmarks how long it takes to verify a partial +// // signature. +// func BenchmarkPartialVerify(b *testing.B) { +// for _, numSigners := range []int{10, 100} { +// for _, sortKeys := range []bool{true, false} { +// name := fmt.Sprintf("sort_keys=%v/num_signers=%v", +// sortKeys, numSigners) +// signers := make(signerSet, numSigners) +// for i := 0; i < numSigners; i++ { +// signers[i] = genSigner(b) +// } +// combinedNonce, err := AggregateNonces( +// signers.pubNonces(), +// ) +// if err != nil { +// b.Fatalf("unable to generate combined "+ +// "nonce: %v", err) +// } +// var sig *PartialSignature +// var msg [32]byte +// copy(msg[:], testMsg[:]) +// b.ReportAllocs() +// b.ResetTimer() +// sig, err = Sign( +// signers[0].nonces.SecNonce, signers[0].privKey, +// combinedNonce, signers.keys(), msg, +// ) +// if err != nil { +// b.Fatalf("unable to generate sig: %v", err) +// } +// keys := signers.keys() +// pubKey := signers[0].pubKey +// b.Run(name, func(b *testing.B) { +// var signOpts []SignOption +// if sortKeys { +// signOpts = append( +// signOpts, WithSortedKeys(), +// ) +// } +// b.ResetTimer() +// b.ReportAllocs() +// var ok bo +// for i := 0; i < b.no; i++ { +// ok = sig.Verify( +// signers[0].nonces.PubNonce, combinedNonce, +// keys, pubKey, msg, signOpts..., +// ) +// if !ok { +// b.Fatalf("generated invalid sig!") +// } +// } +// sigOk = ok +// }) +// +// } +// } +// } + +var finalSchnorrSig *schnorr.Signature + +// BenchmarkCombineSigs benchmarks how long it takes to combine a set amount of +// signatures. +func BenchmarkCombineSigs(b *testing.B) { + for _, numSigners := range []int{10, 100} { + signers := make(signerSet, numSigners) + for i := 0; i < numSigners; i++ { + signers[i] = genSigner(b) + } + combinedNonce, err := AggregateNonces(signers.pubNonces()) + if err != nil { + b.Fatalf("unable to generate combined nonce: %v", err) + } + var msg [32]byte + copy(msg[:], testMsg[:]) + var finalNonce *btcec.PublicKey + for i := range signers { + signer := signers[i] + partialSig, err := Sign( + signer.nonces.SecNonce, signer.privKey, + combinedNonce, signers.keys(), msg, + ) + if err != nil { + b.Fatalf( + "unable to generate partial sig: %v", + err, + ) + } + signers[i].partialSig = partialSig + if finalNonce == nil { + finalNonce = partialSig.R + } + } + sigs := signers.partialSigs() + name := fmt.Sprintf("num_signers=%v", numSigners) + b.Run( + name, func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + finalSig := CombineSigs(finalNonce, sigs) + finalSchnorrSig = finalSig + }, + ) + } +} + +var testNonce [PubNonceSize]byte + +// BenchmarkAggregateNonces benchmarks how long it takes to combine nonces. +func BenchmarkAggregateNonces(b *testing.B) { + for _, numSigners := range []int{10, 100} { + signers := make(signerSet, numSigners) + for i := 0; i < numSigners; i++ { + signers[i] = genSigner(b) + } + nonces := signers.pubNonces() + name := fmt.Sprintf("num_signers=%v", numSigners) + b.Run( + name, func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + pubNonce, err := AggregateNonces(nonces) + if err != nil { + b.Fatalf("unable to generate nonces: %v", err) + } + testNonce = pubNonce + }, + ) + } +} + +var testKey *btcec.PublicKey + +// BenchmarkAggregateKeys benchmarks how long it takes to aggregate public +// keys. +func BenchmarkAggregateKeys(b *testing.B) { + for _, numSigners := range []int{10, 100} { + for _, sortKeys := range []bool{true, false} { + signers := make(signerSet, numSigners) + for i := 0; i < numSigners; i++ { + signers[i] = genSigner(b) + } + signerKeys := signers.keys() + name := fmt.Sprintf( + "num_signers=%v/sort_keys=%v", + numSigners, sortKeys, + ) + uniqueKeyIndex := secondUniqueKeyIndex(signerKeys, false) + b.Run( + name, func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + aggKey, _, _, _ := AggregateKeys( + signerKeys, sortKeys, + WithUniqueKeyIndex(uniqueKeyIndex), + ) + testKey = aggKey.FinalKey + }, + ) + } + } +} diff --git a/pkg/crypto/ec/musig2/context.go b/pkg/crypto/ec/musig2/context.go new file mode 100644 index 0000000..c0858dd --- /dev/null +++ b/pkg/crypto/ec/musig2/context.go @@ -0,0 +1,584 @@ +// Copyright (c) 2013-2022 The btcsuite developers + +package musig2 + +import ( + "fmt" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/schnorr" +) + +var ( + // ErrSignersNotSpecified is returned when a caller attempts to create + // a context without specifying either the total number of signers, or + // the complete set of singers. + ErrSignersNotSpecified = fmt.Errorf( + "total number of signers or all " + + "signers must be known", + ) + // ErrSignerNotInKeySet is returned when a the secret key for a signer + // isn't included in the set of signing public keys. + ErrSignerNotInKeySet = fmt.Errorf( + "signing key is not found in key" + + " set", + ) + // ErrAlreadyHaveAllNonces is called when RegisterPubNonce is called too + // many times for a given signing session. + // + // ErrAlreadyHaveAllNonces is returned when a caller attempts to + // register a signer, once we already have the total set of known + // signers. + ErrAlreadyHaveAllNonces = fmt.Errorf("already have all nonces") + // ErrNotEnoughSigners is returned when a caller attempts to create a + // session from a context, but before all the required signers are + // known. + // + // ErrNotEnoughSigners is returned if a caller attempts to obtain an + // early nonce when it wasn't specified + ErrNotEnoughSigners = fmt.Errorf("not enough signers") + ErrAlreadyHaveAllSigners = fmt.Errorf("all signers registered") + // ErrAlredyHaveAllSigs is called when CombineSig is called too many + // times for a given signing session. + ErrAlredyHaveAllSigs = fmt.Errorf("already have all sigs") + // ErrSigningContextReuse is returned if a user attempts to sign using + // the same signing context more than once. + ErrSigningContextReuse = fmt.Errorf("nonce already used") + // ErrFinalSigInvalid is returned when the combined signature turns out + // to be invalid. + ErrFinalSigInvalid = fmt.Errorf("final signature is invalid") + // ErrCombinedNonceUnavailable is returned when a caller attempts to + // sign a partial signature, without first having collected all the + // required combined nonces. + ErrCombinedNonceUnavailable = fmt.Errorf("missing combined nonce") + // ErrTaprootInternalKeyUnavailable is returned when a user attempts to + // obtain the + ErrTaprootInternalKeyUnavailable = fmt.Errorf("taproot tweak not used") + ErrNoEarlyNonce = fmt.Errorf("no early nonce available") +) + +// Context is a managed signing context for musig2. It takes care of things +// like securely generating secret nonces, aggregating keys and nonces, etc. +type Context struct { + // signingKey is the key we'll use for signing. + signingKey *btcec.SecretKey + // pubKey is our even-y coordinate public key. + pubKey *btcec.PublicKey + // combinedKey is the aggregated public key. + combinedKey *AggregateKey + // uniqueKeyIndex is the index of the second unique key in the keySet. + // This is used to speed up signing and verification computations. + uniqueKeyIndex int + // keysHash is the hash of all the keys as defined in musig2. + keysHash []byte + // opts is the set of options for the context. + opts *contextOptions + // shouldSort keeps track of if the public keys should be sorted before + // any operations. + shouldSort bool + // sessionNonce will be populated if the earlyNonce option is true. + // After the first session is created, this nonce will be blanked out. + sessionNonce *Nonces +} + +// ContextOption is a functional option argument that allows callers to modify +// the musig2 signing is done within a context. +type ContextOption func(*contextOptions) + +// contextOptions houses the set of functional options that can be used to +// musig2 signing protocol. +type contextOptions struct { + // tweaks is the set of optinoal tweaks to apply to the combined public + // key. + tweaks []KeyTweakDesc + // taprootTweak specifies the taproot tweak. If specified, then we'll + // use this as the script root for the BIP 341 taproot (x-only) tweak. + // Normally we'd just apply the raw 32 byte tweak, but for taproot, we + // first need to compute the aggregated key before tweaking, and then + // use it as the internal key. This is required as the taproot tweak + // also commits to the public key, which in this case is the aggregated + // key before the tweak. + taprootTweak []byte + // bip86Tweak if true, then the weak will just be + // h_tapTweak(internalKey) as there is no true script root. + bip86Tweak bool + // keySet is the complete set of signers for this context. + keySet []*btcec.PublicKey + // numSigners is the total number of signers that will eventually be a + // part of the context. + numSigners int + // earlyNonce determines if a nonce should be generated during context + // creation, to be automatically passed to the created session. + earlyNonce bool +} + +// defaultContextOptions returns the default context options. +func defaultContextOptions() *contextOptions { return &contextOptions{} } + +// WithTweakedContext specifies that within the context, the aggregated public +// key should be tweaked with the specified tweaks. +func WithTweakedContext(tweaks ...KeyTweakDesc) ContextOption { + return func(o *contextOptions) { o.tweaks = tweaks } +} + +// WithTaprootTweakCtx specifies that within this context, the final key should +// use the taproot tweak as defined in BIP 341: outputKey = internalKey + +// h_tapTweak(internalKey || scriptRoot). In this case, the aggreaged key +// before the tweak will be used as the internal key. +func WithTaprootTweakCtx(scriptRoot []byte) ContextOption { + return func(o *contextOptions) { o.taprootTweak = scriptRoot } +} + +// WithBip86TweakCtx specifies that within this context, the final key should +// use the taproot tweak as defined in BIP 341, with the BIP 86 modification: +// outputKey = internalKey + h_tapTweak(internalKey)*G. In this case, the +// aggreaged key before the tweak will be used as the internal key. +func WithBip86TweakCtx() ContextOption { + return func(o *contextOptions) { o.bip86Tweak = true } +} + +// WithKnownSigners is an optional parameter that should be used if a session +// can be created as soon as all the singers are known. +func WithKnownSigners(signers []*btcec.PublicKey) ContextOption { + return func(o *contextOptions) { + o.keySet = signers + o.numSigners = len(signers) + } +} + +// WithNumSigners is a functional option used to specify that a context should +// be created without knowing all the signers. Instead the total number of +// signers is specified to ensure that a session can only be created once all +// the signers are known. +// +// NOTE: Either WithKnownSigners or WithNumSigners MUST be specified. +func WithNumSigners(n int) ContextOption { + return func(o *contextOptions) { o.numSigners = n } +} + +// WithEarlyNonceGen allow a caller to specify that a nonce should be generated +// early, before the session is created. This should be used in protocols that +// require some partial nonce exchange before all the signers are known. +// +// NOTE: This option must only be specified with the WithNumSigners option. +func WithEarlyNonceGen() ContextOption { + return func(o *contextOptions) { o.earlyNonce = true } +} + +// NewContext creates a new signing context with the passed singing key and set +// of public keys for each of the other signers. +// +// NOTE: This struct should be used over the raw Sign API whenever possible. +func NewContext( + signingKey *btcec.SecretKey, shouldSort bool, + ctxOpts ...ContextOption, +) (*Context, error) { + + // First, parse the set of optional context options. + opts := defaultContextOptions() + for _, option := range ctxOpts { + option(opts) + } + pubKey := signingKey.PubKey() + ctx := &Context{ + signingKey: signingKey, + pubKey: pubKey, + opts: opts, + shouldSort: shouldSort, + } + switch { + // We know all the signers, so we can compute the aggregated key, along + // with all the other intermediate state we need to do signing and + // verification. + case opts.keySet != nil: + if err := ctx.combineSignerKeys(); chk.T(err) { + return nil, err + } + // The total signers are known, so we add ourselves, and skip key + // aggregation. + case opts.numSigners != 0: + // Otherwise, we'll add ourselves as the only known signer, and + // await further calls to RegisterSigner before a session can + // be created. + opts.keySet = make([]*btcec.PublicKey, 0, opts.numSigners) + opts.keySet = append(opts.keySet, pubKey) + default: + return nil, ErrSignersNotSpecified + } + // If early nonce generation is specified, then we'll generate the + // nonce now to pass in to the session once all the callers are known. + if opts.earlyNonce { + var err error + ctx.sessionNonce, err = GenNonces( + WithPublicKey(ctx.pubKey), + WithNonceSecretKeyAux(signingKey), + ) + if err != nil { + return nil, err + } + } + return ctx, nil +} + +// combineSignerKeys is used to compute the aggregated signer key once all the +// signers are known. +func (c *Context) combineSignerKeys() error { + // As a sanity check, make sure the signing key is actually + // amongst the sit of signers. + var keyFound bool + for _, key := range c.opts.keySet { + if key.IsEqual(c.pubKey) { + keyFound = true + break + } + } + if !keyFound { + return ErrSignerNotInKeySet + } + + // Now that we know that we're actually a signer, we'll + // generate the key hash finger print and second unique key + // index so we can speed up signing later. + c.keysHash = keyHashFingerprint(c.opts.keySet, c.shouldSort) + c.uniqueKeyIndex = secondUniqueKeyIndex( + c.opts.keySet, c.shouldSort, + ) + keyAggOpts := []KeyAggOption{ + WithKeysHash(c.keysHash), + WithUniqueKeyIndex(c.uniqueKeyIndex), + } + switch { + case c.opts.bip86Tweak: + keyAggOpts = append( + keyAggOpts, WithBIP86KeyTweak(), + ) + case c.opts.taprootTweak != nil: + keyAggOpts = append( + keyAggOpts, WithTaprootKeyTweak(c.opts.taprootTweak), + ) + case len(c.opts.tweaks) != 0: + keyAggOpts = append(keyAggOpts, WithKeyTweaks(c.opts.tweaks...)) + } + // Next, we'll use this information to compute the aggregated + // public key that'll be used for signing in practice. + var err error + c.combinedKey, _, _, err = AggregateKeys( + c.opts.keySet, c.shouldSort, keyAggOpts..., + ) + if err != nil { + return err + } + return nil +} + +// EarlySessionNonce returns the early session nonce, if available. +func (c *Context) EarlySessionNonce() (*Nonces, error) { + if c.sessionNonce == nil { + return nil, ErrNoEarlyNonce + } + return c.sessionNonce, nil +} + +// RegisterSigner allows a caller to register a signer after the context has +// been created. This will be used in scenarios where the total number of +// signers is known, but nonce exchange needs to happen before all the signers +// are known. +// +// A bool is returned which indicates if all the signers have been registered. +// +// NOTE: If the set of keys are not to be sorted during signing, then the +// ordering each key is registered with MUST match the desired ordering. +func (c *Context) RegisterSigner(pub *btcec.PublicKey) (bool, error) { + haveAllSigners := len(c.opts.keySet) == c.opts.numSigners + if haveAllSigners { + return false, ErrAlreadyHaveAllSigners + } + c.opts.keySet = append(c.opts.keySet, pub) + // If we have the expected number of signers at this point, then we can + // generate the aggregated key and other necessary information. + haveAllSigners = len(c.opts.keySet) == c.opts.numSigners + if haveAllSigners { + if err := c.combineSignerKeys(); chk.T(err) { + return false, err + } + } + return haveAllSigners, nil +} + +// NumRegisteredSigners returns the total number of registered signers. +func (c *Context) NumRegisteredSigners() int { return len(c.opts.keySet) } + +// CombinedKey returns the combined public key that will be used to generate +// multi-signatures against. +func (c *Context) CombinedKey() (*btcec.PublicKey, error) { + // If the caller hasn't registered all the signers at this point, then + // the combined key won't be available. + if c.combinedKey == nil { + return nil, ErrNotEnoughSigners + } + return c.combinedKey.FinalKey, nil +} + +// PubKey returns the public key of the signer of this session. +func (c *Context) PubKey() btcec.PublicKey { return *c.pubKey } + +// SigningKeys returns the set of keys used for signing. +func (c *Context) SigningKeys() []*btcec.PublicKey { + keys := make([]*btcec.PublicKey, len(c.opts.keySet)) + copy(keys, c.opts.keySet) + return keys +} + +// TaprootInternalKey returns the internal taproot key, which is the aggregated +// key _before_ the tweak is applied. If a taproot tweak was specified, then +// CombinedKey() will return the fully tweaked output key, with this method +// returning the internal key. If a taproot tweak wasn't specified, then this +// method will return an error. +func (c *Context) TaprootInternalKey() (*btcec.PublicKey, error) { + // If the caller hasn't registered all the signers at this point, then + // the combined key won't be available. + if c.combinedKey == nil { + return nil, ErrNotEnoughSigners + } + if c.opts.taprootTweak == nil && !c.opts.bip86Tweak { + return nil, ErrTaprootInternalKeyUnavailable + } + return c.combinedKey.PreTweakedKey, nil +} + +// SessionOption is a functional option argument that allows callers to modify +// the musig2 signing is done within a session. +type SessionOption func(*sessionOptions) + +// sessionOptions houses the set of functional options that can be used to +// modify the musig2 signing protocol. +type sessionOptions struct { + externalNonce *Nonces +} + +// defaultSessionOptions returns the default session options. +func defaultSessionOptions() *sessionOptions { return &sessionOptions{} } + +// WithPreGeneratedNonce allows a caller to start a session using a nonce +// they've generated themselves. This may be useful in protocols where all the +// signer keys may not be known before nonce exchange needs to occur. +func WithPreGeneratedNonce(nonce *Nonces) SessionOption { + return func(o *sessionOptions) { o.externalNonce = nonce } +} + +// Session represents a musig2 signing session. A new instance should be +// created each time a multi-signature is needed. The session struct handles +// nonces management, incremental partial sig vitrifaction, as well as final +// signature combination. Errors are returned when unsafe behavior such as +// nonce re-use is attempted. +// +// NOTE: This struct should be used over the raw Sign API whenever possible. +type Session struct { + opts *sessionOptions + ctx *Context + localNonces *Nonces + pubNonces [][PubNonceSize]byte + combinedNonce *[PubNonceSize]byte + msg [32]byte + ourSig *PartialSignature + sigs []*PartialSignature + finalSig *schnorr.Signature +} + +// NewSession creates a new musig2 signing session. +func (c *Context) NewSession(options ...SessionOption) (*Session, error) { + opts := defaultSessionOptions() + for _, opt := range options { + opt(opts) + } + // At this point we verify that we know of all the signers, as + // otherwise we can't proceed with the session. This check is intended + // to catch misuse of the API wherein a caller forgets to register the + // remaining signers if they're doing nonce generation ahead of time. + if len(c.opts.keySet) != c.opts.numSigners { + return nil, ErrNotEnoughSigners + } + // If an early nonce was specified, then we'll automatically add the + // corresponding session option for the caller. + var localNonces *Nonces + if c.sessionNonce != nil { + // Apply the early nonce to the session, and also blank out the + // session nonce on the context to ensure it isn't ever re-used + // for another session. + localNonces = c.sessionNonce + c.sessionNonce = nil + } else if opts.externalNonce != nil { + // Otherwise if there's a custom nonce passed in via the + // session options, then use that instead. + localNonces = opts.externalNonce + } + // Now that we know we have enough signers, we'll either use the caller + // specified nonce, or generate a fresh set. + var err error + if localNonces == nil { + // At this point we need to generate a fresh nonce. We'll pass + // in some auxiliary information to strengthen the nonce + // generated. + localNonces, err = GenNonces( + WithPublicKey(c.pubKey), + WithNonceSecretKeyAux(c.signingKey), + WithNonceCombinedKeyAux(c.combinedKey.FinalKey), + ) + if err != nil { + return nil, err + } + } + s := &Session{ + opts: opts, + ctx: c, + localNonces: localNonces, + pubNonces: make([][PubNonceSize]byte, 0, c.opts.numSigners), + sigs: make([]*PartialSignature, 0, c.opts.numSigners), + } + s.pubNonces = append(s.pubNonces, localNonces.PubNonce) + return s, nil +} + +// PublicNonce returns the public nonce for a signer. This should be sent to +// other parties before signing begins, so they can compute the aggregated +// public nonce. +func (s *Session) PublicNonce() [PubNonceSize]byte { + return s.localNonces.PubNonce +} + +// NumRegisteredNonces returns the total number of nonces that have been +// regsitered so far. +func (s *Session) NumRegisteredNonces() int { return len(s.pubNonces) } + +// RegisterPubNonce should be called for each public nonce from the set of +// signers. This method returns true once all the public nonces have been +// accounted for. +func (s *Session) RegisterPubNonce(nonce [PubNonceSize]byte) (bool, error) { + // If we already have all the nonces, then this method was called too + // many times. + haveAllNonces := len(s.pubNonces) == s.ctx.opts.numSigners + if haveAllNonces { + return false, ErrAlreadyHaveAllNonces + } + // Add this nonce and check again if we already have tall the nonces we + // need. + s.pubNonces = append(s.pubNonces, nonce) + haveAllNonces = len(s.pubNonces) == s.ctx.opts.numSigners + // If we have all the nonces, then we can go ahead and combine them + // now. + if haveAllNonces { + combinedNonce, err := AggregateNonces(s.pubNonces) + if err != nil { + return false, err + } + s.combinedNonce = &combinedNonce + } + return haveAllNonces, nil +} + +// Sign generates a partial signature for the target message, using the target +// context. If this method is called more than once per context, then an error +// is returned, as that means a nonce was re-used. +func (s *Session) Sign( + msg [32]byte, + signOpts ...SignOption, +) (*PartialSignature, error) { + + switch { + // If no local nonce is present, then this means we already signed, so + // we'll return an error to prevent nonce re-use. + case s.localNonces == nil: + return nil, ErrSigningContextReuse + // We also need to make sure we have the combined nonce, otherwise this + // funciton was called too early. + case s.combinedNonce == nil: + return nil, ErrCombinedNonceUnavailable + } + switch { + case s.ctx.opts.bip86Tweak: + signOpts = append( + signOpts, WithBip86SignTweak(), + ) + case s.ctx.opts.taprootTweak != nil: + signOpts = append( + signOpts, WithTaprootSignTweak(s.ctx.opts.taprootTweak), + ) + case len(s.ctx.opts.tweaks) != 0: + signOpts = append(signOpts, WithTweaks(s.ctx.opts.tweaks...)) + } + partialSig, err := Sign( + s.localNonces.SecNonce, s.ctx.signingKey, *s.combinedNonce, + s.ctx.opts.keySet, msg, signOpts..., + ) + // Now that we've generated our signature, we'll make sure to blank out + // our signing nonce. + s.localNonces = nil + if err != nil { + return nil, err + } + s.msg = msg + s.ourSig = partialSig + s.sigs = append(s.sigs, partialSig) + return partialSig, nil +} + +// CombineSig buffers a partial signature received from a signing party. The +// method returns true once all the signatures are available, and can be +// combined into the final signature. +func (s *Session) CombineSig(sig *PartialSignature) (bool, error) { + // First check if we already have all the signatures we need. We + // already accumulated our own signature when we generated the sig. + haveAllSigs := len(s.sigs) == len(s.ctx.opts.keySet) + if haveAllSigs { + return false, ErrAlredyHaveAllSigs + } + // TODO(roasbeef): incremental check for invalid sig, or just detect at + // the very end? + // + // Accumulate this sig, and check again if we have all the sigs we + // need. + s.sigs = append(s.sigs, sig) + haveAllSigs = len(s.sigs) == len(s.ctx.opts.keySet) + // If we have all the signatures, then we can combine them all into the + // final signature. + if haveAllSigs { + var combineOpts []CombineOption + switch { + case s.ctx.opts.bip86Tweak: + combineOpts = append( + combineOpts, WithBip86TweakedCombine( + s.msg, s.ctx.opts.keySet, + s.ctx.shouldSort, + ), + ) + case s.ctx.opts.taprootTweak != nil: + combineOpts = append( + combineOpts, WithTaprootTweakedCombine( + s.msg, s.ctx.opts.keySet, + s.ctx.opts.taprootTweak, s.ctx.shouldSort, + ), + ) + case len(s.ctx.opts.tweaks) != 0: + combineOpts = append( + combineOpts, WithTweakedCombine( + s.msg, s.ctx.opts.keySet, + s.ctx.opts.tweaks, s.ctx.shouldSort, + ), + ) + } + finalSig := CombineSigs(s.ourSig.R, s.sigs, combineOpts...) + // We'll also verify the signature at this point to ensure it's + // valid. + // + // TODO(roasbef): allow skipping? + if !finalSig.Verify(s.msg[:], s.ctx.combinedKey.FinalKey) { + return false, ErrFinalSigInvalid + } + s.finalSig = finalSig + } + return haveAllSigs, nil +} + +// FinalSig returns the final combined multi-signature, if present. +func (s *Session) FinalSig() *schnorr.Signature { return s.finalSig } diff --git a/pkg/crypto/ec/musig2/data/key_agg_vectors.json b/pkg/crypto/ec/musig2/data/key_agg_vectors.json new file mode 100644 index 0000000..46f4733 --- /dev/null +++ b/pkg/crypto/ec/musig2/data/key_agg_vectors.json @@ -0,0 +1,127 @@ +{ + "pubkeys": [ + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + "023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66", + "020000000000000000000000000000000000000000000000000000000000000005", + "02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", + "04F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9" + ], + "tweaks": [ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + "252E4BD67410A76CDF933D30EAA1608214037F1B105A013ECCD3C5C184A6110B" + ], + "valid_test_cases": [ + { + "key_indices": [ + 0, + 1, + 2 + ], + "expected": "90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C" + }, + { + "key_indices": [ + 2, + 1, + 0 + ], + "expected": "6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B" + }, + { + "key_indices": [ + 0, + 0, + 0 + ], + "expected": "B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935" + }, + { + "key_indices": [ + 0, + 0, + 1, + 1 + ], + "expected": "69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E" + } + ], + "error_test_cases": [ + { + "key_indices": [ + 0, + 3 + ], + "tweak_indices": [], + "is_xonly": [], + "error": { + "type": "invalid_contribution", + "signer": 1, + "contrib": "pubkey" + }, + "comment": "Invalid public key" + }, + { + "key_indices": [ + 0, + 4 + ], + "tweak_indices": [], + "is_xonly": [], + "error": { + "type": "invalid_contribution", + "signer": 1, + "contrib": "pubkey" + }, + "comment": "Public key exceeds field size" + }, + { + "key_indices": [ + 5, + 0 + ], + "tweak_indices": [], + "is_xonly": [], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubkey" + }, + "comment": "First byte of public key is not 2 or 3" + }, + { + "key_indices": [ + 0, + 1 + ], + "tweak_indices": [ + 0 + ], + "is_xonly": [ + true + ], + "error": { + "type": "value", + "message": "The tweak must be less than n." + }, + "comment": "Tweak is out of range" + }, + { + "key_indices": [ + 6 + ], + "tweak_indices": [ + 1 + ], + "is_xonly": [ + false + ], + "error": { + "type": "value", + "message": "The result of tweaking cannot be infinity." + }, + "comment": "Intermediate tweaking result is point at infinity" + } + ] +} diff --git a/pkg/crypto/ec/musig2/data/key_sort_vectors.json b/pkg/crypto/ec/musig2/data/key_sort_vectors.json new file mode 100644 index 0000000..471c67e --- /dev/null +++ b/pkg/crypto/ec/musig2/data/key_sort_vectors.json @@ -0,0 +1,16 @@ +{ + "pubkeys": [ + "02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + "023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66", + "02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8" + ], + "sorted_pubkeys": [ + "023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66", + "02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", + "02DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659" + ] +} diff --git a/pkg/crypto/ec/musig2/data/nonce_agg_vectors.json b/pkg/crypto/ec/musig2/data/nonce_agg_vectors.json new file mode 100644 index 0000000..115c045 --- /dev/null +++ b/pkg/crypto/ec/musig2/data/nonce_agg_vectors.json @@ -0,0 +1,69 @@ +{ + "pnonces": [ + "020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833", + "020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30" + ], + "valid_test_cases": [ + { + "pnonce_indices": [ + 0, + 1 + ], + "expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8" + }, + { + "pnonce_indices": [ + 2, + 3 + ], + "expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000", + "comment": "Sum of second points encoded in the nonces is point at infinity which is serialized as 33 zero bytes" + } + ], + "error_test_cases": [ + { + "pnonce_indices": [ + 0, + 4 + ], + "error": { + "type": "invalid_contribution", + "signer": 1, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 1 is invalid due wrong tag, 0x04, in the first half", + "btcec_err": "invalid public key: unsupported format: 4" + }, + { + "pnonce_indices": [ + 5, + 1 + ], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 0 is invalid because the second half does not correspond to an X coordinate", + "btcec_err": "invalid public key: x coordinate 48c264cdd57d3c24d79990b0f865674eb62a0f9018277a95011b41bfc193b831 is not on the secp256k1 curve" + }, + { + "pnonce_indices": [ + 6, + 1 + ], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 0 is invalid because second half exceeds field size", + "btcec_err": "invalid public key: x >= field prime" + } + ] +} diff --git a/pkg/crypto/ec/musig2/data/nonce_gen_vectors.json b/pkg/crypto/ec/musig2/data/nonce_gen_vectors.json new file mode 100644 index 0000000..9f6fbb2 --- /dev/null +++ b/pkg/crypto/ec/musig2/data/nonce_gen_vectors.json @@ -0,0 +1,40 @@ +{ + "test_cases": [ + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "0101010101010101010101010101010101010101010101010101010101010101", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "227243DCB40EF2A13A981DB188FA433717B506BDFA14B1AE47D5DC027C9C3B9EF2370B2AD206E724243215137C86365699361126991E6FEC816845F837BDDAC3024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "CD0F47FE471D6788FF3243F47345EA0A179AEF69476BE8348322EF39C2723318870C2065AFB52DEDF02BF4FDBF6D2F442E608692F50C2374C08FFFE57042A61C024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "pk": "024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "2626262626262626262626262626262626262626262626262626262626262626262626262626", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "011F8BC60EF061DEEF4D72A0A87200D9994B3F0CD9867910085C38D5366E3E6B9FF03BC0124E56B24069E91EC3F162378983F194E8BD0ED89BE3059649EAE262024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": null, + "pk": "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "aggpk": null, + "msg": null, + "extra_in": null, + "expected": "890E83616A3BC4640AB9B6374F21C81FF89CDDDBAFAA7475AE2A102A92E3EDB29FD7E874E23342813A60D9646948242646B7951CA046B4B36D7D6078506D3C9402F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9" + } + ] +} \ No newline at end of file diff --git a/pkg/crypto/ec/musig2/data/sig_agg_vectors.json b/pkg/crypto/ec/musig2/data/sig_agg_vectors.json new file mode 100644 index 0000000..1370503 --- /dev/null +++ b/pkg/crypto/ec/musig2/data/sig_agg_vectors.json @@ -0,0 +1,151 @@ +{ + "pubkeys": [ + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "02D2DC6F5DF7C56ACF38C7FA0AE7A759AE30E19B37359DFDE015872324C7EF6E05", + "03C7FB101D97FF930ACD0C6760852EF64E69083DE0B06AC6335724754BB4B0522C", + "02352433B21E7E05D3B452B81CAE566E06D2E003ECE16D1074AABA4289E0E3D581" + ], + "pnonces": [ + "036E5EE6E28824029FEA3E8A9DDD2C8483F5AF98F7177C3AF3CB6F47CAF8D94AE902DBA67E4A1F3680826172DA15AFB1A8CA85C7C5CC88900905C8DC8C328511B53E", + "03E4F798DA48A76EEC1C9CC5AB7A880FFBA201A5F064E627EC9CB0031D1D58FC5103E06180315C5A522B7EC7C08B69DCD721C313C940819296D0A7AB8E8795AC1F00", + "02C0068FD25523A31578B8077F24F78F5BD5F2422AFF47C1FADA0F36B3CEB6C7D202098A55D1736AA5FCC21CF0729CCE852575C06C081125144763C2C4C4A05C09B6", + "031F5C87DCFBFCF330DEE4311D85E8F1DEA01D87A6F1C14CDFC7E4F1D8C441CFA40277BF176E9F747C34F81B0D9F072B1B404A86F402C2D86CF9EA9E9C69876EA3B9", + "023F7042046E0397822C4144A17F8B63D78748696A46C3B9F0A901D296EC3406C302022B0B464292CF9751D699F10980AC764E6F671EFCA15069BBE62B0D1C62522A", + "02D97DDA5988461DF58C5897444F116A7C74E5711BF77A9446E27806563F3B6C47020CBAD9C363A7737F99FA06B6BE093CEAFF5397316C5AC46915C43767AE867C00" + ], + "tweaks": [ + "B511DA492182A91B0FFB9A98020D55F260AE86D7ECBD0399C7383D59A5F2AF7C", + "A815FE049EE3C5AAB66310477FBC8BCCCAC2F3395F59F921C364ACD78A2F48DC", + "75448A87274B056468B977BE06EB1E9F657577B7320B0A3376EA51FD420D18A8" + ], + "psigs": [ + "B15D2CD3C3D22B04DAE438CE653F6B4ECF042F42CFDED7C41B64AAF9B4AF53FB", + "6193D6AC61B354E9105BBDC8937A3454A6D705B6D57322A5A472A02CE99FCB64", + "9A87D3B79EC67228CB97878B76049B15DBD05B8158D17B5B9114D3C226887505", + "66F82EA90923689B855D36C6B7E032FB9970301481B99E01CDB4D6AC7C347A15", + "4F5AEE41510848A6447DCD1BBC78457EF69024944C87F40250D3EF2C25D33EFE", + "DDEF427BBB847CC027BEFF4EDB01038148917832253EBC355FC33F4A8E2FCCE4", + "97B890A26C981DA8102D3BC294159D171D72810FDF7C6A691DEF02F0F7AF3FDC", + "53FA9E08BA5243CBCB0D797C5EE83BC6728E539EB76C2D0BF0F971EE4E909971", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" + ], + "msg": "599C67EA410D005B9DA90817CF03ED3B1C868E4DA4EDF00A5880B0082C237869", + "valid_test_cases": [ + { + "aggnonce": "0341432722C5CD0268D829C702CF0D1CBCE57033EED201FD335191385227C3210C03D377F2D258B64AADC0E16F26462323D701D286046A2EA93365656AFD9875982B", + "nonce_indices": [ + 0, + 1 + ], + "key_indices": [ + 0, + 1 + ], + "tweak_indices": [], + "is_xonly": [], + "psig_indices": [ + 0, + 1 + ], + "expected": "041DA22223CE65C92C9A0D6C2CAC828AAF1EEE56304FEC371DDF91EBB2B9EF0912F1038025857FEDEB3FF696F8B99FA4BB2C5812F6095A2E0004EC99CE18DE1E" + }, + { + "aggnonce": "0224AFD36C902084058B51B5D36676BBA4DC97C775873768E58822F87FE437D792028CB15929099EEE2F5DAE404CD39357591BA32E9AF4E162B8D3E7CB5EFE31CB20", + "nonce_indices": [ + 0, + 2 + ], + "key_indices": [ + 0, + 2 + ], + "tweak_indices": [], + "is_xonly": [], + "psig_indices": [ + 2, + 3 + ], + "expected": "1069B67EC3D2F3C7C08291ACCB17A9C9B8F2819A52EB5DF8726E17E7D6B52E9F01800260A7E9DAC450F4BE522DE4CE12BA91AEAF2B4279219EF74BE1D286ADD9" + }, + { + "aggnonce": "0208C5C438C710F4F96A61E9FF3C37758814B8C3AE12BFEA0ED2C87FF6954FF186020B1816EA104B4FCA2D304D733E0E19CEAD51303FF6420BFD222335CAA402916D", + "nonce_indices": [ + 0, + 3 + ], + "key_indices": [ + 0, + 2 + ], + "tweak_indices": [ + 0 + ], + "is_xonly": [ + false + ], + "psig_indices": [ + 4, + 5 + ], + "expected": "5C558E1DCADE86DA0B2F02626A512E30A22CF5255CAEA7EE32C38E9A71A0E9148BA6C0E6EC7683B64220F0298696F1B878CD47B107B81F7188812D593971E0CC" + }, + { + "aggnonce": "02B5AD07AFCD99B6D92CB433FBD2A28FDEB98EAE2EB09B6014EF0F8197CD58403302E8616910F9293CF692C49F351DB86B25E352901F0E237BAFDA11F1C1CEF29FFD", + "nonce_indices": [ + 0, + 4 + ], + "key_indices": [ + 0, + 3 + ], + "tweak_indices": [ + 0, + 1, + 2 + ], + "is_xonly": [ + true, + false, + true + ], + "psig_indices": [ + 6, + 7 + ], + "expected": "839B08820B681DBA8DAF4CC7B104E8F2638F9388F8D7A555DC17B6E6971D7426CE07BF6AB01F1DB50E4E33719295F4094572B79868E440FB3DEFD3FAC1DB589E" + } + ], + "error_test_cases": [ + { + "aggnonce": "02B5AD07AFCD99B6D92CB433FBD2A28FDEB98EAE2EB09B6014EF0F8197CD58403302E8616910F9293CF692C49F351DB86B25E352901F0E237BAFDA11F1C1CEF29FFD", + "nonce_indices": [ + 0, + 4 + ], + "key_indices": [ + 0, + 3 + ], + "tweak_indices": [ + 0, + 1, + 2 + ], + "is_xonly": [ + true, + false, + true + ], + "psig_indices": [ + 7, + 8 + ], + "error": { + "type": "invalid_contribution", + "signer": 1 + }, + "comment": "Partial signature is invalid because it exceeds group size" + } + ] +} \ No newline at end of file diff --git a/pkg/crypto/ec/musig2/data/sign_verify_vectors.json b/pkg/crypto/ec/musig2/data/sign_verify_vectors.json new file mode 100644 index 0000000..c26c4aa --- /dev/null +++ b/pkg/crypto/ec/musig2/data/sign_verify_vectors.json @@ -0,0 +1,287 @@ +{ + "sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671", + "pubkeys": [ + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA661", + "020000000000000000000000000000000000000000000000000000000000000007" + ], + "secnonces": [ + "508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F703935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9" + ], + "pnonces": [ + "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480", + "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046", + "0237C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0387BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480", + "020000000000000000000000000000000000000000000000000000000000000009" + ], + "aggnonces": [ + "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "048465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9", + "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61020000000000000000000000000000000000000000000000000000000000000009", + "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD6102FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30" + ], + "msgs": [ + "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF", + "", + "2626262626262626262626262626262626262626262626262626262626262626262626262626" + ], + "valid_test_cases": [ + { + "key_indices": [ + 0, + 1, + 2 + ], + "nonce_indices": [ + 0, + 1, + 2 + ], + "aggnonce_index": 0, + "msg_index": 0, + "signer_index": 0, + "expected": "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB" + }, + { + "key_indices": [ + 1, + 0, + 2 + ], + "nonce_indices": [ + 1, + 0, + 2 + ], + "aggnonce_index": 0, + "msg_index": 0, + "signer_index": 1, + "expected": "9FF2F7AAA856150CC8819254218D3ADEEB0535269051897724F9DB3789513A52" + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "nonce_indices": [ + 1, + 2, + 0 + ], + "aggnonce_index": 0, + "msg_index": 0, + "signer_index": 2, + "expected": "FA23C359F6FAC4E7796BB93BC9F0532A95468C539BA20FF86D7C76ED92227900" + }, + { + "key_indices": [ + 0, + 1 + ], + "nonce_indices": [ + 0, + 3 + ], + "aggnonce_index": 1, + "msg_index": 0, + "signer_index": 0, + "expected": "AE386064B26105404798F75DE2EB9AF5EDA5387B064B83D049CB7C5E08879531", + "comment": "Both halves of aggregate nonce correspond to point at infinity" + } + ], + "sign_error_test_cases": [ + { + "key_indices": [ + 1, + 2 + ], + "aggnonce_index": 0, + "msg_index": 0, + "secnonce_index": 0, + "error": { + "type": "value", + "message": "The signer's pubkey must be included in the list of pubkeys." + }, + "comment": "The signers pubkey is not in the list of pubkeys" + }, + { + "key_indices": [ + 1, + 0, + 3 + ], + "aggnonce_index": 0, + "msg_index": 0, + "secnonce_index": 0, + "error": { + "type": "invalid_contribution", + "signer": 2, + "contrib": "pubkey" + }, + "comment": "Signer 2 provided an invalid public key" + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "aggnonce_index": 2, + "msg_index": 0, + "secnonce_index": 0, + "error": { + "type": "invalid_contribution", + "signer": null, + "contrib": "aggnonce" + }, + "comment": "Aggregate nonce is invalid due wrong tag, 0x04, in the first half" + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "aggnonce_index": 3, + "msg_index": 0, + "secnonce_index": 0, + "error": { + "type": "invalid_contribution", + "signer": null, + "contrib": "aggnonce" + }, + "comment": "Aggregate nonce is invalid because the second half does not correspond to an X coordinate" + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "aggnonce_index": 4, + "msg_index": 0, + "secnonce_index": 0, + "error": { + "type": "invalid_contribution", + "signer": null, + "contrib": "aggnonce" + }, + "comment": "Aggregate nonce is invalid because second half exceeds field size" + }, + { + "key_indices": [ + 0, + 1, + 2 + ], + "aggnonce_index": 0, + "msg_index": 0, + "signer_index": 0, + "secnonce_index": 1, + "error": { + "type": "value", + "message": "first secnonce value is out of range." + }, + "comment": "Secnonce is invalid which may indicate nonce reuse" + } + ], + "verify_fail_test_cases": [ + { + "sig": "97AC833ADCB1AFA42EBF9E0725616F3C9A0D5B614F6FE283CEAAA37A8FFAF406", + "key_indices": [ + 0, + 1, + 2 + ], + "nonce_indices": [ + 0, + 1, + 2 + ], + "msg_index": 0, + "signer_index": 0, + "comment": "Wrong signature (which is equal to the negation of valid signature)" + }, + { + "sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B", + "key_indices": [ + 0, + 1, + 2 + ], + "nonce_indices": [ + 0, + 1, + 2 + ], + "msg_index": 0, + "signer_index": 1, + "comment": "Wrong signer" + }, + { + "sig": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + "key_indices": [ + 0, + 1, + 2 + ], + "nonce_indices": [ + 0, + 1, + 2 + ], + "msg_index": 0, + "signer_index": 0, + "comment": "Signature exceeds group size" + } + ], + "verify_error_test_cases": [ + { + "sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B", + "key_indices": [ + 0, + 1, + 2 + ], + "nonce_indices": [ + 4, + 1, + 2 + ], + "msg_index": 0, + "signer_index": 0, + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Invalid pubnonce" + }, + { + "sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B", + "key_indices": [ + 3, + 1, + 2 + ], + "nonce_indices": [ + 0, + 1, + 2 + ], + "msg_index": 0, + "signer_index": 0, + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubkey" + }, + "comment": "Invalid pubkey" + } + ] +} diff --git a/pkg/crypto/ec/musig2/data/tweak_vectors.json b/pkg/crypto/ec/musig2/data/tweak_vectors.json new file mode 100644 index 0000000..20f3403 --- /dev/null +++ b/pkg/crypto/ec/musig2/data/tweak_vectors.json @@ -0,0 +1,170 @@ +{ + "sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671", + "pubkeys": [ + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659" + ], + "secnonce": "508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F703935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "pnonces": [ + "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480", + "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046" + ], + "aggnonce": "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9", + "tweaks": [ + "E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB", + "AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455", + "F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0", + "1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" + ], + "msg": "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF", + "valid_test_cases": [ + { + "key_indices": [ + 1, + 2, + 0 + ], + "nonce_indices": [ + 1, + 2, + 0 + ], + "tweak_indices": [ + 0 + ], + "is_xonly": [ + true + ], + "signer_index": 2, + "expected": "E28A5C66E61E178C2BA19DB77B6CF9F7E2F0F56C17918CD13135E60CC848FE91", + "comment": "A single x-only tweak" + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "nonce_indices": [ + 1, + 2, + 0 + ], + "tweak_indices": [ + 0 + ], + "is_xonly": [ + false + ], + "signer_index": 2, + "expected": "38B0767798252F21BF5702C48028B095428320F73A4B14DB1E25DE58543D2D2D", + "comment": "A single plain tweak" + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "nonce_indices": [ + 1, + 2, + 0 + ], + "tweak_indices": [ + 0, + 1 + ], + "is_xonly": [ + false, + true + ], + "signer_index": 2, + "expected": "408A0A21C4A0F5DACAF9646AD6EB6FECD7F7A11F03ED1F48DFFF2185BC2C2408", + "comment": "A plain tweak followed by an x-only tweak" + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "nonce_indices": [ + 1, + 2, + 0 + ], + "tweak_indices": [ + 0, + 1, + 2, + 3 + ], + "is_xonly": [ + false, + false, + true, + true + ], + "signer_index": 2, + "expected": "45ABD206E61E3DF2EC9E264A6FEC8292141A633C28586388235541F9ADE75435", + "comment": "Four tweaks: plain, plain, x-only, x-only." + }, + { + "key_indices": [ + 1, + 2, + 0 + ], + "nonce_indices": [ + 1, + 2, + 0 + ], + "tweak_indices": [ + 0, + 1, + 2, + 3 + ], + "is_xonly": [ + true, + false, + true, + false + ], + "signer_index": 2, + "expected": "B255FDCAC27B40C7CE7848E2D3B7BF5EA0ED756DA81565AC804CCCA3E1D5D239", + "comment": "Four tweaks: x-only, plain, x-only, plain. If an implementation prohibits applying plain tweaks after x-only tweaks, it can skip this test vector or return an error." + } + ], + "error_test_cases": [ + { + "key_indices": [ + 1, + 2, + 0 + ], + "nonce_indices": [ + 1, + 2, + 0 + ], + "tweak_indices": [ + 4 + ], + "is_xonly": [ + false + ], + "signer_index": 2, + "error": { + "type": "value", + "message": "The tweak must be less than n." + }, + "comment": "Tweak is invalid because it exceeds group size" + } + ] +} diff --git a/pkg/crypto/ec/musig2/doc.go b/pkg/crypto/ec/musig2/doc.go new file mode 100644 index 0000000..e55ff54 --- /dev/null +++ b/pkg/crypto/ec/musig2/doc.go @@ -0,0 +1,2 @@ +// Package musig2 provides an implementation of the musig2 protocol for bitcoin. +package musig2 diff --git a/pkg/crypto/ec/musig2/keys.go b/pkg/crypto/ec/musig2/keys.go new file mode 100644 index 0000000..07e5e22 --- /dev/null +++ b/pkg/crypto/ec/musig2/keys.go @@ -0,0 +1,414 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "bytes" + "fmt" + "sort" + + "next.orly.dev/pkg/utils" + + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/chainhash" + "next.orly.dev/pkg/crypto/ec/schnorr" + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +var ( + // KeyAggTagList is the tagged hash tag used to compute the hash of the + // list of sorted public keys. + KeyAggTagList = []byte("KeyAgg list") + // KeyAggTagCoeff is the tagged hash tag used to compute the key + // aggregation coefficient for each key. + KeyAggTagCoeff = []byte("KeyAgg coefficient") + // ErrTweakedKeyIsInfinity is returned if while tweaking a key, we end + // up with the point at infinity. + ErrTweakedKeyIsInfinity = fmt.Errorf("tweaked key is infinity point") + // ErrTweakedKeyOverflows is returned if a tweaking key is larger than + // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141. + ErrTweakedKeyOverflows = fmt.Errorf("tweaked key is too large") +) + +// sortableKeys defines a type of slice of public keys that implements the sort +// interface for BIP 340 keys. +type sortableKeys []*btcec.PublicKey + +// Less reports whether the element with index i must sort before the element +// with index j. +func (s sortableKeys) Less(i, j int) bool { + // TODO(roasbeef): more efficient way to compare... + keyIBytes := s[i].SerializeCompressed() + keyJBytes := s[j].SerializeCompressed() + return bytes.Compare(keyIBytes, keyJBytes) == -1 +} + +// Swap swaps the elements with indexes i and j. +func (s sortableKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Len is the number of elements in the collection. +func (s sortableKeys) Len() int { return len(s) } + +// sortKeys takes a set of public keys and returns a new slice that is a copy +// of the keys sorted in lexicographical order bytes on the x-only pubkey +// serialization. +func sortKeys(keys []*btcec.PublicKey) []*btcec.PublicKey { + keySet := sortableKeys(keys) + if sort.IsSorted(keySet) { + return keys + } + sort.Sort(keySet) + return keySet +} + +// keyHashFingerprint computes the tagged hash of the series of (sorted) public +// keys passed as input. This is used to compute the aggregation coefficient +// for each key. The final computation is: +// - H(tag=KeyAgg list, pk1 || pk2..) +func keyHashFingerprint(keys []*btcec.PublicKey, sort bool) []byte { + if sort { + keys = sortKeys(keys) + } + // We'll create a single buffer and slice into that so the bytes buffer + // doesn't continually need to grow the underlying buffer. + keyAggBuf := make([]byte, 33*len(keys)) + keyBytes := bytes.NewBuffer(keyAggBuf[0:0]) + for _, key := range keys { + keyBytes.Write(key.SerializeCompressed()) + } + h := chainhash.TaggedHash(KeyAggTagList, keyBytes.Bytes()) + return h[:] +} + +// keyBytesEqual returns true if two keys are the same based on the compressed +// serialization of each key. +func keyBytesEqual(a, b *btcec.PublicKey) bool { + return utils.FastEqual(a.SerializeCompressed(), b.SerializeCompressed()) +} + +// aggregationCoefficient computes the key aggregation coefficient for the +// specified target key. The coefficient is computed as: +// - H(tag=KeyAgg coefficient, keyHashFingerprint(pks) || pk) +func aggregationCoefficient( + keySet []*btcec.PublicKey, + targetKey *btcec.PublicKey, keysHash []byte, + secondKeyIdx int, +) *btcec.ModNScalar { + + var mu btcec.ModNScalar + // If this is the second key, then this coefficient is just one. + if secondKeyIdx != -1 && keyBytesEqual(keySet[secondKeyIdx], targetKey) { + return mu.SetInt(1) + } + // Otherwise, we'll compute the full finger print hash for this given + // key and then use that to compute the coefficient tagged hash: + // * H(tag=KeyAgg coefficient, keyHashFingerprint(pks, pk) || pk) + var coefficientBytes [65]byte + copy(coefficientBytes[:], keysHash[:]) + copy(coefficientBytes[32:], targetKey.SerializeCompressed()) + muHash := chainhash.TaggedHash(KeyAggTagCoeff, coefficientBytes[:]) + mu.SetByteSlice(muHash[:]) + return &mu +} + +// secondUniqueKeyIndex returns the index of the second unique key. If all keys +// are the same, then a value of -1 is returned. +func secondUniqueKeyIndex(keySet []*btcec.PublicKey, sort bool) int { + if sort { + keySet = sortKeys(keySet) + } + // Find the first key that isn't the same as the very first key (second + // unique key). + for i := range keySet { + if !keyBytesEqual(keySet[i], keySet[0]) { + return i + } + } + // A value of negative one is used to indicate that all the keys in the + // sign set are actually equal, which in practice actually makes musig2 + // useless, but we need a value to distinguish this case. + return -1 +} + +// KeyTweakDesc describes a tweak to be applied to the aggregated public key +// generation and signing process. The IsXOnly specifies if the target key +// should be converted to an x-only public key before tweaking. +type KeyTweakDesc struct { + // Tweak is the 32-byte value that will modify the public key. + Tweak [32]byte + // IsXOnly if true, then the public key will be mapped to an x-only key + // before the tweaking operation is applied. + IsXOnly bool +} + +// KeyAggOption is a functional option argument that allows callers to specify +// more or less information that has been pre-computed to the main routine. +type KeyAggOption func(*keyAggOption) + +// keyAggOption houses the set of functional options that modify key +// aggregation. +type keyAggOption struct { + // keyHash is the output of keyHashFingerprint for a given set of keys. + keyHash []byte + // uniqueKeyIndex is the pre-computed index of the second unique key. + uniqueKeyIndex *int + // tweaks specifies a series of tweaks to be applied to the aggregated + // public key. + tweaks []KeyTweakDesc + // taprootTweak controls if the tweaks above should be applied in a BIP + // 340 style. + taprootTweak bool + // bip86Tweak specifies that the taproot tweak should be done in a BIP + // 86 style, where we don't expect an actual tweak and instead just + // commit to the public key itself. + bip86Tweak bool +} + +// WithKeysHash allows key aggregation to be optimize, by allowing the caller +// to specify the hash of all the keys. +func WithKeysHash(keyHash []byte) KeyAggOption { + return func(o *keyAggOption) { o.keyHash = keyHash } +} + +// WithUniqueKeyIndex allows the caller to specify the index of the second +// unique key. +func WithUniqueKeyIndex(idx int) KeyAggOption { + return func(o *keyAggOption) { + i := idx + o.uniqueKeyIndex = &i + } +} + +// WithKeyTweaks allows a caller to specify a series of 32-byte tweaks that +// should be applied to the final aggregated public key. +func WithKeyTweaks(tweaks ...KeyTweakDesc) KeyAggOption { + return func(o *keyAggOption) { o.tweaks = tweaks } +} + +// WithTaprootKeyTweak specifies that within this context, the final key should +// use the taproot tweak as defined in BIP 341: outputKey = internalKey + +// h_tapTweak(internalKey || scriptRoot). In this case, the aggregated key +// before the tweak will be used as the internal key. +// +// This option should be used instead of WithKeyTweaks when the aggregated key +// is intended to be used as a taproot output key that commits to a script +// root. +func WithTaprootKeyTweak(scriptRoot []byte) KeyAggOption { + return func(o *keyAggOption) { + var tweak [32]byte + copy(tweak[:], scriptRoot[:]) + o.tweaks = []KeyTweakDesc{ + { + Tweak: tweak, + IsXOnly: true, + }, + } + o.taprootTweak = true + } +} + +// WithBIP86KeyTweak specifies that then during key aggregation, the BIP 86 +// tweak which just commits to the hash of the serialized public key should be +// used. This option should be used when signing with a key that was derived +// using BIP 86. +func WithBIP86KeyTweak() KeyAggOption { + return func(o *keyAggOption) { + o.tweaks = []KeyTweakDesc{{IsXOnly: true}} + o.taprootTweak = true + o.bip86Tweak = true + } +} + +// defaultKeyAggOptions returns the set of default arguments for key +// aggregation. +func defaultKeyAggOptions() *keyAggOption { return &keyAggOption{} } + +// hasEvenY returns true if the affine representation of the passed jacobian +// point has an even y coordinate. +// +// TODO(roasbeef): double check, can just check the y coord even not jacobian? +func hasEvenY(pJ btcec.JacobianPoint) bool { + pJ.ToAffine() + p := btcec.NewPublicKey(&pJ.X, &pJ.Y) + keyBytes := p.SerializeCompressed() + return keyBytes[0] == secp256k1.PubKeyFormatCompressedEven +} + +// tweakKey applies a tweaks to the passed public key using the specified +// tweak. The parityAcc and tweakAcc are returned (in that order) which +// includes the accumulate ration of the parity factor and the tweak multiplied +// by the parity factor. The xOnly bool specifies if this is to be an x-only +// tweak or not. +func tweakKey( + keyJ btcec.JacobianPoint, parityAcc btcec.ModNScalar, + tweak [32]byte, + tweakAcc btcec.ModNScalar, + xOnly bool, +) (btcec.JacobianPoint, btcec.ModNScalar, btcec.ModNScalar, error) { + + // First we'll compute the new parity factor for this key. If the key has + // an odd y coordinate (not even), then we'll need to negate it (multiply + // by -1 mod n, in this case). + var parityFactor btcec.ModNScalar + if xOnly && !hasEvenY(keyJ) { + parityFactor.SetInt(1).Negate() + } else { + parityFactor.SetInt(1) + } + + // Next, map the tweak into a mod n integer so we can use it for + // manipulations below. + tweakInt := new(btcec.ModNScalar) + overflows := tweakInt.SetBytes(&tweak) + if overflows == 1 { + return keyJ, parityAcc, tweakAcc, ErrTweakedKeyOverflows + } + // Next, we'll compute: Q_i = g*Q + t*G, where g is our parityFactor and t + // is the tweakInt above. We'll space things out a bit to make it easier to + // follow. + // + // First compute t*G: + var tweakedGenerator btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(tweakInt, &tweakedGenerator) + // Next compute g*Q: + btcec.ScalarMultNonConst(&parityFactor, &keyJ, &keyJ) + // Finally add both of them together to get our final + // tweaked point. + btcec.AddNonConst(&tweakedGenerator, &keyJ, &keyJ) + // As a sanity check, make sure that we didn't just end up with the + // point at infinity. + if keyJ == infinityPoint { + return keyJ, parityAcc, tweakAcc, ErrTweakedKeyIsInfinity + } + // As a final wrap up step, we'll accumulate the parity + // factor and also this tweak into the final set of accumulators. + parityAcc.Mul(&parityFactor) + tweakAcc.Mul(&parityFactor).Add(tweakInt) + return keyJ, parityAcc, tweakAcc, nil +} + +// AggregateKey is a final aggregated key along with a possible version of the +// key without any tweaks applied. +type AggregateKey struct { + // FinalKey is the final aggregated key which may include one or more + // tweaks applied to it. + FinalKey *btcec.PublicKey + // PreTweakedKey is the aggregated *before* any tweaks have been + // applied. This should be used as the internal key in taproot + // contexts. + PreTweakedKey *btcec.PublicKey +} + +// AggregateKeys takes a list of possibly unsorted keys and returns a single +// aggregated key as specified by the musig2 key aggregation algorithm. A nil +// value can be passed for keyHash, which causes this function to re-derive it. +// In addition to the combined public key, the parity accumulator and the tweak +// accumulator are returned as well. +func AggregateKeys( + keys []*btcec.PublicKey, sort bool, + keyOpts ...KeyAggOption, +) ( + *AggregateKey, *btcec.ModNScalar, *btcec.ModNScalar, error, +) { + // First, parse the set of optional signing options. + opts := defaultKeyAggOptions() + for _, option := range keyOpts { + option(opts) + } + // Sort the set of public key so we know we're working with them in + // sorted order for all the routines below. + if sort { + keys = sortKeys(keys) + } + // The caller may provide the hash of all the keys as an optimization + // during signing, as it already needs to be computed. + if opts.keyHash == nil { + opts.keyHash = keyHashFingerprint(keys, sort) + } + // A caller may also specify the unique key index themselves so we + // don't need to re-compute it. + if opts.uniqueKeyIndex == nil { + idx := secondUniqueKeyIndex(keys, sort) + opts.uniqueKeyIndex = &idx + } + // For each key, we'll compute the intermediate blinded key: a_i*P_i, + // where a_i is the aggregation coefficient for that key, and P_i is + // the key itself, then accumulate that (addition) into the main final + // key: P = P_1 + P_2 ... P_N. + var finalKeyJ btcec.JacobianPoint + for _, key := range keys { + // Port the key over to Jacobian coordinates as we need it in + // this format for the routines below. + var keyJ btcec.JacobianPoint + key.AsJacobian(&keyJ) + // Compute the aggregation coefficient for the key, then + // multiply it by the key itself: P_i' = a_i*P_i. + var tweakedKeyJ btcec.JacobianPoint + a := aggregationCoefficient( + keys, key, opts.keyHash, *opts.uniqueKeyIndex, + ) + btcec.ScalarMultNonConst(a, &keyJ, &tweakedKeyJ) + // Finally accumulate this into the final key in an incremental + // fashion. + btcec.AddNonConst(&finalKeyJ, &tweakedKeyJ, &finalKeyJ) + } + + // We'll copy over the key at this point, since this represents the + // aggregated key before any tweaks have been applied. This'll be used + // as the internal key for script path proofs. + finalKeyJ.ToAffine() + combinedKey := btcec.NewPublicKey(&finalKeyJ.X, &finalKeyJ.Y) + // At this point, if this is a taproot tweak, then we'll modify the + // base tweak value to use the BIP 341 tweak value. + if opts.taprootTweak { + // Emulate the same behavior as txscript.ComputeTaprootOutputKey + // which only operates on the x-only public key. + key, _ := schnorr.ParsePubKey( + schnorr.SerializePubKey( + combinedKey, + ), + ) + // We only use the actual tweak bytes if we're not committing + // to a BIP-0086 key only spend output. Otherwise, we just + // commit to the internal key and an empty byte slice as the + // root hash. + tweakBytes := []byte{} + if !opts.bip86Tweak { + tweakBytes = opts.tweaks[0].Tweak[:] + } + // Compute the taproot key tagged hash of: + // h_tapTweak(internalKey || scriptRoot). We only do this for + // the first one, as you can only specify a single tweak when + // using the taproot mode with this API. + tapTweakHash := chainhash.TaggedHash( + chainhash.TagTapTweak, schnorr.SerializePubKey(key), + tweakBytes, + ) + opts.tweaks[0].Tweak = *tapTweakHash + } + + var ( + err error + tweakAcc btcec.ModNScalar + parityAcc btcec.ModNScalar + ) + parityAcc.SetInt(1) + // In this case we have a set of tweaks, so we'll incrementally apply + // each one, until we have our final tweaked key, and the related + // accumulators. + for i := 1; i <= len(opts.tweaks); i++ { + finalKeyJ, parityAcc, tweakAcc, err = tweakKey( + finalKeyJ, parityAcc, opts.tweaks[i-1].Tweak, tweakAcc, + opts.tweaks[i-1].IsXOnly, + ) + if err != nil { + return nil, nil, nil, err + } + } + finalKeyJ.ToAffine() + finalKey := btcec.NewPublicKey(&finalKeyJ.X, &finalKeyJ.Y) + return &AggregateKey{ + PreTweakedKey: combinedKey, + FinalKey: finalKey, + }, &parityAcc, &tweakAcc, nil +} diff --git a/pkg/crypto/ec/musig2/keys_test.go b/pkg/crypto/ec/musig2/keys_test.go new file mode 100644 index 0000000..3ac79b9 --- /dev/null +++ b/pkg/crypto/ec/musig2/keys_test.go @@ -0,0 +1,332 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "encoding/json" + "fmt" + "os" + "path" + "strings" + "testing" + + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/schnorr" + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/encoders/hex" + + "github.com/stretchr/testify/require" +) + +const ( + keySortTestVectorFileName = "key_sort_vectors.json" + keyAggTestVectorFileName = "key_agg_vectors.json" + keyTweakTestVectorFileName = "tweak_vectors.json" +) + +type keySortTestVector struct { + PubKeys []string `json:"pubkeys"` + SortedKeys []string `json:"sorted_pubkeys"` +} + +// TestMusig2KeySort tests that keys are properly sorted according to the +// musig2 test vectors. +func TestMusig2KeySort(t *testing.T) { + t.Parallel() + testVectorPath := path.Join( + testVectorBaseDir, keySortTestVectorFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + var testCase keySortTestVector + require.NoError(t, json.Unmarshal(testVectorBytes, &testCase)) + keys := make([]*btcec.PublicKey, len(testCase.PubKeys)) + for i, keyStr := range testCase.PubKeys { + pubKey, err := btcec.ParsePubKey(mustParseHex(keyStr)) + require.NoError(t, err) + keys[i] = pubKey + } + sortedKeys := sortKeys(keys) + expectedKeys := make([]*btcec.PublicKey, len(testCase.PubKeys)) + for i, keyStr := range testCase.SortedKeys { + pubKey, err := btcec.ParsePubKey(mustParseHex(keyStr)) + require.NoError(t, err) + expectedKeys[i] = pubKey + } + require.Equal(t, sortedKeys, expectedKeys) +} + +type keyAggValidTest struct { + Indices []int `json:"key_indices"` + Expected string `json:"expected"` +} + +type keyAggError struct { + Type string `json:"type"` + Signer int `json:"signer"` + Contring string `json:"contrib"` +} + +type keyAggInvalidTest struct { + Indices []int `json:"key_indices"` + TweakIndices []int `json:"tweak_indices"` + IsXOnly []bool `json:"is_xonly"` + Comment string `json:"comment"` +} + +type keyAggTestVectors struct { + PubKeys []string `json:"pubkeys"` + Tweaks []string `json:"tweaks"` + ValidCases []keyAggValidTest `json:"valid_test_cases"` + InvalidCases []keyAggInvalidTest `json:"error_test_cases"` +} + +func keysFromIndices( + t *testing.T, indices []int, + pubKeys []string, +) ([]*btcec.PublicKey, error) { + t.Helper() + inputKeys := make([]*btcec.PublicKey, len(indices)) + for i, keyIdx := range indices { + var err error + inputKeys[i], err = btcec.ParsePubKey( + mustParseHex(pubKeys[keyIdx]), + ) + if err != nil { + return nil, err + } + } + return inputKeys, nil +} + +func tweaksFromIndices( + t *testing.T, indices []int, + tweaks []string, isXonly []bool, +) []KeyTweakDesc { + + t.Helper() + testTweaks := make([]KeyTweakDesc, len(indices)) + for i, idx := range indices { + var rawTweak [32]byte + copy(rawTweak[:], mustParseHex(tweaks[idx])) + testTweaks[i] = KeyTweakDesc{ + Tweak: rawTweak, + IsXOnly: isXonly[i], + } + } + return testTweaks +} + +// TestMuSig2KeyAggTestVectors tests that this implementation of musig2 key +// aggregation lines up with the secp256k1-zkp test vectors. +func TestMuSig2KeyAggTestVectors(t *testing.T) { + t.Parallel() + testVectorPath := path.Join( + testVectorBaseDir, keyAggTestVectorFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + var testCases keyAggTestVectors + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + tweaks := make([][]byte, len(testCases.Tweaks)) + for i := range testCases.Tweaks { + tweaks[i] = mustParseHex(testCases.Tweaks[i]) + } + for i, testCase := range testCases.ValidCases { + testCase := testCase + // Assemble the set of keys we'll pass in based on their key + // index. We don't use sorting to ensure we send the keys in + // the exact same order as the test vectors do. + inputKeys, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + require.NoError(t, err) + t.Run( + fmt.Sprintf("test_case=%v", i), func(t *testing.T) { + uniqueKeyIndex := secondUniqueKeyIndex(inputKeys, false) + opts := []KeyAggOption{WithUniqueKeyIndex(uniqueKeyIndex)} + + combinedKey, _, _, err := AggregateKeys( + inputKeys, false, opts..., + ) + require.NoError(t, err) + + require.Equal( + t, schnorr.SerializePubKey(combinedKey.FinalKey), + mustParseHex(testCase.Expected), + ) + }, + ) + } + for _, testCase := range testCases.InvalidCases { + testCase := testCase + testName := fmt.Sprintf( + "invalid_%v", + strings.ToLower(testCase.Comment), + ) + t.Run( + testName, func(t *testing.T) { + // For each test, we'll extract the set of input keys + // as well as the tweaks since this set of cases also + // exercises error cases related to the set of tweaks. + inputKeys, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + // In this set of test cases, we should only get this + // for the very first vector. + if err != nil { + switch testCase.Comment { + case "Invalid public key": + require.ErrorIs( + t, err, + secp256k1.ErrPubKeyNotOnCurve, + ) + case "Public key exceeds field size": + require.ErrorIs( + t, err, secp256k1.ErrPubKeyXTooBig, + ) + case "First byte of public key is not 2 or 3": + require.ErrorIs( + t, err, + secp256k1.ErrPubKeyInvalidFormat, + ) + default: + t.Fatalf("uncaught err: %v", err) + } + return + } + var tweaks []KeyTweakDesc + if len(testCase.TweakIndices) != 0 { + tweaks = tweaksFromIndices( + t, testCase.TweakIndices, testCases.Tweaks, + testCase.IsXOnly, + ) + } + uniqueKeyIndex := secondUniqueKeyIndex(inputKeys, false) + opts := []KeyAggOption{ + WithUniqueKeyIndex(uniqueKeyIndex), + } + if len(tweaks) != 0 { + opts = append(opts, WithKeyTweaks(tweaks...)) + } + _, _, _, err = AggregateKeys( + inputKeys, false, opts..., + ) + require.Error(t, err) + switch testCase.Comment { + case "Tweak is out of range": + require.ErrorIs(t, err, ErrTweakedKeyOverflows) + case "Intermediate tweaking result is point at infinity": + require.ErrorIs(t, err, ErrTweakedKeyIsInfinity) + default: + t.Fatalf("uncaught err: %v", err) + } + }, + ) + } +} + +type keyTweakInvalidTest struct { + Indices []int `json:"key_indices"` + NonceIndices []int `json:"nonce_indices"` + TweakIndices []int `json:"tweak_indices"` + IsXOnly []bool `json:"is_only"` + SignerIndex int `json:"signer_index"` + Comment string `json:"comment"` +} + +type keyTweakValidTest struct { + Indices []int `json:"key_indices"` + NonceIndices []int `json:"nonce_indices"` + TweakIndices []int `json:"tweak_indices"` + IsXOnly []bool `json:"is_xonly"` + SignerIndex int `json:"signer_index"` + Expected string `json:"expected"` + Comment string `json:"comment"` +} + +type keyTweakVector struct { + SecKey string `json:"sk"` + PubKeys []string `json:"pubkeys"` + PrivNonce string `json:"secnonce"` + PubNonces []string `json:"pnonces"` + AggNnoce string `json:"aggnonce"` + Tweaks []string `json:"tweaks"` + Msg string `json:"msg"` + ValidCases []keyTweakValidTest `json:"valid_test_cases"` + InvalidCases []keyTweakInvalidTest `json:"error_test_cases"` +} + +func pubNoncesFromIndices( + t *testing.T, nonceIndices []int, + pubNonces []string, +) [][PubNonceSize]byte { + + nonces := make([][PubNonceSize]byte, len(nonceIndices)) + for i, idx := range nonceIndices { + var pubNonce [PubNonceSize]byte + copy(pubNonce[:], mustParseHex(pubNonces[idx])) + nonces[i] = pubNonce + } + return nonces +} + +// TestMuSig2TweakTestVectors tests that we properly handle the various edge +// cases related to tweaking public keys. +func TestMuSig2TweakTestVectors(t *testing.T) { + t.Parallel() + testVectorPath := path.Join( + testVectorBaseDir, keyTweakTestVectorFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + var testCases keyTweakVector + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + privKey, _ := btcec.SecKeyFromBytes(mustParseHex(testCases.SecKey)) + var msg [32]byte + copy(msg[:], mustParseHex(testCases.Msg)) + var secNonce [SecNonceSize]byte + copy(secNonce[:], mustParseHex(testCases.PrivNonce)) + for _, testCase := range testCases.ValidCases { + testCase := testCase + testName := fmt.Sprintf( + "valid_%v", + strings.ToLower(testCase.Comment), + ) + t.Run( + testName, func(t *testing.T) { + pubKeys, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + require.NoError(t, err) + var tweaks []KeyTweakDesc + if len(testCase.TweakIndices) != 0 { + tweaks = tweaksFromIndices( + t, testCase.TweakIndices, + testCases.Tweaks, testCase.IsXOnly, + ) + } + pubNonces := pubNoncesFromIndices( + t, testCase.NonceIndices, testCases.PubNonces, + ) + combinedNonce, err := AggregateNonces(pubNonces) + require.NoError(t, err) + var opts []SignOption + if len(tweaks) != 0 { + opts = append(opts, WithTweaks(tweaks...)) + } + partialSig, err := Sign( + secNonce, privKey, combinedNonce, pubKeys, + msg, opts..., + ) + var partialSigBytes [32]byte + partialSig.S.PutBytesUnchecked(partialSigBytes[:]) + require.Equal( + t, hex.Enc(partialSigBytes[:]), + hex.Enc(mustParseHex(testCase.Expected)), + ) + + }, + ) + } +} diff --git a/pkg/crypto/ec/musig2/musig2_test.go b/pkg/crypto/ec/musig2/musig2_test.go new file mode 100644 index 0000000..04c07e5 --- /dev/null +++ b/pkg/crypto/ec/musig2/musig2_test.go @@ -0,0 +1,409 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "errors" + "fmt" + "sync" + "testing" + + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/sha256" + "next.orly.dev/pkg/encoders/hex" +) + +const ( + testVectorBaseDir = "data" +) + +func mustParseHex(str string) []byte { + b, err := hex.Dec(str) + if err != nil { + panic(fmt.Errorf("unable to parse hex: %v", err)) + } + return b +} + +type signer struct { + privKey *btcec.SecretKey + pubKey *btcec.PublicKey + nonces *Nonces + partialSig *PartialSignature +} + +type signerSet []signer + +func (s signerSet) keys() []*btcec.PublicKey { + keys := make([]*btcec.PublicKey, len(s)) + for i := 0; i < len(s); i++ { + keys[i] = s[i].pubKey + } + return keys +} + +func (s signerSet) partialSigs() []*PartialSignature { + sigs := make([]*PartialSignature, len(s)) + for i := 0; i < len(s); i++ { + sigs[i] = s[i].partialSig + } + return sigs +} + +func (s signerSet) pubNonces() [][PubNonceSize]byte { + nonces := make([][PubNonceSize]byte, len(s)) + for i := 0; i < len(s); i++ { + nonces[i] = s[i].nonces.PubNonce + } + return nonces +} + +func (s signerSet) combinedKey() *btcec.PublicKey { + uniqueKeyIndex := secondUniqueKeyIndex(s.keys(), false) + key, _, _, _ := AggregateKeys( + s.keys(), false, WithUniqueKeyIndex(uniqueKeyIndex), + ) + return key.FinalKey +} + +// testMultiPartySign executes a multi-party signing context w/ 100 signers. +func testMultiPartySign( + t *testing.T, taprootTweak []byte, + tweaks ...KeyTweakDesc, +) { + + const numSigners = 100 + // First generate the set of signers along with their public keys. + signerKeys := make([]*btcec.SecretKey, numSigners) + signSet := make([]*btcec.PublicKey, numSigners) + for i := 0; i < numSigners; i++ { + privKey, err := btcec.NewSecretKey() + if err != nil { + t.Fatalf("unable to gen priv key: %v", err) + } + pubKey := privKey.PubKey() + signerKeys[i] = privKey + signSet[i] = pubKey + } + var combinedKey *btcec.PublicKey + var ctxOpts []ContextOption + switch { + case len(taprootTweak) == 0: + ctxOpts = append(ctxOpts, WithBip86TweakCtx()) + case taprootTweak != nil: + ctxOpts = append(ctxOpts, WithTaprootTweakCtx(taprootTweak)) + case len(tweaks) != 0: + ctxOpts = append(ctxOpts, WithTweakedContext(tweaks...)) + } + ctxOpts = append(ctxOpts, WithKnownSigners(signSet)) + // Now that we have all the signers, we'll make a new context, then + // generate a new session for each of them(which handles nonce + // generation). + signers := make([]*Session, numSigners) + for i, signerKey := range signerKeys { + signCtx, err := NewContext( + signerKey, false, ctxOpts..., + ) + if err != nil { + t.Fatalf("unable to generate context: %v", err) + } + if combinedKey == nil { + combinedKey, err = signCtx.CombinedKey() + if err != nil { + t.Fatalf("combined key not available: %v", err) + } + } + session, err := signCtx.NewSession() + if err != nil { + t.Fatalf("unable to generate new session: %v", err) + } + signers[i] = session + } + // Next, in the pre-signing phase, we'll send all the nonces to each + // signer. + var wg sync.WaitGroup + for i, signCtx := range signers { + signCtx := signCtx + wg.Add(1) + go func(idx int, signer *Session) { + defer wg.Done() + for j, otherCtx := range signers { + if idx == j { + continue + } + nonce := otherCtx.PublicNonce() + haveAll, err := signer.RegisterPubNonce(nonce) + if err != nil { + t.Fatalf("unable to add public nonce") + } + if j == len(signers)-1 && !haveAll { + t.Fatalf("all public nonces should have been detected") + } + } + }(i, signCtx) + } + wg.Wait() + msg := sha256.Sum256([]byte("let's get taprooty")) + // In the final step, we'll use the first signer as our combiner, and + // generate a signature for each signer, and then accumulate that with + // the combiner. + combiner := signers[0] + for i := range signers { + signer := signers[i] + partialSig, err := signer.Sign(msg) + if err != nil { + t.Fatalf("unable to generate partial sig: %v", err) + } + // We don't need to combine the signature for the very first + // signer, as it already has that partial signature. + if i != 0 { + haveAll, err := combiner.CombineSig(partialSig) + if err != nil { + t.Fatalf("unable to combine sigs: %v", err) + } + + if i == len(signers)-1 && !haveAll { + t.Fatalf("final sig wasn't reconstructed") + } + } + } + // Finally we'll combined all the nonces, and ensure that it validates + // as a single schnorr signature. + finalSig := combiner.FinalSig() + if !finalSig.Verify(msg[:], combinedKey) { + t.Fatalf("final sig is invalid!") + } + // Verify that if we try to sign again with any of the existing + // signers, then we'll get an error as the nonces have already been + // used. + for _, signer := range signers { + _, err := signer.Sign(msg) + if err != ErrSigningContextReuse { + t.Fatalf("expected to get signing context reuse") + } + } +} + +// TestMuSigMultiParty tests that for a given set of 100 signers, we're able to +// properly generate valid sub signatures, which ultimately can be combined +// into a single valid signature. +func TestMuSigMultiParty(t *testing.T) { + t.Parallel() + testTweak := [32]byte{ + 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, + 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, + 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, + 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, + } + t.Run( + "no_tweak", func(t *testing.T) { + t.Parallel() + + testMultiPartySign(t, nil) + }, + ) + t.Run( + "tweaked", func(t *testing.T) { + t.Parallel() + + testMultiPartySign( + t, nil, KeyTweakDesc{ + Tweak: testTweak, + }, + ) + }, + ) + t.Run( + "tweaked_x_only", func(t *testing.T) { + t.Parallel() + + testMultiPartySign( + t, nil, KeyTweakDesc{ + Tweak: testTweak, + IsXOnly: true, + }, + ) + }, + ) + t.Run( + "taproot_tweaked_x_only", func(t *testing.T) { + t.Parallel() + + testMultiPartySign(t, testTweak[:]) + }, + ) + t.Run( + "taproot_bip_86", func(t *testing.T) { + t.Parallel() + + testMultiPartySign(t, []byte{}) + }, + ) +} + +// TestMuSigEarlyNonce tests that for protocols where nonces need to be +// exchagned before all signers are known, the context API works as expected. +func TestMuSigEarlyNonce(t *testing.T) { + t.Parallel() + privKey1, err := btcec.NewSecretKey() + if err != nil { + t.Fatalf("unable to gen priv key: %v", err) + } + privKey2, err := btcec.NewSecretKey() + if err != nil { + t.Fatalf("unable to gen priv key: %v", err) + } + // If we try to make a context, with just the secret key and sorting + // value, we should get an error. + _, err = NewContext(privKey1, true) + if !errors.Is(err, ErrSignersNotSpecified) { + t.Fatalf("unexpected ctx error: %v", err) + } + signers := []*btcec.PublicKey{privKey1.PubKey(), privKey2.PubKey()} + numSigners := len(signers) + ctx1, err := NewContext( + privKey1, true, WithNumSigners(numSigners), WithEarlyNonceGen(), + ) + if err != nil { + t.Fatalf("unable to make ctx: %v", err) + } + pubKey1 := ctx1.PubKey() + ctx2, err := NewContext( + privKey2, true, WithKnownSigners(signers), WithEarlyNonceGen(), + ) + if err != nil { + t.Fatalf("unable to make ctx: %v", err) + } + pubKey2 := ctx2.PubKey() + // At this point, the combined key shouldn't be available for signer 1, + // but should be for signer 2, as they know about all signers. + if _, err := ctx1.CombinedKey(); !errors.Is(err, ErrNotEnoughSigners) { + t.Fatalf("unepxected error: %v", err) + } + _, err = ctx2.CombinedKey() + if err != nil { + t.Fatalf("unable to get combined key: %v", err) + } + // The early nonces _should_ be available at this point. + nonce1, err := ctx1.EarlySessionNonce() + if err != nil { + t.Fatalf("session nonce not available: %v", err) + } + nonce2, err := ctx2.EarlySessionNonce() + if err != nil { + t.Fatalf("session nonce not available: %v", err) + } + // The number of registered signers should still be 1 for both parties. + if ctx1.NumRegisteredSigners() != 1 { + t.Fatalf( + "expected 1 signer, instead have: %v", + ctx1.NumRegisteredSigners(), + ) + } + if ctx2.NumRegisteredSigners() != 2 { + t.Fatalf( + "expected 2 signers, instead have: %v", + ctx2.NumRegisteredSigners(), + ) + } + // If we try to make a session, we should get an error since we dn't + // have all the signers yet. + if _, err := ctx1.NewSession(); !errors.Is(err, ErrNotEnoughSigners) { + t.Fatalf("unexpected session key error: %v", err) + } + // The combined key should also be unavailable as well. + if _, err := ctx1.CombinedKey(); !errors.Is(err, ErrNotEnoughSigners) { + t.Fatalf("unexpected combined key error: %v", err) + } + // We'll now register the other signer for party 1. + done, err := ctx1.RegisterSigner(&pubKey2) + if err != nil { + t.Fatalf("unable to register signer: %v", err) + } + if !done { + t.Fatalf("signer 1 doesn't have all keys") + } + // If we try to register the signer again, we should get an error. + _, err = ctx2.RegisterSigner(&pubKey1) + if !errors.Is(err, ErrAlreadyHaveAllSigners) { + t.Fatalf("should not be able to register too many signers") + } + // We should be able to create the session at this point. + session1, err := ctx1.NewSession() + if err != nil { + t.Fatalf("unable to create new session: %v", err) + } + session2, err := ctx2.NewSession() + if err != nil { + t.Fatalf("unable to create new session: %v", err) + } + msg := sha256.Sum256([]byte("let's get taprooty, LN style")) + // If we try to sign before we have the combined nonce, we shoudl get + // an error. + _, err = session1.Sign(msg) + if !errors.Is(err, ErrCombinedNonceUnavailable) { + t.Fatalf("unable to gen sig: %v", err) + } + // Now we can exchange nonces to continue with the rest of the signing + // process as normal. + done, err = session1.RegisterPubNonce(nonce2.PubNonce) + if err != nil { + t.Fatalf("unable to register nonce: %v", err) + } + if !done { + t.Fatalf("signer 1 doesn't have all nonces") + } + done, err = session2.RegisterPubNonce(nonce1.PubNonce) + if err != nil { + t.Fatalf("unable to register nonce: %v", err) + } + if !done { + t.Fatalf("signer 2 doesn't have all nonces") + } + // Registering the nonce again should error out. + _, err = session2.RegisterPubNonce(nonce1.PubNonce) + if !errors.Is(err, ErrAlreadyHaveAllNonces) { + t.Fatalf("shouldn't be able to register nonces twice") + } + // Sign the message and combine the two partial sigs into one. + _, err = session1.Sign(msg) + if err != nil { + t.Fatalf("unable to gen sig: %v", err) + } + sig2, err := session2.Sign(msg) + if err != nil { + t.Fatalf("unable to gen sig: %v", err) + } + done, err = session1.CombineSig(sig2) + if err != nil { + t.Fatalf("unable to combine sig: %v", err) + } + if !done { + t.Fatalf("all sigs should be known now: %v", err) + } + // If we try to combine another sig, then we should get an error. + _, err = session1.CombineSig(sig2) + if !errors.Is(err, ErrAlredyHaveAllSigs) { + t.Fatalf("shouldn't be able to combine again") + } + // Finally, verify that the final signature is valid. + combinedKey, err := ctx1.CombinedKey() + if err != nil { + t.Fatalf("unexpected combined key error: %v", err) + } + finalSig := session1.FinalSig() + if !finalSig.Verify(msg[:], combinedKey) { + t.Fatalf("final sig is invalid!") + } +} + +type memsetRandReader struct { + i int +} + +func (mr *memsetRandReader) Read(buf []byte) (n int, err error) { + for i := range buf { + buf[i] = byte(mr.i) + } + return len(buf), nil +} diff --git a/pkg/crypto/ec/musig2/nonces.go b/pkg/crypto/ec/musig2/nonces.go new file mode 100644 index 0000000..7d8adc5 --- /dev/null +++ b/pkg/crypto/ec/musig2/nonces.go @@ -0,0 +1,407 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "errors" + "io" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/chainhash" + "next.orly.dev/pkg/crypto/ec/schnorr" +) + +const ( + // PubNonceSize is the size of the public nonces. Each public nonce is + // serialized the full compressed encoding, which uses 32 bytes for each + // nonce. + PubNonceSize = 66 + // SecNonceSize is the size of the secret nonces for musig2. The secret + // nonces are the corresponding secret keys to the public nonce points. + SecNonceSize = 97 +) + +var ( + // NonceAuxTag is the tag used to optionally mix in the secret key with + // the set of aux randomness. + NonceAuxTag = []byte("MuSig/aux") + // NonceGenTag is used to generate the value (from a set of required an + // optional field) that will be used as the part of the secret nonce. + NonceGenTag = []byte("MuSig/nonce") + byteOrder = binary.BigEndian + // ErrPubkeyInvalid is returned when the pubkey of the WithPublicKey + // option is not passed or of invalid length. + ErrPubkeyInvalid = errors.New("nonce generation requires a valid pubkey") +) + +// zeroSecNonce is a secret nonce that's all zeroes. This is used to check that +// we're not attempting to re-use a nonce, and also protect callers from it. +var zeroSecNonce [SecNonceSize]byte + +// Nonces holds the public and secret nonces required for musig2. +// +// TODO(roasbeef): methods on this to help w/ parsing, etc? +type Nonces struct { + // PubNonce holds the two 33-byte compressed encoded points that serve + // as the public set of nonces. + PubNonce [PubNonceSize]byte + // SecNonce holds the two 32-byte scalar values that are the secret + // keys to the two public nonces. + SecNonce [SecNonceSize]byte +} + +// secNonceToPubNonce takes our two secrete nonces, and produces their two +// corresponding EC points, serialized in compressed format. +func secNonceToPubNonce(secNonce [SecNonceSize]byte) [PubNonceSize]byte { + var k1Mod, k2Mod btcec.ModNScalar + k1Mod.SetByteSlice(secNonce[:btcec.SecKeyBytesLen]) + k2Mod.SetByteSlice(secNonce[btcec.SecKeyBytesLen:]) + var r1, r2 btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(&k1Mod, &r1) + btcec.ScalarBaseMultNonConst(&k2Mod, &r2) + // Next, we'll convert the key in jacobian format to a normal public + // key expressed in affine coordinates. + r1.ToAffine() + r2.ToAffine() + r1Pub := btcec.NewPublicKey(&r1.X, &r1.Y) + r2Pub := btcec.NewPublicKey(&r2.X, &r2.Y) + var pubNonce [PubNonceSize]byte + // The public nonces are serialized as: R1 || R2, where both keys are + // serialized in compressed format. + copy(pubNonce[:], r1Pub.SerializeCompressed()) + copy( + pubNonce[btcec.PubKeyBytesLenCompressed:], + r2Pub.SerializeCompressed(), + ) + return pubNonce +} + +// NonceGenOption is a function option that allows callers to modify how nonce +// generation happens. +type NonceGenOption func(*nonceGenOpts) + +// nonceGenOpts is the set of options that control how nonce generation +// happens. +type nonceGenOpts struct { + // randReader is what we'll use to generate a set of random bytes. If + // unspecified, then the normal crypto/rand rand.Read method will be + // used in place. + randReader io.Reader + // publicKey is the mandatory public key that will be mixed into the nonce + // generation. + publicKey []byte + // secretKey is an optional argument that's used to further augment the + // generated nonce by xor'ing it with this secret key. + secretKey []byte + // combinedKey is an optional argument that if specified, will be + // combined along with the nonce generation. + combinedKey []byte + // msg is an optional argument that will be mixed into the nonce + // derivation algorithm. + msg []byte + // auxInput is an optional argument that will be mixed into the nonce + // derivation algorithm. + auxInput []byte +} + +// cryptoRandAdapter is an adapter struct that allows us to pass in the package +// level Read function from crypto/rand into a context that accepts an +// io.Reader. +type cryptoRandAdapter struct{} + +// Read implements the io.Reader interface for the crypto/rand package. By +// default, we always use the crypto/rand reader, but the caller is able to +// specify their own generation, which can be useful for deterministic tests. +func (c *cryptoRandAdapter) Read(p []byte) (n int, err error) { + return rand.Read(p) +} + +// defaultNonceGenOpts returns the default set of nonce generation options. +func defaultNonceGenOpts() *nonceGenOpts { + return &nonceGenOpts{randReader: &cryptoRandAdapter{}} +} + +// WithCustomRand allows a caller to use a custom random number generator in +// place for crypto/rand. This should only really be used to generate +// determinstic tests. +func WithCustomRand(r io.Reader) NonceGenOption { + return func(o *nonceGenOpts) { o.randReader = r } +} + +// WithPublicKey is the mandatory public key that will be mixed into the nonce +// generation. +func WithPublicKey(pubKey *btcec.PublicKey) NonceGenOption { + return func(o *nonceGenOpts) { + o.publicKey = pubKey.SerializeCompressed() + } +} + +// WithNonceSecretKeyAux allows a caller to optionally specify a secret key +// that should be used to augment the randomness used to generate the nonces. +func WithNonceSecretKeyAux(secKey *btcec.SecretKey) NonceGenOption { + return func(o *nonceGenOpts) { o.secretKey = secKey.Serialize() } +} + +var WithNoncePrivateKeyAux = WithNonceSecretKeyAux + +// WithNonceCombinedKeyAux allows a caller to optionally specify the combined +// key used in this signing session to further augment the randomness used to +// generate nonces. +func WithNonceCombinedKeyAux(combinedKey *btcec.PublicKey) NonceGenOption { + return func(o *nonceGenOpts) { + o.combinedKey = schnorr.SerializePubKey(combinedKey) + } +} + +// WithNonceMessageAux allows a caller to optionally specify a message to be +// mixed into the randomness generated to create the nonce. +func WithNonceMessageAux(msg [32]byte) NonceGenOption { + return func(o *nonceGenOpts) { o.msg = msg[:] } +} + +// WithNonceAuxInput is a set of auxiliary randomness, similar to BIP 340 that +// can be used to further augment the nonce generation process. +func WithNonceAuxInput(aux []byte) NonceGenOption { + return func(o *nonceGenOpts) { o.auxInput = aux } +} + +// withCustomOptions allows a caller to pass a complete set of custom +// nonceGenOpts, without needing to create custom and checked structs such as +// *btcec.SecretKey. This is mainly used to match the testcases provided by +// the MuSig2 BIP. +func withCustomOptions(customOpts nonceGenOpts) NonceGenOption { + return func(o *nonceGenOpts) { + o.randReader = customOpts.randReader + o.secretKey = customOpts.secretKey + o.combinedKey = customOpts.combinedKey + o.auxInput = customOpts.auxInput + o.msg = customOpts.msg + o.publicKey = customOpts.publicKey + } +} + +// lengthWriter is a function closure that allows a caller to control how the +// length prefix of a byte slice is written. +// +// TODO(roasbeef): use type params once we bump repo version +type lengthWriter func(w io.Writer, b []byte) error + +// uint8Writer is an implementation of lengthWriter that writes the length of +// the byte slice using 1 byte. +func uint8Writer(w io.Writer, b []byte) error { + return binary.Write(w, byteOrder, uint8(len(b))) +} + +// uint32Writer is an implementation of lengthWriter that writes the length of +// the byte slice using 4 bytes. +func uint32Writer(w io.Writer, b []byte) error { + return binary.Write(w, byteOrder, uint32(len(b))) +} + +// uint32Writer is an implementation of lengthWriter that writes the length of +// the byte slice using 8 bytes. +func uint64Writer(w io.Writer, b []byte) error { + return binary.Write(w, byteOrder, uint64(len(b))) +} + +// writeBytesPrefix is used to write out: len(b) || b, to the passed io.Writer. +// The lengthWriter function closure is used to allow the caller to specify the +// precise byte packing of the length. +func writeBytesPrefix(w io.Writer, b []byte, lenWriter lengthWriter) error { + // Write out the length of the byte first, followed by the set of bytes + // itself. + if err := lenWriter(w, b); chk.T(err) { + return err + } + if _, err := w.Write(b); chk.T(err) { + return err + } + return nil +} + +// genNonceAuxBytes writes out the full byte string used to derive a secret +// nonce based on some initial randomness as well as the series of optional +// fields. The byte string used for derivation is: +// - tagged_hash("MuSig/nonce", rand || len(pk) || pk || +// len(aggpk) || aggpk || m_prefixed || len(in) || in || i). +// +// where i is the ith secret nonce being generated and m_prefixed is: +// - bytes(1, 0) if the message is blank +// - bytes(1, 1) || bytes(8, len(m)) || m if the message is present. +func genNonceAuxBytes( + rand []byte, pubkey []byte, i int, + opts *nonceGenOpts, +) (*chainhash.Hash, error) { + + var w bytes.Buffer + // First, write out the randomness generated in the prior step. + if _, err := w.Write(rand); chk.T(err) { + return nil, err + } + // Next, we'll write out: len(pk) || pk + err := writeBytesPrefix(&w, pubkey, uint8Writer) + if err != nil { + return nil, err + } + // Next, we'll write out: len(aggpk) || aggpk. + err = writeBytesPrefix(&w, opts.combinedKey, uint8Writer) + if err != nil { + return nil, err + } + switch { + // If the message isn't present, then we'll just write out a single + // uint8 of a zero byte: m_prefixed = bytes(1, 0). + case opts.msg == nil: + if _, err := w.Write([]byte{0x00}); chk.T(err) { + return nil, err + } + // Otherwise, we'll write a single byte of 0x01 with a 1 byte length + // prefix, followed by the message itself with an 8 byte length prefix: + // m_prefixed = bytes(1, 1) || bytes(8, len(m)) || m. + case len(opts.msg) == 0: + fallthrough + default: + if _, err := w.Write([]byte{0x01}); chk.T(err) { + return nil, err + } + err = writeBytesPrefix(&w, opts.msg, uint64Writer) + if err != nil { + return nil, err + } + } + // Finally we'll write out the auxiliary input. + err = writeBytesPrefix(&w, opts.auxInput, uint32Writer) + if err != nil { + return nil, err + } + // Next we'll write out the interaction/index number which will + // uniquely generate two nonces given the rest of the possibly static + // parameters. + if err := binary.Write(&w, byteOrder, uint8(i)); chk.T(err) { + return nil, err + } + // With the message buffer complete, we'll now derive the tagged hash + // using our set of params. + return chainhash.TaggedHash(NonceGenTag, w.Bytes()), nil +} + +// GenNonces generates the secret nonces, as well as the public nonces which +// correspond to an EC point generated using the secret nonce as a secret key. +func GenNonces(options ...NonceGenOption) (*Nonces, error) { + opts := defaultNonceGenOpts() + for _, opt := range options { + opt(opts) + } + // We require the pubkey option. + if opts.publicKey == nil || len(opts.publicKey) != 33 { + return nil, ErrPubkeyInvalid + } + // First, we'll start out by generating 32 random bytes drawn from our + // CSPRNG. + var randBytes [32]byte + if _, err := opts.randReader.Read(randBytes[:]); chk.T(err) { + return nil, err + } + // If the options contain a secret key, we XOR it with with the tagged + // random bytes. + if len(opts.secretKey) == 32 { + taggedHash := chainhash.TaggedHash(NonceAuxTag, randBytes[:]) + + for i := 0; i < chainhash.HashSize; i++ { + randBytes[i] = opts.secretKey[i] ^ taggedHash[i] + } + } + // Using our randomness, pubkey and the set of optional params, generate our + // two secret nonces: k1 and k2. + k1, err := genNonceAuxBytes(randBytes[:], opts.publicKey, 0, opts) + if err != nil { + return nil, err + } + k2, err := genNonceAuxBytes(randBytes[:], opts.publicKey, 1, opts) + if err != nil { + return nil, err + } + var k1Mod, k2Mod btcec.ModNScalar + k1Mod.SetBytes((*[32]byte)(k1)) + k2Mod.SetBytes((*[32]byte)(k2)) + // The secret nonces are serialized as the concatenation of the two 32 + // byte secret nonce values and the pubkey. + var nonces Nonces + k1Mod.PutBytesUnchecked(nonces.SecNonce[:]) + k2Mod.PutBytesUnchecked(nonces.SecNonce[btcec.SecKeyBytesLen:]) + copy(nonces.SecNonce[btcec.SecKeyBytesLen*2:], opts.publicKey) + // Next, we'll generate R_1 = k_1*G and R_2 = k_2*G. Along the way we + // need to map our nonce values into mod n scalars so we can work with + // the btcec API. + nonces.PubNonce = secNonceToPubNonce(nonces.SecNonce) + return &nonces, nil +} + +// AggregateNonces aggregates the set of a pair of public nonces for each party +// into a single aggregated nonces to be used for multi-signing. +func AggregateNonces(pubNonces [][PubNonceSize]byte) ( + [PubNonceSize]byte, + error, +) { + + // combineNonces is a helper function that aggregates (adds) up a + // series of nonces encoded in compressed format. It uses a slicing + // function to extra 33 bytes at a time from the packed 2x public + // nonces. + type nonceSlicer func([PubNonceSize]byte) []byte + combineNonces := func(slicer nonceSlicer) (btcec.JacobianPoint, error) { + // Convert the set of nonces into jacobian coordinates we can + // use to accumulate them all into each other. + pubNonceJs := make([]*btcec.JacobianPoint, len(pubNonces)) + for i, pubNonceBytes := range pubNonces { + // Using the slicer, extract just the bytes we need to + // decode. + var nonceJ btcec.JacobianPoint + nonceJ, err := btcec.ParseJacobian(slicer(pubNonceBytes)) + if err != nil { + return btcec.JacobianPoint{}, err + } + pubNonceJs[i] = &nonceJ + } + // Now that we have the set of complete nonces, we'll aggregate + // them: R = R_i + R_i+1 + ... + R_i+n. + var aggregateNonce btcec.JacobianPoint + for _, pubNonceJ := range pubNonceJs { + btcec.AddNonConst( + &aggregateNonce, pubNonceJ, &aggregateNonce, + ) + } + aggregateNonce.ToAffine() + return aggregateNonce, nil + } + // The final nonce public nonce is actually two nonces, one that + // aggregate the first nonce of all the parties, and the other that + // aggregates the second nonce of all the parties. + var finalNonce [PubNonceSize]byte + combinedNonce1, err := combineNonces( + func(n [PubNonceSize]byte) []byte { + return n[:btcec.PubKeyBytesLenCompressed] + }, + ) + if err != nil { + return finalNonce, err + } + combinedNonce2, err := combineNonces( + func(n [PubNonceSize]byte) []byte { + return n[btcec.PubKeyBytesLenCompressed:] + }, + ) + if err != nil { + return finalNonce, err + } + copy(finalNonce[:], btcec.JacobianToByteSlice(combinedNonce1)) + copy( + finalNonce[btcec.PubKeyBytesLenCompressed:], + btcec.JacobianToByteSlice(combinedNonce2), + ) + return finalNonce, nil +} diff --git a/pkg/crypto/ec/musig2/nonces_test.go b/pkg/crypto/ec/musig2/nonces_test.go new file mode 100644 index 0000000..dac6710 --- /dev/null +++ b/pkg/crypto/ec/musig2/nonces_test.go @@ -0,0 +1,151 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "encoding/json" + "fmt" + "os" + "path" + "testing" + + "next.orly.dev/pkg/utils" + + "next.orly.dev/pkg/encoders/hex" + + "github.com/stretchr/testify/require" +) + +type nonceGenTestCase struct { + Rand string `json:"rand_"` + Sk string `json:"sk"` + AggPk string `json:"aggpk"` + Msg *string `json:"msg"` + ExtraIn string `json:"extra_in"` + Pk string `json:"pk"` + Expected string `json:"expected"` +} + +type nonceGenTestCases struct { + TestCases []nonceGenTestCase `json:"test_cases"` +} + +const ( + nonceGenTestVectorsFileName = "nonce_gen_vectors.json" + nonceAggTestVectorsFileName = "nonce_agg_vectors.json" +) + +// TestMusig2NonceGenTestVectors tests the nonce generation function with the +// testvectors defined in the Musig2 BIP. +func TestMusig2NonceGenTestVectors(t *testing.T) { + t.Parallel() + testVectorPath := path.Join( + testVectorBaseDir, nonceGenTestVectorsFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + var testCases nonceGenTestCases + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + for i, testCase := range testCases.TestCases { + testCase := testCase + customOpts := nonceGenOpts{ + randReader: &memsetRandReader{i: 0}, + secretKey: mustParseHex(testCase.Sk), + combinedKey: mustParseHex(testCase.AggPk), + auxInput: mustParseHex(testCase.ExtraIn), + publicKey: mustParseHex(testCase.Pk), + } + if testCase.Msg != nil { + customOpts.msg = mustParseHex(*testCase.Msg) + } + t.Run( + fmt.Sprintf("test_case=%v", i), func(t *testing.T) { + nonce, err := GenNonces(withCustomOptions(customOpts)) + if err != nil { + t.Fatalf("err gen nonce aux bytes %v", err) + } + expectedBytes, _ := hex.Dec(testCase.Expected) + if !utils.FastEqual(nonce.SecNonce[:], expectedBytes) { + t.Fatalf( + "nonces don't match: expected %x, got %x", + expectedBytes, nonce.SecNonce[:], + ) + } + }, + ) + } +} + +type nonceAggError struct { + Type string `json:"type"` + Signer int `json:"signer"` + Contrib string `json:"contrib"` +} + +type nonceAggValidCase struct { + Indices []int `json:"pnonce_indices"` + Expected string `json:"expected"` + Comment string `json:"comment"` +} + +type nonceAggInvalidCase struct { + Indices []int `json:"pnonce_indices"` + Error nonceAggError `json:"error"` + Comment string `json:"comment"` + ExpectedErr string `json:"btcec_err"` +} + +type nonceAggTestCases struct { + Nonces []string `json:"pnonces"` + ValidCases []nonceAggValidCase `json:"valid_test_cases"` + InvalidCases []nonceAggInvalidCase `json:"error_test_cases"` +} + +// TestMusig2AggregateNoncesTestVectors tests that the musig2 implementation +// passes the nonce aggregration test vectors for musig2 1.0. +func TestMusig2AggregateNoncesTestVectors(t *testing.T) { + t.Parallel() + testVectorPath := path.Join( + testVectorBaseDir, nonceAggTestVectorsFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + var testCases nonceAggTestCases + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + nonces := make([][PubNonceSize]byte, len(testCases.Nonces)) + for i := range testCases.Nonces { + var nonce [PubNonceSize]byte + copy(nonce[:], mustParseHex(testCases.Nonces[i])) + nonces[i] = nonce + } + for i, testCase := range testCases.ValidCases { + testCase := testCase + var testNonces [][PubNonceSize]byte + for _, idx := range testCase.Indices { + testNonces = append(testNonces, nonces[idx]) + } + t.Run( + fmt.Sprintf("valid_case=%v", i), func(t *testing.T) { + aggregatedNonce, err := AggregateNonces(testNonces) + require.NoError(t, err) + var expectedNonce [PubNonceSize]byte + copy(expectedNonce[:], mustParseHex(testCase.Expected)) + require.Equal(t, aggregatedNonce[:], expectedNonce[:]) + }, + ) + } + + for i, testCase := range testCases.InvalidCases { + var testNonces [][PubNonceSize]byte + for _, idx := range testCase.Indices { + testNonces = append(testNonces, nonces[idx]) + } + t.Run( + fmt.Sprintf("invalid_case=%v", i), func(t *testing.T) { + _, err := AggregateNonces(testNonces) + require.True(t, err != nil) + require.Equal(t, testCase.ExpectedErr, err.Error()) + }, + ) + } +} diff --git a/pkg/crypto/ec/musig2/sign.go b/pkg/crypto/ec/musig2/sign.go new file mode 100644 index 0000000..80fe255 --- /dev/null +++ b/pkg/crypto/ec/musig2/sign.go @@ -0,0 +1,768 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "bytes" + "fmt" + "io" + + "next.orly.dev/pkg/utils" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/chainhash" + "next.orly.dev/pkg/crypto/ec/schnorr" + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +var ( + // NonceBlindTag is that tag used to construct the value b, which + // blinds the second public nonce of each party. + NonceBlindTag = []byte("MuSig/noncecoef") + + // ChallengeHashTag is the tag used to construct the challenge hash + ChallengeHashTag = []byte("BIP0340/challenge") + + // ErrNoncePointAtInfinity is returned if during signing, the fully + // combined public nonce is the point at infinity. + ErrNoncePointAtInfinity = fmt.Errorf( + "signing nonce is the infinity " + + "point", + ) + + // ErrSecKeyZero is returned when the secret key for signing is + // actually zero. + ErrSecKeyZero = fmt.Errorf("priv key is zero") + + // ErrPartialSigInvalid is returned when a partial is found to be + // invalid. + ErrPartialSigInvalid = fmt.Errorf("partial signature is invalid") + + // ErrSecretNonceZero is returned when a secret nonce is passed in a + // zero. + ErrSecretNonceZero = fmt.Errorf("secret nonce is blank") + + // ErrSecNoncePubkey is returned when the signing key does not match the + // sec nonce pubkey + ErrSecNoncePubkey = fmt.Errorf("public key does not match secnonce") + + // ErrPubkeyNotIncluded is returned when the signers pubkey is not included + // in the list of pubkeys. + ErrPubkeyNotIncluded = fmt.Errorf( + "signer's pubkey must be included" + + " in the list of pubkeys", + ) +) + +// infinityPoint is the jacobian representation of the point at infinity. +var infinityPoint btcec.JacobianPoint + +// PartialSignature reprints a partial (s-only) musig2 multi-signature. This +// isn't a valid schnorr signature by itself, as it needs to be aggregated +// along with the other partial signatures to be completed. +type PartialSignature struct { + S *btcec.ModNScalar + + R *btcec.PublicKey +} + +// NewPartialSignature returns a new instances of the partial sig struct. +func NewPartialSignature( + s *btcec.ModNScalar, + r *btcec.PublicKey, +) PartialSignature { + + return PartialSignature{ + S: s, + R: r, + } +} + +// Encode writes a serialized version of the partial signature to the passed +// io.Writer +func (p *PartialSignature) Encode(w io.Writer) error { + var sBytes [32]byte + p.S.PutBytes(&sBytes) + + if _, err := w.Write(sBytes[:]); chk.T(err) { + return err + } + + return nil +} + +// Decode attempts to parse a serialized PartialSignature stored in the io reader. +func (p *PartialSignature) Decode(r io.Reader) error { + p.S = new(btcec.ModNScalar) + + var sBytes [32]byte + if _, err := io.ReadFull(r, sBytes[:]); chk.T(err) { + return nil + } + + overflows := p.S.SetBytes(&sBytes) + if overflows == 1 { + return ErrPartialSigInvalid + } + + return nil +} + +// SignOption is a functional option argument that allows callers to modify the +// way we generate musig2 schnorr signatures. +type SignOption func(*signOptions) + +// signOptions houses the set of functional options that can be used to modify +// the method used to generate the musig2 partial signature. +type signOptions struct { + // fastSign determines if we'll skip the check at the end of the + // routine where we attempt to verify the produced signature. + fastSign bool + + // sortKeys determines if the set of keys should be sorted before doing + // key aggregation. + sortKeys bool + + // tweaks specifies a series of tweaks to be applied to the aggregated + // public key, which also partially carries over into the signing + // process. + tweaks []KeyTweakDesc + + // taprootTweak specifies a taproot specific tweak. of the tweaks + // specified above. Normally we'd just apply the raw 32 byte tweak, but + // for taproot, we first need to compute the aggregated key before + // tweaking, and then use it as the internal key. This is required as + // the taproot tweak also commits to the public key, which in this case + // is the aggregated key before the tweak. + taprootTweak []byte + + // bip86Tweak specifies that the taproot tweak should be done in a BIP + // 86 style, where we don't expect an actual tweak and instead just + // commit to the public key itself. + bip86Tweak bool +} + +// defaultSignOptions returns the default set of signing operations. +func defaultSignOptions() *signOptions { + return &signOptions{} +} + +// WithFastSign forces signing to skip the extra verification step at the end. +// Performance sensitive applications may opt to use this option to speed up +// the signing operation. +func WithFastSign() SignOption { + return func(o *signOptions) { + o.fastSign = true + } +} + +// WithSortedKeys determines if the set of signing public keys are to be sorted +// or not before doing key aggregation. +func WithSortedKeys() SignOption { + return func(o *signOptions) { + o.sortKeys = true + } +} + +// WithTweaks determines if the aggregated public key used should apply a +// series of tweaks before key aggregation. +func WithTweaks(tweaks ...KeyTweakDesc) SignOption { + return func(o *signOptions) { + o.tweaks = tweaks + } +} + +// WithTaprootSignTweak allows a caller to specify a tweak that should be used +// in a bip 340 manner when signing. This differs from WithTweaks as the tweak +// will be assumed to always be x-only and the intermediate aggregate key +// before tweaking will be used to generate part of the tweak (as the taproot +// tweak also commits to the internal key). +// +// This option should be used in the taproot context to create a valid +// signature for the keypath spend for taproot, when the output key is actually +// committing to a script path, or some other data. +func WithTaprootSignTweak(scriptRoot []byte) SignOption { + return func(o *signOptions) { + o.taprootTweak = scriptRoot + } +} + +// WithBip86SignTweak allows a caller to specify a tweak that should be used in +// a bip 340 manner when signing, factoring in BIP 86 as well. This differs +// from WithTaprootSignTweak as no true script root will be committed to, +// instead we just commit to the internal key. +// +// This option should be used in the taproot context to create a valid +// signature for the keypath spend for taproot, when the output key was +// generated using BIP 86. +func WithBip86SignTweak() SignOption { + return func(o *signOptions) { + o.bip86Tweak = true + } +} + +// computeSigningNonce calculates the final nonce used for signing. This will +// be the R value used in the final signature. +func computeSigningNonce( + combinedNonce [PubNonceSize]byte, + combinedKey *btcec.PublicKey, msg [32]byte, +) ( + *btcec.JacobianPoint, *btcec.ModNScalar, error, +) { + + // Next we'll compute the value b, that blinds our second public + // nonce: + // * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m). + var ( + nonceMsgBuf bytes.Buffer + nonceBlinder btcec.ModNScalar + ) + nonceMsgBuf.Write(combinedNonce[:]) + nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey)) + nonceMsgBuf.Write(msg[:]) + nonceBlindHash := chainhash.TaggedHash( + NonceBlindTag, nonceMsgBuf.Bytes(), + ) + nonceBlinder.SetByteSlice(nonceBlindHash[:]) + + // Next, we'll parse the public nonces into R1 and R2. + r1J, err := btcec.ParseJacobian( + combinedNonce[:btcec.PubKeyBytesLenCompressed], + ) + if err != nil { + return nil, nil, err + } + r2J, err := btcec.ParseJacobian( + combinedNonce[btcec.PubKeyBytesLenCompressed:], + ) + if err != nil { + return nil, nil, err + } + + // With our nonce blinding value, we'll now combine both the public + // nonces, using the blinding factor to tweak the second nonce: + // * R = R_1 + b*R_2 + var nonce btcec.JacobianPoint + btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J) + btcec.AddNonConst(&r1J, &r2J, &nonce) + + // If the combined nonce is the point at infinity, we'll use the + // generator point instead. + if nonce == infinityPoint { + G := btcec.Generator() + G.AsJacobian(&nonce) + } + + return &nonce, &nonceBlinder, nil +} + +// Sign generates a musig2 partial signature given the passed key set, secret +// nonce, public nonce, and secret keys. This method returns an error if the +// generated nonces are either too large, or end up mapping to the point at +// infinity. +func Sign( + secNonce [SecNonceSize]byte, privKey *btcec.SecretKey, + combinedNonce [PubNonceSize]byte, pubKeys []*btcec.PublicKey, + msg [32]byte, signOpts ...SignOption, +) (*PartialSignature, error) { + + // First, parse the set of optional signing options. + opts := defaultSignOptions() + for _, option := range signOpts { + option(opts) + } + + // Check that our signing key belongs to the secNonce + if !utils.FastEqual( + secNonce[btcec.SecKeyBytesLen*2:], + privKey.PubKey().SerializeCompressed(), + ) { + + return nil, ErrSecNoncePubkey + } + + // Check that the key set contains the public key to our secret key. + var containsSecKey bool + for _, pk := range pubKeys { + if privKey.PubKey().IsEqual(pk) { + containsSecKey = true + } + } + + if !containsSecKey { + return nil, ErrPubkeyNotIncluded + } + + // Compute the hash of all the keys here as we'll need it do aggregate + // the keys and also at the final step of signing. + keysHash := keyHashFingerprint(pubKeys, opts.sortKeys) + uniqueKeyIndex := secondUniqueKeyIndex(pubKeys, opts.sortKeys) + + keyAggOpts := []KeyAggOption{ + WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex), + } + switch { + case opts.bip86Tweak: + keyAggOpts = append( + keyAggOpts, WithBIP86KeyTweak(), + ) + case opts.taprootTweak != nil: + keyAggOpts = append( + keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak), + ) + case len(opts.tweaks) != 0: + keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...)) + } + + // Next we'll construct the aggregated public key based on the set of + // signers. + combinedKey, parityAcc, _, err := AggregateKeys( + pubKeys, opts.sortKeys, keyAggOpts..., + ) + if err != nil { + return nil, err + } + + // We'll now combine both the public nonces, using the blinding factor + // to tweak the second nonce: + // * R = R_1 + b*R_2 + nonce, nonceBlinder, err := computeSigningNonce( + combinedNonce, combinedKey.FinalKey, msg, + ) + if err != nil { + return nil, err + } + + // Next we'll parse out our two secret nonces, which we'll be using in + // the core signing process below. + var k1, k2 btcec.ModNScalar + k1.SetByteSlice(secNonce[:btcec.SecKeyBytesLen]) + k2.SetByteSlice(secNonce[btcec.SecKeyBytesLen:]) + + if k1.IsZero() || k2.IsZero() { + return nil, ErrSecretNonceZero + } + + nonce.ToAffine() + + nonceKey := btcec.NewPublicKey(&nonce.X, &nonce.Y) + + // If the nonce R has an odd y coordinate, then we'll negate both our + // secret nonces. + if nonce.Y.IsOdd() { + k1.Negate() + k2.Negate() + } + + privKeyScalar := privKey.Key + if privKeyScalar.IsZero() { + return nil, ErrSecKeyZero + } + + pubKey := privKey.PubKey() + combinedKeyYIsOdd := func() bool { + combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed() + return combinedKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd + }() + + // Next we'll compute the two parity factors for Q, the combined key. + // If the key is odd, then we'll negate it. + parityCombinedKey := new(btcec.ModNScalar).SetInt(1) + if combinedKeyYIsOdd { + parityCombinedKey.Negate() + } + + // Before we sign below, we'll multiply by our various parity factors + // to ensure that the signing key is properly negated (if necessary): + // * d = g⋅gacc⋅d' + privKeyScalar.Mul(parityCombinedKey).Mul(parityAcc) + + // Next we'll create the challenge hash that commits to the combined + // nonce, combined public key and also the message: + // * e = H(tag=ChallengeHashTag, R || Q || m) mod n + var challengeMsg bytes.Buffer + challengeMsg.Write(schnorr.SerializePubKey(nonceKey)) + challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey)) + challengeMsg.Write(msg[:]) + challengeBytes := chainhash.TaggedHash( + ChallengeHashTag, challengeMsg.Bytes(), + ) + var e btcec.ModNScalar + e.SetByteSlice(challengeBytes[:]) + + // Next, we'll compute a, our aggregation coefficient for the key that + // we're signing with. + a := aggregationCoefficient(pubKeys, pubKey, keysHash, uniqueKeyIndex) + + // With mu constructed, we can finally generate our partial signature + // as: s = (k1_1 + b*k_2 + e*a*d) mod n. + s := new(btcec.ModNScalar) + s.Add(&k1).Add(k2.Mul(nonceBlinder)).Add(e.Mul(a).Mul(&privKeyScalar)) + + sig := NewPartialSignature(s, nonceKey) + + // If we're not in fast sign mode, then we'll also validate our partial + // signature. + if !opts.fastSign { + pubNonce := secNonceToPubNonce(secNonce) + sigValid := sig.Verify( + pubNonce, combinedNonce, pubKeys, pubKey, msg, + signOpts..., + ) + if !sigValid { + return nil, fmt.Errorf("sig is invalid!") + } + } + + return &sig, nil +} + +// Verify implements partial signature verification given the public nonce for +// the signer, aggregate nonce, signer set and finally the message being +// signed. +func (p *PartialSignature) Verify( + pubNonce [PubNonceSize]byte, + combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey, + signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption, +) bool { + + pubKey := signingKey.SerializeCompressed() + + return verifyPartialSig( + p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts..., + ) == nil +} + +// verifyPartialSig attempts to verify a partial schnorr signature given the +// necessary parameters. This is the internal version of Verify that returns +// detailed errors. signed. +func verifyPartialSig( + partialSig *PartialSignature, pubNonce [PubNonceSize]byte, + combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey, + pubKey []byte, msg [32]byte, signOpts ...SignOption, +) error { + + opts := defaultSignOptions() + for _, option := range signOpts { + option(opts) + } + + // First we'll map the internal partial signature back into something + // we can manipulate. + s := partialSig.S + + // Next we'll parse out the two public nonces into something we can + // use. + // + // Compute the hash of all the keys here as we'll need it do aggregate + // the keys and also at the final step of verification. + keysHash := keyHashFingerprint(keySet, opts.sortKeys) + uniqueKeyIndex := secondUniqueKeyIndex(keySet, opts.sortKeys) + + keyAggOpts := []KeyAggOption{ + WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex), + } + switch { + case opts.bip86Tweak: + keyAggOpts = append( + keyAggOpts, WithBIP86KeyTweak(), + ) + case opts.taprootTweak != nil: + keyAggOpts = append( + keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak), + ) + case len(opts.tweaks) != 0: + keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...)) + } + + // Next we'll construct the aggregated public key based on the set of + // signers. + combinedKey, parityAcc, _, err := AggregateKeys( + keySet, opts.sortKeys, keyAggOpts..., + ) + if err != nil { + return err + } + + // Next we'll compute the value b, that blinds our second public + // nonce: + // * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m). + var ( + nonceMsgBuf bytes.Buffer + nonceBlinder btcec.ModNScalar + ) + nonceMsgBuf.Write(combinedNonce[:]) + nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey)) + nonceMsgBuf.Write(msg[:]) + nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes()) + nonceBlinder.SetByteSlice(nonceBlindHash[:]) + + r1J, err := btcec.ParseJacobian( + combinedNonce[:btcec.PubKeyBytesLenCompressed], + ) + if err != nil { + return err + } + r2J, err := btcec.ParseJacobian( + combinedNonce[btcec.PubKeyBytesLenCompressed:], + ) + if err != nil { + return err + } + + // With our nonce blinding value, we'll now combine both the public + // nonces, using the blinding factor to tweak the second nonce: + // * R = R_1 + b*R_2 + var nonce btcec.JacobianPoint + btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J) + btcec.AddNonConst(&r1J, &r2J, &nonce) + + // Next, we'll parse out the set of public nonces this signer used to + // generate the signature. + pubNonce1J, err := btcec.ParseJacobian( + pubNonce[:btcec.PubKeyBytesLenCompressed], + ) + if err != nil { + return err + } + pubNonce2J, err := btcec.ParseJacobian( + pubNonce[btcec.PubKeyBytesLenCompressed:], + ) + if err != nil { + return err + } + + // If the nonce is the infinity point we set it to the Generator. + if nonce == infinityPoint { + btcec.GeneratorJacobian(&nonce) + } else { + nonce.ToAffine() + } + + // We'll perform a similar aggregation and blinding operator as we did + // above for the combined nonces: R' = R_1' + b*R_2'. + var pubNonceJ btcec.JacobianPoint + + btcec.ScalarMultNonConst(&nonceBlinder, &pubNonce2J, &pubNonce2J) + btcec.AddNonConst(&pubNonce1J, &pubNonce2J, &pubNonceJ) + + pubNonceJ.ToAffine() + + // If the combined nonce used in the challenge hash has an odd y + // coordinate, then we'll negate our final public nonce. + if nonce.Y.IsOdd() { + pubNonceJ.Y.Negate(1) + pubNonceJ.Y.Normalize() + } + + // Next we'll create the challenge hash that commits to the combined + // nonce, combined public key and also the message: + // * e = H(tag=ChallengeHashTag, R || Q || m) mod n + var challengeMsg bytes.Buffer + challengeMsg.Write( + schnorr.SerializePubKey( + btcec.NewPublicKey( + &nonce.X, &nonce.Y, + ), + ), + ) + challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey)) + challengeMsg.Write(msg[:]) + challengeBytes := chainhash.TaggedHash( + ChallengeHashTag, challengeMsg.Bytes(), + ) + var e btcec.ModNScalar + e.SetByteSlice(challengeBytes[:]) + + signingKey, err := btcec.ParsePubKey(pubKey) + if err != nil { + return err + } + + // Next, we'll compute a, our aggregation coefficient for the key that + // we're signing with. + a := aggregationCoefficient(keySet, signingKey, keysHash, uniqueKeyIndex) + + // If the combined key has an odd y coordinate, then we'll negate + // parity factor for the signing key. + parityCombinedKey := new(btcec.ModNScalar).SetInt(1) + combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed() + if combinedKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd { + parityCombinedKey.Negate() + } + + // Next, we'll construct the final parity factor by multiplying the + // sign key parity factor with the accumulated parity factor for all + // the keys. + finalParityFactor := parityCombinedKey.Mul(parityAcc) + + var signKeyJ btcec.JacobianPoint + signingKey.AsJacobian(&signKeyJ) + + // In the final set, we'll check that: s*G == R' + e*a*g*P. + var sG, rP btcec.JacobianPoint + btcec.ScalarBaseMultNonConst(s, &sG) + btcec.ScalarMultNonConst(e.Mul(a).Mul(finalParityFactor), &signKeyJ, &rP) + btcec.AddNonConst(&rP, &pubNonceJ, &rP) + + sG.ToAffine() + rP.ToAffine() + + if sG != rP { + return ErrPartialSigInvalid + } + + return nil +} + +// CombineOption is a functional option argument that allows callers to modify the +// way we combine musig2 schnorr signatures. +type CombineOption func(*combineOptions) + +// combineOptions houses the set of functional options that can be used to +// modify the method used to combine the musig2 partial signatures. +type combineOptions struct { + msg [32]byte + + combinedKey *btcec.PublicKey + + tweakAcc *btcec.ModNScalar +} + +// defaultCombineOptions returns the default set of signing operations. +func defaultCombineOptions() *combineOptions { + return &combineOptions{} +} + +// WithTweakedCombine is a functional option that allows callers to specify +// that the signature was produced using a tweaked aggregated public key. In +// order to properly aggregate the partial signatures, the caller must specify +// enough information to reconstruct the challenge, and also the final +// accumulated tweak value. +func WithTweakedCombine( + msg [32]byte, keys []*btcec.PublicKey, + tweaks []KeyTweakDesc, sort bool, +) CombineOption { + + return func(o *combineOptions) { + combinedKey, _, tweakAcc, _ := AggregateKeys( + keys, sort, WithKeyTweaks(tweaks...), + ) + + o.msg = msg + o.combinedKey = combinedKey.FinalKey + o.tweakAcc = tweakAcc + } +} + +// WithTaprootTweakedCombine is similar to the WithTweakedCombine option, but +// assumes a BIP 341 context where the final tweaked key is to be used as the +// output key, where the internal key is the aggregated key pre-tweak. +// +// This option should be used over WithTweakedCombine when attempting to +// aggregate signatures for a top-level taproot keyspend, where the output key +// commits to a script root. +func WithTaprootTweakedCombine( + msg [32]byte, keys []*btcec.PublicKey, + scriptRoot []byte, sort bool, +) CombineOption { + + return func(o *combineOptions) { + combinedKey, _, tweakAcc, _ := AggregateKeys( + keys, sort, WithTaprootKeyTweak(scriptRoot), + ) + + o.msg = msg + o.combinedKey = combinedKey.FinalKey + o.tweakAcc = tweakAcc + } +} + +// WithBip86TweakedCombine is similar to the WithTaprootTweakedCombine option, +// but assumes a BIP 341 + BIP 86 context where the final tweaked key is to be +// used as the output key, where the internal key is the aggregated key +// pre-tweak. +// +// This option should be used over WithTaprootTweakedCombine when attempting to +// aggregate signatures for a top-level taproot keyspend, where the output key +// was generated using BIP 86. +func WithBip86TweakedCombine( + msg [32]byte, keys []*btcec.PublicKey, + sort bool, +) CombineOption { + + return func(o *combineOptions) { + combinedKey, _, tweakAcc, _ := AggregateKeys( + keys, sort, WithBIP86KeyTweak(), + ) + + o.msg = msg + o.combinedKey = combinedKey.FinalKey + o.tweakAcc = tweakAcc + } +} + +// CombineSigs combines the set of public keys given the final aggregated +// nonce, and the series of partial signatures for each nonce. +func CombineSigs( + combinedNonce *btcec.PublicKey, + partialSigs []*PartialSignature, + combineOpts ...CombineOption, +) *schnorr.Signature { + + // First, parse the set of optional combine options. + opts := defaultCombineOptions() + for _, option := range combineOpts { + option(opts) + } + + // If signer keys and tweaks are specified, then we need to carry out + // some intermediate steps before we can combine the signature. + var tweakProduct *btcec.ModNScalar + if opts.combinedKey != nil && opts.tweakAcc != nil { + // Next, we'll construct the parity factor of the combined key, + // negating it if the combined key has an even y coordinate. + parityFactor := new(btcec.ModNScalar).SetInt(1) + combinedKeyBytes := opts.combinedKey.SerializeCompressed() + if combinedKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd { + parityFactor.Negate() + } + + // Next we'll reconstruct e the challenge has based on the + // nonce and combined public key. + // * e = H(tag=ChallengeHashTag, R || Q || m) mod n + var challengeMsg bytes.Buffer + challengeMsg.Write(schnorr.SerializePubKey(combinedNonce)) + challengeMsg.Write(schnorr.SerializePubKey(opts.combinedKey)) + challengeMsg.Write(opts.msg[:]) + challengeBytes := chainhash.TaggedHash( + ChallengeHashTag, challengeMsg.Bytes(), + ) + var e btcec.ModNScalar + e.SetByteSlice(challengeBytes[:]) + + tweakProduct = new(btcec.ModNScalar).Set(&e) + tweakProduct.Mul(opts.tweakAcc).Mul(parityFactor) + } + + // Finally, the tweak factor also needs to be re-computed as well. + var combinedSig btcec.ModNScalar + for _, partialSig := range partialSigs { + combinedSig.Add(partialSig.S) + } + + // If the tweak product was set above, then we'll need to add the value + // at the very end in order to produce a valid signature under the + // final tweaked key. + if tweakProduct != nil { + combinedSig.Add(tweakProduct) + } + + // TODO(roasbeef): less verbose way to get the x coord... + var nonceJ btcec.JacobianPoint + combinedNonce.AsJacobian(&nonceJ) + nonceJ.ToAffine() + + return schnorr.NewSignature(&nonceJ.X, &combinedSig) +} diff --git a/pkg/crypto/ec/musig2/sign_test.go b/pkg/crypto/ec/musig2/sign_test.go new file mode 100644 index 0000000..34555eb --- /dev/null +++ b/pkg/crypto/ec/musig2/sign_test.go @@ -0,0 +1,330 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path" + "strings" + "testing" + + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/encoders/hex" + + "github.com/stretchr/testify/require" +) + +const ( + signVerifyTestVectorFileName = "sign_verify_vectors.json" + sigCombineTestVectorFileName = "sig_agg_vectors.json" +) + +type signVerifyValidCase struct { + Indices []int `json:"key_indices"` + NonceIndices []int `json:"nonce_indices"` + AggNonceIndex int `json:"aggnonce_index"` + MsgIndex int `json:"msg_index"` + SignerIndex int `json:"signer_index"` + Expected string `json:"expected"` +} + +type signErrorCase struct { + Indices []int `json:"key_indices"` + AggNonceIndex int `json:"aggnonce_index"` + MsgIndex int `json:"msg_index"` + SecNonceIndex int `json:"secnonce_index"` + Comment string `json:"comment"` +} + +type verifyFailCase struct { + Sig string `json:"sig"` + Indices []int `json:"key_indices"` + NonceIndices []int `json:"nonce_indices"` + MsgIndex int `json:"msg_index"` + SignerIndex int `json:"signer_index"` + Comment string `json:"comment"` +} + +type verifyErrorCase struct { + Sig string `json:"sig"` + Indices []int `json:"key_indices"` + NonceIndices []int `json:"nonce_indices"` + MsgIndex int `json:"msg_index"` + SignerIndex int `json:"signer_index"` + Comment string `json:"comment"` +} + +type signVerifyTestVectors struct { + SecKey string `json:"sk"` + PubKeys []string `json:"pubkeys"` + PrivNonces []string `json:"secnonces"` + PubNonces []string `json:"pnonces"` + AggNonces []string `json:"aggnonces"` + Msgs []string `json:"msgs"` + ValidCases []signVerifyValidCase `json:"valid_test_cases"` + SignErrorCases []signErrorCase `json:"sign_error_test_cases"` + VerifyFailCases []verifyFailCase `json:"verify_fail_test_cases"` + VerifyErrorCases []verifyErrorCase `json:"verify_error_test_cases"` +} + +// TestMusig2SignVerify tests that we pass the musig2 verification tests. +func TestMusig2SignVerify(t *testing.T) { + t.Parallel() + testVectorPath := path.Join( + testVectorBaseDir, signVerifyTestVectorFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + var testCases signVerifyTestVectors + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + privKey, _ := btcec.SecKeyFromBytes(mustParseHex(testCases.SecKey)) + for i, testCase := range testCases.ValidCases { + testCase := testCase + testName := fmt.Sprintf("valid_case_%v", i) + t.Run( + testName, func(t *testing.T) { + pubKeys, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + require.NoError(t, err) + pubNonces := pubNoncesFromIndices( + t, testCase.NonceIndices, testCases.PubNonces, + ) + combinedNonce, err := AggregateNonces(pubNonces) + require.NoError(t, err) + var msg [32]byte + copy(msg[:], mustParseHex(testCases.Msgs[testCase.MsgIndex])) + var secNonce [SecNonceSize]byte + copy(secNonce[:], mustParseHex(testCases.PrivNonces[0])) + partialSig, err := Sign( + secNonce, privKey, combinedNonce, pubKeys, + msg, + ) + var partialSigBytes [32]byte + partialSig.S.PutBytesUnchecked(partialSigBytes[:]) + require.Equal( + t, hex.Enc(partialSigBytes[:]), + hex.Enc(mustParseHex(testCase.Expected)), + ) + }, + ) + } + for _, testCase := range testCases.SignErrorCases { + testCase := testCase + testName := fmt.Sprintf( + "invalid_case_%v", + strings.ToLower(testCase.Comment), + ) + t.Run( + testName, func(t *testing.T) { + pubKeys, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + if err != nil { + require.ErrorIs(t, err, secp256k1.ErrPubKeyNotOnCurve) + return + } + var aggNonce [PubNonceSize]byte + copy( + aggNonce[:], + mustParseHex( + testCases.AggNonces[testCase.AggNonceIndex], + ), + ) + var msg [32]byte + copy(msg[:], mustParseHex(testCases.Msgs[testCase.MsgIndex])) + var secNonce [SecNonceSize]byte + copy( + secNonce[:], + mustParseHex( + testCases.PrivNonces[testCase.SecNonceIndex], + ), + ) + _, err = Sign( + secNonce, privKey, aggNonce, pubKeys, + msg, + ) + require.Error(t, err) + }, + ) + } + for _, testCase := range testCases.VerifyFailCases { + testCase := testCase + testName := fmt.Sprintf( + "verify_fail_%v", + strings.ToLower(testCase.Comment), + ) + t.Run( + testName, func(t *testing.T) { + pubKeys, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + require.NoError(t, err) + pubNonces := pubNoncesFromIndices( + t, testCase.NonceIndices, testCases.PubNonces, + ) + combinedNonce, err := AggregateNonces(pubNonces) + require.NoError(t, err) + var msg [32]byte + copy( + msg[:], + mustParseHex(testCases.Msgs[testCase.MsgIndex]), + ) + var secNonce [SecNonceSize]byte + copy(secNonce[:], mustParseHex(testCases.PrivNonces[0])) + signerNonce := secNonceToPubNonce(secNonce) + var partialSig PartialSignature + err = partialSig.Decode( + bytes.NewReader(mustParseHex(testCase.Sig)), + ) + if err != nil && strings.Contains( + testCase.Comment, "group size", + ) { + require.ErrorIs(t, err, ErrPartialSigInvalid) + } + err = verifyPartialSig( + &partialSig, signerNonce, combinedNonce, + pubKeys, privKey.PubKey().SerializeCompressed(), + msg, + ) + require.Error(t, err) + }, + ) + } + + for _, testCase := range testCases.VerifyErrorCases { + testCase := testCase + testName := fmt.Sprintf( + "verify_error_%v", + strings.ToLower(testCase.Comment), + ) + t.Run( + testName, func(t *testing.T) { + switch testCase.Comment { + case "Invalid pubnonce": + pubNonces := pubNoncesFromIndices( + t, testCase.NonceIndices, testCases.PubNonces, + ) + _, err := AggregateNonces(pubNonces) + require.ErrorIs(t, err, secp256k1.ErrPubKeyNotOnCurve) + + case "Invalid pubkey": + _, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + require.ErrorIs(t, err, secp256k1.ErrPubKeyNotOnCurve) + + default: + t.Fatalf("unhandled case: %v", testCase.Comment) + } + }, + ) + } + +} + +type sigCombineValidCase struct { + AggNonce string `json:"aggnonce"` + NonceIndices []int `json:"nonce_indices"` + Indices []int `json:"key_indices"` + TweakIndices []int `json:"tweak_indices"` + IsXOnly []bool `json:"is_xonly"` + PSigIndices []int `json:"psig_indices"` + Expected string `json:"expected"` +} + +type sigCombineTestVectors struct { + PubKeys []string `json:"pubkeys"` + PubNonces []string `json:"pnonces"` + Tweaks []string `json:"tweaks"` + Psigs []string `json:"psigs"` + Msg string `json:"msg"` + ValidCases []sigCombineValidCase `json:"valid_test_cases"` +} + +func pSigsFromIndicies( + t *testing.T, sigs []string, + indices []int, +) []*PartialSignature { + pSigs := make([]*PartialSignature, len(indices)) + for i, idx := range indices { + var pSig PartialSignature + err := pSig.Decode(bytes.NewReader(mustParseHex(sigs[idx]))) + require.NoError(t, err) + pSigs[i] = &pSig + } + return pSigs +} + +// TestMusig2SignCombine tests that we pass the musig2 sig combination tests. +func TestMusig2SignCombine(t *testing.T) { + t.Parallel() + testVectorPath := path.Join( + testVectorBaseDir, sigCombineTestVectorFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + var testCases sigCombineTestVectors + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + var msg [32]byte + copy(msg[:], mustParseHex(testCases.Msg)) + for i, testCase := range testCases.ValidCases { + testCase := testCase + testName := fmt.Sprintf("valid_case_%v", i) + t.Run( + testName, func(t *testing.T) { + pubKeys, err := keysFromIndices( + t, testCase.Indices, testCases.PubKeys, + ) + require.NoError(t, err) + pubNonces := pubNoncesFromIndices( + t, testCase.NonceIndices, testCases.PubNonces, + ) + partialSigs := pSigsFromIndicies( + t, testCases.Psigs, testCase.PSigIndices, + ) + var ( + combineOpts []CombineOption + keyOpts []KeyAggOption + ) + if len(testCase.TweakIndices) > 0 { + tweaks := tweaksFromIndices( + t, testCase.TweakIndices, + testCases.Tweaks, testCase.IsXOnly, + ) + combineOpts = append( + combineOpts, WithTweakedCombine( + msg, pubKeys, tweaks, false, + ), + ) + keyOpts = append(keyOpts, WithKeyTweaks(tweaks...)) + } + combinedKey, _, _, err := AggregateKeys( + pubKeys, false, keyOpts..., + ) + require.NoError(t, err) + combinedNonce, err := AggregateNonces(pubNonces) + require.NoError(t, err) + finalNonceJ, _, err := computeSigningNonce( + combinedNonce, combinedKey.FinalKey, msg, + ) + finalNonceJ.ToAffine() + finalNonce := btcec.NewPublicKey( + &finalNonceJ.X, &finalNonceJ.Y, + ) + combinedSig := CombineSigs( + finalNonce, partialSigs, combineOpts..., + ) + require.Equal( + t, + strings.ToLower(testCase.Expected), + hex.Enc(combinedSig.Serialize()), + ) + }, + ) + } +} diff --git a/pkg/crypto/ec/pubkey.go b/pkg/crypto/ec/pubkey.go new file mode 100644 index 0000000..d69a169 --- /dev/null +++ b/pkg/crypto/ec/pubkey.go @@ -0,0 +1,52 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// These constants define the lengths of serialized public keys. + +const ( + PubKeyBytesLenCompressed = 33 +) + +const ( + pubkeyCompressed byte = 0x2 // y_bit + x coord + pubkeyUncompressed byte = 0x4 // x coord + y coord + pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord +) + +// IsCompressedPubKey returns true the passed serialized public key has +// been encoded in compressed format, and false otherwise. +func IsCompressedPubKey(pubKey []byte) bool { + // The public key is only compressed if it is the correct length and + // the format (first byte) is one of the compressed pubkey values. + return len(pubKey) == PubKeyBytesLenCompressed && + (pubKey[0]&^byte(0x1) == pubkeyCompressed) +} + +// ParsePubKey parses a public key for a koblitz curve from a bytestring into a +// ecdsa.Publickey, verifying that it is valid. It supports compressed, +// uncompressed and hybrid signature formats. +func ParsePubKey(pubKeyStr []byte) (*PublicKey, error) { + return secp256k1.ParsePubKey(pubKeyStr) +} + +// PublicKey is an ecdsa.PublicKey with additional functions to +// serialize in uncompressed, compressed, and hybrid formats. +type PublicKey = secp256k1.PublicKey + +// 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 +// 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. +func NewPublicKey(x, y *FieldVal) *PublicKey { + return secp256k1.NewPublicKey(x, y) +} diff --git a/pkg/crypto/ec/pubkey_test.go b/pkg/crypto/ec/pubkey_test.go new file mode 100644 index 0000000..5145c20 --- /dev/null +++ b/pkg/crypto/ec/pubkey_test.go @@ -0,0 +1,321 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "testing" + + "next.orly.dev/pkg/utils" + + "github.com/davecgh/go-spew/spew" +) + +type pubKeyTest struct { + name string + key []byte + format byte + isValid bool +} + +var pubKeyTests = []pubKeyTest{ + // pubkey from bitcoin blockchain tx + // 0437cd7f8525ceed2324359c2d0ba26006d92d85 + { + name: "uncompressed ok", + key: []byte{ + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: true, + format: pubkeyUncompressed, + }, + { + name: "uncompressed x changed", + key: []byte{ + 0x04, 0x15, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "uncompressed y changed", + key: []byte{ + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa4, + }, + isValid: false, + }, + { + name: "uncompressed claims compressed", + key: []byte{ + 0x03, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "uncompressed as hybrid ok", + key: []byte{ + 0x07, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: true, + format: pubkeyHybrid, + }, + { + name: "uncompressed as hybrid wrong", + key: []byte{ + 0x06, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + // from tx 0b09c51c51ff762f00fb26217269d2a18e77a4fa87d69b3c363ab4df16543f20 + { + name: "compressed ok (ybit = 0)", + key: []byte{ + 0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, + 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, + 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, + 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, + }, + isValid: true, + format: pubkeyCompressed, + }, + // from tx fdeb8e72524e8dab0da507ddbaf5f88fe4a933eb10a66bc4745bb0aa11ea393c + { + name: "compressed ok (ybit = 1)", + key: []byte{ + 0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, + 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, + 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, + 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, + }, + isValid: true, + format: pubkeyCompressed, + }, + { + name: "compressed claims uncompressed (ybit = 0)", + key: []byte{ + 0x04, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, + 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, + 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, + 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, + }, + isValid: false, + }, + { + name: "compressed claims uncompressed (ybit = 1)", + key: []byte{ + 0x05, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, + 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, + 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, + 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, + }, + isValid: false, + }, + { + name: "wrong length)", + key: []byte{0x05}, + isValid: false, + }, + { + name: "X == P", + key: []byte{ + 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "X > P", + key: []byte{ + 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0x2F, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "Y == P", + key: []byte{ + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFC, 0x2F, + }, + isValid: false, + }, + { + name: "Y > P", + key: []byte{ + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFD, 0x2F, + }, + isValid: false, + }, + { + name: "hybrid", + key: []byte{ + 0x06, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, + 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, + 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, + 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98, 0x48, 0x3a, + 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, + 0xfc, 0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, + 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f, 0xfb, + 0x10, 0xd4, 0xb8, + }, + format: pubkeyHybrid, + isValid: true, + }, +} + +func TestPubKeys(t *testing.T) { + for _, test := range pubKeyTests { + pk, err := ParsePubKey(test.key) + if err != nil { + if test.isValid { + t.Errorf( + "%s pubkey failed when shouldn't %v", + test.name, err, + ) + } + continue + } + if !test.isValid { + t.Errorf( + "%s counted as valid when it should fail", + test.name, + ) + continue + } + var pkStr []byte + switch test.format { + case pubkeyUncompressed: + pkStr = pk.SerializeUncompressed() + case pubkeyCompressed: + pkStr = pk.SerializeCompressed() + case pubkeyHybrid: + pkStr = test.key + } + if !utils.FastEqual(test.key, pkStr) { + t.Errorf( + "%s pubkey: serialized keys do not match.", + test.name, + ) + spew.Dump(test.key) + spew.Dump(pkStr) + } + } +} + +func TestPublicKeyIsEqual(t *testing.T) { + pubKey1, err := ParsePubKey( + []byte{ + 0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, + 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, + 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, + 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, + }, + ) + if err != nil { + t.Fatalf("failed to parse raw bytes for pubKey1: %v", err) + } + pubKey2, err := ParsePubKey( + []byte{ + 0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, + 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, + 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, + 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, + }, + ) + if err != nil { + t.Fatalf("failed to parse raw bytes for pubKey2: %v", err) + } + if !pubKey1.IsEqual(pubKey1) { + t.Fatalf( + "value of IsEqual is incorrect, %v is "+ + "equal to %v", pubKey1, pubKey1, + ) + } + if pubKey1.IsEqual(pubKey2) { + t.Fatalf( + "value of IsEqual is incorrect, %v is not "+ + "equal to %v", pubKey1, pubKey2, + ) + } +} + +func TestIsCompressed(t *testing.T) { + for _, test := range pubKeyTests { + isCompressed := IsCompressedPubKey(test.key) + wantCompressed := (test.format == pubkeyCompressed) + if isCompressed != wantCompressed { + t.Fatalf( + "%s (%x) pubkey: unexpected compressed result, "+ + "got %v, want %v", test.name, test.key, + isCompressed, wantCompressed, + ) + } + } +} diff --git a/pkg/crypto/ec/schnorr/bench_test.go b/pkg/crypto/ec/schnorr/bench_test.go new file mode 100644 index 0000000..3c40629 --- /dev/null +++ b/pkg/crypto/ec/schnorr/bench_test.go @@ -0,0 +1,173 @@ +// Copyright 2013-2016 The btcsuite developers +// Copyright (c) 2015-2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package schnorr + +import ( + "math/big" + "testing" + + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/crypto/sha256" + "next.orly.dev/pkg/encoders/hex" +) + +// hexToBytes converts the passed hex string into bytes 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 hexToBytes(s string) []byte { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} + +// 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) *btcec.ModNScalar { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var scalar btcec.ModNScalar + if overflow := scalar.SetByteSlice(b); overflow { + panic("hex in source file overflows mod N scalar: " + s) + } + 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 +// errors in the source code can be detected. It will only (and must only) be +// called with hard-coded values. +func hexToFieldVal(s string) *btcec.FieldVal { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var f btcec.FieldVal + if overflow := f.SetByteSlice(b); overflow { + panic("hex in source file overflows mod P: " + s) + } + return &f +} + +// fromHex converts the passed hex string into a big integer pointer 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 for initialization purposes. +func fromHex(s string) *big.Int { + if s == "" { + return big.NewInt(0) + } + r, ok := new(big.Int).SetString(s, 16) + if !ok { + panic("invalid hex in source file: " + s) + } + return r +} + +var testOk bool + +// BenchmarkSign benchmarks how long it takes to sign a message. +func BenchmarkSign(b *testing.B) { + // Randomly generated keypair. + d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + privKey := secp256k1.NewSecretKey(d) + // blake256 of by{0x01, 0x02, 0x03, 0x04}. + msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") + var auxBytes [32]byte + copy(auxBytes[:], msgHash) + auxBytes[0] ^= 1 + var ( + sig *Signature + err error + ) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + sig, err = Sign( + privKey, msgHash, CustomNonce(auxBytes), FastSign(), + ) + } + testSig = sig + testErr = err +} + +// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to +// verify signatures. +func BenchmarkSigVerify(b *testing.B) { + // Randomly generated keypair. + d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + privKey := secp256k1.NewSecretKey(d) + pubKey := privKey.PubKey() + // Double sha256 of by{0x01, 0x02, 0x03, 0x04} + msgHash := sha256.Sum256([]byte("benchmark")) + sig, err := Sign(privKey, msgHash[:]) + if err != nil { + b.Fatalf("unable to sign: %v", err) + } + if !sig.Verify(msgHash[:], pubKey) { + b.Errorf("Signature failed to verify") + return + } + var ok bool + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + ok = sig.Verify(msgHash[:], pubKey) + } + testOk = ok +} + +// Used to ensure the compiler doesn't optimize away the benchmark. +var ( + testSig *Signature + testErr error +) + +// BenchmarkSignRfc6979 benchmarks how long it takes to sign a message. +func BenchmarkSignRfc6979(b *testing.B) { + // Randomly generated keypair. + d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + privKey := secp256k1.NewSecretKey(d) + // blake256 of by{0x01, 0x02, 0x03, 0x04}. + msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") + var ( + sig *Signature + err error + ) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + sig, err = Sign(privKey, msgHash, FastSign()) + } + testSig = sig + testErr = err +} + +// BenchmarkSigSerialize benchmarks how long it takes to serialize Schnorr +// signatures. +func BenchmarkSigSerialize(b *testing.B) { + // From randomly generated keypair. + d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") + secKey := secp256k1.NewSecretKey(d) + // blake256 of by{0x01, 0x02, 0x03, 0x04}. + msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") + // Generate the signature. + sig, _ := Sign(secKey, msgHash) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + sig.Serialize() + } +} diff --git a/pkg/crypto/ec/schnorr/bip/bip-0340.mediawiki b/pkg/crypto/ec/schnorr/bip/bip-0340.mediawiki new file mode 100644 index 0000000..c941916 --- /dev/null +++ b/pkg/crypto/ec/schnorr/bip/bip-0340.mediawiki @@ -0,0 +1,303 @@ +
+  BIP: 340
+  Title: Schnorr Signatures for secp256k1
+  Author: Pieter Wuille 
+          Jonas Nick 
+          Tim Ruffing 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0340
+  Status: Final
+  Type: Standards Track
+  License: BSD-2-Clause
+  Created: 2020-01-19
+  Post-History: 2018-07-06: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-July/016203.html [bitcoin-dev] Schnorr signatures BIP
+
+ +== Introduction == + +=== Abstract === + +This document proposes a standard for 64-byte Schnorr signatures over the elliptic curve ''secp256k1''. + +=== Copyright === + +This document is licensed under the 2-clause BSD license. + +=== Motivation === + +Bitcoin has traditionally used +[https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm ECDSA] signatures over the [https://www.secg.org/sec2-v2.pdf secp256k1 curve] with [https://en.wikipedia.org/wiki/SHA-2 SHA256] hashes for authenticating +transactions. These are [https://www.secg.org/sec1-v2.pdf standardized], but have a number of downsides +compared to [http://publikationen.ub.uni-frankfurt.de/opus4/files/4280/schnorr.pdf Schnorr signatures] over the same curve: + +* '''Provable security''': Schnorr signatures are provably secure. In more detail, they are ''strongly unforgeable under chosen message attack (SUF-CMA)''Informally, this means that without knowledge of the secret key but given valid signatures of arbitrary messages, it is not possible to come up with further valid signatures. [https://www.di.ens.fr/~pointche/Documents/Papers/2000_joc.pdf in the random oracle model assuming the hardness of the elliptic curve discrete logarithm problem (ECDLP)] and [http://www.neven.org/papers/schnorr.pdf in the generic group model assuming variants of preimage and second preimage resistance of the used hash function]A detailed security proof in the random oracle model, which essentially restates [https://www.di.ens.fr/~pointche/Documents/Papers/2000_joc.pdf the original security proof by Pointcheval and Stern] more explicitly, can be found in [https://eprint.iacr.org/2016/191 a paper by Kiltz, Masny and Pan]. All these security proofs assume a variant of Schnorr signatures that use ''(e,s)'' instead of ''(R,s)'' (see Design above). Since we use a unique encoding of ''R'', there is an efficiently computable bijection that maps ''(R,s)'' to ''(e,s)'', which allows to convert a successful SUF-CMA attacker for the ''(e,s)'' variant to a successful SUF-CMA attacker for the ''(R,s)'' variant (and vice-versa). Furthermore, the proofs consider a variant of Schnorr signatures without key prefixing (see Design above), but it can be verified that the proofs are also correct for the variant with key prefixing. As a result, all the aforementioned security proofs apply to the variant of Schnorr signatures proposed in this document.. In contrast, the [https://nbn-resolving.de/urn:nbn:de:hbz:294-60803 best known results for the provable security of ECDSA] rely on stronger assumptions. +* '''Non-malleability''': The SUF-CMA security of Schnorr signatures implies that they are non-malleable. On the other hand, ECDSA signatures are inherently malleableIf ''(r,s)'' is a valid ECDSA signature for a given message and key, then ''(r,n-s)'' is also valid for the same message and key. If ECDSA is restricted to only permit one of the two variants (as Bitcoin does through a policy rule on the network), it can be [https://nbn-resolving.de/urn:nbn:de:hbz:294-60803 proven] non-malleable under stronger than usual assumptions.; a third party without access to the secret key can alter an existing valid signature for a given public key and message into another signature that is valid for the same key and message. This issue is discussed in [[bip-0062.mediawiki|BIP62]] and [[bip-0146.mediawiki|BIP146]]. +* '''Linearity''': Schnorr signatures provide a simple and efficient method that enables multiple collaborating parties to produce a signature that is valid for the sum of their public keys. This is the building block for various higher-level constructions that improve efficiency and privacy, such as multisignatures and others (see Applications below). + +For all these advantages, there are virtually no disadvantages, apart +from not being standardized. This document seeks to change that. As we +propose a new standard, a number of improvements not specific to Schnorr signatures can be +made: + +* '''Signature encoding''': Instead of using [https://en.wikipedia.org/wiki/X.690#DER_encoding DER]-encoding for signatures (which are variable size, and up to 72 bytes), we can use a simple fixed 64-byte format. +* '''Public key encoding''': Instead of using [https://www.secg.org/sec1-v2.pdf ''compressed''] 33-byte encodings of elliptic curve points which are common in Bitcoin today, public keys in this proposal are encoded as 32 bytes. +* '''Batch verification''': The specific formulation of ECDSA signatures that is standardized cannot be verified more efficiently in batch compared to individually, unless additional witness data is added. Changing the signature scheme offers an opportunity to address this. +* '''Completely specified''': To be safe for usage in consensus systems, the verification algorithm must be completely specified at the byte level. This guarantees that nobody can construct a signature that is valid to some verifiers but not all. This is traditionally not a requirement for digital signature schemes, and the lack of exact specification for the DER parsing of ECDSA signatures has caused problems for Bitcoin [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-July/009697.html in the past], needing [[bip-0066.mediawiki|BIP66]] to address it. In this document we aim to meet this property by design. For batch verification, which is inherently non-deterministic as the verifier can choose their batches, this property implies that the outcome of verification may only differ from individual verifications with negligible probability, even to an attacker who intentionally tries to make batch- and non-batch verification differ. + +By reusing the same curve and hash function as Bitcoin uses for ECDSA, we are able to retain existing mechanisms for choosing secret and public keys, and we avoid introducing new assumptions about the security of elliptic curves and hash functions. + +== Description == + +We first build up the algebraic formulation of the signature scheme by +going through the design choices. Afterwards, we specify the exact +encodings and operations. + +=== Design === + +'''Schnorr signature variant''' Elliptic Curve Schnorr signatures for message ''m'' and public key ''P'' generally involve a point ''R'', integers ''e'' and ''s'' picked by the signer, and the base point ''G'' which satisfy ''e = hash(R || m)'' and ''s⋅G = R + e⋅P''. Two formulations exist, depending on whether the signer reveals ''e'' or ''R'': +# Signatures are pairs ''(e, s)'' that satisfy ''e = hash(s⋅G - e⋅P || m)''. This variant avoids minor complexity introduced by the encoding of the point ''R'' in the signature (see paragraphs "Encoding R and public key point P" and "Implicit Y coordinates" further below in this subsection). Moreover, revealing ''e'' instead of ''R'' allows for potentially shorter signatures: Whereas an encoding of ''R'' inherently needs about 32 bytes, the hash ''e'' can be tuned to be shorter than 32 bytes, and [http://www.neven.org/papers/schnorr.pdf a short hash of only 16 bytes suffices to provide SUF-CMA security at the target security level of 128 bits]. However, a major drawback of this optimization is that finding collisions in a short hash function is easy. This complicates the implementation of secure signing protocols in scenarios in which a group of mutually distrusting signers work together to produce a single joint signature (see Applications below). In these scenarios, which are not captured by the SUF-CMA model due its assumption of a single honest signer, a promising attack strategy for malicious co-signers is to find a collision in the hash function in order to obtain a valid signature on a message that an honest co-signer did not intend to sign. +# Signatures are pairs ''(R, s)'' that satisfy ''s⋅G = R + hash(R || m)⋅P''. This supports batch verification, as there are no elliptic curve operations inside the hashes. Batch verification enables significant speedups.The speedup that results from batch verification can be demonstrated with the cryptography library [https://github.com/jonasnick/secp256k1/blob/schnorrsig-batch-verify/doc/speedup-batch.md libsecp256k1]. + +Since we would like to avoid the fragility that comes with short hashes, the ''e'' variant does not provide significant advantages. We choose the ''R''-option, which supports batch verification. + +'''Key prefixing''' Using the verification rule above directly makes Schnorr signatures vulnerable to "related-key attacks" in which a third party can convert a signature ''(R, s)'' for public key ''P'' into a signature ''(R, s + a⋅hash(R || m))'' for public key ''P + a⋅G'' and the same message ''m'', for any given additive tweak ''a'' to the signing key. This would render signatures insecure when keys are generated using [[bip-0032.mediawiki#public-parent-key--public-child-key|BIP32's unhardened derivation]] and other methods that rely on additive tweaks to existing keys such as Taproot. + +To protect against these attacks, we choose ''key prefixed''A limitation of committing to the public key (rather than to a short hash of it, or not at all) is that it removes the ability for public key recovery or verifying signatures against a short public key hash. These constructions are generally incompatible with batch verification. Schnorr signatures which means that the public key is prefixed to the message in the challenge hash input. This changes the equation to ''s⋅G = R + hash(R || P || m)⋅P''. [https://eprint.iacr.org/2015/1135.pdf It can be shown] that key prefixing protects against related-key attacks with additive tweaks. In general, key prefixing increases robustness in multi-user settings, e.g., it seems to be a requirement for proving the MuSig multisignature scheme secure (see Applications below). + +We note that key prefixing is not strictly necessary for transaction signatures as used in Bitcoin currently, because signed transactions indirectly commit to the public keys already, i.e., ''m'' contains a commitment to ''pk''. However, this indirect commitment should not be relied upon because it may change with proposals such as SIGHASH_NOINPUT ([[bip-0118.mediawiki|BIP118]]), and would render the signature scheme unsuitable for other purposes than signing transactions, e.g., [https://bitcoin.org/en/developer-reference#signmessage signing ordinary messages]. + +'''Encoding R and public key point P''' There exist several possibilities for encoding elliptic curve points: +# Encoding the full X and Y coordinates of ''P'' and ''R'', resulting in a 64-byte public key and a 96-byte signature. +# Encoding the full X coordinate and one bit of the Y coordinate to determine one of the two possible Y coordinates. This would result in 33-byte public keys and 65-byte signatures. +# Encoding only the X coordinate, resulting in 32-byte public keys and 64-byte signatures. + +Using the first option would be slightly more efficient for verification (around 10%), but we prioritize compactness, and therefore choose option 3. + +'''Implicit Y coordinates''' In order to support efficient verification and batch verification, the Y coordinate of ''P'' and of ''R'' cannot be ambiguous (every valid X coordinate has two possible Y coordinates). We have a choice between several options for symmetry breaking: +# Implicitly choosing the Y coordinate that is in the lower half. +# Implicitly choosing the Y coordinate that is evenSince ''p'' is odd, negation modulo ''p'' will map even numbers to odd numbers and the other way around. This means that for a valid X coordinate, one of the corresponding Y coordinates will be even, and the other will be odd.. +# Implicitly choosing the Y coordinate that is a quadratic residue (i.e. has a square root modulo ''p''). + +The second option offers the greatest compatibility with existing key generation systems, where the standard 33-byte compressed public key format consists of a byte indicating the oddness of the Y coordinate, plus the full X coordinate. To avoid gratuitous incompatibilities, we pick that option for ''P'', and thus our X-only public keys become equivalent to a compressed public key that is the X-only key prefixed by the byte 0x02. For consistency, the same is done for ''R''An earlier version of this draft used the third option instead, based on a belief that this would in general trade signing efficiency for verification efficiency. When using Jacobian coordinates, a common optimization in ECC implementations, it is possible to determine if a Y coordinate is a quadratic residue by computing the Legendre symbol, without converting to affine coordinates first (which needs a modular inversion). As modular inverses and Legendre symbols have similar [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-August/018081.html performance] in practice, this trade-off is not worth it.. + +Despite halving the size of the set of valid public keys, implicit Y coordinates are not a reduction in security. Informally, if a fast algorithm existed to compute the discrete logarithm of an X-only public key, then it could also be used to compute the discrete logarithm of a full public key: apply it to the X coordinate, and then optionally negate the result. This shows that breaking an X-only public key can be at most a small constant term faster than breaking a full one.This can be formalized by a simple reduction that reduces an attack on Schnorr signatures with implicit Y coordinates to an attack to Schnorr signatures with explicit Y coordinates. The reduction works by reencoding public keys and negating the result of the hash function, which is modeled as random oracle, whenever the challenge public key has an explicit Y coordinate that is odd. A proof sketch can be found [https://medium.com/blockstream/reducing-bitcoin-transaction-sizes-with-x-only-pubkeys-f86476af05d7 here].. + +'''Tagged Hashes''' Cryptographic hash functions are used for multiple purposes in the specification below and in Bitcoin in general. To make sure hashes used in one context can't be reinterpreted in another one, hash functions can be tweaked with a context-dependent tag name, in such a way that collisions across contexts can be assumed to be infeasible. Such collisions obviously can not be ruled out completely, but only for schemes using tagging with a unique name. As for other schemes collisions are at least less likely with tagging than without. + +For example, without tagged hashing a BIP340 signature could also be valid for a signature scheme where the only difference is that the arguments to the hash function are reordered. Worse, if the BIP340 nonce derivation function was copied or independently created, then the nonce could be accidentally reused in the other scheme leaking the secret key. + +This proposal suggests to include the tag by prefixing the hashed data with ''SHA256(tag) || SHA256(tag)''. Because this is a 64-byte long context-specific constant and the ''SHA256'' block size is also 64 bytes, optimized implementations are possible (identical to SHA256 itself, but with a modified initial state). Using SHA256 of the tag name itself is reasonably simple and efficient for implementations that don't choose to use the optimization. In general, tags can be arbitrary byte arrays, but are suggested to be textual descriptions in UTF-8 encoding. + +'''Final scheme''' As a result, our final scheme ends up using public key ''pk'' which is the X coordinate of a point ''P'' on the curve whose Y coordinate is even and signatures ''(r,s)'' where ''r'' is the X coordinate of a point ''R'' whose Y coordinate is even. The signature satisfies ''s⋅G = R + tagged_hash(r || pk || m)⋅P''. + +=== Specification === + +The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure schemeAmong other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.. +* Lowercase variables represent integers or byte arrays. +** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''. +** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''. +* Uppercase variables refer to points on the curve with equation ''y2 = x3 + 7'' over the integers modulo ''p''. +** ''is_infinite(P)'' returns whether or not ''P'' is the point at infinity. +** ''x(P)'' and ''y(P)'' are integers in the range ''0..p-1'' and refer to the X and Y coordinates of a point ''P'' (assuming it is not infinity). +** The constant ''G'' refers to the base point, for which ''x(G) = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'' and ''y(G) = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8''. +** Addition of points refers to the usual [https://en.wikipedia.org/wiki/Elliptic_curve#The_group_law elliptic curve group operation]. +** [https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication Multiplication (⋅) of an integer and a point] refers to the repeated application of the group operation. +* Functions and operations: +** ''||'' refers to byte array concatenation. +** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''. +** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first. +** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. +** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. +** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. +** The function ''lift_x(x)'', where ''x'' is a 256-bit unsigned integer, returns the point ''P'' for which ''x(P) = x'' + Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: +*** Fail if ''x ≥ p''. +*** Let ''c = x3 + 7 mod p''. +*** Let ''y = c(p+1)/4 mod p''. +*** Fail if ''c ≠ y2 mod p''. +*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y'' if ''y mod 2 = 0'' or ''y(P) = p-y'' otherwise. +** The function ''hashname(x)'' where ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)'', where ''tag'' is the UTF-8 encoding of ''name''. + +==== Public Key Generation ==== + +Input: +* The secret key ''sk'': a 32-byte array, freshly generated uniformly at random + +The algorithm ''PubKey(sk)'' is defined as: +* Let ''d' = int(sk)''. +* Fail if ''d' = 0'' or ''d' ≥ n''. +* Return ''bytes(d'⋅G)''. + +Note that we use a very different public key format (32 bytes) than the ones used by existing systems (which typically use elliptic curve points as public keys, or 33-byte or 65-byte encodings of them). A side effect is that ''PubKey(sk) = PubKey(bytes(n - int(sk))'', so every public key has two corresponding secret keys. + +==== Public Key Conversion ==== + +As an alternative to generating keys randomly, it is also possible and safe to repurpose existing key generation algorithms for ECDSA in a compatible way. The secret keys constructed by such an algorithm can be used as ''sk'' directly. The public keys constructed by such an algorithm (assuming they use the 33-byte compressed encoding) need to be converted by dropping the first byte. Specifically, [[bip-0032.mediawiki|BIP32]] and schemes built on top of it remain usable. + +==== Default Signing ==== + +Input: +* The secret key ''sk'': a 32-byte array +* The message ''m'': a byte array +* Auxiliary random data ''a'': a 32-byte array + +The algorithm ''Sign(sk, m)'' is defined as: +* Let ''d' = int(sk)'' +* Fail if ''d' = 0'' or ''d' ≥ n'' +* Let ''P = d'⋅G'' +* Let ''d = d' '' if ''has_even_y(P)'', otherwise let ''d = n - d' ''. +* Let ''t'' be the byte-wise xor of ''bytes(d)'' and ''hashBIP0340/aux(a)''The auxiliary random data is hashed (with a unique tag) as a precaution against situations where the randomness may be correlated with the private key itself. It is xored with the private key (rather than combined with it in a hash) to reduce the number of operations exposed to the actual secret key.. +* Let ''rand = hashBIP0340/nonce(t || bytes(P) || m)''Including the [https://moderncrypto.org/mail-archive/curves/2020/001012.html public key as input to the nonce hash] helps ensure the robustness of the signing algorithm by preventing leakage of the secret key if the calculation of the public key ''P'' is performed incorrectly or maliciously, for example if it is left to the caller for performance reasons.. +* Let ''k' = int(rand) mod n''Note that in general, taking a uniformly random 256-bit integer modulo the curve order will produce an unacceptably biased result. However, for the secp256k1 curve, the order is sufficiently close to ''2256'' that this bias is not observable (''1 - n / 2256'' is around ''1.27 * 2-128'').. +* Fail if ''k' = 0''. +* Let ''R = k'⋅G''. +* Let ''k = k' '' if ''has_even_y(R)'', otherwise let ''k = n - k' ''. +* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(P) || m)) mod n''. +* Let ''sig = bytes(R) || bytes((k + ed) mod n)''. +* If ''Verify(bytes(P), m, sig)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. +* Return the signature ''sig''. + +The auxiliary random data should be set to fresh randomness generated at signing time, resulting in what is called a ''synthetic nonce''. Using 32 bytes of randomness is optimal. If obtaining randomness is expensive, 16 random bytes can be padded with 16 null bytes to obtain a 32-byte array. If randomness is not available at all at signing time, a simple counter wide enough to not repeat in practice (e.g., 64 bits or wider) and padded with null bytes to a 32 byte-array can be used, or even the constant array with 32 null bytes. Using any non-repeating value increases protection against [https://moderncrypto.org/mail-archive/curves/2017/000925.html fault injection attacks]. Using unpredictable randomness additionally increases protection against other side-channel attacks, and is '''recommended whenever available'''. Note that while this means the resulting nonce is not deterministic, the randomness is only supplemental to security. The normal security properties (excluding side-channel attacks) do not depend on the quality of the signing-time RNG. + +==== Alternative Signing ==== + +It should be noted that various alternative signing algorithms can be used to produce equally valid signatures. The 32-byte ''rand'' value may be generated in other ways, producing a different but still valid signature (in other words, this is not a ''unique'' signature scheme). '''No matter which method is used to generate the ''rand'' value, the value must be a fresh uniformly random 32-byte string which is not even partially predictable for the attacker.''' For nonces without randomness this implies that the same inputs must not be presented in another context. This can be most reliably accomplished by not reusing the same private key across different signing schemes. For example, if the ''rand'' value was computed as per RFC6979 and the same secret key is used in deterministic ECDSA with RFC6979, the signatures can leak the secret key through nonce reuse. + +'''Nonce exfiltration protection''' It is possible to strengthen the nonce generation algorithm using a second device. In this case, the second device contributes randomness which the actual signer provably incorporates into its nonce. This prevents certain attacks where the signer device is compromised and intentionally tries to leak the secret key through its nonce selection. + +'''Multisignatures''' This signature scheme is compatible with various types of multisignature and threshold schemes such as [https://eprint.iacr.org/2018/068 MuSig], where a single public key requires holders of multiple secret keys to participate in signing (see Applications below). +'''It is important to note that multisignature signing schemes in general are insecure with the ''rand'' generation from the default signing algorithm above (or any other deterministic method).''' + +'''Precomputed public key data''' For many uses the compressed 33-byte encoding of the public key corresponding to the secret key may already be known, making it easy to evaluate ''has_even_y(P)'' and ''bytes(P)''. As such, having signers supply this directly may be more efficient than recalculating the public key from the secret key. However, if this optimization is used and additionally the signature verification at the end of the signing algorithm is dropped for increased efficiency, signers must ensure the public key is correctly calculated and not taken from untrusted sources. + +==== Verification ==== + +Input: +* The public key ''pk'': a 32-byte array +* The message ''m'': a byte array +* A signature ''sig'': a 64-byte array + +The algorithm ''Verify(pk, m, sig)'' is defined as: +* Let ''P = lift_x(int(pk))''; fail if that fails. +* Let ''r = int(sig[0:32])''; fail if ''r ≥ p''. +* Let ''s = int(sig[32:64])''; fail if ''s ≥ n''. +* Let ''e = int(hashBIP0340/challenge(bytes(r) || bytes(P) || m)) mod n''. +* Let ''R = s⋅G - e⋅P''. +* Fail if ''is_infinite(R)''. +* Fail if ''not has_even_y(R)''. +* Fail if ''x(R) ≠ r''. +* Return success iff no failure occurred before reaching this point. + +For every valid secret key ''sk'' and message ''m'', ''Verify(PubKey(sk),m,Sign(sk,m))'' will succeed. + +Note that the correctness of verification relies on the fact that ''lift_x'' always returns a point with an even Y coordinate. A hypothetical verification algorithm that treats points as public keys, and takes the point ''P'' directly as input would fail any time a point with odd Y is used. While it is possible to correct for this by negating points with odd Y coordinate before further processing, this would result in a scheme where every (message, signature) pair is valid for two public keys (a type of malleability that exists for ECDSA as well, but we don't wish to retain). We avoid these problems by treating just the X coordinate as public key. + +==== Batch Verification ==== + +Input: +* The number ''u'' of signatures +* The public keys ''pk1..u'': ''u'' 32-byte arrays +* The messages ''m1..u'': ''u'' byte arrays +* The signatures ''sig1..u'': ''u'' 64-byte arrays + +The algorithm ''BatchVerify(pk1..u, m1..u, sig1..u)'' is defined as: +* Generate ''u-1'' random integers ''a2...u'' in the range ''1...n-1''. They are generated deterministically using a [https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator CSPRNG] seeded by a cryptographic hash of all inputs of the algorithm, i.e. ''seed = seed_hash(pk1..pku || m1..mu || sig1..sigu )''. A safe choice is to instantiate ''seed_hash'' with SHA256 and use [https://tools.ietf.org/html/rfc8439 ChaCha20] with key ''seed'' as a CSPRNG to generate 256-bit integers, skipping integers not in the range ''1...n-1''. +* For ''i = 1 .. u'': +** Let ''Pi = lift_x(int(pki))''; fail if it fails. +** Let ''ri = int(sigi[0:32])''; fail if ''ri ≥ p''. +** Let ''si = int(sigi[32:64])''; fail if ''si ≥ n''. +** Let ''ei = int(hashBIP0340/challenge(bytes(ri) || bytes(Pi) || mi)) mod n''. +** Let ''Ri = lift_x(ri)''; fail if ''lift_x(ri)'' fails. +* Fail if ''(s1 + a2s2 + ... + ausu)⋅G ≠ R1 + a2⋅R2 + ... + au⋅Ru + e1⋅P1 + (a2e2)⋅P2 + ... + (aueu)⋅Pu''. +* Return success iff no failure occurred before reaching this point. + +If all individual signatures are valid (i.e., ''Verify'' would return success for them), ''BatchVerify'' will always return success. If at least one signature is invalid, ''BatchVerify'' will return success with at most a negligible probability. + +=== Usage Considerations === + +==== Messages of Arbitrary Size ==== + +The signature scheme specified in this BIP accepts byte strings of arbitrary size as input messages.In theory, the message size is restricted due to the fact that SHA256 accepts byte strings only up to size of 2^61-1 bytes. +It is understood that implementations may reject messages which are too large in their environment or application context, +e.g., messages which exceed predefined buffers or would otherwise cause resource exhaustion. + +Earlier revisions of this BIP required messages to be exactly 32 bytes. +This restriction puts a burden on callers +who typically need to perform pre-hashing of the actual input message by feeding it through SHA256 (or another collision-resistant cryptographic hash function) +to create a 32-byte digest which can be passed to signing or verification +(as for example done in [[bip-0341.mediawiki|BIP341]].) + +Since pre-hashing may not always be desirable, +e.g., when actual messages are shorter than 32 bytes,Another reason to omit pre-hashing is to protect against certain types of cryptanalytic advances against the hash function used for pre-hashing: If pre-hashing is used, an attacker that can find collisions in the pre-hashing function can necessarily forge signatures under chosen-message attacks. If pre-hashing is not used, an attacker that can find collisions in SHA256 (as used inside the signature scheme) may not be able to forge signatures. However, this seeming advantage is mostly irrelevant in the context of Bitcoin, which already relies on collision resistance of SHA256 in other places, e.g., for transaction hashes. +the restriction to 32-byte messages has been lifted. +We note that pre-hashing is recommended for performance reasons in applications that deal with large messages. +If large messages are not pre-hashed, +the algorithms of the signature scheme will perform more hashing internally. +In particular, the signing algorithm needs two sequential hashing passes over the message, +which means that the full message must necessarily be kept in memory during signing, +and large messages entail a runtime penalty.Typically, messages of 56 bytes or longer enjoy a performance benefit from pre-hashing, assuming the speed of SHA256 inside the signing algorithm matches that of the pre-hashing done by the calling application. + +==== Domain Separation ==== + +It is good cryptographic practice to use a key pair only for a single purpose. +Nevertheless, there may be situations in which it may be desirable to use the same key pair in multiple contexts, +i.e., to sign different types of messages within the same application +or even messages in entirely different applications +(e.g., a secret key may be used to sign Bitcoin transactions as well plain text messages). + +As a consequence, applications should ensure that a signed application message intended for one context is never deemed valid in a different context +(e.g., a signed plain text message should never be misinterpreted as a signed Bitcoin transaction, because this could cause unintended loss of funds). +This is called "domain separation" and it is typically realized by partitioning the message space. +Even if key pairs are intended to be used only within a single context, +domain separation is a good idea because it makes it easy to add more contexts later. + +As a best practice, we recommend applications to use exactly one of the following methods to pre-process application messages before passing it to the signature scheme: +* Either, pre-hash the application message using ''hashname'', where ''name'' identifies the context uniquely (e.g., "foo-app/signed-bar"), +* or prefix the actual message with a 33-byte string that identifies the context uniquely (e.g., the UTF-8 encoding of "foo-app/signed-bar", padded with null bytes to 33 bytes). + +As the two pre-processing methods yield different message sizes (32 bytes vs. at least 33 bytes), there is no risk of collision between them. + +== Applications == + +There are several interesting applications beyond simple signatures. +While recent academic papers claim that they are also possible with ECDSA, consensus support for Schnorr signature verification would significantly simplify the constructions. + +=== Multisignatures and Threshold Signatures === + +By means of an interactive scheme such as [https://eprint.iacr.org/2018/068 MuSig], participants can aggregate their public keys into a single public key which they can jointly sign for. This allows ''n''-of-''n'' multisignatures which, from a verifier's perspective, are no different from ordinary signatures, giving improved privacy and efficiency versus ''CHECKMULTISIG'' or other means. + +Moreover, Schnorr signatures are compatible with [https://web.archive.org/web/20031003232851/http://www.research.ibm.com/security/dkg.ps distributed key generation], which enables interactive threshold signatures schemes, e.g., the schemes described by [http://cacr.uwaterloo.ca/techreports/2001/corr2001-13.ps Stinson and Strobl (2001)] or [https://web.archive.org/web/20060911151529/http://theory.lcs.mit.edu/~stasio/Papers/gjkr03.pdf Gennaro, Jarecki and Krawczyk (2003)]. These protocols make it possible to realize ''k''-of-''n'' threshold signatures, which ensure that any subset of size ''k'' of the set of ''n'' signers can sign but no subset of size less than ''k'' can produce a valid Schnorr signature. However, the practicality of the existing schemes is limited: most schemes in the literature have been proven secure only for the case ''k-1 < n/2'', are not secure when used concurrently in multiple sessions, or require a reliable broadcast mechanism to be secure. Further research is necessary to improve this situation. + +=== Adaptor Signatures === + +[https://download.wpsoftware.net/bitcoin/wizardry/mw-slides/2018-05-18-l2/slides.pdf Adaptor signatures] can be produced by a signer by offsetting his public nonce ''R'' with a known point ''T = t⋅G'', but not offsetting the signature's ''s'' value. +A correct signature (or partial signature, as individual signers' contributions to a multisignature are called) on the same message with same nonce will then be equal to the adaptor signature offset by ''t'', meaning that learning ''t'' is equivalent to learning a correct signature. +This can be used to enable atomic swaps or even [https://eprint.iacr.org/2018/472 general payment channels] in which the atomicity of disjoint transactions is ensured using the signatures themselves, rather than Bitcoin script support. The resulting transactions will appear to verifiers to be no different from ordinary single-signer transactions, except perhaps for the inclusion of locktime refund logic. + +Adaptor signatures, beyond the efficiency and privacy benefits of encoding script semantics into constant-sized signatures, have additional benefits over traditional hash-based payment channels. Specifically, the secret values ''t'' may be reblinded between hops, allowing long chains of transactions to be made atomic while even the participants cannot identify which transactions are part of the chain. Also, because the secret values are chosen at signing time, rather than key generation time, existing outputs may be repurposed for different applications without recourse to the blockchain, even multiple times. + +=== Blind Signatures === + +A blind signature protocol is an interactive protocol that enables a signer to sign a message at the behest of another party without learning any information about the signed message or the signature. Schnorr signatures admit a very [http://publikationen.ub.uni-frankfurt.de/files/4292/schnorr.blind_sigs_attack.2001.pdf simple blind signature scheme] which is however insecure because it's vulnerable to [https://www.iacr.org/archive/crypto2002/24420288/24420288.pdf Wagner's attack]. A known mitigation is to let the signer abort a signing session with a certain probability, and the resulting scheme can be [https://eprint.iacr.org/2019/877 proven secure under non-standard cryptographic assumptions]. + +Blind Schnorr signatures could for example be used in [https://github.com/ElementsProject/scriptless-scripts/blob/master/md/partially-blind-swap.md Partially Blind Atomic Swaps], a construction to enable transferring of coins, mediated by an untrusted escrow agent, without connecting the transactors in the public blockchain transaction graph. + +== Test Vectors and Reference Code == + +For development and testing purposes, we provide a [[bip-0340/test-vectors.csv|collection of test vectors in CSV format]] and a naive, highly inefficient, and non-constant time [[bip-0340/reference.py|pure Python 3.7 reference implementation of the signing and verification algorithm]]. +The reference implementation is for demonstration purposes only and not to be used in production environments. + +== Changelog == + +To help implementors understand updates to this BIP, we keep a list of substantial changes. + +* 2022-08: Fix function signature of lift_x in reference code +* 2023-04: Allow messages of arbitrary size + +== Footnotes == + + + +== Acknowledgements == + +This document is the result of many discussions around Schnorr based signatures over the years, and had input from Johnson Lau, Greg Maxwell, Andrew Poelstra, Rusty Russell, and Anthony Towns. The authors further wish to thank all those who provided valuable feedback and reviews, including the participants of the [https://github.com/ajtowns/taproot-review structured reviews]. diff --git a/pkg/crypto/ec/schnorr/bip/bip340/reference.py b/pkg/crypto/ec/schnorr/bip/bip340/reference.py new file mode 100644 index 0000000..8377e03 --- /dev/null +++ b/pkg/crypto/ec/schnorr/bip/bip340/reference.py @@ -0,0 +1,244 @@ +from typing import Tuple, Optional, Any +import hashlib +import binascii + +# Set DEBUG to True to get a detailed debug output including +# intermediate values during key generation, signing, and +# verification. This is implemented via calls to the +# debug_print_vars() function. +# +# If you want to print values on an individual basis, use +# the pretty() function, e.g., print(pretty(foo)). +DEBUG = False + +p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + +# Points are tuples of X and Y coordinates and the point at infinity is +# represented by the None keyword. +G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, + 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8) + +Point = Tuple[int, int] + + +# This implementation can be sped up by storing the midstate after hashing +# tag_hash instead of rehashing it all the time. +def tagged_hash(tag: str, msg: bytes) -> bytes: + tag_hash = hashlib.sha256(tag.encode()).digest() + return hashlib.sha256(tag_hash + tag_hash + msg).digest() + + +def is_infinite(P: Optional[Point]) -> bool: + return P is None + + +def x(P: Point) -> int: + assert not is_infinite(P) + return P[0] + + +def y(P: Point) -> int: + assert not is_infinite(P) + return P[1] + + +def point_add(P1: Optional[Point], P2: Optional[Point]) -> Optional[Point]: + if P1 is None: + return P2 + if P2 is None: + return P1 + if (x(P1) == x(P2)) and (y(P1) != y(P2)): + return None + if P1 == P2: + lam = (3 * x(P1) * x(P1) * pow(2 * y(P1), p - 2, p)) % p + else: + lam = ((y(P2) - y(P1)) * pow(x(P2) - x(P1), p - 2, p)) % p + x3 = (lam * lam - x(P1) - x(P2)) % p + return (x3, (lam * (x(P1) - x3) - y(P1)) % p) + + +def point_mul(P: Optional[Point], n: int) -> Optional[Point]: + R = None + for i in range(256): + if (n >> i) & 1: + R = point_add(R, P) + P = point_add(P, P) + return R + + +def bytes_from_int(x: int) -> bytes: + return x.to_bytes(32, byteorder="big") + + +def bytes_from_point(P: Point) -> bytes: + return bytes_from_int(x(P)) + + +def xor_bytes(b0: bytes, b1: bytes) -> bytes: + return bytes(x ^ y for (x, y) in zip(b0, b1)) + + +def lift_x(x: int) -> Optional[Point]: + if x >= p: + return None + y_sq = (pow(x, 3, p) + 7) % p + y = pow(y_sq, (p + 1) // 4, p) + if pow(y, 2, p) != y_sq: + return None + return (x, y if y & 1 == 0 else p - y) + + +def int_from_bytes(b: bytes) -> int: + return int.from_bytes(b, byteorder="big") + + +def hash_sha256(b: bytes) -> bytes: + return hashlib.sha256(b).digest() + + +def has_even_y(P: Point) -> bool: + assert not is_infinite(P) + return y(P) % 2 == 0 + + +def pubkey_gen(seckey: bytes) -> bytes: + d0 = int_from_bytes(seckey) + if not (1 <= d0 <= n - 1): + raise ValueError('The secret key must be an integer in the range 1..n-1.') + P = point_mul(G, d0) + assert P is not None + return bytes_from_point(P) + + +def schnorr_sign(msg: bytes, seckey: bytes, aux_rand: bytes) -> bytes: + d0 = int_from_bytes(seckey) + if not (1 <= d0 <= n - 1): + raise ValueError('The secret key must be an integer in the range 1..n-1.') + if len(aux_rand) != 32: + raise ValueError('aux_rand must be 32 bytes instead of %i.' % len(aux_rand)) + P = point_mul(G, d0) + assert P is not None + d = d0 if has_even_y(P) else n - d0 + t = xor_bytes(bytes_from_int(d), tagged_hash("BIP0340/aux", aux_rand)) + k0 = int_from_bytes(tagged_hash("BIP0340/nonce", t + bytes_from_point(P) + msg)) % n + if k0 == 0: + raise RuntimeError('Failure. This happens only with negligible probability.') + R = point_mul(G, k0) + assert R is not None + k = n - k0 if not has_even_y(R) else k0 + e = int_from_bytes(tagged_hash("BIP0340/challenge", bytes_from_point(R) + bytes_from_point(P) + msg)) % n + sig = bytes_from_point(R) + bytes_from_int((k + e * d) % n) + debug_print_vars() + if not schnorr_verify(msg, bytes_from_point(P), sig): + raise RuntimeError('The created signature does not pass verification.') + return sig + + +def schnorr_verify(msg: bytes, pubkey: bytes, sig: bytes) -> bool: + if len(pubkey) != 32: + raise ValueError('The public key must be a 32-byte array.') + if len(sig) != 64: + raise ValueError('The signature must be a 64-byte array.') + P = lift_x(int_from_bytes(pubkey)) + r = int_from_bytes(sig[0:32]) + s = int_from_bytes(sig[32:64]) + if (P is None) or (r >= p) or (s >= n): + debug_print_vars() + return False + e = int_from_bytes(tagged_hash("BIP0340/challenge", sig[0:32] + pubkey + msg)) % n + R = point_add(point_mul(G, s), point_mul(P, n - e)) + if (R is None) or (not has_even_y(R)) or (x(R) != r): + debug_print_vars() + return False + debug_print_vars() + return True + + +# +# The following code is only used to verify the test vectors. +# +import csv +import os +import sys + + +def test_vectors() -> bool: + all_passed = True + with open(os.path.join(sys.path[0], 'test-vectors.csv'), newline='') as csvfile: + reader = csv.reader(csvfile) + reader.__next__() + for row in reader: + (index, seckey_hex, pubkey_hex, aux_rand_hex, msg_hex, sig_hex, result_str, comment) = row + pubkey = bytes.fromhex(pubkey_hex) + msg = bytes.fromhex(msg_hex) + sig = bytes.fromhex(sig_hex) + result = result_str == 'TRUE' + print('\nTest vector', ('#' + index).rjust(3, ' ') + ':') + if seckey_hex != '': + seckey = bytes.fromhex(seckey_hex) + pubkey_actual = pubkey_gen(seckey) + if pubkey != pubkey_actual: + print(' * Failed key generation.') + print(' Expected key:', pubkey.hex().upper()) + print(' Actual key:', pubkey_actual.hex().upper()) + aux_rand = bytes.fromhex(aux_rand_hex) + try: + sig_actual = schnorr_sign(msg, seckey, aux_rand) + if sig == sig_actual: + print(' * Passed signing test.') + else: + print(' * Failed signing test.') + print(' Expected signature:', sig.hex().upper()) + print(' Actual signature:', sig_actual.hex().upper()) + all_passed = False + except RuntimeError as e: + print(' * Signing test raised exception:', e) + all_passed = False + result_actual = schnorr_verify(msg, pubkey, sig) + if result == result_actual: + print(' * Passed verification test.') + else: + print(' * Failed verification test.') + print(' Expected verification result:', result) + print(' Actual verification result:', result_actual) + if comment: + print(' Comment:', comment) + all_passed = False + print() + if all_passed: + print('All test vectors passed.') + else: + print('Some test vectors failed.') + return all_passed + + +# +# The following code is only used for debugging +# +import inspect + + +def pretty(v: Any) -> Any: + if isinstance(v, bytes): + return '0x' + v.hex() + if isinstance(v, int): + return pretty(bytes_from_int(v)) + if isinstance(v, tuple): + return tuple(map(pretty, v)) + return v + + +def debug_print_vars() -> None: + if DEBUG: + current_frame = inspect.currentframe() + assert current_frame is not None + frame = current_frame.f_back + assert frame is not None + print(' Variables in function ', frame.f_code.co_name, ' at line ', frame.f_lineno, ':', sep='') + for var_name, var_val in frame.f_locals.items(): + print(' ' + var_name.rjust(11, ' '), '==', pretty(var_val)) + + +if __name__ == '__main__': + test_vectors() diff --git a/pkg/crypto/ec/schnorr/bip/bip340/test-vectors.csv b/pkg/crypto/ec/schnorr/bip/bip340/test-vectors.csv new file mode 100644 index 0000000..aa317a3 --- /dev/null +++ b/pkg/crypto/ec/schnorr/bip/bip340/test-vectors.csv @@ -0,0 +1,20 @@ +index,secret key,public key,aux_rand,message,signature,verification result,comment +0,0000000000000000000000000000000000000000000000000000000000000003,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0,TRUE, +1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,0000000000000000000000000000000000000000000000000000000000000001,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A,TRUE, +2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9,DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8,C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906,7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7,TRUE, +3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3,TRUE,test fails if msg is reduced modulo p or n +4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4,TRUE, +5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key not on the curve +6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2,FALSE,has_even_y(R) is false +7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD,FALSE,negated message +8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6,FALSE,negated s value +9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0 +10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1 +11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is not an X coordinate on the curve +12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size +13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order +14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size +15,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,,71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63,TRUE,message of size 0 (added 2022-12) +16,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,11,08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF,TRUE,message of size 1 (added 2022-12) +17,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,0102030405060708090A0B0C0D0E0F1011,5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5,TRUE,message of size 17 (added 2022-12) +18,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367,TRUE,message of size 100 (added 2022-12) diff --git a/pkg/crypto/ec/schnorr/bip/bip340/test-vectors.py b/pkg/crypto/ec/schnorr/bip/bip340/test-vectors.py new file mode 100644 index 0000000..d1c6ae6 --- /dev/null +++ b/pkg/crypto/ec/schnorr/bip/bip340/test-vectors.py @@ -0,0 +1,335 @@ +import sys +from reference import * + + +def is_square(x): + return int(pow(x, (p - 1) // 2, p)) == 1 + + +def has_square_y(P): + """Determine if P has a square Y coordinate. Used in an earlier draft of BIP340.""" + assert not is_infinite(P) + return is_square(P[1]) + + +def vector0(): + seckey = bytes_from_int(3) + msg = bytes_from_int(0) + aux_rand = bytes_from_int(0) + sig = schnorr_sign(msg, seckey, aux_rand) + pubkey = pubkey_gen(seckey) + + # We should have at least one test vector where the seckey needs to be + # negated and one where it doesn't. In this one the seckey doesn't need to + # be negated. + x = int_from_bytes(seckey) + P = point_mul(G, x) + assert (y(P) % 2 == 0) + + # For historical reasons (pubkey tiebreaker was squareness and not evenness) + # we should have at least one test vector where the the point reconstructed + # from the public key has a square and one where it has a non-square Y + # coordinate. In this one Y is non-square. + pubkey_point = lift_x(pubkey) + assert (not has_square_y(pubkey_point)) + + # For historical reasons (R tiebreaker was squareness and not evenness) + # we should have at least one test vector where the the point reconstructed + # from the R.x coordinate has a square and one where it has a non-square Y + # coordinate. In this one Y is non-square. + R = lift_x(sig[0:32]) + assert (not has_square_y(R)) + + return seckey, pubkey, aux_rand, msg, sig, "TRUE", None + + +def vector1(): + seckey = bytes_from_int(0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF) + msg = bytes_from_int(0x243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89) + aux_rand = bytes_from_int(1) + + sig = schnorr_sign(msg, seckey, aux_rand) + + # The point reconstructed from the R.x coordinate has a square Y coordinate. + R = lift_x(sig[0:32]) + assert (has_square_y(R)) + + return seckey, pubkey_gen(seckey), aux_rand, msg, sig, "TRUE", None + + +def vector2(): + seckey = bytes_from_int(0xC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9) + msg = bytes_from_int(0x7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C) + aux_rand = bytes_from_int(0xC87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906) + sig = schnorr_sign(msg, seckey, aux_rand) + + # The point reconstructed from the public key has a square Y coordinate. + pubkey = pubkey_gen(seckey) + pubkey_point = lift_x(pubkey) + assert (has_square_y(pubkey_point)) + + # This signature vector would not verify if the implementer checked the + # evenness of the X coordinate of R instead of the Y coordinate. + R = lift_x(sig[0:32]) + assert (R[0] % 2 == 1) + + return seckey, pubkey, aux_rand, msg, sig, "TRUE", None + + +def vector3(): + seckey = bytes_from_int(0x0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710) + + # Need to negate this seckey before signing + x = int_from_bytes(seckey) + P = point_mul(G, x) + assert (y(P) % 2 != 0) + + msg = bytes_from_int(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + aux_rand = bytes_from_int(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + + sig = schnorr_sign(msg, seckey, aux_rand) + return seckey, pubkey_gen(seckey), aux_rand, msg, sig, "TRUE", "test fails if msg is reduced modulo p or n" + + +# Signs with a given nonce. This can be INSECURE and is only INTENDED FOR +# GENERATING TEST VECTORS. Results in an invalid signature if y(kG) is not +# even. +def insecure_schnorr_sign_fixed_nonce(msg, seckey0, k): + if len(msg) != 32: + raise ValueError('The message must be a 32-byte array.') + seckey0 = int_from_bytes(seckey0) + if not (1 <= seckey0 <= n - 1): + raise ValueError('The secret key must be an integer in the range 1..n-1.') + P = point_mul(G, seckey0) + seckey = seckey0 if has_even_y(P) else n - seckey0 + R = point_mul(G, k) + e = int_from_bytes(tagged_hash("BIP0340/challenge", bytes_from_point(R) + bytes_from_point(P) + msg)) % n + return bytes_from_point(R) + bytes_from_int((k + e * seckey) % n) + + +# Creates a singature with a small x(R) by using k = -1/2 +def vector4(): + one_half = n - 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0 + seckey = bytes_from_int(0x763758E5CBEEDEE4F7D3FC86F531C36578933228998226672F13C4F0EBE855EB) + msg = bytes_from_int(0x4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703) + sig = insecure_schnorr_sign_fixed_nonce(msg, seckey, one_half) + return None, pubkey_gen(seckey), None, msg, sig, "TRUE", None + + +default_seckey = bytes_from_int(0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF) +default_msg = bytes_from_int(0x243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89) +default_aux_rand = bytes_from_int(0xC87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906) + + +# Public key is not on the curve +def vector5(): + # This creates a dummy signature that doesn't have anything to do with the + # public key. + seckey = default_seckey + msg = default_msg + sig = schnorr_sign(msg, seckey, default_aux_rand) + + pubkey = bytes_from_int(0xEEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34) + assert (lift_x(pubkey) is None) + + return None, pubkey, None, msg, sig, "FALSE", "public key not on the curve" + + +def vector6(): + seckey = default_seckey + msg = default_msg + k = 6 + sig = insecure_schnorr_sign_fixed_nonce(msg, seckey, k) + + # Y coordinate of R is not even + R = point_mul(G, k) + assert (not has_even_y(R)) + + return None, pubkey_gen(seckey), None, msg, sig, "FALSE", "has_even_y(R) is false" + + +def vector7(): + seckey = default_seckey + msg = int_from_bytes(default_msg) + neg_msg = bytes_from_int(n - msg) + sig = schnorr_sign(neg_msg, seckey, default_aux_rand) + return None, pubkey_gen(seckey), None, bytes_from_int(msg), sig, "FALSE", "negated message" + + +def vector8(): + seckey = default_seckey + msg = default_msg + sig = schnorr_sign(msg, seckey, default_aux_rand) + sig = sig[0:32] + bytes_from_int(n - int_from_bytes(sig[32:64])) + return None, pubkey_gen(seckey), None, msg, sig, "FALSE", "negated s value" + + +def bytes_from_point_inf0(P): + if P is None: + return bytes_from_int(0) + return bytes_from_int(P[0]) + + +def vector9(): + seckey = default_seckey + msg = default_msg + + # Override bytes_from_point in schnorr_sign to allow creating a signature + # with k = 0. + k = 0 + bytes_from_point_tmp = bytes_from_point.__code__ + bytes_from_point.__code__ = bytes_from_point_inf0.__code__ + sig = insecure_schnorr_sign_fixed_nonce(msg, seckey, k) + bytes_from_point.__code__ = bytes_from_point_tmp + + return (None, pubkey_gen(seckey), None, msg, sig, "FALSE", + "sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0") + + +def bytes_from_point_inf1(P): + if P == None: + return bytes_from_int(1) + return bytes_from_int(P[0]) + + +def vector10(): + seckey = default_seckey + msg = default_msg + + # Override bytes_from_point in schnorr_sign to allow creating a signature + # with k = 0. + k = 0 + bytes_from_point_tmp = bytes_from_point.__code__ + bytes_from_point.__code__ = bytes_from_point_inf1.__code__ + sig = insecure_schnorr_sign_fixed_nonce(msg, seckey, k) + bytes_from_point.__code__ = bytes_from_point_tmp + + return (None, pubkey_gen(seckey), None, msg, sig, "FALSE", + "sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1") + + +# It's cryptographically impossible to create a test vector that fails if run +# in an implementation which merely misses the check that sig[0:32] is an X +# coordinate on the curve. This test vector just increases test coverage. +def vector11(): + seckey = default_seckey + msg = default_msg + sig = schnorr_sign(msg, seckey, default_aux_rand) + + # Replace R's X coordinate with an X coordinate that's not on the curve + x_not_on_curve = bytes_from_int(0x4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D) + assert (lift_x(x_not_on_curve) is None) + sig = x_not_on_curve + sig[32:64] + + return None, pubkey_gen(seckey), None, msg, sig, "FALSE", "sig[0:32] is not an X coordinate on the curve" + + +# It's cryptographically impossible to create a test vector that fails if run +# in an implementation which merely misses the check that sig[0:32] is smaller +# than the field size. This test vector just increases test coverage. +def vector12(): + seckey = default_seckey + msg = default_msg + sig = schnorr_sign(msg, seckey, default_aux_rand) + + # Replace R's X coordinate with an X coordinate that's equal to field size + sig = bytes_from_int(p) + sig[32:64] + + return None, pubkey_gen(seckey), None, msg, sig, "FALSE", "sig[0:32] is equal to field size" + + +# It's cryptographically impossible to create a test vector that fails if run +# in an implementation which merely misses the check that sig[32:64] is smaller +# than the curve order. This test vector just increases test coverage. +def vector13(): + seckey = default_seckey + msg = default_msg + sig = schnorr_sign(msg, seckey, default_aux_rand) + + # Replace s with a number that's equal to the curve order + sig = sig[0:32] + bytes_from_int(n) + + return None, pubkey_gen(seckey), None, msg, sig, "FALSE", "sig[32:64] is equal to curve order" + + +# Test out of range pubkey +# It's cryptographically impossible to create a test vector that fails if run +# in an implementation which accepts out of range pubkeys because we can't find +# a secret key for such a public key and therefore can not create a signature. +# This test vector just increases test coverage. +def vector14(): + # This creates a dummy signature that doesn't have anything to do with the + # public key. + seckey = default_seckey + msg = default_msg + sig = schnorr_sign(msg, seckey, default_aux_rand) + pubkey_int = p + 1 + pubkey = bytes_from_int(pubkey_int) + assert (lift_x(pubkey) is None) + # If an implementation would reduce a given public key modulo p then the + # pubkey would be valid + assert (lift_x(bytes_from_int(pubkey_int % p)) is not None) + + return ( + None, pubkey, None, msg, sig, "FALSE", "public key is not a valid X coordinate because it exceeds the field size") + + +def varlen_vector(msg_int): + seckey = bytes_from_int(int(16 * "0340", 16)) + pubkey = pubkey_gen(seckey) + aux_rand = bytes_from_int(0) + msg = msg_int.to_bytes((msg_int.bit_length() + 7) // 8, "big") + sig = schnorr_sign(msg, seckey, aux_rand) + comment = "message of size %d (added 2022-12)" + return seckey, pubkey, aux_rand, msg, sig, "TRUE", comment % len(msg) + + +vector15 = lambda: varlen_vector(0) +vector16 = lambda: varlen_vector(0x11) +vector17 = lambda: varlen_vector(0x0102030405060708090A0B0C0D0E0F1011) +vector18 = lambda: varlen_vector(int(100 * "99", 16)) + +vectors = [ + vector0(), + vector1(), + vector2(), + vector3(), + vector4(), + vector5(), + vector6(), + vector7(), + vector8(), + vector9(), + vector10(), + vector11(), + vector12(), + vector13(), + vector14(), + vector15(), + vector16(), + vector17(), + vector18(), +] + + +# Converts the byte strings of a test vector into hex strings +def bytes_to_hex(seckey, pubkey, aux_rand, msg, sig, result, comment): + return (seckey.hex().upper() if seckey is not None else None, pubkey.hex().upper(), + aux_rand.hex().upper() if aux_rand is not None else None, msg.hex().upper(), sig.hex().upper(), result, + comment) + + +vectors = list( + map(lambda vector: bytes_to_hex(vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6]), + vectors)) + + +def print_csv(vectors): + writer = csv.writer(sys.stdout) + writer.writerow( + ("index", "secret key", "public key", "aux_rand", "message", "signature", "verification result", "comment")) + for (i, v) in enumerate(vectors): + writer.writerow((i,) + v) + + +print_csv(vectors) diff --git a/pkg/crypto/ec/schnorr/doc.go b/pkg/crypto/ec/schnorr/doc.go new file mode 100644 index 0000000..12d1d5b --- /dev/null +++ b/pkg/crypto/ec/schnorr/doc.go @@ -0,0 +1,103 @@ +// Copyright (c) 2020-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package schnorr provides custom Schnorr signing and verification via +// secp256k1. +// +// This package provides data structures and functions necessary to produce and +// verify deterministic canonical Schnorr signatures using a custom scheme named +// EC-Schnorr-DCRv0 that is described herein. The signatures and implementation +// are optimized specifically for the secp256k1 curve. See +// https://www.secg.org/sec2-v2.pdf for details on the secp256k1 standard. +// +// It also provides functions to parse and serialize the Schnorr signatures +// according to the specification described herein. +// +// A comprehensive suite of tests is provided to ensure proper functionality. +// +// # Overview +// +// A Schnorr signature is a digital signature scheme that is known for its +// simplicity, provable security and efficient generation of short signatures. +// +// It provides many advantages over ECDSA signatures that make them ideal for +// use with the only real downside being that they are not well standardized at +// the time of this writing. +// +// Some of the advantages over ECDSA include: +// +// - They are linear which makes them easier to aggregate and use in +// protocols that build on them such as multi-party signatures, threshold +// signatures, adaptor signatures, and blind signatures +// - They are provably secure with weaker assumptions than the best known +// security proofs for ECDSA +// - Specifically Schnorr signatures are provably secure under SUF-CMA (Strong +// Existential Unforgeability under Chosen Message Attack) in the ROM +// (Random Oracle Model) which guarantees that as long as the hash +// function behaves ideally, the only way to break Schnorr signatures is +// by solving the ECDLP (Elliptic Curve Discrete Logarithm Problem). +// - Their relatively straightforward and efficient aggregation properties +// make them excellent for scalability and allow them to provide some nice +// secrecy characteristics +// - They support faster batch verification unlike the standardized version of +// ECDSA signatures +// +// # Custom Schnorr-based Signature Scheme +// +// As mentioned in the overview, the primary downside of Schnorr signatures for +// elliptic curves is that they are not standardized as well as ECDSA signatures, +// which means there are a number of variations that are not compatible with +// each other. +// +// In addition, many of the standardization attempts have had various +// disadvantages that make them unsuitable for use in Decred. Some of these +// details and some insight into the design decisions made are discussed further +// in the README.md file. +// +// Consequently, this package implements a custom Schnorr-based signature scheme +// named EC-Schnorr-DCRv0 suitable for use in Decred. +// +// The following provides a high-level overview of the key design features of +// the scheme: +// +// - Uses signatures of the form (R, s) +// - Produces 64-byte signatures by only encoding the x coordinate of R +// - Enforces even y coordinates for R to support efficient verification by +// disambiguating the two possible y coordinates +// - Canonically encodes by both components of the signature with 32-bytes +// each +// - Uses BLAKE-256 with 14 rounds for the hash function to calculate +// challenge e +// - Uses RFC6979 to obviate the need for an entropy source at signing time +// - Produces deterministic signatures for a given message and secret key pair +// +// # EC-Schnorr-DCRv0 Specification +// +// See the README.md file for the specific details of the signing and +// verification algorithm as well as the signature serialization format. +// +// # Future Design Considerations +// +// It is worth noting that there are some additional optimizations and +// modifications that have been identified since the introduction of +// EC-Schnorr-DCRv0 that can be made to further harden security for multi-party +// and threshold signature use cases as well provide the opportunity for faster +// signature verification with a sufficiently optimized implementation. +// +// However, the v0 scheme is used in the existing consensus rules and any +// changes to the signature scheme would invalidate existing uses. Therefore +// changes in this regard will need to come in the form of a v1 signature scheme +// and be accompanied by the necessary consensus updates. +// +// # Schnorr use in Decred +// +// At the time of this writing, Schnorr signatures are not yet in widespread use +// on the Decred network, largely due to the current lack of support in wallets +// and infrastructure for secure multi-party and threshold signatures. +// +// However, the consensus rules and scripting engine supports the necessary +// primitives and given many of the beneficial properties of Schnorr signatures, +// a good goal is to work towards providing the additional infrastructure to +// increase their usage. +package schnorr diff --git a/pkg/crypto/ec/schnorr/error.go b/pkg/crypto/ec/schnorr/error.go new file mode 100644 index 0000000..32a107e --- /dev/null +++ b/pkg/crypto/ec/schnorr/error.go @@ -0,0 +1,71 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2015-2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package schnorr + +// 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. +const ( + // ErrInvalidHashLen indicates that the input hash to sign or verify is not + // the required length. + ErrInvalidHashLen = ErrorKind("ErrInvalidHashLen") + // ErrSecretKeyIsZero indicates an attempt was made to sign a message with + // a secret key that is equal to zero. + ErrSecretKeyIsZero = ErrorKind("ErrSecretKeyIsZero") + ErrPrivateKeyIsZero = ErrSecretKeyIsZero + // ErrSchnorrHashValue indicates that the hash of (R || m) was too large and + // so a new nonce should be used. + ErrSchnorrHashValue = ErrorKind("ErrSchnorrHashValue") + // ErrPubKeyNotOnCurve indicates that a point was not on the given elliptic + // curve. + ErrPubKeyNotOnCurve = ErrorKind("ErrPubKeyNotOnCurve") + // ErrSigRYIsOdd indicates that the calculated Y value of R was odd. + ErrSigRYIsOdd = ErrorKind("ErrSigRYIsOdd") + // ErrSigRNotOnCurve indicates that the calculated or given point R for some + // signature was not on the curve. + ErrSigRNotOnCurve = ErrorKind("ErrSigRNotOnCurve") + // ErrUnequalRValues indicates that the calculated point R for some + // signature was not the same as the given R value for the signature. + ErrUnequalRValues = ErrorKind("ErrUnequalRValues") + // ErrSigTooShort is returned when a signature that should be a Schnorr + // signature is too short. + ErrSigTooShort = ErrorKind("ErrSigTooShort") + // ErrSigTooLong is returned when a signature that should be a Schnorr + // signature is too long. + ErrSigTooLong = ErrorKind("ErrSigTooLong") + // ErrSigRTooBig is returned when a signature has r with a value that is + // greater than or equal to the prime of the field underlying the group. + ErrSigRTooBig = ErrorKind("ErrSigRTooBig") + // ErrSigSTooBig is returned when a signature has s with a value that is + // greater than or equal to the group order. + ErrSigSTooBig = ErrorKind("ErrSigSTooBig") +) + +// Error satisfies the error interface and prints human-readable errors. +func (err ErrorKind) Error() string { return string(err) } + +// Error identifies an error related to a schnorr signature. 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 { + Err error + Description string +} + +// Error satisfies the error interface and prints human-readable errors. +func (err Error) Error() string { return err.Description } + +// Unwrap returns the underlying wrapped error. +func (err Error) Unwrap() (ee error) { return err.Err } + +// signatureError creates an Error given a set of arguments. +func signatureError(kind ErrorKind, desc string) (err error) { + return Error{Err: kind, Description: desc} +} diff --git a/pkg/crypto/ec/schnorr/pubkey.go b/pkg/crypto/ec/schnorr/pubkey.go new file mode 100644 index 0000000..7bdabfe --- /dev/null +++ b/pkg/crypto/ec/schnorr/pubkey.go @@ -0,0 +1,50 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Copyright (c) 2015-2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package schnorr + +import ( + "fmt" + + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// These constants define the lengths of serialized public keys. + +const ( + PubKeyBytesLen = 32 +) + +// ParsePubKey parses a public key for a koblitz curve from a bytestring into a +// btcec.Publickey, verifying that it is valid. It only supports public keys in +// the BIP-340 32-byte format. +func ParsePubKey(pubKeyStr []byte) (*btcec.PublicKey, error) { + if pubKeyStr == nil { + err := fmt.Errorf("nil pubkey byte string") + return nil, err + } + if len(pubKeyStr) != PubKeyBytesLen { + err := fmt.Errorf( + "bad pubkey byte string size (want %v, have %v)", + PubKeyBytesLen, len(pubKeyStr), + ) + return nil, err + } + // We'll manually prepend the compressed byte so we can re-use the existing + // pubkey parsing routine of the main btcec package. + var keyCompressed [btcec.PubKeyBytesLenCompressed]byte + keyCompressed[0] = secp256k1.PubKeyFormatCompressedEven + copy(keyCompressed[1:], pubKeyStr) + return btcec.ParsePubKey(keyCompressed[:]) +} + +// SerializePubKey serializes a public key as specified by BIP 340. Public keys +// in this format are 32 bytes in length and are assumed to have an even y +// coordinate. +func SerializePubKey(pub *btcec.PublicKey) []byte { + pBytes := pub.SerializeCompressed() + return pBytes[1:] +} diff --git a/pkg/crypto/ec/schnorr/schnorrerror_test.go b/pkg/crypto/ec/schnorr/schnorrerror_test.go new file mode 100644 index 0000000..0e5bf53 --- /dev/null +++ b/pkg/crypto/ec/schnorr/schnorrerror_test.go @@ -0,0 +1,142 @@ +// Copyright (c) 2020 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package schnorr + +import ( + "errors" + "testing" +) + +// TestErrorKindStringer tests the stringized output for the ErrorKind type. +func TestErrorKindStringer(t *testing.T) { + tests := []struct { + in ErrorKind + want string + }{ + {ErrInvalidHashLen, "ErrInvalidHashLen"}, + {ErrSecretKeyIsZero, "ErrSecretKeyIsZero"}, + {ErrSchnorrHashValue, "ErrSchnorrHashValue"}, + {ErrPubKeyNotOnCurve, "ErrPubKeyNotOnCurve"}, + {ErrSigRYIsOdd, "ErrSigRYIsOdd"}, + {ErrSigRNotOnCurve, "ErrSigRNotOnCurve"}, + {ErrUnequalRValues, "ErrUnequalRValues"}, + {ErrSigTooShort, "ErrSigTooShort"}, + {ErrSigTooLong, "ErrSigTooLong"}, + {ErrSigRTooBig, "ErrSigRTooBig"}, + {ErrSigSTooBig, "ErrSigSTooBig"}, + } + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("#%d: got: %s want: %s", i, result, test.want) + continue + } + } +} + +// TestError tests the error output for the Error type. +func TestError(t *testing.T) { + tests := []struct { + in Error + want string + }{ + { + Error{Description: "some error"}, + "some error", + }, { + Error{Description: "human-readable error"}, + "human-readable error", + }, + } + + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("#%d: got: %s want: %s", i, result, test.want) + continue + } + } +} + +// TestErrorKindIsAs ensures both ErrorKind and Error can be identified +// as being a specific error via errors.Is and unwrapped via errors.As. +func TestErrorKindIsAs(t *testing.T) { + tests := []struct { + name string + err error + target error + wantMatch bool + wantAs ErrorKind + }{ + { + name: "ErrInvalidHashLen == ErrInvalidHashLen", + err: ErrInvalidHashLen, + target: ErrInvalidHashLen, + wantMatch: true, + wantAs: ErrInvalidHashLen, + }, { + name: "Error.ErrInvalidHashLen == ErrInvalidHashLen", + err: signatureError(ErrInvalidHashLen, ""), + target: ErrInvalidHashLen, + wantMatch: true, + wantAs: ErrInvalidHashLen, + }, { + name: "Error.ErrInvalidHashLen == Error.ErrInvalidHashLen", + err: signatureError(ErrInvalidHashLen, ""), + target: signatureError(ErrInvalidHashLen, ""), + wantMatch: true, + wantAs: ErrInvalidHashLen, + }, { + name: "ErrSecretKeyIsZero != ErrInvalidHashLen", + err: ErrSecretKeyIsZero, + target: ErrInvalidHashLen, + wantMatch: false, + wantAs: ErrSecretKeyIsZero, + }, { + name: "Error.ErrSecretKeyIsZero != ErrInvalidHashLen", + err: signatureError(ErrSecretKeyIsZero, ""), + target: ErrInvalidHashLen, + wantMatch: false, + wantAs: ErrSecretKeyIsZero, + }, { + name: "ErrSecretKeyIsZero != Error.ErrInvalidHashLen", + err: ErrSecretKeyIsZero, + target: signatureError(ErrInvalidHashLen, ""), + wantMatch: false, + wantAs: ErrSecretKeyIsZero, + }, { + name: "Error.ErrSecretKeyIsZero != Error.ErrInvalidHashLen", + err: signatureError(ErrSecretKeyIsZero, ""), + target: signatureError(ErrInvalidHashLen, ""), + wantMatch: false, + wantAs: ErrSecretKeyIsZero, + }, + } + for _, test := range tests { + // Ensure the error matches or not depending on the expected result. + result := errors.Is(test.err, test.target) + if result != test.wantMatch { + t.Errorf( + "%s: incorrect error identification -- got %v, want %v", + test.name, result, test.wantMatch, + ) + continue + } + // Ensure the underlying error kind can be unwrapped and is the + // expected code. + var code ErrorKind + if !errors.As(test.err, &code) { + t.Errorf("%s: unable to unwrap to error", test.name) + continue + } + if !errors.Is(code, test.wantAs) { + t.Errorf( + "%s: unexpected unwrapped error -- got %v, want %v", + test.name, code, test.wantAs, + ) + continue + } + } +} diff --git a/pkg/crypto/ec/schnorr/signature.go b/pkg/crypto/ec/schnorr/signature.go new file mode 100644 index 0000000..9838ed8 --- /dev/null +++ b/pkg/crypto/ec/schnorr/signature.go @@ -0,0 +1,515 @@ +// Copyright (c) 2013-2022 The btcsuite developers + +package schnorr + +import ( + "fmt" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/chainhash" + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +const ( + // SignatureSize is the size of an encoded Schnorr signature. + SignatureSize = 64 + // scalarSize is the size of an encoded big endian scalar. + scalarSize = 32 +) + +var ( + // rfc6979ExtraDataV0 is the extra data to feed to RFC6979 when generating + // the deterministic nonce for the BIP-340 scheme. This ensures the same + // nonce is not generated for the same message and key as for other signing + // algorithms such as ECDSA. + // + // It is equal to SHA-256(by("BIP-340")). + rfc6979ExtraDataV0 = [32]uint8{ + 0xa3, 0xeb, 0x4c, 0x18, 0x2f, 0xae, 0x7e, 0xf4, + 0xe8, 0x10, 0xc6, 0xee, 0x13, 0xb0, 0xe9, 0x26, + 0x68, 0x6d, 0x71, 0xe8, 0x7f, 0x39, 0x4f, 0x79, + 0x9c, 0x00, 0xa5, 0x21, 0x03, 0xcb, 0x4e, 0x17, + } +) + +// Signature is a type representing a Schnorr signature. +type Signature struct { + r btcec.FieldVal + s btcec.ModNScalar +} + +// NewSignature instantiates a new signature given some r and s values. +func NewSignature(r *btcec.FieldVal, s *btcec.ModNScalar) *Signature { + var sig Signature + sig.r.Set(r).Normalize() + sig.s.Set(s) + return &sig +} + +// Serialize returns the Schnorr signature in a stricter format. +// +// The signatures are encoded as +// +// sig[0:32] +// x coordinate of the point R, encoded as a big-endian uint256 +// sig[32:64] +// s, encoded also as big-endian uint256 +func (sig Signature) Serialize() []byte { + // Total length of returned signature is the length of r and s. + var b [SignatureSize]byte + sig.r.PutBytesUnchecked(b[0:32]) + sig.s.PutBytesUnchecked(b[32:64]) + return b[:] +} + +// ParseSignature parses a signature according to the BIP-340 specification and +// enforces the following additional restrictions specific to secp256k1: +// +// - The r component must be in the valid range for secp256k1 field elements +// +// - The s component must be in the valid range for secp256k1 scalars +func ParseSignature(sig []byte) (*Signature, error) { + // The signature must be the correct length. + sigLen := len(sig) + if sigLen < SignatureSize { + str := fmt.Sprintf( + "malformed signature: too short: %d < %d", sigLen, + SignatureSize, + ) + return nil, signatureError(ErrSigTooShort, str) + } + if sigLen > SignatureSize { + str := fmt.Sprintf( + "malformed signature: too long: %d > %d", sigLen, + SignatureSize, + ) + return nil, signatureError(ErrSigTooLong, str) + } + // The signature is validly encoded at this point, however, enforce + // additional restrictions to ensure r is in the range [0, p-1], and s is in + // the range [0, n-1] since valid Schnorr signatures are required to be in + // that range per spec. + var r btcec.FieldVal + if overflow := r.SetByteSlice(sig[0:32]); overflow { + str := "invalid signature: r >= field prime" + return nil, signatureError(ErrSigRTooBig, str) + } + var s btcec.ModNScalar + s.SetByteSlice(sig[32:64]) + // Return the signature. + return NewSignature(&r, &s), nil +} + +// IsEqual compares this Signature instance to the one passed, returning true if +// both Signatures are equivalent. A signature is equivalent to another if they +// both have the same scalar value for R and S. +func (sig Signature) IsEqual(otherSig *Signature) bool { + return sig.r.Equals(&otherSig.r) && sig.s.Equals(&otherSig.s) +} + +// schnorrVerify attempt to verify the signature for the provided hash and +// secp256k1 public key and either returns nil if successful or a specific error +// indicating why it failed if not successful. +// +// This differs from the exported Verify method in that it returns a specific +// error to support better testing, while the exported method simply returns a +// bool indicating success or failure. +func schnorrVerify(sig *Signature, hash []byte, pubKeyBytes []byte) error { + // The algorithm for producing a BIP-340 signature is described in + // README.md and is reproduced here for reference: + // + // 1. Fail if m is not 32 bytes + // 2. P = lift_x(int(pk)). + // 3. r = int(sig[0:32]); fail is r >= p. + // 4. s = int(sig[32:64]); fail if s >= n. + // 5. e = int(tagged_hash("BIP0340/challenge", bytes(r) || bytes(P) || M)) mod n. + // 6. R = s*G - e*P + // 7. Fail if is_infinite(R) + // 8. Fail if not hash_even_y(R) + // 9. Fail is x(R) != r. + // 10. Return success iff not failure occured before reachign this + // point. + + // // Step 1. + // // + // // Fail if m is not 32 bytes + // if len(hash) != scalarSize { + // str := fmt.Sprintf("wrong size for message (got %v, want %v)", + // len(hash), scalarSize) + // return signatureError(schnorr.ErrInvalidHashLen, str) + // } + + // Step 2. + // + // P = lift_x(int(pk)) + // + // Fail if P is not a point on the curve + pubKey, err := ParsePubKey(pubKeyBytes) + if chk.E(err) { + return err + } + if !pubKey.IsOnCurve() { + str := "pubkey point is not on curve" + return signatureError(ErrPubKeyNotOnCurve, str) + } + // Step 3. + // + // Fail if r >= p + // + // Note this is already handled by the fact r is a field element. + // + // Step 4. + // + // Fail if s >= n + // + // Note this is already handled by the fact s is a mod n scalar. + // + // Step 5. + // + // e = int(tagged_hash("BIP0340/challenge", bytes(r) || bytes(P) || M)) mod n. + var rBytes [32]byte + sig.r.PutBytesUnchecked(rBytes[:]) + pBytes := SerializePubKey(pubKey) + commitment := chainhash.TaggedHash( + chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, + ) + var e btcec.ModNScalar + e.SetBytes((*[32]byte)(commitment)) + // Negate e here so we can use AddNonConst below to subtract the s*G + // point from e*P. + e.Negate() + // Step 6. + // + // R = s*G - e*P + var P, R, sG, eP btcec.JacobianPoint + pubKey.AsJacobian(&P) + btcec.ScalarBaseMultNonConst(&sig.s, &sG) + btcec.ScalarMultNonConst(&e, &P, &eP) + btcec.AddNonConst(&sG, &eP, &R) + // Step 7. + // + // Fail if R is the point at infinity + if (R.X.IsZero() && R.Y.IsZero()) || R.Z.IsZero() { + str := "calculated R point is the point at infinity" + return signatureError(ErrSigRNotOnCurve, str) + } + // Step 8. + // + // Fail if R.y is odd + // + // Note that R must be in affine coordinates for this check. + R.ToAffine() + if R.Y.IsOdd() { + str := "calculated R y-value is odd" + return signatureError(ErrSigRYIsOdd, str) + } + // Step 9. + // + // Verified if R.x == r + // + // Note that R must be in affine coordinates for this check. + if !sig.r.Equals(&R.X) { + str := "calculated R point was not given R" + return signatureError(ErrUnequalRValues, str) + } + // Step 10. + // + // Return success iff not failure occured before reachign this + return nil +} + +// Verify returns whether or not the signature is valid for the provided hash +// and secp256k1 public key. +func (sig *Signature) Verify(hash []byte, pubKey *btcec.PublicKey) bool { + pubkeyBytes := SerializePubKey(pubKey) + return schnorrVerify(sig, hash, pubkeyBytes) == nil +} + +// zeroArray zeroes the memory of a scalar array. +func zeroArray(a *[scalarSize]byte) { + for i := 0; i < scalarSize; i++ { + a[i] = 0x00 + } +} + +// schnorrSign generates an BIP-340 signature over the secp256k1 curve for the +// provided hash (which should be the result of hashing a larger message) using +// the given nonce and secret key. The produced signature is deterministic (the +// same message, nonce, and key yield the same signature) and canonical. +// +// WARNING: The hash MUST be 32 bytes, and both the nonce and secret keys must +// NOT be 0. Since this is an internal use function, these preconditions MUST be +// satisified by the caller. +func schnorrSign( + privKey, nonce *btcec.ModNScalar, pubKey *btcec.PublicKey, + hash []byte, opts *signOptions, +) (*Signature, error) { + + // The algorithm for producing a BIP-340 signature is described in + // README.md and is reproduced here for reference: + // + // G = curve generator + // n = curve order + // d = secret key + // m = message + // a = input randmoness + // r, s = signature + // + // 1. d' = int(d) + // 2. Fail if m is not 32 bytes + // 3. Fail if d = 0 or d >= n + // 4. P = d'*G + // 5. Negate d if P.y is odd + // 6. t = bytes(d) xor tagged_hash("BIP0340/aux", t || bytes(P) || m) + // 7. rand = tagged_hash("BIP0340/nonce", a) + // 8. k' = int(rand) mod n + // 9. Fail if k' = 0 + // 10. R = 'k*G + // 11. Negate k if R.y id odd + // 12. e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || m) mod n + // 13. sig = bytes(R) || bytes((k + e*d)) mod n + // 14. If Verify(bytes(P), m, sig) fails, abort. + // 15. return sig. + // + // Note that the set of functional options passed in may modify the + // above algorithm. Namely if CustomNonce is used, then steps 6-8 are + // replaced with a process that generates the nonce using rfc6979. If + // FastSign is passed, then we skip set 14. + + // NOTE: Steps 1-9 are performed by the caller. + + // + // Step 10. + // + // R = kG + var R btcec.JacobianPoint + k := *nonce + btcec.ScalarBaseMultNonConst(&k, &R) + // Step 11. + // + // Negate nonce k if R.y is odd (R.y is the y coordinate of the point R) + // + // Note that R must be in affine coordinates for this check. + R.ToAffine() + if R.Y.IsOdd() { + k.Negate() + } + // Step 12. + // + // e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || m) mod n + var rBytes [32]byte + r := &R.X + r.PutBytesUnchecked(rBytes[:]) + pBytes := SerializePubKey(pubKey) + commitment := chainhash.TaggedHash( + chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, + ) + var e btcec.ModNScalar + if overflow := e.SetBytes((*[32]byte)(commitment)); overflow != 0 { + k.Zero() + str := "hash of (r || P || m) too big" + return nil, signatureError(ErrSchnorrHashValue, str) + } + // Step 13. + // + // s = k + e*d mod n + s := new(btcec.ModNScalar).Mul2(&e, privKey).Add(&k) + k.Zero() + sig := NewSignature(r, s) + // Step 14. + // + // If Verify(bytes(P), m, sig) fails, abort. + if !opts.fastSign { + if err := schnorrVerify(sig, hash, pBytes); chk.T(err) { + return nil, err + } + } + // Step 15. + // + // Return (r, s) + return sig, nil +} + +// SignOption is a functional option argument that allows callers to modify the +// way we generate BIP-340 schnorr signatures. +type SignOption func(*signOptions) + +// signOptions houses the set of functional options that can be used to modify +// the method used to generate the BIP-340 signature. +type signOptions struct { + // fastSign determines if we'll skip the check at the end of the routine + // where we attempt to verify the produced signature. + fastSign bool + // authNonce allows the user to pass in their own nonce information, which + // is useful for schemes like mu-sig. + authNonce *[32]byte +} + +// defaultSignOptions returns the default set of signing operations. +func defaultSignOptions() *signOptions { return &signOptions{} } + +// FastSign forces signing to skip the extra verification step at the end. +// Peformance sensitive applications may opt to use this option to speed up the +// signing operation. +func FastSign() SignOption { + return func(o *signOptions) { o.fastSign = true } +} + +// CustomNonce allows users to pass in a custom set of auxData that's used as +// input randomness to generate the nonce used during signing. Users may want +// to specify this custom value when using multi-signatures schemes such as +// Mu-Sig2. If this option isn't set, then rfc6979 will be used to generate the +// nonce material. +func CustomNonce(auxData [32]byte) SignOption { + return func(o *signOptions) { o.authNonce = &auxData } +} + +// Sign generates an BIP-340 signature over the secp256k1 curve for the provided +// hash (which should be the result of hashing a larger message) using the given +// secret key. The produced signature is deterministic (the same message and the +// same key yield the same signature) and canonical. +// +// Note that the current signing implementation has a few remaining variable +// time aspects which make use of the secret key and the generated nonce, which +// can expose the signer to constant time attacks. As a result, this function +// should not be used in situations where there is the possibility of someone +// having EM field/cache/etc access. +func Sign( + privKey *btcec.SecretKey, hash []byte, + signOpts ...SignOption, +) (*Signature, error) { + // First, parse the set of optional signing options. + opts := defaultSignOptions() + for _, option := range signOpts { + option(opts) + } + // The algorithm for producing a BIP-340 signature is described in README.md + // and is reproduced here for reference: + // + // G = curve generator + // n = curve order + // d = secret key + // m = message + // a = input randmoness + // r, s = signature + // + // 1. d' = int(d) + // 2. Fail if m is not 32 bytes + // 3. Fail if d = 0 or d >= n + // 4. P = d'*G + // 5. Negate d if P.y is odd + // 6. t = bytes(d) xor tagged_hash("BIP0340/aux", t || bytes(P) || m) + // 7. rand = tagged_hash("BIP0340/nonce", a) + // 8. k' = int(rand) mod n + // 9. Fail if k' = 0 + // 10. R = 'k*G + // 11. Negate k if R.y id odd + // 12. e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || mod) mod n + // 13. sig = bytes(R) || bytes((k + e*d)) mod n + // 14. If Verify(bytes(P), m, sig) fails, abort. + // 15. return sig. + // + // Note that the set of functional options passed in may modify the above + // algorithm. Namely if CustomNonce is used, then steps 6-8 are replaced + // with a process that generates the nonce using rfc6979. If FastSign is + // passed, then we skip set 14. + // + // Step 1. + // + // d' = int(d) + var privKeyScalar btcec.ModNScalar + privKeyScalar.Set(&privKey.Key) + + // Step 2. + // + // Fail if m is not 32 bytes + // if len(hash) != scalarSize { + // str := fmt.Sprintf("wrong size for message hash (got %v, want %v)", + // len(hash), scalarSize) + // return nil, signatureError(schnorr.ErrInvalidHashLen, str) + // } + // + // Step 3. + // + // Fail if d = 0 or d >= n + if privKeyScalar.IsZero() { + str := "secret key is zero" + return nil, signatureError(ErrSecretKeyIsZero, str) + } + // Step 4. + // + // P = 'd*G + pub := privKey.PubKey() + // Step 5. + // + // Negate d if P.y is odd. + pubKeyBytes := pub.SerializeCompressed() + if pubKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd { + privKeyScalar.Negate() + } + // At this point, we check to see if a CustomNonce has been passed in, and + // if so, then we'll deviate from the main routine here by generating the + // nonce value as specified by BIP-0340. + if opts.authNonce != nil { + // Step 6. + // + // t = bytes(d) xor tagged_hash("BIP0340/aux", a) + privBytes := privKeyScalar.Bytes() + t := chainhash.TaggedHash( + chainhash.TagBIP0340Aux, (*opts.authNonce)[:], + ) + for i := 0; i < len(t); i++ { + t[i] ^= privBytes[i] + } + // Step 7. + // + // rand = tagged_hash("BIP0340/nonce", t || bytes(P) || m) + // + // We snip off the first byte of the serialized pubkey, as we only need + // the x coordinate and not the market byte. + rand := chainhash.TaggedHash( + chainhash.TagBIP0340Nonce, t[:], pubKeyBytes[1:], hash, + ) + // Step 8. + // + // k'= int(rand) mod n + var kPrime btcec.ModNScalar + kPrime.SetBytes((*[32]byte)(rand)) + // Step 9. + // + // Fail if k' = 0 + if kPrime.IsZero() { + str := fmt.Sprintf("generated nonce is zero") + return nil, signatureError(ErrSchnorrHashValue, str) + } + sig, err := schnorrSign(&privKeyScalar, &kPrime, pub, hash, opts) + kPrime.Zero() + if err != nil { + return nil, err + } + return sig, nil + } + var privKeyBytes [scalarSize]byte + privKeyScalar.PutBytes(&privKeyBytes) + defer zeroArray(&privKeyBytes) + for iteration := uint32(0); ; iteration++ { + var k *secp256k1.ModNScalar + // Step 6-9. + // + // Use RFC6979 to generate a deterministic nonce k in [1, n-1] + // parameterized by the secret key, message being signed, extra data + // that identifies the scheme, and an iteration count + k = btcec.NonceRFC6979( + privKeyBytes[:], hash, rfc6979ExtraDataV0[:], nil, iteration, + ) + // Steps 10-15. + sig, err := schnorrSign(&privKeyScalar, k, pub, hash, opts) + k.Zero() + if err != nil { + // Try again with a new nonce. + continue + } + return sig, nil + } +} diff --git a/pkg/crypto/ec/schnorr/signature_test.go b/pkg/crypto/ec/schnorr/signature_test.go new file mode 100644 index 0000000..4c9b4ff --- /dev/null +++ b/pkg/crypto/ec/schnorr/signature_test.go @@ -0,0 +1,323 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Copyright (c) 2015-2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package schnorr + +import ( + "errors" + "strings" + "testing" + "testing/quick" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/encoders/hex" + + "github.com/davecgh/go-spew/spew" +) + +type bip340Test struct { + secretKey string + publicKey string + auxRand string + message string + signature string + verifyResult bool + validPubKey bool + expectErr error + rfc6979 bool +} + +var bip340TestVectors = []bip340Test{ + { + secretKey: "0000000000000000000000000000000000000000000000000000000000000003", + publicKey: "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + auxRand: "0000000000000000000000000000000000000000000000000000000000000000", + message: "0000000000000000000000000000000000000000000000000000000000000000", + signature: "04E7F9037658A92AFEB4F25BAE5339E3DDCA81A353493827D26F16D92308E49E2A25E92208678A2DF86970DA91B03A8AF8815A8A60498B358DAF560B347AA557", + verifyResult: true, + validPubKey: true, + rfc6979: true, + }, + { + secretKey: "0000000000000000000000000000000000000000000000000000000000000003", + publicKey: "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + auxRand: "0000000000000000000000000000000000000000000000000000000000000000", + message: "0000000000000000000000000000000000000000000000000000000000000000", + signature: "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", + verifyResult: true, + validPubKey: true, + }, + { + secretKey: "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", + publicKey: "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + auxRand: "0000000000000000000000000000000000000000000000000000000000000001", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", + verifyResult: true, + validPubKey: true, + }, + { + secretKey: "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", + publicKey: "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", + auxRand: "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", + message: "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", + signature: "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7", + verifyResult: true, + validPubKey: true, + }, + { + secretKey: "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", + publicKey: "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", + auxRand: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + message: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + signature: "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3", + verifyResult: true, + validPubKey: true, + }, + { + publicKey: "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", + message: "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", + signature: "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4", + verifyResult: true, + validPubKey: true, + }, + { + publicKey: "EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + verifyResult: false, + validPubKey: false, + expectErr: secp256k1.ErrPubKeyNotOnCurve, + }, + { + publicKey: "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2", + verifyResult: false, + validPubKey: true, + expectErr: ErrSigRYIsOdd, + }, + { + publicKey: "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD", + verifyResult: false, + validPubKey: true, + expectErr: ErrSigRYIsOdd, + }, + { + publicKey: "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6", + verifyResult: false, + validPubKey: true, + expectErr: ErrUnequalRValues, + }, + { + publicKey: "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051", + verifyResult: false, + validPubKey: true, + expectErr: ErrSigRNotOnCurve, + }, + { + publicKey: "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197", + verifyResult: false, + validPubKey: true, + expectErr: ErrSigRNotOnCurve, + }, + { + publicKey: "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + verifyResult: false, + validPubKey: true, + expectErr: ErrUnequalRValues, + }, + { + publicKey: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", + message: "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", + signature: "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", + verifyResult: false, + validPubKey: false, + expectErr: secp256k1.ErrPubKeyXTooBig, + }, + { + secretKey: "0340034003400340034003400340034003400340034003400340034003400340", + publicKey: "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + auxRand: "0000000000000000000000000000000000000000000000000000000000000000", + message: "", + signature: "71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63", + verifyResult: true, + validPubKey: true, + }, + { + secretKey: "0340034003400340034003400340034003400340034003400340034003400340", + publicKey: "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + auxRand: "0000000000000000000000000000000000000000000000000000000000000000", + message: "11", + signature: "08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF", + verifyResult: true, + validPubKey: true, + }, + { + secretKey: "0340034003400340034003400340034003400340034003400340034003400340", + publicKey: "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + auxRand: "0000000000000000000000000000000000000000000000000000000000000000", + message: "0102030405060708090A0B0C0D0E0F1011", + signature: "5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5", + verifyResult: true, + validPubKey: true, + }, + { + secretKey: "0340034003400340034003400340034003400340034003400340034003400340", + publicKey: "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", + auxRand: "0000000000000000000000000000000000000000000000000000000000000000", + message: "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", + signature: "403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367", + verifyResult: true, + validPubKey: true, + }, +} + +// decodeHex decodes the passed hex string and returns the resulting bytes. It +// panics if an error occurs. This is only used in the tests as a helper since +// the only way it can fail is if there is an error in the test source code. +func decodeHex(hexStr string) []byte { + b, err := hex.Dec(hexStr) + if err != nil { + panic( + "invalid hex string in test source: err " + err.Error() + + ", hex: " + hexStr, + ) + } + return b +} + +func TestSchnorrSign(t *testing.T) { + // t.Parallel() + for i, test := range bip340TestVectors { + if len(test.secretKey) == 0 { + continue + } + d := decodeHex(test.secretKey) + privKey, _ := btcec.SecKeyFromBytes(d) + var auxBytes [32]byte + aux := decodeHex(test.auxRand) + copy(auxBytes[:], aux) + msg := decodeHex(test.message) + var signOpts []SignOption + if !test.rfc6979 { + signOpts = []SignOption{CustomNonce(auxBytes)} + } + sig, err := Sign(privKey, msg, signOpts...) + if err != nil { + t.Fatalf("test #%v: sig generation failed: %v", i+1, err) + } + if strings.ToUpper(hex.Enc(sig.Serialize())) != test.signature { + t.Fatalf( + "test #%v: got signature %x : "+ + "want %s", i+1, sig.Serialize(), test.signature, + ) + } + pubKeyBytes := decodeHex(test.publicKey) + err = schnorrVerify(sig, msg, pubKeyBytes) + if err != nil { + t.Fail() + } + verify := err == nil + if test.verifyResult != verify { + t.Fatalf( + "test #%v: verification mismatch: "+ + "expected %v, got %v", i+1, test.verifyResult, verify, + ) + } + } +} + +func TestSchnorrVerify(t *testing.T) { + t.Parallel() + for i, test := range bip340TestVectors { + pubKeyBytes := decodeHex(test.publicKey) + _, err := ParsePubKey(pubKeyBytes) + switch { + case !test.validPubKey && err != nil: + if !errors.Is(err, test.expectErr) { + t.Fatalf( + "test #%v: pubkey validation should "+ + "have failed, expected %v, got %v", i, + test.expectErr, err, + ) + } + continue + case err != nil: + t.Fatalf("test #%v: unable to parse pubkey: %v", i, err) + } + msg := decodeHex(test.message) + sig, err := ParseSignature(decodeHex(test.signature)) + if err != nil { + t.Fatalf("unable to parse sig: %v", err) + } + err = schnorrVerify(sig, msg, pubKeyBytes) + if err != nil && test.verifyResult { + t.Fatalf( + "test #%v: verification shouldn't have failed: %v", i+1, + err, + ) + } + verify := err == nil + if test.verifyResult != verify { + t.Fatalf( + "test #%v: verificaiton mismatch: expected "+ + "%v, got %v", i, test.verifyResult, verify, + ) + } + if !test.verifyResult && test.expectErr != nil { + if !errors.Is(err, test.expectErr) { + t.Fatalf( + "test #%v: expect error %v : got %v", i, + test.expectErr, err, + ) + } + } + } +} + +// TestSchnorrSignNoMutate tests that generating a schnorr signature doesn't +// modify/mutate the underlying secret key. +func TestSchnorrSignNoMutate(t *testing.T) { + t.Parallel() + // Assert that given a random secret key and message, we can generate + // a signature from that w/o modifying the underlying secret key. + f := func(privBytes, msg [32]byte) bool { + privBytesCopy := privBytes + privKey, _ := btcec.SecKeyFromBytes(privBytesCopy[:]) + // Generate a signature for secret key with our message. + _, err := Sign(privKey, msg[:]) + if err != nil { + t.Logf("unable to gen sig: %v", err) + return false + } + // We should be able to re-derive the secret key from raw + // bytes and have that match up again. + privKeyCopy, _ := btcec.SecKeyFromBytes(privBytes[:]) + if *privKey != *privKeyCopy { + t.Logf( + "secret doesn't match: expected %v, got %v", + spew.Sdump(privKeyCopy), spew.Sdump(privKey), + ) + return false + } + return true + } + if err := quick.Check(f, nil); chk.T(err) { + t.Fatalf("secret key modified: %v", err) + } +} diff --git a/pkg/crypto/ec/sec2-v2.pdf b/pkg/crypto/ec/sec2-v2.pdf new file mode 100644 index 0000000..c5c9bda Binary files /dev/null and b/pkg/crypto/ec/sec2-v2.pdf differ diff --git a/pkg/crypto/ec/seckey.go b/pkg/crypto/ec/seckey.go new file mode 100644 index 0000000..2c47547 --- /dev/null +++ b/pkg/crypto/ec/seckey.go @@ -0,0 +1,48 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// SecretKey wraps an ecdsa.SecretKey as a convenience mainly for signing things with the secret key without having to +// directly import the ecdsa package. +type SecretKey = secp256k1.SecretKey + +// PrivateKey wraps an ecdsa.SecretKey as a convenience mainly for signing things with the secret key without having to +// directly import the ecdsa package. +// +// Deprecated: use SecretKey - secret = one person; private = two or more (you don't share secret keys!) +type PrivateKey = SecretKey + +// SecKeyFromBytes returns a secret and public key for `curve' based on the +// secret key passed as an argument as a byte slice. +func SecKeyFromBytes(pk []byte) (*SecretKey, *PublicKey) { + privKey := secp256k1.SecKeyFromBytes(pk) + return privKey, privKey.PubKey() +} + +var PrivKeyFromBytes = SecKeyFromBytes + +// NewSecretKey is a wrapper for ecdsa.GenerateKey that returns a SecretKey instead of the normal ecdsa.PrivateKey. +func NewSecretKey() (*SecretKey, error) { return secp256k1.GenerateSecretKey() } + +// NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a SecretKey instead of the normal ecdsa.PrivateKey. +// +// Deprecated: use SecretKey - secret = one person; private = two or more (you don't share secret keys!) +var NewPrivateKey = NewSecretKey + +// SecKeyFromScalar instantiates a new secret key from a scalar encoded as a +// big integer. +func SecKeyFromScalar(key *ModNScalar) *SecretKey { + return &SecretKey{Key: *key} +} + +var PrivKeyFromScalar = SecKeyFromScalar + +// SecKeyBytesLen defines the length in bytes of a serialized secret key. +const SecKeyBytesLen = 32 +const PrivKeyBytesLen = SecKeyBytesLen diff --git a/pkg/crypto/ec/secp256k1/LICENSE b/pkg/crypto/ec/secp256k1/LICENSE new file mode 100644 index 0000000..ff2562c --- /dev/null +++ b/pkg/crypto/ec/secp256k1/LICENSE @@ -0,0 +1,23 @@ +Due to the presence of substantial material derived from btcec this license is +required. + +However, where it differs, the changed parts are CC0 as with the rest of the +content of this repository. + +ISC License + +Copyright (c) 2013-2017 The btcsuite developers +Copyright (c) 2015-2020 The Decred developers +Copyright (c) 2017 The Lightning Network Developers + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/pkg/crypto/ec/secp256k1/README.md b/pkg/crypto/ec/secp256k1/README.md new file mode 100644 index 0000000..6c24ac6 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/README.md @@ -0,0 +1,54 @@ +# secp256k1 + +> Due to the terrible state of the btcec library, and the ethical character of +> dcred, the two main secp256k1 EC libraries in the Go language, it has become +> necessary to refactor and clean up the mess of btcec's module versioning. +> +> In addition, the code has been updated to use several new features of Go that +> were added to the language since these libraries were first created, notably the +> precomps are here directly generated as binary data instead of nasty base64 +> source code. + +Package secp256k1 implements optimized secp256k1 elliptic curve operations. + +This package provides an optimized pure Go implementation of elliptic curve +cryptography operations over the secp256k1 curve as well as data structures and +functions for working with public and secret secp256k1 keys. See +https://www.secg.org/sec2-v2.pdf for details on the standard. + +In addition, sub packages are provided to produce, verify, parse, and serialize +ECDSA signatures and EC-Schnorr-DCRv0 (a custom Schnorr-based signature scheme +specific to Decred) signatures. See the README.md files in the relevant sub +packages for more details about those aspects. + +An overview of the features provided by this package are as follows: + +- Secret key generation, serialization, and parsing +- Public key generation, serialization and parsing per ANSI X9.62-1998 + - 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 +- Elliptic curve operations in Jacobian projective coordinates + - Point addition + - Point doubling + - Scalar multiplication with an arbitrary point + - Scalar multiplication with the base point (group generator) +- Point decompression from a given x coordinate +- Nonce generation via RFC6979 with support for extra data and version + information that can be used to prevent nonce reuse between signing 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. + +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 +use optimized secp256k1 elliptic curve cryptography. + +Finally, a comprehensive suite of tests is provided to provide a high level of +quality assurance. diff --git a/pkg/crypto/ec/secp256k1/bench_test.go b/pkg/crypto/ec/secp256k1/bench_test.go new file mode 100644 index 0000000..6862204 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/bench_test.go @@ -0,0 +1,177 @@ +// Copyright 2013-2016 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "testing" +) + +// BenchmarkAddNonConst benchmarks the secp256k1 curve AddNonConst function with +// Z values of 1 so that the associated optimizations are used. +func BenchmarkAddNonConst(b *testing.B) { + p1 := jacobianPointFromHex( + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + ) + p2 := jacobianPointFromHex( + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + ) + b.ReportAllocs() + b.ResetTimer() + var result JacobianPoint + for i := 0; i < b.N; i++ { + AddNonConst(&p1, &p2, &result) + } +} + +// BenchmarkAddNonConstNotZOne benchmarks the secp256k1 curve AddNonConst +// 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) + b.ReportAllocs() + b.ResetTimer() + var result JacobianPoint + for i := 0; i < b.N; i++ { + AddNonConst(&p1, &p2, &result) + } +} + +// BenchmarkScalarBaseMultNonConst benchmarks multiplying a scalar by the base +// point of the curve. +func BenchmarkScalarBaseMultNonConst(b *testing.B) { + k := hexToModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + b.ReportAllocs() + b.ResetTimer() + var result JacobianPoint + for i := 0; i < b.N; i++ { + ScalarBaseMultNonConst(k, &result) + } +} + +// BenchmarkSplitK benchmarks decomposing scalars into a balanced length-two +// representation. +func BenchmarkSplitK(b *testing.B) { + // Values computed from the group half order and lambda such that they + // exercise the decomposition edge cases and maximize the bit lengths of the + // produced scalars. + h := "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0" + negOne := new(ModNScalar).NegateVal(oneModN) + halfOrder := hexToModNScalar(h) + halfOrderMOne := new(ModNScalar).Add2(halfOrder, negOne) + halfOrderPOne := new(ModNScalar).Add2(halfOrder, oneModN) + lambdaMOne := new(ModNScalar).Add2(endoLambda, negOne) + lambdaPOne := new(ModNScalar).Add2(endoLambda, oneModN) + negLambda := new(ModNScalar).NegateVal(endoLambda) + halfOrderMOneMLambda := new(ModNScalar).Add2(halfOrderMOne, negLambda) + halfOrderMLambda := new(ModNScalar).Add2(halfOrder, negLambda) + halfOrderPOneMLambda := new(ModNScalar).Add2(halfOrderPOne, negLambda) + lambdaPHalfOrder := new(ModNScalar).Add2(endoLambda, halfOrder) + lambdaPOnePHalfOrder := new(ModNScalar).Add2(lambdaPOne, halfOrder) + scalars := []*ModNScalar{ + new(ModNScalar), // zero + oneModN, // one + negOne, // group order - 1 (aka -1 mod N) + halfOrderMOneMLambda, // group half order - 1 - lambda + halfOrderMLambda, // group half order - lambda + halfOrderPOneMLambda, // group half order + 1 - lambda + halfOrderMOne, // group half order - 1 + halfOrder, // group half order + halfOrderPOne, // group half order + 1 + lambdaMOne, // lambda - 1 + endoLambda, // lambda + lambdaPOne, // lambda + 1 + lambdaPHalfOrder, // lambda + group half order + lambdaPOnePHalfOrder, // lambda + 1 + group half order + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(scalars) { + for j := 0; j < len(scalars); j++ { + _, _ = splitK(scalars[j]) + } + } +} + +// BenchmarkScalarMultNonConst benchmarks multiplying a scalar by an arbitrary +// point on the curve. +func BenchmarkScalarMultNonConst(b *testing.B) { + k := hexToModNScalar("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + point := jacobianPointFromHex( + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + ) + b.ReportAllocs() + b.ResetTimer() + var result JacobianPoint + for i := 0; i < b.N; i++ { + ScalarMultNonConst(k, &point, &result) + } +} + +// BenchmarkNAF benchmarks conversion of a positive integer into its +// non-adjacent form representation. +func BenchmarkNAF(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + kBytes := k.Bytes() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + naf(kBytes) + } +} + +// BenchmarkPubKeyDecompress benchmarks how long it takes to decompress the y +// coordinate from a given public key x coordinate. +func BenchmarkPubKeyDecompress(b *testing.B) { + // Randomly generated keypair. + // Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d + pubKeyX := new(FieldVal).SetHex("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab") + b.ReportAllocs() + b.ResetTimer() + var y FieldVal + for i := 0; i < b.N; i++ { + _ = DecompressY(pubKeyX, false, &y) + } +} + +// BenchmarkParsePubKeyCompressed benchmarks how long it takes to parse a +// compressed public key with an even y coordinate. +func BenchmarkParsePubKeyCompressed(b *testing.B) { + format := "02" + x := "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d" + pubKeyBytes := hexToBytes(format + x) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + ParsePubKey(pubKeyBytes) + } +} + +// BenchmarkParsePubKeyUncompressed benchmarks how long it takes to parse an +// uncompressed public key. +func BenchmarkParsePubKeyUncompressed(b *testing.B) { + format := "04" + x := "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + y := "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3" + pubKeyBytes := hexToBytes(format + x + y) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + ParsePubKey(pubKeyBytes) + } +} diff --git a/pkg/crypto/ec/secp256k1/curve.go b/pkg/crypto/ec/secp256k1/curve.go new file mode 100644 index 0000000..1df990e --- /dev/null +++ b/pkg/crypto/ec/secp256k1/curve.go @@ -0,0 +1,1222 @@ +// Copyright (c) 2015-2022 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. + +package secp256k1 + +import ( + "math/bits" + + "next.orly.dev/pkg/encoders/hex" +) + +// References: +// [SECG]: Recommended Elliptic Curve Domain Parameters +// https://www.secg.org/sec2-v2.pdf +// +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) +// +// [BRID]: On Binary Representations of Integers with Digits -1, 0, 1 +// (Prodinger, Helmut) +// +// [STWS]: Secure-TWS: Authenticating Node to Multi-user Communication in +// Shared Sensor Networks (Oliveira, Leonardo B. et al) + +// All group operations are performed using Jacobian coordinates. For a given +// (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 { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var f FieldVal + if overflow := f.SetByteSlice(b); overflow { + panic("hex in source file overflows mod P: " + 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 { + var isNegative bool + if len(s) > 0 && s[0] == '-' { + isNegative = true + s = s[1:] + } + if len(s)%2 != 0 { + s = "0" + s + } + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var scalar ModNScalar + if overflow := scalar.SetByteSlice(b); overflow { + panic("hex in source file overflows mod N scalar: " + s) + } + if isNegative { + scalar.Negate() + } + return &scalar +} + +var ( + // The following constants are used to accelerate scalar point + // multiplication through the use of the endomorphism: + // + // φ(Q) ⟼ λ*Q = (β*Q.x mod p, Q.y) + // + // See the code in the deriveEndomorphismParams function in genprecomps.go + // for details on their derivation. + // + // 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") + // 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") +) + +// JacobianPoint is an element of the group formed by the secp256k1 curve in +// Jacobian projective coordinates and thus represents a point on the curve. +type JacobianPoint struct { + // The X coordinate in Jacobian projective coordinates. The affine point is + // X/z^2. + X FieldVal + // The Y coordinate in Jacobian projective coordinates. The affine point is + // Y/z^3. + Y FieldVal + // The Z coordinate in Jacobian projective coordinates. + Z FieldVal +} + +// MakeJacobianPoint returns a Jacobian point with the provided X, Y, and Z +// coordinates. +func MakeJacobianPoint(x, y, z *FieldVal) JacobianPoint { + var p JacobianPoint + p.X.Set(x) + p.Y.Set(y) + p.Z.Set(z) + return p +} + +// Set sets the Jacobian point to the provided point. +func (p *JacobianPoint) Set(other *JacobianPoint) { + p.X.Set(&other.X) + p.Y.Set(&other.Y) + p.Z.Set(&other.Z) +} + +// ToAffine reduces the Z value of the existing point to 1 effectively +// making it an affine coordinate in constant time. The point will be +// normalized. +func (p *JacobianPoint) ToAffine() { + // Inversions are expensive and both point addition and point doubling + // are faster when working with points that have a z value of one. So, + // if the point needs to be converted to affine, go ahead and normalize + // the point itself at the same time as the calculation is the same. + var zInv, tempZ FieldVal + zInv.Set(&p.Z).Inverse() // zInv = Z^-1 + tempZ.SquareVal(&zInv) // tempZ = Z^-2 + p.X.Mul(&tempZ) // X = X/Z^2 (mag: 1) + p.Y.Mul(tempZ.Mul(&zInv)) // Y = Y/Z^3 (mag: 1) + p.Z.SetInt(1) // Z = 1 (mag: 1) + // Normalize the x and y values. + p.X.Normalize() + p.Y.Normalize() +} + +// addZ1AndZ2EqualsOne adds two Jacobian points that are already known to have +// z values of 1 and stores the result in the provided result param. That is to +// say result = p1 + p2. It performs faster addition than the generic add +// routine since less arithmetic is needed due to the ability to avoid the z +// value multiplications. +// +// NOTE: The points must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func addZ1AndZ2EqualsOne(p1, p2, result *JacobianPoint) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl + // + // In particular it performs the calculations using the following: + // H = X2-X1, HH = H^2, I = 4*HH, J = H*I, r = 2*(Y2-Y1), V = X1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = 2*H + // + // This results in a cost of 4 field multiplications, 2 field squarings, + // 6 field additions, and 5 integer multiplications. + x1, y1 := &p1.X, &p1.Y + x2, y2 := &p2.X, &p2.Y + x3, y3, z3 := &result.X, &result.Y, &result.Z + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. + if x1.Equals(x2) { + if y1.Equals(y2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + DoubleNonConst(p1, result) + return + } + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, i, j, r, v FieldVal + var negJ, neg2V, negX3 FieldVal + h.Set(x1).Negate(1).Add(x2) // H = X2-X1 (mag: 3) + i.SquareVal(&h).MulInt(4) // I = 4*H^2 (mag: 4) + j.Mul2(&h, &i) // J = H*I (mag: 1) + r.Set(y1).Negate(1).Add(y2).MulInt(2) // r = 2*(Y2-Y1) (mag: 6) + v.Mul2(x1, &i) // V = X1*I (mag: 1) + negJ.Set(&j).Negate(1) // negJ = -J (mag: 2) + neg2V.Set(&v).MulInt(2).Negate(2) // neg2V = -(2*V) (mag: 3) + x3.Set(&r).Square().Add(&negJ).Add(&neg2V) // X3 = r^2-J-2*V (mag: 6) + negX3.Set(x3).Negate(6) // negX3 = -X3 (mag: 7) + j.Mul(y1).MulInt(2).Negate(2) // J = -(2*Y1*J) (mag: 3) + y3.Set(&v).Add(&negX3).Mul(&r).Add(&j) // Y3 = r*(V-X3)-2*Y1*J (mag: 4) + z3.Set(&h).MulInt(2) // Z3 = 2*H (mag: 6) + // Normalize the resulting field values as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// addZ1EqualsZ2 adds two Jacobian points that are already known to have the +// same z value and stores the result in the provided result param. That is to +// say result = p1 + p2. It performs faster addition than the generic add +// routine since less arithmetic is needed due to the known equivalence. +// +// NOTE: The points must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func addZ1EqualsZ2(p1, p2, result *JacobianPoint) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using a slightly modified version + // of the method shown at: + // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-zadd-2007-m + // + // In particular it performs the calculations using the following: + // A = X2-X1, B = A^2, C=Y2-Y1, D = C^2, E = X1*B, ToSliceOfBytes = X2*B + // X3 = D-E-ToSliceOfBytes, Y3 = C*(E-X3)-Y1*(ToSliceOfBytes-E), Z3 = Z1*A + // + // This results in a cost of 5 field multiplications, 2 field squarings, + // 9 field additions, and 0 integer multiplications. + x1, y1, z1 := &p1.X, &p1.Y, &p1.Z + x2, y2 := &p2.X, &p2.Y + x3, y3, z3 := &result.X, &result.Y, &result.Z + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. + if x1.Equals(x2) { + if y1.Equals(y2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + DoubleNonConst(p1, result) + return + } + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var a, b, c, d, err, f FieldVal + var negX1, negY1, negE, negX3 FieldVal + negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2) + negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2) + a.Set(&negX1).Add(x2) // A = X2-X1 (mag: 3) + b.SquareVal(&a) // B = A^2 (mag: 1) + c.Set(&negY1).Add(y2) // C = Y2-Y1 (mag: 3) + d.SquareVal(&c) // D = C^2 (mag: 1) + err.Mul2(x1, &b) // E = X1*B (mag: 1) + negE.Set(&err).Negate(1) // negE = -E (mag: 2) + f.Mul2(x2, &b) // ToSliceOfBytes = X2*B (mag: 1) + x3.Add2(&err, &f).Negate(2).Add(&d) // X3 = D-E-ToSliceOfBytes (mag: 4) + negX3.Set(x3).Negate(4) // negX3 = -X3 (mag: 5) + y3.Set(y1).Mul(f.Add(&negE)).Negate(1) // Y3 = -(Y1*(ToSliceOfBytes-E)) (mag: 2) + y3.Add(err.Add(&negX3).Mul(&c)) // Y3 = C*(E-X3)+Y3 (mag: 3) + z3.Mul2(z1, &a) // Z3 = Z1*A (mag: 1) + // Normalize the resulting field values as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// addZ2EqualsOne adds two Jacobian points when the second point is already +// known to have a z value of 1 (and the z value for the first point is not 1) +// and stores the result in the provided result param. That is to say result = +// p1 + p2. It performs faster addition than the generic add routine since +// less arithmetic is needed due to the ability to avoid multiplications by the +// second point's z value. +// +// NOTE: The points must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func addZ2EqualsOne(p1, p2, result *JacobianPoint) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + // + // In particular it performs the calculations using the following: + // Z1Z1 = Z1^2, U2 = X2*Z1Z1, S2 = Y2*Z1*Z1Z1, H = U2-X1, HH = H^2, + // I = 4*HH, J = H*I, r = 2*(S2-Y1), V = X1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = (Z1+H)^2-Z1Z1-HH + // + // This results in a cost of 7 field multiplications, 4 field squarings, + // 9 field additions, and 4 integer multiplications. + x1, y1, z1 := &p1.X, &p1.Y, &p1.Z + x2, y2 := &p2.X, &p2.Y + x3, y3, z3 := &result.X, &result.Y, &result.Z + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. Since + // any number of Jacobian coordinates can represent the same affine + // point, the x and y values need to be converted to like terms. Due to + // the assumption made for this function that the second point has a z + // value of 1 (z2=1), the first point is already "converted". + var z1z1, u2, s2 FieldVal + z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1) + u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1) + s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1) + if x1.Equals(&u2) { + if y1.Equals(&s2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + DoubleNonConst(p1, result) + return + } + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, hh, i, j, r, rr, v FieldVal + var negX1, negY1, negX3 FieldVal + negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2) + h.Add2(&u2, &negX1) // H = U2-X1 (mag: 3) + hh.SquareVal(&h) // HH = H^2 (mag: 1) + i.Set(&hh).MulInt(4) // I = 4 * HH (mag: 4) + j.Mul2(&h, &i) // J = H*I (mag: 1) + negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2) + r.Set(&s2).Add(&negY1).MulInt(2) // r = 2*(S2-Y1) (mag: 6) + rr.SquareVal(&r) // rr = r^2 (mag: 1) + v.Mul2(x1, &i) // V = X1*I (mag: 1) + x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4) + x3.Add(&rr) // X3 = r^2+X3 (mag: 5) + negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6) + y3.Set(y1).Mul(&j).MulInt(2).Negate(2) // Y3 = -(2*Y1*J) (mag: 3) + y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4) + z3.Add2(z1, &h).Square() // Z3 = (Z1+H)^2 (mag: 1) + z3.Add(z1z1.Add(&hh).Negate(2)) // Z3 = Z3-(Z1Z1+HH) (mag: 4) + // Normalize the resulting field values as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// addGeneric adds two Jacobian points without any assumptions about the z +// values of the two points and stores the result in the provided result param. +// That is to say result = p1 + p2. It is the slowest of the add routines due +// to requiring the most arithmetic. +// +// NOTE: The points must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func addGeneric(p1, p2, result *JacobianPoint) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + // + // In particular it performs the calculations using the following: + // Z1Z1 = Z1^2, Z2Z2 = Z2^2, U1 = X1*Z2Z2, U2 = X2*Z1Z1, S1 = Y1*Z2*Z2Z2 + // S2 = Y2*Z1*Z1Z1, H = U2-U1, I = (2*H)^2, J = H*I, r = 2*(S2-S1) + // V = U1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*S1*J, Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2)*H + // + // This results in a cost of 11 field multiplications, 5 field squarings, + // 9 field additions, and 4 integer multiplications. + x1, y1, z1 := &p1.X, &p1.Y, &p1.Z + x2, y2, z2 := &p2.X, &p2.Y, &p2.Z + x3, y3, z3 := &result.X, &result.Y, &result.Z + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity. Since any number of Jacobian coordinates can represent the + // same affine point, the x and y values need to be converted to like + // terms. + var z1z1, z2z2, u1, u2, s1, s2 FieldVal + z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1) + z2z2.SquareVal(z2) // Z2Z2 = Z2^2 (mag: 1) + u1.Set(x1).Mul(&z2z2).Normalize() // U1 = X1*Z2Z2 (mag: 1) + u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1) + s1.Set(y1).Mul(&z2z2).Mul(z2).Normalize() // S1 = Y1*Z2*Z2Z2 (mag: 1) + s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1) + if u1.Equals(&u2) { + if s1.Equals(&s2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + DoubleNonConst(p1, result) + return + } + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, i, j, r, rr, v FieldVal + var negU1, negS1, negX3 FieldVal + negU1.Set(&u1).Negate(1) // negU1 = -U1 (mag: 2) + h.Add2(&u2, &negU1) // H = U2-U1 (mag: 3) + i.Set(&h).MulInt(2).Square() // I = (2*H)^2 (mag: 1) + j.Mul2(&h, &i) // J = H*I (mag: 1) + negS1.Set(&s1).Negate(1) // negS1 = -S1 (mag: 2) + r.Set(&s2).Add(&negS1).MulInt(2) // r = 2*(S2-S1) (mag: 6) + rr.SquareVal(&r) // rr = r^2 (mag: 1) + v.Mul2(&u1, &i) // V = U1*I (mag: 1) + x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4) + x3.Add(&rr) // X3 = r^2+X3 (mag: 5) + negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6) + y3.Mul2(&s1, &j).MulInt(2).Negate(2) // Y3 = -(2*S1*J) (mag: 3) + y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4) + z3.Add2(z1, z2).Square() // Z3 = (Z1+Z2)^2 (mag: 1) + z3.Add(z1z1.Add(&z2z2).Negate(2)) // Z3 = Z3-(Z1Z1+Z2Z2) (mag: 4) + z3.Mul(&h) // Z3 = Z3*H (mag: 1) + // Normalize the resulting field values as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// AddNonConst adds the passed Jacobian points together and stores the result in +// the provided result param in *non-constant* time. +// +// NOTE: The points must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func AddNonConst(p1, p2, result *JacobianPoint) { + // The point at infinity is the identity according to the group law for + // elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P. + if (p1.X.IsZero() && p1.Y.IsZero()) || p1.Z.IsZero() { + result.Set(p2) + return + } + if (p2.X.IsZero() && p2.Y.IsZero()) || p2.Z.IsZero() { + result.Set(p1) + return + } + // Faster point addition can be achieved when certain assumptions are + // met. For example, when both points have the same z value, arithmetic + // on the z values can be avoided. This section thus checks for these + // conditions and calls an appropriate add function which is accelerated + // by using those assumptions. + isZ1One := p1.Z.IsOne() + isZ2One := p2.Z.IsOne() + switch { + case isZ1One && isZ2One: + addZ1AndZ2EqualsOne(p1, p2, result) + return + case p1.Z.Equals(&p2.Z): + addZ1EqualsZ2(p1, p2, result) + return + case isZ2One: + addZ2EqualsOne(p1, p2, result) + return + } + // None of the above assumptions are true, so fall back to generic + // point addition. + addGeneric(p1, p2, result) +} + +// doubleZ1EqualsOne performs point doubling on the passed Jacobian point when +// the point is already known to have a z value of 1 and stores the result in +// the provided result param. That is to say result = 2*p. It performs faster +// point doubling than the generic routine since less arithmetic is needed due +// to the ability to avoid multiplication by the z value. +// +// NOTE: The resulting point will be normalized. +func doubleZ1EqualsOne(p, result *JacobianPoint) { + // This function uses the assumptions that z1 is 1, thus the point + // doubling formulas reduce to: + // + // X3 = (3*X1^2)^2 - 8*X1*Y1^2 + // Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4 + // Z3 = 2*Y1 + // + // To compute the above efficiently, this implementation splits the + // equation into intermediate elements which are used to minimize the + // number of field multiplications in favor of field squarings which + // are roughly 35% faster than field multiplications with the current + // implementation at the time this was written. + // + // This uses a slightly modified version of the method shown at: + // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl + // + // In particular it performs the calculations using the following: + // A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C) + // E = 3*A, ToSliceOfBytes = E^2, X3 = ToSliceOfBytes-2*D, Y3 = E*(D-X3)-8*C + // Z3 = 2*Y1 + // + // This results in a cost of 1 field multiplication, 5 field squarings, + // 6 field additions, and 5 integer multiplications. + x1, y1 := &p.X, &p.Y + x3, y3, z3 := &result.X, &result.Y, &result.Z + var a, b, c, d, err, f FieldVal + z3.Set(y1).MulInt(2) // Z3 = 2*Y1 (mag: 2) + a.SquareVal(x1) // A = X1^2 (mag: 1) + b.SquareVal(y1) // B = Y1^2 (mag: 1) + c.SquareVal(&b) // C = B^2 (mag: 1) + b.Add(x1).Square() // B = (X1+B)^2 (mag: 1) + d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3) + d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8) + err.Set(&a).MulInt(3) // E = 3*A (mag: 3) + f.SquareVal(&err) // ToSliceOfBytes = E^2 (mag: 1) + x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17) + x3.Add(&f) // X3 = ToSliceOfBytes+X3 (mag: 18) + f.Set(x3).Negate(18).Add(&d).Normalize() // ToSliceOfBytes = D-X3 (mag: 1) + y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9) + y3.Add(f.Mul(&err)) // Y3 = E*ToSliceOfBytes+Y3 (mag: 10) + // Normalize the resulting field values as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// doubleGeneric performs point doubling on the passed Jacobian point without +// any assumptions about the z value and stores the result in the provided +// result param. That is to say result = 2*p. It is the slowest of the point +// doubling routines due to requiring the most arithmetic. +// +// NOTE: The resulting point will be normalized. +func doubleGeneric(p, result *JacobianPoint) { + // Point doubling formula for Jacobian coordinates for the secp256k1 + // curve: + // + // X3 = (3*X1^2)^2 - 8*X1*Y1^2 + // Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4 + // Z3 = 2*Y1*Z1 + // + // To compute the above efficiently, this implementation splits the + // equation into intermediate elements which are used to minimize the + // number of field multiplications in favor of field squarings which + // are roughly 35% faster than field multiplications with the current + // implementation at the time this was written. + // + // This uses a slightly modified version of the method shown at: + // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // In particular it performs the calculations using the following: + // A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C) + // E = 3*A, ToSliceOfBytes = E^2, X3 = ToSliceOfBytes-2*D, Y3 = E*(D-X3)-8*C + // Z3 = 2*Y1*Z1 + // + // This results in a cost of 1 field multiplication, 5 field squarings, + // 6 field additions, and 5 integer multiplications. + x1, y1, z1 := &p.X, &p.Y, &p.Z + x3, y3, z3 := &result.X, &result.Y, &result.Z + var a, b, c, d, err, f FieldVal + z3.Mul2(y1, z1).MulInt(2) // Z3 = 2*Y1*Z1 (mag: 2) + a.SquareVal(x1) // A = X1^2 (mag: 1) + b.SquareVal(y1) // B = Y1^2 (mag: 1) + c.SquareVal(&b) // C = B^2 (mag: 1) + b.Add(x1).Square() // B = (X1+B)^2 (mag: 1) + d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3) + d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8) + err.Set(&a).MulInt(3) // E = 3*A (mag: 3) + f.SquareVal(&err) // ToSliceOfBytes = E^2 (mag: 1) + x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17) + x3.Add(&f) // X3 = ToSliceOfBytes+X3 (mag: 18) + f.Set(x3).Negate(18).Add(&d).Normalize() // ToSliceOfBytes = D-X3 (mag: 1) + y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9) + y3.Add(f.Mul(&err)) // Y3 = E*ToSliceOfBytes+Y3 (mag: 10) + // Normalize the resulting field values as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// DoubleNonConst doubles the passed Jacobian point and stores the result in the +// provided result parameter in *non-constant* time. +// +// NOTE: The point must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func DoubleNonConst(p, result *JacobianPoint) { + // Doubling the point at infinity is still infinity. + if p.Y.IsZero() || p.Z.IsZero() { + result.X.SetInt(0) + result.Y.SetInt(0) + result.Z.SetInt(0) + return + } + // Slightly faster point doubling can be achieved when the z value is 1 + // by avoiding the multiplication on the z value. This section calls + // a point doubling function which is accelerated by using that + // assumption when possible. + if p.Z.IsOne() { + doubleZ1EqualsOne(p, result) + return + } + // Fall back to generic point doubling which works with arbitrary z + // values. + doubleGeneric(p, result) +} + +// mulAdd64 multiplies the two passed base 2^64 digits together, adds the given +// value to the result, and returns the 128-bit result via a (hi, lo) tuple +// where the upper half of the bits are returned in hi and the lower half in lo. +func mulAdd64(digit1, digit2, m uint64) (hi, lo uint64) { + // Note the carry on the final add is safe to discard because the maximum + // possible value is: + // (2^64 - 1)(2^64 - 1) + (2^64 - 1) = 2^128 - 2^64 + // and: + // 2^128 - 2^64 < 2^128. + var c uint64 + hi, lo = bits.Mul64(digit1, digit2) + lo, c = bits.Add64(lo, m, 0) + hi, _ = bits.Add64(hi, 0, c) + return hi, lo +} + +// mulAdd64Carry multiplies the two passed base 2^64 digits together, adds both +// the given value and carry to the result, and returns the 128-bit result via a +// (hi, lo) tuple where the upper half of the bits are returned in hi and the +// lower half in lo. +func mulAdd64Carry(digit1, digit2, m, c uint64) (hi, lo uint64) { + // Note the carry on the high order add is safe to discard because the + // maximum possible value is: + // (2^64 - 1)(2^64 - 1) + 2*(2^64 - 1) = 2^128 - 1 + // and: + // 2^128 - 1 < 2^128. + var c2 uint64 + hi, lo = mulAdd64(digit1, digit2, m) + lo, c2 = bits.Add64(lo, c, 0) + hi, _ = bits.Add64(hi, 0, c2) + return hi, lo +} + +// mul512Rsh320Round computes the full 512-bit product of the two given scalars, +// right shifts the result by 320 bits, rounds to the nearest integer, and +// returns the result in constant time. +// +// Note that despite the inputs and output being mod n scalars, the 512-bit +// product is NOT reduced mod N prior to the right shift. This is intentional +// because it is used for replacing division with multiplication and thus the +// intermediate results must be done via a field extension to a larger field. +func mul512Rsh320Round(n1, n2 *ModNScalar) ModNScalar { + // Convert n1 and n2 to base 2^64 digits. + n1Digit0 := uint64(n1.n[0]) | uint64(n1.n[1])<<32 + n1Digit1 := uint64(n1.n[2]) | uint64(n1.n[3])<<32 + n1Digit2 := uint64(n1.n[4]) | uint64(n1.n[5])<<32 + n1Digit3 := uint64(n1.n[6]) | uint64(n1.n[7])<<32 + n2Digit0 := uint64(n2.n[0]) | uint64(n2.n[1])<<32 + n2Digit1 := uint64(n2.n[2]) | uint64(n2.n[3])<<32 + n2Digit2 := uint64(n2.n[4]) | uint64(n2.n[5])<<32 + n2Digit3 := uint64(n2.n[6]) | uint64(n2.n[7])<<32 + // Compute the full 512-bit product n1*n2. + var r0, r1, r2, r3, r4, r5, r6, r7, c uint64 + // Terms resulting from the product of the first digit of the second number + // by all digits of the first number. + // + // Note that r0 is ignored because it is not needed to compute the higher + // terms and it is shifted out below anyway. + c, _ = bits.Mul64(n2Digit0, n1Digit0) + c, r1 = mulAdd64(n2Digit0, n1Digit1, c) + c, r2 = mulAdd64(n2Digit0, n1Digit2, c) + r4, r3 = mulAdd64(n2Digit0, n1Digit3, c) + // Terms resulting from the product of the second digit of the second number + // by all digits of the first number. + // + // Note that r1 is ignored because it is no longer needed to compute the + // higher terms and it is shifted out below anyway. + c, _ = mulAdd64(n2Digit1, n1Digit0, r1) + c, r2 = mulAdd64Carry(n2Digit1, n1Digit1, r2, c) + c, r3 = mulAdd64Carry(n2Digit1, n1Digit2, r3, c) + r5, r4 = mulAdd64Carry(n2Digit1, n1Digit3, r4, c) + // Terms resulting from the product of the third digit of the second number + // by all digits of the first number. + // + // Note that r2 is ignored because it is no longer needed to compute the + // higher terms and it is shifted out below anyway. + c, _ = mulAdd64(n2Digit2, n1Digit0, r2) + c, r3 = mulAdd64Carry(n2Digit2, n1Digit1, r3, c) + c, r4 = mulAdd64Carry(n2Digit2, n1Digit2, r4, c) + r6, r5 = mulAdd64Carry(n2Digit2, n1Digit3, r5, c) + // Terms resulting from the product of the fourth digit of the second number + // by all digits of the first number. + // + // Note that r3 is ignored because it is no longer needed to compute the + // higher terms and it is shifted out below anyway. + c, _ = mulAdd64(n2Digit3, n1Digit0, r3) + c, r4 = mulAdd64Carry(n2Digit3, n1Digit1, r4, c) + c, r5 = mulAdd64Carry(n2Digit3, n1Digit2, r5, c) + r7, r6 = mulAdd64Carry(n2Digit3, n1Digit3, r6, c) + // At this point the upper 256 bits of the full 512-bit product n1*n2 are in + // r4..r7 (recall the low order results were discarded as noted above). + // + // Right shift the result 320 bits. Note that the MSB of r4 determines + // whether or not to round because it is the final bit that is shifted out. + // + // Also, notice that r3..r7 would also ordinarily be set to 0 as well for + // the full shift, but that is skipped since they are no longer used as + // their values are known to be zero. + roundBit := r4 >> 63 + r2, r1, r0 = r7, r6, r5 + // Conditionally add 1 depending on the round bit in constant time. + r0, c = bits.Add64(r0, roundBit, 0) + r1, c = bits.Add64(r1, 0, c) + r2, r3 = bits.Add64(r2, 0, c) + // Finally, convert the result to a mod n scalar. + // + // No modular reduction is needed because the result is guaranteed to be + // less than the group order given the group order is > 2^255 and the + // maximum possible value of the result is 2^192. + var result ModNScalar + result.n[0] = uint32(r0) + result.n[1] = uint32(r0 >> 32) + result.n[2] = uint32(r1) + result.n[3] = uint32(r1 >> 32) + result.n[4] = uint32(r2) + result.n[5] = uint32(r2 >> 32) + result.n[6] = uint32(r3) + result.n[7] = uint32(r3 >> 32) + return result +} + +// splitK returns two scalars (k1 and k2) that are a balanced length-two +// representation of the provided scalar such that k ≡ k1 + k2*λ (mod N), where +// N is the secp256k1 group order. +func splitK(k *ModNScalar) (ModNScalar, ModNScalar) { + // The ultimate goal is to decompose k into two scalars that are around + // half the bit length of k such that the following equation is satisfied: + // + // k1 + k2*λ ≡ k (mod n) + // + // The strategy used here is based on algorithm 3.74 from [GECC] with a few + // modifications to make use of the more efficient mod n scalar type, avoid + // some costly long divisions, and minimize the number of calculations. + // + // Start by defining a function that takes a vector v = ∈ ℤ⨯ℤ: + // + // f(v) = a + bλ (mod n) + // + // Then, find two vectors, v1 = , and v2 = in ℤ⨯ℤ such that: + // 1) v1 and v2 are linearly independent + // 2) f(v1) = f(v2) = 0 + // 3) v1 and v2 have small Euclidean norm + // + // The vectors that satisfy these properties are found via the Euclidean + // algorithm and are precomputed since both n and λ are fixed values for the + // secp256k1 curve. See genprecomps.go for derivation details. + // + // Next, consider k as a vector in ℚ⨯ℚ and by linear algebra write: + // + // = g1*v1 + g2*v2, where g1, g2 ∈ ℚ + // + // Note that, per above, the components of vector v1 are a1 and b1 while the + // components of vector v2 are a2 and b2. Given the vectors v1 and v2 were + // generated such that a1*b2 - a2*b1 = n, solving the equation for g1 and g2 + // yields: + // + // g1 = b2*k / n + // g2 = -b1*k / n + // + // Observe: + // = g1*v1 + g2*v2 + // = (b2*k/n)* + (-b1*k/n)* | substitute + // = + <-a2*b1*k/n, -b2*b1*k/n> | scalar mul + // = | vector add + // = <[a1*b2*k - a2*b1*k]/n, 0> | simplify + // = | factor out k + // = | substitute + // = | simplify + // + // Now, consider an integer-valued vector v: + // + // v = c1*v1 + c2*v2, where c1, c2 ∈ ℤ (mod n) + // + // Since vectors v1 and v2 are linearly independent and were generated such + // that f(v1) = f(v2) = 0, all possible scalars c1 and c2 also produce a + // vector v such that f(v) = 0. + // + // In other words, c1 and c2 can be any integers and the resulting + // decomposition will still satisfy the required equation. However, since + // the goal is to produce a balanced decomposition that provides a + // performance advantage by minimizing max(k1, k2), c1 and c2 need to be + // integers close to g1 and g2, respectively, so the resulting vector v is + // an integer-valued vector that is close to . + // + // Finally, consider the vector u: + // + // u = - v + // + // It follows that f(u) = k and thus the two components of vector u satisfy + // the required equation: + // + // k1 + k2*λ ≡ k (mod n) + // + // Choosing c1 and c2: + // ------------------- + // + // As mentioned above, c1 and c2 need to be integers close to g1 and g2, + // respectively. The algorithm in [GECC] chooses the following values: + // + // c1 = round(g1) = round(b2*k / n) + // c2 = round(g2) = round(-b1*k / n) + // + // However, as section 3.4.2 of [STWS] notes, the aforementioned approach + // requires costly long divisions that can be avoided by precomputing + // rounded estimates as follows: + // + // t = bitlen(n) + 1 + // z1 = round(2^t * b2 / n) + // z2 = round(2^t * -b1 / n) + // + // Then, use those precomputed estimates to perform a multiplication by k + // along with a floored division by 2^t, which is a simple right shift by t: + // + // c1 = floor(k * z1 / 2^t) = (k * z1) >> t + // c2 = floor(k * z2 / 2^t) = (k * z2) >> t + // + // Finally, round up if last bit discarded in the right shift by t is set by + // adding 1. + // + // As a further optimization, rather than setting t = bitlen(n) + 1 = 257 as + // stated by [STWS], this implementation uses a higher precision estimate of + // t = bitlen(n) + 64 = 320 because it allows simplification of the shifts + // in the internal calculations that are done via uint64s and also allows + // the use of floor in the precomputations. + // + // Thus, the calculations this implementation uses are: + // + // z1 = floor(b2<<320 / n) | precomputed + // z2 = floor((-b1)<<320) / n) | precomputed + // c1 = ((k * z1) >> 320) + (((k * z1) >> 319) & 1) + // c2 = ((k * z2) >> 320) + (((k * z2) >> 319) & 1) + // + // Putting it all together: + // ------------------------ + // + // Calculate the following vectors using the values discussed above: + // + // v = c1*v1 + c2*v2 + // u = - v + // + // The two components of the resulting vector v are: + // va = c1*a1 + c2*a2 + // vb = c1*b1 + c2*b2 + // + // Thus, the two components of the resulting vector u are: + // k1 = k - va + // k2 = 0 - vb = -vb + // + // As some final optimizations: + // + // 1) Note that k1 + k2*λ ≡ k (mod n) means that k1 ≡ k - k2*λ (mod n). + // Therefore, the computation of va can be avoided to save two + // field multiplications and a field addition. + // + // 2) Since k1 = k - k2*λ = k + k2*(-λ), an additional field negation is + // saved by storing and using the negative version of λ. + // + // 3) Since k2 = -vb = -(c1*b1 + c2*b2) = c1*(-b1) + c2*(-b2), one more + // field negation is saved by storing and using the negative versions of + // b1 and b2. + // + // k2 = c1*(-b1) + c2*(-b2) + // k1 = k + k2*(-λ) + var k1, k2 ModNScalar + c1 := mul512Rsh320Round(k, endoZ1) + c2 := mul512Rsh320Round(k, endoZ2) + k2.Add2(c1.Mul(endoNegB1), c2.Mul(endoNegB2)) + k1.Mul2(&k2, endoNegLambda).Add(k) + return k1, k2 +} + +// nafScalar represents a positive integer up to a maximum value of 2^256 - 1 +// encoded in non-adjacent form. +// +// NAF is a signed-digit representation where each digit can be +1, 0, or -1. +// +// In order to efficiently encode that information, this type uses two arrays, a +// "positive" array where set bits represent the +1 signed digits and a +// "negative" array where set bits represent the -1 signed digits. 0 is +// represented by neither array having a bit set in that position. +// +// The Pos and Neg methods return the aforementioned positive and negative +// arrays, respectively. +type nafScalar struct { + // pos houses the positive portion of the representation. An additional + // byte is required for the positive portion because the NAF encoding can be + // up to 1 bit longer than the normal binary encoding of the value. + // + // neg houses the negative portion of the representation. Even though the + // additional byte is not required for the negative portion, since it can + // never exceed the length of the normal binary encoding of the value, + // keeping the same length for positive and negative portions simplifies + // working with the representation and allows extra conditional branches to + // be avoided. + // + // start and end specify the starting and ending index to use within the pos + // and neg arrays, respectively. This allows fixed size arrays to be used + // versus needing to dynamically allocate space on the heap. + // + // NOTE: The fields are defined in the order that they are to minimize the + // padding on 32-bit and 64-bit platforms. + pos [33]byte + start, end uint8 + neg [33]byte +} + +// Pos returns the bytes of the encoded value with bits set in the positions +// that represent a signed digit of +1. +func (s *nafScalar) Pos() []byte { return s.pos[s.start:s.end] } + +// Neg returns the bytes of the encoded value with bits set in the positions +// that represent a signed digit of -1. +func (s *nafScalar) Neg() []byte { return s.neg[s.start:s.end] } + +// naf takes a positive integer up to a maximum value of 2^256 - 1 and returns +// its non-adjacent form (NAF), which is a unique signed-digit representation +// such that no two consecutive digits are nonzero. See the documentation for +// the returned type for details on how the representation is encoded +// efficiently and how to interpret it +// +// NAF is useful in that it has the fewest nonzero digits of any signed digit +// representation, only 1/3rd of its digits are nonzero on average, and at least +// half of the digits will be 0. +// +// The aforementioned properties are particularly beneficial for optimizing +// elliptic curve point multiplication because they effectively minimize the +// number of required point additions in exchange for needing to perform a mix +// of fewer point additions and subtractions and possibly one additional point +// doubling. This is an excellent tradeoff because subtraction of points has +// the same computational complexity as addition of points and point doubling is +// faster than both. +func naf(k []byte) nafScalar { + // Strip leading zero bytes. + for len(k) > 0 && k[0] == 0x00 { + k = k[1:] + } + // The non-adjacent form (NAF) of a positive integer k is an expression + // k = ∑_(i=0, l-1) k_i * 2^i where k_i ∈ {0,±1}, k_(l-1) != 0, and no two + // consecutive digits k_i are nonzero. + // + // The traditional method of computing the NAF of a positive integer is + // given by algorithm 3.30 in [GECC]. It consists of repeatedly dividing k + // by 2 and choosing the remainder so that the quotient (k−r)/2 is even + // which ensures the next NAF digit is 0. This requires log_2(k) steps. + // + // However, in [BRID], Prodinger notes that a closed form expression for the + // NAF representation is the bitwise difference 3k/2 - k/2. This is more + // efficient as it can be computed in O(1) versus the O(log(n)) of the + // traditional approach. + // + // The following code makes use of that formula to compute the NAF more + // efficiently. + // + // To understand the logic here, observe that the only way the NAF has a + // nonzero digit at a given bit is when either 3k/2 or k/2 has a bit set in + // that position, but not both. In other words, the result of a bitwise + // xor. This can be seen simply by considering that when the bits are the + // same, the subtraction is either 0-0 or 1-1, both of which are 0. + // + // Further, observe that the "+1" digits in the result are contributed by + // 3k/2 while the "-1" digits are from k/2. So, they can be determined by + // taking the bitwise and of each respective value with the result of the + // xor which identifies which bits are nonzero. + // + // Using that information, this loops backwards from the least significant + // byte to the most significant byte while performing the aforementioned + // calculations by propagating the potential carry and high order bit from + // the next word during the right shift. + kLen := len(k) + var result nafScalar + var carry uint8 + for byteNum := kLen - 1; byteNum >= 0; byteNum-- { + // Calculate k/2. Notice the carry from the previous word is added and + // the low order bit from the next word is shifted in accordingly. + kc := uint16(k[byteNum]) + uint16(carry) + var nextWord uint8 + if byteNum > 0 { + nextWord = k[byteNum-1] + } + halfK := kc>>1 | uint16(nextWord<<7) + // Calculate 3k/2 and determine the non-zero digits in the result. + threeHalfK := kc + halfK + nonZeroResultDigits := threeHalfK ^ halfK + // Determine the signed digits {0, ±1}. + result.pos[byteNum+1] = uint8(threeHalfK & nonZeroResultDigits) + result.neg[byteNum+1] = uint8(halfK & nonZeroResultDigits) + // Propagate the potential carry from the 3k/2 calculation. + carry = uint8(threeHalfK >> 8) + } + result.pos[0] = carry + // Set the starting and ending positions within the fixed size arrays to + // identify the bytes that are actually used. This is important since the + // encoding is big endian and thus trailing zero bytes changes its value. + result.start = 1 - carry + result.end = uint8(kLen + 1) + return result +} + +// ScalarMultNonConst multiplies k*P where k is a scalar modulo the curve order +// and P is a point in Jacobian projective coordinates and stores the result in +// the provided Jacobian point. +// +// NOTE: The point must be normalized for this function to return the correct +// result. The resulting point will be normalized. +func ScalarMultNonConst(k *ModNScalar, point, result *JacobianPoint) { + // ------------------------------------------------------------------------- + // This makes use of the following efficiently-computable endomorphism to + // accelerate the computation: + // + // φ(P) ⟼ λ*P = (β*P.x mod p, P.y) + // + // In other words, there is a special scalar λ that every point on the + // elliptic curve can be multiplied by that will result in the same point as + // performing a single field multiplication of the point's X coordinate by + // the special value β. + // + // This is useful because scalar point multiplication is significantly more + // expensive than a single field multiplication given the former involves a + // series of point doublings and additions which themselves consist of a + // combination of several field multiplications, squarings, and additions. + // + // So, the idea behind making use of the endomorphism is thus to decompose + // the scalar into two scalars that are each about half the bit length of + // the original scalar such that: + // + // k ≡ k1 + k2*λ (mod n) + // + // This in turn allows the scalar point multiplication to be performed as a + // sum of two smaller half-length multiplications as follows: + // + // k*P = (k1 + k2*λ)*P + // = k1*P + k2*λ*P + // = k1*P + k2*φ(P) + // + // Thus, a speedup is achieved so long as it's faster to decompose the + // scalar, compute φ(P), and perform a simultaneous multiply of the + // half-length point multiplications than it is to compute a full width + // point multiplication. + // + // In practice, benchmarks show the current implementation provides a + // speedup of around 30-35% versus not using the endomorphism. + // + // See section 3.5 in [GECC] for a more rigorous treatment. + // ------------------------------------------------------------------------- + + // Per above, the main equation here to remember is: + // k*P = k1*P + k2*φ(P) + // + // p1 below is P in the equation while p2 is φ(P) in the equation. + // + // NOTE: φ(x,y) = (β*x,y). The Jacobian z coordinates are the same, so this + // math goes through. + // + // Also, calculate -p1 and -p2 for use in the NAF optimization. + p1, p1Neg := new(JacobianPoint), new(JacobianPoint) + p1.Set(point) + p1Neg.Set(p1) + p1Neg.Y.Negate(1).Normalize() + p2, p2Neg := new(JacobianPoint), new(JacobianPoint) + p2.Set(p1) + p2.X.Mul(endoBeta).Normalize() + p2Neg.Set(p2) + p2Neg.Y.Negate(1).Normalize() + // Decompose k into k1 and k2 such that k = k1 + k2*λ (mod n) where k1 and + // k2 are around half the bit length of k in order to halve the number of EC + // operations. + // + // Notice that this also flips the sign of the scalars and points as needed + // to minimize the bit lengths of the scalars k1 and k2. + // + // This is done because the scalars are operating modulo the group order + // which means that when they would otherwise be a small negative magnitude + // they will instead be a large positive magnitude. Since the goal is for + // the scalars to have a small magnitude to achieve a performance boost, use + // their negation when they are greater than the half order of the group and + // flip the positive and negative values of the corresponding point that + // will be multiplied by to compensate. + // + // In other words, transform the calc when k1 is over the half order to: + // k1*P = -k1*-P + // + // Similarly, transform the calc when k2 is over the half order to: + // k2*φ(P) = -k2*-φ(P) + k1, k2 := splitK(k) + if k1.IsOverHalfOrder() { + k1.Negate() + p1, p1Neg = p1Neg, p1 + } + if k2.IsOverHalfOrder() { + k2.Negate() + p2, p2Neg = p2Neg, p2 + } + // Convert k1 and k2 into their NAF representations since NAF has a lot more + // zeros overall on average which minimizes the number of required point + // additions in exchange for a mix of fewer point additions and subtractions + // at the cost of one additional point doubling. + // + // This is an excellent tradeoff because subtraction of points has the same + // computational complexity as addition of points and point doubling is + // faster than both. + // + // Concretely, on average, 1/2 of all bits will be non-zero with the normal + // binary representation whereas only 1/3rd of the bits will be non-zero + // with NAF. + // + // The Pos version of the bytes contain the +1s and the Neg versions contain + // the -1s. + k1Bytes, k2Bytes := k1.Bytes(), k2.Bytes() + k1NAF, k2NAF := naf(k1Bytes[:]), naf(k2Bytes[:]) + k1PosNAF, k1NegNAF := k1NAF.Pos(), k1NAF.Neg() + k2PosNAF, k2NegNAF := k2NAF.Pos(), k2NAF.Neg() + k1Len, k2Len := len(k1PosNAF), len(k2PosNAF) + // Add left-to-right using the NAF optimization. See algorithm 3.77 from + // [GECC]. + // + // Point Q = ∞ (point at infinity). + var q JacobianPoint + m := k1Len + if m < k2Len { + m = k2Len + } + for i := 0; i < m; i++ { + // Since k1 and k2 are potentially different lengths and the calculation + // is being done left to right, pad the front of the shorter one with + // 0s. + var k1BytePos, k1ByteNeg, k2BytePos, k2ByteNeg byte + if i >= m-k1Len { + k1BytePos, k1ByteNeg = k1PosNAF[i-(m-k1Len)], k1NegNAF[i-(m-k1Len)] + } + if i >= m-k2Len { + k2BytePos, k2ByteNeg = k2PosNAF[i-(m-k2Len)], k2NegNAF[i-(m-k2Len)] + } + for mask := uint8(1 << 7); mask > 0; mask >>= 1 { + // Q = 2 * Q + DoubleNonConst(&q, &q) + // Add or subtract the first point based on the signed digit of the + // NAF representation of k1 at this bit position. + // + // +1: Q = Q + p1 + // -1: Q = Q - p1 + // 0: Q = Q (no change) + if k1BytePos&mask == mask { + AddNonConst(&q, p1, &q) + } else if k1ByteNeg&mask == mask { + AddNonConst(&q, p1Neg, &q) + } + // Add or subtract the second point based on the signed digit of the + // NAF representation of k2 at this bit position. + // + // +1: Q = Q + p2 + // -1: Q = Q - p2 + // 0: Q = Q (no change) + if k2BytePos&mask == mask { + AddNonConst(&q, p2, &q) + } else if k2ByteNeg&mask == mask { + AddNonConst(&q, p2Neg, &q) + } + } + } + result.Set(&q) +} + +// ScalarBaseMultNonConst multiplies k*G where k is a scalar modulo the curve +// order and G is the base point of the group and stores the result in the +// provided Jacobian point. +// +// NOTE: The resulting point will be normalized. +func ScalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) { + bytePoints := BytePointTable + // Start with the point at infinity. + result.X.Zero() + result.Y.Zero() + result.Z.Zero() + // bytePoints has all 256 byte points for each 8-bit window. The strategy + // is to add up the byte points. This is best understood by expressing k in + // base-256 which it already sort of is. Each "digit" in the 8-bit window + // can be looked up using bytePoints and added together. + kb := k.Bytes() + for i := 0; i < len(kb); i++ { + pt := &bytePoints[i][kb[i]] + AddNonConst(result, pt, result) + } +} + +// isOnCurve returns whether or not the affine point (x,y) is on the curve. +func isOnCurve(fx, fy *FieldVal) bool { + // Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7 + y2 := new(FieldVal).SquareVal(fy).Normalize() + result := new(FieldVal).SquareVal(fx).Mul(fx).AddInt(7).Normalize() + return y2.Equals(result) +} + +// DecompressY attempts to calculate the Y coordinate for the given X coordinate +// such that the result pair is a point on the secp256k1 curve. It adjusts Y +// based on the desired oddness and returns whether or not it was successful +// since not all X coordinates are valid. +// +// The magnitude of the provided X coordinate field val must be a max of 8 for a +// correct result. The resulting Y field val will have a max magnitude of 2. +func DecompressY(x *FieldVal, odd bool, resultY *FieldVal) bool { + // The curve equation for secp256k1 is: y^2 = x^3 + 7. Thus + // y = +-sqrt(x^3 + 7). + // + // The x coordinate must be invalid if there is no square root for the + // calculated rhs because it means the X coordinate is not for a point on + // the curve. + x3PlusB := new(FieldVal).SquareVal(x).Mul(x).AddInt(7) + if hasSqrt := resultY.SquareRootVal(x3PlusB); !hasSqrt { + return false + } + if resultY.Normalize().IsOdd() != odd { + resultY.Negate(1) + } + return true +} diff --git a/pkg/crypto/ec/secp256k1/curve_test.go b/pkg/crypto/ec/secp256k1/curve_test.go new file mode 100644 index 0000000..6deed23 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/curve_test.go @@ -0,0 +1,1012 @@ +// Copyright (c) 2015-2022 The Decred developers +// Copyright 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "fmt" + "math/big" + "math/bits" + "math/rand" + "testing" + "time" + + "lol.mleku.dev/chk" +) + +var ( + // oneModN is simply the number 1 as a mod n scalar. + oneModN = hexToModNScalar("1") + // endoLambda is the positive version of the lambda constant used in the + // endomorphism. It is stored here for convenience and to avoid recomputing + // it throughout the tests. + endoLambda = new(ModNScalar).NegateVal(endoNegLambda) +) + +// isValidJacobianPoint returns true if the point (x,y,z) is on the secp256k1 +// curve or is the point at infinity. +func isValidJacobianPoint(point *JacobianPoint) bool { + if (point.X.IsZero() && point.Y.IsZero()) || point.Z.IsZero() { + return true + } + // Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7 + // In Jacobian coordinates, Y = y/z^3 and X = x/z^2 + // Thus: + // (y/z^3)^2 = (x/z^2)^3 + 7 + // y^2/z^6 = x^3/z^6 + 7 + // y^2 = x^3 + 7*z^6 + var y2, z2, x3, result FieldVal + y2.SquareVal(&point.Y).Normalize() + z2.SquareVal(&point.Z) + x3.SquareVal(&point.X).Mul(&point.X) + result.SquareVal(&z2).Mul(&z2).MulInt(7).Add(&x3).Normalize() + return y2.Equals(&result) +} + +// 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. +func jacobianPointFromHex(x, y, z string) JacobianPoint { + var p JacobianPoint + p.X.SetHex(x) + p.Y.SetHex(y) + p.Z.SetHex(z) + return p +} + +// IsStrictlyEqual returns whether or not the two Jacobian points are strictly +// equal for use in the tests. Recall that several Jacobian points can be equal +// in affine coordinates, while not having the same coordinates in projective +// space, so the two points not being equal doesn't necessarily mean they aren't +// actually the same affine point. +func (p *JacobianPoint) IsStrictlyEqual(other *JacobianPoint) bool { + return p.X.Equals(&other.X) && p.Y.Equals(&other.Y) && p.Z.Equals(&other.Z) +} + +// TestAddJacobian tests addition of points projected in Jacobian coordinates +// works as intended. +func TestAddJacobian(t *testing.T) { + tests := []struct { + name string // test description + x1, y1, z1 string // hex encoded coordinates of first point to add + x2, y2, z2 string // hex encoded coordinates of second point to add + x3, y3, z3 string // hex encoded coordinates of expected point + }{ + { + // Addition with the point at infinity (left hand side). + name: "∞ + P = P", + x1: "0", + y1: "0", + z1: "0", + x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + z2: "1", + x3: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y3: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + z3: "1", + }, { + // Addition with the point at infinity (right hand side). + name: "P + ∞ = P", + x1: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y1: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + z1: "1", + x2: "0", + y2: "0", + z2: "0", + x3: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y3: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + z3: "1", + }, { + // Addition with z1=z2=1 different x values. + name: "P(x1, y1, 1) + P(x2, y1, 1)", + x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + z1: "1", + x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + z2: "1", + x3: "0cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a6", + y3: "e205f79361bbe0346b037b4010985dbf4f9e1e955e7d0d14aca876bfa79aad87", + z3: "44a5646b446e3877a648d6d381370d9ef55a83b666ebce9df1b1d7d65b817b2f", + }, { + // Addition with z1=z2=1 same x opposite y. + name: "P(x, y, 1) + P(x, -y, 1) = ∞", + x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + z1: "1", + x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y2: "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + z2: "1", + x3: "0", + y3: "0", + z3: "0", + }, { + // Addition with z1=z2=1 same point. + name: "P(x, y, 1) + P(x, y, 1) = 2P", + x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + z1: "1", + x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y2: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + z2: "1", + x3: "ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", + y3: "b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", + z3: "16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", + }, { + // Addition with z1=z2 (!=1) different x values. + name: "P(x1, y1, 2) + P(x2, y2, 2)", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "5d2fe112c21891d440f65a98473cb626111f8a234d2cd82f22172e369f002147", + y2: "98e3386a0a622a35c4561ffb32308d8e1c6758e10ebb1b4ebd3d04b4eb0ecbe8", + z2: "2", + x3: "cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a60", + y3: "817de4d86ef80d1ac0ded00426176fd3e787a5579f43452b2a1db021e6ac3778", + z3: "129591ad11b8e1de99235b4e04dc367bd56a0ed99baf3a77c6c75f5a6e05f08d", + }, { + // Addition with z1=z2 (!=1) same x opposite y. + name: "P(x, y, 2) + P(x, -y, 2) = ∞", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y2: "a470ab21467813b6e0496d2c2b70c11446bab4fcbc9a52b7f225f30e869aea9f", + z2: "2", + x3: "0", + y3: "0", + z3: "0", + }, { + // Addition with z1=z2 (!=1) same point. + name: "P(x, y, 2) + P(x, y, 2) = 2P", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y2: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z2: "2", + x3: "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + y3: "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + z3: "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, { + // Addition with z1!=z2 and z2=1 different x values. + name: "P(x1, y1, 2) + P(x2, y2, 1)", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + z2: "1", + x3: "3ef1f68795a6ccd1181e23eab80a1b9a2cebdcde755413bf097936eb5b91b4f3", + y3: "0bef26c377c068d606f6802130bb7e9f3c3d2abcfa1a295950ed81133561cb04", + z3: "252b235a2371c3bd3246b69c09b86cf7aad41db3375e74ef8d8ebeb4dc0be11a", + }, { + // Addition with z1!=z2 and z2=1 same x opposite y. + name: "P(x, y, 2) + P(x, -y, 1) = ∞", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y2: "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + z2: "1", + x3: "0", + y3: "0", + z3: "0", + }, { + // Addition with z1!=z2 and z2=1 same point. + name: "P(x, y, 2) + P(x, y, 1) = 2P", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y2: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + z2: "1", + x3: "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + y3: "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + z3: "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, { + // Addition with z1!=z2 and z2!=1 different x values. + name: "P(x1, y1, 2) + P(x2, y2, 3)", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4", + y2: "03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1", + z2: "3", + x3: "3f07081927fd3f6dadd4476614c89a09eba7f57c1c6c3b01fa2d64eac1eef31e", + y3: "949166e04ebc7fd95a9d77e5dfd88d1492ecffd189792e3944eb2b765e09e031", + z3: "eb8cba81bcffa4f44d75427506737e1f045f21e6d6f65543ee0e1d163540c931", + }, { + // Addition with z1!=z2 and z2!=1 same x opposite y. + name: "P(x, y, 2) + P(x, -y, 3) = ∞", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", + y2: "cafc41904dd5428934f7d075129c8ba46eb622d4fc88d72cd1401452664add18", + z2: "3", + x3: "0", + y3: "0", + z3: "0", + }, { + // Addition with z1!=z2 and z2!=1 same point. + name: "P(x, y, 2) + P(x, y, 3) = 2P", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x2: "dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", + y2: "3503be6fb22abd76cb082f8aed63745b9149dd2b037728d32ebfebac99b51f17", + z2: "3", + x3: "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + y3: "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + z3: "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + } + for _, test := range tests { + // Convert hex to Jacobian points. + p1 := jacobianPointFromHex(test.x1, test.y1, test.z1) + p2 := jacobianPointFromHex(test.x2, test.y2, test.z2) + want := jacobianPointFromHex(test.x3, test.y3, test.z3) + + // Ensure the test data is using points that are actually on the curve + // (or the point at infinity). + if !isValidJacobianPoint(&p1) { + t.Errorf("%s: first point is not on the curve", test.name) + continue + } + if !isValidJacobianPoint(&p2) { + t.Errorf("%s: second point is not on the curve", test.name) + continue + } + if !isValidJacobianPoint(&want) { + t.Errorf("%s: expected point is not on the curve", test.name) + continue + } + // Add the two points. + var r JacobianPoint + AddNonConst(&p1, &p2, &r) + // Ensure result matches expected. + if !r.IsStrictlyEqual(&want) { + t.Errorf( + "%s: wrong result\ngot: (%v, %v, %v)\nwant: (%v, %v, %v)", + test.name, r.X, r.Y, r.Z, want.X, want.Y, want.Z, + ) + continue + } + } +} + +// TestDoubleJacobian tests doubling of points projected in Jacobian coordinates +// works as intended for some edge cases and known good values. +func TestDoubleJacobian(t *testing.T) { + tests := []struct { + name string // test description + x1, y1, z1 string // hex encoded coordinates of point to double + x3, y3, z3 string // hex encoded coordinates of expected point + }{ + { + // Doubling the point at infinity is still infinity. + name: "2*∞ = ∞ (point at infinity)", + x1: "0", + y1: "0", + z1: "0", + x3: "0", + y3: "0", + z3: "0", + }, { + // Doubling with z1=1. + name: "2*P(x, y, 1)", + x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + z1: "1", + x3: "ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", + y3: "b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", + z3: "16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", + }, { + // Doubling with z1!=1. + name: "2*P(x, y, 2)", + x1: "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + y1: "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + z1: "2", + x3: "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + y3: "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + z3: "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, { + // From btcd issue #709. + name: "carry to bit 256 during normalize", + x1: "201e3f75715136d2f93c4f4598f91826f94ca01f4233a5bd35de9708859ca50d", + y1: "bdf18566445e7562c6ada68aef02d498d7301503de5b18c6aef6e2b1722412e1", + z1: "0000000000000000000000000000000000000000000000000000000000000001", + x3: "4a5e0559863ebb4e9ed85f5c4fa76003d05d9a7626616e614a1f738621e3c220", + y3: "00000000000000000000000000000000000000000000000000000001b1388778", + z3: "7be30acc88bceac58d5b4d15de05a931ae602a07bcb6318d5dedc563e4482993", + }, + } + for _, test := range tests { + // Convert hex to field values. + p1 := jacobianPointFromHex(test.x1, test.y1, test.z1) + want := jacobianPointFromHex(test.x3, test.y3, test.z3) + + // Ensure the test data is using points that are actually on the curve + // (or the point at infinity). + if !isValidJacobianPoint(&p1) { + t.Errorf("%s: first point is not on the curve", test.name) + continue + } + if !isValidJacobianPoint(&want) { + t.Errorf("%s: expected point is not on the curve", test.name) + continue + } + // Double the point. + var result JacobianPoint + DoubleNonConst(&p1, &result) + // Ensure result matches expected. + if !result.IsStrictlyEqual(&want) { + t.Errorf( + "%s: wrong result\ngot: (%v, %v, %v)\nwant: (%v, %v, %v)", + test.name, result.X, result.Y, result.Z, want.X, want.Y, + want.Z, + ) + continue + } + } +} + +// checkNAFEncoding returns an error if the provided positive and negative +// portions of an overall NAF encoding do not adhere to the requirements or they +// do not sum back to the provided original value. +func checkNAFEncoding(pos, neg []byte, origValue *big.Int) (err error) { + // NAF must not have a leading zero byte and the number of negative + // bytes must not exceed the positive portion. + if len(pos) > 0 && pos[0] == 0 { + return fmt.Errorf("positive has leading zero -- got %x", pos) + } + if len(neg) > len(pos) { + return fmt.Errorf( + "negative has len %d > pos len %d", len(neg), + len(pos), + ) + } + // Ensure the result doesn't have any adjacent non-zero digits. + gotPos := new(big.Int).SetBytes(pos) + gotNeg := new(big.Int).SetBytes(neg) + posOrNeg := new(big.Int).Or(gotPos, gotNeg) + prevBit := posOrNeg.Bit(0) + for bit := 1; bit < posOrNeg.BitLen(); bit++ { + thisBit := posOrNeg.Bit(bit) + if prevBit == 1 && thisBit == 1 { + return fmt.Errorf( + "adjacent non-zero digits found at bit pos %d", + bit-1, + ) + } + prevBit = thisBit + } + // Ensure the resulting positive and negative portions of the overall + // NAF representation sum back to the original value. + gotValue := new(big.Int).Sub(gotPos, gotNeg) + if origValue.Cmp(gotValue) != 0 { + return fmt.Errorf( + "pos-neg is not original value: got %x, want %x", + gotValue, origValue, + ) + } + return nil +} + +// TestNAF ensures encoding various edge cases and values to non-adjacent form +// produces valid results. +func TestNAF(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + }{ + { + name: "empty is zero", + in: "", + }, { + name: "zero", + in: "00", + }, { + name: "just before first carry", + in: "aa", + }, { + name: "first carry", + in: "ab", + }, { + name: "leading zeroes", + in: "002f20569b90697ad471c1be6107814f53f47446be298a3a2a6b686b97d35cf9", + }, { + name: "257 bits when NAF encoded", + in: "c000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "32-byte scalar", + in: "6df2b5d30854069ccdec40ae022f5c948936324a4e9ebed8eb82cfd5a6b6d766", + }, { + name: "first term of balanced length-two representation #1", + in: "b776e53fb55f6b006a270d42d64ec2b1", + }, { + name: "second term balanced length-two representation #1", + in: "d6cc32c857f1174b604eefc544f0c7f7", + }, { + name: "first term of balanced length-two representation #2", + in: "45c53aa1bb56fcd68c011e2dad6758e4", + }, { + name: "second term of balanced length-two representation #2", + in: "a2e79d200f27f2360fba57619936159b", + }, + } + for _, test := range tests { + // Ensure the resulting positive and negative portions of the overall + // NAF representation adhere to the requirements of NAF encoding and + // they sum back to the original value. + result := naf(hexToBytes(test.in)) + pos, neg := result.Pos(), result.Neg() + if err := checkNAFEncoding(pos, neg, fromHex(test.in)); chk.T(err) { + t.Errorf("%q: %v", test.name, err) + } + } +} + +// TestNAFRandom ensures that encoding randomly-generated values to non-adjacent +// form produces valid results. +func TestNAFRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Ensure the resulting positive and negative portions of the overall + // NAF representation adhere to the requirements of NAF encoding and + // they sum back to the original value. + bigIntVal, modNVal := randIntAndModNScalar(t, rng) + valBytes := modNVal.Bytes() + result := naf(valBytes[:]) + pos, neg := result.Pos(), result.Neg() + if err := checkNAFEncoding(pos, neg, bigIntVal); chk.T(err) { + t.Fatalf( + "encoding err: %v\nin: %x\npos: %x\nneg: %x", err, + bigIntVal, pos, neg, + ) + } + } +} + +// TestScalarBaseMultJacobian ensures multiplying a given scalar by the base +// point projected in Jacobian coordinates works as intended for some edge cases +// and known values. It also verifies in affine coordinates as well. +func TestScalarBaseMultJacobian(t *testing.T) { + tests := []struct { + name string // test description + k string // hex encoded scalar + x1, y1, z1 string // hex encoded Jacobian coordinates of expected point + x2, y2 string // hex encoded affine coordinates of expected point + }{ + { + name: "zero", + k: "0000000000000000000000000000000000000000000000000000000000000000", + x1: "0000000000000000000000000000000000000000000000000000000000000000", + y1: "0000000000000000000000000000000000000000000000000000000000000000", + z1: "0000000000000000000000000000000000000000000000000000000000000001", + x2: "0000000000000000000000000000000000000000000000000000000000000000", + y2: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "one (aka 1*G = G)", + k: "0000000000000000000000000000000000000000000000000000000000000001", + x1: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + y1: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + z1: "0000000000000000000000000000000000000000000000000000000000000001", + x2: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + y2: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + }, { + name: "group order - 1 (aka -1*G = -G)", + k: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + x1: "667d5346809ba7602db1ea0bd990eee6ff75d7a64004d563534123e6f12a12d7", + y1: "344f2f772f8f4cbd04709dba7837ff1422db8fa6f99a00f93852de2c45284838", + z1: "19e5a058ef4eaada40d19063917bb4dc07f50c3a0f76bd5348a51057a3721c57", + x2: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + y2: "b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + }, { + name: "known good point 1", + k: "aa5e28d6a97a2479a65527f7290311a3624d4cc0fa1578598ee3c2613bf99522", + x1: "5f64fd9364bac24dc32bc01b7d63aaa8249babbdc26b03233e14120840ae20f6", + y1: "a4ced9be1e1ed6ef73bec6866c3adc0695347303c30b814fb0dfddb3a22b090d", + z1: "931a3477a1b1d866842b22577618e134c89ba12e5bb38c465265c8a2cefa69dc", + x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y2: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + }, { + name: "known good point 2", + k: "7e2b897b8cebc6361663ad410835639826d590f393d90a9538881735256dfae3", + x1: "c2cb761af4d6410bea0ed7d5f3c7397b63739b0f37e5c3047f8a45537a9d413e", + y1: "34b9204c55336d2fb94e20e53d5aa2ffe4da6f80d72315b4dcafca11e7c0f768", + z1: "ca5d9e8024575c80fe185416ff4736aff8278873da60cf101d10ab49780ee33b", + x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, { + name: "known good point 3", + k: "6461e6df0fe7dfd05329f41bf771b86578143d4dd1f7866fb4ca7e97c5fa945d", + x1: "09160b87ee751ef9fd51db49afc7af9c534917fad72bf461d21fec2590878267", + y1: "dbc2757c5038e0b059d1e05c2d3706baf1a164e3836a02c240173b22c92da7c0", + z1: "c157ea3f784c37603d9f55e661dd1d6b8759fccbfb2c8cf64c46529d94c8c950", + x2: "e8aecc370aedd953483719a116711963ce201ac3eb21d3f3257bb48668c6a72f", + y2: "c25caf2f0eba1ddb2f0f3f47866299ef907867b7d27e95b3873bf98397b24ee1", + }, { + name: "known good point 4", + k: "376a3a2cdcd12581efff13ee4ad44c4044b8a0524c42422a7e1e181e4deeccec", + x1: "7820c46de3b5a0202bea06870013fcb23adb4a000f89d5b86fe1df24be58fa79", + y1: "95e5a977eb53a582677ff0432eef5bc66f1dd983c3e8c07e1c77c3655542c31e", + z1: "7d71ecfdfa66b003fe96f925b5907f67a1a4a6489f4940ec3b78edbbf847334f", + x2: "14890e61fcd4b0bd92e5b36c81372ca6fed471ef3aa60a3e415ee4fe987daba1", + y2: "297b858d9f752ab42d3bca67ee0eb6dcd1c2b7b0dbe23397e66adc272263f982", + }, { + name: "known good point 5", + k: "1b22644a7be026548810c378d0b2994eefa6d2b9881803cb02ceff865287d1b9", + x1: "68a934fa2d28fb0b0d2b6801a9335d62e65acef9467be2ea67f5b11614b59c78", + y1: "5edd7491e503acf61ed651a10cf466de06bf5c6ba285a7a2885a384bbdd32898", + z1: "f3b28d36c3132b6f4bd66bf0da64b8dc79d66f9a854ba8b609558b6328796755", + x2: "f73c65ead01c5126f28f442d087689bfa08e12763e0cec1d35b01751fd735ed3", + y2: "f449a8376906482a84ed01479bd18882b919c140d638307f0c0934ba12590bde", + }, + } + for _, test := range tests { + // Parse test data. + want := jacobianPointFromHex(test.x1, test.y1, test.z1) + wantAffine := jacobianPointFromHex(test.x2, test.y2, "01") + k := hexToModNScalar(test.k) + // Ensure the test data is using points that are actually on the curve + // (or the point at infinity). + if !isValidJacobianPoint(&want) { + t.Errorf("%q: expected point is not on the curve", test.name) + continue + } + if !isValidJacobianPoint(&wantAffine) { + t.Errorf("%q: expected affine point is not on the curve", test.name) + continue + } + // Ensure the result matches the expected value in Jacobian coordinates. + var r JacobianPoint + ScalarBaseMultNonConst(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, + ) + continue + } + // Ensure the result matches the expected value in affine coordinates. + 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 + } + } +} + +// modNBitLen returns the minimum number of bits required to represent the mod n +// scalar. The result is 0 when the value is 0. +func modNBitLen(s *ModNScalar) uint16 { + if w := s.n[7]; w > 0 { + return uint16(bits.Len32(w)) + 224 + } + if w := s.n[6]; w > 0 { + return uint16(bits.Len32(w)) + 192 + } + if w := s.n[5]; w > 0 { + return uint16(bits.Len32(w)) + 160 + } + if w := s.n[4]; w > 0 { + return uint16(bits.Len32(w)) + 128 + } + if w := s.n[3]; w > 0 { + return uint16(bits.Len32(w)) + 96 + } + if w := s.n[2]; w > 0 { + return uint16(bits.Len32(w)) + 64 + } + if w := s.n[1]; w > 0 { + return uint16(bits.Len32(w)) + 32 + } + return uint16(bits.Len32(s.n[0])) +} + +// checkLambdaDecomposition returns an error if the provided decomposed scalars +// do not satisfy the required equation or they are not small in magnitude. +func checkLambdaDecomposition(origK, k1, k2 *ModNScalar) (err error) { + // Recompose the scalar from the decomposed scalars to ensure they satisfy + // the required equation. + calcK := new(ModNScalar).Mul2(k2, endoLambda).Add(k1) + if !calcK.Equals(origK) { + return fmt.Errorf("recomposed scalar %v != orig scalar", calcK) + } + // Ensure the decomposed scalars are small in magnitude by affirming their + // bit lengths do not exceed one more than half of the bit size of the + // underlying field. This value is max(||v1||, ||v2||), where: + // + // vector v1 = + // vector v2 = + const maxBitLen = 129 + if k1.IsOverHalfOrder() { + k1.Negate() + } + if k2.IsOverHalfOrder() { + k2.Negate() + } + k1BitLen, k2BitLen := modNBitLen(k1), modNBitLen(k2) + if k1BitLen > maxBitLen { + return fmt.Errorf( + "k1 scalar bit len %d > max allowed %d", + k1BitLen, maxBitLen, + ) + } + if k2BitLen > maxBitLen { + return fmt.Errorf( + "k2 scalar bit len %d > max allowed %d", + k2BitLen, maxBitLen, + ) + } + return nil +} + +// TestSplitK ensures decomposing various edge cases and values into a balanced +// length-two representation produces valid results. +func TestSplitK(t *testing.T) { + // Values computed from the group half order and lambda such that they + // exercise the decomposition edge cases and maximize the bit lengths of the + // produced scalars. + h := "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0" + negOne := new(ModNScalar).NegateVal(oneModN) + halfOrder := hexToModNScalar(h) + halfOrderMOne := new(ModNScalar).Add2(halfOrder, negOne) + halfOrderPOne := new(ModNScalar).Add2(halfOrder, oneModN) + lambdaMOne := new(ModNScalar).Add2(endoLambda, negOne) + lambdaPOne := new(ModNScalar).Add2(endoLambda, oneModN) + negLambda := new(ModNScalar).NegateVal(endoLambda) + halfOrderMOneMLambda := new(ModNScalar).Add2(halfOrderMOne, negLambda) + halfOrderMLambda := new(ModNScalar).Add2(halfOrder, negLambda) + halfOrderPOneMLambda := new(ModNScalar).Add2(halfOrderPOne, negLambda) + lambdaPHalfOrder := new(ModNScalar).Add2(endoLambda, halfOrder) + lambdaPOnePHalfOrder := new(ModNScalar).Add2(lambdaPOne, halfOrder) + tests := []struct { + name string // test description + k *ModNScalar // scalar to decompose + }{ + { + name: "zero", + k: new(ModNScalar), + }, { + name: "one", + k: oneModN, + }, { + name: "group order - 1 (aka -1 mod N)", + k: negOne, + }, { + name: "group half order - 1 - lambda", + k: halfOrderMOneMLambda, + }, { + name: "group half order - lambda", + k: halfOrderMLambda, + }, { + name: "group half order + 1 - lambda", + k: halfOrderPOneMLambda, + }, { + name: "group half order - 1", + k: halfOrderMOne, + }, { + name: "group half order", + k: halfOrder, + }, { + name: "group half order + 1", + k: halfOrderPOne, + }, { + name: "lambda - 1", + k: lambdaMOne, + }, { + name: "lambda", + k: endoLambda, + }, { + name: "lambda + 1", + k: lambdaPOne, + }, { + name: "lambda + group half order", + k: lambdaPHalfOrder, + }, { + name: "lambda + 1 + group half order", + k: lambdaPOnePHalfOrder, + }, + } + for _, test := range tests { + // Decompose the scalar and ensure the resulting decomposition satisfies + // the required equation and consists of scalars that are small in + // magnitude. + k1, k2 := splitK(test.k) + if err := checkLambdaDecomposition(test.k, &k1, &k2); chk.T(err) { + t.Errorf("%q: %v", test.name, err) + } + } +} + +// TestSplitKRandom ensures that decomposing randomly-generated scalars into a +// balanced length-two representation produces valid results. +func TestSplitKRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate a random scalar, decompose it, and ensure the resulting + // decomposition satisfies the required equation and consists of scalars + // that are small in magnitude. + origK := randModNScalar(t, rng) + k1, k2 := splitK(origK) + if err := checkLambdaDecomposition(origK, &k1, &k2); chk.T(err) { + t.Fatalf( + "decomposition err: %v\nin: %v\nk1: %v\nk2: %v", err, + origK, k1, k2, + ) + } + } +} + +// TestScalarMultJacobianRandom ensures scalar point multiplication with points +// projected into Jacobian coordinates works as intended for randomly-generated +// scalars and points. +func TestScalarMultJacobianRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + // isSamePoint returns whether the two Jacobian points represent the + // same affine point without modifying the provided points. + isSamePoint := func(p1, p2 *JacobianPoint) bool { + var p1Affine, p2Affine JacobianPoint + p1Affine.Set(p1) + p1Affine.ToAffine() + p2Affine.Set(p2) + p2Affine.ToAffine() + return p1Affine.IsStrictlyEqual(&p2Affine) + } + // The overall idea is to compute the same point different ways. The + // strategy uses two properties: + // + // 1) Compatibility of scalar multiplication with field multiplication + // 2) A point added to its negation is the point at infinity (P+(-P) = ∞) + // + // First, calculate a "chained" point by starting with the base (generator) + // point and then consecutively multiply the resulting points by a series of + // random scalars. + // + // Then, multiply the base point by the product of all of the random scalars + // and ensure the "chained" point matches. + // + // In other words: + // + // k[n]*(...*(k[2]*(k[1]*(k[0]*G)))) = (k[0]*k[1]*k[2]*...*k[n])*G + // + // Along the way, also calculate (-k)*P for each chained point and ensure it + // sums with the current point to the point at infinity. + // + // That is: + // + // k*P + ((-k)*P) = ∞ + const numIterations = 1024 + var infinity JacobianPoint + var chained, negChained, result JacobianPoint + var negK ModNScalar + bigAffineToJacobian(curveParams.Gx, curveParams.Gy, &chained) + product := new(ModNScalar).SetInt(1) + for i := 0; i < numIterations; i++ { + // Generate a random scalar and calculate: + // + // P = k*P + // -P = (-k)*P + // + // Notice that this is intentionally doing the full scalar mult with -k + // as opposed to just flipping the Y coordinate in order to test scalar + // multiplication. + k := randModNScalar(t, rng) + negK.NegateVal(k) + ScalarMultNonConst(&negK, &chained, &negChained) + ScalarMultNonConst(k, &chained, &chained) + + // Ensure kP + ((-k)P) = ∞. + AddNonConst(&chained, &negChained, &result) + if !isSamePoint(&result, &infinity) { + t.Fatalf( + "%d: expected point at infinity\ngot (%v, %v, %v)\n", i, + result.X, result.Y, result.Z, + ) + } + product.Mul(k) + } + // Ensure the point calculated above matches the product of the scalars + // times the base point. + ScalarBaseMultNonConst(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, + ) + } +} + +// TestDecompressY ensures that decompressY works as expected for some edge +// cases. +func TestDecompressY(t *testing.T) { + tests := []struct { + name string // test description + x string // hex encoded x coordinate + valid bool // expected decompress result + wantOddY string // hex encoded expected odd y coordinate + wantEvenY string // hex encoded expected even y coordinate + }{ + { + name: "x = 0 -- not a point on the curve", + x: "0", + valid: false, + wantOddY: "", + wantEvenY: "", + }, { + name: "x = 1", + x: "1", + valid: true, + wantOddY: "bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441", + wantEvenY: "4218f20ae6c646b363db68605822fb14264ca8d2587fdd6fbc750d587e76a7ee", + }, { + name: "x = secp256k1 prime (aka 0) -- not a point on the curve", + x: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + valid: false, + wantOddY: "", + wantEvenY: "", + }, { + name: "x = secp256k1 prime - 1 -- not a point on the curve", + x: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + valid: false, + wantOddY: "", + wantEvenY: "", + }, { + name: "x = secp256k1 group order", + x: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + valid: true, + wantOddY: "670999be34f51e8894b9c14211c28801d9a70fde24b71d3753854b35d07c9a11", + wantEvenY: "98f66641cb0ae1776b463ebdee3d77fe2658f021db48e2c8ac7ab4c92f83621e", + }, { + name: "x = secp256k1 group order - 1 -- not a point on the curve", + x: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + valid: false, + wantOddY: "", + wantEvenY: "", + }, + } + for _, test := range tests { + // 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) + valid := DecompressY(fx, true, &oddY) + if valid != test.valid { + t.Errorf( + "%s: unexpected valid flag -- got: %v, want: %v", + test.name, valid, test.valid, + ) + continue + } + // Decompress the test even y coordinate for the given test x coordinate + // and ensure the returned validity flag matches the expected result. + var evenY FieldVal + valid = DecompressY(fx, false, &evenY) + if valid != test.valid { + t.Errorf( + "%s: unexpected valid flag -- got: %v, want: %v", + test.name, valid, test.valid, + ) + continue + } + // Skip checks related to the y coordinate when there isn't one. + if !valid { + continue + } + // Ensure the decompressed odd Y coordinate is the expected value. + oddY.Normalize() + wantOddY := new(FieldVal).SetHex(test.wantOddY) + if !wantOddY.Equals(&oddY) { + t.Errorf( + "%s: mismatched odd y\ngot: %v, want: %v", test.name, + oddY, wantOddY, + ) + continue + } + // Ensure the decompressed even Y coordinate is the expected value. + evenY.Normalize() + wantEvenY := new(FieldVal).SetHex(test.wantEvenY) + if !wantEvenY.Equals(&evenY) { + t.Errorf( + "%s: mismatched even y\ngot: %v, want: %v", test.name, + evenY, wantEvenY, + ) + continue + } + // Ensure the decompressed odd y coordinate is actually odd. + if !oddY.IsOdd() { + t.Errorf("%s: odd y coordinate is even", test.name) + continue + } + // Ensure the decompressed even y coordinate is actually even. + if evenY.IsOdd() { + t.Errorf("%s: even y coordinate is odd", test.name) + continue + } + } +} + +// TestDecompressYRandom ensures that decompressY works as expected with +// randomly-generated x coordinates. +func TestDecompressYRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + origX := randFieldVal(t, rng) + // Calculate both corresponding y coordinates for the random x when it + // is a valid coordinate. + var oddY, evenY FieldVal + x := new(FieldVal).Set(origX) + oddSuccess := DecompressY(x, true, &oddY) + evenSuccess := DecompressY(x, false, &evenY) + // Ensure that the decompression success matches for both the even and + // odd cases depending on whether or not x is a valid coordinate. + if oddSuccess != evenSuccess { + t.Fatalf( + "mismatched decompress success for x = %v -- odd: %v, "+ + "even: %v", x, oddSuccess, evenSuccess, + ) + } + if !oddSuccess { + continue + } + // Ensure the x coordinate was not changed. + if !x.Equals(origX) { + t.Fatalf("x coordinate changed -- orig: %v, changed: %v", origX, x) + } + // Ensure that the resulting y coordinates match their respective + // expected oddness. + oddY.Normalize() + evenY.Normalize() + if !oddY.IsOdd() { + t.Fatalf("requested odd y is even for x = %v", x) + } + if evenY.IsOdd() { + t.Fatalf("requested even y is odd for x = %v", x) + } + // Ensure that the resulting x and y coordinates are actually on the + // curve for both cases. + if !isOnCurve(x, &oddY) { + t.Fatalf("(%v, %v) is not a valid point", x, oddY) + } + if !isOnCurve(x, &evenY) { + t.Fatalf("(%v, %v) is not a valid point", x, evenY) + } + } +} diff --git a/pkg/crypto/ec/secp256k1/doc.go b/pkg/crypto/ec/secp256k1/doc.go new file mode 100644 index 0000000..3c1d978 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/doc.go @@ -0,0 +1,58 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package secp256k1 implements optimized secp256k1 elliptic curve operations in +// pure Go. This is an update that uses the Go 1.16 embed library instead of +// generated code for the data. +// +// This package provides an optimized pure Go implementation of elliptic curve +// cryptography operations over the secp256k1 curve as well as data structures and +// functions for working with public and secret secp256k1 keys. See +// https://www.secg.org/sec2-v2.pdf for details on the standard. +// +// In addition, sub packages are provided to produce, verify, parse, and serialize +// ECDSA signatures and EC-Schnorr-DCRv0 (a custom Schnorr-based signature scheme +// specific to Decred) signatures. See the README.md files in the relevant sub +// packages for more details about those aspects. +// +// An overview of the features provided by this package are as follows: +// +// - Secret key generation, serialization, and parsing +// - Public key generation, serialization and parsing per ANSI X9.62-1998 +// - 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 +// - Elliptic curve operations in Jacobian projective coordinates +// - Point addition +// - Point doubling +// - Scalar multiplication with an arbitrary point +// - Scalar multiplication with the base point (group generator) +// - Point decompression from a given x coordinate +// - Nonce generation via RFC6979 with support for extra data and version +// information that can be used to prevent nonce reuse between signing +// 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. +// +// 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 +// use optimized secp256k1 elliptic curve cryptography. +// +// Finally, a comprehensive suite of tests is provided to provide a high level of +// quality assurance. +// +// # Use of secp256k1 in Decred +// +// At the time of this writing, the primary public key cryptography in widespread +// use on the Decred network used to secure coins is based on elliptic curves +// defined by the secp256k1 domain parameters. +package secp256k1 diff --git a/pkg/crypto/ec/secp256k1/ecdh.go b/pkg/crypto/ec/secp256k1/ecdh.go new file mode 100644 index 0000000..9e49092 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/ecdh.go @@ -0,0 +1,21 @@ +// Copyright (c) 2015 The btcsuite developers +// Copyright (c) 2015-2023 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +// GenerateSharedSecret generates a shared secret based on a secret key and a +// public key using Diffie-Hellman key exchange (ECDH) (RFC 5903). +// RFC5903 Section 9 states we should only return x. +// +// It is recommended to securely hash the result before using as a cryptographic +// key. +func GenerateSharedSecret(seckey *SecretKey, pubkey *PublicKey) []byte { + var point, result JacobianPoint + pubkey.AsJacobian(&point) + ScalarMultNonConst(&seckey.Key, &point, &result) + result.ToAffine() + xBytes := result.X.Bytes() + return xBytes[:] +} diff --git a/pkg/crypto/ec/secp256k1/ecdh_test.go b/pkg/crypto/ec/secp256k1/ecdh_test.go new file mode 100644 index 0000000..88e43ce --- /dev/null +++ b/pkg/crypto/ec/secp256k1/ecdh_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2015-2017 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "testing" + + "next.orly.dev/pkg/utils" +) + +func TestGenerateSharedSecret(t *testing.T) { + secKey1, err := GenerateSecretKey() + if err != nil { + t.Errorf("secret key generation error: %s", err) + return + } + secKey2, err := GenerateSecretKey() + if err != nil { + t.Errorf("secret key generation error: %s", err) + return + } + pubKey1 := secKey1.PubKey() + pubKey2 := secKey2.PubKey() + secret1 := GenerateSharedSecret(secKey1, pubKey2) + secret2 := GenerateSharedSecret(secKey2, pubKey1) + if !utils.FastEqual(secret1, secret2) { + t.Errorf( + "ECDH failed, secrets mismatch - first: %x, second: %x", + secret1, secret2, + ) + } +} diff --git a/pkg/crypto/ec/secp256k1/ellipticadaptor.go b/pkg/crypto/ec/secp256k1/ellipticadaptor.go new file mode 100644 index 0000000..a6ab16a --- /dev/null +++ b/pkg/crypto/ec/secp256k1/ellipticadaptor.go @@ -0,0 +1,247 @@ +// Copyright 2020-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +// References: +// [SECG]: Recommended Elliptic Curve Domain Parameters +// https://www.secg.org/sec2-v2.pdf +// +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "math/big" +) + +// CurveParams contains the parameters for the secp256k1 curve. +type CurveParams struct { + // P is the prime used in the secp256k1 field. + P *big.Int + // N is the order of the secp256k1 curve group generated by the base point. + N *big.Int + // Gx and Gy are the x and y coordinate of the base point, respectively. + Gx, Gy *big.Int + // BitSize is the size of the underlying secp256k1 field in bits. + BitSize int + // H is the cofactor of the secp256k1 curve. + H int + // ByteSize is simply the bit size / 8 and is provided for convenience + // since it is calculated repeatedly. + ByteSize int +} + +// Curve parameters taken from [SECG] section 2.4.1. +var curveParams = CurveParams{ + P: fromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"), + N: fromHex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), + Gx: fromHex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"), + Gy: fromHex("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"), + BitSize: 256, + H: 1, + ByteSize: 256 / 8, +} + +// Params returns the secp256k1 curve parameters for convenience. +func Params() *CurveParams { return &curveParams } + +// KoblitzCurve provides an implementation for secp256k1 that fits the ECC Curve +// interface from crypto/elliptic. +type KoblitzCurve struct { + *elliptic.CurveParams +} + +// bigAffineToJacobian takes an affine point (x, y) as big integers and converts +// it to Jacobian point with Z=1. +func bigAffineToJacobian(x, y *big.Int, result *JacobianPoint) { + result.X.SetByteSlice(x.Bytes()) + result.Y.SetByteSlice(y.Bytes()) + result.Z.SetInt(1) +} + +// jacobianToBigAffine takes a Jacobian point (x, y, z) as field values and +// converts it to an affine point as big integers. +func jacobianToBigAffine(point *JacobianPoint) (*big.Int, *big.Int) { + point.ToAffine() + // Convert the field values for the now affine point to big.Ints. + x3, y3 := new(big.Int), new(big.Int) + x3.SetBytes(point.X.Bytes()[:]) + y3.SetBytes(point.Y.Bytes()[:]) + return x3, y3 +} + +// Params returns the parameters for the curve. +// +// This is part of the elliptic.Curve interface implementation. +func (curve *KoblitzCurve) Params() *elliptic.CurveParams { + return curve.CurveParams +} + +// IsOnCurve returns whether or not the affine point (x,y) is on the curve. +// +// This is part of the elliptic.Curve interface implementation. This function +// differs from the crypto/elliptic algorithm since a = 0 not -3. +func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool { + // Convert big ints to a Jacobian point for faster arithmetic. + var point JacobianPoint + bigAffineToJacobian(x, y, &point) + return isOnCurve(&point.X, &point.Y) +} + +// Add returns the sum of (x1,y1) and (x2,y2). +// +// This is part of the elliptic.Curve interface implementation. +func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + // The point at infinity is the identity according to the group law for + // elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P. + if x1.Sign() == 0 && y1.Sign() == 0 { + return x2, y2 + } + if x2.Sign() == 0 && y2.Sign() == 0 { + return x1, y1 + } + // Convert the affine coordinates from big integers to Jacobian points, + // do the point addition in Jacobian projective space, and convert the + // Jacobian point back to affine big.Ints. + var p1, p2, result JacobianPoint + bigAffineToJacobian(x1, y1, &p1) + bigAffineToJacobian(x2, y2, &p2) + AddNonConst(&p1, &p2, &result) + return jacobianToBigAffine(&result) +} + +// Double returns 2*(x1,y1). +// +// This is part of the elliptic.Curve interface implementation. +func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + if y1.Sign() == 0 { + return new(big.Int), new(big.Int) + } + // Convert the affine coordinates from big integers to Jacobian points, + // do the point doubling in Jacobian projective space, and convert the + // Jacobian point back to affine big.Ints. + var point, result JacobianPoint + bigAffineToJacobian(x1, y1, &point) + DoubleNonConst(&point, &result) + return jacobianToBigAffine(&result) +} + +// moduloReduce reduces k from more than 32 bytes to 32 bytes and under. This +// is done by doing a simple modulo curve.N. We can do this since G^N = 1 and +// thus any other valid point on the elliptic curve has the same order. +func moduloReduce(k []byte) []byte { + // Since the order of G is curve.N, we can use a much smaller number by + // doing modulo curve.N + if len(k) > curveParams.ByteSize { + tmpK := new(big.Int).SetBytes(k) + tmpK.Mod(tmpK, curveParams.N) + return tmpK.Bytes() + } + return k +} + +// ScalarMult returns k*(Bx, By) where k is a big endian integer. +// +// This is part of the elliptic.Curve interface implementation. +func (curve *KoblitzCurve) ScalarMult(Bx, By *big.Int, k []byte) ( + *big.Int, + *big.Int, +) { + // Convert the affine coordinates from big integers to Jacobian points, + // do the multiplication in Jacobian projective space, and convert the + // Jacobian point back to affine big.Ints. + var kModN ModNScalar + kModN.SetByteSlice(moduloReduce(k)) + var point, result JacobianPoint + bigAffineToJacobian(Bx, By, &point) + ScalarMultNonConst(&kModN, &point, &result) + return jacobianToBigAffine(&result) +} + +// ScalarBaseMult returns k*G where G is the base point of the group and k is a +// big endian integer. +// +// This is part of the elliptic.Curve interface implementation. +func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + // Perform the multiplication and convert the Jacobian point back to affine + // big.Ints. + var kModN ModNScalar + kModN.SetByteSlice(moduloReduce(k)) + var result JacobianPoint + ScalarBaseMultNonConst(&kModN, &result) + return jacobianToBigAffine(&result) +} + +// X returns the x coordinate of the public key. +func (p *PublicKey) X() *big.Int { + return new(big.Int).SetBytes(p.x.Bytes()[:]) +} + +// Y returns the y coordinate of the public key. +func (p *PublicKey) Y() *big.Int { + return new(big.Int).SetBytes(p.y.Bytes()[:]) +} + +// ToECDSA returns the public key as a *ecdsa.PublicKey. +func (p *PublicKey) ToECDSA() *ecdsa.PublicKey { + return &ecdsa.PublicKey{ + Curve: S256(), + X: p.X(), + Y: p.Y(), + } +} + +// ToECDSA returns the secret key as a *ecdsa.SecretKey. +func (p *SecretKey) ToECDSA() *ecdsa.PrivateKey { + var secretKeyBytes [SecKeyBytesLen]byte + p.Key.PutBytes(&secretKeyBytes) + var result JacobianPoint + ScalarBaseMultNonConst(&p.Key, &result) + x, y := jacobianToBigAffine(&result) + newSecKey := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: S256(), + X: x, + Y: y, + }, + D: new(big.Int).SetBytes(secretKeyBytes[:]), + } + zeroArray32(&secretKeyBytes) + return newSecKey +} + +// fromHex converts the passed hex string into a big integer pointer and will +// panic is there is an error. This is only provided for the hard-coded +// constants so errors in the source code can bet detected. It will only (and +// must only) be called for initialization purposes. +func fromHex(s string) *big.Int { + if s == "" { + return big.NewInt(0) + } + r, ok := new(big.Int).SetString(s, 16) + if !ok { + panic("invalid hex in source file: " + s) + } + return r +} + +// secp256k1 is a global instance of the KoblitzCurve implementation which in +// turn embeds and implements elliptic.CurveParams. +var secp256k1 = &KoblitzCurve{ + CurveParams: &elliptic.CurveParams{ + P: curveParams.P, + N: curveParams.N, + B: fromHex("0000000000000000000000000000000000000000000000000000000000000007"), + Gx: curveParams.Gx, + Gy: curveParams.Gy, + BitSize: curveParams.BitSize, + Name: "secp256k1", + }, +} + +// S256 returns an elliptic.Curve which implements secp256k1. +func S256() *KoblitzCurve { + return secp256k1 +} diff --git a/pkg/crypto/ec/secp256k1/ellipticadaptor_bench_test.go b/pkg/crypto/ec/secp256k1/ellipticadaptor_bench_test.go new file mode 100644 index 0000000..8fcb595 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/ellipticadaptor_bench_test.go @@ -0,0 +1,51 @@ +// Copyright 2013-2016 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "testing" +) + +// BenchmarkScalarBaseMultAdaptor benchmarks multiplying a scalar by the base +// point of the curve via the method used to satisfy the elliptic.Curve +// interface. +func BenchmarkScalarBaseMultAdaptor(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + curve := S256() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + curve.ScalarBaseMult(k.Bytes()) + } +} + +// BenchmarkScalarBaseMultLargeAdaptor benchmarks multiplying an abnormally +// large scalar by the base point of the curve via the method used to satisfy +// the elliptic.Curve interface. +func BenchmarkScalarBaseMultLargeAdaptor(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c005751111111011111110") + curve := S256() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + curve.ScalarBaseMult(k.Bytes()) + } +} + +// BenchmarkScalarMultAdaptor benchmarks multiplying a scalar by an arbitrary +// point on the curve via the method used to satisfy the elliptic.Curve +// interface. +func BenchmarkScalarMultAdaptor(b *testing.B) { + x := fromHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") + y := fromHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + curve := S256() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + curve.ScalarMult(x, y, k.Bytes()) + } +} diff --git a/pkg/crypto/ec/secp256k1/ellipticadaptor_test.go b/pkg/crypto/ec/secp256k1/ellipticadaptor_test.go new file mode 100644 index 0000000..a165647 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/ellipticadaptor_test.go @@ -0,0 +1,427 @@ +// Copyright (c) 2020-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "math/big" + "math/rand" + "testing" + "time" + + "lol.mleku.dev/chk" +) + +// randBytes returns a byte slice of the required size created from a random +// value generated by the passed rng. +func randBytes(t *testing.T, rng *rand.Rand, numBytes uint8) []byte { + t.Helper() + + buf := make([]byte, numBytes) + if _, err := rng.Read(buf); chk.T(err) { + t.Fatalf("failed to read random: %v", err) + } + + return buf +} + +// TestIsOnCurveAdaptor ensures the IsOnCurve method used to satisfy the +// elliptic.Curve interface works as intended. +func TestIsOnCurveAdaptor(t *testing.T) { + s256 := S256() + if !s256.IsOnCurve(s256.Params().Gx, s256.Params().Gy) { + t.Fatal("generator point does not claim to be on the curve") + } +} + +// isValidAffinePoint returns true if the point (x,y) is on the secp256k1 curve +// or is the point at infinity. +func isValidAffinePoint(x, y *big.Int) bool { + if x.Sign() == 0 && y.Sign() == 0 { + return true + } + return S256().IsOnCurve(x, y) +} + +// TestAddAffineAdaptor tests addition of points in affine coordinates via the +// method used to satisfy the elliptic.Curve interface works as intended for +// some edge cases and known good values. +func TestAddAffineAdaptor(t *testing.T) { + tests := []struct { + name string // test description + x1, y1 string // hex encoded coordinates of first point to add + x2, y2 string // hex encoded coordinates of second point to add + x3, y3 string // hex encoded coordinates of expected point + }{ + { + // Addition with the point at infinity (left hand side). + name: "∞ + P = P", + x1: "0", + y1: "0", + x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + x3: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y3: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, { + // Addition with the point at infinity (right hand side). + name: "P + ∞ = P", + x1: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y1: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + x2: "0", + y2: "0", + x3: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y3: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, { + // Addition with different x values. + name: "P(x1, y1) + P(x2, y2)", + x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + x2: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + y2: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + x3: "fd5b88c21d3143518d522cd2796f3d726793c88b3e05636bc829448e053fed69", + y3: "21cf4f6a5be5ff6380234c50424a970b1f7e718f5eb58f68198c108d642a137f", + }, { + // Addition with same x opposite y. + name: "P(x, y) + P(x, -y) = ∞", + x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y2: "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + x3: "0", + y3: "0", + }, { + // Addition with same point. + name: "P(x, y) + P(x, y) = 2P", + x1: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y1: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + x2: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + y2: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + x3: "59477d88ae64a104dbb8d31ec4ce2d91b2fe50fa628fb6a064e22582196b365b", + y3: "938dc8c0f13d1e75c987cb1a220501bd614b0d3dd9eb5c639847e1240216e3b6", + }, + } + curve := S256() + for _, test := range tests { + // Parse the test data. + x1, y1 := fromHex(test.x1), fromHex(test.y1) + x2, y2 := fromHex(test.x2), fromHex(test.y2) + x3, y3 := fromHex(test.x3), fromHex(test.y3) + // Ensure the test data is using points that are actually on the curve + // (or the point at infinity). + if !isValidAffinePoint(x1, y1) { + t.Errorf("%s: first point is not on curve", test.name) + continue + } + if !isValidAffinePoint(x2, y2) { + t.Errorf("%s: second point is not on curve", test.name) + continue + } + if !isValidAffinePoint(x3, y3) { + t.Errorf("%s: expected point is not on curve", test.name) + continue + } + // Add the two points and ensure the result matches expected. + rx, ry := curve.Add(x1, y1, x2, y2) + if rx.Cmp(x3) != 0 || ry.Cmp(y3) != 0 { + t.Errorf( + "%s: wrong result\ngot: (%x, %x)\nwant: (%x, %x)", + test.name, rx, ry, x3, y3, + ) + continue + } + } +} + +// TestDoubleAffineAdaptor tests doubling of points in affine coordinates via +// the method used to satisfy the elliptic.Curve interface works as intended for +// some edge cases and known good values. +func TestDoubleAffineAdaptor(t *testing.T) { + tests := []struct { + name string // test description + x1, y1 string // hex encoded coordinates of point to double + x3, y3 string // hex encoded coordinates of expected point + }{ + { + // Doubling the point at infinity is still the point at infinity. + name: "2*∞ = ∞ (point at infinity)", + x1: "0", + y1: "0", + x3: "0", + y3: "0", + }, { + name: "random point 1", + x1: "e41387ffd8baaeeb43c2faa44e141b19790e8ac1f7ff43d480dc132230536f86", + y1: "1b88191d430f559896149c86cbcb703193105e3cf3213c0c3556399836a2b899", + x3: "88da47a089d333371bd798c548ef7caae76e737c1980b452d367b3cfe3082c19", + y3: "3b6f659b09a362821dfcfefdbfbc2e59b935ba081b6c249eb147b3c2100b1bc1", + }, { + name: "random point 2", + x1: "b3589b5d984f03ef7c80aeae444f919374799edf18d375cab10489a3009cff0c", + y1: "c26cf343875b3630e15bccc61202815b5d8f1fd11308934a584a5babe69db36a", + x3: "e193860172998751e527bb12563855602a227fc1f612523394da53b746bb2fb1", + y3: "2bfcf13d2f5ab8bb5c611fab5ebbed3dc2f057062b39a335224c22f090c04789", + }, { + name: "random point 3", + x1: "2b31a40fbebe3440d43ac28dba23eee71c62762c3fe3dbd88b4ab82dc6a82340", + y1: "9ba7deb02f5c010e217607fd49d58db78ec273371ea828b49891ce2fd74959a1", + x3: "2c8d5ef0d343b1a1a48aa336078eadda8481cb048d9305dc4fdf7ee5f65973a2", + y3: "bb4914ac729e26d3cd8f8dc8f702f3f4bb7e0e9c5ae43335f6e94c2de6c3dc95", + }, { + name: "random point 4", + x1: "61c64b760b51981fab54716d5078ab7dffc93730b1d1823477e27c51f6904c7a", + y1: "ef6eb16ea1a36af69d7f66524c75a3a5e84c13be8fbc2e811e0563c5405e49bd", + x3: "5f0dcdd2595f5ad83318a0f9da481039e36f135005420393e72dfca985b482f4", + y3: "a01c849b0837065c1cb481b0932c441f49d1cab1b4b9f355c35173d93f110ae0", + }, + } + curve := S256() + for _, test := range tests { + // Parse test data. + x1, y1 := fromHex(test.x1), fromHex(test.y1) + x3, y3 := fromHex(test.x3), fromHex(test.y3) + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !isValidAffinePoint(x1, y1) { + t.Errorf("%s: first point is not on the curve", test.name) + continue + } + if !isValidAffinePoint(x3, y3) { + t.Errorf("%s: expected point is not on the curve", test.name) + continue + } + // Double the point and ensure the result matches expected. + rx, ry := curve.Double(x1, y1) + if rx.Cmp(x3) != 0 || ry.Cmp(y3) != 0 { + t.Errorf( + "%s: wrong result\ngot: (%x, %x)\nwant: (%x, %x)", + test.name, rx, ry, x3, y3, + ) + continue + } + } +} + +// TestScalarBaseMultAdaptor ensures the ScalarBaseMult method used to satisfy +// the elliptic.Curve interface works as intended for some edge cases and known +// good values. +func TestScalarBaseMultAdaptor(t *testing.T) { + tests := []struct { + name string // test description + k string // hex encoded scalar + rx, ry string // hex encoded coordinates of expected point + }{ + { + name: "zero", + k: "0000000000000000000000000000000000000000000000000000000000000000", + rx: "0000000000000000000000000000000000000000000000000000000000000000", + ry: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "one (aka 1*G = G)", + k: "0000000000000000000000000000000000000000000000000000000000000001", + rx: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + ry: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + }, { + name: "group order - 1 (aka -1*G = -G)", + k: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + rx: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + ry: "b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + }, { + name: "known good point 1", + k: "aa5e28d6a97a2479a65527f7290311a3624d4cc0fa1578598ee3c2613bf99522", + rx: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + ry: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + }, { + name: "known good point 2", + k: "7e2b897b8cebc6361663ad410835639826d590f393d90a9538881735256dfae3", + rx: "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + ry: "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, { + name: "known good point 3", + k: "6461e6df0fe7dfd05329f41bf771b86578143d4dd1f7866fb4ca7e97c5fa945d", + rx: "e8aecc370aedd953483719a116711963ce201ac3eb21d3f3257bb48668c6a72f", + ry: "c25caf2f0eba1ddb2f0f3f47866299ef907867b7d27e95b3873bf98397b24ee1", + }, { + name: "known good point 4", + k: "376a3a2cdcd12581efff13ee4ad44c4044b8a0524c42422a7e1e181e4deeccec", + rx: "14890e61fcd4b0bd92e5b36c81372ca6fed471ef3aa60a3e415ee4fe987daba1", + ry: "297b858d9f752ab42d3bca67ee0eb6dcd1c2b7b0dbe23397e66adc272263f982", + }, { + name: "known good point 5", + k: "1b22644a7be026548810c378d0b2994eefa6d2b9881803cb02ceff865287d1b9", + rx: "f73c65ead01c5126f28f442d087689bfa08e12763e0cec1d35b01751fd735ed3", + ry: "f449a8376906482a84ed01479bd18882b919c140d638307f0c0934ba12590bde", + }, + } + curve := S256() + for _, test := range tests { + // Parse the test data. + k := fromHex(test.k) + xWant, yWant := fromHex(test.rx), fromHex(test.ry) + // Ensure the test data is using points that are actually on the curve + // (or the point at infinity). + if !isValidAffinePoint(xWant, yWant) { + t.Errorf("%s: expected point is not on curve", test.name) + continue + } + rx, ry := curve.ScalarBaseMult(k.Bytes()) + if rx.Cmp(xWant) != 0 || ry.Cmp(yWant) != 0 { + t.Errorf( + "%s: wrong result:\ngot (%x, %x)\nwant (%x, %x)", + test.name, rx, ry, xWant, yWant, + ) + } + } +} + +// TestScalarBaseMultAdaptorRandom ensures that the ScalarBaseMult method used +// to satisfy the elliptic.Curve interface works as intended for +// randomly-generated scalars of all lengths up to 40 bytes. +func TestScalarBaseMultAdaptorRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + s256 := S256() + const maxBytes = 40 + const iterations = 10 + for numBytes := uint8(1); numBytes < maxBytes; numBytes++ { + for i := 0; i < iterations; i++ { + // Generate a random scalar of the current length. + k := randBytes(t, rng, numBytes) + // Ensure the correct results by performing the multiplication with + // both the func under test as well as the generic scalar mult func. + x, y := s256.ScalarBaseMult(k) + xWant, yWant := s256.ScalarMult(s256.Gx, s256.Gy, k) + if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 { + t.Errorf( + "bad output for %x: got (%x, %x), want (%x, %x)", k, + x, y, xWant, yWant, + ) + continue + } + } + } +} + +// TestScalarMultAdaptor ensures the ScalarMult method used to satisfy the +// elliptic.Curve interface works as intended for some edge cases and known good +// values. +func TestScalarMultAdaptor(t *testing.T) { + tests := []struct { + name string // test description + k string // hex encoded scalar + x, y string // hex encoded coordinates of point to multiply + rx, ry string // hex encoded coordinates of expected point + }{ + { + name: "0*P = ∞ (point at infinity)", + k: "0", + x: "7e660beda020e9cc20391cef85374576853b0f22b8925d5d81c5845bb834c21e", + y: "2d114a5edb320cc9806527d1daf1bbb96a8fedc6f9e8ead421eaef2c7208e409", + rx: "0", + ry: "0", + }, { + name: "1*P = P", + k: "1", + x: "c00be8830995d1e44f1420dd3b90d3441fb66f6861c84a35f959c495a3be5440", + y: "ecf9665e6eba45720de652a340600c7356efe24d228bfe6ea2043e7791c51bb7", + rx: "c00be8830995d1e44f1420dd3b90d3441fb66f6861c84a35f959c495a3be5440", + ry: "ecf9665e6eba45720de652a340600c7356efe24d228bfe6ea2043e7791c51bb7", + }, { + name: "(group order - 1)*P = -P (aka -1*P = -P)", + k: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + x: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9", + y: "cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08", + rx: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9", + ry: "339ea810e73639c329e6c27c9ce4415ff6c1f6976bd173cc2a8c80276f1f2127", + }, { + name: "(group order - 1)*-P = P (aka -1*-P = -P, with P from prev test)", + k: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + x: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9", + y: "339ea810e73639c329e6c27c9ce4415ff6c1f6976bd173cc2a8c80276f1f2127", + rx: "74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9", + ry: "cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08", + }, { + name: "known good point from base mult tests (aka k*G)", + k: "aa5e28d6a97a2479a65527f7290311a3624d4cc0fa1578598ee3c2613bf99522", + x: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + y: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + rx: "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + ry: "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + }, { + name: "known good result 1", + k: "7e2b897b8cebc6361663ad410835639826d590f393d90a9538881735256dfae3", + x: "1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5", + y: "b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396", + rx: "6951f3b50aafbc63e21707dd53623b7f42badd633a0567ef1b37f6e42a4237ad", + ry: "9c930796a49110122fbfdedc36418af726197ed950b783a2d29058f8c02130de", + }, { + name: "known good result 2", + k: "6461e6df0fe7dfd05329f41bf771b86578143d4dd1f7866fb4ca7e97c5fa945d", + x: "659214ac1a1790023f53c4cf55a0a63b9e20c1151efa971215b395a558aa151", + y: "b126363aa4243d2759320a356230569a4eea355d9dabd94ed7f4590701e5364d", + rx: "4ffad856833396ef753c0bd4ea40319295f107c476793df0adac2caea53b3df4", + ry: "586fa6b1e9a3ff7df8a2b9b3698badcf40aa06af5600fefc56dd8ae4db5451c5", + }, { + name: "known good result 3", + k: "376a3a2cdcd12581efff13ee4ad44c4044b8a0524c42422a7e1e181e4deeccec", + x: "3f0e80e574456d8f8fa64e044b2eb72ea22eb53fe1efe3a443933aca7f8cb0e3", + y: "cb66d7d7296cbc91e90b9c08485d01b39501253aa65b53a4cb0289e2ea5f404f", + rx: "35ae6480b18e48070709d9276ed97a50c6ee1fc05ac44386c85826533233d28f", + ry: "f88abee3efabd95e80ce8c664bbc3d4d12b24e1a0f4d2b98ba6542789c6715fd", + }, { + name: "known good result 4", + k: "1b22644a7be026548810c378d0b2994eefa6d2b9881803cb02ceff865287d1b9", + x: "d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e", + y: "581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58", + rx: "cca7f9a4b0d379c31c438050e163a8945f2f910498bd3b545be20ed862bd6cd9", + ry: "cfc7bbf37bef62da6e5753ed419168fa1376a3fe949c139a8dd0f5303f4ae947", + }, { + name: "known good result 5", + k: "7f5b2cb4b43840c75e4afad83d792e1965d8c21c1109505f45c7d46df422d73e", + x: "bce74de6d5f98dc027740c2bbff05b6aafe5fd8d103f827e48894a2bd3460117", + y: "5bea1fa17a41b115525a3e7dbf0d8d5a4f7ce5c6fc73a6f4f216512417c9f6b4", + rx: "3d96b9290fe6c4f2d62fe2175f4333907d0c3637fada1010b45c7d80690e16de", + ry: "d59c0e8192d7fbd4846172d6479630b751cd03d0d9be0dca2759c6212b70575d", + }, { + // From btcd issue #709. + name: "early implementation regression point", + k: "a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58", + x: "000000000000000000000000000000000000000000000000000000000000002c", + y: "420e7a99bba18a9d3952597510fd2b6728cfeafc21a4e73951091d4d8ddbe94e", + rx: "a2112dcdfbcd10ae1133a358de7b82db68e0a3eb4b492cc8268d1e7118c98788", + ry: "27fc7463b7bb3c5f98ecf2c84a6272bb1681ed553d92c69f2dfe25a9f9fd3836", + }, + } + curve := S256() + for _, test := range tests { + // Parse the test data. + k := fromHex(test.k) + x, y := fromHex(test.x), fromHex(test.y) + xWant, yWant := fromHex(test.rx), fromHex(test.ry) + // Ensure the test data is using points that are actually on the curve + // (or the point at infinity). + if !isValidAffinePoint(x, y) { + t.Errorf("%s: point is not on curve", test.name) + continue + } + if !isValidAffinePoint(xWant, yWant) { + t.Errorf("%s: expected point is not on curve", test.name) + continue + } + // Perform scalar point multiplication ensure the result matches + // expected. + rx, ry := curve.ScalarMult(x, y, k.Bytes()) + if rx.Cmp(xWant) != 0 || ry.Cmp(yWant) != 0 { + t.Errorf( + "%s: wrong result\ngot: (%x, %x)\nwant: (%x, %x)", + test.name, rx, ry, xWant, yWant, + ) + } + } +} diff --git a/pkg/crypto/ec/secp256k1/error.go b/pkg/crypto/ec/secp256k1/error.go new file mode 100644 index 0000000..88a7030 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/error.go @@ -0,0 +1,56 @@ +// Copyright (c) 2020 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 +// determining the reason for an error. +type ErrorKind string + +// These constants are used to identify a specific RuleError. +const ( + // ErrPubKeyInvalidLen indicates that the length of a serialized public + // key is not one of the allowed lengths. + ErrPubKeyInvalidLen = ErrorKind("ErrPubKeyInvalidLen") + // ErrPubKeyInvalidFormat indicates an attempt was made to parse a public + // key that does not specify one of the supported formats. + ErrPubKeyInvalidFormat = ErrorKind("ErrPubKeyInvalidFormat") + // ErrPubKeyXTooBig indicates that the x coordinate for a public key + // is greater than or equal to the prime of the field underlying the group. + ErrPubKeyXTooBig = ErrorKind("ErrPubKeyXTooBig") + // ErrPubKeyYTooBig indicates that the y coordinate for a public key is + // greater than or equal to the prime of the field underlying the group. + ErrPubKeyYTooBig = ErrorKind("ErrPubKeyYTooBig") + // ErrPubKeyNotOnCurve indicates that a public key is not a point on the + // secp256k1 curve. + ErrPubKeyNotOnCurve = ErrorKind("ErrPubKeyNotOnCurve") + // ErrPubKeyMismatchedOddness indicates that a hybrid public key specified + // an oddness of the y coordinate that does not match the actual oddness of + // the provided y coordinate. + ErrPubKeyMismatchedOddness = ErrorKind("ErrPubKeyMismatchedOddness") +) + +// Error satisfies the error interface and prints human-readable errors. +func (err ErrorKind) Error() string { return string(err) } + +// 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 +// caller can ascertain the specific reason for the error by checking +// the underlying error. +type Error struct { + Err error + Description string +} + +// Error satisfies the error interface and prints human-readable errors. +func (err Error) Error() string { return err.Description } + +// Unwrap returns the underlying wrapped error. +func (err Error) Unwrap() (ee error) { return err.Err } + +// makeError creates an Error given a set of arguments. +func makeError(kind ErrorKind, desc string) (err error) { + return Error{Err: kind, Description: desc} +} diff --git a/pkg/crypto/ec/secp256k1/error_test.go b/pkg/crypto/ec/secp256k1/error_test.go new file mode 100644 index 0000000..6baf2fe --- /dev/null +++ b/pkg/crypto/ec/secp256k1/error_test.go @@ -0,0 +1,136 @@ +// Copyright (c) 2020 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "errors" + "testing" +) + +// TestErrorKindStringer tests the stringized output for the ErrorKind type. +func TestErrorKindStringer(t *testing.T) { + tests := []struct { + in ErrorKind + want string + }{ + {ErrPubKeyInvalidLen, "ErrPubKeyInvalidLen"}, + {ErrPubKeyInvalidFormat, "ErrPubKeyInvalidFormat"}, + {ErrPubKeyXTooBig, "ErrPubKeyXTooBig"}, + {ErrPubKeyYTooBig, "ErrPubKeyYTooBig"}, + {ErrPubKeyNotOnCurve, "ErrPubKeyNotOnCurve"}, + {ErrPubKeyMismatchedOddness, "ErrPubKeyMismatchedOddness"}, + } + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("#%d: got: %s want: %s", i, result, test.want) + continue + } + } +} + +// TestError tests the error output for the Error type. +func TestError(t *testing.T) { + tests := []struct { + in Error + want string + }{ + { + Error{Description: "some error"}, + "some error", + }, { + Error{Description: "human-readable error"}, + "human-readable error", + }, + } + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("#%d: got: %s want: %s", i, result, test.want) + continue + } + } +} + +// TestErrorKindIsAs ensures both ErrorKind and Error can be identified as being +// a specific error kind via errors.Is and unwrapped via errors.As. +func TestErrorKindIsAs(t *testing.T) { + tests := []struct { + name string + err error + target error + wantMatch bool + wantAs ErrorKind + }{ + { + name: "ErrPubKeyInvalidLen == ErrPubKeyInvalidLen", + err: ErrPubKeyInvalidLen, + target: ErrPubKeyInvalidLen, + wantMatch: true, + wantAs: ErrPubKeyInvalidLen, + }, { + name: "Error.ErrPubKeyInvalidLen == ErrPubKeyInvalidLen", + err: makeError(ErrPubKeyInvalidLen, ""), + target: ErrPubKeyInvalidLen, + wantMatch: true, + wantAs: ErrPubKeyInvalidLen, + }, { + name: "Error.ErrPubKeyInvalidLen == Error.ErrPubKeyInvalidLen", + err: makeError(ErrPubKeyInvalidLen, ""), + target: makeError(ErrPubKeyInvalidLen, ""), + wantMatch: true, + wantAs: ErrPubKeyInvalidLen, + }, { + name: "ErrPubKeyInvalidFormat != ErrPubKeyInvalidLen", + err: ErrPubKeyInvalidFormat, + target: ErrPubKeyInvalidLen, + wantMatch: false, + wantAs: ErrPubKeyInvalidFormat, + }, { + name: "Error.ErrPubKeyInvalidFormat != ErrPubKeyInvalidLen", + err: makeError(ErrPubKeyInvalidFormat, ""), + target: ErrPubKeyInvalidLen, + wantMatch: false, + wantAs: ErrPubKeyInvalidFormat, + }, { + name: "ErrPubKeyInvalidFormat != Error.ErrPubKeyInvalidLen", + err: ErrPubKeyInvalidFormat, + target: makeError(ErrPubKeyInvalidLen, ""), + wantMatch: false, + wantAs: ErrPubKeyInvalidFormat, + }, { + name: "Error.ErrPubKeyInvalidFormat != Error.ErrPubKeyInvalidLen", + err: makeError(ErrPubKeyInvalidFormat, ""), + target: makeError(ErrPubKeyInvalidLen, ""), + wantMatch: false, + wantAs: ErrPubKeyInvalidFormat, + }, + } + for _, test := range tests { + // Ensure the error matches or not depending on the expected result. + result := errors.Is(test.err, test.target) + if result != test.wantMatch { + t.Errorf( + "%s: incorrect error identification -- got %v, want %v", + test.name, result, test.wantMatch, + ) + continue + } + // Ensure the underlying error code can be unwrapped and is the expected + // code. + var kind ErrorKind + if !errors.As(test.err, &kind) { + t.Errorf("%s: unable to unwrap to error code", test.name) + continue + } + if kind != test.wantAs { + t.Errorf( + "%s: unexpected unwrapped error code -- got %v, want %v", + test.name, kind, test.wantAs, + ) + continue + } + } +} diff --git a/pkg/crypto/ec/secp256k1/example_test.go b/pkg/crypto/ec/secp256k1/example_test.go new file mode 100644 index 0000000..482868a --- /dev/null +++ b/pkg/crypto/ec/secp256k1/example_test.go @@ -0,0 +1,132 @@ +// Copyright (c) 2014 The btcsuite developers +// Copyright (c) 2015-2020 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1_test + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/binary" + "fmt" + + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/crypto/sha256" + "next.orly.dev/pkg/encoders/hex" +) + +// 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 secret key. +func Example_encryptDecryptMessage() { + newAEAD := func(key []byte) (cipher.AEAD, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewGCM(block) + } + // Decode the hex-encoded pubkey of the recipient. + pubKeyBytes, err := hex.Dec( + "04115c42e757b2efb7671c578530ec191a1359381e6a71127a9d37c486fd30da" + + "e57e76dc58f693bd7e7010358ce6b165e483a2921010db67ac11b1b51b651953d2", + ) // uncompressed pubkey + if err != nil { + fmt.Println(err) + return + } + pubKey, err := secp256k1.ParsePubKey(pubKeyBytes) + if err != nil { + fmt.Println(err) + return + } + // Derive an ephemeral public/secret keypair for performing ECDHE with + // the recipient. + ephemeralSecKey, err := secp256k1.GenerateSecretKey() + if err != nil { + fmt.Println(err) + return + } + ephemeralPubKey := ephemeralSecKey.PubKey().SerializeCompressed() + // Using ECDHE, derive a shared symmetric key for encryption of the plaintext. + cipherKey := sha256.Sum256( + secp256k1.GenerateSharedSecret( + ephemeralSecKey, + pubKey, + ), + ) + // Seal the message using an AEAD. Here we use AES-256-GCM. + // The ephemeral public key must be included in this message, and becomes + // the authenticated data for the AEAD. + // + // Note that unless a unique nonce can be guaranteed, the ephemeral + // and/or shared keys must not be reused to encrypt different messages. + // Doing so destroys the security of the scheme. Random nonces may be + // used if XChaCha20-Poly1305 is used instead, but the message must then + // also encode the nonce (which we don't do here). + // + // Since a new ephemeral key is generated for every message ensuring there + // is no key reuse and AES-GCM permits the nonce to be used as a counter, + // the nonce is intentionally initialized to all zeros so it acts like the + // first (and only) use of a counter. + plaintext := []byte("test message") + aead, err := newAEAD(cipherKey[:]) + if err != nil { + fmt.Println(err) + return + } + nonce := make([]byte, aead.NonceSize()) + ciphertext := make([]byte, 4+len(ephemeralPubKey)) + binary.LittleEndian.PutUint32(ciphertext, uint32(len(ephemeralPubKey))) + copy(ciphertext[4:], ephemeralPubKey) + ciphertext = aead.Seal(ciphertext, nonce, plaintext, ephemeralPubKey) + // The remainder of this example is performed by the recipient on the + // ciphertext shared by the sender. + // + // Decode the hex-encoded secret key. + pkBytes, err := hex.Dec( + "a11b0a4e1a132305652ee7a8eb7848f6ad5ea381e3ce20a2c086a2e388230811", + ) + if err != nil { + fmt.Println(err) + return + } + secKey := secp256k1.SecKeyFromBytes(pkBytes) + // Read the sender's ephemeral public key from the start of the message. + // Error handling for inappropriate pubkey lengths is elided here for + // brevity. + pubKeyLen := binary.LittleEndian.Uint32(ciphertext[:4]) + senderPubKeyBytes := ciphertext[4 : 4+pubKeyLen] + senderPubKey, err := secp256k1.ParsePubKey(senderPubKeyBytes) + if err != nil { + fmt.Println(err) + return + } + // Derive the key used to seal the message, this time from the + // recipient's secret key and the sender's public key. + recoveredCipherKey := sha256.Sum256( + secp256k1.GenerateSharedSecret( + secKey, + senderPubKey, + ), + ) + // Open the sealed message. + aead, err = newAEAD(recoveredCipherKey[:]) + if err != nil { + fmt.Println(err) + return + } + nonce = make([]byte, aead.NonceSize()) + recoveredPlaintext, err := aead.Open( + nil, nonce, ciphertext[4+pubKeyLen:], + senderPubKeyBytes, + ) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(string(recoveredPlaintext)) + // Output: + // test message +} diff --git a/pkg/crypto/ec/secp256k1/field.go b/pkg/crypto/ec/secp256k1/field.go new file mode 100644 index 0000000..4cf12d1 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/field.go @@ -0,0 +1,1615 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2015-2023 The Decred developers +// Copyright (c) 2013-2023 Dave Collins +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +// References: +// [HAC]: Handbook of Applied Cryptography Menezes, van Oorschot, Vanstone. +// http://cacr.uwaterloo.ca/hac/ + +// All elliptic curve operations for secp256k1 are done in a finite field +// characterized by a 256-bit prime. Given this precision is larger than the +// biggest available native type, obviously some form of bignum math is needed. +// This package implements specialized fixed-precision field arithmetic rather +// than relying on an arbitrary-precision arithmetic package such as math/big +// for dealing with the field math since the size is known. As a result, rather +// large performance gains are achieved by taking advantage of many +// optimizations not available to arbitrary-precision arithmetic and generic +// modular arithmetic algorithms. +// +// There are various ways to internally represent each finite field element. +// For example, the most obvious representation would be to use an array of 4 +// uint64s (64 bits * 4 = 256 bits). However, that representation suffers from +// a couple of issues. First, there is no native Go type large enough to handle +// the intermediate results while adding or multiplying two 64-bit numbers, and +// second there is no space left for overflows when performing the intermediate +// arithmetic between each array element which would lead to expensive carry +// propagation. +// +// Given the above, this implementation represents the field elements as +// 10 uint32s with each word (array entry) treated as base 2^26. This was +// chosen for the following reasons: +// 1) Most systems at the current time are 64-bit (or at least have 64-bit +// registers available for specialized purposes such as MMX) so the +// intermediate results can typically be done using a native register (and +// using uint64s to avoid the need for additional half-word arithmetic) +// 2) In order to allow addition of the internal words without having to +// propagate the carry, the max normalized value for each register must +// be less than the number of bits available in the register +// 3) Since we're dealing with 32-bit values, 64-bits of overflow is a +// reasonable choice for #2 +// 4) Given the need for 256-bits of precision and the properties stated in #1, +// #2, and #3, the representation which best accommodates this is 10 uint32s +// with base 2^26 (26 bits * 10 = 260 bits, so the final word only needs 22 +// bits) which leaves the desired 64 bits (32 * 10 = 320, 320 - 256 = 64) for +// overflow +// +// 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. + +import ( + "next.orly.dev/pkg/encoders/hex" +) + +// Constants used to make the code more readable. +const ( + twoBitsMask = 0x3 + fourBitsMask = 0xf + sixBitsMask = 0x3f + eightBitsMask = 0xff +) + +// Constants related to the field representation. +const ( + // fieldWords is the number of words used to internally represent the + // 256-bit value. + fieldWords = 10 + // fieldBase is the exponent used to form the numeric base of each word. + // 2^(fieldBase*i) where i is the word position. + fieldBase = 26 + // fieldBaseMask is the mask for the bits in each word needed to + // represent the numeric base of each word (except the most significant + // word). + fieldBaseMask = (1 << fieldBase) - 1 + // fieldMSBBits is the number of bits in the most significant word used + // to represent the value. + fieldMSBBits = 256 - (fieldBase * (fieldWords - 1)) + // fieldMSBMask is the mask for the bits in the most significant word + // needed to represent the value. + fieldMSBMask = (1 << fieldMSBBits) - 1 + // These fields provide convenient access to each of the words of the + // secp256k1 prime in the internal field representation to improve code + // readability. + fieldPrimeWordZero = 0x03fffc2f + fieldPrimeWordOne = 0x03ffffbf + fieldPrimeWordTwo = 0x03ffffff + fieldPrimeWordThree = 0x03ffffff + fieldPrimeWordFour = 0x03ffffff + fieldPrimeWordFive = 0x03ffffff + fieldPrimeWordSix = 0x03ffffff + fieldPrimeWordSeven = 0x03ffffff + fieldPrimeWordEight = 0x03ffffff + fieldPrimeWordNine = 0x003fffff +) + +// FieldVal implements optimized fixed-precision arithmetic over the +// secp256k1 finite field. This means all arithmetic is performed modulo +// +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f. +// +// WARNING: Since it is so important for the field arithmetic to be extremely +// fast for high performance crypto, this type does not perform any validation +// of documented preconditions where it ordinarily would. As a result, it is +// IMPERATIVE for callers to understand some key concepts that are described +// below and ensure the methods are called with the necessary preconditions that +// each method is documented with. For example, some methods only give the +// correct result if the field value is normalized and others require the field +// values involved to have a maximum magnitude and THERE ARE NO EXPLICIT CHECKS +// TO ENSURE THOSE PRECONDITIONS ARE SATISFIED. This does, unfortunately, make +// the type more difficult to use correctly and while I typically prefer to +// ensure all state and input is valid for most code, this is a bit of an +// exception because those extra checks really add up in what ends up being +// critical hot paths. +// +// The first key concept when working with this type is normalization. In order +// to avoid the need to propagate a ton of carries, the internal representation +// provides additional overflow bits for each word of the overall 256-bit value. +// This means that there are multiple internal representations for the same +// value and, as a result, any methods that rely on comparison of the value, +// such as equality and oddness determination, require the caller to provide a +// normalized value. +// +// The second key concept when working with this type is magnitude. As +// previously mentioned, the internal representation provides additional +// overflow bits which means that the more math operations that are performed on +// the field value between normalizations, the more those overflow bits +// accumulate. The magnitude is effectively that maximum possible number of +// those overflow bits that could possibly be required as a result of a given +// operation. Since there are only a limited number of overflow bits available, +// this implies that the max possible magnitude MUST be tracked by the caller +// and the caller MUST normalize the field value if a given operation would +// cause the magnitude of the result to exceed the max allowed value. +// +// IMPORTANT: The max allowed magnitude of a field value is 64. +type FieldVal struct { + // Each 256-bit value is represented as 10 32-bit integers in base 2^26. + // This provides 6 bits of overflow in each word (10 bits in the most + // significant word) for a total of 64 bits of overflow (9*6 + 10 = 64). It + // only implements the arithmetic needed for elliptic curve operations. + // + // The following depicts the internal representation: + // ----------------------------------------------------------------- + // | n[9] | n[8] | ... | n[0] | + // | 32 bits available | 32 bits available | ... | 32 bits available | + // | 22 bits for value | 26 bits for value | ... | 26 bits for value | + // | 10 bits overflow | 6 bits overflow | ... | 6 bits overflow | + // | Mult: 2^(26*9) | Mult: 2^(26*8) | ... | Mult: 2^(26*0) | + // ----------------------------------------------------------------- + // + // For example, consider the number 2^49 + 1. It would be represented as: + // n[0] = 1 + // n[1] = 2^23 + // n[2..9] = 0 + // + // The full 256-bit value is then calculated by looping i from 9..0 and + // doing sum(n[i] * 2^(26i)) like so: + // n[9] * 2^(26*9) = 0 * 2^234 = 0 + // n[8] * 2^(26*8) = 0 * 2^208 = 0 + // ... + // n[1] * 2^(26*1) = 2^23 * 2^26 = 2^49 + // n[0] * 2^(26*0) = 1 * 2^0 = 1 + // Sum: 0 + 0 + ... + 2^49 + 1 = 2^49 + 1 + n [10]uint32 +} + +// String returns the field value as a normalized human-readable hex string. +// +// Preconditions: None +// Output Normalized: Field is not modified -- same as input value +// Output Max Magnitude: Field is not modified -- same as input value +func (f FieldVal) String() string { + // f is a copy, so it's safe to normalize it without mutating the original. + f.Normalize() + return hex.Enc(f.Bytes()[:]) +} + +// Zero sets the field value to zero in constant time. A newly created field +// value is already set to zero. This function can be useful to clear an +// existing field value for reuse. +// +// Preconditions: None +// Output Normalized: Yes +// Output Max Magnitude: 1 +func (f *FieldVal) Zero() { + f.n[0] = 0 + f.n[1] = 0 + f.n[2] = 0 + f.n[3] = 0 + f.n[4] = 0 + f.n[5] = 0 + f.n[6] = 0 + f.n[7] = 0 + f.n[8] = 0 + f.n[9] = 0 +} + +// Set sets the field value equal to the passed value in constant time. The +// normalization and magnitude of the two fields will be identical. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(FieldVal).Set(f2).Add(1) so that f = f2 + 1 where f2 is not +// modified. +// +// Preconditions: None +// Output Normalized: Same as input value +// Output Max Magnitude: Same as input value +func (f *FieldVal) Set(val *FieldVal) *FieldVal { + *f = *val + return f +} + +// SetInt sets the field value to the passed integer in constant time. This is +// a convenience function since it is fairly common to perform some arithmetic +// with small native integers. +// +// The field value is returned to support chaining. This enables syntax such +// as f := new(FieldVal).SetInt(2).Mul(f2) so that f = 2 * f2. +// +// Preconditions: None +// Output Normalized: Yes +// Output Max Magnitude: 1 +func (f *FieldVal) SetInt(ui uint16) *FieldVal { + f.Zero() + f.n[0] = uint32(ui) + return f +} + +// 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. +// +// 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. +// +// Preconditions: None +// Output Normalized: Yes if no overflow, no otherwise +// Output Max Magnitude: 1 +func (f *FieldVal) SetBytes(b *[32]byte) uint32 { + // Pack the 256 total bits across the 10 uint32 words with a max of + // 26-bits per word. This could be done with a couple of for loops, + // but this unrolled version is significantly faster. Benchmarks show + // this is about 34 times faster than the variant which uses loops. + f.n[0] = uint32(b[31]) | uint32(b[30])<<8 | uint32(b[29])<<16 | + (uint32(b[28])&twoBitsMask)<<24 + f.n[1] = uint32(b[28])>>2 | uint32(b[27])<<6 | uint32(b[26])<<14 | + (uint32(b[25])&fourBitsMask)<<22 + f.n[2] = uint32(b[25])>>4 | uint32(b[24])<<4 | uint32(b[23])<<12 | + (uint32(b[22])&sixBitsMask)<<20 + f.n[3] = uint32(b[22])>>6 | uint32(b[21])<<2 | uint32(b[20])<<10 | + uint32(b[19])<<18 + f.n[4] = uint32(b[18]) | uint32(b[17])<<8 | uint32(b[16])<<16 | + (uint32(b[15])&twoBitsMask)<<24 + f.n[5] = uint32(b[15])>>2 | uint32(b[14])<<6 | uint32(b[13])<<14 | + (uint32(b[12])&fourBitsMask)<<22 + f.n[6] = uint32(b[12])>>4 | uint32(b[11])<<4 | uint32(b[10])<<12 | + (uint32(b[9])&sixBitsMask)<<20 + f.n[7] = uint32(b[9])>>6 | uint32(b[8])<<2 | uint32(b[7])<<10 | + uint32(b[6])<<18 + f.n[8] = uint32(b[5]) | uint32(b[4])<<8 | uint32(b[3])<<16 | + (uint32(b[2])&twoBitsMask)<<24 + f.n[9] = uint32(b[2])>>2 | uint32(b[1])<<6 | uint32(b[0])<<14 + // The intuition here is that the field value is greater than the prime if + // one of the higher individual words is greater than corresponding word of + // the prime and all higher words in the field value are equal to their + // corresponding word of the prime. Since this type is modulo the prime, + // being equal is also an overflow back to 0. + // + // Note that because the input is 32 bytes and it was just packed into the + // field representation, the only words that can possibly be greater are + // zero and one, because ceil(log_2(2^256 - 1 - P)) = 33 bits max and the + // internal field representation encodes 26 bits with each word. + // + // Thus, there is no need to test if the upper words of the field value + // exceeds them, hence, only equality is checked for them. + highWordsEq := constantTimeEq(f.n[9], fieldPrimeWordNine) + highWordsEq &= constantTimeEq(f.n[8], fieldPrimeWordEight) + highWordsEq &= constantTimeEq(f.n[7], fieldPrimeWordSeven) + highWordsEq &= constantTimeEq(f.n[6], fieldPrimeWordSix) + highWordsEq &= constantTimeEq(f.n[5], fieldPrimeWordFive) + highWordsEq &= constantTimeEq(f.n[4], fieldPrimeWordFour) + highWordsEq &= constantTimeEq(f.n[3], fieldPrimeWordThree) + highWordsEq &= constantTimeEq(f.n[2], fieldPrimeWordTwo) + overflow := highWordsEq & constantTimeGreater(f.n[1], fieldPrimeWordOne) + highWordsEq &= constantTimeEq(f.n[1], fieldPrimeWordOne) + overflow |= highWordsEq & constantTimeGreaterOrEq( + f.n[0], + fieldPrimeWordZero, + ) + return overflow +} + +// SetByteSlice interprets the provided slice as a 256-bit big-endian unsigned +// integer (meaning it is truncated to the first 32 bytes), packs it into the +// internal field value representation, and returns whether or not the resulting +// truncated 256-bit integer is greater than or equal to the field prime (aka it +// overflowed) in constant time. +// +// Note that since passing a slice with more than 32 bytes is truncated, it is +// possible that the truncated value is less than the field prime and hence it +// will not be reported as having overflowed in that case. It is up to the +// caller to decide whether it needs to provide numbers of the appropriate size +// or it if is acceptable to use this function with the described truncation and +// overflow behavior. +// +// Preconditions: None +// Output Normalized: Yes if no overflow, no otherwise +// Output Max Magnitude: 1 +func (f *FieldVal) SetByteSlice(b []byte) bool { + var b32 [32]byte + b = b[:constantTimeMin(uint32(len(b)), 32)] + copy(b32[:], b32[:32-len(b)]) + copy(b32[32-len(b):], b) + result := f.SetBytes(&b32) + zeroArray32(&b32) + return result != 0 +} + +// Normalize normalizes the internal field words into the desired range and +// performs fast modular reduction over the secp256k1 prime by making use of the +// special form of the prime in constant time. +// +// Preconditions: None +// Output Normalized: Yes +// Output Max Magnitude: 1 +func (f *FieldVal) Normalize() *FieldVal { + // The field representation leaves 6 bits of overflow in each word so + // intermediate calculations can be performed without needing to + // propagate the carry to each higher word during the calculations. In + // order to normalize, we need to "compact" the full 256-bit value to + // the right while propagating any carries through to the high order + // word. + // + // Since this field is doing arithmetic modulo the secp256k1 prime, we + // also need to perform modular reduction over the prime. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // The algorithm presented in the referenced section typically repeats + // until the quotient is zero. However, due to our field representation + // we already know to within one reduction how many times we would need + // to repeat as it's the uppermost bits of the high order word. Thus we + // can simply multiply the magnitude by the field representation of the + // prime and do a single iteration. After this step there might be an + // additional carry to bit 256 (bit 22 of the high order word). + t9 := f.n[9] + m := t9 >> fieldMSBBits + t9 = t9 & fieldMSBMask + t0 := f.n[0] + m*977 + t1 := (t0 >> fieldBase) + f.n[1] + (m << 6) + t0 = t0 & fieldBaseMask + t2 := (t1 >> fieldBase) + f.n[2] + t1 = t1 & fieldBaseMask + t3 := (t2 >> fieldBase) + f.n[3] + t2 = t2 & fieldBaseMask + t4 := (t3 >> fieldBase) + f.n[4] + t3 = t3 & fieldBaseMask + t5 := (t4 >> fieldBase) + f.n[5] + t4 = t4 & fieldBaseMask + t6 := (t5 >> fieldBase) + f.n[6] + t5 = t5 & fieldBaseMask + t7 := (t6 >> fieldBase) + f.n[7] + t6 = t6 & fieldBaseMask + t8 := (t7 >> fieldBase) + f.n[8] + t7 = t7 & fieldBaseMask + t9 = (t8 >> fieldBase) + t9 + t8 = t8 & fieldBaseMask + // At this point, the magnitude is guaranteed to be one, however, the + // value could still be greater than the prime if there was either a + // carry through to bit 256 (bit 22 of the higher order word) or the + // value is greater than or equal to the field characteristic. The + // following determines if either or these conditions are true and does + // the final reduction in constant time. + // + // Also note that 'm' will be zero when neither of the aforementioned + // conditions are true and the value will not be changed when 'm' is zero. + m = constantTimeEq(t9, fieldMSBMask) + m &= constantTimeEq(t8&t7&t6&t5&t4&t3&t2, fieldBaseMask) + m &= constantTimeGreater(t1+64+((t0+977)>>fieldBase), fieldBaseMask) + m |= t9 >> fieldMSBBits + t0 = t0 + m*977 + t1 = (t0 >> fieldBase) + t1 + (m << 6) + t0 = t0 & fieldBaseMask + t2 = (t1 >> fieldBase) + t2 + t1 = t1 & fieldBaseMask + t3 = (t2 >> fieldBase) + t3 + t2 = t2 & fieldBaseMask + t4 = (t3 >> fieldBase) + t4 + t3 = t3 & fieldBaseMask + t5 = (t4 >> fieldBase) + t5 + t4 = t4 & fieldBaseMask + t6 = (t5 >> fieldBase) + t6 + t5 = t5 & fieldBaseMask + t7 = (t6 >> fieldBase) + t7 + t6 = t6 & fieldBaseMask + t8 = (t7 >> fieldBase) + t8 + t7 = t7 & fieldBaseMask + t9 = (t8 >> fieldBase) + t9 + t8 = t8 & fieldBaseMask + t9 = t9 & fieldMSBMask // Remove potential multiple of 2^256. + // Finally, set the normalized and reduced words. + f.n[0] = t0 + f.n[1] = t1 + f.n[2] = t2 + f.n[3] = t3 + f.n[4] = t4 + f.n[5] = t5 + f.n[6] = t6 + f.n[7] = t7 + f.n[8] = t8 + f.n[9] = t9 + return f +} + +// PutBytesUnchecked unpacks the field value to a 32-byte big-endian value +// 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. +// +// Preconditions: +// - The field value MUST be normalized +// - The target slice MUST have at least 32 bytes available +func (f *FieldVal) PutBytesUnchecked(b []byte) { + // Unpack the 256 total bits from the 10 uint32 words with a max of + // 26-bits per word. This could be done with a couple of for loops, + // but this unrolled version is a bit faster. Benchmarks show this is + // about 10 times faster than the variant which uses loops. + b[31] = byte(f.n[0] & eightBitsMask) + b[30] = byte((f.n[0] >> 8) & eightBitsMask) + b[29] = byte((f.n[0] >> 16) & eightBitsMask) + b[28] = byte((f.n[0]>>24)&twoBitsMask | (f.n[1]&sixBitsMask)<<2) + b[27] = byte((f.n[1] >> 6) & eightBitsMask) + b[26] = byte((f.n[1] >> 14) & eightBitsMask) + b[25] = byte((f.n[1]>>22)&fourBitsMask | (f.n[2]&fourBitsMask)<<4) + b[24] = byte((f.n[2] >> 4) & eightBitsMask) + b[23] = byte((f.n[2] >> 12) & eightBitsMask) + b[22] = byte((f.n[2]>>20)&sixBitsMask | (f.n[3]&twoBitsMask)<<6) + b[21] = byte((f.n[3] >> 2) & eightBitsMask) + b[20] = byte((f.n[3] >> 10) & eightBitsMask) + b[19] = byte((f.n[3] >> 18) & eightBitsMask) + b[18] = byte(f.n[4] & eightBitsMask) + b[17] = byte((f.n[4] >> 8) & eightBitsMask) + b[16] = byte((f.n[4] >> 16) & eightBitsMask) + b[15] = byte((f.n[4]>>24)&twoBitsMask | (f.n[5]&sixBitsMask)<<2) + b[14] = byte((f.n[5] >> 6) & eightBitsMask) + b[13] = byte((f.n[5] >> 14) & eightBitsMask) + b[12] = byte((f.n[5]>>22)&fourBitsMask | (f.n[6]&fourBitsMask)<<4) + b[11] = byte((f.n[6] >> 4) & eightBitsMask) + b[10] = byte((f.n[6] >> 12) & eightBitsMask) + b[9] = byte((f.n[6]>>20)&sixBitsMask | (f.n[7]&twoBitsMask)<<6) + b[8] = byte((f.n[7] >> 2) & eightBitsMask) + b[7] = byte((f.n[7] >> 10) & eightBitsMask) + b[6] = byte((f.n[7] >> 18) & eightBitsMask) + b[5] = byte(f.n[8] & eightBitsMask) + b[4] = byte((f.n[8] >> 8) & eightBitsMask) + b[3] = byte((f.n[8] >> 16) & eightBitsMask) + b[2] = byte((f.n[8]>>24)&twoBitsMask | (f.n[9]&sixBitsMask)<<2) + b[1] = byte((f.n[9] >> 6) & eightBitsMask) + b[0] = byte((f.n[9] >> 14) & eightBitsMask) +} + +// 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. +// +// 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. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) PutBytes(b *[32]byte) { f.PutBytesUnchecked(b[:]) } + +// 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. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) Bytes() *[32]byte { + b := new([32]byte) + f.PutBytesUnchecked(b[:]) + return b +} + +// IsZeroBit returns 1 when the field value is equal to zero 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 +// operations require a numeric value. See IsZero for the version that returns +// a bool. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) IsZeroBit() uint32 { + // The value can only be zero if no bits are set in any of the words. + // This is a constant time implementation. + bits := f.n[0] | f.n[1] | f.n[2] | f.n[3] | f.n[4] | + f.n[5] | f.n[6] | f.n[7] | f.n[8] | f.n[9] + return constantTimeEq(bits, 0) +} + +// IsZero returns whether or not the field value is equal to zero in constant +// time. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) IsZero() bool { + // The value can only be zero if no bits are set in any of the words. + // This is a constant time implementation. + bits := f.n[0] | f.n[1] | f.n[2] | f.n[3] | f.n[4] | + f.n[5] | f.n[6] | f.n[7] | f.n[8] | f.n[9] + return bits == 0 +} + +// IsOneBit returns 1 when the field value is equal to one 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 +// operations require a numeric value. See IsOne for the version that returns a +// bool. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) IsOneBit() uint32 { + // The value can only be one if the single lowest significant bit is set in + // the first word and no other bits are set in any of the other words. + // This is a constant time implementation. + bits := (f.n[0] ^ 1) | f.n[1] | f.n[2] | f.n[3] | f.n[4] | f.n[5] | + f.n[6] | f.n[7] | f.n[8] | f.n[9] + + return constantTimeEq(bits, 0) +} + +// IsOne returns whether the field value is equal to one in constant +// time. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) IsOne() bool { + // The value can only be one if the single lowest significant bit is set in + // the first word and no other bits are set in any of the other words. + // This is a constant time implementation. + bits := (f.n[0] ^ 1) | f.n[1] | f.n[2] | f.n[3] | f.n[4] | f.n[5] | + f.n[6] | f.n[7] | f.n[8] | f.n[9] + + return bits == 0 +} + +// IsOddBit returns 1 when the field value is an odd number 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 +// operations require a numeric value. See IsOdd for the version that returns a +// bool. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) IsOddBit() uint32 { + // Only odd numbers have the bottom bit set. + return f.n[0] & 1 +} + +// IsOdd returns whether the field value is an odd number in constant +// time. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) IsOdd() bool { + // Only odd numbers have the bottom bit set. + return f.n[0]&1 == 1 +} + +// Equals returns whether or not the two field values are the same in constant +// time. +// +// Preconditions: +// - Both field values being compared MUST be normalized +func (f *FieldVal) Equals(val *FieldVal) bool { + // Xor only sets bits when they are different, so the two field values + // can only be the same if no bits are set after xoring each word. + // This is a constant time implementation. + bits := (f.n[0] ^ val.n[0]) | (f.n[1] ^ val.n[1]) | (f.n[2] ^ val.n[2]) | + (f.n[3] ^ val.n[3]) | (f.n[4] ^ val.n[4]) | (f.n[5] ^ val.n[5]) | + (f.n[6] ^ val.n[6]) | (f.n[7] ^ val.n[7]) | (f.n[8] ^ val.n[8]) | + (f.n[9] ^ val.n[9]) + + return bits == 0 +} + +// NegateVal negates the passed value and stores the result in f in constant +// time. The caller must provide the magnitude of the passed value for a +// correct result. +// +// The field value is returned to support chaining. This enables syntax like: +// f.NegateVal(f2).AddInt(1) so that f = -f2 + 1. +// +// Preconditions: +// - The max magnitude MUST be 63 +// Output Normalized: No +// Output Max Magnitude: Input magnitude + 1 +func (f *FieldVal) NegateVal(val *FieldVal, magnitude uint32) *FieldVal { + // Negation in the field is just the prime minus the value. However, + // in order to allow negation against a field value without having to + // normalize/reduce it first, multiply by the magnitude (that is how + // "far" away it is from the normalized value) to adjust. Also, since + // negating a value pushes it one more order of magnitude away from the + // normalized range, add 1 to compensate. + // + // For some intuition here, imagine you're performing mod 12 arithmetic + // (picture a clock) and you are negating the number 7. So you start at + // 12 (which is of course 0 under mod 12) and count backwards (left on + // the clock) 7 times to arrive at 5. Notice this is just 12-7 = 5. + // Now, assume you're starting with 19, which is a number that is + // already larger than the modulus and congruent to 7 (mod 12). When a + // value is already in the desired range, its magnitude is 1. Since 19 + // is an additional "step", its magnitude (mod 12) is 2. Since any + // multiple of the modulus is congruent to zero (mod m), the answer can + // be shortcut by simply multiplying the magnitude by the modulus and + // subtracting. Keeping with the example, this would be (2*12)-19 = 5. + f.n[0] = (magnitude+1)*fieldPrimeWordZero - val.n[0] + f.n[1] = (magnitude+1)*fieldPrimeWordOne - val.n[1] + f.n[2] = (magnitude+1)*fieldBaseMask - val.n[2] + f.n[3] = (magnitude+1)*fieldBaseMask - val.n[3] + f.n[4] = (magnitude+1)*fieldBaseMask - val.n[4] + f.n[5] = (magnitude+1)*fieldBaseMask - val.n[5] + f.n[6] = (magnitude+1)*fieldBaseMask - val.n[6] + f.n[7] = (magnitude+1)*fieldBaseMask - val.n[7] + f.n[8] = (magnitude+1)*fieldBaseMask - val.n[8] + f.n[9] = (magnitude+1)*fieldMSBMask - val.n[9] + return f +} + +// Negate negates the field value in constant time. The existing field value is +// modified. The caller must provide the magnitude of the field value for a +// correct result. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Negate().AddInt(1) so that f = -f + 1. +// +// Preconditions: +// - The max magnitude MUST be 63 +// Output Normalized: No +// Output Max Magnitude: Input magnitude + 1 +func (f *FieldVal) Negate(magnitude uint32) *FieldVal { + return f.NegateVal(f, magnitude) +} + +// AddInt adds the passed integer to the existing field value and stores the +// result in f in constant time. This is a convenience function since it is +// fairly common to perform some arithmetic with small native integers. +// +// The field value is returned to support chaining. This enables syntax like: +// f.AddInt(1).Add(f2) so that f = f + 1 + f2. +// +// Preconditions: +// - The field value MUST have a max magnitude of 63 +// Output Normalized: No +// Output Max Magnitude: Existing field magnitude + 1 +func (f *FieldVal) AddInt(ui uint16) *FieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // the word and will be normalized out. + f.n[0] += uint32(ui) + return f +} + +// Add adds the passed value to the existing field value and stores the result +// in f in constant time. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Add(f2).AddInt(1) so that f = f + f2 + 1. +// +// Preconditions: +// - The sum of the magnitudes of the two field values MUST be a max of 64 +// Output Normalized: No +// Output Max Magnitude: Sum of the magnitude of the two individual field values +func (f *FieldVal) Add(val *FieldVal) *FieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // each word and will be normalized out. This could obviously be done + // in a loop, but the unrolled version is faster. + f.n[0] += val.n[0] + f.n[1] += val.n[1] + f.n[2] += val.n[2] + f.n[3] += val.n[3] + f.n[4] += val.n[4] + f.n[5] += val.n[5] + f.n[6] += val.n[6] + f.n[7] += val.n[7] + f.n[8] += val.n[8] + f.n[9] += val.n[9] + return f +} + +// Add2 adds the passed two field values together and stores the result in f in +// constant time. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.Add2(f, f2).AddInt(1) so that f3 = f + f2 + 1. +// +// Preconditions: +// - The sum of the magnitudes of the two field values MUST be a max of 64 +// Output Normalized: No +// Output Max Magnitude: Sum of the magnitude of the two field values +func (f *FieldVal) Add2(val *FieldVal, val2 *FieldVal) *FieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // each word and will be normalized out. This could obviously be done + // in a loop, but the unrolled version is faster. + f.n[0] = val.n[0] + val2.n[0] + f.n[1] = val.n[1] + val2.n[1] + f.n[2] = val.n[2] + val2.n[2] + f.n[3] = val.n[3] + val2.n[3] + f.n[4] = val.n[4] + val2.n[4] + f.n[5] = val.n[5] + val2.n[5] + f.n[6] = val.n[6] + val2.n[6] + f.n[7] = val.n[7] + val2.n[7] + f.n[8] = val.n[8] + val2.n[8] + f.n[9] = val.n[9] + val2.n[9] + return f +} + +// MulInt multiplies the field value by the passed int and stores the result in +// f in constant time. Note that this function can overflow if multiplying the +// value by any of the individual words exceeds a max uint32. Therefore it is +// important that the caller ensures no overflows will occur before using this +// function. +// +// The field value is returned to support chaining. This enables syntax like: +// f.MulInt(2).Add(f2) so that f = 2 * f + f2. +// +// Preconditions: +// - The field value magnitude multiplied by given val MUST be a max of 64 +// Output Normalized: No +// Output Max Magnitude: Existing field magnitude times the provided integer val +func (f *FieldVal) MulInt(val uint8) *FieldVal { + // Since each word of the field representation can hold up to + // 32 - fieldBase extra bits which will be normalized out, it's safe + // to multiply each word without using a larger type or carry + // propagation so long as the values won't overflow a uint32. This + // could obviously be done in a loop, but the unrolled version is + // faster. + ui := uint32(val) + f.n[0] *= ui + f.n[1] *= ui + f.n[2] *= ui + f.n[3] *= ui + f.n[4] *= ui + f.n[5] *= ui + f.n[6] *= ui + f.n[7] *= ui + f.n[8] *= ui + f.n[9] *= ui + return f +} + +// Mul multiplies the passed value to the existing field value and stores the +// result in f in constant time. Note that this function can overflow if +// multiplying any of the individual words exceeds a max uint32. In practice, +// this means the magnitude of either value involved in the multiplication must +// be a max of 8. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Mul(f2).AddInt(1) so that f = (f * f2) + 1. +// +// Preconditions: +// - Both field values MUST have a max magnitude of 8 +// Output Normalized: No +// Output Max Magnitude: 1 +func (f *FieldVal) Mul(val *FieldVal) *FieldVal { + return f.Mul2(f, val) +} + +// Mul2 multiplies the passed two field values together and stores the result in +// f in constant time. Note that this function can overflow if multiplying any +// of the individual words exceeds a max uint32. In practice, this means the +// magnitude of either value involved in the multiplication must be a max of 8. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.Mul2(f, f2).AddInt(1) so that f3 = (f * f2) + 1. +// +// Preconditions: +// - Both input field values MUST have a max magnitude of 8 +// Output Normalized: No +// Output Max Magnitude: 1 +func (f *FieldVal) Mul2(val *FieldVal, val2 *FieldVal) *FieldVal { + // This could be done with a couple of for loops and an array to store + // the intermediate terms, but this unrolled version is significantly + // faster. + // Terms for 2^(fieldBase*0). + m := uint64(val.n[0]) * uint64(val2.n[0]) + t0 := m & fieldBaseMask + // Terms for 2^(fieldBase*1). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[1]) + + uint64(val.n[1])*uint64(val2.n[0]) + t1 := m & fieldBaseMask + // Terms for 2^(fieldBase*2). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[2]) + + uint64(val.n[1])*uint64(val2.n[1]) + + uint64(val.n[2])*uint64(val2.n[0]) + t2 := m & fieldBaseMask + // Terms for 2^(fieldBase*3). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[3]) + + uint64(val.n[1])*uint64(val2.n[2]) + + uint64(val.n[2])*uint64(val2.n[1]) + + uint64(val.n[3])*uint64(val2.n[0]) + t3 := m & fieldBaseMask + // Terms for 2^(fieldBase*4). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[4]) + + uint64(val.n[1])*uint64(val2.n[3]) + + uint64(val.n[2])*uint64(val2.n[2]) + + uint64(val.n[3])*uint64(val2.n[1]) + + uint64(val.n[4])*uint64(val2.n[0]) + t4 := m & fieldBaseMask + // Terms for 2^(fieldBase*5). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[5]) + + uint64(val.n[1])*uint64(val2.n[4]) + + uint64(val.n[2])*uint64(val2.n[3]) + + uint64(val.n[3])*uint64(val2.n[2]) + + uint64(val.n[4])*uint64(val2.n[1]) + + uint64(val.n[5])*uint64(val2.n[0]) + t5 := m & fieldBaseMask + // Terms for 2^(fieldBase*6). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[6]) + + uint64(val.n[1])*uint64(val2.n[5]) + + uint64(val.n[2])*uint64(val2.n[4]) + + uint64(val.n[3])*uint64(val2.n[3]) + + uint64(val.n[4])*uint64(val2.n[2]) + + uint64(val.n[5])*uint64(val2.n[1]) + + uint64(val.n[6])*uint64(val2.n[0]) + t6 := m & fieldBaseMask + // Terms for 2^(fieldBase*7). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[7]) + + uint64(val.n[1])*uint64(val2.n[6]) + + uint64(val.n[2])*uint64(val2.n[5]) + + uint64(val.n[3])*uint64(val2.n[4]) + + uint64(val.n[4])*uint64(val2.n[3]) + + uint64(val.n[5])*uint64(val2.n[2]) + + uint64(val.n[6])*uint64(val2.n[1]) + + uint64(val.n[7])*uint64(val2.n[0]) + t7 := m & fieldBaseMask + // Terms for 2^(fieldBase*8). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[8]) + + uint64(val.n[1])*uint64(val2.n[7]) + + uint64(val.n[2])*uint64(val2.n[6]) + + uint64(val.n[3])*uint64(val2.n[5]) + + uint64(val.n[4])*uint64(val2.n[4]) + + uint64(val.n[5])*uint64(val2.n[3]) + + uint64(val.n[6])*uint64(val2.n[2]) + + uint64(val.n[7])*uint64(val2.n[1]) + + uint64(val.n[8])*uint64(val2.n[0]) + t8 := m & fieldBaseMask + // Terms for 2^(fieldBase*9). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[9]) + + uint64(val.n[1])*uint64(val2.n[8]) + + uint64(val.n[2])*uint64(val2.n[7]) + + uint64(val.n[3])*uint64(val2.n[6]) + + uint64(val.n[4])*uint64(val2.n[5]) + + uint64(val.n[5])*uint64(val2.n[4]) + + uint64(val.n[6])*uint64(val2.n[3]) + + uint64(val.n[7])*uint64(val2.n[2]) + + uint64(val.n[8])*uint64(val2.n[1]) + + uint64(val.n[9])*uint64(val2.n[0]) + t9 := m & fieldBaseMask + // Terms for 2^(fieldBase*10). + m = (m >> fieldBase) + + uint64(val.n[1])*uint64(val2.n[9]) + + uint64(val.n[2])*uint64(val2.n[8]) + + uint64(val.n[3])*uint64(val2.n[7]) + + uint64(val.n[4])*uint64(val2.n[6]) + + uint64(val.n[5])*uint64(val2.n[5]) + + uint64(val.n[6])*uint64(val2.n[4]) + + uint64(val.n[7])*uint64(val2.n[3]) + + uint64(val.n[8])*uint64(val2.n[2]) + + uint64(val.n[9])*uint64(val2.n[1]) + t10 := m & fieldBaseMask + // Terms for 2^(fieldBase*11). + m = (m >> fieldBase) + + uint64(val.n[2])*uint64(val2.n[9]) + + uint64(val.n[3])*uint64(val2.n[8]) + + uint64(val.n[4])*uint64(val2.n[7]) + + uint64(val.n[5])*uint64(val2.n[6]) + + uint64(val.n[6])*uint64(val2.n[5]) + + uint64(val.n[7])*uint64(val2.n[4]) + + uint64(val.n[8])*uint64(val2.n[3]) + + uint64(val.n[9])*uint64(val2.n[2]) + t11 := m & fieldBaseMask + // Terms for 2^(fieldBase*12). + m = (m >> fieldBase) + + uint64(val.n[3])*uint64(val2.n[9]) + + uint64(val.n[4])*uint64(val2.n[8]) + + uint64(val.n[5])*uint64(val2.n[7]) + + uint64(val.n[6])*uint64(val2.n[6]) + + uint64(val.n[7])*uint64(val2.n[5]) + + uint64(val.n[8])*uint64(val2.n[4]) + + uint64(val.n[9])*uint64(val2.n[3]) + t12 := m & fieldBaseMask + // Terms for 2^(fieldBase*13). + m = (m >> fieldBase) + + uint64(val.n[4])*uint64(val2.n[9]) + + uint64(val.n[5])*uint64(val2.n[8]) + + uint64(val.n[6])*uint64(val2.n[7]) + + uint64(val.n[7])*uint64(val2.n[6]) + + uint64(val.n[8])*uint64(val2.n[5]) + + uint64(val.n[9])*uint64(val2.n[4]) + t13 := m & fieldBaseMask + // Terms for 2^(fieldBase*14). + m = (m >> fieldBase) + + uint64(val.n[5])*uint64(val2.n[9]) + + uint64(val.n[6])*uint64(val2.n[8]) + + uint64(val.n[7])*uint64(val2.n[7]) + + uint64(val.n[8])*uint64(val2.n[6]) + + uint64(val.n[9])*uint64(val2.n[5]) + t14 := m & fieldBaseMask + // Terms for 2^(fieldBase*15). + m = (m >> fieldBase) + + uint64(val.n[6])*uint64(val2.n[9]) + + uint64(val.n[7])*uint64(val2.n[8]) + + uint64(val.n[8])*uint64(val2.n[7]) + + uint64(val.n[9])*uint64(val2.n[6]) + t15 := m & fieldBaseMask + // Terms for 2^(fieldBase*16). + m = (m >> fieldBase) + + uint64(val.n[7])*uint64(val2.n[9]) + + uint64(val.n[8])*uint64(val2.n[8]) + + uint64(val.n[9])*uint64(val2.n[7]) + t16 := m & fieldBaseMask + // Terms for 2^(fieldBase*17). + m = (m >> fieldBase) + + uint64(val.n[8])*uint64(val2.n[9]) + + uint64(val.n[9])*uint64(val2.n[8]) + t17 := m & fieldBaseMask + // Terms for 2^(fieldBase*18). + m = (m >> fieldBase) + uint64(val.n[9])*uint64(val2.n[9]) + t18 := m & fieldBaseMask + // What's left is for 2^(fieldBase*19). + t19 := m >> fieldBase + // At this point, all of the terms are grouped into their respective + // base. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved per the provided algorithm. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // Since each word is in base 26, the upper terms (t10 and up) start + // at 260 bits (versus the final desired range of 256 bits), so the + // field representation of 'c' from above needs to be adjusted for the + // extra 4 bits by multiplying it by 2^4 = 16. 4294968273 * 16 = + // 68719492368. Thus, the adjusted field representation of 'c' is: + // n[0] = 977 * 16 = 15632 + // n[1] = 64 * 16 = 1024 + // That is to say (2^26 * 1024) + 15632 = 68719492368 + // + // To reduce the final term, t19, the entire 'c' value is needed instead + // of only n[0] because there are no more terms left to handle n[1]. + // This means there might be some magnitude left in the upper bits that + // is handled below. + m = t0 + t10*15632 + t0 = m & fieldBaseMask + m = (m >> fieldBase) + t1 + t10*1024 + t11*15632 + t1 = m & fieldBaseMask + m = (m >> fieldBase) + t2 + t11*1024 + t12*15632 + t2 = m & fieldBaseMask + m = (m >> fieldBase) + t3 + t12*1024 + t13*15632 + t3 = m & fieldBaseMask + m = (m >> fieldBase) + t4 + t13*1024 + t14*15632 + t4 = m & fieldBaseMask + m = (m >> fieldBase) + t5 + t14*1024 + t15*15632 + t5 = m & fieldBaseMask + m = (m >> fieldBase) + t6 + t15*1024 + t16*15632 + t6 = m & fieldBaseMask + m = (m >> fieldBase) + t7 + t16*1024 + t17*15632 + t7 = m & fieldBaseMask + m = (m >> fieldBase) + t8 + t17*1024 + t18*15632 + t8 = m & fieldBaseMask + m = (m >> fieldBase) + t9 + t18*1024 + t19*68719492368 + t9 = m & fieldMSBMask + m = m >> fieldMSBBits + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. + // + // The algorithm presented in [HAC] section 14.3.4 repeats until the + // quotient is zero. However, due to the above, we already know at + // least how many times we would need to repeat as it's the value + // currently in m. Thus we can simply multiply the magnitude by the + // field representation of the prime and do a single iteration. Notice + // that nothing will be changed when the magnitude is zero, so we could + // skip this in that case, however always running regardless allows it + // to run in constant time. The final result will be in the range + // 0 <= result <= prime + (2^64 - c), so it is guaranteed to have a + // magnitude of 1, but it is denormalized. + d := t0 + m*977 + f.n[0] = uint32(d & fieldBaseMask) + d = (d >> fieldBase) + t1 + m*64 + f.n[1] = uint32(d & fieldBaseMask) + f.n[2] = uint32((d >> fieldBase) + t2) + f.n[3] = uint32(t3) + f.n[4] = uint32(t4) + f.n[5] = uint32(t5) + f.n[6] = uint32(t6) + f.n[7] = uint32(t7) + f.n[8] = uint32(t8) + f.n[9] = uint32(t9) + return f +} + +// SquareRootVal either calculates the square root of the passed value when it +// exists or the square root of the negation of the value when it does not exist +// and stores the result in f in constant time. The return flag is true when +// the calculated square root is for the passed value itself and false when it +// is for its negation. +// +// Note that this function can overflow if multiplying any of the individual +// words exceeds a max uint32. In practice, this means the magnitude of the +// field must be a max of 8 to prevent overflow. The magnitude of the result +// will be 1. +// +// Preconditions: +// - The input field value MUST have a max magnitude of 8 +// Output Normalized: No +// Output Max Magnitude: 1 +func (f *FieldVal) SquareRootVal(val *FieldVal) bool { + // This uses the Tonelli-Shanks method for calculating the square root of + // the value when it exists. The key principles of the method follow. + // + // Fermat's little theorem states that for a nonzero number 'a' and prime + // 'p', a^(p-1) ≡ 1 (mod p). + // + // Further, Euler's criterion states that an integer 'a' has a square root + // (aka is a quadratic residue) modulo a prime if a^((p-1)/2) ≡ 1 (mod p) + // and, conversely, when it does NOT have a square root (aka 'a' is a + // non-residue) a^((p-1)/2) ≡ -1 (mod p). + // + // This can be seen by considering that Fermat's little theorem can be + // written as (a^((p-1)/2) - 1)(a^((p-1)/2) + 1) ≡ 0 (mod p). Therefore, + // one of the two factors must be 0. Then, when a ≡ x^2 (aka 'a' is a + // quadratic residue), (x^2)^((p-1)/2) ≡ x^(p-1) ≡ 1 (mod p) which implies + // the first factor must be zero. Finally, per Lagrange's theorem, the + // non-residues are the only remaining possible solutions and thus must make + // the second factor zero to satisfy Fermat's little theorem implying that + // a^((p-1)/2) ≡ -1 (mod p) for that case. + // + // The Tonelli-Shanks method uses these facts along with factoring out + // powers of two to solve a congruence that results in either the solution + // when the square root exists or the square root of the negation of the + // value when it does not. In the case of primes that are ≡ 3 (mod 4), the + // possible solutions are r = ±a^((p+1)/4) (mod p). Therefore, either r^2 ≡ + // a (mod p) is true in which case ±r are the two solutions, or r^2 ≡ -a + // (mod p) in which case 'a' is a non-residue and there are no solutions. + // + // The secp256k1 prime is ≡ 3 (mod 4), so this result applies. + // + // In other words, calculate a^((p+1)/4) and then square it and check it + // against the original value to determine if it is actually the square + // root. + // + // In order to efficiently compute a^((p+1)/4), (p+1)/4 needs to be split + // into a sequence of squares and multiplications that minimizes the number + // of multiplications needed (since they are more costly than squarings). + // + // The secp256k1 prime + 1 / 4 is 2^254 - 2^30 - 244. In binary, that is: + // + // 00111111 11111111 11111111 11111111 + // 11111111 11111111 11111111 11111111 + // 11111111 11111111 11111111 11111111 + // 11111111 11111111 11111111 11111111 + // 11111111 11111111 11111111 11111111 + // 11111111 11111111 11111111 11111111 + // 11111111 11111111 11111111 11111111 + // 10111111 11111111 11111111 00001100 + // + // Notice that can be broken up into three windows of consecutive 1s (in + // order of least to most significant) as: + // + // 6-bit window with two bits set (bits 4, 5, 6, 7 unset) + // 23-bit window with 22 bits set (bit 30 unset) + // 223-bit window with all 223 bits set + // + // Thus, the groups of 1 bits in each window forms the set: + // S = {2, 22, 223}. + // + // The strategy is to calculate a^(2^n - 1) for each grouping via an + // addition chain with a sliding window. + // + // The addition chain used is (credits to Peter Dettman): + // (0,0),(1,0),(2,2),(3,2),(4,1),(5,5),(6,6),(7,7),(8,8),(9,7),(10,2) + // => 2^1 2^[2] 2^3 2^6 2^9 2^11 2^[22] 2^44 2^88 2^176 2^220 2^[223] + // + // This has a cost of 254 field squarings and 13 field multiplications. + var a, a2, a3, a6, a9, a11, a22, a44, a88, a176, a220, a223 FieldVal + a.Set(val) + a2.SquareVal(&a).Mul(&a) // a2 = a^(2^2 - 1) + a3.SquareVal(&a2).Mul(&a) // a3 = a^(2^3 - 1) + a6.SquareVal(&a3).Square().Square() // a6 = a^(2^6 - 2^3) + a6.Mul(&a3) // a6 = a^(2^6 - 1) + a9.SquareVal(&a6).Square().Square() // a9 = a^(2^9 - 2^3) + a9.Mul(&a3) // a9 = a^(2^9 - 1) + a11.SquareVal(&a9).Square() // a11 = a^(2^11 - 2^2) + a11.Mul(&a2) // a11 = a^(2^11 - 1) + a22.SquareVal(&a11).Square().Square().Square().Square() // a22 = a^(2^16 - 2^5) + a22.Square().Square().Square().Square().Square() // a22 = a^(2^21 - 2^10) + a22.Square() // a22 = a^(2^22 - 2^11) + a22.Mul(&a11) // a22 = a^(2^22 - 1) + a44.SquareVal(&a22).Square().Square().Square().Square() // a44 = a^(2^27 - 2^5) + a44.Square().Square().Square().Square().Square() // a44 = a^(2^32 - 2^10) + a44.Square().Square().Square().Square().Square() // a44 = a^(2^37 - 2^15) + a44.Square().Square().Square().Square().Square() // a44 = a^(2^42 - 2^20) + a44.Square().Square() // a44 = a^(2^44 - 2^22) + a44.Mul(&a22) // a44 = a^(2^44 - 1) + a88.SquareVal(&a44).Square().Square().Square().Square() // a88 = a^(2^49 - 2^5) + a88.Square().Square().Square().Square().Square() // a88 = a^(2^54 - 2^10) + a88.Square().Square().Square().Square().Square() // a88 = a^(2^59 - 2^15) + a88.Square().Square().Square().Square().Square() // a88 = a^(2^64 - 2^20) + a88.Square().Square().Square().Square().Square() // a88 = a^(2^69 - 2^25) + a88.Square().Square().Square().Square().Square() // a88 = a^(2^74 - 2^30) + a88.Square().Square().Square().Square().Square() // a88 = a^(2^79 - 2^35) + a88.Square().Square().Square().Square().Square() // a88 = a^(2^84 - 2^40) + a88.Square().Square().Square().Square() // a88 = a^(2^88 - 2^44) + a88.Mul(&a44) // a88 = a^(2^88 - 1) + a176.SquareVal(&a88).Square().Square().Square().Square() // a176 = a^(2^93 - 2^5) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^98 - 2^10) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^103 - 2^15) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^108 - 2^20) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^113 - 2^25) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^118 - 2^30) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^123 - 2^35) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^128 - 2^40) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^133 - 2^45) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^138 - 2^50) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^143 - 2^55) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^148 - 2^60) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^153 - 2^65) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^158 - 2^70) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^163 - 2^75) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^168 - 2^80) + a176.Square().Square().Square().Square().Square() // a176 = a^(2^173 - 2^85) + a176.Square().Square().Square() // a176 = a^(2^176 - 2^88) + a176.Mul(&a88) // a176 = a^(2^176 - 1) + a220.SquareVal(&a176).Square().Square().Square().Square() // a220 = a^(2^181 - 2^5) + a220.Square().Square().Square().Square().Square() // a220 = a^(2^186 - 2^10) + a220.Square().Square().Square().Square().Square() // a220 = a^(2^191 - 2^15) + a220.Square().Square().Square().Square().Square() // a220 = a^(2^196 - 2^20) + a220.Square().Square().Square().Square().Square() // a220 = a^(2^201 - 2^25) + a220.Square().Square().Square().Square().Square() // a220 = a^(2^206 - 2^30) + a220.Square().Square().Square().Square().Square() // a220 = a^(2^211 - 2^35) + a220.Square().Square().Square().Square().Square() // a220 = a^(2^216 - 2^40) + a220.Square().Square().Square().Square() // a220 = a^(2^220 - 2^44) + a220.Mul(&a44) // a220 = a^(2^220 - 1) + a223.SquareVal(&a220).Square().Square() // a223 = a^(2^223 - 2^3) + a223.Mul(&a3) // a223 = a^(2^223 - 1) + + f.SquareVal(&a223).Square().Square().Square().Square() // f = a^(2^228 - 2^5) + f.Square().Square().Square().Square().Square() // f = a^(2^233 - 2^10) + f.Square().Square().Square().Square().Square() // f = a^(2^238 - 2^15) + f.Square().Square().Square().Square().Square() // f = a^(2^243 - 2^20) + f.Square().Square().Square() // f = a^(2^246 - 2^23) + f.Mul(&a22) // f = a^(2^246 - 2^22 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^251 - 2^27 - 2^5) + f.Square() // f = a^(2^252 - 2^28 - 2^6) + f.Mul(&a2) // f = a^(2^252 - 2^28 - 2^6 - 2^1 - 1) + f.Square().Square() // f = a^(2^254 - 2^30 - 2^8 - 2^3 - 2^2) + // // = a^(2^254 - 2^30 - 244) + // // = a^((p+1)/4) + // Ensure the calculated result is actually the square root by squaring it + // and checking against the original value. + var sqr FieldVal + return sqr.SquareVal(f).Normalize().Equals(val.Normalize()) +} + +// Square squares the field value in constant time. The existing field value is +// modified. Note that this function can overflow if multiplying any of the +// individual words exceeds a max uint32. In practice, this means the magnitude +// of the field must be a max of 8 to prevent overflow. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Square().Mul(f2) so that f = f^2 * f2. +// +// Preconditions: +// - The field value MUST have a max magnitude of 8 +// Output Normalized: No +// Output Max Magnitude: 1 +func (f *FieldVal) Square() *FieldVal { return f.SquareVal(f) } + +// SquareVal squares the passed value and stores the result in f in constant +// time. Note that this function can overflow if multiplying any of the +// individual words exceeds a max uint32. In practice, this means the magnitude +// of the field being squared must be a max of 8 to prevent overflow. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.SquareVal(f).Mul(f) so that f3 = f^2 * f = f^3. +// +// Preconditions: +// - The input field value MUST have a max magnitude of 8 +// Output Normalized: No +// Output Max Magnitude: 1 +func (f *FieldVal) SquareVal(val *FieldVal) *FieldVal { + // This could be done with a couple of for loops and an array to store + // the intermediate terms, but this unrolled version is significantly + // faster. + // + // Terms for 2^(fieldBase*0). + m := uint64(val.n[0]) * uint64(val.n[0]) + t0 := m & fieldBaseMask + // Terms for 2^(fieldBase*1). + m = (m >> fieldBase) + 2*uint64(val.n[0])*uint64(val.n[1]) + t1 := m & fieldBaseMask + // Terms for 2^(fieldBase*2). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[2]) + + uint64(val.n[1])*uint64(val.n[1]) + t2 := m & fieldBaseMask + // Terms for 2^(fieldBase*3). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[3]) + + 2*uint64(val.n[1])*uint64(val.n[2]) + t3 := m & fieldBaseMask + // Terms for 2^(fieldBase*4). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[4]) + + 2*uint64(val.n[1])*uint64(val.n[3]) + + uint64(val.n[2])*uint64(val.n[2]) + t4 := m & fieldBaseMask + // Terms for 2^(fieldBase*5). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[5]) + + 2*uint64(val.n[1])*uint64(val.n[4]) + + 2*uint64(val.n[2])*uint64(val.n[3]) + t5 := m & fieldBaseMask + // Terms for 2^(fieldBase*6). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[6]) + + 2*uint64(val.n[1])*uint64(val.n[5]) + + 2*uint64(val.n[2])*uint64(val.n[4]) + + uint64(val.n[3])*uint64(val.n[3]) + t6 := m & fieldBaseMask + // Terms for 2^(fieldBase*7). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[7]) + + 2*uint64(val.n[1])*uint64(val.n[6]) + + 2*uint64(val.n[2])*uint64(val.n[5]) + + 2*uint64(val.n[3])*uint64(val.n[4]) + t7 := m & fieldBaseMask + // Terms for 2^(fieldBase*8). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[8]) + + 2*uint64(val.n[1])*uint64(val.n[7]) + + 2*uint64(val.n[2])*uint64(val.n[6]) + + 2*uint64(val.n[3])*uint64(val.n[5]) + + uint64(val.n[4])*uint64(val.n[4]) + t8 := m & fieldBaseMask + // Terms for 2^(fieldBase*9). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[9]) + + 2*uint64(val.n[1])*uint64(val.n[8]) + + 2*uint64(val.n[2])*uint64(val.n[7]) + + 2*uint64(val.n[3])*uint64(val.n[6]) + + 2*uint64(val.n[4])*uint64(val.n[5]) + t9 := m & fieldBaseMask + // Terms for 2^(fieldBase*10). + m = (m >> fieldBase) + + 2*uint64(val.n[1])*uint64(val.n[9]) + + 2*uint64(val.n[2])*uint64(val.n[8]) + + 2*uint64(val.n[3])*uint64(val.n[7]) + + 2*uint64(val.n[4])*uint64(val.n[6]) + + uint64(val.n[5])*uint64(val.n[5]) + t10 := m & fieldBaseMask + // Terms for 2^(fieldBase*11). + m = (m >> fieldBase) + + 2*uint64(val.n[2])*uint64(val.n[9]) + + 2*uint64(val.n[3])*uint64(val.n[8]) + + 2*uint64(val.n[4])*uint64(val.n[7]) + + 2*uint64(val.n[5])*uint64(val.n[6]) + t11 := m & fieldBaseMask + // Terms for 2^(fieldBase*12). + m = (m >> fieldBase) + + 2*uint64(val.n[3])*uint64(val.n[9]) + + 2*uint64(val.n[4])*uint64(val.n[8]) + + 2*uint64(val.n[5])*uint64(val.n[7]) + + uint64(val.n[6])*uint64(val.n[6]) + t12 := m & fieldBaseMask + // Terms for 2^(fieldBase*13). + m = (m >> fieldBase) + + 2*uint64(val.n[4])*uint64(val.n[9]) + + 2*uint64(val.n[5])*uint64(val.n[8]) + + 2*uint64(val.n[6])*uint64(val.n[7]) + t13 := m & fieldBaseMask + // Terms for 2^(fieldBase*14). + m = (m >> fieldBase) + + 2*uint64(val.n[5])*uint64(val.n[9]) + + 2*uint64(val.n[6])*uint64(val.n[8]) + + uint64(val.n[7])*uint64(val.n[7]) + t14 := m & fieldBaseMask + // Terms for 2^(fieldBase*15). + m = (m >> fieldBase) + + 2*uint64(val.n[6])*uint64(val.n[9]) + + 2*uint64(val.n[7])*uint64(val.n[8]) + t15 := m & fieldBaseMask + // Terms for 2^(fieldBase*16). + m = (m >> fieldBase) + + 2*uint64(val.n[7])*uint64(val.n[9]) + + uint64(val.n[8])*uint64(val.n[8]) + t16 := m & fieldBaseMask + // Terms for 2^(fieldBase*17). + m = (m >> fieldBase) + 2*uint64(val.n[8])*uint64(val.n[9]) + t17 := m & fieldBaseMask + // Terms for 2^(fieldBase*18). + m = (m >> fieldBase) + uint64(val.n[9])*uint64(val.n[9]) + t18 := m & fieldBaseMask + // What's left is for 2^(fieldBase*19). + t19 := m >> fieldBase + // At this point, all of the terms are grouped into their respective + // base. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved per the provided algorithm. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // Since each word is in base 26, the upper terms (t10 and up) start + // at 260 bits (versus the final desired range of 256 bits), so the + // field representation of 'c' from above needs to be adjusted for the + // extra 4 bits by multiplying it by 2^4 = 16. 4294968273 * 16 = + // 68719492368. Thus, the adjusted field representation of 'c' is: + // n[0] = 977 * 16 = 15632 + // n[1] = 64 * 16 = 1024 + // That is to say (2^26 * 1024) + 15632 = 68719492368 + // + // To reduce the final term, t19, the entire 'c' value is needed instead + // of only n[0] because there are no more terms left to handle n[1]. + // This means there might be some magnitude left in the upper bits that + // is handled below. + m = t0 + t10*15632 + t0 = m & fieldBaseMask + m = (m >> fieldBase) + t1 + t10*1024 + t11*15632 + t1 = m & fieldBaseMask + m = (m >> fieldBase) + t2 + t11*1024 + t12*15632 + t2 = m & fieldBaseMask + m = (m >> fieldBase) + t3 + t12*1024 + t13*15632 + t3 = m & fieldBaseMask + m = (m >> fieldBase) + t4 + t13*1024 + t14*15632 + t4 = m & fieldBaseMask + m = (m >> fieldBase) + t5 + t14*1024 + t15*15632 + t5 = m & fieldBaseMask + m = (m >> fieldBase) + t6 + t15*1024 + t16*15632 + t6 = m & fieldBaseMask + m = (m >> fieldBase) + t7 + t16*1024 + t17*15632 + t7 = m & fieldBaseMask + m = (m >> fieldBase) + t8 + t17*1024 + t18*15632 + t8 = m & fieldBaseMask + m = (m >> fieldBase) + t9 + t18*1024 + t19*68719492368 + t9 = m & fieldMSBMask + m = m >> fieldMSBBits + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. + // + // The algorithm presented in [HAC] section 14.3.4 repeats until the + // quotient is zero. However, due to the above, we already know at + // least how many times we would need to repeat as it's the value + // currently in m. Thus we can simply multiply the magnitude by the + // field representation of the prime and do a single iteration. Notice + // that nothing will be changed when the magnitude is zero, so we could + // skip this in that case, however always running regardless allows it + // to run in constant time. The final result will be in the range + // 0 <= result <= prime + (2^64 - c), so it is guaranteed to have a + // magnitude of 1, but it is denormalized. + n := t0 + m*977 + f.n[0] = uint32(n & fieldBaseMask) + n = (n >> fieldBase) + t1 + m*64 + f.n[1] = uint32(n & fieldBaseMask) + f.n[2] = uint32((n >> fieldBase) + t2) + f.n[3] = uint32(t3) + f.n[4] = uint32(t4) + f.n[5] = uint32(t5) + f.n[6] = uint32(t6) + f.n[7] = uint32(t7) + f.n[8] = uint32(t8) + f.n[9] = uint32(t9) + return f +} + +// Inverse finds the modular multiplicative inverse of the field value in +// constant time. The existing field value is modified. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Inverse().Mul(f2) so that f = f^-1 * f2. +// +// Preconditions: +// - The field value MUST have a max magnitude of 8 +// Output Normalized: No +// Output Max Magnitude: 1 +func (f *FieldVal) Inverse() *FieldVal { + // Fermat's little theorem states that for a nonzero number a and prime + // p, a^(p-1) ≡ 1 (mod p). Since the multiplicative inverse is + // a*b ≡ 1 (mod p), it follows that b ≡ a*a^(p-2) ≡ a^(p-1) ≡ 1 (mod p). + // Thus, a^(p-2) is the multiplicative inverse. + // + // In order to efficiently compute a^(p-2), p-2 needs to be split into + // a sequence of squares and multiplications that minimizes the number + // of multiplications needed (since they are more costly than + // squarings). Intermediate results are saved and reused as well. + // + // The secp256k1 prime - 2 is 2^256 - 4294968275. + // + // This has a cost of 258 field squarings and 33 field multiplications. + var a2, a3, a4, a10, a11, a21, a42, a45, a63, a1019, a1023 FieldVal + a2.SquareVal(f) + a3.Mul2(&a2, f) + a4.SquareVal(&a2) + a10.SquareVal(&a4).Mul(&a2) + a11.Mul2(&a10, f) + a21.Mul2(&a10, &a11) + a42.SquareVal(&a21) + a45.Mul2(&a42, &a3) + a63.Mul2(&a42, &a21) + a1019.SquareVal(&a63).Square().Square().Square().Mul(&a11) + a1023.Mul2(&a1019, &a4) + f.Set(&a63) // f = a^(2^6 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^11 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^16 - 1024) + f.Mul(&a1023) // f = a^(2^16 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^21 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^26 - 1024) + f.Mul(&a1023) // f = a^(2^26 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^31 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^36 - 1024) + f.Mul(&a1023) // f = a^(2^36 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^41 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^46 - 1024) + f.Mul(&a1023) // f = a^(2^46 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^51 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^56 - 1024) + f.Mul(&a1023) // f = a^(2^56 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^61 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^66 - 1024) + f.Mul(&a1023) // f = a^(2^66 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^71 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^76 - 1024) + f.Mul(&a1023) // f = a^(2^76 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^81 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^86 - 1024) + f.Mul(&a1023) // f = a^(2^86 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^91 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^96 - 1024) + f.Mul(&a1023) // f = a^(2^96 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^101 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^106 - 1024) + f.Mul(&a1023) // f = a^(2^106 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^111 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^116 - 1024) + f.Mul(&a1023) // f = a^(2^116 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^121 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^126 - 1024) + f.Mul(&a1023) // f = a^(2^126 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^131 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^136 - 1024) + f.Mul(&a1023) // f = a^(2^136 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^141 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^146 - 1024) + f.Mul(&a1023) // f = a^(2^146 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^151 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^156 - 1024) + f.Mul(&a1023) // f = a^(2^156 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^161 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^166 - 1024) + f.Mul(&a1023) // f = a^(2^166 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^171 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^176 - 1024) + f.Mul(&a1023) // f = a^(2^176 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^181 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^186 - 1024) + f.Mul(&a1023) // f = a^(2^186 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^191 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^196 - 1024) + f.Mul(&a1023) // f = a^(2^196 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^201 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^206 - 1024) + f.Mul(&a1023) // f = a^(2^206 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^211 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^216 - 1024) + f.Mul(&a1023) // f = a^(2^216 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^221 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^226 - 1024) + f.Mul(&a1019) // f = a^(2^226 - 5) + f.Square().Square().Square().Square().Square() // f = a^(2^231 - 160) + f.Square().Square().Square().Square().Square() // f = a^(2^236 - 5120) + f.Mul(&a1023) // f = a^(2^236 - 4097) + f.Square().Square().Square().Square().Square() // f = a^(2^241 - 131104) + f.Square().Square().Square().Square().Square() // f = a^(2^246 - 4195328) + f.Mul(&a1023) // f = a^(2^246 - 4194305) + f.Square().Square().Square().Square().Square() // f = a^(2^251 - 134217760) + f.Square().Square().Square().Square().Square() // f = a^(2^256 - 4294968320) + return f.Mul(&a45) // f = a^(2^256 - 4294968275) = a^(p-2) +} + +// IsGtOrEqPrimeMinusOrder returns whether the field value exceeds the +// group order divided by 2 in constant time. +// +// Preconditions: +// - The field value MUST be normalized +func (f *FieldVal) IsGtOrEqPrimeMinusOrder() bool { + // The secp256k1 prime is equivalent to 2^256 - 4294968273 and the group + // order is 2^256 - 432420386565659656852420866394968145599. Thus, + // the prime minus the group order is: + // 432420386565659656852420866390673177326 + // + // In hex that is: + // 0x00000000 00000000 00000000 00000001 45512319 50b75fc4 402da172 2fc9baee + // + // Converting that to field representation (base 2^26) is: + // + // n[0] = 0x03c9baee + // n[1] = 0x03685c8b + // n[2] = 0x01fc4402 + // n[3] = 0x006542dd + // n[4] = 0x01455123 + // + // This can be verified with the following test code: + // pMinusN := new(big.Int).Sub(curveParams.P, curveParams.N) + // var fv FieldVal + // fv.SetByteSlice(pMinusN.Bytes()) + // t.Logf("%x", fv.n) + // + // Outputs: [3c9baee 3685c8b 1fc4402 6542dd 1455123 0 0 0 0 0] + const ( + pMinusNWordZero = 0x03c9baee + pMinusNWordOne = 0x03685c8b + pMinusNWordTwo = 0x01fc4402 + pMinusNWordThree = 0x006542dd + pMinusNWordFour = 0x01455123 + pMinusNWordFive = 0x00000000 + pMinusNWordSix = 0x00000000 + pMinusNWordSeven = 0x00000000 + pMinusNWordEight = 0x00000000 + pMinusNWordNine = 0x00000000 + ) + // The intuition here is that the value is greater than field prime minus + // the group order if one of the higher individual words is greater than the + // corresponding word and all higher words in the value are equal. + result := constantTimeGreater(f.n[9], pMinusNWordNine) + highWordsEqual := constantTimeEq(f.n[9], pMinusNWordNine) + result |= highWordsEqual & constantTimeGreater(f.n[8], pMinusNWordEight) + highWordsEqual &= constantTimeEq(f.n[8], pMinusNWordEight) + result |= highWordsEqual & constantTimeGreater(f.n[7], pMinusNWordSeven) + highWordsEqual &= constantTimeEq(f.n[7], pMinusNWordSeven) + result |= highWordsEqual & constantTimeGreater(f.n[6], pMinusNWordSix) + highWordsEqual &= constantTimeEq(f.n[6], pMinusNWordSix) + result |= highWordsEqual & constantTimeGreater(f.n[5], pMinusNWordFive) + highWordsEqual &= constantTimeEq(f.n[5], pMinusNWordFive) + result |= highWordsEqual & constantTimeGreater(f.n[4], pMinusNWordFour) + highWordsEqual &= constantTimeEq(f.n[4], pMinusNWordFour) + result |= highWordsEqual & constantTimeGreater(f.n[3], pMinusNWordThree) + highWordsEqual &= constantTimeEq(f.n[3], pMinusNWordThree) + result |= highWordsEqual & constantTimeGreater(f.n[2], pMinusNWordTwo) + highWordsEqual &= constantTimeEq(f.n[2], pMinusNWordTwo) + result |= highWordsEqual & constantTimeGreater(f.n[1], pMinusNWordOne) + highWordsEqual &= constantTimeEq(f.n[1], pMinusNWordOne) + result |= highWordsEqual & constantTimeGreaterOrEq(f.n[0], pMinusNWordZero) + return result != 0 +} diff --git a/pkg/crypto/ec/secp256k1/field_bench_test.go b/pkg/crypto/ec/secp256k1/field_bench_test.go new file mode 100644 index 0000000..5a55aaf --- /dev/null +++ b/pkg/crypto/ec/secp256k1/field_bench_test.go @@ -0,0 +1,92 @@ +// Copyright (c) 2020-2023 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "math/big" + "testing" +) + +// BenchmarkFieldNormalize benchmarks how long it takes the internal field +// to perform normalization (which includes modular reduction). +func BenchmarkFieldNormalize(b *testing.B) { + // The function is constant time so any value is fine. + f := &FieldVal{ + n: [10]uint32{ + 0x000148f6, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x00000007, + }, + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + f.Normalize() + } +} + +// BenchmarkFieldSqrt benchmarks calculating the square root of an unsigned +// 256-bit big-endian integer modulo the field prime with the specialized type. +func BenchmarkFieldSqrt(b *testing.B) { + // The function is constant time so any value is fine. + valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + f := new(FieldVal).SetHex(valHex).Normalize() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + var result FieldVal + _ = result.SquareRootVal(f) + } +} + +// BenchmarkBigSqrt benchmarks calculating the square root of an unsigned +// 256-bit big-endian integer modulo the field prime with stdlib big integers. +func BenchmarkBigSqrt(b *testing.B) { + // The function is constant time so any value is fine. + valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + val, ok := new(big.Int).SetString(valHex, 16) + if !ok { + b.Fatalf("failed to parse hex %s", valHex) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = new(big.Int).ModSqrt(val, curveParams.P) + } +} + +// BenchmarkFieldIsGtOrEqPrimeMinusOrder benchmarks determining whether a value +// is greater than or equal to the field prime minus the group order with the +// specialized type. +func BenchmarkFieldIsGtOrEqPrimeMinusOrder(b *testing.B) { + // The function is constant time so any value is fine. + valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + f := new(FieldVal).SetHex(valHex).Normalize() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = f.IsGtOrEqPrimeMinusOrder() + } +} + +// BenchmarkBigIsGtOrEqPrimeMinusOrder benchmarks determining whether a value +// is greater than or equal to the field prime minus the group order with stdlib +// big integers. +func BenchmarkBigIsGtOrEqPrimeMinusOrder(b *testing.B) { + // Same value used in field val version. + valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca" + val, ok := new(big.Int).SetString(valHex, 16) + if !ok { + b.Fatalf("failed to parse hex %s", valHex) + } + bigPMinusN := new(big.Int).Sub(curveParams.P, curveParams.N) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + // In practice, the internal value to compare would have to be converted + // to a big integer from bytes, so it's a fair comparison to allocate a + // new big int here and set all bytes. + _ = new(big.Int).SetBytes(val.Bytes()).Cmp(bigPMinusN) >= 0 + } +} diff --git a/pkg/crypto/ec/secp256k1/field_test.go b/pkg/crypto/ec/secp256k1/field_test.go new file mode 100644 index 0000000..b31bd2b --- /dev/null +++ b/pkg/crypto/ec/secp256k1/field_test.go @@ -0,0 +1,2018 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Copyright (c) 2013-2022 Dave Collins +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "fmt" + "math/big" + "math/rand" + "reflect" + "testing" + "time" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/encoders/hex" + "next.orly.dev/pkg/utils" +) + +// SetHex decodes the passed big-endian hex string into the internal field value +// representation. Only the first 32-bytes are used. +// +// 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.Dec(hexString) + f.SetByteSlice(bytes) + return f +} + +// randFieldVal returns a field value created from a random value generated by +// the passed rng. +func randFieldVal(t *testing.T, rng *rand.Rand) *FieldVal { + t.Helper() + var buf [32]byte + if _, err := rng.Read(buf[:]); chk.T(err) { + t.Fatalf("failed to read random: %v", err) + } + // Create and return a field value. + var fv FieldVal + fv.SetBytes(&buf) + return &fv +} + +// randIntAndFieldVal returns a big integer and a field value both created from +// the same random value generated by the passed rng. +func randIntAndFieldVal(t *testing.T, rng *rand.Rand) (*big.Int, *FieldVal) { + t.Helper() + var buf [32]byte + if _, err := rng.Read(buf[:]); chk.T(err) { + t.Fatalf("failed to read random: %v", err) + } + // Create and return both a big integer and a field value. + bigIntVal := new(big.Int).SetBytes(buf[:]) + bigIntVal.Mod(bigIntVal, curveParams.N) + var fv FieldVal + fv.SetBytes(&buf) + return bigIntVal, &fv +} + +// TestFieldSetInt ensures that setting a field value to various native +// integers works as expected. +func TestFieldSetInt(t *testing.T) { + tests := []struct { + name string // test description + in uint16 // test value + expected [10]uint32 // expected raw ints + }{ + { + name: "one", + in: 1, + expected: [10]uint32{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "five", + in: 5, + expected: [10]uint32{5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^16 - 1", + in: 65535, + expected: [10]uint32{65535, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + } + for _, test := range tests { + f := new(FieldVal).SetInt(test.in) + if !reflect.DeepEqual(f.n, test.expected) { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, f.n, + test.expected, + ) + continue + } + } +} + +// TestFieldSetBytes ensures that setting a field value to a 256-bit big-endian +// unsigned integer via both the slice and array methods works as expected for +// edge cases. Random cases are tested via the various other tests. +func TestFieldSetBytes(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected [10]uint32 // expected raw ints + overflow bool // expected overflow result + }{ + { + name: "zero", + in: "00", + expected: [10]uint32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + overflow: false, + }, { + name: "field prime", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + overflow: true, + }, { + name: "field prime - 1", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + expected: [10]uint32{ + 0x03fffc2e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + overflow: false, + }, { + name: "field prime + 1 (overflow in word zero)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + expected: [10]uint32{ + 0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + overflow: true, + }, { + name: "field prime first 32 bits", + in: "fffffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x00000003f, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + overflow: false, + }, { + name: "field prime word zero", + in: "03fffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + overflow: false, + }, { + name: "field prime first 64 bits", + in: "fffffffefffffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x00000fff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + overflow: false, + }, { + name: "field prime word zero and one", + in: "0ffffefffffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + overflow: false, + }, { + name: "field prime first 96 bits", + in: "fffffffffffffffefffffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x0003ffff, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + overflow: false, + }, { + name: "field prime word zero, one, and two", + in: "3ffffffffffefffffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + overflow: false, + }, { + name: "overflow in word one (prime + 1<<26)", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff03fffc2f", + expected: [10]uint32{ + 0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + overflow: true, + }, { + name: "(field prime - 1) * 2 NOT mod P, truncated >32 bytes", + in: "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffff85c", + expected: [10]uint32{ + 0x01fffff8, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x00007fff, + }, + overflow: false, + }, { + name: "2^256 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expected: [10]uint32{ + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + overflow: true, + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: [10]uint32{ + 0x01a5a5a5, 0x01696969, 0x025a5a5a, 0x02969696, 0x01a5a5a5, + 0x01696969, 0x025a5a5a, 0x02969696, 0x01a5a5a5, 0x00296969, + }, + overflow: false, + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: [10]uint32{ + 0x025a5a5a, 0x02969696, 0x01a5a5a5, 0x01696969, 0x025a5a5a, + 0x02969696, 0x01a5a5a5, 0x01696969, 0x025a5a5a, 0x00169696, + }, + overflow: false, + }, + } + for _, test := range tests { + inBytes := hexToBytes(test.in) + // Ensure setting the bytes via the slice method works as expected. + var f FieldVal + overflow := f.SetByteSlice(inBytes) + if !reflect.DeepEqual(f.n, test.expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, f.n, + test.expected, + ) + continue + } + // Ensure the setting the bytes via the slice method produces the + // expected overflow result. + if overflow != test.overflow { + t.Errorf( + "%s: unexpected overflow -- got: %v, want: %v", test.name, + overflow, test.overflow, + ) + continue + } + // Ensure setting the bytes via the array method works as expected. + var f2 FieldVal + var b32 [32]byte + truncatedInBytes := inBytes + if len(truncatedInBytes) > 32 { + truncatedInBytes = truncatedInBytes[:32] + } + copy(b32[32-len(truncatedInBytes):], truncatedInBytes) + overflow = f2.SetBytes(&b32) != 0 + if !reflect.DeepEqual(f2.n, test.expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + f2.n, test.expected, + ) + continue + } + // Ensure the setting the bytes via the array method produces the + // expected overflow result. + if overflow != test.overflow { + t.Errorf( + "%s: unexpected overflow -- got: %v, want: %v", test.name, + overflow, test.overflow, + ) + continue + } + } +} + +// TestFieldBytes ensures that retrieving the bytes for a 256-bit big-endian +// unsigned integer via the various methods works as expected for edge cases. +// Random cases are tested via the various other tests. +func TestFieldBytes(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected string // expected hex encoded bytes + }{ + { + name: "zero", + in: "0", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "field prime (aka 0)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "field prime - 1", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + }, { + name: "field prime + 1 (aka 1, overflow in word zero)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "field prime first 32 bits", + in: "fffffc2f", + expected: "00000000000000000000000000000000000000000000000000000000fffffc2f", + }, { + name: "field prime word zero", + in: "03fffc2f", + expected: "0000000000000000000000000000000000000000000000000000000003fffc2f", + }, { + name: "field prime first 64 bits", + in: "fffffffefffffc2f", + expected: "000000000000000000000000000000000000000000000000fffffffefffffc2f", + }, { + name: "field prime word zero and one", + in: "0ffffefffffc2f", + expected: "000000000000000000000000000000000000000000000000000ffffefffffc2f", + }, { + name: "field prime first 96 bits", + in: "fffffffffffffffefffffc2f", + expected: "0000000000000000000000000000000000000000fffffffffffffffefffffc2f", + }, { + name: "field prime word zero, one, and two", + in: "3ffffffffffefffffc2f", + expected: "000000000000000000000000000000000000000000003ffffffffffefffffc2f", + }, { + name: "overflow in word one (prime + 1<<26)", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff03fffc2f", + expected: "0000000000000000000000000000000000000000000000000000000004000000", + }, { + name: "2^256 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expected: "00000000000000000000000000000000000000000000000000000001000003d0", + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in).Normalize() + expected := hexToBytes(test.expected) + // Ensure getting the bytes works as expected. + gotBytes := f.Bytes() + if !utils.FastEqual(gotBytes[:], expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + *gotBytes, expected, + ) + continue + } + // Ensure getting the bytes directly into an array works as expected. + var b32 [32]byte + f.PutBytes(&b32) + if !utils.FastEqual(b32[:], expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + b32, expected, + ) + continue + } + // Ensure getting the bytes directly into a slice works as expected. + var buffer [64]byte + f.PutBytesUnchecked(buffer[:]) + if !utils.FastEqual(buffer[:32], expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + buffer[:32], expected, + ) + continue + } + } +} + +// TestFieldZero ensures that zeroing a field value works as expected. +func TestFieldZero(t *testing.T) { + var f FieldVal + f.SetHex("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") + f.Zero() + for idx, rawInt := range f.n { + if rawInt != 0 { + t.Errorf( + "internal integer at index #%d is not zero - got %d", idx, + rawInt, + ) + } + } +} + +// TestFieldIsZero ensures that checking if a field is zero via IsZero and +// IsZeroBit works as expected. +func TestFieldIsZero(t *testing.T) { + f := new(FieldVal) + if !f.IsZero() { + t.Errorf("new field value is not zero - got %v (rawints %x)", f, f.n) + } + if f.IsZeroBit() != 1 { + t.Errorf("new field value is not zero - got %v (rawints %x)", f, f.n) + } + f.SetInt(1) + if f.IsZero() { + t.Errorf("claims zero for nonzero field - got %v (rawints %x)", f, f.n) + } + if f.IsZeroBit() == 1 { + t.Errorf("claims zero for nonzero field - got %v (rawints %x)", f, f.n) + } + f.Zero() + if !f.IsZero() { + t.Errorf("claims nonzero for zero field - got %v (rawints %x)", f, f.n) + } + if f.IsZeroBit() != 1 { + t.Errorf("claims nonzero for zero field - got %v (rawints %x)", f, f.n) + } + f.SetInt(1) + f.Zero() + if !f.IsZero() { + t.Errorf("claims zero for nonzero field - got %v (rawints %x)", f, f.n) + } + if f.IsZeroBit() != 1 { + t.Errorf("claims zero for nonzero field - got %v (rawints %x)", f, f.n) + } +} + +// TestFieldIsOne ensures that checking if a field is one via IsOne and IsOneBit +// works as expected. +func TestFieldIsOne(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + normalize bool // whether or not to normalize the test value + expected bool // expected result + }{ + { + name: "zero", + in: "0", + normalize: true, + expected: false, + }, { + name: "one", + in: "1", + normalize: true, + expected: true, + }, { + name: "secp256k1 prime NOT normalized (would be 0)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + normalize: false, + expected: false, + }, { + name: "secp256k1 prime normalized (aka 0)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + normalize: true, + expected: false, + }, { + name: "secp256k1 prime + 1 normalized (aka 1)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + normalize: true, + expected: true, + }, { + name: "secp256k1 prime + 1 NOT normalized (would be 1)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + normalize: false, + expected: false, + }, { + name: "2^26 (one bit in second internal field word", + in: "4000000", + normalize: false, + expected: false, + }, { + name: "2^52 (one bit in third internal field word", + in: "10000000000000", + normalize: false, + expected: false, + }, { + name: "2^78 (one bit in fourth internal field word", + in: "40000000000000000000", + normalize: false, + expected: false, + }, { + name: "2^104 (one bit in fifth internal field word", + in: "100000000000000000000000000", + normalize: false, + expected: false, + }, { + name: "2^130 (one bit in sixth internal field word", + in: "400000000000000000000000000000000", + normalize: false, + expected: false, + }, { + name: "2^156 (one bit in seventh internal field word", + in: "1000000000000000000000000000000000000000", + normalize: false, + expected: false, + }, { + name: "2^182 (one bit in eighth internal field word", + in: "4000000000000000000000000000000000000000000000", + normalize: false, + expected: false, + }, { + name: "2^208 (one bit in ninth internal field word", + in: "10000000000000000000000000000000000000000000000000000", + normalize: false, + expected: false, + }, { + name: "2^234 (one bit in tenth internal field word", + in: "40000000000000000000000000000000000000000000000000000000000", + normalize: false, + expected: false, + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in) + if test.normalize { + f.Normalize() + } + result := f.IsOne() + if result != test.expected { + t.Errorf( + "%s: wrong result -- got: %v, want: %v", test.name, result, + test.expected, + ) + continue + } + result2 := f.IsOneBit() == 1 + if result2 != test.expected { + t.Errorf( + "%s: wrong result -- got: %v, want: %v", test.name, + result2, test.expected, + ) + continue + } + } +} + +// TestFieldStringer ensures the stringer returns the appropriate hex string. +func TestFieldStringer(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected string // expected result + }{ + { + name: "zero", + in: "0", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "one", + in: "1", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "ten", + in: "a", + expected: "000000000000000000000000000000000000000000000000000000000000000a", + }, { + name: "eleven", + in: "b", + expected: "000000000000000000000000000000000000000000000000000000000000000b", + }, { + name: "twelve", + in: "c", + expected: "000000000000000000000000000000000000000000000000000000000000000c", + }, { + name: "thirteen", + in: "d", + expected: "000000000000000000000000000000000000000000000000000000000000000d", + }, { + name: "fourteen", + in: "e", + expected: "000000000000000000000000000000000000000000000000000000000000000e", + }, { + name: "fifteen", + in: "f", + expected: "000000000000000000000000000000000000000000000000000000000000000f", + }, { + name: "240", + in: "f0", + expected: "00000000000000000000000000000000000000000000000000000000000000f0", + }, { + name: "2^26 - 1", + in: "3ffffff", + expected: "0000000000000000000000000000000000000000000000000000000003ffffff", + }, { + name: "2^32 - 1", + in: "ffffffff", + expected: "00000000000000000000000000000000000000000000000000000000ffffffff", + }, { + name: "2^64 - 1", + in: "ffffffffffffffff", + expected: "000000000000000000000000000000000000000000000000ffffffffffffffff", + }, { + name: "2^96 - 1", + in: "ffffffffffffffffffffffff", + expected: "0000000000000000000000000000000000000000ffffffffffffffffffffffff", + }, { + name: "2^128 - 1", + in: "ffffffffffffffffffffffffffffffff", + expected: "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + }, { + name: "2^160 - 1", + in: "ffffffffffffffffffffffffffffffffffffffff", + expected: "000000000000000000000000ffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^192 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffff", + expected: "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^224 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expected: "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^256-4294968273 (the secp256k1 prime, so should result in 0)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + 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) + result := f.String() + if result != test.expected { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, result, + test.expected, + ) + continue + } + } +} + +// TestFieldNormalize ensures that normalizing the internal field words works as +// expected. +func TestFieldNormalize(t *testing.T) { + tests := []struct { + name string // test description + raw [10]uint32 // Intentionally denormalized value + normalized [10]uint32 // Normalized form of the raw value + }{ + { + name: "5", + raw: [10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + normalized: [10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^26", + raw: [10]uint32{0x04000000, 0x0, 0, 0, 0, 0, 0, 0, 0, 0}, + normalized: [10]uint32{0x00000000, 0x1, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^26 + 1", + raw: [10]uint32{0x04000001, 0x0, 0, 0, 0, 0, 0, 0, 0, 0}, + normalized: [10]uint32{0x00000001, 0x1, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^32 - 1", + raw: [10]uint32{0xffffffff, 0x00, 0, 0, 0, 0, 0, 0, 0, 0}, + normalized: [10]uint32{0x03ffffff, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^32", + raw: [10]uint32{0x04000000, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + normalized: [10]uint32{0x00000000, 0x40, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^32 + 1", + raw: [10]uint32{0x04000001, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + normalized: [10]uint32{0x00000001, 0x40, 0, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^64 - 1", + raw: [10]uint32{ + 0xffffffff, 0xffffffc0, 0xfc0, 0, 0, 0, 0, 0, 0, + 0, + }, + normalized: [10]uint32{ + 0x03ffffff, 0x03ffffff, 0xfff, 0, 0, 0, 0, 0, 0, + 0, + }, + }, { + name: "2^64", + raw: [10]uint32{ + 0x04000000, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, + 0, + }, + normalized: [10]uint32{ + 0x00000000, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, + 0, + }, + }, { + name: "2^64 + 1", + raw: [10]uint32{ + 0x04000001, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, + 0, + }, + normalized: [10]uint32{ + 0x00000001, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, + 0, + }, + }, { + name: "2^96 - 1", + raw: [10]uint32{ + 0xffffffff, 0xffffffc0, 0xffffffc0, 0x3ffc0, 0, + 0, 0, 0, 0, 0, + }, + normalized: [10]uint32{ + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, + 0, 0, 0, 0, 0, + }, + }, { + name: "2^96", + raw: [10]uint32{ + 0x04000000, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, + 0, 0, 0, 0, 0, + }, + normalized: [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x40000, 0, + 0, 0, 0, 0, 0, + }, + }, { + name: "2^128 - 1", + raw: [10]uint32{ + 0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffc0, 0, 0, 0, 0, 0, + }, + normalized: [10]uint32{ + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0xffffff, 0, 0, 0, 0, 0, + }, + }, { + name: "2^128", + raw: [10]uint32{ + 0x04000000, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x0ffffff, 0, 0, 0, 0, 0, + }, + normalized: [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x1000000, 0, 0, 0, 0, 0, + }, + }, { + name: "2^256 - 4294968273 (secp256k1 prime)", + raw: [10]uint32{ + 0xfffffc2f, 0xffffff80, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0x3fffc0, + }, + normalized: [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x000000, + }, + }, { + // Value larger than P where both first and second words are larger than + // P's first and second words + name: "Value > P with 1st and 2nd words > P's 1st and 2nd words", + raw: [10]uint32{ + 0xfffffc30, 0xffffff86, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0x3fffc0, + }, + normalized: [10]uint32{ + 0x00000001, 0x00000006, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x000000, + }, + }, { + // Value larger than P where only the second word is larger than P's + // second word. + name: "Value > P with 2nd word > P's 2nd word", + raw: [10]uint32{ + 0xfffffc2a, 0xffffff87, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0x3fffc0, + }, + normalized: [10]uint32{ + 0x03fffffb, 0x00000006, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x000000, + }, + }, { + name: "2^256 - 1", + raw: [10]uint32{ + 0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0xffffffc0, + 0xffffffc0, 0xffffffc0, 0x3fffc0, + }, + normalized: [10]uint32{ + 0x000003d0, 0x00000040, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x000000, + }, + }, { + // Prime with field representation such that the initial reduction does + // not result in a carry to bit 256. + // + // 2^256 - 4294968273 (secp256k1 prime) + name: "2^256 - 4294968273 (secp256k1 prime)", + raw: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + normalized: [10]uint32{ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + }, + }, { + // Value larger than P that reduces to a value which is still larger + // than P when it has a magnitude of 1 due to its first word and does + // not result in a carry to bit 256. + // + // 2^256 - 4294968272 (secp256k1 prime + 1) + name: "2^256 - 4294968272 (secp256k1 prime + 1)", + raw: [10]uint32{ + 0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + normalized: [10]uint32{ + 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + }, + }, { + // Value larger than P that reduces to a value which is still larger + // than P when it has a magnitude of 1 due to its second word and does + // not result in a carry to bit 256. + // + // 2^256 - 4227859409 (secp256k1 prime + 0x4000000) + name: "2^256 - 4227859409 (secp256k1 prime + 0x4000000)", + raw: [10]uint32{ + 0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + normalized: [10]uint32{ + 0x00000000, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + }, + }, { + // Value larger than P that reduces to a value which is still larger + // than P when it has a magnitude of 1 due to a carry to bit 256, but + // would not be without the carry. These values come from the fact that + // P is 2^256 - 4294968273 and 977 is the low order word in the internal + // field representation. + // + // 2^256 * 5 - ((4294968273 - (977+1)) * 4) + name: "2^256 * 5 - ((4294968273 - (977+1)) * 4)", + raw: [10]uint32{ + 0x03ffffff, 0x03fffeff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x0013fffff, + }, + normalized: [10]uint32{ + 0x00001314, 0x00000040, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x000000000, + }, + }, { + // Value larger than P that reduces to a value which is still larger + // than P when it has a magnitude of 1 due to both a carry to bit 256 + // and the first word. + name: "Value > P with redux > P at mag 1 due to 1st word and carry to bit 256", + raw: [10]uint32{ + 0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x07ffffff, 0x003fffff, + }, + normalized: [10]uint32{ + 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000001, + }, + }, { + // Value larger than P that reduces to a value which is still larger + // than P when it has a magnitude of 1 due to both a carry to bit 256 + // and the second word. + name: "Value > P with redux > P at mag 1 due to 2nd word and carry to bit 256", + raw: [10]uint32{ + 0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x3ffffff, 0x07ffffff, 0x003fffff, + }, + normalized: [10]uint32{ + 0x00000000, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x0000000, 0x00000000, 0x00000001, + }, + }, { + // Value larger than P that reduces to a value which is still larger + // than P when it has a magnitude of 1 due to a carry to bit 256 and the + // first and second words. + name: "Value > P with redux > P at mag 1 due to 1st and 2nd words and carry to bit 256", + raw: [10]uint32{ + 0x03fffc30, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x07ffffff, 0x003fffff, + }, + normalized: [10]uint32{ + 0x00000001, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000001, + }, + }, { + // --------------------------------------------------------------------- + // There are 3 main conditions that must be true if the final reduction + // is needed after the initial reduction to magnitude 1 when there was + // NOT a carry to bit 256 (in other words when the original value was < + // 2^256): + // 1) The final word of the reduced value is equal to the one of P + // 2) The 3rd through 9th words are equal to those of P + // 3) Either: + // - The 2nd word is greater than the one of P; or + // - The 2nd word is equal to that of P AND the 1st word is greater + // + // Therefore the eight possible combinations of those 3 main conditions + // can be thought of in binary where each bit starting from the left + // corresponds to the aforementioned conditions as such: + // 000, 001, 010, 011, 100, 101, 110, 111 + // + // For example, combination 6 is when both conditons 1 and 2 are true, + // but condition 3 is NOT true. + // + // The following tests hit each of these combinations and refer to each + // by its decimal equivalent for ease of reference. + // + // NOTE: The final combination (7) is already tested above since it only + // happens when the original value is already the normalized + // representation of P. + // --------------------------------------------------------------------- + name: "Value < 2^256 final reduction combination 0", + raw: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003ffffe, + }, + normalized: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003ffffe, + }, + }, { + name: "Value < 2^256 final reduction combination 1 via 2nd word", + raw: [10]uint32{ + 0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003ffffe, + }, + normalized: [10]uint32{ + 0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003ffffe, + }, + }, { + name: "Value < 2^256 final reduction combination 1 via 1st word", + raw: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003ffffe, + }, + normalized: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003ffffe, + }, + }, { + name: "Value < 2^256 final reduction combination 2", + raw: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003ffffe, + }, + normalized: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003ffffe, + }, + }, { + name: "Value < 2^256 final reduction combination 3 via 2nd word", + raw: [10]uint32{ + 0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003ffffe, + }, + normalized: [10]uint32{ + 0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003ffffe, + }, + }, { + name: "Value < 2^256 final reduction combination 3 via 1st word", + raw: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003ffffe, + }, + normalized: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003ffffe, + }, + }, { + name: "Value < 2^256 final reduction combination 4", + raw: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003fffff, + }, + normalized: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003fffff, + }, + }, { + name: "Value < 2^256 final reduction combination 5 via 2nd word", + raw: [10]uint32{ + 0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003fffff, + }, + normalized: [10]uint32{ + 0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003fffff, + }, + }, { + name: "Value < 2^256 final reduction combination 5 via 1st word", + raw: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003fffff, + }, + normalized: [10]uint32{ + 0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03fffffe, 0x003fffff, + }, + }, { + name: "Value < 2^256 final reduction combination 6", + raw: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + normalized: [10]uint32{ + 0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x003fffff, + }, + }, + } + for _, test := range tests { + f := new(FieldVal) + f.n = test.raw + f.Normalize() + if !reflect.DeepEqual(f.n, test.normalized) { + t.Errorf( + "%s: wrong normalized result\ngot: %x\nwant: %x", + test.name, f.n, test.normalized, + ) + continue + } + } +} + +// TestFieldIsOdd ensures that checking if a field value is odd via IsOdd and +// IsOddBit works as expected. +func TestFieldIsOdd(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded value + expected bool // expected oddness + }{ + { + name: "zero", + in: "0", + expected: false, + }, { + name: "one", + in: "1", + expected: true, + }, { + name: "two", + in: "2", + expected: false, + }, { + name: "2^32 - 1", + in: "ffffffff", + expected: true, + }, { + name: "2^64 - 2", + in: "fffffffffffffffe", + expected: false, + }, { + name: "secp256k1 prime (not normalized so should be incorrect result)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + expected: true, + }, { + name: "secp256k1 prime + 1 (not normalized so should be incorrect result)", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + expected: false, + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in) + result := f.IsOdd() + if result != test.expected { + t.Errorf( + "%s: wrong result -- got: %v, want: %v", test.name, + result, test.expected, + ) + continue + } + result2 := f.IsOddBit() == 1 + if result2 != test.expected { + t.Errorf( + "%s: wrong result -- got: %v, want: %v", test.name, + result2, test.expected, + ) + continue + } + } +} + +// TestFieldEquals ensures that checking two field values for equality via +// Equals works as expected. +func TestFieldEquals(t *testing.T) { + tests := []struct { + name string // test description + in1 string // hex encoded value + in2 string // hex encoded value + expected bool // expected equality + }{ + { + name: "0 == 0?", + in1: "0", + in2: "0", + expected: true, + }, { + name: "0 == 1?", + in1: "0", + in2: "1", + expected: false, + }, { + name: "1 == 0?", + in1: "1", + in2: "0", + expected: false, + }, { + name: "2^32 - 1 == 2^32 - 1?", + in1: "ffffffff", + in2: "ffffffff", + expected: true, + }, { + name: "2^64 - 1 == 2^64 - 2?", + in1: "ffffffffffffffff", + in2: "fffffffffffffffe", + expected: false, + }, { + name: "0 == prime (mod prime)?", + in1: "0", + in2: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + expected: true, + }, { + name: "1 == prime + 1 (mod prime)?", + in1: "1", + in2: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + expected: true, + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in1).Normalize() + f2 := new(FieldVal).SetHex(test.in2).Normalize() + result := f.Equals(f2) + if result != test.expected { + t.Errorf( + "%s: wrong result -- got: %v, want: %v", test.name, result, + test.expected, + ) + continue + } + } +} + +// TestFieldNegate ensures that negating field values via Negate works as +// expected. +func TestFieldNegate(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected string // hex encoded expected result + }{ + { + name: "zero", + in: "0", + expected: "0", + }, { + 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", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + }, { + name: "secp256k1 prime - 1 -> 1", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + expected: "1", + }, { + name: "2 -> secp256k1 prime - 2", + in: "2", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, { + name: "secp256k1 prime - 2 -> 2", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + expected: "2", + }, { + name: "random sampling #1", + in: "b3d9aac9c5e43910b4385b53c7e78c21d4cd5f8e683c633aed04c233efc2e120", + expected: "4c2655363a1bc6ef4bc7a4ac381873de2b32a07197c39cc512fb3dcb103d1b0f", + }, { + name: "random sampling #2", + in: "f8a85984fee5a12a7c8dd08830d83423c937d77c379e4a958e447a25f407733f", + expected: "757a67b011a5ed583722f77cf27cbdc36c82883c861b56a71bb85d90bf888f0", + }, { + name: "random sampling #3", + in: "45ee6142a7fda884211e93352ed6cb2807800e419533be723a9548823ece8312", + expected: "ba119ebd5802577bdee16ccad12934d7f87ff1be6acc418dc56ab77cc131791d", + }, { + name: "random sampling #4", + in: "53c2a668f07e411a2e473e1c3b6dcb495dec1227af27673761d44afe5b43d22b", + expected: "ac3d59970f81bee5d1b8c1e3c49234b6a213edd850d898c89e2bb500a4bc2a04", + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in).Normalize() + expected := new(FieldVal).SetHex(test.expected).Normalize() + // Ensure negating another value produces the expected result. + result := new(FieldVal).NegateVal(f, 1).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result, expected, + ) + continue + } + // Ensure self negating also produces the expected result. + result2 := f.Negate(1).Normalize() + if !result2.Equals(expected) { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result2, expected, + ) + continue + } + } +} + +// TestFieldAddInt ensures that adding an integer to field values via AddInt +// works as expected. +func TestFieldAddInt(t *testing.T) { + tests := []struct { + name string // test description + in1 string // hex encoded value + in2 uint16 // unsigned integer to add to the value above + expected string // expected hex encoded value + }{ + { + name: "zero + one", + in1: "0", + in2: 1, + expected: "1", + }, { + name: "one + zero", + in1: "1", + in2: 0, + expected: "1", + }, { + name: "one + one", + in1: "1", + in2: 1, + expected: "2", + }, { + name: "secp256k1 prime-1 + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: 1, + expected: "0", + }, { + name: "secp256k1 prime + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: 1, + expected: "1", + }, { + name: "random sampling #1", + in1: "ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d6c1", + in2: 0x10f, + expected: "ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d7d0", + }, { + name: "random sampling #2", + in1: "44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41deea9cecf", + in2: 0x3196, + expected: "44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41deeaa0065", + }, { + name: "random sampling #3", + in1: "88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f7105122c9c", + in2: 0x966f, + expected: "88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f710512c30b", + }, { + name: "random sampling #4", + in1: "8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee09a015e2a6", + in2: 0xc54, + expected: "8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee09a015eefa", + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in1).Normalize() + expected := new(FieldVal).SetHex(test.expected).Normalize() + result := f.AddInt(test.in2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: wrong result -- got: %v -- want: %v", test.name, + result, expected, + ) + continue + } + } +} + +// TestFieldAdd ensures that adding two field values together via Add and Add2 +// works as expected. +func TestFieldAdd(t *testing.T) { + tests := []struct { + name string // test description + in1 string // first hex encoded value + in2 string // second hex encoded value to add + expected string // expected hex encoded value + }{ + { + name: "zero + one", + in1: "0", + in2: "1", + expected: "1", + }, { + name: "one + zero", + in1: "1", + in2: "0", + expected: "1", + }, { + name: "secp256k1 prime-1 + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: "1", + expected: "0", + }, { + name: "secp256k1 prime + 1", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: "1", + expected: "1", + }, { + name: "random sampling #1", + in1: "2b2012f975404e5065b4292fb8bed0a5d315eacf24c74d8b27e73bcc5430edcc", + in2: "2c3cefa4e4753e8aeec6ac4c12d99da4d78accefda3b7885d4c6bab46c86db92", + expected: "575d029e59b58cdb547ad57bcb986e4aaaa0b7beff02c610fcadf680c0b7c95e", + }, { + name: "random sampling #2", + in1: "8131e8722fe59bb189692b96c9f38de92885730f1dd39ab025daffb94c97f79c", + in2: "ff5454b765f0aab5f0977dcc629becc84cabeb9def48e79c6aadb2622c490fa9", + expected: "80863d2995d646677a00a9632c8f7ab175315ead0d1c824c9088b21c78e10b16", + }, { + name: "random sampling #3", + in1: "c7c95e93d0892b2b2cdd77e80eb646ea61be7a30ac7e097e9f843af73fad5c22", + in2: "3afe6f91a74dfc1c7f15c34907ee981656c37236d946767dd53ccad9190e437c", + expected: "2c7ce2577d72747abf33b3116a4df00b881ec6785c47ffc74c105d158bba36f", + }, { + name: "random sampling #4", + in1: "fd1c26f6a23381e5d785ba889494ec059369b888ad8431cd67d8c934b580dbe1", + in2: "a475aa5a31dcca90ef5b53c097d9133d6b7117474b41e7877bb199590fc0489c", + expected: "a191d150d4104c76c6e10e492c6dff42fedacfcff8c61954e38a628ec541284e", + }, { + name: "random sampling #5", + in1: "ad82b8d1cc136e23e9fd77fe2c7db1fe5a2ecbfcbde59ab3529758334f862d28", + in2: "4d6a4e95d6d61f4f46b528bebe152d408fd741157a28f415639347a84f6f574b", + expected: "faed0767a2e98d7330b2a0bcea92df3eea060d12380e8ec8b62a9fdb9ef58473", + }, { + name: "random sampling #6", + in1: "f3f43a2540054a86e1df98547ec1c0e157b193e5350fb4a3c3ea214b228ac5e7", + in2: "25706572592690ea3ddc951a1b48b504a4c83dc253756e1b96d56fdfb3199522", + expected: "19649f97992bdb711fbc2d6e9a0a75e5fc79d1a7888522bf5abf912bd5a45eda", + }, { + name: "random sampling #7", + in1: "6915bb94eef13ff1bb9b2633d997e13b9b1157c713363cc0e891416d6734f5b8", + in2: "11f90d6ac6fe1c4e8900b1c85fb575c251ec31b9bc34b35ada0aea1c21eded22", + expected: "7b0ec8ffb5ef5c40449bd7fc394d56fdecfd8980cf6af01bc29c2b898922e2da", + }, { + name: "random sampling #8", + in1: "48b0c9eae622eed9335b747968544eb3e75cb2dc8128388f948aa30f88cabde4", + in2: "0989882b52f85f9d524a3a3061a0e01f46d597839d2ba637320f4b9510c8d2d5", + expected: "523a5216391b4e7685a5aea9c9f52ed32e324a601e53dec6c699eea4999390b9", + }, + } + 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() + // Ensure adding the two values with the result going to another + // variable produces the expected result. + result := new(FieldVal).Add2(f1, f2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + result, expected, + ) + continue + } + // Ensure adding the value to an existing field value produces the + // expected result. + f1.Add(f2).Normalize() + if !f1.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + f1, expected, + ) + continue + } + } +} + +// TestFieldMulInt ensures that multiplying an integer to field values via +// MulInt works as expected. +func TestFieldMulInt(t *testing.T) { + tests := []struct { + name string // test description + in1 string // hex encoded value + in2 uint8 // unsigned integer to multiply with value above + expected string // expected hex encoded value + }{ + { + name: "zero * zero", + in1: "0", + in2: 0, + expected: "0", + }, { + name: "one * zero", + in1: "1", + in2: 0, + expected: "0", + }, { + name: "zero * one", + in1: "0", + in2: 1, + expected: "0", + }, { + name: "one * one", + in1: "1", + in2: 1, + expected: "1", + }, { + name: "secp256k1 prime-1 * 2", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: 2, + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, { + name: "secp256k1 prime * 3", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: 3, + expected: "0", + }, { + name: "secp256k1 prime-1 * 8", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: 8, + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27", + }, { + // Random samples for first value. The second value is limited + // to 8 since that is the maximum int used in the elliptic curve + // calculations. + name: "random sampling #1", + in1: "b75674dc9180d306c692163ac5e089f7cef166af99645c0c23568ab6d967288a", + in2: 6, + expected: "4c06bd2b6904f228a76c8560a3433bced9a8681d985a2848d407404d186b0280", + }, { + name: "random sampling #2", + in1: "54873298ac2b5ba8591c125ae54931f5ea72040aee07b208d6135476fb5b9c0e", + in2: 3, + expected: "fd9597ca048212f90b543710afdb95e1bf560c20ca17161a8239fd64f212d42a", + }, { + name: "random sampling #3", + in1: "7c30fbd363a74c17e1198f56b090b59bbb6c8755a74927a6cba7a54843506401", + in2: 5, + expected: "6cf4eb20f2447c77657fccb172d38c0aa91ea4ac446dc641fa463a6b5091fba7", + }, { + name: "random sampling #3", + in1: "fb4529be3e027a3d1587d8a500b72f2d312e3577340ef5175f96d113be4c2ceb", + in2: 8, + expected: "da294df1f013d1e8ac3ec52805b979698971abb9a077a8bafcb688a4f261820f", + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in1).Normalize() + expected := new(FieldVal).SetHex(test.expected).Normalize() + result := f.MulInt(test.in2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: wrong result -- got: %v -- want: %v", test.name, + result, expected, + ) + continue + } + } +} + +// TestFieldMul ensures that multiplying two field values via Mul and Mul2 works +// as expected. +func TestFieldMul(t *testing.T) { + tests := []struct { + name string // test description + in1 string // first hex encoded value + in2 string // second hex encoded value to multiply with + expected string // expected hex encoded value + }{ + { + name: "zero * zero", + in1: "0", + in2: "0", + expected: "0", + }, { + name: "one * zero", + in1: "1", + in2: "0", + expected: "0", + }, { + name: "zero * one", + in1: "0", + in2: "1", + expected: "0", + }, { + name: "one * one", + in1: "1", + in2: "1", + expected: "1", + }, { + name: "slightly over prime", + in1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff1ffff", + in2: "1000", + expected: "1ffff3d1", + }, { + name: "secp256k1 prime-1 * 2", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: "2", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, { + name: "secp256k1 prime * 3", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + in2: "3", + expected: "0", + }, { + name: "secp256k1 prime * 3", + in1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + in2: "8", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27", + }, { + name: "random sampling #1", + in1: "cfb81753d5ef499a98ecc04c62cb7768c2e4f1740032946db1c12e405248137e", + in2: "58f355ad27b4d75fb7db0442452e732c436c1f7c5a7c4e214fa9cc031426a7d3", + expected: "1018cd2d7c2535235b71e18db9cd98027386328d2fa6a14b36ec663c4c87282b", + }, { + name: "random sampling #2", + in1: "26e9d61d1cdf3920e9928e85fa3df3e7556ef9ab1d14ec56d8b4fc8ed37235bf", + in2: "2dfc4bbe537afee979c644f8c97b31e58be5296d6dbc460091eae630c98511cf", + expected: "da85f48da2dc371e223a1ae63bd30b7e7ee45ae9b189ac43ff357e9ef8cf107a", + }, { + name: "random sampling #3", + in1: "5db64ed5afb71646c8b231585d5b2bf7e628590154e0854c4c29920b999ff351", + in2: "279cfae5eea5d09ade8e6a7409182f9de40981bc31c84c3d3dfe1d933f152e9a", + expected: "2c78fbae91792dd0b157abe3054920049b1879a7cc9d98cfda927d83be411b37", + }, { + name: "random sampling #4", + in1: "b66dfc1f96820b07d2bdbd559c19319a3a73c97ceb7b3d662f4fe75ecb6819e6", + in2: "bf774aba43e3e49eb63a6e18037d1118152568f1a3ac4ec8b89aeb6ff8008ae1", + expected: "c4f016558ca8e950c21c3f7fc15f640293a979c7b01754ee7f8b3340d4902ebb", + }, + } + 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() + // Ensure multiplying the two values with the result going to another + // variable produces the expected result. + result := new(FieldVal).Mul2(f1, f2).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + result, expected, + ) + continue + } + // Ensure multiplying the value to an existing field value produces the + // expected result. + f1.Mul(f2).Normalize() + if !f1.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + f1, expected, + ) + continue + } + } +} + +// TestFieldSquare ensures that squaring field values via Square and SqualVal +// works as expected. +func TestFieldSquare(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded value + expected string // expected hex encoded value + }{ + { + name: "zero", + in: "0", + expected: "0", + }, { + 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", + expected: "1", + }, { + name: "secp256k1 prime - 2", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + expected: "4", + }, { + name: "random sampling #1", + in: "b0ba920360ea8436a216128047aab9766d8faf468895eb5090fc8241ec758896", + expected: "133896b0b69fda8ce9f648b9a3af38f345290c9eea3cbd35bafcadf7c34653d3", + }, { + name: "random sampling #2", + in: "c55d0d730b1d0285a1599995938b042a756e6e8857d390165ffab480af61cbd5", + expected: "cd81758b3f5877cbe7e5b0a10cebfa73bcbf0957ca6453e63ee8954ab7780bee", + }, { + name: "random sampling #3", + in: "e89c1f9a70d93651a1ba4bca5b78658f00de65a66014a25544d3365b0ab82324", + expected: "39ffc7a43e5dbef78fd5d0354fb82c6d34f5a08735e34df29da14665b43aa1f", + }, { + name: "random sampling #4", + in: "7dc26186079d22bcbe1614aa20ae627e62d72f9be7ad1e99cac0feb438956f05", + expected: "bf86bcfc4edb3d81f916853adfda80c07c57745b008b60f560b1912f95bce8ae", + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in).Normalize() + expected := new(FieldVal).SetHex(test.expected).Normalize() + // Ensure squaring the value with the result going to another variable + // produces the expected result. + result := new(FieldVal).SquareVal(f).Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + result, expected, + ) + continue + } + // Ensure self squaring an existing field value produces the expected + // result. + f.Square().Normalize() + if !f.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %v\nwant: %v", test.name, + f, expected, + ) + continue + } + } +} + +// TestFieldSquareRoot ensures that calculating the square root of field values +// via SquareRootVal works as expected for edge cases. +func TestFieldSquareRoot(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded value + valid bool // whether the value has a square root + want string // expected hex encoded value + }{ + { + name: "secp256k1 prime (as 0 in and out)", + in: "0", + valid: true, + want: "0", + }, { + name: "secp256k1 prime (direct val with 0 out)", + 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", + valid: false, + want: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "secp256k1 prime-2", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + valid: false, + want: "210c790573632359b1edb4302c117d8a132654692c3feeb7de3a86ac3f3b53f7", + }, { + name: "(secp256k1 prime-2)^2", + in: "0000000000000000000000000000000000000000000000000000000000000004", + valid: true, + want: "0000000000000000000000000000000000000000000000000000000000000002", + }, { + name: "value 1", + in: "0000000000000000000000000000000000000000000000000000000000000001", + valid: true, + want: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "value 2", + in: "0000000000000000000000000000000000000000000000000000000000000002", + valid: true, + want: "210c790573632359b1edb4302c117d8a132654692c3feeb7de3a86ac3f3b53f7", + }, { + name: "random sampling 1", + in: "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca", + valid: false, + want: "6a27dcfca440cf7930a967be533b9620e397f122787c53958aaa7da7ad3d89a4", + }, { + name: "square of random sampling 1", + in: "f4a8c3738ace0a1c3abf77737ae737f07687b5e24c07a643398298bd96893a18", + valid: true, + want: "e90468feb8565338c9ab2b41dcc33b7478a31df5dedd2db0f8c2d641d77fa165", + }, { + name: "random sampling 2", + in: "69d1323ce9f1f7b3bd3c7320b0d6311408e30281e273e39a0d8c7ee1c8257919", + valid: true, + want: "61f4a7348274a52d75dfe176b8e3aaff61c1c833b6678260ba73def0fb2ad148", + }, { + name: "random sampling 3", + in: "e0debf988ae098ecda07d0b57713e97c6d213db19753e8c95aa12a2fc1cc5272", + valid: false, + want: "6e1cc9c311d33d901670135244f994b1ea39501f38002269b34ce231750cfbac", + }, { + name: "random sampling 4", + in: "dcd394f91f74c2ba16aad74a22bb0ed47fe857774b8f2d6c09e28bfb14642878", + valid: true, + want: "72b22fe6f173f8bcb21898806142ed4c05428601256eafce5d36c1b08fb82bab", + }, + } + for _, test := range tests { + input := new(FieldVal).SetHex(test.in).Normalize() + want := new(FieldVal).SetHex(test.want).Normalize() + // Calculate the square root and enusre the validity flag matches the + // expected value. + var result FieldVal + isValid := result.SquareRootVal(input) + if isValid != test.valid { + t.Errorf( + "%s: mismatched validity -- got %v, want %v", test.name, + isValid, test.valid, + ) + continue + } + // Ensure the calculated result matches the expected value. + result.Normalize() + if !result.Equals(want) { + t.Errorf( + "%s: d wrong result\ngot: %v\nwant: %v", test.name, result, + want, + ) + continue + } + } +} + +// TestFieldSquareRootRandom ensures that calculating the square root for random +// field values works as expected by also performing the same operation with big +// ints and comparing the results. +func TestFieldSquareRootRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate big integer and field value with the same random value. + bigIntVal, fVal := randIntAndFieldVal(t, rng) + // Calculate the square root of the value using big ints. + bigIntResult := new(big.Int).ModSqrt(bigIntVal, curveParams.P) + bigIntHasSqrt := bigIntResult != nil + // Calculate the square root of the value using a field value. + var fValResult FieldVal + fValHasSqrt := fValResult.SquareRootVal(fVal) + // Ensure they match. + if bigIntHasSqrt != fValHasSqrt { + t.Fatalf( + "mismatched square root existence\nbig int in: %x\nfield "+ + "in: %v\nbig int result: %v\nfield result %v", bigIntVal, + fVal, + bigIntHasSqrt, fValHasSqrt, + ) + } + if !fValHasSqrt { + continue + } + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) + fieldValResultHex := fmt.Sprintf("%v", fValResult) + if bigIntResultHex != fieldValResultHex { + t.Fatalf( + "mismatched square root\nbig int in: %x\nfield in: %v\n"+ + "big int result: %x\nfield result %v", bigIntVal, fVal, + bigIntResult, fValResult, + ) + } + } +} + +// TestFieldInverse ensures that finding the multiplicative inverse via Inverse +// works as expected. +func TestFieldInverse(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded value + expected string // expected hex encoded value + }{ + { + name: "zero", + in: "0", + expected: "0", + }, { + 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", + expected: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + }, { + name: "secp256k1 prime - 2", + in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + expected: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe17", + }, { + name: "random sampling #1", + in: "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca", + expected: "987aeb257b063df0c6d1334051c47092b6d8766c4bf10c463786d93f5bc54354", + }, { + name: "random sampling #2", + in: "69d1323ce9f1f7b3bd3c7320b0d6311408e30281e273e39a0d8c7ee1c8257919", + expected: "49340981fa9b8d3dad72de470b34f547ed9179c3953797d0943af67806f4bb6", + }, { + name: "random sampling #3", + in: "e0debf988ae098ecda07d0b57713e97c6d213db19753e8c95aa12a2fc1cc5272", + expected: "64f58077b68af5b656b413ea366863f7b2819f8d27375d9c4d9804135ca220c2", + }, { + name: "random sampling #4", + in: "dcd394f91f74c2ba16aad74a22bb0ed47fe857774b8f2d6c09e28bfb14642878", + expected: "fb848ec64d0be572a63c38fe83df5e7f3d032f60bf8c969ef67d36bf4ada22a9", + }, + } + for _, test := range tests { + f := new(FieldVal).SetHex(test.in).Normalize() + expected := new(FieldVal).SetHex(test.expected).Normalize() + result := f.Inverse().Normalize() + if !result.Equals(expected) { + t.Errorf( + "%s: d wrong result\ngot: %v\nwant: %v", test.name, result, + expected, + ) + continue + } + } +} + +// TestFieldIsGtOrEqPrimeMinusOrder ensures that field values report whether or +// not they are greater than or equal to the field prime minus the group order +// as expected for edge cases. +func TestFieldIsGtOrEqPrimeMinusOrder(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected bool // expected result + }{ + { + name: "zero", + in: "0", + expected: false, + }, { + name: "one", + in: "1", + expected: false, + }, { + name: "p - n - 1", + in: "14551231950b75fc4402da1722fc9baed", + expected: false, + }, { + name: "p - n", + in: "14551231950b75fc4402da1722fc9baee", + expected: true, + }, { + name: "p - n + 1", + in: "14551231950b75fc4402da1722fc9baef", + expected: true, + }, { + name: "over p - n word one", + in: "14551231950b75fc4402da17233c9baee", + expected: true, + }, { + name: "over p - n word two", + in: "14551231950b75fc4403da1722fc9baee", + expected: true, + }, { + name: "over p - n word three", + in: "14551231950b79fc4402da1722fc9baee", + expected: true, + }, { + name: "over p - n word four", + in: "14551241950b75fc4402da1722fc9baee", + expected: true, + }, { + name: "over p - n word five", + in: "54551231950b75fc4402da1722fc9baee", + expected: true, + }, { + name: "over p - n word six", + in: "100000014551231950b75fc4402da1722fc9baee", + expected: true, + }, { + name: "over p - n word seven", + in: "000000000000000000400000000000014551231950b75fc4402da1722fc9baee", + expected: true, + }, { + name: "over p - n word eight", + in: "000000000001000000000000000000014551231950b75fc4402da1722fc9baee", + expected: true, + }, { + name: "over p - n word nine", + in: "000004000000000000000000000000014551231950b75fc4402da1722fc9baee", + expected: true, + }, + } + for _, test := range tests { + result := new(FieldVal).SetHex(test.in).IsGtOrEqPrimeMinusOrder() + if result != test.expected { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result, test.expected, + ) + continue + } + } +} + +// TestFieldIsGtOrEqPrimeMinusOrderRandom ensures that field values report +// whether or not they are greater than or equal to the field prime minus the +// group order as expected by also performing the same operation with big ints +// and comparing the results. +func TestFieldIsGtOrEqPrimeMinusOrderRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + bigPMinusN := new(big.Int).Sub(curveParams.P, curveParams.N) + for i := 0; i < 100; i++ { + // Generate big integer and field value with the same random value. + bigIntVal, fVal := randIntAndFieldVal(t, rng) + // Determine the value is greater than or equal to the prime minus the + // order using big ints. + bigIntResult := bigIntVal.Cmp(bigPMinusN) >= 0 + // Determine the value is greater than or equal to the prime minus the + // order using a field value. + fValResult := fVal.IsGtOrEqPrimeMinusOrder() + // Ensure they match. + if bigIntResult != fValResult { + t.Fatalf( + "mismatched is gt or eq prime minus order\nbig int in: "+ + "%x\nscalar in: %v\nbig int result: %v\nscalar result %v", + bigIntVal, fVal, bigIntResult, fValResult, + ) + } + } +} diff --git a/pkg/crypto/ec/secp256k1/loadprecomputed.go b/pkg/crypto/ec/secp256k1/loadprecomputed.go new file mode 100644 index 0000000..fe35a3e --- /dev/null +++ b/pkg/crypto/ec/secp256k1/loadprecomputed.go @@ -0,0 +1,88 @@ +// Copyright 2015 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "compress/zlib" + "encoding/base64" + "io" + "strings" + "sync" +) + +//go:generate go run genprecomps.go + +// bytePointTable describes a table used to house pre-computed values for +// accelerating scalar base multiplication. +type bytePointTable [32][256]JacobianPoint + +// compressedBytePointsFn is set to a real function by the code generation to +// return the compressed pre-computed values for accelerating scalar base +// multiplication. +var compressedBytePointsFn func() string + +// s256BytePoints houses pre-computed values used to accelerate scalar base +// multiplication such that they are only loaded on first use. +var s256BytePoints = func() func() *bytePointTable { + // mustLoadBytePoints decompresses and deserializes the pre-computed byte + // points used to accelerate scalar base multiplication for the secp256k1 + // curve. + // + // This approach is used since it allows the compile to use significantly + // less ram and be performed much faster than it is with hard-coding the + // final in-memory data structure. At the same time, it is quite fast to + // generate the in-memory data structure on first use with this approach + // versus computing the table. + // + // It will panic on any errors because the data is hard coded and thus any + // errors means something is wrong in the source code. + var data *bytePointTable + mustLoadBytePoints := func() { + // There will be no byte points to load when generating them. + if compressedBytePointsFn == nil { + return + } + bp := compressedBytePointsFn() + // Decompress the pre-computed table used to accelerate scalar base + // multiplication. + decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(bp)) + r, err := zlib.NewReader(decoder) + if err != nil { + panic(err) + } + serialized, err := io.ReadAll(r) + if err != nil { + panic(err) + } + // Deserialize the precomputed byte points and set the memory table to + // them. + offset := 0 + var bytePoints bytePointTable + for byteNum := 0; byteNum < len(bytePoints); byteNum++ { + // All points in this window. + for i := 0; i < len(bytePoints[byteNum]); i++ { + p := &bytePoints[byteNum][i] + p.X.SetByteSlice(serialized[offset:]) + offset += 32 + p.Y.SetByteSlice(serialized[offset:]) + offset += 32 + p.Z.SetInt(1) + } + } + data = &bytePoints + } + // Return a closure that initializes the data on first access. This is done + // because the table takes a non-trivial amount of memory and initializing + // it unconditionally would cause anything that imports the package, either + // directly, or indirectly via transitive deps, to use that memory even if + // the caller never accesses any parts of the package that actually needs + // access to it. + var loadBytePointsOnce sync.Once + return func() *bytePointTable { + loadBytePointsOnce.Do(mustLoadBytePoints) + return data + } +}() diff --git a/pkg/crypto/ec/secp256k1/modnscalar.go b/pkg/crypto/ec/secp256k1/modnscalar.go new file mode 100644 index 0000000..cb84a85 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/modnscalar.go @@ -0,0 +1,1055 @@ +// Copyright (c) 2020-2023 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "math/big" + + "next.orly.dev/pkg/encoders/hex" +) + +// References: +// [SECG]: Recommended Elliptic Curve Domain Parameters +// https://www.secg.org/sec2-v2.pdf +// +// [HAC]: Handbook of Applied Cryptography Menezes, van Oorschot, Vanstone. +// http://cacr.uwaterloo.ca/hac/ + +// Many elliptic curve operations require working with scalars in a finite field +// characterized by the order of the group underlying the secp256k1 curve. +// Given this precision is larger than the biggest available native type, +// obviously some form of bignum math is needed. This code implements +// specialized fixed-precision field arithmetic rather than relying on an +// arbitrary-precision arithmetic package such as math/big for dealing with the +// math modulo the group order since the size is known. As a result, rather +// large performance gains are achieved by taking advantage of many +// optimizations not available to arbitrary-precision arithmetic and generic +// modular arithmetic algorithms. +// +// There are various ways to internally represent each element. For example, +// the most obvious representation would be to use an array of 4 uint64s (64 +// bits * 4 = 256 bits). However, that representation suffers from the fact +// that there is no native Go type large enough to handle the intermediate +// results while adding or multiplying two 64-bit numbers. +// +// Given the above, this implementation represents the field elements as 8 +// uint32s with each word (array entry) treated as base 2^32. This was chosen +// because most systems at the current time are 64-bit (or at least have 64-bit +// registers available for specialized purposes such as MMX) so the intermediate +// results can typically be done using a native register (and using uint64s to +// avoid the need for additional half-word arithmetic) + +const ( + // These fields provide convenient access to each of the words of the + // secp256k1 curve group order N to improve code readability. + // + // The group order of the curve per [SECG] is: + // 0xffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141 + // + // nolint: dupword + orderWordZero uint32 = 0xd0364141 + orderWordOne uint32 = 0xbfd25e8c + orderWordTwo uint32 = 0xaf48a03b + orderWordThree uint32 = 0xbaaedce6 + orderWordFour uint32 = 0xfffffffe + orderWordFive uint32 = 0xffffffff + orderWordSix uint32 = 0xffffffff + orderWordSeven uint32 = 0xffffffff + // These fields provide convenient access to each of the words of the two's + // complement of the secp256k1 curve group order N to improve code + // readability. + // + // The two's complement of the group order is: + // 0x00000000 00000000 00000000 00000001 45512319 50b75fc4 402da173 2fc9bebf + orderComplementWordZero = (^orderWordZero) + 1 + orderComplementWordOne = ^orderWordOne + orderComplementWordTwo = ^orderWordTwo + orderComplementWordThree = ^orderWordThree + // orderComplementWordFour uint32 = ^orderWordFour // unused + // orderComplementWordFive uint32 = ^orderWordFive // unused + // orderComplementWordSix uint32 = ^orderWordSix // unused + // orderComplementWordSeven uint32 = ^orderWordSeven // unused + // These fields provide convenient access to each of the words of the + // secp256k1 curve group order N / 2 to improve code readability and avoid + // the need to recalculate them. + // + // The half order of the secp256k1 curve group is: + // 0x7fffffff ffffffff ffffffff ffffffff 5d576e73 57a4501d dfe92f46 681b20a0 + // + // nolint: dupword + halfOrderWordZero uint32 = 0x681b20a0 + halfOrderWordOne uint32 = 0xdfe92f46 + halfOrderWordTwo uint32 = 0x57a4501d + halfOrderWordThree uint32 = 0x5d576e73 + halfOrderWordFour uint32 = 0xffffffff + halfOrderWordFive uint32 = 0xffffffff + halfOrderWordSix uint32 = 0xffffffff + halfOrderWordSeven uint32 = 0x7fffffff + // uint32Mask is simply a mask with all bits set for a uint32 and is used to + // improve the readability of the code. + uint32Mask = 0xffffffff +) + +var ( + // zero32 is an array of 32 bytes used for the purposes of zeroing and is + // defined here to avoid extra allocations. + zero32 = [32]byte{} +) + +// ModNScalar implements optimized 256-bit constant-time fixed-precision +// arithmetic over the secp256k1 group order. This means all arithmetic is +// performed modulo: +// +// 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 +// +// It only implements the arithmetic needed for elliptic curve operations, +// however, the operations that are not implemented can typically be worked +// around if absolutely needed. For example, subtraction can be performed by +// adding the negation. +// +// Should it be absolutely necessary, conversion to the standard library +// math/big.Int can be accomplished by using the Bytes method, slicing the +// resulting fixed-size array, and feeding it to big.Int.SetBytes. However, +// that should typically be avoided when possible as conversion to big.Ints +// requires allocations, is not constant time, and is slower when working modulo +// the group order. +type ModNScalar struct { + // The scalar is represented as 8 32-bit integers in base 2^32. + // + // The following depicts the internal representation: + // --------------------------------------------------------- + // | n[7] | n[6] | ... | n[0] | + // | 32 bits | 32 bits | ... | 32 bits | + // | Mult: 2^(32*7) | Mult: 2^(32*6) | ... | Mult: 2^(32*0) | + // --------------------------------------------------------- + // + // For example, consider the number 2^87 + 2^42 + 1. It would be + // represented as: + // n[0] = 1 + // n[1] = 2^10 + // n[2] = 2^23 + // n[3..7] = 0 + // + // The full 256-bit value is then calculated by looping i from 7..0 and + // doing sum(n[i] * 2^(32i)) like so: + // n[7] * 2^(32*7) = 0 * 2^224 = 0 + // n[6] * 2^(32*6) = 0 * 2^192 = 0 + // ... + // n[2] * 2^(32*2) = 2^23 * 2^64 = 2^87 + // n[1] * 2^(32*1) = 2^10 * 2^32 = 2^42 + // n[0] * 2^(32*0) = 1 * 2^0 = 1 + // Sum: 0 + 0 + ... + 2^87 + 2^42 + 1 = 2^87 + 2^42 + 1 + n [8]uint32 +} + +// String returns the scalar as a human-readable hex string. +// +// This is NOT constant time. +func (s ModNScalar) String() string { + b := s.Bytes() + return hex.Enc(b[:]) +} + +// Set sets the scalar equal to a copy of the passed one in constant time. +// +// The scalar is returned to support chaining. This enables syntax like: +// s := new(ModNScalar).Set(s2).Add(1) so that s = s2 + 1 where s2 is not +// modified. +func (s *ModNScalar) Set(val *ModNScalar) *ModNScalar { + *s = *val + return s +} + +// Zero sets the scalar to zero in constant time. A newly created scalar is +// already set to zero. This function can be useful to clear an existing scalar +// for reuse. +func (s *ModNScalar) Zero() { + s.n[0] = 0 + s.n[1] = 0 + s.n[2] = 0 + s.n[3] = 0 + s.n[4] = 0 + s.n[5] = 0 + s.n[6] = 0 + s.n[7] = 0 +} + +// IsZeroBit returns 1 when the scalar is equal to zero 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 +// operations require a numeric value. See 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] + return constantTimeEq(bits, 0) +} + +// IsZero returns whether or not the scalar is equal to zero in constant time. +func (s *ModNScalar) IsZero() bool { + // 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] + return bits == 0 +} + +// SetInt sets the scalar to the passed integer in constant time. This is a +// convenience function since it is fairly common to perform some arithmetic +// with small native integers. +// +// The scalar is returned to support chaining. This enables syntax like: +// s := new(ModNScalar).SetInt(2).Mul(s2) so that s = 2 * s2. +func (s *ModNScalar) SetInt(ui uint32) *ModNScalar { + s.Zero() + s.n[0] = ui + return s +} + +// constantTimeEq returns 1 if a == b or 0 otherwise in constant time. +func constantTimeEq(a, b uint32) uint32 { + return uint32((uint64(a^b) - 1) >> 63) +} + +// constantTimeNotEq returns 1 if a != b or 0 otherwise in constant time. +func constantTimeNotEq(a, b uint32) uint32 { + return ^uint32((uint64(a^b)-1)>>63) & 1 +} + +// constantTimeLess returns 1 if a < b or 0 otherwise in constant time. +func constantTimeLess(a, b uint32) uint32 { + return uint32((uint64(a) - uint64(b)) >> 63) +} + +// constantTimeLessOrEq returns 1 if a <= b or 0 otherwise in constant time. +func constantTimeLessOrEq(a, b uint32) uint32 { + return uint32((uint64(a) - uint64(b) - 1) >> 63) +} + +// constantTimeGreater returns 1 if a > b or 0 otherwise in constant time. +func constantTimeGreater(a, b uint32) uint32 { + return constantTimeLess(b, a) +} + +// constantTimeGreaterOrEq returns 1 if a >= b or 0 otherwise in constant time. +func constantTimeGreaterOrEq(a, b uint32) uint32 { + return constantTimeLessOrEq(b, a) +} + +// constantTimeMin returns min(a,b) in constant time. +func constantTimeMin(a, b uint32) uint32 { + return b ^ ((a ^ b) & -constantTimeLess(a, b)) +} + +// overflows determines if the current scalar is greater than or equal to the +// group order in constant time and returns 1 if it is or 0 otherwise. +func (s *ModNScalar) overflows() uint32 { + // The intuition here is that the scalar is greater than the group order if + // one of the higher individual words is greater than corresponding word of + // the group order and all higher words in the scalar are equal to their + // corresponding word of the group order. Since this type is modulo the + // group order, being equal is also an overflow back to 0. + // + // Note that the words 5, 6, and 7 are all the max uint32 value, so there is + // no need to test if those individual words of the scalar exceeds them, + // hence, only equality is checked for them. + highWordsEqual := constantTimeEq(s.n[7], orderWordSeven) + highWordsEqual &= constantTimeEq(s.n[6], orderWordSix) + highWordsEqual &= constantTimeEq(s.n[5], orderWordFive) + overflow := highWordsEqual & constantTimeGreater(s.n[4], orderWordFour) + highWordsEqual &= constantTimeEq(s.n[4], orderWordFour) + overflow |= highWordsEqual & constantTimeGreater(s.n[3], orderWordThree) + highWordsEqual &= constantTimeEq(s.n[3], orderWordThree) + overflow |= highWordsEqual & constantTimeGreater(s.n[2], orderWordTwo) + highWordsEqual &= constantTimeEq(s.n[2], orderWordTwo) + overflow |= highWordsEqual & constantTimeGreater(s.n[1], orderWordOne) + highWordsEqual &= constantTimeEq(s.n[1], orderWordOne) + overflow |= highWordsEqual & constantTimeGreaterOrEq(s.n[0], orderWordZero) + return overflow +} + +// reduce256 reduces the current scalar modulo the group order in accordance +// with the overflows parameter in constant time. The overflows parameter +// specifies whether or not the scalar is known to be greater than the group +// order and MUST either be 1 in the case it is or 0 in the case it is not for a +// correct result. +func (s *ModNScalar) reduce256(overflows uint32) { + // Notice that since s < 2^256 < 2N (where N is the group order), the max + // possible number of reductions required is one. Therefore, in the case a + // reduction is needed, it can be performed with a single subtraction of N. + // Also, recall that subtraction is equivalent to addition by the two's + // complement while ignoring the carry. + // + // When s >= N, the overflows parameter will be 1. Conversely, it will be 0 + // when s < N. Thus multiplying by the overflows parameter will either + // result in 0 or the multiplicand itself. + // + // Combining the above along with the fact that s + 0 = s, the following is + // a constant time implementation that works by either adding 0 or the two's + // complement of N as needed. + // + // The final result will be in the range 0 <= s < N as expected. + overflows64 := uint64(overflows) + c := uint64(s.n[0]) + overflows64*uint64(orderComplementWordZero) + s.n[0] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(s.n[1]) + overflows64*uint64(orderComplementWordOne) + s.n[1] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(s.n[2]) + overflows64*uint64(orderComplementWordTwo) + s.n[2] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(s.n[3]) + overflows64*uint64(orderComplementWordThree) + s.n[3] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(s.n[4]) + overflows64 // * 1 + s.n[4] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(s.n[5]) // + overflows64 * 0 + s.n[5] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(s.n[6]) // + overflows64 * 0 + s.n[6] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(s.n[7]) // + overflows64 * 0 + s.n[7] = uint32(c & uint32Mask) +} + +// SetBytes interprets the provided array as a 256-bit big-endian unsigned +// integer, reduces it modulo the group order, sets the scalar to the result, +// and returns either 1 if it was reduced (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 +// operations require a numeric value. +func (s *ModNScalar) SetBytes(b *[32]byte) uint32 { + // Pack the 256 total bits across the 8 uint32 words. This could be done + // with a for loop, but benchmarks show this unrolled version is about 2 + // times faster than the variant that uses a loop. + s.n[0] = uint32(b[31]) | uint32(b[30])<<8 | uint32(b[29])<<16 | uint32(b[28])<<24 + s.n[1] = uint32(b[27]) | uint32(b[26])<<8 | uint32(b[25])<<16 | uint32(b[24])<<24 + s.n[2] = uint32(b[23]) | uint32(b[22])<<8 | uint32(b[21])<<16 | uint32(b[20])<<24 + s.n[3] = uint32(b[19]) | uint32(b[18])<<8 | uint32(b[17])<<16 | uint32(b[16])<<24 + s.n[4] = uint32(b[15]) | uint32(b[14])<<8 | uint32(b[13])<<16 | uint32(b[12])<<24 + s.n[5] = uint32(b[11]) | uint32(b[10])<<8 | uint32(b[9])<<16 | uint32(b[8])<<24 + s.n[6] = uint32(b[7]) | uint32(b[6])<<8 | uint32(b[5])<<16 | uint32(b[4])<<24 + s.n[7] = uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 + // The value might be >= N, so reduce it as required and return whether or + // not it was reduced. + needsReduce := s.overflows() + s.reduce256(needsReduce) + return needsReduce +} + +// zeroArray32 zeroes the provided 32-byte buffer. +func zeroArray32(b *[32]byte) { copy(b[:], zero32[:]) } + +// SetByteSlice interprets the provided slice as a 256-bit big-endian unsigned +// integer (meaning it is truncated to the first 32 bytes), reduces it modulo +// the group order, sets the scalar to the result, and returns whether or not +// the resulting truncated 256-bit integer overflowed in constant time. +// +// Note that since passing a slice with more than 32 bytes is truncated, it is +// possible that the truncated value is less than the order of the curve and +// hence it will not be reported as having overflowed in that case. It is up to +// the caller to decide whether it needs to provide numbers of the appropriate +// size or it is acceptable to use this function with the described truncation +// and overflow behavior. +func (s *ModNScalar) SetByteSlice(b []byte) bool { + var b32 [32]byte + b = b[:constantTimeMin(uint32(len(b)), 32)] + copy(b32[:], b32[:32-len(b)]) + copy(b32[32-len(b):], b) + result := s.SetBytes(&b32) + zeroArray32(&b32) + return result != 0 +} + +// PutBytesUnchecked unpacks the scalar to a 32-byte big-endian value 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 scalar 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 target slice MUST have at least 32 bytes available +func (s *ModNScalar) PutBytesUnchecked(b []byte) { + // Unpack the 256 total bits from the 8 uint32 words. This could be done + // with a for loop, but benchmarks show this unrolled version is about 2 + // times faster than the variant which uses a loop. + b[31] = byte(s.n[0]) + b[30] = byte(s.n[0] >> 8) + b[29] = byte(s.n[0] >> 16) + b[28] = byte(s.n[0] >> 24) + b[27] = byte(s.n[1]) + b[26] = byte(s.n[1] >> 8) + b[25] = byte(s.n[1] >> 16) + b[24] = byte(s.n[1] >> 24) + b[23] = byte(s.n[2]) + b[22] = byte(s.n[2] >> 8) + b[21] = byte(s.n[2] >> 16) + b[20] = byte(s.n[2] >> 24) + b[19] = byte(s.n[3]) + b[18] = byte(s.n[3] >> 8) + b[17] = byte(s.n[3] >> 16) + b[16] = byte(s.n[3] >> 24) + b[15] = byte(s.n[4]) + b[14] = byte(s.n[4] >> 8) + b[13] = byte(s.n[4] >> 16) + b[12] = byte(s.n[4] >> 24) + b[11] = byte(s.n[5]) + b[10] = byte(s.n[5] >> 8) + b[9] = byte(s.n[5] >> 16) + b[8] = byte(s.n[5] >> 24) + b[7] = byte(s.n[6]) + b[6] = byte(s.n[6] >> 8) + b[5] = byte(s.n[6] >> 16) + b[4] = byte(s.n[6] >> 24) + b[3] = byte(s.n[7]) + b[2] = byte(s.n[7] >> 8) + b[1] = byte(s.n[7] >> 16) + b[0] = byte(s.n[7] >> 24) +} + +// 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. +// +// 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. +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. +func (s *ModNScalar) Bytes() [32]byte { + var b [32]byte + s.PutBytesUnchecked(b[:]) + return b +} + +// IsOdd returns whether the scalar is an odd number in constant time. +func (s *ModNScalar) IsOdd() bool { + // Only odd numbers have the bottom bit set. + return s.n[0]&1 == 1 +} + +// Equals returns whether or not the two scalars are the same in constant time. +func (s *ModNScalar) Equals(val *ModNScalar) bool { + // Xor only sets bits when they are different, so the two scalars can only + // be the same if no bits are set after xoring each word. + bits := (s.n[0] ^ val.n[0]) | (s.n[1] ^ val.n[1]) | (s.n[2] ^ val.n[2]) | + (s.n[3] ^ val.n[3]) | (s.n[4] ^ val.n[4]) | (s.n[5] ^ val.n[5]) | + (s.n[6] ^ val.n[6]) | (s.n[7] ^ val.n[7]) + + return bits == 0 +} + +// Add2 adds the passed two scalars together modulo the group order in constant +// time and stores the result in s. +// +// The scalar is returned to support chaining. This enables syntax like: +// s3.Add2(s, s2).AddInt(1) so that s3 = s + s2 + 1. +func (s *ModNScalar) Add2(val1, val2 *ModNScalar) *ModNScalar { + c := uint64(val1.n[0]) + uint64(val2.n[0]) + s.n[0] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(val1.n[1]) + uint64(val2.n[1]) + s.n[1] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(val1.n[2]) + uint64(val2.n[2]) + s.n[2] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(val1.n[3]) + uint64(val2.n[3]) + s.n[3] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(val1.n[4]) + uint64(val2.n[4]) + s.n[4] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(val1.n[5]) + uint64(val2.n[5]) + s.n[5] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(val1.n[6]) + uint64(val2.n[6]) + s.n[6] = uint32(c & uint32Mask) + c = (c >> 32) + uint64(val1.n[7]) + uint64(val2.n[7]) + s.n[7] = uint32(c & uint32Mask) + // The result is now 256 bits, but it might still be >= N, so use the + // existing normal reduce method for 256-bit values. + s.reduce256(uint32(c>>32) + s.overflows()) + return s +} + +// Add adds the passed scalar to the existing one modulo the group order in +// constant time and stores the result in s. +// +// The scalar is returned to support chaining. This enables syntax like: +// s.Add(s2).AddInt(1) so that s = s + s2 + 1. +func (s *ModNScalar) Add(val *ModNScalar) *ModNScalar { return s.Add2(s, val) } + +// accumulator96 provides a 96-bit accumulator for use in the intermediate +// calculations requiring more than 64-bits. +type accumulator96 struct { + n [3]uint32 +} + +// Add adds the passed unsigned 64-bit value to the accumulator. +func (a *accumulator96) Add(v uint64) { + low := uint32(v & uint32Mask) + hi := uint32(v >> 32) + a.n[0] += low + hi += constantTimeLess(a.n[0], low) // Carry if overflow in n[0]. + a.n[1] += hi + a.n[2] += constantTimeLess(a.n[1], hi) // Carry if overflow in n[1]. +} + +// Rsh32 right shifts the accumulator by 32 bits. +func (a *accumulator96) Rsh32() { + a.n[0] = a.n[1] + a.n[1] = a.n[2] + a.n[2] = 0 +} + +// reduce385 reduces the 385-bit intermediate result in the passed terms modulo +// the group order in constant time and stores the result in s. +func (s *ModNScalar) reduce385(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12 uint64) { + // At this point, the intermediate result in the passed terms has been + // reduced to fit within 385 bits, so reduce it again using the same method + // described in reduce512. As before, the intermediate result will end up + // being reduced by another 127 bits to 258 bits, thus 9 32-bit terms are + // needed for this iteration. The reduced terms are assigned back to t0 + // through t8. + // + // Note that several of the intermediate calculations require adding 64-bit + // products together which would overflow a uint64, so a 96-bit accumulator + // is used instead until the value is reduced enough to use native uint64s. + // Terms for 2^(32*0). + var acc accumulator96 + acc.n[0] = uint32(t0) // == acc.Add(t0) because acc is guaranteed to be 0. + acc.Add(t8 * uint64(orderComplementWordZero)) + t0 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*1). + acc.Add(t1) + acc.Add(t8 * uint64(orderComplementWordOne)) + acc.Add(t9 * uint64(orderComplementWordZero)) + t1 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*2). + acc.Add(t2) + acc.Add(t8 * uint64(orderComplementWordTwo)) + acc.Add(t9 * uint64(orderComplementWordOne)) + acc.Add(t10 * uint64(orderComplementWordZero)) + t2 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*3). + acc.Add(t3) + acc.Add(t8 * uint64(orderComplementWordThree)) + acc.Add(t9 * uint64(orderComplementWordTwo)) + acc.Add(t10 * uint64(orderComplementWordOne)) + acc.Add(t11 * uint64(orderComplementWordZero)) + t3 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*4). + acc.Add(t4) + acc.Add(t8) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t9 * uint64(orderComplementWordThree)) + acc.Add(t10 * uint64(orderComplementWordTwo)) + acc.Add(t11 * uint64(orderComplementWordOne)) + acc.Add(t12 * uint64(orderComplementWordZero)) + t4 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*5). + acc.Add(t5) + // acc.Add(t8 * uint64(orderComplementWordFive)) // 0 + acc.Add(t9) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t10 * uint64(orderComplementWordThree)) + acc.Add(t11 * uint64(orderComplementWordTwo)) + acc.Add(t12 * uint64(orderComplementWordOne)) + t5 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*6). + acc.Add(t6) + // acc.Add(t8 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t9 * uint64(orderComplementWordFive)) // 0 + acc.Add(t10) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t11 * uint64(orderComplementWordThree)) + acc.Add(t12 * uint64(orderComplementWordTwo)) + t6 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*7). + acc.Add(t7) + // acc.Add(t8 * uint64(orderComplementWordSeven)) // 0 + // acc.Add(t9 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t10 * uint64(orderComplementWordFive)) // 0 + acc.Add(t11) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t12 * uint64(orderComplementWordThree)) + t7 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*8). + // acc.Add(t9 * uint64(orderComplementWordSeven)) // 0 + // acc.Add(t10 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t11 * uint64(orderComplementWordFive)) // 0 + acc.Add(t12) // * uint64(orderComplementWordFour) // * 1 + t8 = uint64(acc.n[0]) + // acc.Rsh32() // No need since not used after this. Guaranteed to be 0. + // + // NOTE: All of the remaining multiplications for this iteration result in 0 + // as they all involve multiplying by combinations of the fifth, sixth, and + // seventh words of the two's complement of N, which are 0, so skip them. + // + // At this point, the result is reduced to fit within 258 bits, so reduce it + // again using a slightly modified version of the same method. The maximum + // value in t8 is 2 at this point and therefore multiplying it by each word + // of the two's complement of N and adding it to a 32-bit term will result + // in a maximum requirement of 33 bits, so it is safe to use native uint64s + // here for the intermediate term carry propagation. + // + // Also, since the maximum value in t8 is 2, this ends up reducing by + // another 2 bits to 256 bits. + c := t0 + t8*uint64(orderComplementWordZero) + s.n[0] = uint32(c & uint32Mask) + c = (c >> 32) + t1 + t8*uint64(orderComplementWordOne) + s.n[1] = uint32(c & uint32Mask) + c = (c >> 32) + t2 + t8*uint64(orderComplementWordTwo) + s.n[2] = uint32(c & uint32Mask) + c = (c >> 32) + t3 + t8*uint64(orderComplementWordThree) + s.n[3] = uint32(c & uint32Mask) + c = (c >> 32) + t4 + t8 // * uint64(orderComplementWordFour) == * 1 + s.n[4] = uint32(c & uint32Mask) + c = (c >> 32) + t5 // + t8*uint64(orderComplementWordFive) == 0 + s.n[5] = uint32(c & uint32Mask) + c = (c >> 32) + t6 // + t8*uint64(orderComplementWordSix) == 0 + s.n[6] = uint32(c & uint32Mask) + c = (c >> 32) + t7 // + t8*uint64(orderComplementWordSeven) == 0 + s.n[7] = uint32(c & uint32Mask) + // The result is now 256 bits, but it might still be >= N, so use the + // existing normal reduce method for 256-bit values. + s.reduce256(uint32(c>>32) + s.overflows()) +} + +// reduce512 reduces the 512-bit intermediate result in the passed terms modulo +// the group order down to 385 bits in constant time and stores the result in s. +func (s *ModNScalar) reduce512(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15 uint64) { + // At this point, the intermediate result in the passed terms is grouped + // into the respective bases. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, where log_2(c) < t, + // highly efficient reduction can be achieved per the provided algorithm. + // + // The secp256k1 group order fits this criteria since it is: + // 2^256 - 432420386565659656852420866394968145599 + // + // Technically the max possible value here is (N-1)^2 since the two scalars + // being multiplied are always mod N. Nevertheless, it is safer to consider + // it to be (2^256-1)^2 = 2^512 - 2^256 + 1 since it is the product of two + // 256-bit values. + // + // The algorithm is to reduce the result modulo the prime by subtracting + // multiples of the group order N. However, in order simplify carry + // propagation, this adds with the two's complement of N to achieve the same + // result. + // + // Since the two's complement of N has 127 leading zero bits, this will end + // up reducing the intermediate result from 512 bits to 385 bits, resulting + // in 13 32-bit terms. The reduced terms are assigned back to t0 through + // t12. + // + // Note that several of the intermediate calculations require adding 64-bit + // products together which would overflow a uint64, so a 96-bit accumulator + // is used instead. + // + // Terms for 2^(32*0). + var acc accumulator96 + acc.n[0] = uint32(t0) // == acc.Add(t0) because acc is guaranteed to be 0. + acc.Add(t8 * uint64(orderComplementWordZero)) + t0 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*1). + acc.Add(t1) + acc.Add(t8 * uint64(orderComplementWordOne)) + acc.Add(t9 * uint64(orderComplementWordZero)) + t1 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*2). + acc.Add(t2) + acc.Add(t8 * uint64(orderComplementWordTwo)) + acc.Add(t9 * uint64(orderComplementWordOne)) + acc.Add(t10 * uint64(orderComplementWordZero)) + t2 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*3). + acc.Add(t3) + acc.Add(t8 * uint64(orderComplementWordThree)) + acc.Add(t9 * uint64(orderComplementWordTwo)) + acc.Add(t10 * uint64(orderComplementWordOne)) + acc.Add(t11 * uint64(orderComplementWordZero)) + t3 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*4). + acc.Add(t4) + acc.Add(t8) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t9 * uint64(orderComplementWordThree)) + acc.Add(t10 * uint64(orderComplementWordTwo)) + acc.Add(t11 * uint64(orderComplementWordOne)) + acc.Add(t12 * uint64(orderComplementWordZero)) + t4 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*5). + acc.Add(t5) + // acc.Add(t8 * uint64(orderComplementWordFive)) // 0 + acc.Add(t9) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t10 * uint64(orderComplementWordThree)) + acc.Add(t11 * uint64(orderComplementWordTwo)) + acc.Add(t12 * uint64(orderComplementWordOne)) + acc.Add(t13 * uint64(orderComplementWordZero)) + t5 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*6). + acc.Add(t6) + // acc.Add(t8 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t9 * uint64(orderComplementWordFive)) // 0 + acc.Add(t10) // * uint64(orderComplementWordFour)) // * 1 + acc.Add(t11 * uint64(orderComplementWordThree)) + acc.Add(t12 * uint64(orderComplementWordTwo)) + acc.Add(t13 * uint64(orderComplementWordOne)) + acc.Add(t14 * uint64(orderComplementWordZero)) + t6 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*7). + acc.Add(t7) + // acc.Add(t8 * uint64(orderComplementWordSeven)) // 0 + // acc.Add(t9 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t10 * uint64(orderComplementWordFive)) // 0 + acc.Add(t11) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t12 * uint64(orderComplementWordThree)) + acc.Add(t13 * uint64(orderComplementWordTwo)) + acc.Add(t14 * uint64(orderComplementWordOne)) + acc.Add(t15 * uint64(orderComplementWordZero)) + t7 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*8). + // acc.Add(t9 * uint64(orderComplementWordSeven)) // 0 + // acc.Add(t10 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t11 * uint64(orderComplementWordFive)) // 0 + acc.Add(t12) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t13 * uint64(orderComplementWordThree)) + acc.Add(t14 * uint64(orderComplementWordTwo)) + acc.Add(t15 * uint64(orderComplementWordOne)) + t8 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*9). + // acc.Add(t10 * uint64(orderComplementWordSeven)) // 0 + // acc.Add(t11 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t12 * uint64(orderComplementWordFive)) // 0 + acc.Add(t13) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t14 * uint64(orderComplementWordThree)) + acc.Add(t15 * uint64(orderComplementWordTwo)) + t9 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*10). + // acc.Add(t11 * uint64(orderComplementWordSeven)) // 0 + // acc.Add(t12 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t13 * uint64(orderComplementWordFive)) // 0 + acc.Add(t14) // * uint64(orderComplementWordFour) // * 1 + acc.Add(t15 * uint64(orderComplementWordThree)) + t10 = uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*11). + // acc.Add(t12 * uint64(orderComplementWordSeven)) // 0 + // acc.Add(t13 * uint64(orderComplementWordSix)) // 0 + // acc.Add(t14 * uint64(orderComplementWordFive)) // 0 + acc.Add(t15) // * uint64(orderComplementWordFour) // * 1 + t11 = uint64(acc.n[0]) + acc.Rsh32() + // NOTE: All of the remaining multiplications for this iteration result in 0 + // as they all involve multiplying by combinations of the fifth, sixth, and + // seventh words of the two's complement of N, which are 0, so skip them. + // + // Terms for 2^(32*12). + t12 = uint64(acc.n[0]) + // acc.Rsh32() // No need since not used after this. Guaranteed to be 0. + // + // At this point, the result is reduced to fit within 385 bits, so reduce it + // again using the same method accordingly. + s.reduce385(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) +} + +// Mul2 multiplies the passed two scalars together modulo the group order in +// constant time and stores the result in s. +// +// The scalar is returned to support chaining. This enables syntax like: +// s3.Mul2(s, s2).AddInt(1) so that s3 = (s * s2) + 1. +func (s *ModNScalar) Mul2(val, val2 *ModNScalar) *ModNScalar { + // This could be done with for loops and an array to store the intermediate + // terms, but this unrolled version is significantly faster. + // + // The overall strategy employed here is: + // 1) Calculate the 512-bit product of the two scalars using the standard + // pencil-and-paper method. + // 2) Reduce the result modulo the prime by effectively subtracting + // multiples of the group order N (actually performed by adding multiples + // of the two's complement of N to avoid implementing subtraction). + // 3) Repeat step 2 noting that each iteration reduces the required number + // of bits by 127 because the two's complement of N has 127 leading zero + // bits. + // 4) Once reduced to 256 bits, call the existing reduce method to perform + // a final reduction as needed. + // + // Note that several of the intermediate calculations require adding 64-bit + // products together which would overflow a uint64, so a 96-bit accumulator + // is used instead. + // + // Terms for 2^(32*0). + var acc accumulator96 + acc.Add(uint64(val.n[0]) * uint64(val2.n[0])) + t0 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*1). + acc.Add(uint64(val.n[0]) * uint64(val2.n[1])) + acc.Add(uint64(val.n[1]) * uint64(val2.n[0])) + t1 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*2). + acc.Add(uint64(val.n[0]) * uint64(val2.n[2])) + acc.Add(uint64(val.n[1]) * uint64(val2.n[1])) + acc.Add(uint64(val.n[2]) * uint64(val2.n[0])) + t2 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*3). + acc.Add(uint64(val.n[0]) * uint64(val2.n[3])) + acc.Add(uint64(val.n[1]) * uint64(val2.n[2])) + acc.Add(uint64(val.n[2]) * uint64(val2.n[1])) + acc.Add(uint64(val.n[3]) * uint64(val2.n[0])) + t3 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*4). + acc.Add(uint64(val.n[0]) * uint64(val2.n[4])) + acc.Add(uint64(val.n[1]) * uint64(val2.n[3])) + acc.Add(uint64(val.n[2]) * uint64(val2.n[2])) + acc.Add(uint64(val.n[3]) * uint64(val2.n[1])) + acc.Add(uint64(val.n[4]) * uint64(val2.n[0])) + t4 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*5). + acc.Add(uint64(val.n[0]) * uint64(val2.n[5])) + acc.Add(uint64(val.n[1]) * uint64(val2.n[4])) + acc.Add(uint64(val.n[2]) * uint64(val2.n[3])) + acc.Add(uint64(val.n[3]) * uint64(val2.n[2])) + acc.Add(uint64(val.n[4]) * uint64(val2.n[1])) + acc.Add(uint64(val.n[5]) * uint64(val2.n[0])) + t5 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*6). + acc.Add(uint64(val.n[0]) * uint64(val2.n[6])) + acc.Add(uint64(val.n[1]) * uint64(val2.n[5])) + acc.Add(uint64(val.n[2]) * uint64(val2.n[4])) + acc.Add(uint64(val.n[3]) * uint64(val2.n[3])) + acc.Add(uint64(val.n[4]) * uint64(val2.n[2])) + acc.Add(uint64(val.n[5]) * uint64(val2.n[1])) + acc.Add(uint64(val.n[6]) * uint64(val2.n[0])) + t6 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*7). + acc.Add(uint64(val.n[0]) * uint64(val2.n[7])) + acc.Add(uint64(val.n[1]) * uint64(val2.n[6])) + acc.Add(uint64(val.n[2]) * uint64(val2.n[5])) + acc.Add(uint64(val.n[3]) * uint64(val2.n[4])) + acc.Add(uint64(val.n[4]) * uint64(val2.n[3])) + acc.Add(uint64(val.n[5]) * uint64(val2.n[2])) + acc.Add(uint64(val.n[6]) * uint64(val2.n[1])) + acc.Add(uint64(val.n[7]) * uint64(val2.n[0])) + t7 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*8). + acc.Add(uint64(val.n[1]) * uint64(val2.n[7])) + acc.Add(uint64(val.n[2]) * uint64(val2.n[6])) + acc.Add(uint64(val.n[3]) * uint64(val2.n[5])) + acc.Add(uint64(val.n[4]) * uint64(val2.n[4])) + acc.Add(uint64(val.n[5]) * uint64(val2.n[3])) + acc.Add(uint64(val.n[6]) * uint64(val2.n[2])) + acc.Add(uint64(val.n[7]) * uint64(val2.n[1])) + t8 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*9). + acc.Add(uint64(val.n[2]) * uint64(val2.n[7])) + acc.Add(uint64(val.n[3]) * uint64(val2.n[6])) + acc.Add(uint64(val.n[4]) * uint64(val2.n[5])) + acc.Add(uint64(val.n[5]) * uint64(val2.n[4])) + acc.Add(uint64(val.n[6]) * uint64(val2.n[3])) + acc.Add(uint64(val.n[7]) * uint64(val2.n[2])) + t9 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*10). + acc.Add(uint64(val.n[3]) * uint64(val2.n[7])) + acc.Add(uint64(val.n[4]) * uint64(val2.n[6])) + acc.Add(uint64(val.n[5]) * uint64(val2.n[5])) + acc.Add(uint64(val.n[6]) * uint64(val2.n[4])) + acc.Add(uint64(val.n[7]) * uint64(val2.n[3])) + t10 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*11). + acc.Add(uint64(val.n[4]) * uint64(val2.n[7])) + acc.Add(uint64(val.n[5]) * uint64(val2.n[6])) + acc.Add(uint64(val.n[6]) * uint64(val2.n[5])) + acc.Add(uint64(val.n[7]) * uint64(val2.n[4])) + t11 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*12). + acc.Add(uint64(val.n[5]) * uint64(val2.n[7])) + acc.Add(uint64(val.n[6]) * uint64(val2.n[6])) + acc.Add(uint64(val.n[7]) * uint64(val2.n[5])) + t12 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*13). + acc.Add(uint64(val.n[6]) * uint64(val2.n[7])) + acc.Add(uint64(val.n[7]) * uint64(val2.n[6])) + t13 := uint64(acc.n[0]) + acc.Rsh32() + // Terms for 2^(32*14). + acc.Add(uint64(val.n[7]) * uint64(val2.n[7])) + t14 := uint64(acc.n[0]) + acc.Rsh32() + // What's left is for 2^(32*15). + t15 := uint64(acc.n[0]) + // acc.Rsh32() // No need since not used after this. Guaranteed to be 0. + // + // At this point, all of the terms are grouped into their respective base + // and occupy up to 512 bits. Reduce the result accordingly. + s.reduce512( + t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, + t15, + ) + return s +} + +// Mul multiplies the passed scalar with the existing one modulo the group order +// in constant time and stores the result in s. +// +// The scalar is returned to support chaining. This enables syntax like: +// s.Mul(s2).AddInt(1) so that s = (s * s2) + 1. +func (s *ModNScalar) Mul(val *ModNScalar) *ModNScalar { + return s.Mul2(s, val) +} + +// SquareVal squares the passed scalar modulo the group order in constant time +// and stores the result in s. +// +// The scalar is returned to support chaining. This enables syntax like: +// s3.SquareVal(s).Mul(s) so that s3 = s^2 * s = s^3. +func (s *ModNScalar) SquareVal(val *ModNScalar) *ModNScalar { + // This could technically be optimized slightly to take advantage of the + // fact that many of the intermediate calculations in squaring are just + // doubling, however, benchmarking has shown that due to the need to use a + // 96-bit accumulator, any savings are essentially offset by that and + // consequently there is no real difference in performance over just + // multiplying the value by itself to justify the extra code for now. This + // can be revisited in the future if it becomes a bottleneck in practice. + return s.Mul2(val, val) +} + +// Square squares the scalar modulo the group order in constant time. The +// existing scalar is modified. +// +// The scalar is returned to support chaining. This enables syntax like: +// s.Square().Mul(s2) so that s = s^2 * s2. +func (s *ModNScalar) Square() *ModNScalar { + return s.SquareVal(s) +} + +// NegateVal negates the passed scalar modulo the group order and stores the +// result in s in constant time. +// +// The scalar is returned to support chaining. This enables syntax like: +// s.NegateVal(s2).AddInt(1) so that s = -s2 + 1. +func (s *ModNScalar) NegateVal(val *ModNScalar) *ModNScalar { + // Since the scalar is already in the range 0 <= val < N, where N is the + // group order, negation modulo the group order is just the group order + // minus the value. This implies that the result will always be in the + // desired range with the sole exception of 0 because N - 0 = N itself. + // + // Therefore, in order to avoid the need to reduce the result for every + // other case in order to achieve constant time, this creates a mask that is + // all 0s in the case of the scalar being negated is 0 and all 1s otherwise + // and bitwise ands that mask with each word. + // + // Finally, to simplify the carry propagation, this adds the two's + // complement of the scalar to N in order to achieve the same result. + bits := val.n[0] | val.n[1] | val.n[2] | val.n[3] | val.n[4] | val.n[5] | + val.n[6] | val.n[7] + mask := uint64(uint32Mask * constantTimeNotEq(bits, 0)) + c := uint64(orderWordZero) + (uint64(^val.n[0]) + 1) + s.n[0] = uint32(c & mask) + c = (c >> 32) + uint64(orderWordOne) + uint64(^val.n[1]) + s.n[1] = uint32(c & mask) + c = (c >> 32) + uint64(orderWordTwo) + uint64(^val.n[2]) + s.n[2] = uint32(c & mask) + c = (c >> 32) + uint64(orderWordThree) + uint64(^val.n[3]) + s.n[3] = uint32(c & mask) + c = (c >> 32) + uint64(orderWordFour) + uint64(^val.n[4]) + s.n[4] = uint32(c & mask) + c = (c >> 32) + uint64(orderWordFive) + uint64(^val.n[5]) + s.n[5] = uint32(c & mask) + c = (c >> 32) + uint64(orderWordSix) + uint64(^val.n[6]) + s.n[6] = uint32(c & mask) + c = (c >> 32) + uint64(orderWordSeven) + uint64(^val.n[7]) + s.n[7] = uint32(c & mask) + return s +} + +// Negate negates the scalar modulo the group order in constant time. The +// existing scalar is modified. +// +// The scalar is returned to support chaining. This enables syntax like: +// s.Negate().AddInt(1) so that s = -s + 1. +func (s *ModNScalar) Negate() *ModNScalar { return s.NegateVal(s) } + +// InverseValNonConst finds the modular multiplicative inverse of the passed +// scalar and stores result in s in *non-constant* time. +// +// The scalar is returned to support chaining. This enables syntax like: +// s3.InverseVal(s1).Mul(s2) so that s3 = s1^-1 * s2. +func (s *ModNScalar) InverseValNonConst(val *ModNScalar) *ModNScalar { + // This is making use of big integers for now. Ideally it will be replaced + // with an implementation that does not depend on big integers. + valBytes := val.Bytes() + bigVal := new(big.Int).SetBytes(valBytes[:]) + bigVal.ModInverse(bigVal, curveParams.N) + s.SetByteSlice(bigVal.Bytes()) + return s +} + +// InverseNonConst finds the modular multiplicative inverse of the scalar in +// *non-constant* time. The existing scalar is modified. +// +// The scalar is returned to support chaining. This enables syntax like: +// s.Inverse().Mul(s2) so that s = s^-1 * s2. +func (s *ModNScalar) InverseNonConst() *ModNScalar { + return s.InverseValNonConst(s) +} + +// IsOverHalfOrder returns whether the scalar exceeds the group order +// divided by 2 in constant time. +func (s *ModNScalar) IsOverHalfOrder() bool { + // The intuition here is that the scalar is greater than half of the group + // order if one of the higher individual words is greater than the + // corresponding word of the half group order and all higher words in the + // scalar are equal to their corresponding word of the half group order. + // + // Note that the words 4, 5, and 6 are all the max uint32 value, so there is + // no need to test if those individual words of the scalar exceeds them, + // hence, only equality is checked for them. + result := constantTimeGreater(s.n[7], halfOrderWordSeven) + highWordsEqual := constantTimeEq(s.n[7], halfOrderWordSeven) + highWordsEqual &= constantTimeEq(s.n[6], halfOrderWordSix) + highWordsEqual &= constantTimeEq(s.n[5], halfOrderWordFive) + highWordsEqual &= constantTimeEq(s.n[4], halfOrderWordFour) + result |= highWordsEqual & constantTimeGreater(s.n[3], halfOrderWordThree) + highWordsEqual &= constantTimeEq(s.n[3], halfOrderWordThree) + result |= highWordsEqual & constantTimeGreater(s.n[2], halfOrderWordTwo) + highWordsEqual &= constantTimeEq(s.n[2], halfOrderWordTwo) + result |= highWordsEqual & constantTimeGreater(s.n[1], halfOrderWordOne) + highWordsEqual &= constantTimeEq(s.n[1], halfOrderWordOne) + result |= highWordsEqual & constantTimeGreater(s.n[0], halfOrderWordZero) + return result != 0 +} diff --git a/pkg/crypto/ec/secp256k1/modnscalar_bench_test.go b/pkg/crypto/ec/secp256k1/modnscalar_bench_test.go new file mode 100644 index 0000000..847734f --- /dev/null +++ b/pkg/crypto/ec/secp256k1/modnscalar_bench_test.go @@ -0,0 +1,274 @@ +// Copyright (c) 2020-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "math/big" + "testing" +) + +// benchmarkVals returns the raw bytes for a couple of unsigned 256-bit +// big-endian integers used throughout the benchmarks. +func benchmarkVals() [2][]byte { + return [2][]byte{ + hexToBytes("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364143"), + hexToBytes("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364144"), + } +} + +// BenchmarkBigIntModN benchmarks setting and reducing an unsigned 256-bit +// big-endian integer modulo the group order with stdlib big integers. +func BenchmarkBigIntModN(b *testing.B) { + buf := benchmarkVals()[0] + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v := new(big.Int).SetBytes(buf) + v.Mod(v, curveParams.N) + } +} + +// BenchmarkModNScalar benchmarks setting and reducing an unsigned 256-bit +// big-endian integer modulo the group order with the specialized type. +func BenchmarkModNScalar(b *testing.B) { + slice := benchmarkVals()[0] + var buf [32]byte + copy(buf[:], slice) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + var s ModNScalar + s.SetBytes(&buf) + } +} + +// BenchmarkBigIntZero benchmarks zeroing an unsigned 256-bit big-endian +// integer modulo the group order with stdlib big integers. +func BenchmarkBigIntZero(b *testing.B) { + v1 := new(big.Int).SetBytes(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v1.SetUint64(0) + } +} + +// BenchmarkModNScalarZero benchmarks zeroing an unsigned 256-bit big-endian +// integer modulo the group order with the specialized type. +func BenchmarkModNScalarZero(b *testing.B) { + var s1 ModNScalar + s1.SetByteSlice(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + s1.Zero() + } +} + +// BenchmarkBigIntIsZero benchmarks determining if an unsigned 256-bit +// big-endian integer modulo the group order is zero with stdlib big integers. +func BenchmarkBigIntIsZero(b *testing.B) { + v1 := new(big.Int).SetBytes(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = v1.Sign() == 0 + } +} + +// BenchmarkModNScalarIsZero benchmarks determining if an unsigned 256-bit +// big-endian integer modulo the group order is zero with the specialized type. +func BenchmarkModNScalarIsZero(b *testing.B) { + var s1 ModNScalar + s1.SetByteSlice(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = s1.IsZero() + } +} + +// BenchmarkBigIntEquals benchmarks determining equality between two unsigned +// 256-bit big-endian integers modulo the group order with stdlib big integers. +func BenchmarkBigIntEquals(b *testing.B) { + bufs := benchmarkVals() + v1 := new(big.Int).SetBytes(bufs[0]) + v2 := new(big.Int).SetBytes(bufs[1]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v1.Cmp(v2) + } +} + +// BenchmarkModNScalarEquals benchmarks determining equality between two +// unsigned 256-bit big-endian integers modulo the group order with the +// specialized type. +func BenchmarkModNScalarEquals(b *testing.B) { + bufs := benchmarkVals() + var s1, s2 ModNScalar + s1.SetByteSlice(bufs[0]) + s2.SetByteSlice(bufs[1]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + s1.Equals(&s2) + } +} + +// BenchmarkBigIntAddModN benchmarks adding two unsigned 256-bit big-endian +// integers modulo the group order with stdlib big integers. +func BenchmarkBigIntAddModN(b *testing.B) { + bufs := benchmarkVals() + v1 := new(big.Int).SetBytes(bufs[0]) + v2 := new(big.Int).SetBytes(bufs[1]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result := new(big.Int).Add(v1, v2) + result.Mod(result, curveParams.N) + } +} + +// BenchmarkModNScalarAdd benchmarks adding two unsigned 256-bit big-endian +// integers modulo the group order with the specialized type. +func BenchmarkModNScalarAdd(b *testing.B) { + bufs := benchmarkVals() + var s1, s2 ModNScalar + s1.SetByteSlice(bufs[0]) + s2.SetByteSlice(bufs[1]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = new(ModNScalar).Add2(&s1, &s2) + } +} + +// BenchmarkBigIntMulModN benchmarks multiplying two unsigned 256-bit big-endian +// integers modulo the group order with stdlib big integers. +func BenchmarkBigIntMulModN(b *testing.B) { + bufs := benchmarkVals() + v1 := new(big.Int).SetBytes(bufs[0]) + v2 := new(big.Int).SetBytes(bufs[1]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result := new(big.Int).Mul(v1, v2) + result.Mod(result, curveParams.N) + } +} + +// BenchmarkModNScalarMul benchmarks multiplying two unsigned 256-bit big-endian +// integers modulo the group order with the specialized type. +func BenchmarkModNScalarMul(b *testing.B) { + bufs := benchmarkVals() + var s1, s2 ModNScalar + s1.SetByteSlice(bufs[0]) + s2.SetByteSlice(bufs[1]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = new(ModNScalar).Mul2(&s1, &s2) + } +} + +// BenchmarkBigIntSquareModN benchmarks squaring an unsigned 256-bit big-endian +// integer modulo the group order is zero with stdlib big integers. +func BenchmarkBigIntSquareModN(b *testing.B) { + v1 := new(big.Int).SetBytes(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result := new(big.Int).Mul(v1, v1) + result.Mod(result, curveParams.N) + } +} + +// BenchmarkModNScalarSquare benchmarks squaring an unsigned 256-bit big-endian +// integer modulo the group order is zero with the specialized type. +func BenchmarkModNScalarSquare(b *testing.B) { + var s1 ModNScalar + s1.SetByteSlice(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = new(ModNScalar).SquareVal(&s1) + } +} + +// BenchmarkBigIntNegateModN benchmarks negating an unsigned 256-bit big-endian +// integer modulo the group order is zero with stdlib big integers. +func BenchmarkBigIntNegateModN(b *testing.B) { + v1 := new(big.Int).SetBytes(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result := new(big.Int).Neg(v1) + result.Mod(result, curveParams.N) + } +} + +// BenchmarkModNScalarNegate benchmarks negating an unsigned 256-bit big-endian +// integer modulo the group order is zero with the specialized type. +func BenchmarkModNScalarNegate(b *testing.B) { + var s1 ModNScalar + s1.SetByteSlice(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = new(ModNScalar).NegateVal(&s1) + } +} + +// BenchmarkBigIntInverseModN benchmarks calculating the multiplicative inverse +// of an unsigned 256-bit big-endian integer modulo the group order is zero with +// stdlib big integers. +func BenchmarkBigIntInverseModN(b *testing.B) { + v1 := new(big.Int).SetBytes(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + new(big.Int).ModInverse(v1, curveParams.N) + } +} + +// BenchmarkModNScalarInverse benchmarks calculating the multiplicative inverse +// of an unsigned 256-bit big-endian integer modulo the group order is zero with +// the specialized type. +func BenchmarkModNScalarInverse(b *testing.B) { + var s1 ModNScalar + s1.SetByteSlice(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = new(ModNScalar).InverseValNonConst(&s1) + } +} + +// BenchmarkBigIntIsOverHalfOrder benchmarks determining if an unsigned 256-bit +// big-endian integer modulo the group order exceeds half the group order with +// stdlib big integers. +func BenchmarkBigIntIsOverHalfOrder(b *testing.B) { + v1 := new(big.Int).SetBytes(benchmarkVals()[0]) + bigHalfOrder := new(big.Int).Rsh(curveParams.N, 1) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = v1.Cmp(bigHalfOrder) + } +} + +// BenchmarkModNScalarIsOverHalfOrder benchmarks determining if an unsigned +// 256-bit big-endian integer modulo the group order exceeds half the group +// order with the specialized type. +func BenchmarkModNScalarIsOverHalfOrder(b *testing.B) { + var s1 ModNScalar + s1.SetByteSlice(benchmarkVals()[0]) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + s1.IsOverHalfOrder() + } +} diff --git a/pkg/crypto/ec/secp256k1/modnscalar_test.go b/pkg/crypto/ec/secp256k1/modnscalar_test.go new file mode 100644 index 0000000..98b4758 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/modnscalar_test.go @@ -0,0 +1,1323 @@ +// Copyright (c) 2020-2023 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "fmt" + "math/big" + "math/rand" + "reflect" + "testing" + "time" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/encoders/hex" + "next.orly.dev/pkg/utils" +) + +// 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. +// +// 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.Dec(hexString) + s.SetByteSlice(bytes) + return s +} + +// randModNScalar returns a mod N scalar created from a random value generated +// by the passed rng. +func randModNScalar(t *testing.T, rng *rand.Rand) *ModNScalar { + t.Helper() + var buf [32]byte + if _, err := rng.Read(buf[:]); chk.T(err) { + t.Fatalf("failed to read random: %v", err) + } + // Create and return a mod N scalar. + var modNVal ModNScalar + modNVal.SetBytes(&buf) + return &modNVal +} + +// randIntAndModNScalar returns a big integer and mod N scalar both created from +// the same random value generated by the passed rng. +func randIntAndModNScalar(t *testing.T, rng *rand.Rand) ( + *big.Int, + *ModNScalar, +) { + t.Helper() + var buf [32]byte + if _, err := rng.Read(buf[:]); chk.T(err) { + t.Fatalf("failed to read random: %v", err) + } + // Create and return both a big integer and a mod N scalar. + bigIntVal := new(big.Int).SetBytes(buf[:]) + bigIntVal.Mod(bigIntVal, curveParams.N) + var modNVal ModNScalar + modNVal.SetBytes(&buf) + return bigIntVal, &modNVal +} + +// TestModNScalarZero ensures that zeroing a scalar modulo the group order works +// as expected. +func TestModNScalarZero(t *testing.T) { + var s ModNScalar + s.SetHex("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") + s.Zero() + for idx, rawInt := range s.n { + if rawInt != 0 { + t.Errorf( + "internal integer at index #%d is not zero - got %d", idx, + rawInt, + ) + } + } +} + +// TestModNScalarIsZero ensures that checking if a scalar is zero via IsZero and +// IsZeroBit works as expected. +func TestModNScalarIsZero(t *testing.T) { + var s ModNScalar + if !s.IsZero() { + t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n) + } + if s.IsZeroBit() != 1 { + t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n) + } + s.SetInt(1) + if s.IsZero() { + t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n) + } + if s.IsZeroBit() == 1 { + t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n) + } + + s.SetInt(0) + if !s.IsZero() { + t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) + } + if s.IsZeroBit() != 1 { + t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) + } + s.SetInt(1) + s.Zero() + if !s.IsZero() { + t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) + } + if s.IsZeroBit() != 1 { + t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n) + } +} + +// TestModNScalarSetInt ensures that setting a scalar to various native integers +// works as expected. +func TestModNScalarSetInt(t *testing.T) { + tests := []struct { + name string // test description + in uint32 // test value + expected [8]uint32 // expected raw ints + }{ + { + name: "five", + in: 5, + expected: [8]uint32{5, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "group order word zero", + in: orderWordZero, + expected: [8]uint32{orderWordZero, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "group order word zero + 1", + in: orderWordZero + 1, + expected: [8]uint32{orderWordZero + 1, 0, 0, 0, 0, 0, 0, 0}, + }, { + name: "2^32 - 1", + in: 4294967295, + expected: [8]uint32{4294967295, 0, 0, 0, 0, 0, 0, 0}, + }, + } + for _, test := range tests { + s := new(ModNScalar).SetInt(test.in) + if !reflect.DeepEqual(s.n, test.expected) { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, s.n, + test.expected, + ) + continue + } + } +} + +// TestModNScalarSetBytes ensures that setting a scalar to a 256-bit big-endian +// unsigned integer via both the slice and array methods works as expected for +// edge cases. Random cases are tested via the various other tests. +func TestModNScalarSetBytes(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected [8]uint32 // expected raw ints + overflow bool // expected overflow result + }{ + { + name: "zero", + in: "00", + expected: [8]uint32{0, 0, 0, 0, 0, 0, 0, 0}, + overflow: false, + }, { + name: "group order (aka 0)", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + expected: [8]uint32{0, 0, 0, 0, 0, 0, 0, 0}, + overflow: true, + }, { + name: "group order - 1", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + expected: [8]uint32{ + 0xd0364140, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, + 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, + }, + overflow: false, + }, { + name: "group order + 1 (aka 1, overflow in word zero)", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + expected: [8]uint32{1, 0, 0, 0, 0, 0, 0, 0}, + overflow: true, + }, { + name: "group order word zero", + in: "d0364141", + expected: [8]uint32{0xd0364141, 0, 0, 0, 0, 0, 0, 0}, + overflow: false, + }, { + name: "group order word zero and one", + in: "bfd25e8cd0364141", + expected: [8]uint32{0xd0364141, 0xbfd25e8c, 0, 0, 0, 0, 0, 0}, + overflow: false, + }, { + name: "group order words zero, one, and two", + in: "af48a03bbfd25e8cd0364141", + expected: [8]uint32{ + 0xd0364141, 0xbfd25e8c, 0xaf48a03b, 0, 0, 0, 0, 0, + }, + overflow: false, + }, { + name: "overflow in word one", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8dd0364141", + expected: [8]uint32{0, 1, 0, 0, 0, 0, 0, 0}, + overflow: true, + }, { + name: "overflow in word two", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03cbfd25e8cd0364141", + expected: [8]uint32{0, 0, 1, 0, 0, 0, 0, 0}, + overflow: true, + }, { + name: "overflow in word three", + in: "fffffffffffffffffffffffffffffffebaaedce7af48a03bbfd25e8cd0364141", + expected: [8]uint32{0, 0, 0, 1, 0, 0, 0, 0}, + overflow: true, + }, { + name: "overflow in word four", + in: "ffffffffffffffffffffffffffffffffbaaedce6af48a03bbfd25e8cd0364141", + expected: [8]uint32{0, 0, 0, 0, 1, 0, 0, 0}, + overflow: true, + }, { + name: "(group order - 1) * 2 NOT mod N, truncated >32 bytes", + in: "01fffffffffffffffffffffffffffffffd755db9cd5e9140777fa4bd19a06c8284", + expected: [8]uint32{ + 0x19a06c82, 0x777fa4bd, 0xcd5e9140, 0xfd755db9, + 0xffffffff, 0xffffffff, 0xffffffff, 0x01ffffff, + }, + overflow: false, + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: [8]uint32{ + 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, + 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, + }, + overflow: false, + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: [8]uint32{ + 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, + 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, + }, + overflow: false, + }, + } + for _, test := range tests { + inBytes := hexToBytes(test.in) + + // Ensure setting the bytes via the slice method works as expected. + var s ModNScalar + overflow := s.SetByteSlice(inBytes) + if !reflect.DeepEqual(s.n, test.expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, s.n, + test.expected, + ) + continue + } + // Ensure the setting the bytes via the slice method produces the + // expected overflow result. + if overflow != test.overflow { + t.Errorf( + "%s: unexpected overflow -- got: %v, want: %v", test.name, + overflow, test.overflow, + ) + continue + } + // Ensure setting the bytes via the array method works as expected. + var s2 ModNScalar + var b32 [32]byte + truncatedInBytes := inBytes + if len(truncatedInBytes) > 32 { + truncatedInBytes = truncatedInBytes[:32] + } + copy(b32[32-len(truncatedInBytes):], truncatedInBytes) + overflow = s2.SetBytes(&b32) != 0 + if !reflect.DeepEqual(s2.n, test.expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + s2.n, test.expected, + ) + continue + } + // Ensure the setting the bytes via the array method produces the + // expected overflow result. + if overflow != test.overflow { + t.Errorf( + "%s: unexpected overflow -- got: %v, want: %v", test.name, + overflow, test.overflow, + ) + continue + } + } +} + +// TestModNScalarBytes ensures that retrieving the bytes for a 256-bit +// big-endian unsigned integer via the various methods works as expected for +// edge cases. Random cases are tested via the various other tests. +func TestModNScalarBytes(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected string // expected hex encoded bytes + overflow bool // expected overflow result + }{ + { + name: "zero", + in: "0", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "group order (aka 0)", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "group order - 1", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + }, { + name: "group order + 1 (aka 1, overflow in word zero)", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "group order word zero", + in: "d0364141", + expected: "00000000000000000000000000000000000000000000000000000000d0364141", + }, { + name: "group order word zero and one", + in: "bfd25e8cd0364141", + expected: "000000000000000000000000000000000000000000000000bfd25e8cd0364141", + }, { + name: "group order words zero, one, and two", + in: "af48a03bbfd25e8cd0364141", + expected: "0000000000000000000000000000000000000000af48a03bbfd25e8cd0364141", + }, { + name: "overflow in word one", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8dd0364141", + expected: "0000000000000000000000000000000000000000000000000000000100000000", + }, { + name: "overflow in word two", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03cbfd25e8cd0364141", + expected: "0000000000000000000000000000000000000000000000010000000000000000", + }, { + name: "overflow in word three", + in: "fffffffffffffffffffffffffffffffebaaedce7af48a03bbfd25e8cd0364141", + expected: "0000000000000000000000000000000000000001000000000000000000000000", + }, { + name: "overflow in word four", + in: "ffffffffffffffffffffffffffffffffbaaedce6af48a03bbfd25e8cd0364141", + expected: "0000000000000000000000000000000100000000000000000000000000000000", + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + }, + } + for _, test := range tests { + s := new(ModNScalar).SetHex(test.in) + expected := hexToBytes(test.expected) + // Ensure getting the bytes works as expected. + gotBytes := s.Bytes() + if !utils.FastEqual(gotBytes[:], expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + gotBytes, expected, + ) + continue + } + // Ensure getting the bytes directly into an array works as expected. + var b32 [32]byte + s.PutBytes(&b32) + if !utils.FastEqual(b32[:], expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + b32, expected, + ) + continue + } + // Ensure getting the bytes directly into a slice works as expected. + var buffer [64]byte + s.PutBytesUnchecked(buffer[:]) + if !utils.FastEqual(buffer[:32], expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + buffer[:32], expected, + ) + continue + } + } +} + +// TestModNScalarIsOdd ensures that checking if a scalar is odd works as +// expected. +func TestModNScalarIsOdd(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded value + expected bool // expected oddness + }{ + { + name: "zero", + in: "0", + expected: false, + }, { + name: "one", + in: "1", + expected: true, + }, { + name: "two", + in: "2", + expected: false, + }, { + name: "2^32 - 1", + in: "ffffffff", + expected: true, + }, { + name: "2^64 - 2", + in: "fffffffffffffffe", + expected: false, + }, { + name: "group order (aka 0)", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + expected: false, + }, { + name: "group order + 1 (aka 1)", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + expected: true, + }, + } + for _, test := range tests { + result := new(ModNScalar).SetHex(test.in).IsOdd() + if result != test.expected { + t.Errorf( + "%s: wrong result -- got: %v, want: %v", test.name, + result, test.expected, + ) + continue + } + } +} + +// TestModNScalarEquals ensures that checking two scalars for equality works as +// expected for edge cases. +func TestModNScalarEquals(t *testing.T) { + tests := []struct { + name string // test description + in1 string // hex encoded value + in2 string // hex encoded value + expected bool // expected equality + }{ + { + name: "0 == 0?", + in1: "0", + in2: "0", + expected: true, + }, { + name: "0 == 1?", + in1: "0", + in2: "1", + expected: false, + }, { + name: "1 == 0?", + in1: "1", + in2: "0", + expected: false, + }, { + name: "2^32 - 1 == 2^32 - 1?", + in1: "ffffffff", + in2: "ffffffff", + expected: true, + }, { + name: "2^64 - 1 == 2^64 - 2?", + in1: "ffffffffffffffff", + in2: "fffffffffffffffe", + expected: false, + }, { + name: "0 == group order?", + in1: "0", + in2: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + expected: true, + }, { + name: "1 == group order + 1?", + in1: "1", + in2: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + expected: true, + }, + } + for _, test := range tests { + s1 := new(ModNScalar).SetHex(test.in1) + s2 := new(ModNScalar).SetHex(test.in2) + result := s1.Equals(s2) + if result != test.expected { + t.Errorf( + "%s: wrong result -- got: %v, want: %v", test.name, result, + test.expected, + ) + continue + } + } +} + +// TestModNScalarEqualsRandom ensures that scalars for random values works as +// expected. +func TestModNScalarEqualsRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Ensure a randomly-generated scalar equals itself. + s := randModNScalar(t, rng) + if !s.Equals(s) { + t.Fatalf("failed equality check\nscalar in: %v", s) + } + // Flip a random bit in a random word and ensure it's no longer equal. + randomWord := rng.Int31n(int32(len(s.n))) + randomBit := uint32(1 << uint32(rng.Int31n(32))) + s2 := new(ModNScalar).Set(s) + s2.n[randomWord] ^= randomBit + if s2.Equals(s) { + t.Fatalf("failed inequality check\nscalar in: %v", s2) + } + } +} + +// TestModNScalarAdd ensures that adding two scalars works as expected for edge +// cases. +func TestModNScalarAdd(t *testing.T) { + tests := []struct { + name string // test description + in1 string // first hex encoded test value + in2 string // second hex encoded test value + expected string // expected hex encoded bytes + }{ + { + name: "zero + one", + in1: "0", + in2: "1", + expected: "1", + }, { + name: "one + zero", + in1: "1", + in2: "0", + expected: "1", + }, { + name: "group order (aka 0) + 1 (gets reduced, no overflow)", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + in2: "1", + expected: "1", + }, { + name: "group order - 1 + 1 (aka 0, overflow to prime)", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + in2: "1", + expected: "0", + }, { + name: "group order - 1 + 2 (aka 1, overflow in word zero)", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + in2: "2", + expected: "1", + }, { + name: "overflow in word one", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8bd0364141", + in2: "100000001", + expected: "1", + }, { + name: "overflow in word two", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03abfd25e8cd0364141", + in2: "10000000000000001", + expected: "1", + }, { + name: "overflow in word three", + in1: "fffffffffffffffffffffffffffffffebaaedce5af48a03bbfd25e8cd0364141", + in2: "1000000000000000000000001", + expected: "1", + }, { + name: "overflow in word four", + in1: "fffffffffffffffffffffffffffffffdbaaedce6af48a03bbfd25e8cd0364141", + in2: "100000000000000000000000000000001", + expected: "1", + }, { + name: "overflow in word five", + in1: "fffffffffffffffffffffffefffffffebaaedce6af48a03bbfd25e8cd0364141", + in2: "10000000000000000000000000000000000000001", + expected: "1", + }, { + name: "overflow in word six", + in1: "fffffffffffffffefffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + in2: "1000000000000000000000000000000000000000000000001", + expected: "1", + }, { + name: "overflow in word seven", + in1: "fffffffefffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + in2: "100000000000000000000000000000000000000000000000000000001", + expected: "1", + }, { + name: "alternating bits", + in1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + in2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: "14551231950b75fc4402da1732fc9bebe", + }, { + name: "alternating bits 2", + in1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + in2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: "14551231950b75fc4402da1732fc9bebe", + }, + } + 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) + + // Ensure the result has the expected value. + s1.Add(s2) + if !s1.Equals(expected) { + t.Errorf( + "%s: unexpected result\ngot: %x\nwant: %x", test.name, + s1, expected, + ) + continue + } + } +} + +// TestModNScalarAddRandom ensures that adding two scalars together for random +// values works as expected by also performing the same operation with big ints +// and comparing the results. +func TestModNScalarAddRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate two big integer and mod n scalar pairs. + bigIntVal1, modNVal1 := randIntAndModNScalar(t, rng) + bigIntVal2, modNVal2 := randIntAndModNScalar(t, rng) + + // Calculate the sum of the values using big ints. + bigIntResult := new(big.Int).Add(bigIntVal1, bigIntVal2) + bigIntResult.Mod(bigIntResult, curveParams.N) + // Calculate the sum of the values using mod n scalars. + modNValResult := new(ModNScalar).Add2(modNVal1, modNVal2) + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) + modNResultHex := fmt.Sprintf("%v", modNValResult) + if bigIntResultHex != modNResultHex { + t.Fatalf( + "mismatched add\nbig int in 1: %x\nbig int in 2: %x\n"+ + "scalar in 1: %v\nscalar in 2: %v\nbig int result: %x\nscalar "+ + "result %v", bigIntVal1, bigIntVal2, modNVal1, modNVal2, + bigIntResult, modNValResult, + ) + } + } +} + +// TestAccumulator96Add ensures that the internal 96-bit accumulator used by +// multiplication works as expected for overflow edge cases including overflow. +func TestAccumulator96Add(t *testing.T) { + tests := []struct { + name string // test description + start accumulator96 // starting value of accumulator + in uint64 // value to add to accumulator + expected accumulator96 // expected value of accumulator after addition + }{ + { + name: "0 + 0 = 0", + start: accumulator96{[3]uint32{0, 0, 0}}, + in: 0, + expected: accumulator96{[3]uint32{0, 0, 0}}, + }, { + name: "overflow in word zero", + start: accumulator96{[3]uint32{0xffffffff, 0, 0}}, + in: 1, + expected: accumulator96{[3]uint32{0, 1, 0}}, + }, { + name: "overflow in word one", + start: accumulator96{[3]uint32{0, 0xffffffff, 0}}, + in: 0x100000000, + expected: accumulator96{[3]uint32{0, 0, 1}}, + }, { + name: "overflow in words one and two", + start: accumulator96{[3]uint32{0xffffffff, 0xffffffff, 0}}, + in: 1, + expected: accumulator96{[3]uint32{0, 0, 1}}, + }, { + // Start accumulator at 129127208455837319175 which is the result of + // 4294967295 * 4294967295 accumulated seven times. + name: "max result from eight adds of max uint32 multiplications", + start: accumulator96{[3]uint32{7, 4294967282, 6}}, + in: 18446744065119617025, + expected: accumulator96{[3]uint32{8, 4294967280, 7}}, + }, + } + for _, test := range tests { + acc := test.start + acc.Add(test.in) + if acc.n != test.expected.n { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, acc.n, + test.expected.n, + ) + } + } +} + +// TestModNScalarMul ensures that multiplying two scalars together works as +// expected for edge cases. +func TestModNScalarMul(t *testing.T) { + tests := []struct { + name string // test description + in1 string // first hex encoded value + in2 string // second hex encoded value to multiply with + expected string // expected hex encoded value + }{ + { + name: "zero * zero", + in1: "0", + in2: "0", + expected: "0", + }, { + name: "one * zero", + in1: "1", + in2: "0", + expected: "0", + }, { + name: "one * one", + in1: "1", + in2: "1", + expected: "1", + }, { + name: "(group order-1) * 2", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + in2: "2", + expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f", + }, { + name: "(group order-1) * (group order-1)", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + in2: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + expected: "1", + }, { + name: "slightly over group order", + in1: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", + in2: "2", + expected: "1", + }, { + name: "group order (aka 0) * 3", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + in2: "3", + expected: "0", + }, { + name: "overflow in word eight", + in1: "100000000000000000000000000000000", + in2: "100000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf", + }, { + name: "overflow in word nine", + in1: "1000000000000000000000000000000000000", + in2: "1000000000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf00000000", + }, { + name: "overflow in word ten", + in1: "10000000000000000000000000000000000000000", + in2: "10000000000000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf0000000000000000", + }, { + name: "overflow in word eleven", + in1: "100000000000000000000000000000000000000000000", + in2: "100000000000000000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf000000000000000000000000", + }, { + name: "overflow in word twelve", + in1: "1000000000000000000000000000000000000000000000000", + in2: "1000000000000000000000000000000000000000000000000", + expected: "4551231950b75fc4402da1732fc9bec04551231950b75fc4402da1732fc9bebf", + }, { + name: "overflow in word thirteen", + in1: "10000000000000000000000000000000000000000000000000000", + in2: "10000000000000000000000000000000000000000000000000000", + expected: "50b75fc4402da1732fc9bec09d671cd51b343a1b66926b57d2a4c1c61536bda7", + }, { + name: "overflow in word fourteen", + in1: "100000000000000000000000000000000000000000000000000000000", + in2: "100000000000000000000000000000000000000000000000000000000", + expected: "402da1732fc9bec09d671cd581c69bc59509b0b074ec0aea8f564d667ec7eb3c", + }, { + name: "overflow in word fifteen", + in1: "1000000000000000000000000000000000000000000000000000000000000", + in2: "1000000000000000000000000000000000000000000000000000000000000", + expected: "2fc9bec09d671cd581c69bc5e697f5e41f12c33a0a7b6f4e3302b92ea029cecd", + }, { + name: "double overflow in internal accumulator", + in1: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + in2: "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c2", + expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b7f", + }, { + name: "alternating bits", + in1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + in2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: "88edea3d29272800e7988455cfdf19b039dbfbb1c93b5b44a48c2ba462316838", + }, { + name: "alternating bits 2", + in1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + in2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: "88edea3d29272800e7988455cfdf19b039dbfbb1c93b5b44a48c2ba462316838", + }, + } + for _, test := range tests { + v1 := new(ModNScalar).SetHex(test.in1) + v2 := new(ModNScalar).SetHex(test.in2) + expected := new(ModNScalar).SetHex(test.expected) + // Ensure multiplying two other values produces the expected result. + result := new(ModNScalar).Mul2(v1, v2) + if !result.Equals(expected) { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, result, + expected, + ) + continue + } + // Ensure self multiplying with another value also produces the expected + // result. + result2 := new(ModNScalar).Set(v1).Mul(v2) + if !result2.Equals(expected) { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, result2, + expected, + ) + continue + } + } +} + +// TestModNScalarMulRandom ensures that multiplying two scalars together for +// random values works as expected by also performing the same operation with +// big ints and comparing the results. +func TestModNScalarMulRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate two big integer and mod n scalar pairs. + bigIntVal1, modNVal1 := randIntAndModNScalar(t, rng) + bigIntVal2, modNVal2 := randIntAndModNScalar(t, rng) + // Calculate the square of the value using big ints. + bigIntResult := new(big.Int).Mul(bigIntVal1, bigIntVal2) + bigIntResult.Mod(bigIntResult, curveParams.N) + // Calculate the square of the value using mod n scalar. + modNValResult := new(ModNScalar).Mul2(modNVal1, modNVal2) + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) + modNResultHex := fmt.Sprintf("%v", modNValResult) + if bigIntResultHex != modNResultHex { + t.Fatalf( + "mismatched mul\nbig int in 1: %x\nbig int in 2: %x\n"+ + "scalar in 1: %v\nscalar in 2: %v\nbig int result: %x\nscalar "+ + "result %v", bigIntVal1, bigIntVal2, modNVal1, modNVal2, + bigIntResult, modNValResult, + ) + } + } +} + +// TestModNScalarSquare ensures that squaring scalars works as expected for edge +// cases. +func TestModNScalarSquare(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected string // expected hex encoded value + }{ + { + name: "zero", + in: "0", + expected: "0", + }, { + name: "one", + in: "1", + expected: "1", + }, { + name: "over group order", + in: "0000000000000000000000000000000100000000000000000000000000000000", + expected: "000000000000000000000000000000014551231950b75fc4402da1732fc9bebf", + }, { + name: "group order - 1", + in: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "overflow in word eight", + in: "100000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf", + }, { + name: "overflow in word nine", + in: "1000000000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf00000000", + }, { + name: "overflow in word ten", + in: "10000000000000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf0000000000000000", + }, { + name: "overflow in word eleven", + in: "100000000000000000000000000000000000000000000", + expected: "14551231950b75fc4402da1732fc9bebf000000000000000000000000", + }, { + name: "overflow in word twelve", + in: "1000000000000000000000000000000000000000000000000", + expected: "4551231950b75fc4402da1732fc9bec04551231950b75fc4402da1732fc9bebf", + }, { + name: "overflow in word thirteen", + in: "10000000000000000000000000000000000000000000000000000", + expected: "50b75fc4402da1732fc9bec09d671cd51b343a1b66926b57d2a4c1c61536bda7", + }, { + name: "overflow in word fourteen", + in: "100000000000000000000000000000000000000000000000000000000", + expected: "402da1732fc9bec09d671cd581c69bc59509b0b074ec0aea8f564d667ec7eb3c", + }, { + name: "overflow in word fifteen", + in: "1000000000000000000000000000000000000000000000000000000000000", + expected: "2fc9bec09d671cd581c69bc5e697f5e41f12c33a0a7b6f4e3302b92ea029cecd", + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: "fb0982c5761d1eac534247f2a7c3af186a134d709b977ca88300faad5eafe9bc", + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: "9081c595b95b2d17c424a546144b25488104c5889d914635bc9d1a51859e1c19", + }, + } + for _, test := range tests { + v := new(ModNScalar).SetHex(test.in) + expected := new(ModNScalar).SetHex(test.expected) + // Ensure squaring another value produces the expected result. + result := new(ModNScalar).SquareVal(v) + if !result.Equals(expected) { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, result, + expected, + ) + continue + } + // Ensure self squaring also produces the expected result. + result2 := new(ModNScalar).Set(v).Square() + if !result2.Equals(expected) { + t.Errorf( + "%s: wrong result\ngot: %v\nwant: %v", test.name, result2, + expected, + ) + continue + } + } +} + +// TestModNScalarSquareRandom ensures that squaring scalars for random values +// works as expected by also performing the same operation with big ints and +// comparing the results. +func TestModNScalarSquareRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate big integer and mod n scalar with the same random value. + bigIntVal, modNVal := randIntAndModNScalar(t, rng) + // Calculate the square of the value using big ints. + bigIntResult := new(big.Int).Mul(bigIntVal, bigIntVal) + bigIntResult.Mod(bigIntResult, curveParams.N) + // Calculate the square of the value using mod n scalar. + modNValResult := new(ModNScalar).SquareVal(modNVal) + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) + modNResultHex := fmt.Sprintf("%v", modNValResult) + if bigIntResultHex != modNResultHex { + t.Fatalf( + "mismatched square\nbig int in: %x\nscalar in: %v\n"+ + "big int result: %x\nscalar result %v", bigIntVal, modNVal, + bigIntResult, modNValResult, + ) + } + } +} + +// TestModNScalarNegate ensures that negating scalars works as expected for edge +// cases. +func TestModNScalarNegate(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected string // hex encoded expected result + }{ + { + name: "zero", + in: "0", + expected: "0", + }, { + name: "one", + in: "1", + expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + }, { + name: "negation in word one", + in: "0000000000000000000000000000000000000000000000000000000100000000", + expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8bd0364141", + }, { + name: "negation in word two", + in: "0000000000000000000000000000000000000000000000010000000000000000", + expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03abfd25e8cd0364141", + }, { + name: "negation in word three", + in: "0000000000000000000000000000000000000001000000000000000000000000", + expected: "fffffffffffffffffffffffffffffffebaaedce5af48a03bbfd25e8cd0364141", + }, { + name: "negation in word four", + in: "0000000000000000000000000000000100000000000000000000000000000000", + expected: "fffffffffffffffffffffffffffffffdbaaedce6af48a03bbfd25e8cd0364141", + }, { + name: "negation in word five", + in: "0000000000000000000000010000000000000000000000000000000000000000", + expected: "fffffffffffffffffffffffefffffffebaaedce6af48a03bbfd25e8cd0364141", + }, { + name: "negation in word six", + in: "0000000000000001000000000000000000000000000000000000000000000000", + expected: "fffffffffffffffefffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + }, { + name: "negation in word seven", + in: "0000000100000000000000000000000000000000000000000000000000000000", + expected: "fffffffefffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a591509374109a2fa961a2cb8e72a909b9c", + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a46054828c54ee45e16578043275dbe6e7", + }, + } + for _, test := range tests { + s := new(ModNScalar).SetHex(test.in) + expected := new(ModNScalar).SetHex(test.expected) + // Ensure negating another value produces the expected result. + result := new(ModNScalar).NegateVal(s) + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result, expected, + ) + continue + } + // Ensure self negating also produces the expected result. + result2 := new(ModNScalar).Set(s).Negate() + if !result2.Equals(expected) { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result2, expected, + ) + continue + } + } +} + +// TestModNScalarNegateRandom ensures that negating scalars for random values +// works as expected by also performing the same operation with big ints and +// comparing the results. +func TestModNScalarNegateRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate big integer and mod n scalar with the same random value. + bigIntVal, modNVal := randIntAndModNScalar(t, rng) + // Calculate the negation of the value using big ints. + bigIntResult := new(big.Int).Neg(bigIntVal) + bigIntResult.Mod(bigIntResult, curveParams.N) + // Calculate the negation of the value using mod n scalar. + modNValResult := new(ModNScalar).NegateVal(modNVal) + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) + modNResultHex := fmt.Sprintf("%v", modNValResult) + if bigIntResultHex != modNResultHex { + t.Fatalf( + "mismatched negate\nbig int in: %x\nscalar in: %v\n"+ + "big int result: %x\nscalar result %v", bigIntVal, modNVal, + bigIntResult, modNValResult, + ) + } + } +} + +// TestModNScalarInverseNonConst ensures that calculating the multiplicative +// inverse of scalars in *non-constant* time works as expected for edge cases. +func TestModNScalarInverseNonConst(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected string // hex encoded expected result + }{ + { + name: "zero", + in: "0", + expected: "0", + }, { + name: "one", + in: "1", + expected: "1", + }, { + name: "inverse carry in word one", + in: "0000000000000000000000000000000000000000000000000000000100000000", + expected: "5588b13effffffffffffffffffffffff934e5b00ca8417bf50177f7ba415411a", + }, { + name: "inverse carry in word two", + in: "0000000000000000000000000000000000000000000000010000000000000000", + expected: "4b0dff665588b13effffffffffffffffa09f710af01555259d4ad302583de6dc", + }, { + name: "inverse carry in word three", + in: "0000000000000000000000000000000000000001000000000000000000000000", + expected: "34b9ec244b0dff665588b13effffffffbcff4127932a971a78274c9d74176b38", + }, { + name: "inverse carry in word four", + in: "0000000000000000000000000000000100000000000000000000000000000000", + expected: "50a51ac834b9ec244b0dff665588b13e9984d5b3cf80ef0fd6a23766a3ee9f22", + }, { + name: "inverse carry in word five", + in: "0000000000000000000000010000000000000000000000000000000000000000", + expected: "27cfab5e50a51ac834b9ec244b0dff6622f16e85b683d5a059bcd5a3b29d9dff", + }, { + name: "inverse carry in word six", + in: "0000000000000001000000000000000000000000000000000000000000000000", + expected: "897f30c127cfab5e50a51ac834b9ec239c53f268b4700c14f19b9499ac58d8ad", + }, { + name: "inverse carry in word seven", + in: "0000000100000000000000000000000000000000000000000000000000000000", + expected: "6494ef93897f30c127cfab5e50a51ac7b4e8f713e0cddd182234e907286ae6b3", + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + expected: "cb6086e560b8597a85c934e46f5b6e8a445bf3f0a88e4160d7fa8d83fd10338d", + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + expected: "9f864ca486a74eb5f546364d76d24aa93716dc78f84847aa6c1c09fca2707d77", + }, + } + for _, test := range tests { + s := new(ModNScalar).SetHex(test.in) + expected := new(ModNScalar).SetHex(test.expected) + // Ensure calculating the multiplicative inverse of another value + // produces the expected result. + result := new(ModNScalar).InverseValNonConst(s) + if !result.Equals(expected) { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result, expected, + ) + continue + } + // Ensure calculating the multiplicative inverse in place also produces + // the expected result. + result2 := new(ModNScalar).Set(s).InverseNonConst() + if !result2.Equals(expected) { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result2, expected, + ) + continue + } + } +} + +// TestModNScalarInverseNonConstRandom ensures that calculating the +// multiplicative inverse of scalars in *non-constant* time for random values +// works as expected by also performing the same operation with big ints and +// comparing the results. +func TestModNScalarInverseNonConstRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + for i := 0; i < 100; i++ { + // Generate big integer and mod n scalar with the same random value. + bigIntVal, modNVal := randIntAndModNScalar(t, rng) + // Calculate the inverse of the value using big ints. + bigIntResult := new(big.Int).ModInverse(bigIntVal, curveParams.N) + // Calculate the inverse of the value using a mod n scalar. + modNValResult := new(ModNScalar).InverseValNonConst(modNVal) + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult) + modNResultHex := fmt.Sprintf("%v", modNValResult) + if bigIntResultHex != modNResultHex { + t.Fatalf( + "mismatched inverse\nbig int in: %x\nscalar in: %v\n"+ + "big int result: %x\nscalar result %v", bigIntVal, modNVal, + bigIntResult, modNValResult, + ) + } + } +} + +// TestModNScalarIsOverHalfOrder ensures that scalars report whether or not they +// exceeed the half order works as expected for edge cases. +func TestModNScalarIsOverHalfOrder(t *testing.T) { + tests := []struct { + name string // test description + in string // hex encoded test value + expected bool // expected result + }{ + { + name: "zero", + in: "0", + expected: false, + }, { + name: "one", + in: "1", + expected: false, + }, { + name: "group half order - 1", + in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b209f", + expected: false, + }, { + name: "group half order", + in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", + expected: false, + }, { + name: "group half order + 1", + in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", + expected: true, + }, { + name: "over half order word one", + in: "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f47681b20a0", + expected: true, + }, { + name: "over half order word two", + in: "7fffffffffffffffffffffffffffffff5d576e7357a4501edfe92f46681b20a0", + expected: true, + }, { + name: "over half order word three", + in: "7fffffffffffffffffffffffffffffff5d576e7457a4501ddfe92f46681b20a0", + expected: true, + }, { + name: "over half order word seven", + in: "8fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", + expected: true, + }, + } + for _, test := range tests { + result := new(ModNScalar).SetHex(test.in).IsOverHalfOrder() + if result != test.expected { + t.Errorf( + "%s: unexpected result -- got: %v, want: %v", test.name, + result, test.expected, + ) + continue + } + } +} + +// TestModNScalarIsOverHalfOrderRandom ensures that scalars report whether or +// not they exceeed the half order for random values works as expected by also +// performing the same operation with big ints and comparing the results. +func TestModNScalarIsOverHalfOrderRandom(t *testing.T) { + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + bigHalfOrder := new(big.Int).Rsh(curveParams.N, 1) + for i := 0; i < 100; i++ { + // Generate big integer and mod n scalar with the same random value. + bigIntVal, modNVal := randIntAndModNScalar(t, rng) + // Determine the value exceeds the half order using big ints. + bigIntResult := bigIntVal.Cmp(bigHalfOrder) > 0 + // Determine the value exceeds the half order using a mod n scalar. + modNValResult := modNVal.IsOverHalfOrder() + // Ensure they match. + if bigIntResult != modNValResult { + t.Fatalf( + "mismatched is over half order\nbig int in: %x\nscalar "+ + "in: %v\nbig int result: %v\nscalar result %v", bigIntVal, + modNVal, bigIntResult, modNValResult, + ) + } + } +} diff --git a/pkg/crypto/ec/secp256k1/nonce.go b/pkg/crypto/ec/secp256k1/nonce.go new file mode 100644 index 0000000..2a1fe9c --- /dev/null +++ b/pkg/crypto/ec/secp256k1/nonce.go @@ -0,0 +1,251 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2015-2020 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "bytes" + "hash" + + "next.orly.dev/pkg/crypto/sha256" +) + +// References: +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) +// +// [ISO/IEC 8825-1]: Information technology — ASN.1 encoding rules: +// Specification of Basic Encoding Rules (BER), Canonical Encoding Rules +// (CER) and Distinguished Encoding Rules (DER) +// +// [SEC1]: Elliptic Curve Cryptography (May 31, 2009, Version 2.0) +// https://www.secg.org/sec1-v2.pdf + +var ( + // singleZero is used during RFC6979 nonce generation. It is provided + // here to avoid the need to create it multiple times. + singleZero = []byte{0x00} + // zeroInitializer is used during RFC6979 nonce generation. It is provided + // here to avoid the need to create it multiple times. + zeroInitializer = bytes.Repeat([]byte{0x00}, sha256.BlockSize) + // singleOne is used during RFC6979 nonce generation. It is provided + // here to avoid the need to create it multiple times. + singleOne = []byte{0x01} + // oneInitializer is used during RFC6979 nonce generation. It is provided + // here to avoid the need to create it multiple times. + oneInitializer = bytes.Repeat([]byte{0x01}, sha256.Size) +) + +// hmacsha256 implements a resettable version of HMAC-SHA256. +type hmacsha256 struct { + inner, outer hash.Hash + ipad, opad [sha256.BlockSize]byte +} + +// Write adds data to the running hash. +func (h *hmacsha256) Write(p []byte) { h.inner.Write(p) } + +// initKey initializes the HMAC-SHA256 instance to the provided key. +func (h *hmacsha256) initKey(key []byte) { + // Hash the key if it is too large. + if len(key) > sha256.BlockSize { + h.outer.Write(key) + key = h.outer.Sum(nil) + } + copy(h.ipad[:], key) + copy(h.opad[:], key) + for i := range h.ipad { + h.ipad[i] ^= 0x36 + } + for i := range h.opad { + h.opad[i] ^= 0x5c + } + h.inner.Write(h.ipad[:]) +} + +// ResetKey resets the HMAC-SHA256 to its initial state and then initializes it +// with the provided key. It is equivalent to creating a new instance with the +// provided key without allocating more memory. +func (h *hmacsha256) ResetKey(key []byte) { + h.inner.Reset() + h.outer.Reset() + copy(h.ipad[:], zeroInitializer) + copy(h.opad[:], zeroInitializer) + h.initKey(key) +} + +// Resets the HMAC-SHA256 to its initial state using the current key. +func (h *hmacsha256) Reset() { + h.inner.Reset() + h.inner.Write(h.ipad[:]) +} + +// Sum returns the hash of the written data. +func (h *hmacsha256) Sum() []byte { + h.outer.Reset() + h.outer.Write(h.opad[:]) + h.outer.Write(h.inner.Sum(nil)) + return h.outer.Sum(nil) +} + +// newHMACSHA256 returns a new HMAC-SHA256 hasher using the provided key. +func newHMACSHA256(key []byte) *hmacsha256 { + h := new(hmacsha256) + h.inner = sha256.New() + h.outer = sha256.New() + h.initKey(key) + return h +} + +// NonceRFC6979 generates a nonce deterministically according to RFC 6979 using +// HMAC-SHA256 for the hashing function. It takes a 32-byte hash as an input +// and returns a 32-byte nonce to be used for deterministic signing. The extra +// and version arguments are optional, but allow additional data to be added to +// the input of the HMAC. When provided, the extra data must be 32-bytes and +// version must be 16 bytes or they will be ignored. +// +// Finally, the extraIterations parameter provides a method to produce a stream +// of deterministic nonces to ensure the signing code is able to produce a nonce +// that results in a valid signature in the extremely unlikely event the +// original nonce produced results in an invalid signature (e.g. R == 0). +// Signing code should start with 0 and increment it if necessary. +func NonceRFC6979( + secKey []byte, hash []byte, extra []byte, version []byte, + extraIterations uint32, +) *ModNScalar { + // Input to HMAC is the 32-byte secret key and the 32-byte hash. In + // addition, it may include the optional 32-byte extra data and 16-byte + // version. Create a fixed-size array to avoid extra allocs and slice it + // properly. + const ( + secKeyLen = 32 + hashLen = 32 + extraLen = 32 + versionLen = 16 + ) + var keyBuf [secKeyLen + hashLen + extraLen + versionLen]byte + // Truncate rightmost bytes of secret key and hash if they are too long and + // leave left padding of zeros when they're too short. + if len(secKey) > secKeyLen { + secKey = secKey[:secKeyLen] + } + if len(hash) > hashLen { + hash = hash[:hashLen] + } + offset := secKeyLen - len(secKey) // Zero left padding if needed. + offset += copy(keyBuf[offset:], secKey) + offset += hashLen - len(hash) // Zero left padding if needed. + offset += copy(keyBuf[offset:], hash) + if len(extra) == extraLen { + offset += copy(keyBuf[offset:], extra) + if len(version) == versionLen { + offset += copy(keyBuf[offset:], version) + } + } else if len(version) == versionLen { + // When the version was specified, but not the extra data, leave the + // extra data portion all zero. + offset += secKeyLen + offset += copy(keyBuf[offset:], version) + } + key := keyBuf[:offset] + // Step B. + // + // V = 0x01 0x01 0x01 ... 0x01 such that the length of V, in bits, is + // equal to 8*ceil(hashLen/8). + // + // Note that since the hash length is a multiple of 8 for the chosen hash + // function in this optimized implementation, the result is just the hash + // length, so avoid the extra calculations. Also, since it isn't modified, + // start with a global value. + v := oneInitializer + // Step C (Go zeroes all allocated memory). + // + // K = 0x00 0x00 0x00 ... 0x00 such that the length of K, in bits, is + // equal to 8*ceil(hashLen/8). + // + // As above, since the hash length is a multiple of 8 for the chosen hash + // function in this optimized implementation, the result is just the hash + // length, so avoid the extra calculations. + k := zeroInitializer[:hashLen] + // Step D. + // + // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1)) + // + // Note that key is the "int2octets(x) || bits2octets(h1)" portion along + // with potential additional data as described by section 3.6 of the RFC. + hasher := newHMACSHA256(k) + hasher.Write(oneInitializer) + hasher.Write(singleZero[:]) + hasher.Write(key) + k = hasher.Sum() + // Step E. + // + // V = HMAC_K(V) + hasher.ResetKey(k) + hasher.Write(v) + v = hasher.Sum() + // Step ToSliceOfBytes. + // + // K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1)) + // + // Note that key is the "int2octets(x) || bits2octets(h1)" portion along + // with potential additional data as described by section 3.6 of the RFC. + hasher.Reset() + hasher.Write(v) + hasher.Write(singleOne[:]) + hasher.Write(key[:]) + k = hasher.Sum() + // Step G. + // + // V = HMAC_K(V) + hasher.ResetKey(k) + hasher.Write(v) + v = hasher.Sum() + // Step H. + // + // Repeat until the value is nonzero and less than the curve order. + var generated uint32 + for { + // Step H1 and H2. + // + // Set T to the empty sequence. The length of T (in bits) is denoted + // tlen; thus, at that point, tlen = 0. + // + // While tlen < qlen, do the following: + // V = HMAC_K(V) + // T = T || V + // + // Note that because the hash function output is the same length as the + // secret key in this optimized implementation, there is no need to + // loop or create an intermediate T. + hasher.Reset() + hasher.Write(v) + v = hasher.Sum() + // Step H3. + // + // k = bits2int(T) + // If k is within the range [1,q-1], return it. + // + // Otherwise, compute: + // K = HMAC_K(V || 0x00) + // V = HMAC_K(V) + var secret ModNScalar + overflow := secret.SetByteSlice(v) + if !overflow && !secret.IsZero() { + generated++ + if generated > extraIterations { + return &secret + } + } + // K = HMAC_K(V || 0x00) + hasher.Reset() + hasher.Write(v) + hasher.Write(singleZero[:]) + k = hasher.Sum() + // V = HMAC_K(V) + hasher.ResetKey(k) + hasher.Write(v) + v = hasher.Sum() + } +} diff --git a/pkg/crypto/ec/secp256k1/nonce_test.go b/pkg/crypto/ec/secp256k1/nonce_test.go new file mode 100644 index 0000000..e3a1a91 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/nonce_test.go @@ -0,0 +1,225 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2015-2020 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "testing" + + "next.orly.dev/pkg/crypto/sha256" + "next.orly.dev/pkg/encoders/hex" + "next.orly.dev/pkg/utils" +) + +// hexToBytes converts the passed hex string into bytes 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 hexToBytes(s string) []byte { + b, err := hex.Dec(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} + +// TestNonceRFC6979 ensures that the deterministic nonces generated by +// NonceRFC6979 produces the expected nonces, including things such as when +// providing extra data and version information, short hashes, and multiple +// iterations. +func TestNonceRFC6979(t *testing.T) { + tests := []struct { + name string + key string + hash string + extraData string + version string + iterations uint32 + expected string + }{ + { + name: "key 32 bytes, hash 32 bytes, no extra data, no version", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + // Should be same as key with 32 bytes due to zero padding. + name: "key <32 bytes, hash 32 bytes, no extra data, no version", + key: "11111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + // Should be same as key with 32 bytes due to truncation. + name: "key >32 bytes, hash 32 bytes, no extra data, no version", + key: "001111111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + name: "hash <32 bytes (padded), no extra data, no version", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "00000000000000000000000000000000000000000000000000000000000001", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + name: "hash >32 bytes (truncated), no extra data, no version", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "000000000000000000000000000000000000000000000000000000000000000100", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + name: "hash 32 bytes, extra data <32 bytes (ignored), no version", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + extraData: "00000000000000000000000000000000000000000000000000000000000002", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + name: "hash 32 bytes, extra data >32 bytes (ignored), no version", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + extraData: "000000000000000000000000000000000000000000000000000000000000000002", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + name: "hash 32 bytes, no extra data, version <16 bytes (ignored)", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + version: "000000000000000000000000000003", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + name: "hash 32 bytes, no extra data, version >16 bytes (ignored)", + key: "001111111111111111111111111111111111111111111111111111111111111122", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + version: "0000000000000000000000000000000003", + iterations: 0, + expected: "154e92760f77ad9af6b547edd6f14ad0fae023eb2221bc8be2911675d8a686a3", + }, { + name: "hash 32 bytes, extra data 32 bytes, no version", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + extraData: "0000000000000000000000000000000000000000000000000000000000000002", + iterations: 0, + expected: "67893461ade51cde61824b20bc293b585d058e6b9f40fb68453d5143f15116ae", + }, { + name: "hash 32 bytes, no extra data, version 16 bytes", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + version: "00000000000000000000000000000003", + iterations: 0, + expected: "7b27d6ceff87e1ded1860ca4e271a530e48514b9d3996db0af2bb8bda189007d", + }, { + // Should be same as no extra data + version specified due to padding. + name: "hash 32 bytes, extra data 32 bytes all zero, version 16 bytes", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + extraData: "0000000000000000000000000000000000000000000000000000000000000000", + version: "00000000000000000000000000000003", + iterations: 0, + expected: "7b27d6ceff87e1ded1860ca4e271a530e48514b9d3996db0af2bb8bda189007d", + }, { + name: "hash 32 bytes, extra data 32 bytes, version 16 bytes", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + extraData: "0000000000000000000000000000000000000000000000000000000000000002", + version: "00000000000000000000000000000003", + iterations: 0, + expected: "9b5657643dfd4b77d99dfa505ed8a17e1b9616354fc890669b4aabece2170686", + }, { + name: "hash 32 bytes, no extra data, no version, extra iteration", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + iterations: 1, + expected: "66fca3fe494a6216e4a3f15cfbc1d969c60d9cdefda1a1c193edabd34aa8cd5e", + }, { + name: "hash 32 bytes, no extra data, no version, 2 extra iterations", + key: "0011111111111111111111111111111111111111111111111111111111111111", + hash: "0000000000000000000000000000000000000000000000000000000000000001", + iterations: 2, + expected: "70da248c92b5d28a52eafca1848b1a37d4cb36526c02553c9c48bb0b895fc77d", + }, + } + for _, test := range tests { + secKey := hexToBytes(test.key) + hash := hexToBytes(test.hash) + extraData := hexToBytes(test.extraData) + version := hexToBytes(test.version) + wantNonce := hexToBytes(test.expected) + // Ensure deterministically generated nonce is the expected value. + gotNonce := NonceRFC6979( + secKey, hash[:], extraData, version, + test.iterations, + ) + gotNonceBytes := gotNonce.Bytes() + if !utils.FastEqual(gotNonceBytes[:], wantNonce) { + t.Errorf( + "%s: unexpected nonce -- got %x, want %x", test.name, + gotNonceBytes, wantNonce, + ) + continue + } + } +} + +// TestRFC6979Compat ensures that the deterministic nonces generated by +// NonceRFC6979 produces the expected nonces for known values from other +// implementations. +func TestRFC6979Compat(t *testing.T) { + // Test vectors matching Trezor and CoreBitcoin implementations. + // - https://github.com/trezor/trezor-crypto/blob/9fea8f8ab377dc514e40c6fd1f7c89a74c1d8dc6/tests.c#L432-L453 + // - https://github.com/oleganza/CoreBitcoin/blob/e93dd71207861b5bf044415db5fa72405e7d8fbc/CoreBitcoin/BTCKey%2BTests.m#L23-L49 + tests := []struct { + key string + msg string + nonce string + }{ + { + "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", + "sample", + "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", + }, { + // This signature hits the case when S is higher than halforder. + // If S is not canonicalized (lowered by halforder), this test will fail. + "0000000000000000000000000000000000000000000000000000000000000001", + "Satoshi Nakamoto", + "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15", + }, { + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "Satoshi Nakamoto", + "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90", + }, { + "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181", + "Alan Turing", + "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1", + }, { + "0000000000000000000000000000000000000000000000000000000000000001", + "All those moments will be lost in time, like tears in rain. Time to die...", + "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3", + }, { + "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2", + "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", + "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d", + }, + } + for i, test := range tests { + secKey := hexToBytes(test.key) + hash := sha256.Sum256([]byte(test.msg)) + // Ensure deterministically generated nonce is the expected value. + gotNonce := NonceRFC6979(secKey, hash[:], nil, nil, 0) + wantNonce := hexToBytes(test.nonce) + gotNonceBytes := gotNonce.Bytes() + if !utils.FastEqual(gotNonceBytes[:], wantNonce) { + t.Errorf( + "NonceRFC6979 #%d (%s): Nonce is incorrect: "+ + "%x (expected %x)", i, test.msg, gotNonce, + wantNonce, + ) + continue + } + } +} diff --git a/pkg/crypto/ec/secp256k1/precomps.go b/pkg/crypto/ec/secp256k1/precomps.go new file mode 100644 index 0000000..4ac19e0 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/precomps.go @@ -0,0 +1,22 @@ +package secp256k1 + +import ( + _ "embed" +) + +//go:embed rawbytepoints.bin +var bytepoints []byte +var BytePointTable [32][256]JacobianPoint + +func init() { + var cursor int + for i := range BytePointTable { + for j := range BytePointTable[i] { + BytePointTable[i][j].X.SetByteSlice(bytepoints[cursor:]) + cursor += 32 + BytePointTable[i][j].Y.SetByteSlice(bytepoints[cursor:]) + cursor += 32 + BytePointTable[i][j].Z.SetInt(1) + } + } +} diff --git a/pkg/crypto/ec/secp256k1/precomps/doc.go b/pkg/crypto/ec/secp256k1/precomps/doc.go new file mode 100644 index 0000000..ce32035 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/precomps/doc.go @@ -0,0 +1,3 @@ +// Package main provides a generator for precomputed constants for secp256k1 +// signatures. This has been updated to use go 1.16 embed library. +package main diff --git a/pkg/crypto/ec/secp256k1/precomps/genprecomps.go b/pkg/crypto/ec/secp256k1/precomps/genprecomps.go new file mode 100644 index 0000000..464bbda --- /dev/null +++ b/pkg/crypto/ec/secp256k1/precomps/genprecomps.go @@ -0,0 +1,340 @@ +// Copyright 2015 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +// References: +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) + +import ( + "fmt" + "math/big" + "os" + + "lol.mleku.dev/chk" + "lol.mleku.dev/log" + "next.orly.dev/pkg/crypto/ec/secp256k1" +) + +// curveParams houses the secp256k1 curve parameters for convenient access. +var curveParams = secp256k1.Params() + +// bigAffineToJacobian takes an affine point (x, y) as big integers and converts +// it to Jacobian point with Z=1. +func bigAffineToJacobian(x, y *big.Int, result *secp256k1.JacobianPoint) { + result.X.SetByteSlice(x.Bytes()) + result.Y.SetByteSlice(y.Bytes()) + result.Z.SetInt(1) +} + +// serializedBytePoints returns a serialized byte slice which contains all possible points per +// 8-bit window. This is used to when generating compressedbytepoints.go. +func serializedBytePoints() []byte { + // Calculate G^(2^i) for i in 0..255. These are used to avoid recomputing + // them for each digit of the 8-bit windows. + doublingPoints := make([]secp256k1.JacobianPoint, curveParams.BitSize) + var q secp256k1.JacobianPoint + bigAffineToJacobian(curveParams.Gx, curveParams.Gy, &q) + for i := 0; i < curveParams.BitSize; i++ { + // Q = 2*Q. + doublingPoints[i] = q + secp256k1.DoubleNonConst(&q, &q) + } + + // Separate the bits into byte-sized windows. + curveByteSize := curveParams.BitSize / 8 + serialized := make([]byte, curveByteSize*256*2*32) + offset := 0 + for byteNum := 0; byteNum < curveByteSize; byteNum++ { + // Grab the 8 bits that make up this byte from doubling points. + startingBit := 8 * (curveByteSize - byteNum - 1) + windowPoints := doublingPoints[startingBit : startingBit+8] + + // Compute all points in this window, convert them to affine, and + // serialize them. + for i := 0; i < 256; i++ { + var point secp256k1.JacobianPoint + for bit := 0; bit < 8; bit++ { + if i>>uint(bit)&1 == 1 { + secp256k1.AddNonConst(&point, &windowPoints[bit], &point) + } + } + point.ToAffine() + point.X.PutBytesUnchecked(serialized[offset:]) + offset += 32 + point.Y.PutBytesUnchecked(serialized[offset:]) + offset += 32 + } + } + return serialized +} + +// sqrt returns the square root of the provided big integer using Newton's +// method. It's only compiled and used during generation of pre-computed +// values, so speed is not a huge concern. +func sqrt(n *big.Int) *big.Int { + // Initial guess = 2^(log_2(n)/2) + guess := big.NewInt(2) + guess.Exp(guess, big.NewInt(int64(n.BitLen()/2)), nil) + // Now refine using Newton's method. + big2 := big.NewInt(2) + prevGuess := big.NewInt(0) + for { + prevGuess.Set(guess) + guess.Add(guess, new(big.Int).Div(n, guess)) + guess.Div(guess, big2) + if guess.Cmp(prevGuess) == 0 { + break + } + } + return guess +} + +// endomorphismParams houses the parameters needed to make use of the secp256k1 +// endomorphism. +type endomorphismParams struct { + lambda *big.Int + beta *big.Int + a1, b1 *big.Int + a2, b2 *big.Int + z1, z2 *big.Int +} + +// endomorphismVectors runs the first 3 steps of algorithm 3.74 from [GECC] to +// generate the linearly independent vectors needed to generate a balanced +// length-two representation of a multiplier such that k = k1 + k2λ (mod N) and +// returns them. Since the values will always be the same given the fact that N +// and λ are fixed, the final results can be accelerated by storing the +// precomputed values. +func endomorphismVectors(lambda *big.Int) (a1, b1, a2, b2 *big.Int) { + // This section uses an extended Euclidean algorithm to generate a + // sequence of equations: + // s[i] * N + t[i] * λ = r[i] + nSqrt := sqrt(curveParams.N) + u, v := new(big.Int).Set(curveParams.N), new(big.Int).Set(lambda) + x1, y1 := big.NewInt(1), big.NewInt(0) + x2, y2 := big.NewInt(0), big.NewInt(1) + q, r := new(big.Int), new(big.Int) + qu, qx1, qy1 := new(big.Int), new(big.Int), new(big.Int) + s, t := new(big.Int), new(big.Int) + ri, ti := new(big.Int), new(big.Int) + a1, b1, a2, b2 = new(big.Int), new(big.Int), new(big.Int), new(big.Int) + found, oneMore := false, false + for u.Sign() != 0 { + // q = v/u + q.Div(v, u) + // r = v - q*u + qu.Mul(q, u) + r.Sub(v, qu) + // s = x2 - q*x1 + qx1.Mul(q, x1) + s.Sub(x2, qx1) + // t = y2 - q*y1 + qy1.Mul(q, y1) + t.Sub(y2, qy1) + // v = u, u = r, x2 = x1, x1 = s, y2 = y1, y1 = t + v.Set(u) + u.Set(r) + x2.Set(x1) + x1.Set(s) + y2.Set(y1) + y1.Set(t) + // As soon as the remainder is less than the sqrt of n, the + // values of a1 and b1 are known. + if !found && r.Cmp(nSqrt) < 0 { + // When this condition executes ri and ti represent the + // r[i] and t[i] values such that i is the greatest + // index for which r >= sqrt(n). Meanwhile, the current + // r and t values are r[i+1] and t[i+1], respectively. + // + // a1 = r[i+1], b1 = -t[i+1] + a1.Set(r) + b1.Neg(t) + found = true + oneMore = true + // Skip to the next iteration so ri and ti are not + // modified. + continue + + } else if oneMore { + // When this condition executes ri and ti still + // represent the r[i] and t[i] values while the current + // r and t are r[i+2] and t[i+2], respectively. + // + // sum1 = r[i]^2 + t[i]^2 + rSquared := new(big.Int).Mul(ri, ri) + tSquared := new(big.Int).Mul(ti, ti) + sum1 := new(big.Int).Add(rSquared, tSquared) + // sum2 = r[i+2]^2 + t[i+2]^2 + r2Squared := new(big.Int).Mul(r, r) + t2Squared := new(big.Int).Mul(t, t) + sum2 := new(big.Int).Add(r2Squared, t2Squared) + // if (r[i]^2 + t[i]^2) <= (r[i+2]^2 + t[i+2]^2) + if sum1.Cmp(sum2) <= 0 { + // a2 = r[i], b2 = -t[i] + a2.Set(ri) + b2.Neg(ti) + } else { + // a2 = r[i+2], b2 = -t[i+2] + a2.Set(r) + b2.Neg(t) + } + // All done. + break + } + ri.Set(r) + ti.Set(t) + } + + return a1, b1, a2, b2 +} + +// deriveEndomorphismParams calculates and returns parameters needed to make use +// of the secp256k1 endomorphism. +func deriveEndomorphismParams() [2]endomorphismParams { + // roots returns the solutions of the characteristic polynomial of the + // secp256k1 endomorphism. + // + // The characteristic polynomial for the endomorphism is: + // + // X^2 + X + 1 ≡ 0 (mod n). + // + // Solving for X: + // + // 4X^2 + 4X + 4 ≡ 0 (mod n) | (*4, possible because gcd(4, n) = 1) + // (2X + 1)^2 + 3 ≡ 0 (mod n) | (factor by completing the square) + // (2X + 1)^2 ≡ -3 (mod n) | (-3) + // (2X + 1) ≡ ±sqrt(-3) (mod n) | (sqrt) + // 2X ≡ ±sqrt(-3) - 1 (mod n) | (-1) + // X ≡ (±sqrt(-3)-1)*2^-1 (mod n) | (*2^-1) + // + // So, the roots are: + // X1 ≡ (-(sqrt(-3)+1)*2^-1 (mod n) + // X2 ≡ (sqrt(-3)-1)*2^-1 (mod n) + // + // It is also worth noting that X is a cube root of unity, meaning + // X^3 - 1 ≡ 0 (mod n), hence it can be factored as (X - 1)(X^2 + X + 1) ≡ 0 + // and (X1)^2 ≡ X2 (mod n) and (X2)^2 ≡ X1 (mod n). + roots := func(prime *big.Int) [2]big.Int { + var result [2]big.Int + one := big.NewInt(1) + twoInverse := new(big.Int).ModInverse(big.NewInt(2), prime) + negThree := new(big.Int).Neg(big.NewInt(3)) + sqrtNegThree := new(big.Int).ModSqrt(negThree, prime) + sqrtNegThreePlusOne := new(big.Int).Add(sqrtNegThree, one) + negSqrtNegThreePlusOne := new(big.Int).Neg(sqrtNegThreePlusOne) + result[0].Mul(negSqrtNegThreePlusOne, twoInverse) + result[0].Mod(&result[0], prime) + sqrtNegThreeMinusOne := new(big.Int).Sub(sqrtNegThree, one) + result[1].Mul(sqrtNegThreeMinusOne, twoInverse) + result[1].Mod(&result[1], prime) + return result + } + // Find the λ's and β's which are the solutions for the characteristic + // polynomial of the secp256k1 endomorphism modulo the curve order and + // modulo the field order, respectively. + lambdas := roots(curveParams.N) + betas := roots(curveParams.P) + // Ensure the calculated roots are actually the roots of the characteristic + // polynomial. + checkRoots := func(foundRoots [2]big.Int, prime *big.Int) { + // X^2 + X + 1 ≡ 0 (mod p) + one := big.NewInt(1) + for i := 0; i < len(foundRoots); i++ { + root := &foundRoots[i] + result := new(big.Int).Mul(root, root) + result.Add(result, root) + result.Add(result, one) + result.Mod(result, prime) + if result.Sign() != 0 { + panic( + fmt.Sprintf( + "%[1]x^2 + %[1]x + 1 != 0 (mod %x)", root, + prime, + ), + ) + } + } + } + checkRoots(lambdas, curveParams.N) + checkRoots(betas, curveParams.P) + // checkVectors ensures the passed vectors satisfy the equation: + // a + b*λ ≡ 0 (mod n) + checkVectors := func(a, b *big.Int, lambda *big.Int) { + result := new(big.Int).Mul(b, lambda) + result.Add(result, a) + result.Mod(result, curveParams.N) + if result.Sign() != 0 { + panic( + fmt.Sprintf( + "%x + %x*lambda != 0 (mod %x)", a, b, + curveParams.N, + ), + ) + } + } + var endoParams [2]endomorphismParams + for i := 0; i < 2; i++ { + // Calculate the linearly independent vectors needed to generate a + // balanced length-two representation of a scalar such that + // k = k1 + k2*λ (mod n) for each of the solutions. + lambda := &lambdas[i] + a1, b1, a2, b2 := endomorphismVectors(lambda) + // Ensure the derived vectors satisfy the required equation. + checkVectors(a1, b1, lambda) + checkVectors(a2, b2, lambda) + // Calculate the precomputed estimates also used when generating the + // aforementioned decomposition. + // + // z1 = floor(b2<<320 / n) + // z2 = floor(((-b1)%n)<<320) / n) + const shift = 320 + z1 := new(big.Int).Lsh(b2, shift) + z1.Div(z1, curveParams.N) + z2 := new(big.Int).Neg(b1) + z2.Lsh(z2, shift) + z2.Div(z2, curveParams.N) + params := &endoParams[i] + params.lambda = lambda + params.beta = &betas[i] + params.a1 = a1 + params.b1 = b1 + params.a2 = a2 + params.b2 = b2 + params.z1 = z1 + params.z2 = z2 + } + return endoParams +} + +func main() { + if _, err := os.Stat(".git"); chk.T(err) { + fmt.Printf("File exists\n") + _, _ = fmt.Fprintln( + os.Stderr, + "This generator must be run with working directory at the root of"+ + " the repository", + ) + os.Exit(1) + } + serialized := serializedBytePoints() + embedded, err := os.Create("secp256k1/rawbytepoints.bin") + if err != nil { + log.F.Ln(err) + os.Exit(1) + } + n, err := embedded.Write(serialized) + if err != nil { + panic(err) + } + if n != len(serialized) { + fmt.Printf( + "failed to write all of byte points, wrote %d expected %d", + n, len(serialized), + ) + panic("fail") + } + _ = embedded.Close() +} diff --git a/pkg/crypto/ec/secp256k1/pubkey.go b/pkg/crypto/ec/secp256k1/pubkey.go new file mode 100644 index 0000000..ac7ec96 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/pubkey.go @@ -0,0 +1,234 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2015-2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +// References: +// [SEC1] Elliptic Curve Cryptography +// https://www.secg.org/sec1-v2.pdf +// +// [SEC2] Recommended Elliptic Curve Domain Parameters +// https://www.secg.org/sec2-v2.pdf +// +// [ANSI X9.62-1998] Public Key Cryptography For The Financial Services +// Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA) + +import ( + "fmt" +) + +const ( + // PubKeyBytesLenCompressed is the number of bytes of a serialized + // compressed public key. + PubKeyBytesLenCompressed = 33 + // PubKeyBytesLenUncompressed is the number of bytes of a serialized + // uncompressed public key. + PubKeyBytesLenUncompressed = 65 + // PubKeyFormatCompressedEven is the identifier prefix byte for a public key + // whose Y coordinate is even when serialized in the compressed format per + // section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4). + PubKeyFormatCompressedEven byte = 0x02 + // PubKeyFormatCompressedOdd is the identifier prefix byte for a public key + // whose Y coordinate is odd when serialized in the compressed format per + // section 2.3.4 of [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4). + PubKeyFormatCompressedOdd byte = 0x03 + // PubKeyFormatUncompressed is the identifier prefix byte for a public key + // when serialized according in the uncompressed format per section 2.3.3 of + // [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3). + PubKeyFormatUncompressed byte = 0x04 + // PubKeyFormatHybridEven is the identifier prefix byte for a public key + // whose Y coordinate is even when serialized according to the hybrid format + // per section 4.3.6 of [ANSI X9.62-1998]. + // + // NOTE: This format makes little sense in practice an therefore this + // package will not produce public keys serialized in this format. However, + // it will parse them since they exist in the wild. + PubKeyFormatHybridEven byte = 0x06 + // PubKeyFormatHybridOdd is the identifier prefix byte for a public key + // whose Y coordingate is odd when serialized according to the hybrid format + // per section 4.3.6 of [ANSI X9.62-1998]. + // + // NOTE: This format makes little sense in practice an therefore this + // package will not produce public keys serialized in this format. However, + // it will parse them since they exist in the wild. + PubKeyFormatHybridOdd byte = 0x07 +) + +// PublicKey provides facilities for efficiently working with secp256k1 public +// keys within this package and includes functions to serialize in both +// uncompressed and compressed SEC (Standards for Efficient Cryptography) +// formats. +type PublicKey struct { + x FieldVal + y FieldVal +} + +// 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 +// 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. +func NewPublicKey(x, y *FieldVal) *PublicKey { + var pubKey PublicKey + pubKey.x.Set(x) + pubKey.y.Set(y) + return &pubKey +} + +// ParsePubKey parses a secp256k1 public key encoded according to the format +// specified by ANSI X9.62-1998, which means it is also compatible with the +// SEC (Standards for Efficient Cryptography) specification which is a subset of +// the former. In other words, it supports the uncompressed, compressed, and +// hybrid formats as follows: +// +// Compressed: +// +// <32-byte X coordinate> +// +// Uncompressed: +// +// <32-byte X coordinate><32-byte Y coordinate> +// +// Hybrid: +// +// <32-byte X coordinate><32-byte Y coordinate> +// +// NOTE: The hybrid format makes little sense in practice an therefore this +// package will not produce public keys serialized in this format. However, +// this function will properly parse them since they exist in the wild. +func ParsePubKey(serialized []byte) (key *PublicKey, err error) { + var x, y FieldVal + switch len(serialized) { + case PubKeyBytesLenUncompressed: + // Reject unsupported public key formats for the given length. + format := serialized[0] + switch format { + case PubKeyFormatUncompressed: + case PubKeyFormatHybridEven, PubKeyFormatHybridOdd: + default: + str := fmt.Sprintf( + "invalid public key: unsupported format: %x", + format, + ) + return nil, makeError(ErrPubKeyInvalidFormat, str) + } + // Parse the x and y coordinates while ensuring that they are in the + // allowed range. + if overflow := x.SetByteSlice(serialized[1:33]); overflow { + str := "invalid public key: x >= field prime" + return nil, makeError(ErrPubKeyXTooBig, str) + } + if overflow := y.SetByteSlice(serialized[33:]); overflow { + str := "invalid public key: y >= field prime" + return nil, makeError(ErrPubKeyYTooBig, str) + } + // Ensure the oddness of the y coordinate matches the specified format + // for hybrid public keys. + if format == PubKeyFormatHybridEven || format == PubKeyFormatHybridOdd { + wantOddY := format == PubKeyFormatHybridOdd + if y.IsOdd() != wantOddY { + str := fmt.Sprintf( + "invalid public key: y oddness does not "+ + "match specified value of %v", wantOddY, + ) + return nil, makeError(ErrPubKeyMismatchedOddness, str) + } + } + // Reject public keys that are not on the secp256k1 curve. + if !isOnCurve(&x, &y) { + str := fmt.Sprintf( + "invalid public key: [%v,%v] not on secp256k1 "+ + "curve", x, y, + ) + return nil, makeError(ErrPubKeyNotOnCurve, str) + } + case PubKeyBytesLenCompressed: + // Reject unsupported public key formats for the given length. + format := serialized[0] + switch format { + case PubKeyFormatCompressedEven, PubKeyFormatCompressedOdd: + default: + str := fmt.Sprintf( + "invalid public key: unsupported format: %x", + format, + ) + return nil, makeError(ErrPubKeyInvalidFormat, str) + } + // Parse the x coordinate while ensuring that it is in the allowed + // range. + if overflow := x.SetByteSlice(serialized[1:33]); overflow { + str := "invalid public key: x >= field prime" + return nil, makeError(ErrPubKeyXTooBig, str) + } + // Attempt to calculate the y coordinate for the given x coordinate such + // that the result pair is a point on the secp256k1 curve and the + // solution with desired oddness is chosen. + wantOddY := format == PubKeyFormatCompressedOdd + if !DecompressY(&x, wantOddY, &y) { + str := fmt.Sprintf( + "invalid public key: x coordinate %v is not on "+ + "the secp256k1 curve", x, + ) + return nil, makeError(ErrPubKeyNotOnCurve, str) + } + y.Normalize() + default: + str := fmt.Sprintf( + "malformed public key: invalid length: %d", + len(serialized), + ) + return nil, makeError(ErrPubKeyInvalidLen, str) + } + return NewPublicKey(&x, &y), nil +} + +// SerializeUncompressed serializes a public key in the 65-byte uncompressed +// format. +func (p PublicKey) SerializeUncompressed() []byte { + // 0x04 || 32-byte x coordinate || 32-byte y coordinate + var b [PubKeyBytesLenUncompressed]byte + b[0] = PubKeyFormatUncompressed + p.x.PutBytesUnchecked(b[1:33]) + p.y.PutBytesUnchecked(b[33:65]) + return b[:] +} + +// SerializeCompressed serializes a public key in the 33-byte compressed format. +func (p PublicKey) SerializeCompressed() []byte { + // Choose the format byte depending on the oddness of the Y coordinate. + format := PubKeyFormatCompressedEven + if p.y.IsOdd() { + format = PubKeyFormatCompressedOdd + } + // 0x02 or 0x03 || 32-byte x coordinate + var b [PubKeyBytesLenCompressed]byte + b[0] = format + p.x.PutBytesUnchecked(b[1:33]) + return b[:] +} + +// IsEqual compares this public key instance to the one passed, returning true +// if both public keys are equivalent. A public key is equivalent to another, +// if they both have the same X and Y coordinates. +func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool { + return p.x.Equals(&otherPubKey.x) && p.y.Equals(&otherPubKey.y) +} + +// AsJacobian converts the public key into a Jacobian point with Z=1 and stores +// the result in the provided result param. This allows the public key to be +// treated a Jacobian point in the secp256k1 group in calculations. +func (p *PublicKey) AsJacobian(result *JacobianPoint) { + result.X.Set(&p.x) + result.Y.Set(&p.y) + result.Z.SetInt(1) +} + +// IsOnCurve returns whether or not the public key represents a point on the +// secp256k1 curve. +func (p *PublicKey) IsOnCurve() bool { + return isOnCurve(&p.x, &p.y) +} diff --git a/pkg/crypto/ec/secp256k1/pubkey_test.go b/pkg/crypto/ec/secp256k1/pubkey_test.go new file mode 100644 index 0000000..186a3a4 --- /dev/null +++ b/pkg/crypto/ec/secp256k1/pubkey_test.go @@ -0,0 +1,493 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2015-2020 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "errors" + "testing" + + "next.orly.dev/pkg/utils" +) + +// TestParsePubKey ensures that public keys are properly parsed according +// to the spec including both the positive and negative cases. +func TestParsePubKey(t *testing.T) { + tests := []struct { + name string // test description + key string // hex encoded public key + err error // expected error + wantX string // expected x coordinate + wantY string // expected y coordinate + }{ + { + name: "uncompressed ok", + key: "04" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + err: nil, + wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + wantY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + }, { + name: "uncompressed x changed (not on curve)", + key: "04" + + "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + err: ErrPubKeyNotOnCurve, + }, { + name: "uncompressed y changed (not on curve)", + key: "04" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4", + err: ErrPubKeyNotOnCurve, + }, { + name: "uncompressed claims compressed", + key: "03" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + err: ErrPubKeyInvalidFormat, + }, { + name: "uncompressed as hybrid ok (ybit = 0)", + key: "06" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c", + err: nil, + wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + wantY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c", + }, { + name: "uncompressed as hybrid ok (ybit = 1)", + key: "07" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + err: nil, + wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + wantY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + }, { + name: "uncompressed as hybrid wrong oddness", + key: "06" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + err: ErrPubKeyMismatchedOddness, + }, { + name: "compressed ok (ybit = 0)", + key: "02" + + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", + err: nil, + wantX: "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", + wantY: "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032", + }, { + name: "compressed ok (ybit = 1)", + key: "03" + + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", + err: nil, + wantX: "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", + wantY: "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f", + }, { + name: "compressed claims uncompressed (ybit = 0)", + key: "04" + + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", + err: ErrPubKeyInvalidFormat, + }, { + name: "compressed claims uncompressed (ybit = 1)", + key: "04" + + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", + err: ErrPubKeyInvalidFormat, + }, { + name: "compressed claims hybrid (ybit = 0)", + key: "06" + + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", + err: ErrPubKeyInvalidFormat, + }, { + name: "compressed claims hybrid (ybit = 1)", + key: "07" + + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", + err: ErrPubKeyInvalidFormat, + }, { + name: "compressed with invalid x coord (ybit = 0)", + key: "03" + + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c", + err: ErrPubKeyNotOnCurve, + }, { + name: "compressed with invalid x coord (ybit = 1)", + key: "03" + + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d", + err: ErrPubKeyNotOnCurve, + }, { + name: "empty", + key: "", + err: ErrPubKeyInvalidLen, + }, { + name: "wrong length", + key: "05", + err: ErrPubKeyInvalidLen, + }, { + name: "uncompressed x == p", + key: "04" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + err: ErrPubKeyXTooBig, + }, { + // The y coordinate produces a valid point for x == 1 (mod p), but it + // should fail to parse instead of wrapping around. + name: "uncompressed x > p (p + 1 -- aka 1)", + key: "04" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" + + "bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441", + err: ErrPubKeyXTooBig, + }, { + name: "uncompressed y == p", + key: "04" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + err: ErrPubKeyYTooBig, + }, { + // The x coordinate produces a valid point for y == 1 (mod p), but it + // should fail to parse instead of wrapping around. + name: "uncompressed y > p (p + 1 -- aka 1)", + key: "04" + + "1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + err: ErrPubKeyYTooBig, + }, { + name: "compressed x == p (ybit = 0)", + key: "02" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + err: ErrPubKeyXTooBig, + }, { + name: "compressed x == p (ybit = 1)", + key: "03" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + err: ErrPubKeyXTooBig, + }, { + // This would be valid for x == 2 (mod p), but it should fail to parse + // instead of wrapping around. + name: "compressed x > p (p + 2 -- aka 2) (ybit = 0)", + key: "02" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc31", + err: ErrPubKeyXTooBig, + }, { + // This would be valid for x == 1 (mod p), but it should fail to parse + // instead of wrapping around. + name: "compressed x > p (p + 1 -- aka 1) (ybit = 1)", + key: "03" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + err: ErrPubKeyXTooBig, + }, { + name: "hybrid x == p (ybit = 1)", + key: "07" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + err: ErrPubKeyXTooBig, + }, { + // The y coordinate produces a valid point for x == 1 (mod p), but it + // should fail to parse instead of wrapping around. + name: "hybrid x > p (p + 1 -- aka 1) (ybit = 0)", + key: "06" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" + + "bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441", + err: ErrPubKeyXTooBig, + }, { + name: "hybrid y == p (ybit = 0 when mod p)", + key: "06" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + err: ErrPubKeyYTooBig, + }, { + // The x coordinate produces a valid point for y == 1 (mod p), but it + // should fail to parse instead of wrapping around. + name: "hybrid y > p (p + 1 -- aka 1) (ybit = 1 when mod p)", + key: "07" + + "1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + err: ErrPubKeyYTooBig, + }, + } + for _, test := range tests { + pubKeyBytes := hexToBytes(test.key) + pubKey, err := ParsePubKey(pubKeyBytes) + if !errors.Is(err, test.err) { + t.Errorf( + "%s mismatched e -- got %v, want %v", test.name, err, + test.err, + ) + continue + } + if err != nil { + continue + } + // Ensure the x and y coordinates match the expected values upon + // successful parse. + wantX, wantY := hexToFieldVal(test.wantX), hexToFieldVal(test.wantY) + if !pubKey.x.Equals(wantX) { + t.Errorf( + "%s: mismatched x coordinate -- got %v, want %v", + test.name, pubKey.x, wantX, + ) + continue + } + if !pubKey.y.Equals(wantY) { + t.Errorf( + "%s: mismatched y coordinate -- got %v, want %v", + test.name, pubKey.y, wantY, + ) + continue + } + } +} + +// TestPubKeySerialize ensures that serializing public keys works as expected +// for both the compressed and uncompressed cases. +func TestPubKeySerialize(t *testing.T) { + tests := []struct { + name string // test description + pubX string // hex encoded x coordinate for pubkey to serialize + pubY string // hex encoded y coordinate for pubkey to serialize + compress bool // whether to serialize compressed or uncompressed + expected string // hex encoded expected pubkey serialization + }{ + { + name: "uncompressed (ybit = 0)", + pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c", + compress: false, + expected: "04" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c", + }, { + name: "uncompressed (ybit = 1)", + pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + compress: false, + expected: "04" + + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + }, { + // It's invalid to parse pubkeys that are not on the curve, however it + // is possible to manually create them and they should serialize + // correctly. + name: "uncompressed not on the curve due to x coord", + pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + compress: false, + expected: "04" + + "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + }, { + // It's invalid to parse pubkeys that are not on the curve, however it + // is possible to manually create them and they should serialize + // correctly. + name: "uncompressed not on the curve due to y coord", + pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4", + compress: false, + expected: "04" + + "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4", + }, { + name: "compressed (ybit = 0)", + pubX: "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", + pubY: "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032", + compress: true, + expected: "02" + + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", + }, { + name: "compressed (ybit = 1)", + pubX: "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", + pubY: "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f", + compress: true, + expected: "03" + + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", + }, { + // It's invalid to parse pubkeys that are not on the curve, however it + // is possible to manually create them and they should serialize + // correctly. + name: "compressed not on curve (ybit = 0)", + pubX: "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c", + pubY: "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032", + compress: true, + expected: "02" + + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c", + }, { + // It's invalid to parse pubkeys that are not on the curve, however it + // is possible to manually create them and they should serialize + // correctly. + name: "compressed not on curve (ybit = 1)", + pubX: "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d", + pubY: "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f", + compress: true, + expected: "03" + + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d", + }, + } + for _, test := range tests { + // Parse the test data. + x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY) + pubKey := NewPublicKey(x, y) + // Serialize with the correct method and ensure the result matches the + // expected value. + var serialized []byte + if test.compress { + serialized = pubKey.SerializeCompressed() + } else { + serialized = pubKey.SerializeUncompressed() + } + expected := hexToBytes(test.expected) + if !utils.FastEqual(serialized, expected) { + t.Errorf( + "%s: mismatched serialized public key -- got %x, want %x", + test.name, serialized, expected, + ) + continue + } + } +} + +// TestPublicKeyIsEqual ensures that equality testing between two public keys +// works as expected. +func TestPublicKeyIsEqual(t *testing.T) { + pubKey1 := &PublicKey{ + x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"), + y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"), + } + pubKey1Copy := &PublicKey{ + x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"), + y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"), + } + pubKey2 := &PublicKey{ + x: *hexToFieldVal("ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"), + y: *hexToFieldVal("0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032"), + } + + if !pubKey1.IsEqual(pubKey1) { + t.Fatalf( + "bad self public key equality check: (%v, %v)", pubKey1.x, + pubKey1.y, + ) + } + if !pubKey1.IsEqual(pubKey1Copy) { + t.Fatalf( + "bad public key equality check: (%v, %v) == (%v, %v)", + pubKey1.x, pubKey1.y, pubKey1Copy.x, pubKey1Copy.y, + ) + } + + if pubKey1.IsEqual(pubKey2) { + t.Fatalf( + "bad public key equality check: (%v, %v) != (%v, %v)", + pubKey1.x, pubKey1.y, pubKey2.x, pubKey2.y, + ) + } +} + +// TestPublicKeyAsJacobian ensures converting a public key to a jacobian point +// with a Z coordinate of 1 works as expected. +func TestPublicKeyAsJacobian(t *testing.T) { + tests := []struct { + name string // test description + pubKey string // hex encoded serialized compressed pubkey + wantX string // hex encoded expected X coordinate + wantY string // hex encoded expected Y coordinate + }{ + { + name: "public key for secret key 0x01", + pubKey: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + wantX: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + wantY: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + }, { + name: "public for secret key 0x03", + pubKey: "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", + wantX: "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", + wantY: "388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672", + }, { + name: "public for secret key 0x06", + pubKey: "03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556", + wantX: "fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556", + wantY: "ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297", + }, + } + for _, test := range tests { + // Parse the test data. + pubKeyBytes := hexToBytes(test.pubKey) + wantX := hexToFieldVal(test.wantX) + wantY := hexToFieldVal(test.wantY) + pubKey, err := ParsePubKey(pubKeyBytes) + if err != nil { + t.Errorf("%s: failed to parse public key: %v", test.name, err) + continue + } + // Convert the public key to a jacobian point and ensure the coordinates + // match the expected values. + var point JacobianPoint + pubKey.AsJacobian(&point) + if !point.Z.IsOne() { + t.Errorf( + "%s: invalid Z coordinate -- got %v, want 1", test.name, + point.Z, + ) + continue + } + if !point.X.Equals(wantX) { + t.Errorf( + "%s: invalid X coordinate - got %v, want %v", test.name, + point.X, wantX, + ) + continue + } + if !point.Y.Equals(wantY) { + t.Errorf( + "%s: invalid Y coordinate - got %v, want %v", test.name, + point.Y, wantY, + ) + continue + } + } +} + +// TestPublicKeyIsOnCurve ensures testing if a public key is on the curve works +// as expected. +func TestPublicKeyIsOnCurve(t *testing.T) { + tests := []struct { + name string // test description + pubX string // hex encoded x coordinate for pubkey to serialize + pubY string // hex encoded y coordinate for pubkey to serialize + want bool // expected result + }{ + { + name: "valid with even y", + pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c", + want: true, + }, { + name: "valid with odd y", + pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + want: true, + }, { + name: "invalid due to x coord", + pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", + want: false, + }, { + name: "invalid due to y coord", + pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c", + pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4", + want: false, + }, + } + for _, test := range tests { + // Parse the test data. + x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY) + pubKey := NewPublicKey(x, y) + + result := pubKey.IsOnCurve() + if result != test.want { + t.Errorf( + "%s: mismatched is on curve result -- got %v, want %v", + test.name, result, test.want, + ) + continue + } + } +} diff --git a/pkg/crypto/ec/secp256k1/rawbytepoints.bin b/pkg/crypto/ec/secp256k1/rawbytepoints.bin new file mode 100644 index 0000000..f0520ed Binary files /dev/null and b/pkg/crypto/ec/secp256k1/rawbytepoints.bin differ diff --git a/pkg/crypto/ec/secp256k1/seckey.go b/pkg/crypto/ec/secp256k1/seckey.go new file mode 100644 index 0000000..80b994d --- /dev/null +++ b/pkg/crypto/ec/secp256k1/seckey.go @@ -0,0 +1,130 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2015-2023 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "crypto/rand" + "io" + + "lol.mleku.dev/chk" +) + +// SecretKey provides facilities for working with secp256k1 secret keys within +// this package and includes functionality such as serializing and parsing them +// as well as computing their associated public key. +type SecretKey struct { + Key ModNScalar +} + +// PrivateKey is a secret key. +// +// Deprecated: use SecretKey - secret = one person; private = two or more (you don't share secret keys!) +type PrivateKey = SecretKey + +// NewSecretKey instantiates a new secret key from a scalar encoded as a big integer. +func NewSecretKey(key *ModNScalar) *SecretKey { return &SecretKey{Key: *key} } + +// NewPrivateKey instantiates a new secret key from a scalar encoded as a big integer. +// +// Deprecated: use NewSecretKey - secret = one person; private = two or more (you don't share secret keys!) +var NewPrivateKey = NewSecretKey + +// SecKeyFromBytes returns a secret based on the provided byte slice which is +// interpreted as an unsigned 256-bit big-endian integer in the range [0, N-1], +// where N is the order of the curve. +// +// WARNING: This means passing a slice with more than 32 bytes is truncated and +// that truncated value is reduced modulo N. Further, 0 is not a valid secret +// key. It is up to the caller to provide a value in the appropriate range of +// [1, N-1]. Failure to do so will either result in an invalid secret key or +// potentially weak secret keys that have bias that could be exploited. +// +// This function primarily exists to provide a mechanism for converting +// serialized secret keys that are already known to be good. +// +// Typically, callers should make use of GenerateSecretKey or +// GenerateSecretKeyFromRand when creating secret keys since they properly +// handle generation of appropriate values. +func SecKeyFromBytes(secKeyBytes []byte) *SecretKey { + var secKey SecretKey + secKey.Key.SetByteSlice(secKeyBytes) + return &secKey +} + +var PrivKeyFromBytes = SecKeyFromBytes + +// generateSecretKey generates and returns a new secret key that is suitable +// for use with secp256k1 using the provided reader as a source of entropy. The +// provided reader must be a source of cryptographically secure randomness to +// avoid weak secret keys. +func generateSecretKey(rand io.Reader) (*SecretKey, error) { + // The group order is close enough to 2^256 that there is only roughly a 1 + // in 2^128 chance of generating an invalid secret key, so this loop will + // virtually never run more than a single iteration in practice. + var key SecretKey + var b32 [32]byte + for valid := false; !valid; { + if _, err := io.ReadFull(rand, b32[:]); chk.T(err) { + return nil, err + } + // The secret key is only valid when it is in the range [1, N-1], where + // N is the order of the curve. + overflow := key.Key.SetBytes(&b32) + valid = (key.Key.IsZeroBit() | overflow) == 0 + } + zeroArray32(&b32) + return &key, nil +} + +// GenerateSecretKey generates and returns a new cryptographically secure secret key that is suitable for use with +// secp256k1. +func GenerateSecretKey() (*SecretKey, error) { + return generateSecretKey(rand.Reader) +} + +// GeneratePrivateKey generates and returns a new cryptographically secure secret key that is suitable for use with +// secp256k1. +// +// Deprecated: use NewSecretKey - secret = one person; private = two or more (you don't share secret keys!) +var GeneratePrivateKey = GenerateSecretKey + +// GenerateSecretKeyFromRand generates a secret key that is suitable for use with secp256k1 using the provided reader as +// a source of entropy. The provided reader must be a source of cryptographically secure randomness, such as +// [crypto/rand.Reader], to avoid weak secret keys. +func GenerateSecretKeyFromRand(rand io.Reader) (*SecretKey, error) { + return generateSecretKey(rand) +} + +// GeneratePrivateKeyFromRand generates a secret key that is suitable for use with secp256k1 using the provided reader as +// a source of entropy. The provided reader must be a source of cryptographically secure randomness, such as +// [crypto/rand.Reader], to avoid weak secret keys. +// +// Deprecated: use GenerateSecretKeyFromRand - secret = one person; private = two or more (you don't share secret keys!) +var GeneratePrivateKeyFromRand = GenerateSecretKeyFromRand + +// PubKey computes and returns the public key corresponding to this secret key. +func (p *SecretKey) PubKey() *PublicKey { + var result JacobianPoint + ScalarBaseMultNonConst(&p.Key, &result) + result.ToAffine() + return NewPublicKey(&result.X, &result.Y) +} + +// Zero manually clears the memory associated with the secret key. This can be +// used to explicitly clear key material from memory for enhanced security +// against memory scraping. +func (p *SecretKey) Zero() { p.Key.Zero() } + +// SecKeyBytesLen defines the length in bytes of a serialized secret key. +const SecKeyBytesLen = 32 + +// Serialize returns the secret key as a 256-bit big-endian binary-encoded +// number, padded to a length of 32 bytes. +func (p *SecretKey) Serialize() []byte { + var secKeyBytes [SecKeyBytesLen]byte + p.Key.PutBytes(&secKeyBytes) + return secKeyBytes[:] +} diff --git a/pkg/crypto/ec/secp256k1/seckey_bench_test.go b/pkg/crypto/ec/secp256k1/seckey_bench_test.go new file mode 100644 index 0000000..bbede9a --- /dev/null +++ b/pkg/crypto/ec/secp256k1/seckey_bench_test.go @@ -0,0 +1,22 @@ +// Copyright (c) 2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "testing" +) + +// BenchmarkSecretKeyGenerate benchmarks generating new cryptographically +// secure secret keys. +func BenchmarkSecretKeyGenerate(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := GenerateSecretKey() + if err != nil { + b.Fatal(err) + } + } +} diff --git a/pkg/crypto/ec/secp256k1/seckey_test.go b/pkg/crypto/ec/secp256k1/seckey_test.go new file mode 100644 index 0000000..afaf9ca --- /dev/null +++ b/pkg/crypto/ec/secp256k1/seckey_test.go @@ -0,0 +1,179 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2015-2023 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package secp256k1 + +import ( + "bytes" + "crypto/rand" + "errors" + "math/big" + "testing" + + "next.orly.dev/pkg/utils" +) + +// TestGenerateSecretKey ensures the key generation works as expected. +func TestGenerateSecretKey(t *testing.T) { + sec, err := GenerateSecretKey() + if err != nil { + t.Errorf("failed to generate secret key: %s", err) + return + } + pub := sec.PubKey() + if !isOnCurve(&pub.x, &pub.y) { + t.Error("public key is not on the curve") + } +} + +// TestGenerateSecretKeyFromRand ensures generating a secret key from a random +// entropy source works as expected. +func TestGenerateSecretKeyFromRand(t *testing.T) { + sec, err := GenerateSecretKeyFromRand(rand.Reader) + if err != nil { + t.Errorf("failed to generate secret key: %s", err) + return + } + pub := sec.PubKey() + if !isOnCurve(&pub.x, &pub.y) { + t.Error("public key is not on the curve") + } +} + +// mockSecretKeyReaderFunc is an adapter to allow the use of an ordinary +// function as an io.Reader. +type mockSecretKeyReaderFunc func([]byte) (int, error) + +// Read calls the function with the provided parameter and returns the result. +func (f mockSecretKeyReaderFunc) Read(p []byte) (int, error) { + return f(p) +} + +// TestGenerateSecretKeyCorners ensures random values that secret key +// generation correctly handles entropy values that are invalid for use as +// secret keys by creating a fake source of randomness to inject known bad +// values. +func TestGenerateSecretKeyCorners(t *testing.T) { + // Create a mock reader that returns the following sequence of values: + // 1st invocation: 0 + // 2nd invocation: The curve order + // 3rd invocation: The curve order + 1 + // 4th invocation: 1 (32-byte big endian) + oneModN := hexToModNScalar("01") + var numReads int + mockReader := mockSecretKeyReaderFunc( + func(p []byte) (int, error) { + numReads++ + switch numReads { + case 1: + return copy(p, bytes.Repeat([]byte{0x00}, len(p))), nil + case 2: + return copy(p, curveParams.N.Bytes()), nil + case 3: + nPlusOne := new(big.Int).Add(curveParams.N, big.NewInt(1)) + return copy(p, nPlusOne.Bytes()), nil + } + oneModNBytes := oneModN.Bytes() + return copy(p, oneModNBytes[:]), nil + }, + ) + // Generate a secret key using the mock reader and ensure the resulting key + // is the expected one. It should be the value "1" since the other values + // the sequence produces are invalid and thus should be rejected. + sec, err := GenerateSecretKeyFromRand(mockReader) + if err != nil { + t.Errorf("failed to generate secret key: %s", err) + return + } + if !sec.Key.Equals(oneModN) { + t.Fatalf( + "unexpected secret key -- got: %x, want %x", sec.Serialize(), + oneModN.Bytes(), + ) + } +} + +// TestGenerateSecretKeyError ensures the secret key generation properly +// handles errors when attempting to read from the source of randomness. +func TestGenerateSecretKeyError(t *testing.T) { + // Create a mock reader that returns an error. + errDisabled := errors.New("disabled") + mockReader := mockSecretKeyReaderFunc( + func(p []byte) (int, error) { + return 0, errDisabled + }, + ) + // Generate a secret key using the mock reader and ensure the expected + // error is returned. + _, err := GenerateSecretKeyFromRand(mockReader) + if !errors.Is(err, errDisabled) { + t.Fatalf("mismatched err -- got %v, want %v", err, errDisabled) + return + } +} + +// TestSecKeys ensures a secret key created from bytes produces both the +// correct associated public key as well serializes back to the original bytes. +func TestSecKeys(t *testing.T) { + tests := []struct { + name string + sec string // hex encoded secret key to test + pub string // expected hex encoded serialized compressed public key + }{ + { + name: "random secret key 1", + sec: "eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694", + pub: "025ceeba2ab4a635df2c0301a3d773da06ac5a18a7c3e0d09a795d7e57d233edf1", + }, { + name: "random secret key 2", + sec: "24b860d0651db83feba821e7a94ba8b87162665509cefef0cbde6a8fbbedfe7c", + pub: "032a6e51bf218085647d330eac2fafaeee07617a777ad9e8e7141b4cdae92cb637", + }, + } + + for _, test := range tests { + // Parse test data. + secKeyBytes := hexToBytes(test.sec) + wantPubKeyBytes := hexToBytes(test.pub) + + sec := SecKeyFromBytes(secKeyBytes) + pub := sec.PubKey() + + serializedPubKey := pub.SerializeCompressed() + if !utils.FastEqual(serializedPubKey, wantPubKeyBytes) { + t.Errorf( + "%s unexpected serialized public key - got: %x, want: %x", + test.name, serializedPubKey, wantPubKeyBytes, + ) + } + + serializedSecKey := sec.Serialize() + if !utils.FastEqual(serializedSecKey, secKeyBytes) { + t.Errorf( + "%s unexpected serialized secret key - got: %x, want: %x", + test.name, serializedSecKey, secKeyBytes, + ) + } + } +} + +// TestSecretKeyZero ensures that zeroing a secret key clears the memory +// associated with it. +func TestSecretKeyZero(t *testing.T) { + // Create a new secret key and zero the initial key material that is now + // copied into the secret key. + key := new(ModNScalar).SetHex("eaf02ca348c524e6392655ba4d29603cd1a7347d9d65cfe93ce1ebffdca22694") + secKey := NewSecretKey(key) + key.Zero() + // Ensure the secret key is non zero. + if secKey.Key.IsZero() { + t.Fatal("secret key is zero when it should be non zero") + } + // Zero the secret key and ensure it was properly zeroed. + secKey.Zero() + if !secKey.Key.IsZero() { + t.Fatal("secret key is non zero when it should be zero") + } +} diff --git a/pkg/crypto/ec/taproot/taproot.go b/pkg/crypto/ec/taproot/taproot.go new file mode 100644 index 0000000..f3c247b --- /dev/null +++ b/pkg/crypto/ec/taproot/taproot.go @@ -0,0 +1,172 @@ +// Package taproot provides a collection of tools for encoding bitcoin taproot +// addresses. +package taproot + +import ( + "bytes" + "errors" + "fmt" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/crypto/ec/bech32" + "next.orly.dev/pkg/crypto/ec/chaincfg" + "next.orly.dev/pkg/utils" +) + +// AddressSegWit is the base address type for all SegWit addresses. +type AddressSegWit struct { + hrp []byte + witnessVersion byte + witnessProgram []byte +} + +// AddressTaproot is an Address for a pay-to-taproot (P2TR) output. See BIP 341 +// for further details. +type AddressTaproot struct { + AddressSegWit +} + +// NewAddressTaproot returns a new AddressTaproot. +func NewAddressTaproot( + witnessProg []byte, + net *chaincfg.Params, +) (*AddressTaproot, error) { + + return newAddressTaproot(net.Bech32HRPSegwit, witnessProg) +} + +// newAddressWitnessScriptHash is an internal helper function to create an +// AddressWitnessScriptHash with a known human-readable part, rather than +// looking it up through its parameters. +func newAddressTaproot(hrp []byte, witnessProg []byte) ( + *AddressTaproot, error, +) { + // Check for valid program length for witness version 1, which is 32 + // for P2TR. + if len(witnessProg) != 32 { + return nil, errors.New( + "witness program must be 32 bytes for " + + "p2tr", + ) + } + addr := &AddressTaproot{ + AddressSegWit{ + hrp: bytes.ToLower(hrp), + witnessVersion: 0x01, + witnessProgram: witnessProg, + }, + } + return addr, nil +} + +// decodeSegWitAddress parses a bech32 encoded segwit address string and +// returns the witness version and witness program byte representation. +func decodeSegWitAddress(address []byte) (byte, []byte, error) { + // Decode the bech32 encoded address. + _, data, bech32version, err := bech32.DecodeGeneric(address) + if chk.E(err) { + return 0, nil, err + } + // The first byte of the decoded address is the witness version, it must + // exist. + if len(data) < 1 { + return 0, nil, fmt.Errorf("no witness version") + } + // ...and be <= 16. + version := data[0] + if version > 16 { + return 0, nil, fmt.Errorf("invalid witness version: %v", version) + } + // The remaining characters of the address returned are grouped into + // words of 5 bits. In order to restore the original witness program + // bytes, we'll need to regroup into 8 bit words. + regrouped, err := bech32.ConvertBits(data[1:], 5, 8, false) + if chk.E(err) { + return 0, nil, err + } + // The regrouped data must be between 2 and 40 bytes. + if len(regrouped) < 2 || len(regrouped) > 40 { + return 0, nil, fmt.Errorf("invalid data length") + } + // For witness version 0, address MUST be exactly 20 or 32 bytes. + if version == 0 && len(regrouped) != 20 && len(regrouped) != 32 { + return 0, nil, fmt.Errorf( + "invalid data length for witness "+ + "version 0: %v", len(regrouped), + ) + } + // For witness version 0, the bech32 encoding must be used. + if version == 0 && bech32version != bech32.Version0 { + return 0, nil, fmt.Errorf( + "invalid checksum expected bech32 " + + "encoding for address with witness version 0", + ) + } + // For witness version 1, the bech32m encoding must be used. + if version == 1 && bech32version != bech32.VersionM { + return 0, nil, fmt.Errorf( + "invalid checksum expected bech32m " + + "encoding for address with witness version 1", + ) + } + return version, regrouped, nil +} + +// encodeSegWitAddress creates a bech32 (or bech32m for SegWit v1) encoded +// address string representation from witness version and witness program. +func encodeSegWitAddress( + hrp []byte, witnessVersion byte, + witnessProgram []byte, +) ([]byte, error) { + // Group the address bytes into 5 bit groups, as this is what is used to + // encode each character in the address string. + converted, err := bech32.ConvertBits(witnessProgram, 8, 5, true) + if chk.E(err) { + return nil, err + } + // Concatenate the witness version and program, and encode the resulting + // bytes using bech32 encoding. + combined := make([]byte, len(converted)+1) + combined[0] = witnessVersion + copy(combined[1:], converted) + var bech []byte + switch witnessVersion { + case 0: + bech, err = bech32.Encode(hrp, combined) + + case 1: + bech, err = bech32.EncodeM(hrp, combined) + + default: + return nil, fmt.Errorf( + "unsupported witness version %d", + witnessVersion, + ) + } + if chk.E(err) { + return nil, err + } + // Check validity by decoding the created address. + version, program, err := decodeSegWitAddress(bech) + if chk.E(err) { + return nil, fmt.Errorf("invalid segwit address: %v", err) + } + if version != witnessVersion || !utils.FastEqual(program, witnessProgram) { + return nil, fmt.Errorf("invalid segwit address") + } + return bech, nil +} + +// EncodeAddress returns the bech32 (or bech32m for SegWit v1) string encoding +// of an AddressSegWit. +// +// NOTE: This method is part of the Address interface. +func (a *AddressSegWit) EncodeAddress() []byte { + str, err := encodeSegWitAddress( + a.hrp, a.witnessVersion, a.witnessProgram[:], + ) + if chk.E(err) { + return nil + } + return str +} diff --git a/pkg/crypto/ec/wire/blockheader.go b/pkg/crypto/ec/wire/blockheader.go new file mode 100644 index 0000000..1490858 --- /dev/null +++ b/pkg/crypto/ec/wire/blockheader.go @@ -0,0 +1,25 @@ +package wire + +import ( + "time" + + "next.orly.dev/pkg/crypto/ec/chainhash" +) + +// BlockHeader defines information about a block and is used in the bitcoin +// block (MsgBlock) and headers (MsgHeaders) messages. +type BlockHeader struct { + // Version of the block. This is not the same as the protocol version. + Version int32 + // Hash of the previous block header in the block chain. + PrevBlock chainhash.Hash + // Merkle tree reference to hash of all transactions for the block. + MerkleRoot chainhash.Hash + // Time the block was created. This is, unfortunately, encoded as a + // uint32 on the wire and therefore is limited to 2106. + Timestamp time.Time + // Difficulty target for the block. + Bits uint32 + // Nonce used to generate the block. + Nonce uint32 +} diff --git a/pkg/crypto/ec/wire/msgblock.go b/pkg/crypto/ec/wire/msgblock.go new file mode 100644 index 0000000..a46ac97 --- /dev/null +++ b/pkg/crypto/ec/wire/msgblock.go @@ -0,0 +1,9 @@ +package wire + +// MsgBlock implements the Message interface and represents a bitcoin +// block message. It is used to deliver block and transaction information in +// response to a getdata message (MsgGetData) for a given block hash. +type MsgBlock struct { + Header BlockHeader + Transactions []*MsgTx +} diff --git a/pkg/crypto/ec/wire/msgtx.go b/pkg/crypto/ec/wire/msgtx.go new file mode 100644 index 0000000..aa53ed2 --- /dev/null +++ b/pkg/crypto/ec/wire/msgtx.go @@ -0,0 +1,43 @@ +package wire + +import ( + "next.orly.dev/pkg/crypto/ec/chainhash" +) + +// OutPoint defines a bitcoin data type that is used to track previous +// transaction outputs. +type OutPoint struct { + Hash chainhash.Hash + Index uint32 +} + +// TxWitness defines the witness for a TxIn. A witness is to be interpreted as +// a slice of byte slices, or a stack with one or many elements. +type TxWitness [][]byte + +// TxIn defines a bitcoin transaction input. +type TxIn struct { + PreviousOutPoint OutPoint + SignatureScript []byte + Witness TxWitness + Sequence uint32 +} + +// TxOut defines a bitcoin transaction output. +type TxOut struct { + Value int64 + PkScript []byte +} + +// MsgTx implements the Message interface and represents a bitcoin tx message. +// It is used to deliver transaction information in response to a getdata +// message (MsgGetData) for a given transaction. +// +// Use the AddTxIn and AddTxOut functions to build up the list of transaction +// inputs and outputs. +type MsgTx struct { + Version int32 + TxIn []*TxIn + TxOut []*TxOut + LockTime uint32 +} diff --git a/pkg/crypto/ec/wire/wire.go b/pkg/crypto/ec/wire/wire.go new file mode 100644 index 0000000..b9d0ddf --- /dev/null +++ b/pkg/crypto/ec/wire/wire.go @@ -0,0 +1,24 @@ +// Package wire contains a set of data structure definitions for the bitcoin +// blockchain. +package wire + +// BitcoinNet represents which bitcoin network a message belongs to. +type BitcoinNet uint32 + +// Constants used to indicate the message bitcoin network. They can also be +// used to seek to the next message when a stream's state is unknown, but +// this package does not provide that functionality since it's generally a +// better idea to simply disconnect clients that are misbehaving over TCP. +const ( + // MainNet represents the main bitcoin network. + MainNet BitcoinNet = 0xd9b4bef9 + + // TestNet represents the regression test network. + TestNet BitcoinNet = 0xdab5bffa + + // TestNet3 represents the test network (version 3). + TestNet3 BitcoinNet = 0x0709110b + + // SimNet represents the simulation test network. + SimNet BitcoinNet = 0x12141c16 +) diff --git a/pkg/crypto/p256k/README.md b/pkg/crypto/p256k/README.md new file mode 100644 index 0000000..ee0e7b6 --- /dev/null +++ b/pkg/crypto/p256k/README.md @@ -0,0 +1,68 @@ +# p256k1 + +This is a library that uses the `bitcoin-core` optimized secp256k1 elliptic +curve signatures library for `nostr` schnorr signatures. + +If you need to build it without `libsecp256k1` C library, you must disable cgo: + + export CGO_ENABLED='0' + +This enables the fallback `btcec` pure Go library to be used in its place. This +CGO setting is not default for Go, so it must be set in order to disable this. + +The standard `libsecp256k1-0` and `libsecp256k1-dev` available through the +ubuntu dpkg repositories do not include support for the BIP-340 schnorr +signatures or the ECDH X-only shared secret generation algorithm, so you must +follow the following instructions to get the benefits of using this library. It +is 4x faster at signing and generating shared secrets so it is a must if your +intention is to use it for high throughput systems like a network transport. + +The easy way to install it, if you have ubuntu/debian, is the script +[../ubuntu_install_libsecp256k1.sh](../../../scripts/ubuntu_install_libsecp256k1.sh), +it +handles the dependencies and runs the build all in one step for you. Note that +it + +For ubuntu, you need these: + + sudo apt -y install build-essential autoconf libtool + +For other linux distributions, the process is the same but the dependencies are +likely different. The main thing is it requires make, gcc/++, autoconf and +libtool to run. The most important thing to point out is that you must enable +the schnorr signatures feature, and ECDH. + +The directory `p256k/secp256k1` needs to be initialized, built and installed, +like so: + +```bash +cd secp256k1 +git submodule init +git submodule update +``` + +Then to build, you can refer to the [instructions](./secp256k1/README.md) or +just use the default autotools: + +```bash +./autogen.sh +./configure --enable-module-schnorrsig --enable-module-ecdh --prefix=/usr +make +sudo make install +``` + +On WSL2 you may have to attend to various things to make this work, setting up +your basic locale (uncomment one or more in `/etc/locale.gen`, and run +`locale-gen`), installing the basic build tools (build-essential or base-devel) +and of course git, curl, wget, libtool and +autoconf. + +## ECDH + +TODO: Currently the use of the libsecp256k1 library for ECDH, used in nip-04 and +nip-44 encryption is not enabled, because the default version uses the Y +coordinate and this is incorrect for nostr. It will be enabled soon... for now +it is done with the `btcec` fallback version. This is slower, however previous +tests have shown that this ECDH library is fast enough to enable 8mb/s +throughput per CPU thread when used to generate a distinct secret for TCP +packets. The C library will likely raise this to 20mb/s or more. \ No newline at end of file diff --git a/pkg/crypto/p256k/btcec.go b/pkg/crypto/p256k/btcec.go new file mode 100644 index 0000000..d6a6286 --- /dev/null +++ b/pkg/crypto/p256k/btcec.go @@ -0,0 +1,25 @@ +//go:build !cgo + +package p256k + +import ( + "lol.mleku.dev/log" + "next.orly.dev/pkg/crypto/p256k/btcec" +) + +func init() { + log.T.Ln("using btcec signature library") +} + +// BTCECSigner is always available but enabling it disables the use of +// github.com/bitcoin-core/secp256k1 CGO signature implementation and points it at the btec +// version. + +type Signer = btcec.Signer +type Keygen = btcec.Keygen + +func NewKeygen() (k *Keygen) { return new(Keygen) } + +var NewSecFromHex = btcec.NewSecFromHex[string] +var NewPubFromHex = btcec.NewPubFromHex[string] +var HexToBin = btcec.HexToBin diff --git a/pkg/crypto/p256k/btcec/btcec.go b/pkg/crypto/p256k/btcec/btcec.go new file mode 100644 index 0000000..047335f --- /dev/null +++ b/pkg/crypto/p256k/btcec/btcec.go @@ -0,0 +1,170 @@ +//go:build !cgo + +// Package btcec implements the signer.I interface for signatures and ECDH with nostr. +package btcec + +import ( + "lol.mleku.dev/chk" + "lol.mleku.dev/errorf" + btcec3 "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/schnorr" + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/interfaces/signer" +) + +// Signer is an implementation of signer.I that uses the btcec library. +type Signer struct { + SecretKey *secp256k1.SecretKey + PublicKey *secp256k1.PublicKey + BTCECSec *btcec3.SecretKey + pkb, skb []byte +} + +var _ signer.I = &Signer{} + +// Generate creates a new Signer. +func (s *Signer) Generate() (err error) { + if s.SecretKey, err = btcec3.NewSecretKey(); chk.E(err) { + return + } + s.skb = s.SecretKey.Serialize() + s.BTCECSec, _ = btcec3.PrivKeyFromBytes(s.skb) + s.PublicKey = s.SecretKey.PubKey() + s.pkb = schnorr.SerializePubKey(s.PublicKey) + return +} + +// InitSec initialises a Signer using raw secret key bytes. +func (s *Signer) InitSec(sec []byte) (err error) { + if len(sec) != secp256k1.SecKeyBytesLen { + err = errorf.E("sec key must be %d bytes", secp256k1.SecKeyBytesLen) + return + } + s.skb = sec + s.SecretKey = secp256k1.SecKeyFromBytes(sec) + s.PublicKey = s.SecretKey.PubKey() + s.pkb = schnorr.SerializePubKey(s.PublicKey) + s.BTCECSec, _ = btcec3.PrivKeyFromBytes(s.skb) + return +} + +// InitPub initializes a signature verifier Signer from raw public key bytes. +func (s *Signer) InitPub(pub []byte) (err error) { + if s.PublicKey, err = schnorr.ParsePubKey(pub); chk.E(err) { + return + } + s.pkb = pub + return +} + +// Sec returns the raw secret key bytes. +func (s *Signer) Sec() (b []byte) { + if s == nil { + return nil + } + return s.skb +} + +// Pub returns the raw BIP-340 schnorr public key bytes. +func (s *Signer) Pub() (b []byte) { + if s == nil { + return nil + } + return s.pkb +} + +// Sign a message with the Signer. Requires an initialised secret key. +func (s *Signer) Sign(msg []byte) (sig []byte, err error) { + if s.SecretKey == nil { + err = errorf.E("btcec: Signer not initialized") + return + } + var si *schnorr.Signature + if si, err = schnorr.Sign(s.SecretKey, msg); chk.E(err) { + return + } + sig = si.Serialize() + return +} + +// Verify a message signature, only requires the public key is initialised. +func (s *Signer) Verify(msg, sig []byte) (valid bool, err error) { + if s.PublicKey == nil { + err = errorf.E("btcec: Pubkey not initialized") + return + } + + // First try to verify using the schnorr package + var si *schnorr.Signature + if si, err = schnorr.ParseSignature(sig); err == nil { + valid = si.Verify(msg, s.PublicKey) + return + } + + // If parsing the signature failed, log it at debug level + chk.D(err) + + // If the signature is exactly 64 bytes, try to verify it directly + // This is to handle signatures created by p256k.Signer which uses libsecp256k1 + if len(sig) == schnorr.SignatureSize { + // Create a new signature with the raw bytes + var r secp256k1.FieldVal + var sScalar secp256k1.ModNScalar + + // Split the signature into r and s components + if overflow := r.SetByteSlice(sig[0:32]); !overflow { + sScalar.SetByteSlice(sig[32:64]) + + // Create a new signature and verify it + newSig := schnorr.NewSignature(&r, &sScalar) + valid = newSig.Verify(msg, s.PublicKey) + return + } + } + + // If all verification methods failed, return an error + err = errorf.E( + "failed to verify signature:\n%d %s", len(sig), sig, + ) + return +} + +// Zero wipes the bytes of the secret key. +func (s *Signer) Zero() { s.SecretKey.Key.Zero() } + +// ECDH creates a shared secret from a secret key and a provided public key bytes. It is advised +// to hash this result for security reasons. +func (s *Signer) ECDH(pubkeyBytes []byte) (secret []byte, err error) { + var pub *secp256k1.PublicKey + if pub, err = secp256k1.ParsePubKey( + append( + []byte{0x02}, pubkeyBytes..., + ), + ); chk.E(err) { + return + } + secret = btcec3.GenerateSharedSecret(s.BTCECSec, pub) + return +} + +// Keygen implements a key generator. Used for such things as vanity npub mining. +type Keygen struct { + Signer +} + +// Generate a new key pair. If the result is suitable, the embedded Signer can have its contents +// extracted. +func (k *Keygen) Generate() (pubBytes []byte, err error) { + if k.Signer.SecretKey, err = btcec3.NewSecretKey(); chk.E(err) { + return + } + k.Signer.PublicKey = k.SecretKey.PubKey() + k.Signer.pkb = schnorr.SerializePubKey(k.Signer.PublicKey) + pubBytes = k.Signer.pkb + return +} + +// KeyPairBytes returns the raw bytes of the embedded Signer. +func (k *Keygen) KeyPairBytes() (secBytes, cmprPubBytes []byte) { + return k.Signer.SecretKey.Serialize(), k.Signer.PublicKey.SerializeCompressed() +} diff --git a/pkg/crypto/p256k/btcec/btcec_test.go b/pkg/crypto/p256k/btcec/btcec_test.go new file mode 100644 index 0000000..9529715 --- /dev/null +++ b/pkg/crypto/p256k/btcec/btcec_test.go @@ -0,0 +1,195 @@ +//go:build !cgo + +package btcec_test + +import ( + "testing" + "time" + + "next.orly.dev/pkg/utils" + + "lol.mleku.dev/chk" + "lol.mleku.dev/log" + "next.orly.dev/pkg/crypto/p256k/btcec" +) + +func TestSigner_Generate(t *testing.T) { + for _ = range 100 { + var err error + signer := &btcec.Signer{} + var skb []byte + if err = signer.Generate(); chk.E(err) { + t.Fatal(err) + } + skb = signer.Sec() + if err = signer.InitSec(skb); chk.E(err) { + t.Fatal(err) + } + } +} + +// func TestBTCECSignerVerify(t *testing.T) { +// evs := make([]*event.E, 0, 10000) +// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache)) +// buf := make([]byte, 1_000_000) +// scanner.Buffer(buf, len(buf)) +// var err error +// +// // Create both btcec and p256k signers +// btcecSigner := &btcec.Signer{} +// p256kSigner := &p256k.Signer{} +// +// for scanner.Scan() { +// var valid bool +// b := scanner.Bytes() +// ev := event.New() +// if _, err = ev.Unmarshal(b); chk.E(err) { +// t.Errorf("failed to marshal\n%s", b) +// } else { +// // We know ev.Verify() works, so we'll use it as a reference +// if valid, err = ev.Verify(); chk.E(err) || !valid { +// t.Errorf("invalid signature\n%s", b) +// continue +// } +// } +// +// // Get the ID from the event +// storedID := ev.ID +// calculatedID := ev.GetIDBytes() +// +// // Check if the stored ID matches the calculated ID +// if !utils.FastEqual(storedID, calculatedID) { +// log.D.Ln("Event ID mismatch: stored ID doesn't match calculated ID") +// // Use the calculated ID for verification as ev.Verify() would do +// ev.ID = calculatedID +// } +// +// if len(ev.ID) != sha256.Size { +// t.Errorf("id should be 32 bytes, got %d", len(ev.ID)) +// continue +// } +// +// // Initialize both signers with the same public key +// if err = btcecSigner.InitPub(ev.Pubkey); chk.E(err) { +// t.Errorf("failed to init btcec pub key: %s\n%0x", err, b) +// } +// if err = p256kSigner.InitPub(ev.Pubkey); chk.E(err) { +// t.Errorf("failed to init p256k pub key: %s\n%0x", err, b) +// } +// +// // First try to verify with btcec.Signer +// if valid, err = btcecSigner.Verify(ev.ID, ev.Sig); err == nil && valid { +// // If btcec.Signer verification succeeds, great! +// log.D.Ln("btcec.Signer verification succeeded") +// } else { +// // If btcec.Signer verification fails, try with p256k.Signer +// // Use chk.T(err) like ev.Verify() does +// if valid, err = p256kSigner.Verify(ev.ID, ev.Sig); chk.T(err) { +// // If there's an error, log it but don't fail the test +// log.D.Ln("p256k.Signer verification error:", err) +// } else if !valid { +// // Only fail the test if both verifications fail +// t.Errorf( +// "invalid signature for pub %0x %0x %0x", ev.Pubkey, ev.ID, +// ev.Sig, +// ) +// } else { +// log.D.Ln("p256k.Signer verification succeeded where btcec.Signer failed") +// } +// } +// +// evs = append(evs, ev) +// } +// } + +// func TestBTCECSignerSign(t *testing.T) { +// evs := make([]*event.E, 0, 10000) +// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache)) +// buf := make([]byte, 1_000_000) +// scanner.Buffer(buf, len(buf)) +// var err error +// signer := &btcec.Signer{} +// var skb []byte +// if err = signer.Generate(); chk.E(err) { +// t.Fatal(err) +// } +// skb = signer.Sec() +// if err = signer.InitSec(skb); chk.E(err) { +// t.Fatal(err) +// } +// verifier := &btcec.Signer{} +// pkb := signer.Pub() +// if err = verifier.InitPub(pkb); chk.E(err) { +// t.Fatal(err) +// } +// counter := 0 +// for scanner.Scan() { +// counter++ +// if counter > 1000 { +// break +// } +// b := scanner.Bytes() +// ev := event.New() +// if _, err = ev.Unmarshal(b); chk.E(err) { +// t.Errorf("failed to marshal\n%s", b) +// } +// evs = append(evs, ev) +// } +// var valid bool +// sig := make([]byte, schnorr.SignatureSize) +// for _, ev := range evs { +// ev.Pubkey = pkb +// id := ev.GetIDBytes() +// if sig, err = signer.Sign(id); chk.E(err) { +// t.Errorf("failed to sign: %s\n%0x", err, id) +// } +// if valid, err = verifier.Verify(id, sig); chk.E(err) { +// t.Errorf("failed to verify: %s\n%0x", err, id) +// } +// if !valid { +// t.Errorf("invalid signature") +// } +// } +// signer.Zero() +// } + +func TestBTCECECDH(t *testing.T) { + n := time.Now() + var err error + var counter int + const total = 50 + for _ = range total { + s1 := new(btcec.Signer) + if err = s1.Generate(); chk.E(err) { + t.Fatal(err) + } + s2 := new(btcec.Signer) + if err = s2.Generate(); chk.E(err) { + t.Fatal(err) + } + for _ = range total { + var secret1, secret2 []byte + if secret1, err = s1.ECDH(s2.Pub()); chk.E(err) { + t.Fatal(err) + } + if secret2, err = s2.ECDH(s1.Pub()); chk.E(err) { + t.Fatal(err) + } + if !utils.FastEqual(secret1, secret2) { + counter++ + t.Errorf( + "ECDH generation failed to work in both directions, %x %x", + secret1, + secret2, + ) + } + } + } + a := time.Now() + duration := a.Sub(n) + log.I.Ln( + "errors", counter, "total", total, "time", duration, "time/op", + int(duration/total), + "ops/sec", int(time.Second)/int(duration/total), + ) +} diff --git a/pkg/crypto/p256k/btcec/helpers-btcec.go b/pkg/crypto/p256k/btcec/helpers-btcec.go new file mode 100644 index 0000000..ef53aa8 --- /dev/null +++ b/pkg/crypto/p256k/btcec/helpers-btcec.go @@ -0,0 +1,41 @@ +//go:build !cgo + +package btcec + +import ( + "lol.mleku.dev/chk" + "next.orly.dev/pkg/encoders/hex" + "next.orly.dev/pkg/interfaces/signer" +) + +func NewSecFromHex[V []byte | string](skh V) (sign signer.I, err error) { + sk := make([]byte, len(skh)/2) + if _, err = hex.DecBytes(sk, []byte(skh)); chk.E(err) { + return + } + sign = &Signer{} + if err = sign.InitSec(sk); chk.E(err) { + return + } + return +} + +func NewPubFromHex[V []byte | string](pkh V) (sign signer.I, err error) { + pk := make([]byte, len(pkh)/2) + if _, err = hex.DecBytes(pk, []byte(pkh)); chk.E(err) { + return + } + sign = &Signer{} + if err = sign.InitPub(pk); chk.E(err) { + return + } + return +} + +func HexToBin(hexStr string) (b []byte, err error) { + b = make([]byte, len(hexStr)/2) + if _, err = hex.DecBytes(b, []byte(hexStr)); chk.E(err) { + return + } + return +} diff --git a/pkg/crypto/p256k/doc.go b/pkg/crypto/p256k/doc.go new file mode 100644 index 0000000..d88e08e --- /dev/null +++ b/pkg/crypto/p256k/doc.go @@ -0,0 +1,6 @@ +// Package p256k is a signer interface that (by default) uses the +// bitcoin/libsecp256k1 library for fast signature creation and verification of +// the BIP-340 nostr X-only signatures and public keys, and ECDH. +// +// Currently the ECDH is only implemented with the btcec library. +package p256k diff --git a/pkg/crypto/p256k/helpers.go b/pkg/crypto/p256k/helpers.go new file mode 100644 index 0000000..1f4a740 --- /dev/null +++ b/pkg/crypto/p256k/helpers.go @@ -0,0 +1,40 @@ +//go:build cgo + +package p256k + +import ( + "lol.mleku.dev/chk" + "next.orly.dev/pkg/encoders/hex" + "next.orly.dev/pkg/interfaces/signer" +) + +func NewSecFromHex[V []byte | string](skh V) (sign signer.I, err error) { + sk := make([]byte, len(skh)/2) + if _, err = hex.DecBytes(sk, []byte(skh)); chk.E(err) { + return + } + sign = &Signer{} + if err = sign.InitSec(sk); chk.E(err) { + return + } + return +} + +func NewPubFromHex[V []byte | string](pkh V) (sign signer.I, err error) { + pk := make([]byte, len(pkh)/2) + if _, err = hex.DecBytes(pk, []byte(pkh)); chk.E(err) { + return + } + sign = &Signer{} + if err = sign.InitPub(pk); chk.E(err) { + return + } + return +} + +func HexToBin(hexStr string) (b []byte, err error) { + if b, err = hex.DecAppend(b, []byte(hexStr)); chk.E(err) { + return + } + return +} diff --git a/pkg/crypto/p256k/p256k.go b/pkg/crypto/p256k/p256k.go new file mode 100644 index 0000000..68d10ea --- /dev/null +++ b/pkg/crypto/p256k/p256k.go @@ -0,0 +1,140 @@ +//go:build cgo + +package p256k + +import "C" +import ( + "lol.mleku.dev/chk" + "lol.mleku.dev/errorf" + "lol.mleku.dev/log" + "next.orly.dev/pkg/crypto/ec" + "next.orly.dev/pkg/crypto/ec/secp256k1" + realy "next.orly.dev/pkg/interfaces/signer" +) + +func init() { + log.T.Ln("using bitcoin/secp256k1 signature library") +} + +// Signer implements the signer.I interface. +// +// Either the Sec or Pub must be populated, the former is for generating +// signatures, the latter is for verifying them. +// +// When using this library only for verification, a constructor that converts +// from bytes to PubKey is needed prior to calling Verify. +type Signer struct { + // SecretKey is the secret key. + SecretKey *SecKey + // PublicKey is the public key. + PublicKey *PubKey + // BTCECSec is needed for ECDH as currently the CGO bindings don't include it + BTCECSec *btcec.SecretKey + skb, pkb []byte +} + +var _ realy.I = &Signer{} + +// Generate a new Signer key pair using the CGO bindings to libsecp256k1 +func (s *Signer) Generate() (err error) { + var cs *Sec + var cx *XPublicKey + if s.skb, s.pkb, cs, cx, err = Generate(); chk.E(err) { + return + } + s.SecretKey = &cs.Key + s.PublicKey = cx.Key + s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb) + return +} + +func (s *Signer) InitSec(skb []byte) (err error) { + var cs *Sec + var cx *XPublicKey + // var cp *PublicKey + if s.pkb, cs, cx, err = FromSecretBytes(skb); chk.E(err) { + if err.Error() != "provided secret generates a public key with odd Y coordinate, fixed version returned" { + log.E.Ln(err) + return + } + } + s.skb = skb + s.SecretKey = &cs.Key + s.PublicKey = cx.Key + // s.ECPublicKey = cp.Key + // needed for ecdh + s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb) + return +} + +func (s *Signer) InitPub(pub []byte) (err error) { + var up *Pub + if up, err = PubFromBytes(pub); chk.E(err) { + return + } + s.PublicKey = &up.Key + s.pkb = up.PubB() + return +} + +func (s *Signer) Sec() (b []byte) { + if s == nil { + return nil + } + return s.skb +} +func (s *Signer) Pub() (b []byte) { + if s == nil { + return nil + } + return s.pkb +} + +// func (s *Signer) ECPub() (b []byte) { return s.pkb } + +func (s *Signer) Sign(msg []byte) (sig []byte, err error) { + if s.SecretKey == nil { + err = errorf.E("p256k: I secret not initialized") + return + } + u := ToUchar(msg) + if sig, err = Sign(u, s.SecretKey); chk.E(err) { + return + } + return +} + +func (s *Signer) Verify(msg, sig []byte) (valid bool, err error) { + if s.PublicKey == nil { + err = errorf.E("p256k: Pubkey not initialized") + return + } + var uMsg, uSig *Uchar + if uMsg, err = Msg(msg); chk.E(err) { + return + } + if uSig, err = Sig(sig); chk.E(err) { + return + } + valid = Verify(uMsg, uSig, s.PublicKey) + if !valid { + err = errorf.E("p256k: invalid signature") + } + return +} + +func (s *Signer) ECDH(pubkeyBytes []byte) (secret []byte, err error) { + var pub *secp256k1.PublicKey + if pub, err = secp256k1.ParsePubKey( + append( + []byte{0x02}, + pubkeyBytes..., + ), + ); chk.E(err) { + return + } + secret = btcec.GenerateSharedSecret(s.BTCECSec, pub) + return +} + +func (s *Signer) Zero() { Zero(s.SecretKey) } diff --git a/pkg/crypto/p256k/p256k_test.go b/pkg/crypto/p256k/p256k_test.go new file mode 100644 index 0000000..8364480 --- /dev/null +++ b/pkg/crypto/p256k/p256k_test.go @@ -0,0 +1,162 @@ +//go:build cgo + +package p256k_test + +import ( + "testing" + "time" + + "next.orly.dev/pkg/utils" + + "lol.mleku.dev/chk" + "lol.mleku.dev/log" + "next.orly.dev/pkg/crypto/p256k" + realy "next.orly.dev/pkg/interfaces/signer" +) + +func TestSigner_Generate(t *testing.T) { + for _ = range 10000 { + var err error + signer := &p256k.Signer{} + var skb []byte + if err = signer.Generate(); chk.E(err) { + t.Fatal(err) + } + skb = signer.Sec() + if err = signer.InitSec(skb); chk.E(err) { + t.Fatal(err) + } + } +} + +// func TestSignerVerify(t *testing.T) { +// // evs := make([]*event.E, 0, 10000) +// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache)) +// buf := make([]byte, 1_000_000) +// scanner.Buffer(buf, len(buf)) +// var err error +// signer := &p256k.Signer{} +// for scanner.Scan() { +// var valid bool +// b := scanner.Bytes() +// bc := make([]byte, 0, len(b)) +// bc = append(bc, b...) +// ev := event.New() +// if _, err = ev.Unmarshal(b); chk.E(err) { +// t.Errorf("failed to marshal\n%s", b) +// } else { +// if valid, err = ev.Verify(); chk.T(err) || !valid { +// t.Errorf("invalid signature\n%s", bc) +// continue +// } +// } +// id := ev.GetIDBytes() +// if len(id) != sha256.Size { +// t.Errorf("id should be 32 bytes, got %d", len(id)) +// continue +// } +// if err = signer.InitPub(ev.Pubkey); chk.T(err) { +// t.Errorf("failed to init pub key: %s\n%0x", err, ev.Pubkey) +// continue +// } +// if valid, err = signer.Verify(id, ev.Sig); chk.E(err) { +// t.Errorf("failed to verify: %s\n%0x", err, ev.ID) +// continue +// } +// if !valid { +// t.Errorf( +// "invalid signature for\npub %0x\neid %0x\nsig %0x\n%s", +// ev.Pubkey, id, ev.Sig, bc, +// ) +// continue +// } +// // fmt.Printf("%s\n", bc) +// // evs = append(evs, ev) +// } +// } + +// func TestSignerSign(t *testing.T) { +// evs := make([]*event.E, 0, 10000) +// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache)) +// buf := make([]byte, 1_000_000) +// scanner.Buffer(buf, len(buf)) +// var err error +// signer := &p256k.Signer{} +// var skb, pkb []byte +// if skb, pkb, _, _, err = p256k.Generate(); chk.E(err) { +// t.Fatal(err) +// } +// log.I.S(skb, pkb) +// if err = signer.InitSec(skb); chk.E(err) { +// t.Fatal(err) +// } +// verifier := &p256k.Signer{} +// if err = verifier.InitPub(pkb); chk.E(err) { +// t.Fatal(err) +// } +// for scanner.Scan() { +// b := scanner.Bytes() +// ev := event.New() +// if _, err = ev.Unmarshal(b); chk.E(err) { +// t.Errorf("failed to marshal\n%s", b) +// } +// evs = append(evs, ev) +// } +// var valid bool +// sig := make([]byte, schnorr.SignatureSize) +// for _, ev := range evs { +// ev.Pubkey = pkb +// id := ev.GetIDBytes() +// if sig, err = signer.Sign(id); chk.E(err) { +// t.Errorf("failed to sign: %s\n%0x", err, id) +// } +// if valid, err = verifier.Verify(id, sig); chk.E(err) { +// t.Errorf("failed to verify: %s\n%0x", err, id) +// } +// if !valid { +// t.Errorf("invalid signature") +// } +// } +// signer.Zero() +// } + +func TestECDH(t *testing.T) { + n := time.Now() + var err error + var s1, s2 realy.I + var counter int + const total = 100 + for _ = range total { + s1, s2 = &p256k.Signer{}, &p256k.Signer{} + if err = s1.Generate(); chk.E(err) { + t.Fatal(err) + } + for _ = range total { + if err = s2.Generate(); chk.E(err) { + t.Fatal(err) + } + var secret1, secret2 []byte + if secret1, err = s1.ECDH(s2.Pub()); chk.E(err) { + t.Fatal(err) + } + if secret2, err = s2.ECDH(s1.Pub()); chk.E(err) { + t.Fatal(err) + } + if !utils.FastEqual(secret1, secret2) { + counter++ + t.Errorf( + "ECDH generation failed to work in both directions, %x %x", + secret1, + secret2, + ) + } + } + } + a := time.Now() + duration := a.Sub(n) + log.I.Ln( + "errors", counter, "total", total*total, "time", duration, "time/op", + duration/total/total, "ops/sec", + float64(time.Second)/float64(duration/total/total), + ) +} diff --git a/pkg/crypto/p256k/secp256k1.go b/pkg/crypto/p256k/secp256k1.go new file mode 100644 index 0000000..c76bb5e --- /dev/null +++ b/pkg/crypto/p256k/secp256k1.go @@ -0,0 +1,426 @@ +//go:build cgo + +package p256k + +import ( + "crypto/rand" + "unsafe" + + "lol.mleku.dev/chk" + "lol.mleku.dev/errorf" + "lol.mleku.dev/log" + "next.orly.dev/pkg/crypto/ec/schnorr" + "next.orly.dev/pkg/crypto/ec/secp256k1" + "next.orly.dev/pkg/crypto/sha256" +) + +/* +#cgo LDFLAGS: -lsecp256k1 +#include +#include +#include +*/ +import "C" + +type ( + Context = C.secp256k1_context + Uchar = C.uchar + Cint = C.int + SecKey = C.secp256k1_keypair + PubKey = C.secp256k1_xonly_pubkey + ECPubKey = C.secp256k1_pubkey +) + +var ( + ctx *Context +) + +func CreateContext() *Context { + return C.secp256k1_context_create( + C.SECP256K1_CONTEXT_SIGN | + C.SECP256K1_CONTEXT_VERIFY, + ) +} + +func GetRandom() (u *Uchar) { + rnd := make([]byte, 32) + _, _ = rand.Read(rnd) + return ToUchar(rnd) +} + +func AssertLen(b []byte, length int, name string) (err error) { + if len(b) != length { + err = errorf.E("%s should be %d bytes, got %d", name, length, len(b)) + } + return +} + +func RandomizeContext(ctx *C.secp256k1_context) { + C.secp256k1_context_randomize(ctx, GetRandom()) + return +} + +func CreateRandomContext() (c *Context) { + c = CreateContext() + RandomizeContext(c) + return +} + +func init() { + if ctx = CreateContext(); ctx == nil { + panic("failed to create secp256k1 context") + } +} + +func ToUchar(b []byte) (u *Uchar) { return (*Uchar)(unsafe.Pointer(&b[0])) } + +type Sec struct { + Key SecKey +} + +func GenSec() (sec *Sec, err error) { + if _, _, sec, _, err = Generate(); chk.E(err) { + return + } + return +} + +func SecFromBytes(sk []byte) (sec *Sec, err error) { + sec = new(Sec) + if C.secp256k1_keypair_create(ctx, &sec.Key, ToUchar(sk)) != 1 { + err = errorf.E("failed to parse private key") + return + } + return +} + +func (s *Sec) Sec() *SecKey { return &s.Key } + +func (s *Sec) Pub() (p *Pub, err error) { + p = new(Pub) + if C.secp256k1_keypair_xonly_pub(ctx, &p.Key, nil, s.Sec()) != 1 { + err = errorf.E("pubkey derivation failed") + return + } + return +} + +// type PublicKey struct { +// Key *C.secp256k1_pubkey +// } +// +// func NewPublicKey() *PublicKey { +// return &PublicKey{ +// Key: &C.secp256k1_pubkey{}, +// } +// } + +type XPublicKey struct { + Key *C.secp256k1_xonly_pubkey +} + +func NewXPublicKey() *XPublicKey { + return &XPublicKey{ + Key: &C.secp256k1_xonly_pubkey{}, + } +} + +// FromSecretBytes parses and processes what should be a secret key. If it is a correct key within the curve order, but +// with a public key having an odd Y coordinate, it returns an error with the fixed key. +func FromSecretBytes(skb []byte) ( + pkb []byte, + sec *Sec, + pub *XPublicKey, + // ecPub *PublicKey, + err error, +) { + xpkb := make([]byte, schnorr.PubKeyBytesLen) + // clen := C.size_t(secp256k1.PubKeyBytesLenCompressed - 1) + pkb = make([]byte, schnorr.PubKeyBytesLen) + var parity Cint + // ecPub = NewPublicKey() + pub = NewXPublicKey() + sec = &Sec{} + uskb := ToUchar(skb) + res := C.secp256k1_keypair_create(ctx, &sec.Key, uskb) + if res != 1 { + err = errorf.E("failed to create secp256k1 keypair") + return + } + // C.secp256k1_keypair_pub(ctx, ecPub.Key, &sec.Key) + // C.secp256k1_ec_pubkey_serialize(ctx, ToUchar(ecpkb), &clen, ecPub.Key, + // C.SECP256K1_EC_COMPRESSED) + // if ecpkb[0] != 2 { + // log.W.ToSliceOfBytes("odd pubkey from %0x -> %0x", skb, ecpkb) + // Negate(skb) + // uskb = ToUchar(skb) + // res = C.secp256k1_keypair_create(ctx, &sec.Key, uskb) + // if res != 1 { + // err = errorf.E("failed to create secp256k1 keypair") + // return + // } + // C.secp256k1_keypair_pub(ctx, ecPub.Key, &sec.Key) + // C.secp256k1_ec_pubkey_serialize(ctx, ToUchar(ecpkb), &clen, ecPub.Key, C.SECP256K1_EC_COMPRESSED) + // C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key) + // err = errors.New("provided secret generates a public key with odd Y coordinate, fixed version returned") + // } + C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key) + C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(xpkb), pub.Key) + pkb = xpkb + // log.I.S(sec, pub, skb, pkb) + return +} + +// Generate gathers entropy to generate a full set of bytes and CGO values of it and derived from it to perform +// signature and ECDH operations. +func Generate() ( + skb, pkb []byte, + sec *Sec, + pub *XPublicKey, + err error, +) { + skb = make([]byte, secp256k1.SecKeyBytesLen) + pkb = make([]byte, schnorr.PubKeyBytesLen) + upkb := ToUchar(pkb) + var parity Cint + pub = NewXPublicKey() + sec = &Sec{} + for { + if _, err = rand.Read(skb); chk.E(err) { + return + } + uskb := ToUchar(skb) + if res := C.secp256k1_keypair_create(ctx, &sec.Key, uskb); res != 1 { + err = errorf.E("failed to create secp256k1 keypair") + continue + } + C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key) + C.secp256k1_xonly_pubkey_serialize(ctx, upkb, pub.Key) + break + } + return +} + +// Negate inverts a secret key so an odd prefix bit becomes even and vice versa. +func Negate(uskb []byte) { C.secp256k1_ec_seckey_negate(ctx, ToUchar(uskb)) } + +type ECPub struct { + Key ECPubKey +} + +// ECPubFromSchnorrBytes converts a BIP-340 public key to its even standard 33 byte encoding. +// +// This function is for the purpose of getting a key to do ECDH from an x-only key. +func ECPubFromSchnorrBytes(xkb []byte) (pub *ECPub, err error) { + if err = AssertLen(xkb, schnorr.PubKeyBytesLen, "pubkey"); chk.E(err) { + return + } + pub = &ECPub{} + p := append([]byte{0}, xkb...) + if C.secp256k1_ec_pubkey_parse( + ctx, &pub.Key, ToUchar(p), + secp256k1.PubKeyBytesLenCompressed, + ) != 1 { + err = errorf.E("failed to parse pubkey from %0x", p) + log.I.S(pub) + return + } + return +} + +// // ECPubFromBytes parses a pubkey from 33 bytes to the bitcoin-core/secp256k1 struct. +// func ECPubFromBytes(pkb []byte) (pub *ECPub, err error) { +// if err = AssertLen(pkb, secp256k1.PubKeyBytesLenCompressed, "pubkey"); chk.E(err) { +// return +// } +// pub = &ECPub{} +// if C.secp256k1_ec_pubkey_parse(ctx, &pub.Key, ToUchar(pkb), +// secp256k1.PubKeyBytesLenCompressed) != 1 { +// err = errorf.E("failed to parse pubkey from %0x", pkb) +// log.I.S(pub) +// return +// } +// return +// } + +// Pub is a schnorr BIP-340 public key. +type Pub struct { + Key PubKey +} + +// PubFromBytes creates a public key from raw bytes. +func PubFromBytes(pk []byte) (pub *Pub, err error) { + if err = AssertLen(pk, schnorr.PubKeyBytesLen, "pubkey"); chk.E(err) { + return + } + pub = new(Pub) + if C.secp256k1_xonly_pubkey_parse(ctx, &pub.Key, ToUchar(pk)) != 1 { + err = errorf.E("failed to parse pubkey from %0x", pk) + return + } + return +} + +// PubB returns the contained public key as bytes. +func (p *Pub) PubB() (b []byte) { + b = make([]byte, schnorr.PubKeyBytesLen) + C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(b), &p.Key) + return +} + +// Pub returns the public key as a PubKey. +func (p *Pub) Pub() *PubKey { return &p.Key } + +// ToBytes returns the contained public key as bytes. +func (p *Pub) ToBytes() (b []byte, err error) { + b = make([]byte, schnorr.PubKeyBytesLen) + if C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(b), p.Pub()) != 1 { + err = errorf.E("pubkey serialize failed") + return + } + return +} + +// Sign a message and return a schnorr BIP-340 64 byte signature. +func Sign(msg *Uchar, sk *SecKey) (sig []byte, err error) { + sig = make([]byte, schnorr.SignatureSize) + c := CreateRandomContext() + if C.secp256k1_schnorrsig_sign32( + c, ToUchar(sig), msg, sk, + GetRandom(), + ) != 1 { + err = errorf.E("failed to sign message") + return + } + return +} + +// SignFromBytes Signs a message using a provided secret key and message as raw bytes. +func SignFromBytes(msg, sk []byte) (sig []byte, err error) { + var umsg *Uchar + if umsg, err = Msg(msg); chk.E(err) { + return + } + var sec *Sec + if sec, err = SecFromBytes(sk); chk.E(err) { + return + } + return Sign(umsg, sec.Sec()) +} + +// Msg checks that a message hash is correct, and converts it for use with a Signer. +func Msg(b []byte) (id *Uchar, err error) { + if err = AssertLen(b, sha256.Size, "id"); chk.E(err) { + return + } + id = ToUchar(b) + return +} + +// Sig checks that a signature bytes is correct, and converts it for use with a Signer. +func Sig(b []byte) (sig *Uchar, err error) { + if err = AssertLen(b, schnorr.SignatureSize, "sig"); chk.E(err) { + return + } + sig = ToUchar(b) + return +} + +// Verify a message signature matches the provided PubKey. +func Verify(msg, sig *Uchar, pk *PubKey) (valid bool) { + return C.secp256k1_schnorrsig_verify(ctx, sig, msg, 32, pk) == 1 +} + +// VerifyFromBytes a signature from the raw bytes of the message hash, signature and public key +func VerifyFromBytes(msg, sig, pk []byte) (err error) { + var umsg, usig *Uchar + if umsg, err = Msg(msg); chk.E(err) { + return + } + if usig, err = Sig(sig); chk.E(err) { + return + } + var pub *Pub + if pub, err = PubFromBytes(pk); chk.E(err) { + return + } + valid := Verify(umsg, usig, pub.Pub()) + if !valid { + err = errorf.E("failed to verify signature") + } + return +} + +// Zero wipes the memory of a SecKey by overwriting it three times with random data and then +// zeroing it. +func Zero(sk *SecKey) { + b := (*[96]byte)(unsafe.Pointer(sk))[:96] + for range 3 { + rand.Read(b) + // reverse the order and negate + lb := len(b) + l := lb / 2 + for j := range l { + b[j] = ^b[lb-1-j] + } + } + for i := range b { + b[i] = 0 + } +} + +// Keygen is an implementation of a key miner designed to be used for vanity key generation with X-only BIP-340 keys. +type Keygen struct { + secBytes, comprPubBytes []byte + secUchar, cmprPubUchar *Uchar + sec *Sec + // ecpub *PublicKey + cmprLen C.size_t +} + +// NewKeygen allocates the required buffers for deriving a key. This should only be done once to avoid garbage and make +// the key mining as fast as possible. +// +// This allocates everything and creates proper CGO variables needed for the generate function so they only need to be +// allocated once per thread. +func NewKeygen() (k *Keygen) { + k = new(Keygen) + k.cmprLen = C.size_t(secp256k1.PubKeyBytesLenCompressed) + k.secBytes = make([]byte, secp256k1.SecKeyBytesLen) + k.comprPubBytes = make([]byte, secp256k1.PubKeyBytesLenCompressed) + k.secUchar = ToUchar(k.secBytes) + k.cmprPubUchar = ToUchar(k.comprPubBytes) + k.sec = &Sec{} + // k.ecpub = NewPublicKey() + return +} + +// Generate takes a pair of buffers for the secret and ec pubkey bytes and gathers new entropy and returns a valid +// secret key and the compressed pubkey bytes for the partial collision search. +// +// The first byte of pubBytes must be sliced off before deriving the hex/Bech32 forms of the nostr public key. +func (k *Keygen) Generate() ( + sec *Sec, + pub *XPublicKey, + pubBytes []byte, + err error, +) { + if _, err = rand.Read(k.secBytes); chk.E(err) { + return + } + if res := C.secp256k1_keypair_create( + ctx, &k.sec.Key, k.secUchar, + ); res != 1 { + err = errorf.E("failed to create secp256k1 keypair") + return + } + var parity Cint + C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key) + // C.secp256k1_keypair_pub(ctx, k.ecpub.Key, &k.sec.Key) + // C.secp256k1_ec_pubkey_serialize(ctx, k.cmprPubUchar, &k.cmprLen, k.ecpub.Key, + // C.SECP256K1_EC_COMPRESSED) + // pubBytes = k.comprPubBytes + C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(pubBytes), pub.Key) + // pubBytes = + return +} diff --git a/pkg/crypto/p256k/secp256k1_test.go b/pkg/crypto/p256k/secp256k1_test.go new file mode 100644 index 0000000..c49e9c6 --- /dev/null +++ b/pkg/crypto/p256k/secp256k1_test.go @@ -0,0 +1,76 @@ +//go:build cgo + +package p256k_test + +// func TestVerify(t *testing.T) { +// evs := make([]*event.E, 0, 10000) +// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache)) +// buf := make([]byte, 1_000_000) +// scanner.Buffer(buf, len(buf)) +// var err error +// for scanner.Scan() { +// var valid bool +// b := scanner.Bytes() +// ev := event.New() +// if _, err = ev.Unmarshal(b); chk.E(err) { +// t.Errorf("failed to marshal\n%s", b) +// } else { +// if valid, err = ev.Verify(); chk.E(err) || !valid { +// t.Errorf("btcec: invalid signature\n%s", b) +// continue +// } +// } +// id := ev.GetIDBytes() +// if len(id) != sha256.Size { +// t.Errorf("id should be 32 bytes, got %d", len(id)) +// continue +// } +// if err = p256k.VerifyFromBytes(id, ev.Sig, ev.Pubkey); chk.E(err) { +// t.Error(err) +// continue +// } +// evs = append(evs, ev) +// } +// } + +// func TestSign(t *testing.T) { +// evs := make([]*event.E, 0, 10000) +// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache)) +// buf := make([]byte, 1_000_000) +// scanner.Buffer(buf, len(buf)) +// var err error +// var sec1 *p256k.Sec +// var pub1 *p256k.XPublicKey +// var pb []byte +// if _, pb, sec1, pub1, err = p256k.Generate(); chk.E(err) { +// t.Fatal(err) +// } +// for scanner.Scan() { +// b := scanner.Bytes() +// ev := event.New() +// if _, err = ev.Unmarshal(b); chk.E(err) { +// t.Errorf("failed to marshal\n%s", b) +// } +// evs = append(evs, ev) +// } +// sig := make([]byte, schnorr.SignatureSize) +// for _, ev := range evs { +// ev.Pubkey = pb +// var uid *p256k.Uchar +// if uid, err = p256k.Msg(ev.GetIDBytes()); chk.E(err) { +// t.Fatal(err) +// } +// if sig, err = p256k.Sign(uid, sec1.Sec()); chk.E(err) { +// t.Fatal(err) +// } +// ev.Sig = sig +// var usig *p256k.Uchar +// if usig, err = p256k.Sig(sig); chk.E(err) { +// t.Fatal(err) +// } +// if !p256k.Verify(uid, usig, pub1.Key) { +// t.Errorf("invalid signature") +// } +// } +// p256k.Zero(&sec1.Key) +// } diff --git a/pkg/crypto/sha256/LICENSE b/pkg/crypto/sha256/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/pkg/crypto/sha256/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/pkg/crypto/sha256/README.md b/pkg/crypto/sha256/README.md new file mode 100644 index 0000000..db2c3c2 --- /dev/null +++ b/pkg/crypto/sha256/README.md @@ -0,0 +1,197 @@ +# sha256-simd + +Accelerate SHA256 computations in pure Go using AVX512, SHA Extensions for x86 +and ARM64 for ARM. +On AVX512 it provides an up to 8x improvement (over 3 GB/s per core). +SHA Extensions give a performance boost of close to 4x over native. + +## Introduction + +This package is designed as a replacement for `crypto/sha256`. +For ARM CPUs with the Cryptography Extensions, advantage is taken of the SHA2 +instructions resulting in a massive performance improvement. + +This package uses Golang assembly. +The AVX512 version is based on the Intel's "multi-buffer crypto library for +IPSec" whereas the other Intel implementations are described in "Fast SHA-256 +Implementations on Intel Architecture Processors" by J. Guilford et al. + +## Support for Intel SHA Extensions + +Support for the Intel SHA Extensions has been added by Kristofer Peterson ( +@svenski123), originally developed for +spacemeshos [here](https://github.com/spacemeshos/POET/issues/23). On CPUs that +support it (known thus far Intel Celeron J3455 and AMD Ryzen) it gives a +significant boost in performance (with thanks to @AudriusButkevicius for +reporting the results; full +results [here](https://github.com/minio/sha256-simd/pull/37#issuecomment-451607827)). + +``` +$ benchcmp avx2.txt sha-ext.txt +benchmark AVX2 MB/s SHA Ext MB/s speedup +BenchmarkHash5M 514.40 1975.17 3.84x +``` + +Thanks to Kristofer Peterson, we also added additional performance changes such +as optimized padding, +endian conversions which sped up all implementations i.e. Intel SHA alone while +doubled performance for small sizes, +the other changes increased everything roughly 50%. + +## Support for AVX512 + +We have added support for AVX512 which results in an up to 8x performance +improvement over AVX2 (3.0 GHz Xeon Platinum 8124M CPU): + +``` +$ benchcmp avx2.txt avx512.txt +benchmark AVX2 MB/s AVX512 MB/s speedup +BenchmarkHash5M 448.62 3498.20 7.80x +``` + +The original code was developed by Intel as part of +the [multi-buffer crypto library](https://github.com/intel/intel-ipsec-mb) for +IPSec or more specifically +this [AVX512](https://github.com/intel/intel-ipsec-mb/blob/master/avx512/sha256_x16_avx512.asm) +implementation. The key idea behind it is to process a total of 16 checksums in +parallel by “transposing” 16 (independent) messages of 64 bytes between a total +of 16 ZMM registers (each 64 bytes wide). + +Transposing the input messages means that in order to take full advantage of the +speedup you need to have a (server) workload where multiple threads are doing +SHA256 calculations in parallel. Unfortunately for this algorithm it is not +possible for two message blocks processed in parallel to be dependent on one +another — because then the (interim) result of the first part of the message has +to be an input into the processing of the second part of the message. + +Whereas the original Intel C implementation requires some sort of explicit +scheduling of messages to be processed in parallel, for Golang it makes sense to +take advantage of channels in order to group messages together and use channels +as well for sending back the results (thereby effectively decoupling the +calculations). We have implemented a fairly simple scheduling mechanism that +seems to work well in practice. + +Due to this different way of scheduling, we decided to use an explicit method to +instantiate the AVX512 version. Essentially one or more AVX512 processing +servers ([ +`Avx512Server`](https://github.com/minio/sha256-simd/blob/master/sha256blockAvx512_amd64.go#L294)) +have to be created whereby each server can hash over 3 GB/s on a single core. An +`hash.Hash` object ([ +`Avx512Digest`](https://github.com/minio/sha256-simd/blob/master/sha256blockAvx512_amd64.go#L45)) +is then instantiated using one of these servers and used in the regular fashion: + +```go +import "mleku.dev/pkg/sha256" + +func main() { + server := sha256.NewAvx512Server() + h512 := sha256.NewAvx512(server) + h512.Write(fileBlock) + digest := h512.Sum([]byte{}) +} +``` + +Note that, because of the scheduling overhead, for small messages (< 1 MB) you +will be better off using the regular SHA256 hashing (but those are typically not +performance critical anyway). Some other tips to get the best performance: + +* Have many go routines doing SHA256 calculations in parallel. +* Try to Write() messages in multiples of 64 bytes. +* Try to keep the overall length of messages to a roughly similar size ie. 5 + MB (this way all 16 ‘lanes’ in the AVX512 computations are contributing as + much as possible). + +More detailed information can be found in +this [blog](https://blog.minio.io/accelerate-sha256-up-to-8x-over-3-gb-s-per-core-with-avx512-a0b1d64f78f) +post including scaling across cores. + +## Drop-In Replacement + +The following code snippet shows how you can use `github.com/minio/sha256-simd`. +This will automatically select the fastest method for the architecture on which +it will be executed. + +```go +import "github.com/minio/sha256-simd" + +func main() { + ... + shaWriter := sha256.New() + io.Copy(shaWriter, file) + ... +} +``` + +## Performance + +Below is the speed in MB/s for a single core (ranked fast to slow) for blocks +larger than 1 MB. + +| Processor | SIMD | Speed (MB/s) | +|-----------------------------------|---------|-------------:| +| 3.0 GHz Intel Xeon Platinum 8124M | AVX512 | 3498 | +| 3.7 GHz AMD Ryzen 7 2700X | SHA Ext | 1979 | +| 1.2 GHz ARM Cortex-A53 | ARM64 | 638 | + +## asm2plan9s + +In order to be able to work more easily with AVX512/AVX2 instructions, a +separate tool was developed to convert SIMD instructions into the corresponding +BYTE sequence as accepted by Go assembly. +See [asm2plan9s](https://github.com/minio/asm2plan9s) for more information. + +## Why and benefits + +One of the most performance sensitive parts of +the [Minio](https://github.com/minio/minio) object storage server is related to +SHA256 hash sums calculations. For instance during multi part uploads each part +that is uploaded needs to be verified for data integrity by the server. + +Other applications that can benefit from enhanced SHA256 performance are +deduplication in storage systems, intrusion detection, version control systems, +integrity checking, etc. + +## ARM SHA Extensions + +The 64-bit ARMv8 core has introduced new instructions for SHA1 and SHA2 +acceleration as part of +the [Cryptography Extensions](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0501f/CHDFJBCJ.html). +Below you can see a small excerpt highlighting one of the rounds as is done for +the SHA256 calculation process (for full code +see [sha256block_arm64.s](https://github.com/minio/sha256-simd/blob/master/sha256block_arm64.s)). + + ``` + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v5.4s, v6.4s + rev32 v8.16b, v8.16b + add v9.4s, v7.4s, v18.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v6.4s, v7.4s + sha256su1 v5.4s, v7.4s, v8.4s + ``` + +### Detailed benchmarks + +Benchmarks generated on a 1.2 Ghz Quad-Core ARM Cortex A53 +equipped [Pine64](https://www.pine64.com/). + +``` +minio@minio-arm:$ benchcmp golang.txt arm64.txt +benchmark golang arm64 speedup +BenchmarkHash8Bytes-4 0.68 MB/s 5.70 MB/s 8.38x +BenchmarkHash1K-4 5.65 MB/s 326.30 MB/s 57.75x +BenchmarkHash8K-4 6.00 MB/s 570.63 MB/s 95.11x +BenchmarkHash1M-4 6.05 MB/s 638.23 MB/s 105.49x +``` + +## License + +Released under the Apache License v2.0. You can find the complete text in the +file LICENSE. + +## Contributing + +Contributions are welcome, please send PRs for any enhancements. diff --git a/pkg/crypto/sha256/cpuid_other.go b/pkg/crypto/sha256/cpuid_other.go new file mode 100644 index 0000000..afe55c5 --- /dev/null +++ b/pkg/crypto/sha256/cpuid_other.go @@ -0,0 +1,55 @@ +// Minio Cloud Storage, (C) 2021 Minio, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package sha256 + +import ( + "bytes" + "github.com/klauspost/cpuid/v2" + "io/ioutil" + "runtime" +) + +var ( + hasIntelSha = runtime.GOARCH == "amd64" && cpuid.CPU.Supports( + cpuid.SHA, cpuid.SSSE3, + cpuid.SSE4, + ) + hasAvx512 = cpuid.CPU.Supports( + cpuid.AVX512F, cpuid.AVX512DQ, cpuid.AVX512BW, + cpuid.AVX512VL, + ) +) + +func hasArmSha2() bool { + if cpuid.CPU.Has(cpuid.SHA2) { + return true + } + if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" { + return false + } + + // Fall back to hacky cpuinfo parsing... + const procCPUInfo = "/proc/cpuinfo" + + // Feature to check for. + const sha256Feature = "sha2" + + cpuInfo, err := ioutil.ReadFile(procCPUInfo) + if err != nil { + return false + } + return bytes.Contains(cpuInfo, []byte(sha256Feature)) +} diff --git a/pkg/crypto/sha256/doc.go b/pkg/crypto/sha256/doc.go new file mode 100644 index 0000000..b00ce98 --- /dev/null +++ b/pkg/crypto/sha256/doc.go @@ -0,0 +1,6 @@ +// Package sha256 is taken from github.com/minio/sha256-simd, implementing, +// where available, an accelerated SIMD implementation of sha256. +// +// This package should be updated against the upstream version from time to +// time. +package sha256 diff --git a/pkg/crypto/sha256/sha256.go b/pkg/crypto/sha256/sha256.go new file mode 100644 index 0000000..bca4a61 --- /dev/null +++ b/pkg/crypto/sha256/sha256.go @@ -0,0 +1,470 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sha256 + +import ( + "crypto/sha256" + "encoding/binary" + "errors" + "hash" +) + +// Size - The size of a SHA256 checksum in bytes. +const Size = 32 + +// BlockSize - The blocksize of SHA256 in bytes. +const BlockSize = 64 + +const ( + chunk = BlockSize + init0 = 0x6A09E667 + init1 = 0xBB67AE85 + init2 = 0x3C6EF372 + init3 = 0xA54FF53A + init4 = 0x510E527F + init5 = 0x9B05688C + init6 = 0x1F83D9AB + init7 = 0x5BE0CD19 +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + h [8]uint32 + x [chunk]byte + nx int + len uint64 +} + +// Reset digest back to default +func (d *digest) Reset() { + d.h[0] = init0 + d.h[1] = init1 + d.h[2] = init2 + d.h[3] = init3 + d.h[4] = init4 + d.h[5] = init5 + d.h[6] = init6 + d.h[7] = init7 + d.nx = 0 + d.len = 0 +} + +type blockfuncType int + +const ( + blockfuncStdlib blockfuncType = iota + blockfuncIntelSha + blockfuncArmSha2 + blockfuncForceGeneric = -1 +) + +var blockfunc blockfuncType + +func init() { + switch { + case hasIntelSha: + blockfunc = blockfuncIntelSha + case hasArmSha2(): + blockfunc = blockfuncArmSha2 + } +} + +// New returns a new hash.Hash computing the SHA256 checksum. +func New() hash.Hash { + if blockfunc == blockfuncStdlib { + // Fallback to the standard golang implementation + // if no features were found. + return sha256.New() + } + + d := new(digest) + d.Reset() + return d +} + +// Sum256 - single caller sha256 helper +func Sum256(data []byte) (result [Size]byte) { + var d digest + d.Reset() + d.Write(data) + result = d.checkSum() + return +} + +// Return size of checksum +func (d *digest) Size() int { return Size } + +// Return blocksize of checksum +func (d *digest) BlockSize() int { return BlockSize } + +// Write to digest +func (d *digest) Write(p []byte) (nn int, err error) { + nn = len(p) + d.len += uint64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx:], p) + d.nx += n + if d.nx == chunk { + block(d, d.x[:]) + d.nx = 0 + } + p = p[n:] + } + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + block(d, p[:n]) + p = p[n:] + } + if len(p) > 0 { + d.nx = copy(d.x[:], p) + } + return +} + +// Return sha256 sum in bytes +func (d *digest) Sum(in []byte) []byte { + // Make a copy of d0 so that caller can keep writing and summing. + d0 := *d + hash := d0.checkSum() + return append(in, hash[:]...) +} + +// Intermediate checksum function +func (d *digest) checkSum() (digest [Size]byte) { + n := d.nx + + var k [64]byte + copy(k[:], d.x[:n]) + + k[n] = 0x80 + + if n >= 56 { + block(d, k[:]) + + // clear block buffer - go compiles this to optimal 1x xorps + 4x movups + // unfortunately expressing this more succinctly results in much worse code + k[0] = 0 + k[1] = 0 + k[2] = 0 + k[3] = 0 + k[4] = 0 + k[5] = 0 + k[6] = 0 + k[7] = 0 + k[8] = 0 + k[9] = 0 + k[10] = 0 + k[11] = 0 + k[12] = 0 + k[13] = 0 + k[14] = 0 + k[15] = 0 + k[16] = 0 + k[17] = 0 + k[18] = 0 + k[19] = 0 + k[20] = 0 + k[21] = 0 + k[22] = 0 + k[23] = 0 + k[24] = 0 + k[25] = 0 + k[26] = 0 + k[27] = 0 + k[28] = 0 + k[29] = 0 + k[30] = 0 + k[31] = 0 + k[32] = 0 + k[33] = 0 + k[34] = 0 + k[35] = 0 + k[36] = 0 + k[37] = 0 + k[38] = 0 + k[39] = 0 + k[40] = 0 + k[41] = 0 + k[42] = 0 + k[43] = 0 + k[44] = 0 + k[45] = 0 + k[46] = 0 + k[47] = 0 + k[48] = 0 + k[49] = 0 + k[50] = 0 + k[51] = 0 + k[52] = 0 + k[53] = 0 + k[54] = 0 + k[55] = 0 + k[56] = 0 + k[57] = 0 + k[58] = 0 + k[59] = 0 + k[60] = 0 + k[61] = 0 + k[62] = 0 + k[63] = 0 + } + binary.BigEndian.PutUint64(k[56:64], uint64(d.len)<<3) + block(d, k[:]) + + { + const i = 0 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + { + const i = 1 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + { + const i = 2 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + { + const i = 3 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + { + const i = 4 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + { + const i = 5 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + { + const i = 6 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + { + const i = 7 + binary.BigEndian.PutUint32(digest[i*4:i*4+4], d.h[i]) + } + + return +} + +func block(dig *digest, p []byte) { + if blockfunc == blockfuncIntelSha { + blockIntelShaGo(dig, p) + } else if blockfunc == blockfuncArmSha2 { + blockArmSha2Go(dig, p) + } else { + blockGeneric(dig, p) + } +} + +func blockGeneric(dig *digest, p []byte) { + var w [64]uint32 + h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] + for len(p) >= chunk { + // Can interlace the computation of w with the + // rounds below if needed for speed. + for i := 0; i < 16; i++ { + j := i * 4 + w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) + } + for i := 16; i < 64; i++ { + v1 := w[i-2] + t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10) + v2 := w[i-15] + t2 := (v2>>7 | v2<<(32-7)) ^ (v2>>18 | v2<<(32-18)) ^ (v2 >> 3) + w[i] = t1 + w[i-7] + t2 + w[i-16] + } + + a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7 + + for i := 0; i < 64; i++ { + t1 := h + ((e>>6 | e<<(32-6)) ^ (e>>11 | e<<(32-11)) ^ (e>>25 | e<<(32-25))) + ((e & f) ^ (^e & g)) + _K[i] + w[i] + + t2 := ((a>>2 | a<<(32-2)) ^ (a>>13 | a<<(32-13)) ^ (a>>22 | a<<(32-22))) + ((a & b) ^ (a & c) ^ (b & c)) + + h = g + g = f + f = e + e = d + t1 + d = c + c = b + b = a + a = t1 + t2 + } + + h0 += a + h1 += b + h2 += c + h3 += d + h4 += e + h5 += f + h6 += g + h7 += h + + p = p[chunk:] + } + + dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 +} + +var _K = []uint32{ + 0x428a2f98, + 0x71374491, + 0xb5c0fbcf, + 0xe9b5dba5, + 0x3956c25b, + 0x59f111f1, + 0x923f82a4, + 0xab1c5ed5, + 0xd807aa98, + 0x12835b01, + 0x243185be, + 0x550c7dc3, + 0x72be5d74, + 0x80deb1fe, + 0x9bdc06a7, + 0xc19bf174, + 0xe49b69c1, + 0xefbe4786, + 0x0fc19dc6, + 0x240ca1cc, + 0x2de92c6f, + 0x4a7484aa, + 0x5cb0a9dc, + 0x76f988da, + 0x983e5152, + 0xa831c66d, + 0xb00327c8, + 0xbf597fc7, + 0xc6e00bf3, + 0xd5a79147, + 0x06ca6351, + 0x14292967, + 0x27b70a85, + 0x2e1b2138, + 0x4d2c6dfc, + 0x53380d13, + 0x650a7354, + 0x766a0abb, + 0x81c2c92e, + 0x92722c85, + 0xa2bfe8a1, + 0xa81a664b, + 0xc24b8b70, + 0xc76c51a3, + 0xd192e819, + 0xd6990624, + 0xf40e3585, + 0x106aa070, + 0x19a4c116, + 0x1e376c08, + 0x2748774c, + 0x34b0bcb5, + 0x391c0cb3, + 0x4ed8aa4a, + 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, + 0x78a5636f, + 0x84c87814, + 0x8cc70208, + 0x90befffa, + 0xa4506ceb, + 0xbef9a3f7, + 0xc67178f2, +} + +const ( + magic256 = "sha\x03" + marshaledSize = len(magic256) + 8*4 + chunk + 8 +) + +func (d *digest) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, marshaledSize) + b = append(b, magic256...) + b = appendUint32(b, d.h[0]) + b = appendUint32(b, d.h[1]) + b = appendUint32(b, d.h[2]) + b = appendUint32(b, d.h[3]) + b = appendUint32(b, d.h[4]) + b = appendUint32(b, d.h[5]) + b = appendUint32(b, d.h[6]) + b = appendUint32(b, d.h[7]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-d.nx] // already zero + b = appendUint64(b, d.len) + return b, nil +} + +func (d *digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic256) || string(b[:len(magic256)]) != magic256 { + return errors.New("crypto/sha256: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("crypto/sha256: invalid hash state size") + } + b = b[len(magic256):] + b, d.h[0] = consumeUint32(b) + b, d.h[1] = consumeUint32(b) + b, d.h[2] = consumeUint32(b) + b, d.h[3] = consumeUint32(b) + b, d.h[4] = consumeUint32(b) + b, d.h[5] = consumeUint32(b) + b, d.h[6] = consumeUint32(b) + b, d.h[7] = consumeUint32(b) + b = b[copy(d.x[:], b):] + b, d.len = consumeUint64(b) + d.nx = int(d.len % chunk) + return nil +} + +func appendUint32(b []byte, v uint32) []byte { + return append( + b, + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} + +func appendUint64(b []byte, v uint64) []byte { + return append( + b, + byte(v>>56), + byte(v>>48), + byte(v>>40), + byte(v>>32), + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + _ = b[7] + x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 + return b[8:], x +} + +func consumeUint32(b []byte) ([]byte, uint32) { + _ = b[3] + x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 + return b[4:], x +} diff --git a/pkg/crypto/sha256/sha256_test.go b/pkg/crypto/sha256/sha256_test.go new file mode 100644 index 0000000..ac6cca8 --- /dev/null +++ b/pkg/crypto/sha256/sha256_test.go @@ -0,0 +1,2887 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Copyright (c) 2009 The Go Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Using this part of Minio codebase under the license +// Apache License Version 2.0 with modifications + +// SHA256 hash algorithm. See FIPS 180-2. + +package sha256 + +import ( + "encoding" + "encoding/hex" + "fmt" + "hash" + "io" + "strings" + "testing" + + "lol.mleku.dev/chk" + "next.orly.dev/pkg/utils" +) + +type sha256Test struct { + out [32]byte + in string +} + +var golden = []sha256Test{ + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + "", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + }, + "a", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + }, + "ab", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + }, + "abc", + }, + { + [32]byte{ + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + }, + "abcd", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + "abcde", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + "abcdef", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + }, + "abcdefg", + }, + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + }, + "abcdefgh", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + }, + "abcdefghi", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + }, + "abcdefghij", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "Discard medicine more than two years old.", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "He who has a shady past knows that nice guys finish last.", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + "I wouldn't marry him with a ten foot pole.", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", + }, + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + }, + "The days of the digital watch are numbered. -Tom Stoppard", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "Nepal premier won't resign.", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + }, + "For every action there is an equal and opposite government program.", + }, + { + [32]byte{ + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + "His money is twice tainted: 'taint yours and 'taint mine.", + }, + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "It's a tiny change to the code and not completely disgusting. - Bob Manchek", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + "size: a.out: bad magic", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + }, + "The major problem is with sendmail. -Mark Horton", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "Give me a rock, paper and scissors and I will move the world. CCFestoon", + }, + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "If the enemy is within range, then so are you.", + }, + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + "It's well we cannot hear the screams/That we create in others' dreams.", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + "You remind me of a TV show, but that's all right: I watch it anyway.", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "C is as portable as Stonehedge!!", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + }, + "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", + }, + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", + }, + { + [32]byte{ + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + }, + "How can you write a big system without C++? -Paul Glick", + }, + // $ echo -n "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123" | sha256sum + // 13d8b6bf5cc79c03c07c719c48597bd33b79677e65098589b1580fca7f22bb22 + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123", + }, + // $ echo -n "BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234" | sha256sum + // 624ddef3009879c6874da2dd771d54f7330781b60e1955ceff5f9dce8bf4ea43 + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + "BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234", + }, + // $ echo -n "CDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz12345" | sha256sum + // cc031589b70dd4b24dc6def2121835ef1aa8074ff6952cdd3f81b5099a93c58d + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + }, + "CDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz12345", + }, + // $ echo -n "DEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456" | sha256sum + // d354abb6d538402db3d73daf95537a255ebaf3a943c80205be163e044fc46a70 + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + }, + "DEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456", + }, + // $ echo -n "EFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567" | sha256sum + // f78410b90a20b521afb28f41d6388482afab7265ff8884aa6290cc9f9ada30d3 + { + [32]byte{ + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "EFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567", + }, + // $ echo -n "FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz12345678" | sha256sum + // c93a8cb7ed80166b15b79c8617410ca69e46fa1e3c1d14876699d3ce6090384f + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + "FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz12345678", + }, + // $ echo -n "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789" | sha256sum + // 6cb808e9a7fb53fa680824f08554b660d29a4afc9a101f990b4bae3a12b7fbd8 + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789", + }, + // $ echo -n "HIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890" | sha256sum + // 84e8dd1afa78db222860ed40b6fcfc7a269469365f81f5712fb589555bdb01fe + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + }, + "HIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", + }, + // $ echo -n "IJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890A" | sha256sum + // accab8e85b6bd178e975aaaa354aed8258bcd6af3e61bd4f12267635856cab0b + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + }, + "IJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890A", + }, + // $ echo -n "JKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890AB" | sha256sum + // 107f5ad8bc5d427246fc5f9c581134b61d8ba447e877df56cddad2bf53789172 + { + [32]byte{ + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + }, + "JKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890AB", + }, + // $ echo -n "KLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABC" | sha256sum + // 7666f65b234f78aa537c8d098b181091ce8b7866a0285b52e6bf31b6f21ca9bb + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + }, + "KLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABC", + }, + // $ echo -n "LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCD" | sha256sum + // 4eba948ccee7289ab1f01628a1ab756dee39a6894aed217edc9a91a8b35e50ca + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCD", + }, + // $ echo -n "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDE" | sha256sum + // 5011218873e7ca84871668d26461e449e7033b7959d69cfb5c2fee773c3d432d + { + [32]byte{ + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDE", + }, + // $ echo -n "NOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEF" | sha256sum + // 6932b4ddaf3696e5d5270739bdbe6ab120bb8034b877bd3a8e5a5d5ca263e1c5 + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + "NOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEF", + }, + // $ echo -n "OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEFG" | sha256sum + // 91bb1bcbfcb4c093aab255a0b8c8b5b93605e2f51dd6b0898b70b9f3c10fc1f9 + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + }, + "OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEFG", + }, + // $ echo -n "PQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGH" | sha256sum + // 0d1fa5355388e361c4591bd49c004e3d99044be274db43e91036611365aead02 + { + [32]byte{ + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + }, + "PQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGH", + }, + // $ echo -n "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | sha256sum + // b6ac3cc10386331c765f04f041c147d0f278f2aed8eaa021e2d0057fc6f6ff9e + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + }, + strings.Repeat("A", 128), + }, + // $ echo -n "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" | sha256sum + // 7abaa701a6f4bb8d9ea3872a315597eb6f2ccfd03392d8d10560837f6136d06a + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + }, + strings.Repeat("B", 128), + }, + // $ echo -n "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" | sha256sum + // 6e8b9325f779dba60c4c148dee5ded43b19ed20d25d66e338abec53b99174fe8 + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + }, + strings.Repeat("C", 128), + }, + // $ echo -n "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" | sha256sum + // 7aa020c91ac4d32e17efd9b64648b92e375987e0eae7d0a58544ca1e4fc32c3c + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + strings.Repeat("D", 128), + }, + // $ echo -n "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" | sha256sum + // 997f6a2fc44f1400e9f64d7eac11fe99e21f4b7a3fc2ff3ec95c2ef016abb9e5 + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + }, + strings.Repeat("E", 128), + }, + // $ echo -n "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" | sha256sum + // 5c6cdeb9ccaa1d9c57662605ab738ec4ecf0467f576d4c2d7fae48710215582a + { + [32]byte{ + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + }, + strings.Repeat("F", 128), + }, + // $ echo -n "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG" | sha256sum + // 394394b5f0e91a21d1e932f9ed55e098c8b05f3668f77134eeee843fef1d1758 + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + strings.Repeat("G", 128), + }, + // $ echo -n "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" | sha256sum + // cab546612de68eaa849487342baadbac2561df6380ddac66137ef649e0cdfd0a + { + [32]byte{ + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + }, + strings.Repeat("H", 128), + }, + // $ echo -n "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII" | sha256sum + // 2be96cc28445876429be3005db465d1b9c8ed1432e3ac6f1514b6e9eee725ad8 + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + strings.Repeat("I", 128), + }, + // $ echo -n "JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ" | sha256sum + // 238e5f81d54f2af58049b944c4a1b9516a36c2ef1e20887450b3482045714444 + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + strings.Repeat("J", 128), + }, + // $ echo -n "KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK" | sha256sum + // f3a5b826c64951661ce22dc67f0f79d13f633f0601aca2f5e1cf1a9f17dffd4f + { + [32]byte{ + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + strings.Repeat("K", 128), + }, + // $ echo -n "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" | sha256sum + // 1e90c05bedd24dc3e297d5b8fb215b95d8b7f4a040ee912069614c7a3382725d + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + strings.Repeat("L", 128), + }, + // $ echo -n "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" | sha256sum + // 96239ac6fb99822797308f18d8455778fb5885103aa5ff59afe2219df657df99 + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + }, + strings.Repeat("M", 128), + }, + // $ echo -n "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" | sha256sum + // 11e7f5a6f15a4addba9b6b21bc4f8ecbdd969e179335269fc68d3a05f0f3da4a + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + }, + strings.Repeat("N", 128), + }, + // $ echo -n "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" | sha256sum + // ae843b7e4e00afeb972bf948a345b319cca8bd0bcaa1428c1c67c88ea663c1e0 + { + [32]byte{ + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + }, + strings.Repeat("O", 128), + }, + // $ echo -n "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" | sha256sum + // f16ef3e254ffb74b7e3c97d99486ef8c549e4c80bc6dfed7fe8c5e7e76f4fbcd + { + [32]byte{ + (1 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (0 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (0 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (0 * 64) + (1 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (0 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (1 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (0 * 128), + (0 * 1) + (0 * 2) + (1 * 4) + (0 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (1 * 2) + (0 * 4) + (1 * 8) + (1 * 16) + (1 * 32) + (1 * 64) + (1 * 128), + (1 * 1) + (0 * 2) + (1 * 4) + (1 * 8) + (0 * 16) + (0 * 32) + (1 * 64) + (1 * 128), + }, + strings.Repeat("P", 128), + }, +} + +func TestGolden(t *testing.T) { + blockfuncSaved := blockfunc + + defer func() { + blockfunc = blockfuncSaved + }() + + if true { + blockfunc = blockfuncForceGeneric + for _, g := range golden { + s := fmt.Sprintf("%x", Sum256([]byte(g.in))) + if Sum256([]byte(g.in)) != g.out { + t.Fatalf( + "Generic: Sum256 function: sha256(%s) = %s want %s", g.in, + s, + hex.EncodeToString(g.out[:]), + ) + } + } + } + + if hasIntelSha { + blockfunc = blockfuncIntelSha + for _, g := range golden { + s := fmt.Sprintf("%x", Sum256([]byte(g.in))) + if Sum256([]byte(g.in)) != g.out { + t.Fatalf( + "SHA: Sum256 function: sha256(%s) = %s want %s", g.in, s, + hex.EncodeToString(g.out[:]), + ) + } + } + } + + if hasArmSha2() { + blockfunc = blockfuncArmSha2 + for _, g := range golden { + s := fmt.Sprintf("%x", Sum256([]byte(g.in))) + if Sum256([]byte(g.in)) != g.out { + t.Fatalf( + "ARM: Sum256 function: sha256(%s) = %s want %s", g.in, s, + hex.EncodeToString(g.out[:]), + ) + } + } + } +} + +func TestSize(t *testing.T) { + c := New() + if got := c.Size(); got != Size { + t.Errorf("Size = %d; want %d", got, Size) + } +} + +func TestBlockSize(t *testing.T) { + c := New() + if got := c.BlockSize(); got != BlockSize { + t.Errorf("BlockSize = %d want %d", got, BlockSize) + } +} + +func benchmarkSize(b *testing.B, size int) { + var bench = New() + var buf = make([]byte, size) + b.SetBytes(int64(size)) + sum := make([]byte, bench.Size()) + b.ResetTimer() + for i := 0; i < b.N; i++ { + bench.Reset() + bench.Write(buf) + bench.Sum(sum[:0]) + } +} + +func BenchmarkHash(b *testing.B) { + type alg struct { + n string + t blockfuncType + } + algos := make([]alg, 0, 2) + + algos = append(algos, alg{"Generic", blockfuncForceGeneric}) + if hasIntelSha { + algos = append(algos, alg{"IntelSHA", blockfuncIntelSha}) + } + if hasArmSha2() { + algos = append(algos, alg{"ArmSha2", blockfuncArmSha2}) + } + algos = append(algos, alg{"GoStdlib", blockfuncStdlib}) + + sizes := []struct { + n string + f func(*testing.B, int) + s int + }{ + {"8Bytes", benchmarkSize, 1 << 3}, + {"64Bytes", benchmarkSize, 1 << 6}, + {"1K", benchmarkSize, 1 << 10}, + {"8K", benchmarkSize, 1 << 13}, + {"1M", benchmarkSize, 1 << 20}, + {"5M", benchmarkSize, 5 << 20}, + {"10M", benchmarkSize, 5 << 21}, + } + + for _, a := range algos { + func() { + orig := blockfunc + defer func() { blockfunc = orig }() + + blockfunc = a.t + for _, y := range sizes { + s := a.n + "/" + y.n + b.Run(s, func(b *testing.B) { y.f(b, y.s) }) + } + }() + } +} + +type sha256TestGo struct { + out string + in string + halfState string // marshaled hash state after first half of in written, used by TestGoldenMarshal +} + +var golden256 = []sha256TestGo{ + { + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "", + "sha\x03j\t\xe6g\xbbg\xae\x85 0 { + t.Errorf("allocs = %d, want 0", n) + } +} diff --git a/pkg/crypto/sha256/sha256blockAvx512_amd64.asm b/pkg/crypto/sha256/sha256blockAvx512_amd64.asm new file mode 100644 index 0000000..c959b1a --- /dev/null +++ b/pkg/crypto/sha256/sha256blockAvx512_amd64.asm @@ -0,0 +1,686 @@ + +// 16x Parallel implementation of SHA256 for AVX512 + +// +// Minio Cloud Storage, (C) 2017 Minio, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// This code is based on the Intel Multi-Buffer Crypto for IPSec library +// and more specifically the following implementation: +// https://github.com/intel/intel-ipsec-mb/blob/master/avx512/sha256_x16_avx512.asm +// +// For Golang it has been converted into Plan 9 assembly with the help of +// github.com/minio/asm2plan9s to assemble the AVX512 instructions +// + +// Copyright (c) 2017, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#define SHA256_DIGEST_ROW_SIZE 64 + +// arg1 +#define STATE rdi +#define STATE_P9 DI +// arg2 +#define INP_SIZE rsi +#define INP_SIZE_P9 SI + +#define IDX rcx +#define TBL rdx +#define TBL_P9 DX + +#define INPUT rax +#define INPUT_P9 AX + +#define inp0 r9 +#define SCRATCH_P9 R12 +#define SCRATCH r12 +#define maskp r13 +#define MASKP_P9 R13 +#define mask r14 +#define MASK_P9 R14 + +#define A zmm0 +#define B zmm1 +#define C zmm2 +#define D zmm3 +#define E zmm4 +#define F zmm5 +#define G zmm6 +#define H zmm7 +#define T1 zmm8 +#define TMP0 zmm9 +#define TMP1 zmm10 +#define TMP2 zmm11 +#define TMP3 zmm12 +#define TMP4 zmm13 +#define TMP5 zmm14 +#define TMP6 zmm15 + +#define W0 zmm16 +#define W1 zmm17 +#define W2 zmm18 +#define W3 zmm19 +#define W4 zmm20 +#define W5 zmm21 +#define W6 zmm22 +#define W7 zmm23 +#define W8 zmm24 +#define W9 zmm25 +#define W10 zmm26 +#define W11 zmm27 +#define W12 zmm28 +#define W13 zmm29 +#define W14 zmm30 +#define W15 zmm31 + + +#define TRANSPOSE16(_r0, _r1, _r2, _r3, _r4, _r5, _r6, _r7, _r8, _r9, _r10, _r11, _r12, _r13, _r14, _r15, _t0, _t1) \ + \ + \ // input r0 = {a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1 a0} + \ // r1 = {b15 b14 b13 b12 b11 b10 b9 b8 b7 b6 b5 b4 b3 b2 b1 b0} + \ // r2 = {c15 c14 c13 c12 c11 c10 c9 c8 c7 c6 c5 c4 c3 c2 c1 c0} + \ // r3 = {d15 d14 d13 d12 d11 d10 d9 d8 d7 d6 d5 d4 d3 d2 d1 d0} + \ // r4 = {e15 e14 e13 e12 e11 e10 e9 e8 e7 e6 e5 e4 e3 e2 e1 e0} + \ // r5 = {f15 f14 f13 f12 f11 f10 f9 f8 f7 f6 f5 f4 f3 f2 f1 f0} + \ // r6 = {g15 g14 g13 g12 g11 g10 g9 g8 g7 g6 g5 g4 g3 g2 g1 g0} + \ // r7 = {h15 h14 h13 h12 h11 h10 h9 h8 h7 h6 h5 h4 h3 h2 h1 h0} + \ // r8 = {i15 i14 i13 i12 i11 i10 i9 i8 i7 i6 i5 i4 i3 i2 i1 i0} + \ // r9 = {j15 j14 j13 j12 j11 j10 j9 j8 j7 j6 j5 j4 j3 j2 j1 j0} + \ // r10 = {k15 k14 k13 k12 k11 k10 k9 k8 k7 k6 k5 k4 k3 k2 k1 k0} + \ // r11 = {l15 l14 l13 l12 l11 l10 l9 l8 l7 l6 l5 l4 l3 l2 l1 l0} + \ // r12 = {m15 m14 m13 m12 m11 m10 m9 m8 m7 m6 m5 m4 m3 m2 m1 m0} + \ // r13 = {n15 n14 n13 n12 n11 n10 n9 n8 n7 n6 n5 n4 n3 n2 n1 n0} + \ // r14 = {o15 o14 o13 o12 o11 o10 o9 o8 o7 o6 o5 o4 o3 o2 o1 o0} + \ // r15 = {p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0} + \ + \ // output r0 = { p0 o0 n0 m0 l0 k0 j0 i0 h0 g0 f0 e0 d0 c0 b0 a0} + \ // r1 = { p1 o1 n1 m1 l1 k1 j1 i1 h1 g1 f1 e1 d1 c1 b1 a1} + \ // r2 = { p2 o2 n2 m2 l2 k2 j2 i2 h2 g2 f2 e2 d2 c2 b2 a2} + \ // r3 = { p3 o3 n3 m3 l3 k3 j3 i3 h3 g3 f3 e3 d3 c3 b3 a3} + \ // r4 = { p4 o4 n4 m4 l4 k4 j4 i4 h4 g4 f4 e4 d4 c4 b4 a4} + \ // r5 = { p5 o5 n5 m5 l5 k5 j5 i5 h5 g5 f5 e5 d5 c5 b5 a5} + \ // r6 = { p6 o6 n6 m6 l6 k6 j6 i6 h6 g6 f6 e6 d6 c6 b6 a6} + \ // r7 = { p7 o7 n7 m7 l7 k7 j7 i7 h7 g7 f7 e7 d7 c7 b7 a7} + \ // r8 = { p8 o8 n8 m8 l8 k8 j8 i8 h8 g8 f8 e8 d8 c8 b8 a8} + \ // r9 = { p9 o9 n9 m9 l9 k9 j9 i9 h9 g9 f9 e9 d9 c9 b9 a9} + \ // r10 = {p10 o10 n10 m10 l10 k10 j10 i10 h10 g10 f10 e10 d10 c10 b10 a10} + \ // r11 = {p11 o11 n11 m11 l11 k11 j11 i11 h11 g11 f11 e11 d11 c11 b11 a11} + \ // r12 = {p12 o12 n12 m12 l12 k12 j12 i12 h12 g12 f12 e12 d12 c12 b12 a12} + \ // r13 = {p13 o13 n13 m13 l13 k13 j13 i13 h13 g13 f13 e13 d13 c13 b13 a13} + \ // r14 = {p14 o14 n14 m14 l14 k14 j14 i14 h14 g14 f14 e14 d14 c14 b14 a14} + \ // r15 = {p15 o15 n15 m15 l15 k15 j15 i15 h15 g15 f15 e15 d15 c15 b15 a15} + \ + \ // process top half + vshufps _t0, _r0, _r1, 0x44 \ // t0 = {b13 b12 a13 a12 b9 b8 a9 a8 b5 b4 a5 a4 b1 b0 a1 a0} + vshufps _r0, _r0, _r1, 0xEE \ // r0 = {b15 b14 a15 a14 b11 b10 a11 a10 b7 b6 a7 a6 b3 b2 a3 a2} + vshufps _t1, _r2, _r3, 0x44 \ // t1 = {d13 d12 c13 c12 d9 d8 c9 c8 d5 d4 c5 c4 d1 d0 c1 c0} + vshufps _r2, _r2, _r3, 0xEE \ // r2 = {d15 d14 c15 c14 d11 d10 c11 c10 d7 d6 c7 c6 d3 d2 c3 c2} + \ + vshufps _r3, _t0, _t1, 0xDD \ // r3 = {d13 c13 b13 a13 d9 c9 b9 a9 d5 c5 b5 a5 d1 c1 b1 a1} + vshufps _r1, _r0, _r2, 0x88 \ // r1 = {d14 c14 b14 a14 d10 c10 b10 a10 d6 c6 b6 a6 d2 c2 b2 a2} + vshufps _r0, _r0, _r2, 0xDD \ // r0 = {d15 c15 b15 a15 d11 c11 b11 a11 d7 c7 b7 a7 d3 c3 b3 a3} + vshufps _t0, _t0, _t1, 0x88 \ // t0 = {d12 c12 b12 a12 d8 c8 b8 a8 d4 c4 b4 a4 d0 c0 b0 a0} + \ + \ // use r2 in place of t0 + vshufps _r2, _r4, _r5, 0x44 \ // r2 = {f13 f12 e13 e12 f9 f8 e9 e8 f5 f4 e5 e4 f1 f0 e1 e0} + vshufps _r4, _r4, _r5, 0xEE \ // r4 = {f15 f14 e15 e14 f11 f10 e11 e10 f7 f6 e7 e6 f3 f2 e3 e2} + vshufps _t1, _r6, _r7, 0x44 \ // t1 = {h13 h12 g13 g12 h9 h8 g9 g8 h5 h4 g5 g4 h1 h0 g1 g0} + vshufps _r6, _r6, _r7, 0xEE \ // r6 = {h15 h14 g15 g14 h11 h10 g11 g10 h7 h6 g7 g6 h3 h2 g3 g2} + \ + vshufps _r7, _r2, _t1, 0xDD \ // r7 = {h13 g13 f13 e13 h9 g9 f9 e9 h5 g5 f5 e5 h1 g1 f1 e1} + vshufps _r5, _r4, _r6, 0x88 \ // r5 = {h14 g14 f14 e14 h10 g10 f10 e10 h6 g6 f6 e6 h2 g2 f2 e2} + vshufps _r4, _r4, _r6, 0xDD \ // r4 = {h15 g15 f15 e15 h11 g11 f11 e11 h7 g7 f7 e7 h3 g3 f3 e3} + vshufps _r2, _r2, _t1, 0x88 \ // r2 = {h12 g12 f12 e12 h8 g8 f8 e8 h4 g4 f4 e4 h0 g0 f0 e0} + \ + \ // use r6 in place of t0 + vshufps _r6, _r8, _r9, 0x44 \ // r6 = {j13 j12 i13 i12 j9 j8 i9 i8 j5 j4 i5 i4 j1 j0 i1 i0} + vshufps _r8, _r8, _r9, 0xEE \ // r8 = {j15 j14 i15 i14 j11 j10 i11 i10 j7 j6 i7 i6 j3 j2 i3 i2} + vshufps _t1, _r10, _r11, 0x44 \ // t1 = {l13 l12 k13 k12 l9 l8 k9 k8 l5 l4 k5 k4 l1 l0 k1 k0} + vshufps _r10, _r10, _r11, 0xEE \ // r10 = {l15 l14 k15 k14 l11 l10 k11 k10 l7 l6 k7 k6 l3 l2 k3 k2} + \ + vshufps _r11, _r6, _t1, 0xDD \ // r11 = {l13 k13 j13 113 l9 k9 j9 i9 l5 k5 j5 i5 l1 k1 j1 i1} + vshufps _r9, _r8, _r10, 0x88 \ // r9 = {l14 k14 j14 114 l10 k10 j10 i10 l6 k6 j6 i6 l2 k2 j2 i2} + vshufps _r8, _r8, _r10, 0xDD \ // r8 = {l15 k15 j15 115 l11 k11 j11 i11 l7 k7 j7 i7 l3 k3 j3 i3} + vshufps _r6, _r6, _t1, 0x88 \ // r6 = {l12 k12 j12 112 l8 k8 j8 i8 l4 k4 j4 i4 l0 k0 j0 i0} + \ + \ // use r10 in place of t0 + vshufps _r10, _r12, _r13, 0x44 \ // r10 = {n13 n12 m13 m12 n9 n8 m9 m8 n5 n4 m5 m4 n1 n0 a1 m0} + vshufps _r12, _r12, _r13, 0xEE \ // r12 = {n15 n14 m15 m14 n11 n10 m11 m10 n7 n6 m7 m6 n3 n2 a3 m2} + vshufps _t1, _r14, _r15, 0x44 \ // t1 = {p13 p12 013 012 p9 p8 09 08 p5 p4 05 04 p1 p0 01 00} + vshufps _r14, _r14, _r15, 0xEE \ // r14 = {p15 p14 015 014 p11 p10 011 010 p7 p6 07 06 p3 p2 03 02} + \ + vshufps _r15, _r10, _t1, 0xDD \ // r15 = {p13 013 n13 m13 p9 09 n9 m9 p5 05 n5 m5 p1 01 n1 m1} + vshufps _r13, _r12, _r14, 0x88 \ // r13 = {p14 014 n14 m14 p10 010 n10 m10 p6 06 n6 m6 p2 02 n2 m2} + vshufps _r12, _r12, _r14, 0xDD \ // r12 = {p15 015 n15 m15 p11 011 n11 m11 p7 07 n7 m7 p3 03 n3 m3} + vshufps _r10, _r10, _t1, 0x88 \ // r10 = {p12 012 n12 m12 p8 08 n8 m8 p4 04 n4 m4 p0 00 n0 m0} + \ + \ // At this point, the registers that contain interesting data are: + \ // t0, r3, r1, r0, r2, r7, r5, r4, r6, r11, r9, r8, r10, r15, r13, r12 + \ // Can use t1 and r14 as scratch registers + LEAQ PSHUFFLE_TRANSPOSE16_MASK1<>(SB), BX \ + LEAQ PSHUFFLE_TRANSPOSE16_MASK2<>(SB), R8 \ + \ + vmovdqu32 _r14, [rbx] \ + vpermi2q _r14, _t0, _r2 \ // r14 = {h8 g8 f8 e8 d8 c8 b8 a8 h0 g0 f0 e0 d0 c0 b0 a0} + vmovdqu32 _t1, [r8] \ + vpermi2q _t1, _t0, _r2 \ // t1 = {h12 g12 f12 e12 d12 c12 b12 a12 h4 g4 f4 e4 d4 c4 b4 a4} + \ + vmovdqu32 _r2, [rbx] \ + vpermi2q _r2, _r3, _r7 \ // r2 = {h9 g9 f9 e9 d9 c9 b9 a9 h1 g1 f1 e1 d1 c1 b1 a1} + vmovdqu32 _t0, [r8] \ + vpermi2q _t0, _r3, _r7 \ // t0 = {h13 g13 f13 e13 d13 c13 b13 a13 h5 g5 f5 e5 d5 c5 b5 a5} + \ + vmovdqu32 _r3, [rbx] \ + vpermi2q _r3, _r1, _r5 \ // r3 = {h10 g10 f10 e10 d10 c10 b10 a10 h2 g2 f2 e2 d2 c2 b2 a2} + vmovdqu32 _r7, [r8] \ + vpermi2q _r7, _r1, _r5 \ // r7 = {h14 g14 f14 e14 d14 c14 b14 a14 h6 g6 f6 e6 d6 c6 b6 a6} + \ + vmovdqu32 _r1, [rbx] \ + vpermi2q _r1, _r0, _r4 \ // r1 = {h11 g11 f11 e11 d11 c11 b11 a11 h3 g3 f3 e3 d3 c3 b3 a3} + vmovdqu32 _r5, [r8] \ + vpermi2q _r5, _r0, _r4 \ // r5 = {h15 g15 f15 e15 d15 c15 b15 a15 h7 g7 f7 e7 d7 c7 b7 a7} + \ + vmovdqu32 _r0, [rbx] \ + vpermi2q _r0, _r6, _r10 \ // r0 = {p8 o8 n8 m8 l8 k8 j8 i8 p0 o0 n0 m0 l0 k0 j0 i0} + vmovdqu32 _r4, [r8] \ + vpermi2q _r4, _r6, _r10 \ // r4 = {p12 o12 n12 m12 l12 k12 j12 i12 p4 o4 n4 m4 l4 k4 j4 i4} + \ + vmovdqu32 _r6, [rbx] \ + vpermi2q _r6, _r11, _r15 \ // r6 = {p9 o9 n9 m9 l9 k9 j9 i9 p1 o1 n1 m1 l1 k1 j1 i1} + vmovdqu32 _r10, [r8] \ + vpermi2q _r10, _r11, _r15 \ // r10 = {p13 o13 n13 m13 l13 k13 j13 i13 p5 o5 n5 m5 l5 k5 j5 i5} + \ + vmovdqu32 _r11, [rbx] \ + vpermi2q _r11, _r9, _r13 \ // r11 = {p10 o10 n10 m10 l10 k10 j10 i10 p2 o2 n2 m2 l2 k2 j2 i2} + vmovdqu32 _r15, [r8] \ + vpermi2q _r15, _r9, _r13 \ // r15 = {p14 o14 n14 m14 l14 k14 j14 i14 p6 o6 n6 m6 l6 k6 j6 i6} + \ + vmovdqu32 _r9, [rbx] \ + vpermi2q _r9, _r8, _r12 \ // r9 = {p11 o11 n11 m11 l11 k11 j11 i11 p3 o3 n3 m3 l3 k3 j3 i3} + vmovdqu32 _r13, [r8] \ + vpermi2q _r13, _r8, _r12 \ // r13 = {p15 o15 n15 m15 l15 k15 j15 i15 p7 o7 n7 m7 l7 k7 j7 i7} + \ + \ // At this point r8 and r12 can be used as scratch registers + vshuff64x2 _r8, _r14, _r0, 0xEE \ // r8 = {p8 o8 n8 m8 l8 k8 j8 i8 h8 g8 f8 e8 d8 c8 b8 a8} + vshuff64x2 _r0, _r14, _r0, 0x44 \ // r0 = {p0 o0 n0 m0 l0 k0 j0 i0 h0 g0 f0 e0 d0 c0 b0 a0} + \ + vshuff64x2 _r12, _t1, _r4, 0xEE \ // r12 = {p12 o12 n12 m12 l12 k12 j12 i12 h12 g12 f12 e12 d12 c12 b12 a12} + vshuff64x2 _r4, _t1, _r4, 0x44 \ // r4 = {p4 o4 n4 m4 l4 k4 j4 i4 h4 g4 f4 e4 d4 c4 b4 a4} + \ + vshuff64x2 _r14, _r7, _r15, 0xEE \ // r14 = {p14 o14 n14 m14 l14 k14 j14 i14 h14 g14 f14 e14 d14 c14 b14 a14} + vshuff64x2 _t1, _r7, _r15, 0x44 \ // t1 = {p6 o6 n6 m6 l6 k6 j6 i6 h6 g6 f6 e6 d6 c6 b6 a6} + \ + vshuff64x2 _r15, _r5, _r13, 0xEE \ // r15 = {p15 o15 n15 m15 l15 k15 j15 i15 h15 g15 f15 e15 d15 c15 b15 a15} + vshuff64x2 _r7, _r5, _r13, 0x44 \ // r7 = {p7 o7 n7 m7 l7 k7 j7 i7 h7 g7 f7 e7 d7 c7 b7 a7} + \ + vshuff64x2 _r13, _t0, _r10, 0xEE \ // r13 = {p13 o13 n13 m13 l13 k13 j13 i13 h13 g13 f13 e13 d13 c13 b13 a13} + vshuff64x2 _r5, _t0, _r10, 0x44 \ // r5 = {p5 o5 n5 m5 l5 k5 j5 i5 h5 g5 f5 e5 d5 c5 b5 a5} + \ + vshuff64x2 _r10, _r3, _r11, 0xEE \ // r10 = {p10 o10 n10 m10 l10 k10 j10 i10 h10 g10 f10 e10 d10 c10 b10 a10} + vshuff64x2 _t0, _r3, _r11, 0x44 \ // t0 = {p2 o2 n2 m2 l2 k2 j2 i2 h2 g2 f2 e2 d2 c2 b2 a2} + \ + vshuff64x2 _r11, _r1, _r9, 0xEE \ // r11 = {p11 o11 n11 m11 l11 k11 j11 i11 h11 g11 f11 e11 d11 c11 b11 a11} + vshuff64x2 _r3, _r1, _r9, 0x44 \ // r3 = {p3 o3 n3 m3 l3 k3 j3 i3 h3 g3 f3 e3 d3 c3 b3 a3} + \ + vshuff64x2 _r9, _r2, _r6, 0xEE \ // r9 = {p9 o9 n9 m9 l9 k9 j9 i9 h9 g9 f9 e9 d9 c9 b9 a9} + vshuff64x2 _r1, _r2, _r6, 0x44 \ // r1 = {p1 o1 n1 m1 l1 k1 j1 i1 h1 g1 f1 e1 d1 c1 b1 a1} + \ + vmovdqu32 _r2, _t0 \ // r2 = {p2 o2 n2 m2 l2 k2 j2 i2 h2 g2 f2 e2 d2 c2 b2 a2} + vmovdqu32 _r6, _t1 \ // r6 = {p6 o6 n6 m6 l6 k6 j6 i6 h6 g6 f6 e6 d6 c6 b6 a6} + + +// CH(A, B, C) = (A&B) ^ (~A&C) +// MAJ(E, F, G) = (E&F) ^ (E&G) ^ (F&G) +// SIGMA0 = ROR_2 ^ ROR_13 ^ ROR_22 +// SIGMA1 = ROR_6 ^ ROR_11 ^ ROR_25 +// sigma0 = ROR_7 ^ ROR_18 ^ SHR_3 +// sigma1 = ROR_17 ^ ROR_19 ^ SHR_10 + +// Main processing loop per round +#define PROCESS_LOOP(_WT, _ROUND, _A, _B, _C, _D, _E, _F, _G, _H) \ + \ // T1 = H + SIGMA1(E) + CH(E, F, G) + Kt + Wt + \ // T2 = SIGMA0(A) + MAJ(A, B, C) + \ // H=G, G=F, F=E, E=D+T1, D=C, C=B, B=A, A=T1+T2 + \ + \ // H becomes T2, then add T1 for A + \ // D becomes D + T1 for E + \ + vpaddd T1, _H, TMP3 \ // T1 = H + Kt + vmovdqu32 TMP0, _E \ + vprord TMP1, _E, 6 \ // ROR_6(E) + vprord TMP2, _E, 11 \ // ROR_11(E) + vprord TMP3, _E, 25 \ // ROR_25(E) + vpternlogd TMP0, _F, _G, 0xCA \ // TMP0 = CH(E,F,G) + vpaddd T1, T1, _WT \ // T1 = T1 + Wt + vpternlogd TMP1, TMP2, TMP3, 0x96 \ // TMP1 = SIGMA1(E) + vpaddd T1, T1, TMP0 \ // T1 = T1 + CH(E,F,G) + vpaddd T1, T1, TMP1 \ // T1 = T1 + SIGMA1(E) + vpaddd _D, _D, T1 \ // D = D + T1 + \ + vprord _H, _A, 2 \ // ROR_2(A) + vprord TMP2, _A, 13 \ // ROR_13(A) + vprord TMP3, _A, 22 \ // ROR_22(A) + vmovdqu32 TMP0, _A \ + vpternlogd TMP0, _B, _C, 0xE8 \ // TMP0 = MAJ(A,B,C) + vpternlogd _H, TMP2, TMP3, 0x96 \ // H(T2) = SIGMA0(A) + vpaddd _H, _H, TMP0 \ // H(T2) = SIGMA0(A) + MAJ(A,B,C) + vpaddd _H, _H, T1 \ // H(A) = H(T2) + T1 + \ + vmovdqu32 TMP3, [TBL + ((_ROUND+1)*64)] \ // Next Kt + + +#define MSG_SCHED_ROUND_16_63(_WT, _WTp1, _WTp9, _WTp14) \ + vprord TMP4, _WTp14, 17 \ // ROR_17(Wt-2) + vprord TMP5, _WTp14, 19 \ // ROR_19(Wt-2) + vpsrld TMP6, _WTp14, 10 \ // SHR_10(Wt-2) + vpternlogd TMP4, TMP5, TMP6, 0x96 \ // TMP4 = sigma1(Wt-2) + \ + vpaddd _WT, _WT, TMP4 \ // Wt = Wt-16 + sigma1(Wt-2) + vpaddd _WT, _WT, _WTp9 \ // Wt = Wt-16 + sigma1(Wt-2) + Wt-7 + \ + vprord TMP4, _WTp1, 7 \ // ROR_7(Wt-15) + vprord TMP5, _WTp1, 18 \ // ROR_18(Wt-15) + vpsrld TMP6, _WTp1, 3 \ // SHR_3(Wt-15) + vpternlogd TMP4, TMP5, TMP6, 0x96 \ // TMP4 = sigma0(Wt-15) + \ + vpaddd _WT, _WT, TMP4 \ // Wt = Wt-16 + sigma1(Wt-2) + + \ // Wt-7 + sigma0(Wt-15) + + + +// Note this is reading in a block of data for one lane +// When all 16 are read, the data must be transposed to build msg schedule +#define MSG_SCHED_ROUND_00_15(_WT, OFFSET, LABEL) \ + TESTQ $(1<(SB), TBL_P9 + vmovdqu32 TMP2, [TBL] + + // Get first K from table + MOVQ table+16(FP), TBL_P9 + vmovdqu32 TMP3, [TBL] + + // Save digests for later addition + vmovdqu32 [SCRATCH + 64*0], A + vmovdqu32 [SCRATCH + 64*1], B + vmovdqu32 [SCRATCH + 64*2], C + vmovdqu32 [SCRATCH + 64*3], D + vmovdqu32 [SCRATCH + 64*4], E + vmovdqu32 [SCRATCH + 64*5], F + vmovdqu32 [SCRATCH + 64*6], G + vmovdqu32 [SCRATCH + 64*7], H + + add IDX, 64 + + // Transpose input data + TRANSPOSE16(W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15, TMP0, TMP1) + + vpshufb W0, W0, TMP2 + vpshufb W1, W1, TMP2 + vpshufb W2, W2, TMP2 + vpshufb W3, W3, TMP2 + vpshufb W4, W4, TMP2 + vpshufb W5, W5, TMP2 + vpshufb W6, W6, TMP2 + vpshufb W7, W7, TMP2 + vpshufb W8, W8, TMP2 + vpshufb W9, W9, TMP2 + vpshufb W10, W10, TMP2 + vpshufb W11, W11, TMP2 + vpshufb W12, W12, TMP2 + vpshufb W13, W13, TMP2 + vpshufb W14, W14, TMP2 + vpshufb W15, W15, TMP2 + + // MSG Schedule for W0-W15 is now complete in registers + // Process first 48 rounds + // Calculate next Wt+16 after processing is complete and Wt is unneeded + + PROCESS_LOOP( W0, 0, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_16_63( W0, W1, W9, W14) + PROCESS_LOOP( W1, 1, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_16_63( W1, W2, W10, W15) + PROCESS_LOOP( W2, 2, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_16_63( W2, W3, W11, W0) + PROCESS_LOOP( W3, 3, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_16_63( W3, W4, W12, W1) + PROCESS_LOOP( W4, 4, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_16_63( W4, W5, W13, W2) + PROCESS_LOOP( W5, 5, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_16_63( W5, W6, W14, W3) + PROCESS_LOOP( W6, 6, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_16_63( W6, W7, W15, W4) + PROCESS_LOOP( W7, 7, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_16_63( W7, W8, W0, W5) + PROCESS_LOOP( W8, 8, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_16_63( W8, W9, W1, W6) + PROCESS_LOOP( W9, 9, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_16_63( W9, W10, W2, W7) + PROCESS_LOOP(W10, 10, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_16_63(W10, W11, W3, W8) + PROCESS_LOOP(W11, 11, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_16_63(W11, W12, W4, W9) + PROCESS_LOOP(W12, 12, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_16_63(W12, W13, W5, W10) + PROCESS_LOOP(W13, 13, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_16_63(W13, W14, W6, W11) + PROCESS_LOOP(W14, 14, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_16_63(W14, W15, W7, W12) + PROCESS_LOOP(W15, 15, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_16_63(W15, W0, W8, W13) + PROCESS_LOOP( W0, 16, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_16_63( W0, W1, W9, W14) + PROCESS_LOOP( W1, 17, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_16_63( W1, W2, W10, W15) + PROCESS_LOOP( W2, 18, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_16_63( W2, W3, W11, W0) + PROCESS_LOOP( W3, 19, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_16_63( W3, W4, W12, W1) + PROCESS_LOOP( W4, 20, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_16_63( W4, W5, W13, W2) + PROCESS_LOOP( W5, 21, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_16_63( W5, W6, W14, W3) + PROCESS_LOOP( W6, 22, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_16_63( W6, W7, W15, W4) + PROCESS_LOOP( W7, 23, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_16_63( W7, W8, W0, W5) + PROCESS_LOOP( W8, 24, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_16_63( W8, W9, W1, W6) + PROCESS_LOOP( W9, 25, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_16_63( W9, W10, W2, W7) + PROCESS_LOOP(W10, 26, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_16_63(W10, W11, W3, W8) + PROCESS_LOOP(W11, 27, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_16_63(W11, W12, W4, W9) + PROCESS_LOOP(W12, 28, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_16_63(W12, W13, W5, W10) + PROCESS_LOOP(W13, 29, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_16_63(W13, W14, W6, W11) + PROCESS_LOOP(W14, 30, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_16_63(W14, W15, W7, W12) + PROCESS_LOOP(W15, 31, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_16_63(W15, W0, W8, W13) + PROCESS_LOOP( W0, 32, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_16_63( W0, W1, W9, W14) + PROCESS_LOOP( W1, 33, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_16_63( W1, W2, W10, W15) + PROCESS_LOOP( W2, 34, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_16_63( W2, W3, W11, W0) + PROCESS_LOOP( W3, 35, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_16_63( W3, W4, W12, W1) + PROCESS_LOOP( W4, 36, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_16_63( W4, W5, W13, W2) + PROCESS_LOOP( W5, 37, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_16_63( W5, W6, W14, W3) + PROCESS_LOOP( W6, 38, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_16_63( W6, W7, W15, W4) + PROCESS_LOOP( W7, 39, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_16_63( W7, W8, W0, W5) + PROCESS_LOOP( W8, 40, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_16_63( W8, W9, W1, W6) + PROCESS_LOOP( W9, 41, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_16_63( W9, W10, W2, W7) + PROCESS_LOOP(W10, 42, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_16_63(W10, W11, W3, W8) + PROCESS_LOOP(W11, 43, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_16_63(W11, W12, W4, W9) + PROCESS_LOOP(W12, 44, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_16_63(W12, W13, W5, W10) + PROCESS_LOOP(W13, 45, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_16_63(W13, W14, W6, W11) + PROCESS_LOOP(W14, 46, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_16_63(W14, W15, W7, W12) + PROCESS_LOOP(W15, 47, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_16_63(W15, W0, W8, W13) + + // Check if this is the last block + sub INP_SIZE, 1 + JE lastLoop + + // Load next mask for inputs + ADDQ $8, MASKP_P9 + MOVQ (MASKP_P9), MASK_P9 + + // Process last 16 rounds + // Read in next block msg data for use in first 16 words of msg sched + + PROCESS_LOOP( W0, 48, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_00_15( W0, 0, skipNext0) + PROCESS_LOOP( W1, 49, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_00_15( W1, 1, skipNext1) + PROCESS_LOOP( W2, 50, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_00_15( W2, 2, skipNext2) + PROCESS_LOOP( W3, 51, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_00_15( W3, 3, skipNext3) + PROCESS_LOOP( W4, 52, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_00_15( W4, 4, skipNext4) + PROCESS_LOOP( W5, 53, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_00_15( W5, 5, skipNext5) + PROCESS_LOOP( W6, 54, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_00_15( W6, 6, skipNext6) + PROCESS_LOOP( W7, 55, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_00_15( W7, 7, skipNext7) + PROCESS_LOOP( W8, 56, A, B, C, D, E, F, G, H) + MSG_SCHED_ROUND_00_15( W8, 8, skipNext8) + PROCESS_LOOP( W9, 57, H, A, B, C, D, E, F, G) + MSG_SCHED_ROUND_00_15( W9, 9, skipNext9) + PROCESS_LOOP(W10, 58, G, H, A, B, C, D, E, F) + MSG_SCHED_ROUND_00_15(W10, 10, skipNext10) + PROCESS_LOOP(W11, 59, F, G, H, A, B, C, D, E) + MSG_SCHED_ROUND_00_15(W11, 11, skipNext11) + PROCESS_LOOP(W12, 60, E, F, G, H, A, B, C, D) + MSG_SCHED_ROUND_00_15(W12, 12, skipNext12) + PROCESS_LOOP(W13, 61, D, E, F, G, H, A, B, C) + MSG_SCHED_ROUND_00_15(W13, 13, skipNext13) + PROCESS_LOOP(W14, 62, C, D, E, F, G, H, A, B) + MSG_SCHED_ROUND_00_15(W14, 14, skipNext14) + PROCESS_LOOP(W15, 63, B, C, D, E, F, G, H, A) + MSG_SCHED_ROUND_00_15(W15, 15, skipNext15) + + // Add old digest + vmovdqu32 TMP2, A + vmovdqu32 A, [SCRATCH + 64*0] + vpaddd A{k1}, A, TMP2 + vmovdqu32 TMP2, B + vmovdqu32 B, [SCRATCH + 64*1] + vpaddd B{k1}, B, TMP2 + vmovdqu32 TMP2, C + vmovdqu32 C, [SCRATCH + 64*2] + vpaddd C{k1}, C, TMP2 + vmovdqu32 TMP2, D + vmovdqu32 D, [SCRATCH + 64*3] + vpaddd D{k1}, D, TMP2 + vmovdqu32 TMP2, E + vmovdqu32 E, [SCRATCH + 64*4] + vpaddd E{k1}, E, TMP2 + vmovdqu32 TMP2, F + vmovdqu32 F, [SCRATCH + 64*5] + vpaddd F{k1}, F, TMP2 + vmovdqu32 TMP2, G + vmovdqu32 G, [SCRATCH + 64*6] + vpaddd G{k1}, G, TMP2 + vmovdqu32 TMP2, H + vmovdqu32 H, [SCRATCH + 64*7] + vpaddd H{k1}, H, TMP2 + + kmovq k1, mask + JMP lloop + +lastLoop: + // Process last 16 rounds + PROCESS_LOOP( W0, 48, A, B, C, D, E, F, G, H) + PROCESS_LOOP( W1, 49, H, A, B, C, D, E, F, G) + PROCESS_LOOP( W2, 50, G, H, A, B, C, D, E, F) + PROCESS_LOOP( W3, 51, F, G, H, A, B, C, D, E) + PROCESS_LOOP( W4, 52, E, F, G, H, A, B, C, D) + PROCESS_LOOP( W5, 53, D, E, F, G, H, A, B, C) + PROCESS_LOOP( W6, 54, C, D, E, F, G, H, A, B) + PROCESS_LOOP( W7, 55, B, C, D, E, F, G, H, A) + PROCESS_LOOP( W8, 56, A, B, C, D, E, F, G, H) + PROCESS_LOOP( W9, 57, H, A, B, C, D, E, F, G) + PROCESS_LOOP(W10, 58, G, H, A, B, C, D, E, F) + PROCESS_LOOP(W11, 59, F, G, H, A, B, C, D, E) + PROCESS_LOOP(W12, 60, E, F, G, H, A, B, C, D) + PROCESS_LOOP(W13, 61, D, E, F, G, H, A, B, C) + PROCESS_LOOP(W14, 62, C, D, E, F, G, H, A, B) + PROCESS_LOOP(W15, 63, B, C, D, E, F, G, H, A) + + // Add old digest + vmovdqu32 TMP2, A + vmovdqu32 A, [SCRATCH + 64*0] + vpaddd A{k1}, A, TMP2 + vmovdqu32 TMP2, B + vmovdqu32 B, [SCRATCH + 64*1] + vpaddd B{k1}, B, TMP2 + vmovdqu32 TMP2, C + vmovdqu32 C, [SCRATCH + 64*2] + vpaddd C{k1}, C, TMP2 + vmovdqu32 TMP2, D + vmovdqu32 D, [SCRATCH + 64*3] + vpaddd D{k1}, D, TMP2 + vmovdqu32 TMP2, E + vmovdqu32 E, [SCRATCH + 64*4] + vpaddd E{k1}, E, TMP2 + vmovdqu32 TMP2, F + vmovdqu32 F, [SCRATCH + 64*5] + vpaddd F{k1}, F, TMP2 + vmovdqu32 TMP2, G + vmovdqu32 G, [SCRATCH + 64*6] + vpaddd G{k1}, G, TMP2 + vmovdqu32 TMP2, H + vmovdqu32 H, [SCRATCH + 64*7] + vpaddd H{k1}, H, TMP2 + + // Write out digest + vmovdqu32 [STATE + 0*SHA256_DIGEST_ROW_SIZE], A + vmovdqu32 [STATE + 1*SHA256_DIGEST_ROW_SIZE], B + vmovdqu32 [STATE + 2*SHA256_DIGEST_ROW_SIZE], C + vmovdqu32 [STATE + 3*SHA256_DIGEST_ROW_SIZE], D + vmovdqu32 [STATE + 4*SHA256_DIGEST_ROW_SIZE], E + vmovdqu32 [STATE + 5*SHA256_DIGEST_ROW_SIZE], F + vmovdqu32 [STATE + 6*SHA256_DIGEST_ROW_SIZE], G + vmovdqu32 [STATE + 7*SHA256_DIGEST_ROW_SIZE], H + + VZEROUPPER + RET + +// +// Tables +// + +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x000(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x008(SB)/8, $0x0c0d0e0f08090a0b +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x010(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x018(SB)/8, $0x0c0d0e0f08090a0b +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x020(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x028(SB)/8, $0x0c0d0e0f08090a0b +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x030(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x038(SB)/8, $0x0c0d0e0f08090a0b +GLOBL PSHUFFLE_BYTE_FLIP_MASK<>(SB), 8, $64 + +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x000(SB)/8, $0x0000000000000000 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x008(SB)/8, $0x0000000000000001 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x010(SB)/8, $0x0000000000000008 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x018(SB)/8, $0x0000000000000009 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x020(SB)/8, $0x0000000000000004 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x028(SB)/8, $0x0000000000000005 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x030(SB)/8, $0x000000000000000C +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x038(SB)/8, $0x000000000000000D +GLOBL PSHUFFLE_TRANSPOSE16_MASK1<>(SB), 8, $64 + +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x000(SB)/8, $0x0000000000000002 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x008(SB)/8, $0x0000000000000003 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x010(SB)/8, $0x000000000000000A +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x018(SB)/8, $0x000000000000000B +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x020(SB)/8, $0x0000000000000006 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x028(SB)/8, $0x0000000000000007 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x030(SB)/8, $0x000000000000000E +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x038(SB)/8, $0x000000000000000F +GLOBL PSHUFFLE_TRANSPOSE16_MASK2<>(SB), 8, $64 diff --git a/pkg/crypto/sha256/sha256blockAvx512_amd64.go b/pkg/crypto/sha256/sha256blockAvx512_amd64.go new file mode 100644 index 0000000..3b52387 --- /dev/null +++ b/pkg/crypto/sha256/sha256blockAvx512_amd64.go @@ -0,0 +1,663 @@ +//go:build !noasm && !appengine && gc +// +build !noasm,!appengine,gc + +/* + * Minio Cloud Storage, (C) 2017 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sha256 + +import ( + "encoding/binary" + "errors" + "hash" + "sort" + "sync/atomic" + "time" +) + +//go:noescape +func sha256X16Avx512( + digests *[512]byte, scratch *[512]byte, table *[512]uint64, mask []uint64, + inputs [16][]byte, +) + +// Avx512ServerUID - Do not start at 0 but next multiple of 16 so as to be able to +// differentiate with default initialiation value of 0 +const Avx512ServerUID = 16 + +var uidCounter uint64 + +// NewAvx512 - initialize sha256 Avx512 implementation. +func NewAvx512(a512srv *Avx512Server) hash.Hash { + uid := atomic.AddUint64(&uidCounter, 1) + return &Avx512Digest{uid: uid, a512srv: a512srv} +} + +// Avx512Digest - Type for computing SHA256 using Avx512 +type Avx512Digest struct { + uid uint64 + a512srv *Avx512Server + x [chunk]byte + nx int + len uint64 + final bool + result [Size]byte +} + +// Size - Return size of checksum +func (d *Avx512Digest) Size() int { return Size } + +// BlockSize - Return blocksize of checksum +func (d Avx512Digest) BlockSize() int { return BlockSize } + +// Reset - reset sha digest to its initial values +func (d *Avx512Digest) Reset() { + d.a512srv.blocksCh <- blockInput{uid: d.uid, reset: true} + d.nx = 0 + d.len = 0 + d.final = false +} + +// Write to digest +func (d *Avx512Digest) Write(p []byte) (nn int, err error) { + + if d.final { + return 0, errors.New("Avx512Digest already finalized. Reset first before writing again") + } + + nn = len(p) + d.len += uint64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx:], p) + d.nx += n + if d.nx == chunk { + d.a512srv.blocksCh <- blockInput{uid: d.uid, msg: d.x[:]} + d.nx = 0 + } + p = p[n:] + } + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + d.a512srv.blocksCh <- blockInput{uid: d.uid, msg: p[:n]} + p = p[n:] + } + if len(p) > 0 { + d.nx = copy(d.x[:], p) + } + return +} + +// Sum - Return sha256 sum in bytes +func (d *Avx512Digest) Sum(in []byte) (result []byte) { + + if d.final { + return append(in, d.result[:]...) + } + + trail := make([]byte, 0, 128) + trail = append(trail, d.x[:d.nx]...) + + len := d.len + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + var tmp [64]byte + tmp[0] = 0x80 + if len%64 < 56 { + trail = append(trail, tmp[0:56-len%64]...) + } else { + trail = append(trail, tmp[0:64+56-len%64]...) + } + d.nx = 0 + + // Length in bits. + len <<= 3 + for i := uint(0); i < 8; i++ { + tmp[i] = byte(len >> (56 - 8*i)) + } + trail = append(trail, tmp[0:8]...) + + sumCh := make(chan [Size]byte) + d.a512srv.blocksCh <- blockInput{ + uid: d.uid, msg: trail, final: true, sumCh: sumCh, + } + d.result = <-sumCh + d.final = true + return append(in, d.result[:]...) +} + +var table = [512]uint64{ + 0x428a2f98428a2f98, 0x428a2f98428a2f98, 0x428a2f98428a2f98, + 0x428a2f98428a2f98, + 0x428a2f98428a2f98, 0x428a2f98428a2f98, 0x428a2f98428a2f98, + 0x428a2f98428a2f98, + 0x7137449171374491, 0x7137449171374491, 0x7137449171374491, + 0x7137449171374491, + 0x7137449171374491, 0x7137449171374491, 0x7137449171374491, + 0x7137449171374491, + 0xb5c0fbcfb5c0fbcf, 0xb5c0fbcfb5c0fbcf, 0xb5c0fbcfb5c0fbcf, + 0xb5c0fbcfb5c0fbcf, + 0xb5c0fbcfb5c0fbcf, 0xb5c0fbcfb5c0fbcf, 0xb5c0fbcfb5c0fbcf, + 0xb5c0fbcfb5c0fbcf, + 0xe9b5dba5e9b5dba5, 0xe9b5dba5e9b5dba5, 0xe9b5dba5e9b5dba5, + 0xe9b5dba5e9b5dba5, + 0xe9b5dba5e9b5dba5, 0xe9b5dba5e9b5dba5, 0xe9b5dba5e9b5dba5, + 0xe9b5dba5e9b5dba5, + 0x3956c25b3956c25b, 0x3956c25b3956c25b, 0x3956c25b3956c25b, + 0x3956c25b3956c25b, + 0x3956c25b3956c25b, 0x3956c25b3956c25b, 0x3956c25b3956c25b, + 0x3956c25b3956c25b, + 0x59f111f159f111f1, 0x59f111f159f111f1, 0x59f111f159f111f1, + 0x59f111f159f111f1, + 0x59f111f159f111f1, 0x59f111f159f111f1, 0x59f111f159f111f1, + 0x59f111f159f111f1, + 0x923f82a4923f82a4, 0x923f82a4923f82a4, 0x923f82a4923f82a4, + 0x923f82a4923f82a4, + 0x923f82a4923f82a4, 0x923f82a4923f82a4, 0x923f82a4923f82a4, + 0x923f82a4923f82a4, + 0xab1c5ed5ab1c5ed5, 0xab1c5ed5ab1c5ed5, 0xab1c5ed5ab1c5ed5, + 0xab1c5ed5ab1c5ed5, + 0xab1c5ed5ab1c5ed5, 0xab1c5ed5ab1c5ed5, 0xab1c5ed5ab1c5ed5, + 0xab1c5ed5ab1c5ed5, + 0xd807aa98d807aa98, 0xd807aa98d807aa98, 0xd807aa98d807aa98, + 0xd807aa98d807aa98, + 0xd807aa98d807aa98, 0xd807aa98d807aa98, 0xd807aa98d807aa98, + 0xd807aa98d807aa98, + 0x12835b0112835b01, 0x12835b0112835b01, 0x12835b0112835b01, + 0x12835b0112835b01, + 0x12835b0112835b01, 0x12835b0112835b01, 0x12835b0112835b01, + 0x12835b0112835b01, + 0x243185be243185be, 0x243185be243185be, 0x243185be243185be, + 0x243185be243185be, + 0x243185be243185be, 0x243185be243185be, 0x243185be243185be, + 0x243185be243185be, + 0x550c7dc3550c7dc3, 0x550c7dc3550c7dc3, 0x550c7dc3550c7dc3, + 0x550c7dc3550c7dc3, + 0x550c7dc3550c7dc3, 0x550c7dc3550c7dc3, 0x550c7dc3550c7dc3, + 0x550c7dc3550c7dc3, + 0x72be5d7472be5d74, 0x72be5d7472be5d74, 0x72be5d7472be5d74, + 0x72be5d7472be5d74, + 0x72be5d7472be5d74, 0x72be5d7472be5d74, 0x72be5d7472be5d74, + 0x72be5d7472be5d74, + 0x80deb1fe80deb1fe, 0x80deb1fe80deb1fe, 0x80deb1fe80deb1fe, + 0x80deb1fe80deb1fe, + 0x80deb1fe80deb1fe, 0x80deb1fe80deb1fe, 0x80deb1fe80deb1fe, + 0x80deb1fe80deb1fe, + 0x9bdc06a79bdc06a7, 0x9bdc06a79bdc06a7, 0x9bdc06a79bdc06a7, + 0x9bdc06a79bdc06a7, + 0x9bdc06a79bdc06a7, 0x9bdc06a79bdc06a7, 0x9bdc06a79bdc06a7, + 0x9bdc06a79bdc06a7, + 0xc19bf174c19bf174, 0xc19bf174c19bf174, 0xc19bf174c19bf174, + 0xc19bf174c19bf174, + 0xc19bf174c19bf174, 0xc19bf174c19bf174, 0xc19bf174c19bf174, + 0xc19bf174c19bf174, + 0xe49b69c1e49b69c1, 0xe49b69c1e49b69c1, 0xe49b69c1e49b69c1, + 0xe49b69c1e49b69c1, + 0xe49b69c1e49b69c1, 0xe49b69c1e49b69c1, 0xe49b69c1e49b69c1, + 0xe49b69c1e49b69c1, + 0xefbe4786efbe4786, 0xefbe4786efbe4786, 0xefbe4786efbe4786, + 0xefbe4786efbe4786, + 0xefbe4786efbe4786, 0xefbe4786efbe4786, 0xefbe4786efbe4786, + 0xefbe4786efbe4786, + 0x0fc19dc60fc19dc6, 0x0fc19dc60fc19dc6, 0x0fc19dc60fc19dc6, + 0x0fc19dc60fc19dc6, + 0x0fc19dc60fc19dc6, 0x0fc19dc60fc19dc6, 0x0fc19dc60fc19dc6, + 0x0fc19dc60fc19dc6, + 0x240ca1cc240ca1cc, 0x240ca1cc240ca1cc, 0x240ca1cc240ca1cc, + 0x240ca1cc240ca1cc, + 0x240ca1cc240ca1cc, 0x240ca1cc240ca1cc, 0x240ca1cc240ca1cc, + 0x240ca1cc240ca1cc, + 0x2de92c6f2de92c6f, 0x2de92c6f2de92c6f, 0x2de92c6f2de92c6f, + 0x2de92c6f2de92c6f, + 0x2de92c6f2de92c6f, 0x2de92c6f2de92c6f, 0x2de92c6f2de92c6f, + 0x2de92c6f2de92c6f, + 0x4a7484aa4a7484aa, 0x4a7484aa4a7484aa, 0x4a7484aa4a7484aa, + 0x4a7484aa4a7484aa, + 0x4a7484aa4a7484aa, 0x4a7484aa4a7484aa, 0x4a7484aa4a7484aa, + 0x4a7484aa4a7484aa, + 0x5cb0a9dc5cb0a9dc, 0x5cb0a9dc5cb0a9dc, 0x5cb0a9dc5cb0a9dc, + 0x5cb0a9dc5cb0a9dc, + 0x5cb0a9dc5cb0a9dc, 0x5cb0a9dc5cb0a9dc, 0x5cb0a9dc5cb0a9dc, + 0x5cb0a9dc5cb0a9dc, + 0x76f988da76f988da, 0x76f988da76f988da, 0x76f988da76f988da, + 0x76f988da76f988da, + 0x76f988da76f988da, 0x76f988da76f988da, 0x76f988da76f988da, + 0x76f988da76f988da, + 0x983e5152983e5152, 0x983e5152983e5152, 0x983e5152983e5152, + 0x983e5152983e5152, + 0x983e5152983e5152, 0x983e5152983e5152, 0x983e5152983e5152, + 0x983e5152983e5152, + 0xa831c66da831c66d, 0xa831c66da831c66d, 0xa831c66da831c66d, + 0xa831c66da831c66d, + 0xa831c66da831c66d, 0xa831c66da831c66d, 0xa831c66da831c66d, + 0xa831c66da831c66d, + 0xb00327c8b00327c8, 0xb00327c8b00327c8, 0xb00327c8b00327c8, + 0xb00327c8b00327c8, + 0xb00327c8b00327c8, 0xb00327c8b00327c8, 0xb00327c8b00327c8, + 0xb00327c8b00327c8, + 0xbf597fc7bf597fc7, 0xbf597fc7bf597fc7, 0xbf597fc7bf597fc7, + 0xbf597fc7bf597fc7, + 0xbf597fc7bf597fc7, 0xbf597fc7bf597fc7, 0xbf597fc7bf597fc7, + 0xbf597fc7bf597fc7, + 0xc6e00bf3c6e00bf3, 0xc6e00bf3c6e00bf3, 0xc6e00bf3c6e00bf3, + 0xc6e00bf3c6e00bf3, + 0xc6e00bf3c6e00bf3, 0xc6e00bf3c6e00bf3, 0xc6e00bf3c6e00bf3, + 0xc6e00bf3c6e00bf3, + 0xd5a79147d5a79147, 0xd5a79147d5a79147, 0xd5a79147d5a79147, + 0xd5a79147d5a79147, + 0xd5a79147d5a79147, 0xd5a79147d5a79147, 0xd5a79147d5a79147, + 0xd5a79147d5a79147, + 0x06ca635106ca6351, 0x06ca635106ca6351, 0x06ca635106ca6351, + 0x06ca635106ca6351, + 0x06ca635106ca6351, 0x06ca635106ca6351, 0x06ca635106ca6351, + 0x06ca635106ca6351, + 0x1429296714292967, 0x1429296714292967, 0x1429296714292967, + 0x1429296714292967, + 0x1429296714292967, 0x1429296714292967, 0x1429296714292967, + 0x1429296714292967, + 0x27b70a8527b70a85, 0x27b70a8527b70a85, 0x27b70a8527b70a85, + 0x27b70a8527b70a85, + 0x27b70a8527b70a85, 0x27b70a8527b70a85, 0x27b70a8527b70a85, + 0x27b70a8527b70a85, + 0x2e1b21382e1b2138, 0x2e1b21382e1b2138, 0x2e1b21382e1b2138, + 0x2e1b21382e1b2138, + 0x2e1b21382e1b2138, 0x2e1b21382e1b2138, 0x2e1b21382e1b2138, + 0x2e1b21382e1b2138, + 0x4d2c6dfc4d2c6dfc, 0x4d2c6dfc4d2c6dfc, 0x4d2c6dfc4d2c6dfc, + 0x4d2c6dfc4d2c6dfc, + 0x4d2c6dfc4d2c6dfc, 0x4d2c6dfc4d2c6dfc, 0x4d2c6dfc4d2c6dfc, + 0x4d2c6dfc4d2c6dfc, + 0x53380d1353380d13, 0x53380d1353380d13, 0x53380d1353380d13, + 0x53380d1353380d13, + 0x53380d1353380d13, 0x53380d1353380d13, 0x53380d1353380d13, + 0x53380d1353380d13, + 0x650a7354650a7354, 0x650a7354650a7354, 0x650a7354650a7354, + 0x650a7354650a7354, + 0x650a7354650a7354, 0x650a7354650a7354, 0x650a7354650a7354, + 0x650a7354650a7354, + 0x766a0abb766a0abb, 0x766a0abb766a0abb, 0x766a0abb766a0abb, + 0x766a0abb766a0abb, + 0x766a0abb766a0abb, 0x766a0abb766a0abb, 0x766a0abb766a0abb, + 0x766a0abb766a0abb, + 0x81c2c92e81c2c92e, 0x81c2c92e81c2c92e, 0x81c2c92e81c2c92e, + 0x81c2c92e81c2c92e, + 0x81c2c92e81c2c92e, 0x81c2c92e81c2c92e, 0x81c2c92e81c2c92e, + 0x81c2c92e81c2c92e, + 0x92722c8592722c85, 0x92722c8592722c85, 0x92722c8592722c85, + 0x92722c8592722c85, + 0x92722c8592722c85, 0x92722c8592722c85, 0x92722c8592722c85, + 0x92722c8592722c85, + 0xa2bfe8a1a2bfe8a1, 0xa2bfe8a1a2bfe8a1, 0xa2bfe8a1a2bfe8a1, + 0xa2bfe8a1a2bfe8a1, + 0xa2bfe8a1a2bfe8a1, 0xa2bfe8a1a2bfe8a1, 0xa2bfe8a1a2bfe8a1, + 0xa2bfe8a1a2bfe8a1, + 0xa81a664ba81a664b, 0xa81a664ba81a664b, 0xa81a664ba81a664b, + 0xa81a664ba81a664b, + 0xa81a664ba81a664b, 0xa81a664ba81a664b, 0xa81a664ba81a664b, + 0xa81a664ba81a664b, + 0xc24b8b70c24b8b70, 0xc24b8b70c24b8b70, 0xc24b8b70c24b8b70, + 0xc24b8b70c24b8b70, + 0xc24b8b70c24b8b70, 0xc24b8b70c24b8b70, 0xc24b8b70c24b8b70, + 0xc24b8b70c24b8b70, + 0xc76c51a3c76c51a3, 0xc76c51a3c76c51a3, 0xc76c51a3c76c51a3, + 0xc76c51a3c76c51a3, + 0xc76c51a3c76c51a3, 0xc76c51a3c76c51a3, 0xc76c51a3c76c51a3, + 0xc76c51a3c76c51a3, + 0xd192e819d192e819, 0xd192e819d192e819, 0xd192e819d192e819, + 0xd192e819d192e819, + 0xd192e819d192e819, 0xd192e819d192e819, 0xd192e819d192e819, + 0xd192e819d192e819, + 0xd6990624d6990624, 0xd6990624d6990624, 0xd6990624d6990624, + 0xd6990624d6990624, + 0xd6990624d6990624, 0xd6990624d6990624, 0xd6990624d6990624, + 0xd6990624d6990624, + 0xf40e3585f40e3585, 0xf40e3585f40e3585, 0xf40e3585f40e3585, + 0xf40e3585f40e3585, + 0xf40e3585f40e3585, 0xf40e3585f40e3585, 0xf40e3585f40e3585, + 0xf40e3585f40e3585, + 0x106aa070106aa070, 0x106aa070106aa070, 0x106aa070106aa070, + 0x106aa070106aa070, + 0x106aa070106aa070, 0x106aa070106aa070, 0x106aa070106aa070, + 0x106aa070106aa070, + 0x19a4c11619a4c116, 0x19a4c11619a4c116, 0x19a4c11619a4c116, + 0x19a4c11619a4c116, + 0x19a4c11619a4c116, 0x19a4c11619a4c116, 0x19a4c11619a4c116, + 0x19a4c11619a4c116, + 0x1e376c081e376c08, 0x1e376c081e376c08, 0x1e376c081e376c08, + 0x1e376c081e376c08, + 0x1e376c081e376c08, 0x1e376c081e376c08, 0x1e376c081e376c08, + 0x1e376c081e376c08, + 0x2748774c2748774c, 0x2748774c2748774c, 0x2748774c2748774c, + 0x2748774c2748774c, + 0x2748774c2748774c, 0x2748774c2748774c, 0x2748774c2748774c, + 0x2748774c2748774c, + 0x34b0bcb534b0bcb5, 0x34b0bcb534b0bcb5, 0x34b0bcb534b0bcb5, + 0x34b0bcb534b0bcb5, + 0x34b0bcb534b0bcb5, 0x34b0bcb534b0bcb5, 0x34b0bcb534b0bcb5, + 0x34b0bcb534b0bcb5, + 0x391c0cb3391c0cb3, 0x391c0cb3391c0cb3, 0x391c0cb3391c0cb3, + 0x391c0cb3391c0cb3, + 0x391c0cb3391c0cb3, 0x391c0cb3391c0cb3, 0x391c0cb3391c0cb3, + 0x391c0cb3391c0cb3, + 0x4ed8aa4a4ed8aa4a, 0x4ed8aa4a4ed8aa4a, 0x4ed8aa4a4ed8aa4a, + 0x4ed8aa4a4ed8aa4a, + 0x4ed8aa4a4ed8aa4a, 0x4ed8aa4a4ed8aa4a, 0x4ed8aa4a4ed8aa4a, + 0x4ed8aa4a4ed8aa4a, + 0x5b9cca4f5b9cca4f, 0x5b9cca4f5b9cca4f, 0x5b9cca4f5b9cca4f, + 0x5b9cca4f5b9cca4f, + 0x5b9cca4f5b9cca4f, 0x5b9cca4f5b9cca4f, 0x5b9cca4f5b9cca4f, + 0x5b9cca4f5b9cca4f, + 0x682e6ff3682e6ff3, 0x682e6ff3682e6ff3, 0x682e6ff3682e6ff3, + 0x682e6ff3682e6ff3, + 0x682e6ff3682e6ff3, 0x682e6ff3682e6ff3, 0x682e6ff3682e6ff3, + 0x682e6ff3682e6ff3, + 0x748f82ee748f82ee, 0x748f82ee748f82ee, 0x748f82ee748f82ee, + 0x748f82ee748f82ee, + 0x748f82ee748f82ee, 0x748f82ee748f82ee, 0x748f82ee748f82ee, + 0x748f82ee748f82ee, + 0x78a5636f78a5636f, 0x78a5636f78a5636f, 0x78a5636f78a5636f, + 0x78a5636f78a5636f, + 0x78a5636f78a5636f, 0x78a5636f78a5636f, 0x78a5636f78a5636f, + 0x78a5636f78a5636f, + 0x84c8781484c87814, 0x84c8781484c87814, 0x84c8781484c87814, + 0x84c8781484c87814, + 0x84c8781484c87814, 0x84c8781484c87814, 0x84c8781484c87814, + 0x84c8781484c87814, + 0x8cc702088cc70208, 0x8cc702088cc70208, 0x8cc702088cc70208, + 0x8cc702088cc70208, + 0x8cc702088cc70208, 0x8cc702088cc70208, 0x8cc702088cc70208, + 0x8cc702088cc70208, + 0x90befffa90befffa, 0x90befffa90befffa, 0x90befffa90befffa, + 0x90befffa90befffa, + 0x90befffa90befffa, 0x90befffa90befffa, 0x90befffa90befffa, + 0x90befffa90befffa, + 0xa4506ceba4506ceb, 0xa4506ceba4506ceb, 0xa4506ceba4506ceb, + 0xa4506ceba4506ceb, + 0xa4506ceba4506ceb, 0xa4506ceba4506ceb, 0xa4506ceba4506ceb, + 0xa4506ceba4506ceb, + 0xbef9a3f7bef9a3f7, 0xbef9a3f7bef9a3f7, 0xbef9a3f7bef9a3f7, + 0xbef9a3f7bef9a3f7, + 0xbef9a3f7bef9a3f7, 0xbef9a3f7bef9a3f7, 0xbef9a3f7bef9a3f7, + 0xbef9a3f7bef9a3f7, + 0xc67178f2c67178f2, 0xc67178f2c67178f2, 0xc67178f2c67178f2, + 0xc67178f2c67178f2, + 0xc67178f2c67178f2, 0xc67178f2c67178f2, 0xc67178f2c67178f2, + 0xc67178f2c67178f2, +} + +// Interface function to assembly ode +func blockAvx512( + digests *[512]byte, input [16][]byte, mask []uint64, +) [16][Size]byte { + + scratch := [512]byte{} + sha256X16Avx512(digests, &scratch, &table, mask, input) + + output := [16][Size]byte{} + for i := 0; i < 16; i++ { + output[i] = getDigest(i, digests[:]) + } + + return output +} + +func getDigest(index int, state []byte) (sum [Size]byte) { + for j := 0; j < 16; j += 2 { + for i := index*4 + j*Size; i < index*4+(j+1)*Size; i += Size { + binary.BigEndian.PutUint32( + sum[j*2:], binary.LittleEndian.Uint32(state[i:i+4]), + ) + } + } + return +} + +// Message to send across input channel +type blockInput struct { + uid uint64 + msg []byte + reset bool + final bool + sumCh chan [Size]byte +} + +// Avx512Server - Type to implement 16x parallel handling of SHA256 invocations +type Avx512Server struct { + blocksCh chan blockInput // Input channel + totalIn int // Total number of inputs waiting to be processed + lanes [16]Avx512LaneInfo // Array with info per lane (out of 16) + digests map[uint64][Size]byte // Map of uids to (interim) digest results +} + +// Avx512LaneInfo - Info for each lane +type Avx512LaneInfo struct { + uid uint64 // unique identification for this SHA processing + block []byte // input block to be processed + outputCh chan [Size]byte // channel for output result +} + +// NewAvx512Server - Create new object for parallel processing handling +func NewAvx512Server() *Avx512Server { + a512srv := &Avx512Server{} + a512srv.digests = make(map[uint64][Size]byte) + a512srv.blocksCh = make(chan blockInput) + + // Start a single thread for reading from the input channel + go a512srv.Process() + return a512srv +} + +// Process - Sole handler for reading from the input channel +func (a512srv *Avx512Server) Process() { + for { + select { + case block := <-a512srv.blocksCh: + if block.reset { + a512srv.reset(block.uid) + continue + } + index := block.uid & 0xf + // fmt.Println("Adding message:", block.uid, index) + + if a512srv.lanes[index].block != nil { // If slot is already filled, process all inputs + // fmt.Println("Invoking Blocks()") + a512srv.blocks() + } + a512srv.totalIn++ + a512srv.lanes[index] = Avx512LaneInfo{ + uid: block.uid, block: block.msg, + } + if block.final { + a512srv.lanes[index].outputCh = block.sumCh + } + if a512srv.totalIn == len(a512srv.lanes) { + // fmt.Println("Invoking Blocks() while FULL: ") + a512srv.blocks() + } + + // TODO: test with larger timeout + case <-time.After(1 * time.Microsecond): + for _, lane := range a512srv.lanes { + if lane.block != nil { // check if there is any input to process + // fmt.Println("Invoking Blocks() on TIMEOUT: ") + a512srv.blocks() + break // we are done + } + } + } + } +} + +// Do a reset for this calculation +func (a512srv *Avx512Server) reset(uid uint64) { + + // Check if there is a message still waiting to be processed (and remove if so) + for i, lane := range a512srv.lanes { + if lane.uid == uid { + if lane.block != nil { + a512srv.lanes[i] = Avx512LaneInfo{} // clear message + a512srv.totalIn-- + } + } + } + + // Delete entry from hash map + delete(a512srv.digests, uid) +} + +// Invoke assembly and send results back +func (a512srv *Avx512Server) blocks() { + + inputs := [16][]byte{} + for i := range inputs { + inputs[i] = a512srv.lanes[i].block + } + + mask := expandMask(genMask(inputs)) + outputs := blockAvx512(a512srv.getDigests(), inputs, mask) + + a512srv.totalIn = 0 + for i := 0; i < len(outputs); i++ { + uid, outputCh := a512srv.lanes[i].uid, a512srv.lanes[i].outputCh + a512srv.digests[uid] = outputs[i] + a512srv.lanes[i] = Avx512LaneInfo{} + + if outputCh != nil { + // Send back result + outputCh <- outputs[i] + delete(a512srv.digests, uid) // Delete entry from hashmap + } + } +} + +func (a512srv *Avx512Server) Write(uid uint64, p []byte) (nn int, err error) { + a512srv.blocksCh <- blockInput{uid: uid, msg: p} + return len(p), nil +} + +// Sum - return sha256 sum in bytes for a given sum id. +func (a512srv *Avx512Server) Sum(uid uint64, p []byte) [32]byte { + sumCh := make(chan [32]byte) + a512srv.blocksCh <- blockInput{uid: uid, msg: p, final: true, sumCh: sumCh} + return <-sumCh +} + +func (a512srv *Avx512Server) getDigests() *[512]byte { + digests := [512]byte{} + for i, lane := range a512srv.lanes { + a, ok := a512srv.digests[lane.uid] + if ok { + binary.BigEndian.PutUint32( + digests[(i+0*16)*4:], binary.LittleEndian.Uint32(a[0:4]), + ) + binary.BigEndian.PutUint32( + digests[(i+1*16)*4:], binary.LittleEndian.Uint32(a[4:8]), + ) + binary.BigEndian.PutUint32( + digests[(i+2*16)*4:], + binary.LittleEndian.Uint32(a[8:12]), + ) + binary.BigEndian.PutUint32( + digests[(i+3*16)*4:], + binary.LittleEndian.Uint32(a[12:16]), + ) + binary.BigEndian.PutUint32( + digests[(i+4*16)*4:], + binary.LittleEndian.Uint32(a[16:20]), + ) + binary.BigEndian.PutUint32( + digests[(i+5*16)*4:], + binary.LittleEndian.Uint32(a[20:24]), + ) + binary.BigEndian.PutUint32( + digests[(i+6*16)*4:], + binary.LittleEndian.Uint32(a[24:28]), + ) + binary.BigEndian.PutUint32( + digests[(i+7*16)*4:], + binary.LittleEndian.Uint32(a[28:32]), + ) + } else { + binary.LittleEndian.PutUint32(digests[(i+0*16)*4:], init0) + binary.LittleEndian.PutUint32(digests[(i+1*16)*4:], init1) + binary.LittleEndian.PutUint32(digests[(i+2*16)*4:], init2) + binary.LittleEndian.PutUint32(digests[(i+3*16)*4:], init3) + binary.LittleEndian.PutUint32(digests[(i+4*16)*4:], init4) + binary.LittleEndian.PutUint32(digests[(i+5*16)*4:], init5) + binary.LittleEndian.PutUint32(digests[(i+6*16)*4:], init6) + binary.LittleEndian.PutUint32(digests[(i+7*16)*4:], init7) + } + } + return &digests +} + +// Helper struct for sorting blocks based on length +type lane struct { + len uint + pos uint +} + +type lanes []lane + +func (lns lanes) Len() int { return len(lns) } +func (lns lanes) Swap(i, j int) { lns[i], lns[j] = lns[j], lns[i] } +func (lns lanes) Less(i, j int) bool { return lns[i].len < lns[j].len } + +// Helper struct for +type maskRounds struct { + mask uint64 + rounds uint64 +} + +func genMask(input [16][]byte) [16]maskRounds { + + // Sort on blocks length small to large + var sorted [16]lane + for c, inpt := range input { + sorted[c] = lane{uint(len(inpt)), uint(c)} + } + sort.Sort(lanes(sorted[:])) + + // Create mask array including 'rounds' between masks + m, round, index := uint64(0xffff), uint64(0), 0 + var mr [16]maskRounds + for _, s := range sorted { + if s.len > 0 { + if uint64(s.len)>>6 > round { + mr[index] = maskRounds{m, (uint64(s.len) >> 6) - round} + index++ + } + round = uint64(s.len) >> 6 + } + m = m & ^(1 << uint(s.pos)) + } + + return mr +} + +// TODO: remove function +func expandMask(mr [16]maskRounds) []uint64 { + size := uint64(0) + for _, r := range mr { + size += r.rounds + } + result, index := make([]uint64, size), 0 + for _, r := range mr { + for j := uint64(0); j < r.rounds; j++ { + result[index] = r.mask + index++ + } + } + return result +} diff --git a/pkg/crypto/sha256/sha256blockAvx512_amd64.s b/pkg/crypto/sha256/sha256blockAvx512_amd64.s new file mode 100644 index 0000000..cca534e --- /dev/null +++ b/pkg/crypto/sha256/sha256blockAvx512_amd64.s @@ -0,0 +1,267 @@ +//+build !noasm,!appengine,gc + +TEXT ·sha256X16Avx512(SB), 7, $0 + MOVQ digests+0(FP), DI + MOVQ scratch+8(FP), R12 + MOVQ mask_len+32(FP), SI + MOVQ mask_base+24(FP), R13 + MOVQ (R13), R14 + LONG $0x92fbc1c4; BYTE $0xce + LEAQ inputs+48(FP), AX + QUAD $0xf162076f487ef162; QUAD $0x7ef162014f6f487e; QUAD $0x487ef16202576f48; QUAD $0x6f487ef162035f6f; QUAD $0x6f6f487ef1620467; QUAD $0x06776f487ef16205; LONG $0x487ef162; WORD $0x7f6f; BYTE $0x07 + MOVQ table+16(FP), DX + WORD $0x3148; BYTE $0xc9 + TESTQ $(1<<0), R14 + JE skipInput0 + MOVQ 0*24(AX), R9 + LONG $0x487cc162; WORD $0x0410; BYTE $0x09 + +skipInput0: + TESTQ $(1<<1), R14 + JE skipInput1 + MOVQ 1*24(AX), R9 + LONG $0x487cc162; WORD $0x0c10; BYTE $0x09 + +skipInput1: + TESTQ $(1<<2), R14 + JE skipInput2 + MOVQ 2*24(AX), R9 + LONG $0x487cc162; WORD $0x1410; BYTE $0x09 + +skipInput2: + TESTQ $(1<<3), R14 + JE skipInput3 + MOVQ 3*24(AX), R9 + LONG $0x487cc162; WORD $0x1c10; BYTE $0x09 + +skipInput3: + TESTQ $(1<<4), R14 + JE skipInput4 + MOVQ 4*24(AX), R9 + LONG $0x487cc162; WORD $0x2410; BYTE $0x09 + +skipInput4: + TESTQ $(1<<5), R14 + JE skipInput5 + MOVQ 5*24(AX), R9 + LONG $0x487cc162; WORD $0x2c10; BYTE $0x09 + +skipInput5: + TESTQ $(1<<6), R14 + JE skipInput6 + MOVQ 6*24(AX), R9 + LONG $0x487cc162; WORD $0x3410; BYTE $0x09 + +skipInput6: + TESTQ $(1<<7), R14 + JE skipInput7 + MOVQ 7*24(AX), R9 + LONG $0x487cc162; WORD $0x3c10; BYTE $0x09 + +skipInput7: + TESTQ $(1<<8), R14 + JE skipInput8 + MOVQ 8*24(AX), R9 + LONG $0x487c4162; WORD $0x0410; BYTE $0x09 + +skipInput8: + TESTQ $(1<<9), R14 + JE skipInput9 + MOVQ 9*24(AX), R9 + LONG $0x487c4162; WORD $0x0c10; BYTE $0x09 + +skipInput9: + TESTQ $(1<<10), R14 + JE skipInput10 + MOVQ 10*24(AX), R9 + LONG $0x487c4162; WORD $0x1410; BYTE $0x09 + +skipInput10: + TESTQ $(1<<11), R14 + JE skipInput11 + MOVQ 11*24(AX), R9 + LONG $0x487c4162; WORD $0x1c10; BYTE $0x09 + +skipInput11: + TESTQ $(1<<12), R14 + JE skipInput12 + MOVQ 12*24(AX), R9 + LONG $0x487c4162; WORD $0x2410; BYTE $0x09 + +skipInput12: + TESTQ $(1<<13), R14 + JE skipInput13 + MOVQ 13*24(AX), R9 + LONG $0x487c4162; WORD $0x2c10; BYTE $0x09 + +skipInput13: + TESTQ $(1<<14), R14 + JE skipInput14 + MOVQ 14*24(AX), R9 + LONG $0x487c4162; WORD $0x3410; BYTE $0x09 + +skipInput14: + TESTQ $(1<<15), R14 + JE skipInput15 + MOVQ 15*24(AX), R9 + LONG $0x487c4162; WORD $0x3c10; BYTE $0x09 + +skipInput15: +lloop: + LEAQ PSHUFFLE_BYTE_FLIP_MASK<>(SB), DX + LONG $0x487e7162; WORD $0x1a6f + MOVQ table+16(FP), DX + QUAD $0xd162226f487e7162; QUAD $0x7ed16224047f487e; QUAD $0x7ed16201244c7f48; QUAD $0x7ed1620224547f48; QUAD $0x7ed16203245c7f48; QUAD $0x7ed1620424647f48; QUAD $0x7ed16205246c7f48; QUAD $0x7ed1620624747f48; QUAD $0xc1834807247c7f48; QUAD $0x44c9c6407c316240; QUAD $0x62eec1c6407ca162; QUAD $0xa16244d3c6406c31; QUAD $0x34c162eed3c6406c; QUAD $0x407ca162dddac648; QUAD $0xc6407ca16288cac6; QUAD $0xcac648345162ddc2; QUAD $0x44d5c6405ca16288; QUAD $0x62eee5c6405ca162; QUAD $0xa16244d7c6404c31; QUAD $0x6cc162eef7c6404c; QUAD $0x405ca162ddfac640; QUAD $0xc6405ca16288eec6; QUAD $0xd2c6406cc162dde6; QUAD $0x44f1c6403c816288; QUAD $0x62eec1c6403c0162; QUAD $0x016244d3c6402c11; QUAD $0x4c4162eed3c6402c; QUAD $0x403c0162dddac640; QUAD $0xc6403c016288cac6; QUAD $0xf2c6404cc162ddc2; QUAD $0x44d5c6401c016288; QUAD $0x62eee5c6401c0162; QUAD $0x016244d7c6400c11; QUAD $0x2c4162eef7c6400c; QUAD $0x401c0162ddfac640; QUAD $0xc6401c016288eec6; QUAD $0xd2c6402c4162dde6; BYTE $0x88 + LEAQ PSHUFFLE_TRANSPOSE16_MASK1<>(SB), BX + LEAQ PSHUFFLE_TRANSPOSE16_MASK2<>(SB), R8 + QUAD $0x2262336f487e6162; QUAD $0x487e5162f27648b5; QUAD $0xd27648b53262106f; QUAD $0xa262136f487ee162; QUAD $0x487e5162d77640e5; QUAD $0xcf7640e53262086f; QUAD $0xa2621b6f487ee162; QUAD $0x487ec162dd7640f5; QUAD $0xfd7640f5a262386f; QUAD $0xa2620b6f487ee162; QUAD $0x487ec162cc7640fd; QUAD $0xec7640fda262286f; QUAD $0x8262036f487ee162; QUAD $0x487ec162c27640cd; QUAD $0xe27640cd8262206f; QUAD $0x8262336f487ee162; QUAD $0x487e4162f77640a5; QUAD $0xd77640a50262106f; QUAD $0x02621b6f487e6162; QUAD $0x487e4162dd7640b5; QUAD $0xfd7640b50262386f; QUAD $0x02620b6f487e6162; QUAD $0x487e4162cc7640bd; QUAD $0xec7640bd0262286f; QUAD $0x62eec023408d2362; QUAD $0x236244c023408da3; QUAD $0xada362eee42348ad; QUAD $0x40c5036244e42348; QUAD $0x2340c51362eef723; QUAD $0xfd2340d5036244d7; QUAD $0x44fd2340d58362ee; QUAD $0x62eeea2348b50362; QUAD $0x036244ea2348b583; QUAD $0xe51362eed32340e5; QUAD $0x40f5036244cb2340; QUAD $0x2340f58362eed923; QUAD $0xce2340ed236244d9; QUAD $0x44ce2340eda362ee; QUAD $0xc162d16f487ec162; QUAD $0x407dc262f26f487e; QUAD $0xcb004075c262c300; QUAD $0xc262d300406dc262; QUAD $0x405dc262db004065; QUAD $0xeb004055c262e300; QUAD $0xc262f300404dc262; QUAD $0x403d4262fb004045; QUAD $0xcb0040354262c300; QUAD $0x4262d300402d4262; QUAD $0x401d4262db004025; QUAD $0xeb0040154262e300; QUAD $0x4262f300400d4262; QUAD $0x48455162fb004005; QUAD $0xcc6f487e7162c4fe; QUAD $0x6206c472482df162; QUAD $0xf1620bc4724825f1; QUAD $0x55736219c472481d; QUAD $0x483d3162cace2548; QUAD $0xd42548255362c0fe; QUAD $0x62c1fe483d516296; QUAD $0x65d162c2fe483d51; QUAD $0x724845f162d8fe48; QUAD $0xc0724825f16202c0; QUAD $0x16c072481df1620d; QUAD $0x7362c86f487e7162; QUAD $0x25d362e8ca254875; QUAD $0x4845d16296fc2548; QUAD $0xf8fe4845d162f9fe; QUAD $0x6201626f487e7162; QUAD $0x916211c672481591; QUAD $0x05916213c672480d; QUAD $0x480d53620ad67248; QUAD $0xfe407dc16296ef25; QUAD $0x62c1fe407d8162c5; QUAD $0xb16207c1724815b1; QUAD $0x05b16212c172480d; QUAD $0x480d536203d17248; QUAD $0xfe407dc16296ef25; QUAD $0x62c4fe484d5162c5; QUAD $0x2df162cb6f487e71; QUAD $0x4825f16206c37248; QUAD $0x72481df1620bc372; QUAD $0xcd25485d736219c3; QUAD $0x62c1fe483d3162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xd0fe486dd162c2fe; QUAD $0x6202c772484df162; QUAD $0xf1620dc7724825f1; QUAD $0x7e716216c772481d; QUAD $0x25487d7362cf6f48; QUAD $0xf4254825d362e8c9; QUAD $0x62f1fe484dd16296; QUAD $0x7e7162f0fe484dd1; QUAD $0x4815916202626f48; QUAD $0x72480d916211c772; QUAD $0xd7724805916213c7; QUAD $0x96ef25480d53620a; QUAD $0x8162cdfe4075c162; QUAD $0x4815b162cafe4075; QUAD $0x72480db16207c272; QUAD $0xd2724805b16212c2; QUAD $0x96ef25480d536203; QUAD $0x5162cdfe4075c162; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x3162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x11c0724815b16203; QUAD $0x6213c072480db162; QUAD $0x53620ad0724805b1; QUAD $0x6dc16296ef25480d; QUAD $0xfe406d8162d5fe40; QUAD $0x07c3724815b162d3; QUAD $0x6212c372480db162; QUAD $0x536203d3724805b1; QUAD $0x6dc16296ef25480d; QUAD $0xfe485d5162d5fe40; QUAD $0x62c96f487e7162c4; QUAD $0xf16206c172482df1; QUAD $0x1df1620bc1724825; QUAD $0x486d736219c17248; QUAD $0xfe483d3162cacb25; QUAD $0x96d42548255362c3; QUAD $0x5162c1fe483d5162; QUAD $0x487dd162c2fe483d; QUAD $0xc572485df162c0fe; QUAD $0x0dc5724825f16202; QUAD $0x6216c572481df162; QUAD $0x4d7362cd6f487e71; QUAD $0x4825d362e8cf2548; QUAD $0xfe485dd16296e425; QUAD $0x62e0fe485dd162e1; QUAD $0xb16204626f487e71; QUAD $0x0db16211c1724815; QUAD $0x4805b16213c17248; QUAD $0x25480d53620ad172; QUAD $0xddfe4065c16296ef; QUAD $0xb162dcfe40658162; QUAD $0x0db16207c4724815; QUAD $0x4805b16212c47248; QUAD $0x25480d536203d472; QUAD $0xddfe4065c16296ef; QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; QUAD $0x724815b16205626f; QUAD $0xc272480db16211c2; QUAD $0x0ad2724805b16213; QUAD $0x6296ef25480d5362; QUAD $0x5d8162e5fe405dc1; QUAD $0x724815b162e5fe40; QUAD $0xc572480db16207c5; QUAD $0x03d5724805b16212; QUAD $0x6296ef25480d5362; QUAD $0x6d5162e5fe405dc1; QUAD $0x6f487e7162c4fe48; QUAD $0x06c772482df162cf; QUAD $0x620bc7724825f162; QUAD $0x736219c772481df1; QUAD $0x3d3162cac925487d; QUAD $0x2548255362c5fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x486df162f0fe484d; QUAD $0x724825f16202c372; QUAD $0xc372481df1620dc3; QUAD $0x62cb6f487e716216; QUAD $0xd362e8cd25485d73; QUAD $0x6dd16296d4254825; QUAD $0xfe486dd162d1fe48; QUAD $0x06626f487e7162d0; QUAD $0x6211c3724815b162; QUAD $0xb16213c372480db1; QUAD $0x0d53620ad3724805; QUAD $0x4055c16296ef2548; QUAD $0xeefe40558162edfe; QUAD $0x6207c6724815b162; QUAD $0xb16212c672480db1; QUAD $0x0d536203d6724805; QUAD $0x4055c16296ef2548; QUAD $0xc4fe48755162edfe; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d3162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x15b16207626f487e; QUAD $0x480db16211c47248; QUAD $0x724805b16213c472; QUAD $0xef25480d53620ad4; QUAD $0x62f5fe404dc16296; QUAD $0x15b162f7fe404d81; QUAD $0x480db16207c77248; QUAD $0x724805b16212c772; QUAD $0xef25480d536203d7; QUAD $0x62f5fe404dc16296; QUAD $0x7e7162c4fe487d51; QUAD $0x72482df162cd6f48; QUAD $0xc5724825f16206c5; QUAD $0x19c572481df1620b; QUAD $0x62cacf25484d7362; QUAD $0x255362c7fe483d31; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162e0fe485dd162; QUAD $0x25f16202c172487d; QUAD $0x481df1620dc17248; QUAD $0x6f487e716216c172; QUAD $0xe8cb25486d7362c9; QUAD $0x6296c4254825d362; QUAD $0x7dd162c1fe487dd1; QUAD $0x6f487e7162c0fe48; QUAD $0xc5724815b1620862; QUAD $0x13c572480db16211; QUAD $0x620ad5724805b162; QUAD $0xc16296ef25480d53; QUAD $0x4045a162fdfe4045; QUAD $0xc07248159162f8fe; QUAD $0x12c072480d916207; QUAD $0x6203d07248059162; QUAD $0xc16296ef25480d53; QUAD $0x48455162fdfe4045; QUAD $0xcc6f487e7162c4fe; QUAD $0x6206c472482df162; QUAD $0xf1620bc4724825f1; QUAD $0x55736219c472481d; QUAD $0x483d1162cace2548; QUAD $0xd42548255362c0fe; QUAD $0x62c1fe483d516296; QUAD $0x65d162c2fe483d51; QUAD $0x724845f162d8fe48; QUAD $0xc0724825f16202c0; QUAD $0x16c072481df1620d; QUAD $0x7362c86f487e7162; QUAD $0x25d362e8ca254875; QUAD $0x4845d16296fc2548; QUAD $0xf8fe4845d162f9fe; QUAD $0x6209626f487e7162; QUAD $0xb16211c6724815b1; QUAD $0x05b16213c672480d; QUAD $0x480d53620ad67248; QUAD $0xfe403d416296ef25; QUAD $0x62c1fe403d2162c5; QUAD $0x916207c172481591; QUAD $0x05916212c172480d; QUAD $0x480d536203d17248; QUAD $0xfe403d416296ef25; QUAD $0x62c4fe484d5162c5; QUAD $0x2df162cb6f487e71; QUAD $0x4825f16206c37248; QUAD $0x72481df1620bc372; QUAD $0xcd25485d736219c3; QUAD $0x62c1fe483d1162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xd0fe486dd162c2fe; QUAD $0x6202c772484df162; QUAD $0xf1620dc7724825f1; QUAD $0x7e716216c772481d; QUAD $0x25487d7362cf6f48; QUAD $0xf4254825d362e8c9; QUAD $0x62f1fe484dd16296; QUAD $0x7e7162f0fe484dd1; QUAD $0x4815b1620a626f48; QUAD $0x72480db16211c772; QUAD $0xd7724805b16213c7; QUAD $0x96ef25480d53620a; QUAD $0x2162cdfe40354162; QUAD $0x48159162cafe4035; QUAD $0x72480d916207c272; QUAD $0xd2724805916212c2; QUAD $0x96ef25480d536203; QUAD $0x5162cdfe40354162; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x1162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x11c072481591620b; QUAD $0x6213c072480d9162; QUAD $0x53620ad072480591; QUAD $0x2d416296ef25480d; QUAD $0xfe402d2162d5fe40; QUAD $0x07c37248159162d3; QUAD $0x6212c372480d9162; QUAD $0x536203d372480591; QUAD $0x2d416296ef25480d; QUAD $0xfe485d5162d5fe40; QUAD $0x62c96f487e7162c4; QUAD $0xf16206c172482df1; QUAD $0x1df1620bc1724825; QUAD $0x486d736219c17248; QUAD $0xfe483d1162cacb25; QUAD $0x96d42548255362c3; QUAD $0x5162c1fe483d5162; QUAD $0x487dd162c2fe483d; QUAD $0xc572485df162c0fe; QUAD $0x0dc5724825f16202; QUAD $0x6216c572481df162; QUAD $0x4d7362cd6f487e71; QUAD $0x4825d362e8cf2548; QUAD $0xfe485dd16296e425; QUAD $0x62e0fe485dd162e1; QUAD $0x91620c626f487e71; QUAD $0x0d916211c1724815; QUAD $0x4805916213c17248; QUAD $0x25480d53620ad172; QUAD $0xddfe4025416296ef; QUAD $0x9162dcfe40252162; QUAD $0x0d916207c4724815; QUAD $0x4805916212c47248; QUAD $0x25480d536203d472; QUAD $0xddfe4025416296ef; QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; QUAD $0x72481591620d626f; QUAD $0xc272480d916211c2; QUAD $0x0ad2724805916213; QUAD $0x6296ef25480d5362; QUAD $0x1d2162e5fe401d41; QUAD $0x7248159162e5fe40; QUAD $0xc572480d916207c5; QUAD $0x03d5724805916212; QUAD $0x6296ef25480d5362; QUAD $0x6d5162e5fe401d41; QUAD $0x6f487e7162c4fe48; QUAD $0x06c772482df162cf; QUAD $0x620bc7724825f162; QUAD $0x736219c772481df1; QUAD $0x3d1162cac925487d; QUAD $0x2548255362c5fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x486df162f0fe484d; QUAD $0x724825f16202c372; QUAD $0xc372481df1620dc3; QUAD $0x62cb6f487e716216; QUAD $0xd362e8cd25485d73; QUAD $0x6dd16296d4254825; QUAD $0xfe486dd162d1fe48; QUAD $0x0e626f487e7162d0; QUAD $0x6211c37248159162; QUAD $0x916213c372480d91; QUAD $0x0d53620ad3724805; QUAD $0x4015416296ef2548; QUAD $0xeefe40152162edfe; QUAD $0x6207c67248159162; QUAD $0x916212c672480d91; QUAD $0x0d536203d6724805; QUAD $0x4015416296ef2548; QUAD $0xc4fe48755162edfe; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d1162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x1591620f626f487e; QUAD $0x480d916211c47248; QUAD $0x724805916213c472; QUAD $0xef25480d53620ad4; QUAD $0x62f5fe400d416296; QUAD $0x159162f7fe400d21; QUAD $0x480d916207c77248; QUAD $0x724805916212c772; QUAD $0xef25480d536203d7; QUAD $0x62f5fe400d416296; QUAD $0x7e7162c4fe487d51; QUAD $0x72482df162cd6f48; QUAD $0xc5724825f16206c5; QUAD $0x19c572481df1620b; QUAD $0x62cacf25484d7362; QUAD $0x255362c7fe483d11; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162e0fe485dd162; QUAD $0x25f16202c172487d; QUAD $0x481df1620dc17248; QUAD $0x6f487e716216c172; QUAD $0xe8cb25486d7362c9; QUAD $0x6296c4254825d362; QUAD $0x7dd162c1fe487dd1; QUAD $0x6f487e7162c0fe48; QUAD $0xc572481591621062; QUAD $0x13c572480d916211; QUAD $0x620ad57248059162; QUAD $0x416296ef25480d53; QUAD $0x40050162fdfe4005; QUAD $0xc0724815b162f8fe; QUAD $0x12c072480db16207; QUAD $0x6203d0724805b162; QUAD $0x416296ef25480d53; QUAD $0x48455162fdfe4005; QUAD $0xcc6f487e7162c4fe; QUAD $0x6206c472482df162; QUAD $0xf1620bc4724825f1; QUAD $0x55736219c472481d; QUAD $0x483d3162cace2548; QUAD $0xd42548255362c0fe; QUAD $0x62c1fe483d516296; QUAD $0x65d162c2fe483d51; QUAD $0x724845f162d8fe48; QUAD $0xc0724825f16202c0; QUAD $0x16c072481df1620d; QUAD $0x7362c86f487e7162; QUAD $0x25d362e8ca254875; QUAD $0x4845d16296fc2548; QUAD $0xf8fe4845d162f9fe; QUAD $0x6211626f487e7162; QUAD $0x916211c672481591; QUAD $0x05916213c672480d; QUAD $0x480d53620ad67248; QUAD $0xfe407dc16296ef25; QUAD $0x62c1fe407d8162c5; QUAD $0xb16207c1724815b1; QUAD $0x05b16212c172480d; QUAD $0x480d536203d17248; QUAD $0xfe407dc16296ef25; QUAD $0x62c4fe484d5162c5; QUAD $0x2df162cb6f487e71; QUAD $0x4825f16206c37248; QUAD $0x72481df1620bc372; QUAD $0xcd25485d736219c3; QUAD $0x62c1fe483d3162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xd0fe486dd162c2fe; QUAD $0x6202c772484df162; QUAD $0xf1620dc7724825f1; QUAD $0x7e716216c772481d; QUAD $0x25487d7362cf6f48; QUAD $0xf4254825d362e8c9; QUAD $0x62f1fe484dd16296; QUAD $0x7e7162f0fe484dd1; QUAD $0x4815916212626f48; QUAD $0x72480d916211c772; QUAD $0xd7724805916213c7; QUAD $0x96ef25480d53620a; QUAD $0x8162cdfe4075c162; QUAD $0x4815b162cafe4075; QUAD $0x72480db16207c272; QUAD $0xd2724805b16212c2; QUAD $0x96ef25480d536203; QUAD $0x5162cdfe4075c162; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x3162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x11c0724815b16213; QUAD $0x6213c072480db162; QUAD $0x53620ad0724805b1; QUAD $0x6dc16296ef25480d; QUAD $0xfe406d8162d5fe40; QUAD $0x07c3724815b162d3; QUAD $0x6212c372480db162; QUAD $0x536203d3724805b1; QUAD $0x6dc16296ef25480d; QUAD $0xfe485d5162d5fe40; QUAD $0x62c96f487e7162c4; QUAD $0xf16206c172482df1; QUAD $0x1df1620bc1724825; QUAD $0x486d736219c17248; QUAD $0xfe483d3162cacb25; QUAD $0x96d42548255362c3; QUAD $0x5162c1fe483d5162; QUAD $0x487dd162c2fe483d; QUAD $0xc572485df162c0fe; QUAD $0x0dc5724825f16202; QUAD $0x6216c572481df162; QUAD $0x4d7362cd6f487e71; QUAD $0x4825d362e8cf2548; QUAD $0xfe485dd16296e425; QUAD $0x62e0fe485dd162e1; QUAD $0xb16214626f487e71; QUAD $0x0db16211c1724815; QUAD $0x4805b16213c17248; QUAD $0x25480d53620ad172; QUAD $0xddfe4065c16296ef; QUAD $0xb162dcfe40658162; QUAD $0x0db16207c4724815; QUAD $0x4805b16212c47248; QUAD $0x25480d536203d472; QUAD $0xddfe4065c16296ef; QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; QUAD $0x724815b16215626f; QUAD $0xc272480db16211c2; QUAD $0x0ad2724805b16213; QUAD $0x6296ef25480d5362; QUAD $0x5d8162e5fe405dc1; QUAD $0x724815b162e5fe40; QUAD $0xc572480db16207c5; QUAD $0x03d5724805b16212; QUAD $0x6296ef25480d5362; QUAD $0x6d5162e5fe405dc1; QUAD $0x6f487e7162c4fe48; QUAD $0x06c772482df162cf; QUAD $0x620bc7724825f162; QUAD $0x736219c772481df1; QUAD $0x3d3162cac925487d; QUAD $0x2548255362c5fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x486df162f0fe484d; QUAD $0x724825f16202c372; QUAD $0xc372481df1620dc3; QUAD $0x62cb6f487e716216; QUAD $0xd362e8cd25485d73; QUAD $0x6dd16296d4254825; QUAD $0xfe486dd162d1fe48; QUAD $0x16626f487e7162d0; QUAD $0x6211c3724815b162; QUAD $0xb16213c372480db1; QUAD $0x0d53620ad3724805; QUAD $0x4055c16296ef2548; QUAD $0xeefe40558162edfe; QUAD $0x6207c6724815b162; QUAD $0xb16212c672480db1; QUAD $0x0d536203d6724805; QUAD $0x4055c16296ef2548; QUAD $0xc4fe48755162edfe; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d3162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x15b16217626f487e; QUAD $0x480db16211c47248; QUAD $0x724805b16213c472; QUAD $0xef25480d53620ad4; QUAD $0x62f5fe404dc16296; QUAD $0x15b162f7fe404d81; QUAD $0x480db16207c77248; QUAD $0x724805b16212c772; QUAD $0xef25480d536203d7; QUAD $0x62f5fe404dc16296; QUAD $0x7e7162c4fe487d51; QUAD $0x72482df162cd6f48; QUAD $0xc5724825f16206c5; QUAD $0x19c572481df1620b; QUAD $0x62cacf25484d7362; QUAD $0x255362c7fe483d31; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162e0fe485dd162; QUAD $0x25f16202c172487d; QUAD $0x481df1620dc17248; QUAD $0x6f487e716216c172; QUAD $0xe8cb25486d7362c9; QUAD $0x6296c4254825d362; QUAD $0x7dd162c1fe487dd1; QUAD $0x6f487e7162c0fe48; QUAD $0xc5724815b1621862; QUAD $0x13c572480db16211; QUAD $0x620ad5724805b162; QUAD $0xc16296ef25480d53; QUAD $0x4045a162fdfe4045; QUAD $0xc07248159162f8fe; QUAD $0x12c072480d916207; QUAD $0x6203d07248059162; QUAD $0xc16296ef25480d53; QUAD $0x48455162fdfe4045; QUAD $0xcc6f487e7162c4fe; QUAD $0x6206c472482df162; QUAD $0xf1620bc4724825f1; QUAD $0x55736219c472481d; QUAD $0x483d1162cace2548; QUAD $0xd42548255362c0fe; QUAD $0x62c1fe483d516296; QUAD $0x65d162c2fe483d51; QUAD $0x724845f162d8fe48; QUAD $0xc0724825f16202c0; QUAD $0x16c072481df1620d; QUAD $0x7362c86f487e7162; QUAD $0x25d362e8ca254875; QUAD $0x4845d16296fc2548; QUAD $0xf8fe4845d162f9fe; QUAD $0x6219626f487e7162; QUAD $0xb16211c6724815b1; QUAD $0x05b16213c672480d; QUAD $0x480d53620ad67248; QUAD $0xfe403d416296ef25; QUAD $0x62c1fe403d2162c5; QUAD $0x916207c172481591; QUAD $0x05916212c172480d; QUAD $0x480d536203d17248; QUAD $0xfe403d416296ef25; QUAD $0x62c4fe484d5162c5; QUAD $0x2df162cb6f487e71; QUAD $0x4825f16206c37248; QUAD $0x72481df1620bc372; QUAD $0xcd25485d736219c3; QUAD $0x62c1fe483d1162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xd0fe486dd162c2fe; QUAD $0x6202c772484df162; QUAD $0xf1620dc7724825f1; QUAD $0x7e716216c772481d; QUAD $0x25487d7362cf6f48; QUAD $0xf4254825d362e8c9; QUAD $0x62f1fe484dd16296; QUAD $0x7e7162f0fe484dd1; QUAD $0x4815b1621a626f48; QUAD $0x72480db16211c772; QUAD $0xd7724805b16213c7; QUAD $0x96ef25480d53620a; QUAD $0x2162cdfe40354162; QUAD $0x48159162cafe4035; QUAD $0x72480d916207c272; QUAD $0xd2724805916212c2; QUAD $0x96ef25480d536203; QUAD $0x5162cdfe40354162; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x1162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x11c072481591621b; QUAD $0x6213c072480d9162; QUAD $0x53620ad072480591; QUAD $0x2d416296ef25480d; QUAD $0xfe402d2162d5fe40; QUAD $0x07c37248159162d3; QUAD $0x6212c372480d9162; QUAD $0x536203d372480591; QUAD $0x2d416296ef25480d; QUAD $0xfe485d5162d5fe40; QUAD $0x62c96f487e7162c4; QUAD $0xf16206c172482df1; QUAD $0x1df1620bc1724825; QUAD $0x486d736219c17248; QUAD $0xfe483d1162cacb25; QUAD $0x96d42548255362c3; QUAD $0x5162c1fe483d5162; QUAD $0x487dd162c2fe483d; QUAD $0xc572485df162c0fe; QUAD $0x0dc5724825f16202; QUAD $0x6216c572481df162; QUAD $0x4d7362cd6f487e71; QUAD $0x4825d362e8cf2548; QUAD $0xfe485dd16296e425; QUAD $0x62e0fe485dd162e1; QUAD $0x91621c626f487e71; QUAD $0x0d916211c1724815; QUAD $0x4805916213c17248; QUAD $0x25480d53620ad172; QUAD $0xddfe4025416296ef; QUAD $0x9162dcfe40252162; QUAD $0x0d916207c4724815; QUAD $0x4805916212c47248; QUAD $0x25480d536203d472; QUAD $0xddfe4025416296ef; QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; QUAD $0x72481591621d626f; QUAD $0xc272480d916211c2; QUAD $0x0ad2724805916213; QUAD $0x6296ef25480d5362; QUAD $0x1d2162e5fe401d41; QUAD $0x7248159162e5fe40; QUAD $0xc572480d916207c5; QUAD $0x03d5724805916212; QUAD $0x6296ef25480d5362; QUAD $0x6d5162e5fe401d41; QUAD $0x6f487e7162c4fe48; QUAD $0x06c772482df162cf; QUAD $0x620bc7724825f162; QUAD $0x736219c772481df1; QUAD $0x3d1162cac925487d; QUAD $0x2548255362c5fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x486df162f0fe484d; QUAD $0x724825f16202c372; QUAD $0xc372481df1620dc3; QUAD $0x62cb6f487e716216; QUAD $0xd362e8cd25485d73; QUAD $0x6dd16296d4254825; QUAD $0xfe486dd162d1fe48; QUAD $0x1e626f487e7162d0; QUAD $0x6211c37248159162; QUAD $0x916213c372480d91; QUAD $0x0d53620ad3724805; QUAD $0x4015416296ef2548; QUAD $0xeefe40152162edfe; QUAD $0x6207c67248159162; QUAD $0x916212c672480d91; QUAD $0x0d536203d6724805; QUAD $0x4015416296ef2548; QUAD $0xc4fe48755162edfe; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d1162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x1591621f626f487e; QUAD $0x480d916211c47248; QUAD $0x724805916213c472; QUAD $0xef25480d53620ad4; QUAD $0x62f5fe400d416296; QUAD $0x159162f7fe400d21; QUAD $0x480d916207c77248; QUAD $0x724805916212c772; QUAD $0xef25480d536203d7; QUAD $0x62f5fe400d416296; QUAD $0x7e7162c4fe487d51; QUAD $0x72482df162cd6f48; QUAD $0xc5724825f16206c5; QUAD $0x19c572481df1620b; QUAD $0x62cacf25484d7362; QUAD $0x255362c7fe483d11; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162e0fe485dd162; QUAD $0x25f16202c172487d; QUAD $0x481df1620dc17248; QUAD $0x6f487e716216c172; QUAD $0xe8cb25486d7362c9; QUAD $0x6296c4254825d362; QUAD $0x7dd162c1fe487dd1; QUAD $0x6f487e7162c0fe48; QUAD $0xc572481591622062; QUAD $0x13c572480d916211; QUAD $0x620ad57248059162; QUAD $0x416296ef25480d53; QUAD $0x40050162fdfe4005; QUAD $0xc0724815b162f8fe; QUAD $0x12c072480db16207; QUAD $0x6203d0724805b162; QUAD $0x416296ef25480d53; QUAD $0x48455162fdfe4005; QUAD $0xcc6f487e7162c4fe; QUAD $0x6206c472482df162; QUAD $0xf1620bc4724825f1; QUAD $0x55736219c472481d; QUAD $0x483d3162cace2548; QUAD $0xd42548255362c0fe; QUAD $0x62c1fe483d516296; QUAD $0x65d162c2fe483d51; QUAD $0x724845f162d8fe48; QUAD $0xc0724825f16202c0; QUAD $0x16c072481df1620d; QUAD $0x7362c86f487e7162; QUAD $0x25d362e8ca254875; QUAD $0x4845d16296fc2548; QUAD $0xf8fe4845d162f9fe; QUAD $0x6221626f487e7162; QUAD $0x916211c672481591; QUAD $0x05916213c672480d; QUAD $0x480d53620ad67248; QUAD $0xfe407dc16296ef25; QUAD $0x62c1fe407d8162c5; QUAD $0xb16207c1724815b1; QUAD $0x05b16212c172480d; QUAD $0x480d536203d17248; QUAD $0xfe407dc16296ef25; QUAD $0x62c4fe484d5162c5; QUAD $0x2df162cb6f487e71; QUAD $0x4825f16206c37248; QUAD $0x72481df1620bc372; QUAD $0xcd25485d736219c3; QUAD $0x62c1fe483d3162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xd0fe486dd162c2fe; QUAD $0x6202c772484df162; QUAD $0xf1620dc7724825f1; QUAD $0x7e716216c772481d; QUAD $0x25487d7362cf6f48; QUAD $0xf4254825d362e8c9; QUAD $0x62f1fe484dd16296; QUAD $0x7e7162f0fe484dd1; QUAD $0x4815916222626f48; QUAD $0x72480d916211c772; QUAD $0xd7724805916213c7; QUAD $0x96ef25480d53620a; QUAD $0x8162cdfe4075c162; QUAD $0x4815b162cafe4075; QUAD $0x72480db16207c272; QUAD $0xd2724805b16212c2; QUAD $0x96ef25480d536203; QUAD $0x5162cdfe4075c162; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x3162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x11c0724815b16223; QUAD $0x6213c072480db162; QUAD $0x53620ad0724805b1; QUAD $0x6dc16296ef25480d; QUAD $0xfe406d8162d5fe40; QUAD $0x07c3724815b162d3; QUAD $0x6212c372480db162; QUAD $0x536203d3724805b1; QUAD $0x6dc16296ef25480d; QUAD $0xfe485d5162d5fe40; QUAD $0x62c96f487e7162c4; QUAD $0xf16206c172482df1; QUAD $0x1df1620bc1724825; QUAD $0x486d736219c17248; QUAD $0xfe483d3162cacb25; QUAD $0x96d42548255362c3; QUAD $0x5162c1fe483d5162; QUAD $0x487dd162c2fe483d; QUAD $0xc572485df162c0fe; QUAD $0x0dc5724825f16202; QUAD $0x6216c572481df162; QUAD $0x4d7362cd6f487e71; QUAD $0x4825d362e8cf2548; QUAD $0xfe485dd16296e425; QUAD $0x62e0fe485dd162e1; QUAD $0xb16224626f487e71; QUAD $0x0db16211c1724815; QUAD $0x4805b16213c17248; QUAD $0x25480d53620ad172; QUAD $0xddfe4065c16296ef; QUAD $0xb162dcfe40658162; QUAD $0x0db16207c4724815; QUAD $0x4805b16212c47248; QUAD $0x25480d536203d472; QUAD $0xddfe4065c16296ef; QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; QUAD $0x724815b16225626f; QUAD $0xc272480db16211c2; QUAD $0x0ad2724805b16213; QUAD $0x6296ef25480d5362; QUAD $0x5d8162e5fe405dc1; QUAD $0x724815b162e5fe40; QUAD $0xc572480db16207c5; QUAD $0x03d5724805b16212; QUAD $0x6296ef25480d5362; QUAD $0x6d5162e5fe405dc1; QUAD $0x6f487e7162c4fe48; QUAD $0x06c772482df162cf; QUAD $0x620bc7724825f162; QUAD $0x736219c772481df1; QUAD $0x3d3162cac925487d; QUAD $0x2548255362c5fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x486df162f0fe484d; QUAD $0x724825f16202c372; QUAD $0xc372481df1620dc3; QUAD $0x62cb6f487e716216; QUAD $0xd362e8cd25485d73; QUAD $0x6dd16296d4254825; QUAD $0xfe486dd162d1fe48; QUAD $0x26626f487e7162d0; QUAD $0x6211c3724815b162; QUAD $0xb16213c372480db1; QUAD $0x0d53620ad3724805; QUAD $0x4055c16296ef2548; QUAD $0xeefe40558162edfe; QUAD $0x6207c6724815b162; QUAD $0xb16212c672480db1; QUAD $0x0d536203d6724805; QUAD $0x4055c16296ef2548; QUAD $0xc4fe48755162edfe; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d3162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x15b16227626f487e; QUAD $0x480db16211c47248; QUAD $0x724805b16213c472; QUAD $0xef25480d53620ad4; QUAD $0x62f5fe404dc16296; QUAD $0x15b162f7fe404d81; QUAD $0x480db16207c77248; QUAD $0x724805b16212c772; QUAD $0xef25480d536203d7; QUAD $0x62f5fe404dc16296; QUAD $0x7e7162c4fe487d51; QUAD $0x72482df162cd6f48; QUAD $0xc5724825f16206c5; QUAD $0x19c572481df1620b; QUAD $0x62cacf25484d7362; QUAD $0x255362c7fe483d31; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162e0fe485dd162; QUAD $0x25f16202c172487d; QUAD $0x481df1620dc17248; QUAD $0x6f487e716216c172; QUAD $0xe8cb25486d7362c9; QUAD $0x6296c4254825d362; QUAD $0x7dd162c1fe487dd1; QUAD $0x6f487e7162c0fe48; QUAD $0xc5724815b1622862; QUAD $0x13c572480db16211; QUAD $0x620ad5724805b162; QUAD $0xc16296ef25480d53; QUAD $0x4045a162fdfe4045; QUAD $0xc07248159162f8fe; QUAD $0x12c072480d916207; QUAD $0x6203d07248059162; QUAD $0xc16296ef25480d53; QUAD $0x48455162fdfe4045; QUAD $0xcc6f487e7162c4fe; QUAD $0x6206c472482df162; QUAD $0xf1620bc4724825f1; QUAD $0x55736219c472481d; QUAD $0x483d1162cace2548; QUAD $0xd42548255362c0fe; QUAD $0x62c1fe483d516296; QUAD $0x65d162c2fe483d51; QUAD $0x724845f162d8fe48; QUAD $0xc0724825f16202c0; QUAD $0x16c072481df1620d; QUAD $0x7362c86f487e7162; QUAD $0x25d362e8ca254875; QUAD $0x4845d16296fc2548; QUAD $0xf8fe4845d162f9fe; QUAD $0x6229626f487e7162; QUAD $0xb16211c6724815b1; QUAD $0x05b16213c672480d; QUAD $0x480d53620ad67248; QUAD $0xfe403d416296ef25; QUAD $0x62c1fe403d2162c5; QUAD $0x916207c172481591; QUAD $0x05916212c172480d; QUAD $0x480d536203d17248; QUAD $0xfe403d416296ef25; QUAD $0x62c4fe484d5162c5; QUAD $0x2df162cb6f487e71; QUAD $0x4825f16206c37248; QUAD $0x72481df1620bc372; QUAD $0xcd25485d736219c3; QUAD $0x62c1fe483d1162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xd0fe486dd162c2fe; QUAD $0x6202c772484df162; QUAD $0xf1620dc7724825f1; QUAD $0x7e716216c772481d; QUAD $0x25487d7362cf6f48; QUAD $0xf4254825d362e8c9; QUAD $0x62f1fe484dd16296; QUAD $0x7e7162f0fe484dd1; QUAD $0x4815b1622a626f48; QUAD $0x72480db16211c772; QUAD $0xd7724805b16213c7; QUAD $0x96ef25480d53620a; QUAD $0x2162cdfe40354162; QUAD $0x48159162cafe4035; QUAD $0x72480d916207c272; QUAD $0xd2724805916212c2; QUAD $0x96ef25480d536203; QUAD $0x5162cdfe40354162; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x1162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x11c072481591622b; QUAD $0x6213c072480d9162; QUAD $0x53620ad072480591; QUAD $0x2d416296ef25480d; QUAD $0xfe402d2162d5fe40; QUAD $0x07c37248159162d3; QUAD $0x6212c372480d9162; QUAD $0x536203d372480591; QUAD $0x2d416296ef25480d; QUAD $0xfe485d5162d5fe40; QUAD $0x62c96f487e7162c4; QUAD $0xf16206c172482df1; QUAD $0x1df1620bc1724825; QUAD $0x486d736219c17248; QUAD $0xfe483d1162cacb25; QUAD $0x96d42548255362c3; QUAD $0x5162c1fe483d5162; QUAD $0x487dd162c2fe483d; QUAD $0xc572485df162c0fe; QUAD $0x0dc5724825f16202; QUAD $0x6216c572481df162; QUAD $0x4d7362cd6f487e71; QUAD $0x4825d362e8cf2548; QUAD $0xfe485dd16296e425; QUAD $0x62e0fe485dd162e1; QUAD $0x91622c626f487e71; QUAD $0x0d916211c1724815; QUAD $0x4805916213c17248; QUAD $0x25480d53620ad172; QUAD $0xddfe4025416296ef; QUAD $0x9162dcfe40252162; QUAD $0x0d916207c4724815; QUAD $0x4805916212c47248; QUAD $0x25480d536203d472; QUAD $0xddfe4025416296ef; QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; QUAD $0x72481591622d626f; QUAD $0xc272480d916211c2; QUAD $0x0ad2724805916213; QUAD $0x6296ef25480d5362; QUAD $0x1d2162e5fe401d41; QUAD $0x7248159162e5fe40; QUAD $0xc572480d916207c5; QUAD $0x03d5724805916212; QUAD $0x6296ef25480d5362; QUAD $0x6d5162e5fe401d41; QUAD $0x6f487e7162c4fe48; QUAD $0x06c772482df162cf; QUAD $0x620bc7724825f162; QUAD $0x736219c772481df1; QUAD $0x3d1162cac925487d; QUAD $0x2548255362c5fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x486df162f0fe484d; QUAD $0x724825f16202c372; QUAD $0xc372481df1620dc3; QUAD $0x62cb6f487e716216; QUAD $0xd362e8cd25485d73; QUAD $0x6dd16296d4254825; QUAD $0xfe486dd162d1fe48; QUAD $0x2e626f487e7162d0; QUAD $0x6211c37248159162; QUAD $0x916213c372480d91; QUAD $0x0d53620ad3724805; QUAD $0x4015416296ef2548; QUAD $0xeefe40152162edfe; QUAD $0x6207c67248159162; QUAD $0x916212c672480d91; QUAD $0x0d536203d6724805; QUAD $0x4015416296ef2548; QUAD $0xc4fe48755162edfe; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d1162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x1591622f626f487e; QUAD $0x480d916211c47248; QUAD $0x724805916213c472; QUAD $0xef25480d53620ad4; QUAD $0x62f5fe400d416296; QUAD $0x159162f7fe400d21; QUAD $0x480d916207c77248; QUAD $0x724805916212c772; QUAD $0xef25480d536203d7; QUAD $0x62f5fe400d416296; QUAD $0x7e7162c4fe487d51; QUAD $0x72482df162cd6f48; QUAD $0xc5724825f16206c5; QUAD $0x19c572481df1620b; QUAD $0x62cacf25484d7362; QUAD $0x255362c7fe483d11; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162e0fe485dd162; QUAD $0x25f16202c172487d; QUAD $0x481df1620dc17248; QUAD $0x6f487e716216c172; QUAD $0xe8cb25486d7362c9; QUAD $0x6296c4254825d362; QUAD $0x7dd162c1fe487dd1; QUAD $0x6f487e7162c0fe48; QUAD $0xc572481591623062; QUAD $0x13c572480d916211; QUAD $0x620ad57248059162; QUAD $0x416296ef25480d53; QUAD $0x40050162fdfe4005; QUAD $0xc0724815b162f8fe; QUAD $0x12c072480db16207; QUAD $0x6203d0724805b162; QUAD $0x416296ef25480d53; QUAD $0x01ee8348fdfe4005 + JE lastLoop + ADDQ $8, R13 + MOVQ (R13), R14 + QUAD $0x7162c4fe48455162; QUAD $0x482df162cc6f487e; QUAD $0x724825f16206c472; QUAD $0xc472481df1620bc4; QUAD $0xcace254855736219; QUAD $0x5362c0fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62d8fe4865d162c2; QUAD $0xf16202c0724845f1; QUAD $0x1df1620dc0724825; QUAD $0x487e716216c07248; QUAD $0xca2548757362c86f; QUAD $0x96fc254825d362e8; QUAD $0xd162f9fe4845d162; QUAD $0x487e7162f8fe4845; WORD $0x626f; BYTE $0x31 + TESTQ $(1<<0), R14 + JE skipNext0 + MOVQ 0*24(AX), R9 + LONG $0x487cc162; WORD $0x0410; BYTE $0x09 + +skipNext0: + QUAD $0x7162c4fe484d5162; QUAD $0x482df162cb6f487e; QUAD $0x724825f16206c372; QUAD $0xc372481df1620bc3; QUAD $0xcacd25485d736219; QUAD $0x5362c1fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62d0fe486dd162c2; QUAD $0xf16202c772484df1; QUAD $0x1df1620dc7724825; QUAD $0x487e716216c77248; QUAD $0xc925487d7362cf6f; QUAD $0x96f4254825d362e8; QUAD $0xd162f1fe484dd162; QUAD $0x487e7162f0fe484d; WORD $0x626f; BYTE $0x32 + TESTQ $(1<<1), R14 + JE skipNext1 + MOVQ 1*24(AX), R9 + LONG $0x487cc162; WORD $0x0c10; BYTE $0x09 + +skipNext1: + QUAD $0x7162c4fe48555162; QUAD $0x482df162ca6f487e; QUAD $0x724825f16206c272; QUAD $0xc272481df1620bc2; QUAD $0xcacc254865736219; QUAD $0x5362c2fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62c8fe4875d162c2; QUAD $0xf16202c6724855f1; QUAD $0x1df1620dc6724825; QUAD $0x487e716216c67248; QUAD $0xc82548457362ce6f; QUAD $0x96ec254825d362e8; QUAD $0xd162e9fe4855d162; QUAD $0x487e7162e8fe4855; WORD $0x626f; BYTE $0x33 + TESTQ $(1<<2), R14 + JE skipNext2 + MOVQ 2*24(AX), R9 + LONG $0x487cc162; WORD $0x1410; BYTE $0x09 + +skipNext2: + QUAD $0x7162c4fe485d5162; QUAD $0x482df162c96f487e; QUAD $0x724825f16206c172; QUAD $0xc172481df1620bc1; QUAD $0xcacb25486d736219; QUAD $0x5362c3fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62c0fe487dd162c2; QUAD $0xf16202c572485df1; QUAD $0x1df1620dc5724825; QUAD $0x487e716216c57248; QUAD $0xcf25484d7362cd6f; QUAD $0x96e4254825d362e8; QUAD $0xd162e1fe485dd162; QUAD $0x487e7162e0fe485d; WORD $0x626f; BYTE $0x34 + TESTQ $(1<<3), R14 + JE skipNext3 + MOVQ 3*24(AX), R9 + LONG $0x487cc162; WORD $0x1c10; BYTE $0x09 + +skipNext3: + QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; WORD $0x626f; BYTE $0x35 + TESTQ $(1<<4), R14 + JE skipNext4 + MOVQ 4*24(AX), R9 + LONG $0x487cc162; WORD $0x2410; BYTE $0x09 + +skipNext4: + QUAD $0x7162c4fe486d5162; QUAD $0x482df162cf6f487e; QUAD $0x724825f16206c772; QUAD $0xc772481df1620bc7; QUAD $0xcac925487d736219; QUAD $0x5362c5fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f0fe484dd162c2; QUAD $0xf16202c372486df1; QUAD $0x1df1620dc3724825; QUAD $0x487e716216c37248; QUAD $0xcd25485d7362cb6f; QUAD $0x96d4254825d362e8; QUAD $0xd162d1fe486dd162; QUAD $0x487e7162d0fe486d; WORD $0x626f; BYTE $0x36 + TESTQ $(1<<5), R14 + JE skipNext5 + MOVQ 5*24(AX), R9 + LONG $0x487cc162; WORD $0x2c10; BYTE $0x09 + +skipNext5: + QUAD $0x7162c4fe48755162; QUAD $0x482df162ce6f487e; QUAD $0x724825f16206c672; QUAD $0xc672481df1620bc6; QUAD $0xcac8254845736219; QUAD $0x5362c6fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62e8fe4855d162c2; QUAD $0xf16202c2724875f1; QUAD $0x1df1620dc2724825; QUAD $0x487e716216c27248; QUAD $0xcc2548657362ca6f; QUAD $0x96cc254825d362e8; QUAD $0xd162c9fe4875d162; QUAD $0x487e7162c8fe4875; WORD $0x626f; BYTE $0x37 + TESTQ $(1<<6), R14 + JE skipNext6 + MOVQ 6*24(AX), R9 + LONG $0x487cc162; WORD $0x3410; BYTE $0x09 + +skipNext6: + QUAD $0x7162c4fe487d5162; QUAD $0x482df162cd6f487e; QUAD $0x724825f16206c572; QUAD $0xc572481df1620bc5; QUAD $0xcacf25484d736219; QUAD $0x5362c7fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62e0fe485dd162c2; QUAD $0xf16202c172487df1; QUAD $0x1df1620dc1724825; QUAD $0x487e716216c17248; QUAD $0xcb25486d7362c96f; QUAD $0x96c4254825d362e8; QUAD $0xd162c1fe487dd162; QUAD $0x487e7162c0fe487d; WORD $0x626f; BYTE $0x38 + TESTQ $(1<<7), R14 + JE skipNext7 + MOVQ 7*24(AX), R9 + LONG $0x487cc162; WORD $0x3c10; BYTE $0x09 + +skipNext7: + QUAD $0x7162c4fe48455162; QUAD $0x482df162cc6f487e; QUAD $0x724825f16206c472; QUAD $0xc472481df1620bc4; QUAD $0xcace254855736219; QUAD $0x5362c0fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62d8fe4865d162c2; QUAD $0xf16202c0724845f1; QUAD $0x1df1620dc0724825; QUAD $0x487e716216c07248; QUAD $0xca2548757362c86f; QUAD $0x96fc254825d362e8; QUAD $0xd162f9fe4845d162; QUAD $0x487e7162f8fe4845; WORD $0x626f; BYTE $0x39 + TESTQ $(1<<8), R14 + JE skipNext8 + MOVQ 8*24(AX), R9 + LONG $0x487c4162; WORD $0x0410; BYTE $0x09 + +skipNext8: + QUAD $0x7162c4fe484d5162; QUAD $0x482df162cb6f487e; QUAD $0x724825f16206c372; QUAD $0xc372481df1620bc3; QUAD $0xcacd25485d736219; QUAD $0x5362c1fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62d0fe486dd162c2; QUAD $0xf16202c772484df1; QUAD $0x1df1620dc7724825; QUAD $0x487e716216c77248; QUAD $0xc925487d7362cf6f; QUAD $0x96f4254825d362e8; QUAD $0xd162f1fe484dd162; QUAD $0x487e7162f0fe484d; WORD $0x626f; BYTE $0x3a + TESTQ $(1<<9), R14 + JE skipNext9 + MOVQ 9*24(AX), R9 + LONG $0x487c4162; WORD $0x0c10; BYTE $0x09 + +skipNext9: + QUAD $0x7162c4fe48555162; QUAD $0x482df162ca6f487e; QUAD $0x724825f16206c272; QUAD $0xc272481df1620bc2; QUAD $0xcacc254865736219; QUAD $0x5362c2fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62c8fe4875d162c2; QUAD $0xf16202c6724855f1; QUAD $0x1df1620dc6724825; QUAD $0x487e716216c67248; QUAD $0xc82548457362ce6f; QUAD $0x96ec254825d362e8; QUAD $0xd162e9fe4855d162; QUAD $0x487e7162e8fe4855; WORD $0x626f; BYTE $0x3b + TESTQ $(1<<10), R14 + JE skipNext10 + MOVQ 10*24(AX), R9 + LONG $0x487c4162; WORD $0x1410; BYTE $0x09 + +skipNext10: + QUAD $0x7162c4fe485d5162; QUAD $0x482df162c96f487e; QUAD $0x724825f16206c172; QUAD $0xc172481df1620bc1; QUAD $0xcacb25486d736219; QUAD $0x5362c3fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62c0fe487dd162c2; QUAD $0xf16202c572485df1; QUAD $0x1df1620dc5724825; QUAD $0x487e716216c57248; QUAD $0xcf25484d7362cd6f; QUAD $0x96e4254825d362e8; QUAD $0xd162e1fe485dd162; QUAD $0x487e7162e0fe485d; WORD $0x626f; BYTE $0x3c + TESTQ $(1<<11), R14 + JE skipNext11 + MOVQ 11*24(AX), R9 + LONG $0x487c4162; WORD $0x1c10; BYTE $0x09 + +skipNext11: + QUAD $0x7162c4fe48655162; QUAD $0x482df162c86f487e; QUAD $0x724825f16206c072; QUAD $0xc072481df1620bc0; QUAD $0xcaca254875736219; QUAD $0x5362c4fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f8fe4845d162c2; QUAD $0xf16202c4724865f1; QUAD $0x1df1620dc4724825; QUAD $0x487e716216c47248; QUAD $0xce2548557362cc6f; QUAD $0x96dc254825d362e8; QUAD $0xd162d9fe4865d162; QUAD $0x487e7162d8fe4865; WORD $0x626f; BYTE $0x3d + TESTQ $(1<<12), R14 + JE skipNext12 + MOVQ 12*24(AX), R9 + LONG $0x487c4162; WORD $0x2410; BYTE $0x09 + +skipNext12: + QUAD $0x7162c4fe486d5162; QUAD $0x482df162cf6f487e; QUAD $0x724825f16206c772; QUAD $0xc772481df1620bc7; QUAD $0xcac925487d736219; QUAD $0x5362c5fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62f0fe484dd162c2; QUAD $0xf16202c372486df1; QUAD $0x1df1620dc3724825; QUAD $0x487e716216c37248; QUAD $0xcd25485d7362cb6f; QUAD $0x96d4254825d362e8; QUAD $0xd162d1fe486dd162; QUAD $0x487e7162d0fe486d; WORD $0x626f; BYTE $0x3e + TESTQ $(1<<13), R14 + JE skipNext13 + MOVQ 13*24(AX), R9 + LONG $0x487c4162; WORD $0x2c10; BYTE $0x09 + +skipNext13: + QUAD $0x7162c4fe48755162; QUAD $0x482df162ce6f487e; QUAD $0x724825f16206c672; QUAD $0xc672481df1620bc6; QUAD $0xcac8254845736219; QUAD $0x5362c6fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62e8fe4855d162c2; QUAD $0xf16202c2724875f1; QUAD $0x1df1620dc2724825; QUAD $0x487e716216c27248; QUAD $0xcc2548657362ca6f; QUAD $0x96cc254825d362e8; QUAD $0xd162c9fe4875d162; QUAD $0x487e7162c8fe4875; WORD $0x626f; BYTE $0x3f + TESTQ $(1<<14), R14 + JE skipNext14 + MOVQ 14*24(AX), R9 + LONG $0x487c4162; WORD $0x3410; BYTE $0x09 + +skipNext14: + QUAD $0x7162c4fe487d5162; QUAD $0x482df162cd6f487e; QUAD $0x724825f16206c572; QUAD $0xc572481df1620bc5; QUAD $0xcacf25484d736219; QUAD $0x5362c7fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62e0fe485dd162c2; QUAD $0xf16202c172487df1; QUAD $0x1df1620dc1724825; QUAD $0x487e716216c17248; QUAD $0xcb25486d7362c96f; QUAD $0x96c4254825d362e8; QUAD $0xd162c1fe487dd162; QUAD $0x487e7162c0fe487d; WORD $0x626f; BYTE $0x40 + TESTQ $(1<<15), R14 + JE skipNext15 + MOVQ 15*24(AX), R9 + LONG $0x487c4162; WORD $0x3c10; BYTE $0x09 + +skipNext15: + QUAD $0xd162d86f487e7162; QUAD $0x7dd16224046f487e; QUAD $0x6f487e7162c3fe49; QUAD $0x244c6f487ed162d9; QUAD $0x62cbfe4975d16201; QUAD $0x7ed162da6f487e71; QUAD $0x6dd1620224546f48; QUAD $0x6f487e7162d3fe49; QUAD $0x245c6f487ed162db; QUAD $0x62dbfe4965d16203; QUAD $0x7ed162dc6f487e71; QUAD $0x5dd1620424646f48; QUAD $0x6f487e7162e3fe49; QUAD $0x246c6f487ed162dd; QUAD $0x62ebfe4955d16205; QUAD $0x7ed162de6f487e71; QUAD $0x4dd1620624746f48; QUAD $0x6f487e7162f3fe49; QUAD $0x247c6f487ed162df; QUAD $0xc4fbfe4945d16207; LONG $0xce92fbc1 + JMP lloop + +lastLoop: + QUAD $0x7162c4fe48455162; QUAD $0x482df162cc6f487e; QUAD $0x724825f16206c472; QUAD $0xc472481df1620bc4; QUAD $0xcace254855736219; QUAD $0x5362c0fe483d3162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62d8fe4865d162c2; QUAD $0xf16202c0724845f1; QUAD $0x1df1620dc0724825; QUAD $0x487e716216c07248; QUAD $0xca2548757362c86f; QUAD $0x96fc254825d362e8; QUAD $0xd162f9fe4845d162; QUAD $0x487e7162f8fe4845; QUAD $0xfe484d516231626f; QUAD $0x62cb6f487e7162c4; QUAD $0xf16206c372482df1; QUAD $0x1df1620bc3724825; QUAD $0x485d736219c37248; QUAD $0xfe483d3162cacd25; QUAD $0x96d42548255362c1; QUAD $0x5162c1fe483d5162; QUAD $0x486dd162c2fe483d; QUAD $0xc772484df162d0fe; QUAD $0x0dc7724825f16202; QUAD $0x6216c772481df162; QUAD $0x7d7362cf6f487e71; QUAD $0x4825d362e8c92548; QUAD $0xfe484dd16296f425; QUAD $0x62f0fe484dd162f1; QUAD $0x516232626f487e71; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x3162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x62c4fe485d516233; QUAD $0x2df162c96f487e71; QUAD $0x4825f16206c17248; QUAD $0x72481df1620bc172; QUAD $0xcb25486d736219c1; QUAD $0x62c3fe483d3162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xc0fe487dd162c2fe; QUAD $0x6202c572485df162; QUAD $0xf1620dc5724825f1; QUAD $0x7e716216c572481d; QUAD $0x25484d7362cd6f48; QUAD $0xe4254825d362e8cf; QUAD $0x62e1fe485dd16296; QUAD $0x7e7162e0fe485dd1; QUAD $0x4865516234626f48; QUAD $0xc86f487e7162c4fe; QUAD $0x6206c072482df162; QUAD $0xf1620bc0724825f1; QUAD $0x75736219c072481d; QUAD $0x483d3162caca2548; QUAD $0xd42548255362c4fe; QUAD $0x62c1fe483d516296; QUAD $0x45d162c2fe483d51; QUAD $0x724865f162f8fe48; QUAD $0xc4724825f16202c4; QUAD $0x16c472481df1620d; QUAD $0x7362cc6f487e7162; QUAD $0x25d362e8ce254855; QUAD $0x4865d16296dc2548; QUAD $0xd8fe4865d162d9fe; QUAD $0x6235626f487e7162; QUAD $0x7e7162c4fe486d51; QUAD $0x72482df162cf6f48; QUAD $0xc7724825f16206c7; QUAD $0x19c772481df1620b; QUAD $0x62cac925487d7362; QUAD $0x255362c5fe483d31; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162f0fe484dd162; QUAD $0x25f16202c372486d; QUAD $0x481df1620dc37248; QUAD $0x6f487e716216c372; QUAD $0xe8cd25485d7362cb; QUAD $0x6296d4254825d362; QUAD $0x6dd162d1fe486dd1; QUAD $0x6f487e7162d0fe48; QUAD $0xc4fe487551623662; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d3162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x7d516237626f487e; QUAD $0x6f487e7162c4fe48; QUAD $0x06c572482df162cd; QUAD $0x620bc5724825f162; QUAD $0x736219c572481df1; QUAD $0x3d3162cacf25484d; QUAD $0x2548255362c7fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x487df162e0fe485d; QUAD $0x724825f16202c172; QUAD $0xc172481df1620dc1; QUAD $0x62c96f487e716216; QUAD $0xd362e8cb25486d73; QUAD $0x7dd16296c4254825; QUAD $0xfe487dd162c1fe48; QUAD $0x38626f487e7162c0; QUAD $0x7162c4fe48455162; QUAD $0x482df162cc6f487e; QUAD $0x724825f16206c472; QUAD $0xc472481df1620bc4; QUAD $0xcace254855736219; QUAD $0x5362c0fe483d1162; QUAD $0x3d516296d4254825; QUAD $0xfe483d5162c1fe48; QUAD $0x62d8fe4865d162c2; QUAD $0xf16202c0724845f1; QUAD $0x1df1620dc0724825; QUAD $0x487e716216c07248; QUAD $0xca2548757362c86f; QUAD $0x96fc254825d362e8; QUAD $0xd162f9fe4845d162; QUAD $0x487e7162f8fe4845; QUAD $0xfe484d516239626f; QUAD $0x62cb6f487e7162c4; QUAD $0xf16206c372482df1; QUAD $0x1df1620bc3724825; QUAD $0x485d736219c37248; QUAD $0xfe483d1162cacd25; QUAD $0x96d42548255362c1; QUAD $0x5162c1fe483d5162; QUAD $0x486dd162c2fe483d; QUAD $0xc772484df162d0fe; QUAD $0x0dc7724825f16202; QUAD $0x6216c772481df162; QUAD $0x7d7362cf6f487e71; QUAD $0x4825d362e8c92548; QUAD $0xfe484dd16296f425; QUAD $0x62f0fe484dd162f1; QUAD $0x51623a626f487e71; QUAD $0x487e7162c4fe4855; QUAD $0xc272482df162ca6f; QUAD $0x0bc2724825f16206; QUAD $0x6219c272481df162; QUAD $0x1162cacc25486573; QUAD $0x48255362c2fe483d; QUAD $0xfe483d516296d425; QUAD $0x62c2fe483d5162c1; QUAD $0x55f162c8fe4875d1; QUAD $0x4825f16202c67248; QUAD $0x72481df1620dc672; QUAD $0xce6f487e716216c6; QUAD $0x62e8c82548457362; QUAD $0xd16296ec254825d3; QUAD $0x4855d162e9fe4855; QUAD $0x626f487e7162e8fe; QUAD $0x62c4fe485d51623b; QUAD $0x2df162c96f487e71; QUAD $0x4825f16206c17248; QUAD $0x72481df1620bc172; QUAD $0xcb25486d736219c1; QUAD $0x62c3fe483d1162ca; QUAD $0x516296d425482553; QUAD $0x483d5162c1fe483d; QUAD $0xc0fe487dd162c2fe; QUAD $0x6202c572485df162; QUAD $0xf1620dc5724825f1; QUAD $0x7e716216c572481d; QUAD $0x25484d7362cd6f48; QUAD $0xe4254825d362e8cf; QUAD $0x62e1fe485dd16296; QUAD $0x7e7162e0fe485dd1; QUAD $0x486551623c626f48; QUAD $0xc86f487e7162c4fe; QUAD $0x6206c072482df162; QUAD $0xf1620bc0724825f1; QUAD $0x75736219c072481d; QUAD $0x483d1162caca2548; QUAD $0xd42548255362c4fe; QUAD $0x62c1fe483d516296; QUAD $0x45d162c2fe483d51; QUAD $0x724865f162f8fe48; QUAD $0xc4724825f16202c4; QUAD $0x16c472481df1620d; QUAD $0x7362cc6f487e7162; QUAD $0x25d362e8ce254855; QUAD $0x4865d16296dc2548; QUAD $0xd8fe4865d162d9fe; QUAD $0x623d626f487e7162; QUAD $0x7e7162c4fe486d51; QUAD $0x72482df162cf6f48; QUAD $0xc7724825f16206c7; QUAD $0x19c772481df1620b; QUAD $0x62cac925487d7362; QUAD $0x255362c5fe483d11; QUAD $0x483d516296d42548; QUAD $0xc2fe483d5162c1fe; QUAD $0xf162f0fe484dd162; QUAD $0x25f16202c372486d; QUAD $0x481df1620dc37248; QUAD $0x6f487e716216c372; QUAD $0xe8cd25485d7362cb; QUAD $0x6296d4254825d362; QUAD $0x6dd162d1fe486dd1; QUAD $0x6f487e7162d0fe48; QUAD $0xc4fe487551623e62; QUAD $0xf162ce6f487e7162; QUAD $0x25f16206c672482d; QUAD $0x481df1620bc67248; QUAD $0x254845736219c672; QUAD $0xc6fe483d1162cac8; QUAD $0x6296d42548255362; QUAD $0x3d5162c1fe483d51; QUAD $0xfe4855d162c2fe48; QUAD $0x02c2724875f162e8; QUAD $0x620dc2724825f162; QUAD $0x716216c272481df1; QUAD $0x48657362ca6f487e; QUAD $0x254825d362e8cc25; QUAD $0xc9fe4875d16296cc; QUAD $0x7162c8fe4875d162; QUAD $0x7d51623f626f487e; QUAD $0x6f487e7162c4fe48; QUAD $0x06c572482df162cd; QUAD $0x620bc5724825f162; QUAD $0x736219c572481df1; QUAD $0x3d1162cacf25484d; QUAD $0x2548255362c7fe48; QUAD $0xc1fe483d516296d4; QUAD $0xd162c2fe483d5162; QUAD $0x487df162e0fe485d; QUAD $0x724825f16202c172; QUAD $0xc172481df1620dc1; QUAD $0x62c96f487e716216; QUAD $0xd362e8cb25486d73; QUAD $0x7dd16296c4254825; QUAD $0xfe487dd162c1fe48; QUAD $0x40626f487e7162c0; QUAD $0xd162d86f487e7162; QUAD $0x7dd16224046f487e; QUAD $0x6f487e7162c3fe49; QUAD $0x244c6f487ed162d9; QUAD $0x62cbfe4975d16201; QUAD $0x7ed162da6f487e71; QUAD $0x6dd1620224546f48; QUAD $0x6f487e7162d3fe49; QUAD $0x245c6f487ed162db; QUAD $0x62dbfe4965d16203; QUAD $0x7ed162dc6f487e71; QUAD $0x5dd1620424646f48; QUAD $0x6f487e7162e3fe49; QUAD $0x246c6f487ed162dd; QUAD $0x62ebfe4955d16205; QUAD $0x7ed162de6f487e71; QUAD $0x4dd1620624746f48; QUAD $0x6f487e7162f3fe49; QUAD $0x247c6f487ed162df; QUAD $0x62fbfe4945d16207; QUAD $0x7ef162077f487ef1; QUAD $0x487ef162014f7f48; QUAD $0x7f487ef16202577f; QUAD $0x677f487ef162035f; QUAD $0x056f7f487ef16204; QUAD $0x6206777f487ef162; LONG $0x7f487ef1; WORD $0x077f + VZEROUPPER + RET + +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x000(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x008(SB)/8, $0x0c0d0e0f08090a0b +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x010(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x018(SB)/8, $0x0c0d0e0f08090a0b +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x020(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x028(SB)/8, $0x0c0d0e0f08090a0b +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x030(SB)/8, $0x0405060700010203 +DATA PSHUFFLE_BYTE_FLIP_MASK<>+0x038(SB)/8, $0x0c0d0e0f08090a0b +GLOBL PSHUFFLE_BYTE_FLIP_MASK<>(SB), 8, $64 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x000(SB)/8, $0x0000000000000000 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x008(SB)/8, $0x0000000000000001 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x010(SB)/8, $0x0000000000000008 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x018(SB)/8, $0x0000000000000009 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x020(SB)/8, $0x0000000000000004 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x028(SB)/8, $0x0000000000000005 +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x030(SB)/8, $0x000000000000000C +DATA PSHUFFLE_TRANSPOSE16_MASK1<>+0x038(SB)/8, $0x000000000000000D +GLOBL PSHUFFLE_TRANSPOSE16_MASK1<>(SB), 8, $64 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x000(SB)/8, $0x0000000000000002 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x008(SB)/8, $0x0000000000000003 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x010(SB)/8, $0x000000000000000A +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x018(SB)/8, $0x000000000000000B +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x020(SB)/8, $0x0000000000000006 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x028(SB)/8, $0x0000000000000007 +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x030(SB)/8, $0x000000000000000E +DATA PSHUFFLE_TRANSPOSE16_MASK2<>+0x038(SB)/8, $0x000000000000000F +GLOBL PSHUFFLE_TRANSPOSE16_MASK2<>(SB), 8, $64 diff --git a/pkg/crypto/sha256/sha256blockAvx512_amd64_test.go b/pkg/crypto/sha256/sha256blockAvx512_amd64_test.go new file mode 100644 index 0000000..2656540 --- /dev/null +++ b/pkg/crypto/sha256/sha256blockAvx512_amd64_test.go @@ -0,0 +1,545 @@ +//go:build !noasm && !appengine && gc +// +build !noasm,!appengine,gc + +/* + * Minio Cloud Storage, (C) 2017 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sha256 + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "hash" + "reflect" + "sync" + "testing" +) + +func TestGoldenAVX512(t *testing.T) { + + if !hasAvx512 { + // t.SkipNow() + return + } + + server := NewAvx512Server() + h512 := NewAvx512(server) + + for _, g := range golden { + h512.Reset() + h512.Write([]byte(g.in)) + digest := h512.Sum([]byte{}) + s := fmt.Sprintf("%x", digest) + if !reflect.DeepEqual(digest, g.out[:]) { + t.Fatalf( + "Sum256 function: sha256(%s) = %s want %s", g.in, s, + hex.EncodeToString(g.out[:]), + ) + } + } +} + +func createInputs(size int) [16][]byte { + input := [16][]byte{} + for i := 0; i < 16; i++ { + input[i] = make([]byte, size) + } + return input +} + +func initDigests() *[512]byte { + digests := [512]byte{} + for i := 0; i < 16; i++ { + binary.LittleEndian.PutUint32(digests[(i+0*16)*4:], init0) + binary.LittleEndian.PutUint32(digests[(i+1*16)*4:], init1) + binary.LittleEndian.PutUint32(digests[(i+2*16)*4:], init2) + binary.LittleEndian.PutUint32(digests[(i+3*16)*4:], init3) + binary.LittleEndian.PutUint32(digests[(i+4*16)*4:], init4) + binary.LittleEndian.PutUint32(digests[(i+5*16)*4:], init5) + binary.LittleEndian.PutUint32(digests[(i+6*16)*4:], init6) + binary.LittleEndian.PutUint32(digests[(i+7*16)*4:], init7) + } + return &digests +} + +func testSha256Avx512(t *testing.T, offset, padding int) [16][]byte { + + if !hasAvx512 { + // t.SkipNow() + return [16][]byte{} + } + + l := uint(len(golden[offset].in)) + extraBlock := uint(0) + if padding == 0 { + extraBlock += 9 + } else { + extraBlock += 64 + } + input := createInputs(int(l + extraBlock)) + for i := 0; i < 16; i++ { + copy(input[i], golden[offset+i].in) + input[i][l] = 0x80 + copy(input[i][l+1:], bytes.Repeat([]byte{0}, padding)) + + // Length in bits. + len := uint64(l) + len <<= 3 + for ii := uint(0); ii < 8; ii++ { + input[i][l+1+uint(padding)+ii] = byte(len >> (56 - 8*ii)) + } + } + mask := make([]uint64, len(input[0])>>6) + for m := range mask { + mask[m] = 0xffff + } + output := blockAvx512(initDigests(), input, mask) + for i := 0; i < 16; i++ { + if bytes.Compare(output[i][:], golden[offset+i].out[:]) != 0 { + t.Fatalf( + "Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, + hex.EncodeToString(output[i][:]), + hex.EncodeToString(golden[offset+i].out[:]), + ) + } + } + return input +} + +func TestAvx512_1Block(t *testing.T) { testSha256Avx512(t, 31, 0) } +func TestAvx512_3Blocks(t *testing.T) { testSha256Avx512(t, 47, 55) } + +func TestAvx512_MixedBlocks(t *testing.T) { + + if !hasAvx512 { + // t.SkipNow() + return + } + + inputSingleBlock := testSha256Avx512(t, 31, 0) + inputMultiBlock := testSha256Avx512(t, 47, 55) + + input := [16][]byte{} + + for i := range input { + if i%2 == 0 { + input[i] = inputMultiBlock[i] + } else { + input[i] = inputSingleBlock[i] + } + } + + mask := [3]uint64{0xffff, 0x5555, 0x5555} + output := blockAvx512(initDigests(), input, mask[:]) + var offset int + for i := 0; i < len(output); i++ { + if i%2 == 0 { + offset = 47 + } else { + offset = 31 + } + if bytes.Compare(output[i][:], golden[offset+i].out[:]) != 0 { + t.Fatalf( + "Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, + hex.EncodeToString(output[i][:]), + hex.EncodeToString(golden[offset+i].out[:]), + ) + } + } +} + +func TestAvx512_MixedWithNilBlocks(t *testing.T) { + + if !hasAvx512 { + // t.SkipNow() + return + } + + inputSingleBlock := testSha256Avx512(t, 31, 0) + inputMultiBlock := testSha256Avx512(t, 47, 55) + + input := [16][]byte{} + + for i := range input { + if i%3 == 0 { + input[i] = inputMultiBlock[i] + } else if i%3 == 1 { + input[i] = inputSingleBlock[i] + } else { + input[i] = nil + } + } + + mask := [3]uint64{0xb6db, 0x9249, 0x9249} + output := blockAvx512(initDigests(), input, mask[:]) + var offset int + for i := 0; i < len(output); i++ { + if i%3 == 2 { // for nil inputs + initvec := [32]byte{ + 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, 0x85, + 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a, + 0x51, 0x0e, 0x52, 0x7f, 0x9b, 0x05, 0x68, 0x8c, + 0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19, + } + if bytes.Compare(output[i][:], initvec[:]) != 0 { + t.Fatalf( + "Sum256 function: sha256 for nil vector = %s want %s", + hex.EncodeToString(output[i][:]), + hex.EncodeToString(initvec[:]), + ) + } + continue + } + if i%3 == 0 { + offset = 47 + } else { + offset = 31 + } + if bytes.Compare(output[i][:], golden[offset+i].out[:]) != 0 { + t.Fatalf( + "Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, + hex.EncodeToString(output[i][:]), + hex.EncodeToString(golden[offset+i].out[:]), + ) + } + } +} + +func TestAvx512Server(t *testing.T) { + + if !hasAvx512 { + // t.SkipNow() + return + } + + const offset = 31 + 16 + server := NewAvx512Server() + + // First block of 64 bytes + for i := 0; i < 16; i++ { + input := make([]byte, 64) + copy(input, golden[offset+i].in) + server.Write(uint64(Avx512ServerUID+i), input) + } + + // Second block of 64 bytes + for i := 0; i < 16; i++ { + input := make([]byte, 64) + copy(input, golden[offset+i].in[64:]) + server.Write(uint64(Avx512ServerUID+i), input) + } + + wg := sync.WaitGroup{} + wg.Add(16) + + // Third and final block + for i := 0; i < 16; i++ { + input := make([]byte, 64) + input[0] = 0x80 + copy(input[1:], bytes.Repeat([]byte{0}, 63-8)) + + // Length in bits. + len := uint64(128) + len <<= 3 + for ii := uint(0); ii < 8; ii++ { + input[63-8+1+ii] = byte(len >> (56 - 8*ii)) + } + go func(i int, uid uint64, input []byte) { + output := server.Sum(uid, input) + if bytes.Compare(output[:], golden[offset+i].out[:]) != 0 { + t.Fatalf( + "Sum256 function: sha256(%s) = %s want %s", + golden[offset+i].in, + hex.EncodeToString(output[:]), + hex.EncodeToString(golden[offset+i].out[:]), + ) + } + wg.Done() + }(i, uint64(Avx512ServerUID+i), input) + } + + wg.Wait() +} + +func TestAvx512Digest(t *testing.T) { + + if !hasAvx512 { + // t.SkipNow() + return + } + + server := NewAvx512Server() + + const tests = 16 + h512 := [16]hash.Hash{} + for i := 0; i < tests; i++ { + h512[i] = NewAvx512(server) + } + + const offset = 31 + 16 + for i := 0; i < tests; i++ { + input := make([]byte, 64) + copy(input, golden[offset+i].in) + h512[i].Write(input) + } + for i := 0; i < tests; i++ { + input := make([]byte, 64) + copy(input, golden[offset+i].in[64:]) + h512[i].Write(input) + } + for i := 0; i < tests; i++ { + output := h512[i].Sum([]byte{}) + if bytes.Compare(output[:], golden[offset+i].out[:]) != 0 { + t.Fatalf( + "Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, + hex.EncodeToString(output[:]), + hex.EncodeToString(golden[offset+i].out[:]), + ) + } + } +} + +func benchmarkAvx512SingleCore(h512 []hash.Hash, body []byte) { + + for i := 0; i < len(h512); i++ { + h512[i].Write(body) + } + for i := 0; i < len(h512); i++ { + _ = h512[i].Sum([]byte{}) + } +} + +func benchmarkAvx512(b *testing.B, size int) { + + if !hasAvx512 { + b.SkipNow() + return + } + + server := NewAvx512Server() + + const tests = 16 + body := make([]byte, size) + + b.SetBytes(int64(len(body) * tests)) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + h512 := make([]hash.Hash, tests) + for i := 0; i < tests; i++ { + h512[i] = NewAvx512(server) + } + + benchmarkAvx512SingleCore(h512, body) + } +} + +func BenchmarkAvx512_05M(b *testing.B) { benchmarkAvx512(b, 512*1024) } +func BenchmarkAvx512_1M(b *testing.B) { benchmarkAvx512(b, 1*1024*1024) } +func BenchmarkAvx512_5M(b *testing.B) { benchmarkAvx512(b, 5*1024*1024) } +func BenchmarkAvx512_10M(b *testing.B) { benchmarkAvx512(b, 10*1024*1024) } + +func benchmarkAvx512MultiCore(b *testing.B, size, cores int) { + + if !hasAvx512 { + b.SkipNow() + return + } + + servers := make([]*Avx512Server, cores) + for c := 0; c < cores; c++ { + servers[c] = NewAvx512Server() + } + + const tests = 16 + + body := make([]byte, size) + + h512 := make([]hash.Hash, tests*cores) + for i := 0; i < tests*cores; i++ { + h512[i] = NewAvx512(servers[i>>4]) + } + + b.SetBytes(int64(size * 16 * cores)) + b.ResetTimer() + + var wg sync.WaitGroup + + for i := 0; i < b.N; i++ { + wg.Add(cores) + for c := 0; c < cores; c++ { + go func(c int) { + benchmarkAvx512SingleCore( + h512[c*tests:(c+1)*tests], + body, + ) + wg.Done() + }(c) + } + wg.Wait() + } +} + +func BenchmarkAvx512_5M_2Cores(b *testing.B) { + benchmarkAvx512MultiCore( + b, 5*1024*1024, 2, + ) +} +func BenchmarkAvx512_5M_4Cores(b *testing.B) { + benchmarkAvx512MultiCore( + b, 5*1024*1024, 4, + ) +} +func BenchmarkAvx512_5M_6Cores(b *testing.B) { + benchmarkAvx512MultiCore( + b, 5*1024*1024, 6, + ) +} + +type maskTest struct { + in [16]int + out [16]maskRounds +} + +var goldenMask = []maskTest{ + {[16]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, [16]maskRounds{}}, + { + [16]int{64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0}, + [16]maskRounds{{0x5555, 1}}, + }, + { + [16]int{0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64}, + [16]maskRounds{{0xaaaa, 1}}, + }, + { + [16]int{64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}, + [16]maskRounds{{0xffff, 1}}, + }, + { + [16]int{ + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, + }, + [16]maskRounds{{0xffff, 2}}, + }, + { + [16]int{ + 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, + 128, + }, + [16]maskRounds{{0xffff, 1}, {0xaaaa, 1}}, + }, + { + [16]int{ + 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, + 64, + }, + [16]maskRounds{{0xffff, 1}, {0x5555, 1}}, + }, + { + [16]int{ + 64, 192, 64, 192, 64, 192, 64, 192, 64, 192, 64, 192, 64, 192, 64, + 192, + }, + [16]maskRounds{{0xffff, 1}, {0xaaaa, 2}}, + }, + // + // >= 64 0110=6 1011=b 1101=d 0110=6 + // >=128 0100=4 0010=2 1001=9 0100=4 + { + [16]int{0, 64, 128, 0, 64, 128, 0, 64, 128, 0, 64, 128, 0, 64, 128, 0}, + [16]maskRounds{{0x6db6, 1}, {0x4924, 1}}, + }, + { + [16]int{ + 1 * 64, 2 * 64, 3 * 64, 4 * 64, 5 * 64, 6 * 64, 7 * 64, 8 * 64, + 9 * 64, 10 * 64, + 11 * 64, 12 * 64, 13 * 64, 14 * 64, 15 * 64, 16 * 64, + }, + [16]maskRounds{ + {0xffff, 1}, {0xfffe, 1}, {0xfffc, 1}, {0xfff8, 1}, {0xfff0, 1}, + {0xffe0, 1}, {0xffc0, 1}, {0xff80, 1}, + {0xff00, 1}, {0xfe00, 1}, {0xfc00, 1}, {0xf800, 1}, {0xf000, 1}, + {0xe000, 1}, + {0xc000, 1}, {0x8000, 1}, + }, + }, + { + [16]int{ + 2 * 64, 1 * 64, 3 * 64, 4 * 64, 5 * 64, 6 * 64, 7 * 64, 8 * 64, + 9 * 64, 10 * 64, + 11 * 64, 12 * 64, 13 * 64, 14 * 64, 15 * 64, 16 * 64, + }, + [16]maskRounds{ + {0xffff, 1}, {0xfffd, 1}, {0xfffc, 1}, {0xfff8, 1}, {0xfff0, 1}, + {0xffe0, 1}, {0xffc0, 1}, {0xff80, 1}, + {0xff00, 1}, {0xfe00, 1}, {0xfc00, 1}, {0xf800, 1}, {0xf000, 1}, + {0xe000, 1}, + {0xc000, 1}, {0x8000, 1}, + }, + }, + { + [16]int{ + 10 * 64, 20 * 64, 30 * 64, 40 * 64, 50 * 64, 60 * 64, 70 * 64, + 80 * 64, 90 * 64, + 100 * 64, 110 * 64, 120 * 64, 130 * 64, 140 * 64, 150 * 64, + 160 * 64, + }, + [16]maskRounds{ + {0xffff, 10}, {0xfffe, 10}, {0xfffc, 10}, {0xfff8, 10}, + {0xfff0, 10}, + {0xffe0, 10}, {0xffc0, 10}, {0xff80, 10}, + {0xff00, 10}, {0xfe00, 10}, {0xfc00, 10}, {0xf800, 10}, + {0xf000, 10}, {0xe000, 10}, + {0xc000, 10}, {0x8000, 10}, + }, + }, + { + [16]int{ + 10 * 64, 19 * 64, 27 * 64, 34 * 64, 40 * 64, 45 * 64, 49 * 64, + 52 * 64, 54 * 64, + 55 * 64, 57 * 64, 60 * 64, 64 * 64, 69 * 64, 75 * 64, 82 * 64, + }, + [16]maskRounds{ + {0xffff, 10}, {0xfffe, 9}, {0xfffc, 8}, {0xfff8, 7}, {0xfff0, 6}, + {0xffe0, 5}, {0xffc0, 4}, {0xff80, 3}, + {0xff00, 2}, {0xfe00, 1}, {0xfc00, 2}, {0xf800, 3}, {0xf000, 4}, + {0xe000, 5}, + {0xc000, 6}, {0x8000, 7}, + }, + }, +} + +func TestMaskGen(t *testing.T) { + input := [16][]byte{} + for gcase, g := range goldenMask { + for i, l := range g.in { + buf := make([]byte, l) + input[i] = buf[:] + } + + mr := genMask(input) + + if !reflect.DeepEqual(mr, g.out) { + t.Fatalf( + "case %d: got %04x\n want %04x", gcase, mr, + g.out, + ) + } + } +} diff --git a/pkg/crypto/sha256/sha256block_amd64.go b/pkg/crypto/sha256/sha256block_amd64.go new file mode 100644 index 0000000..e536f54 --- /dev/null +++ b/pkg/crypto/sha256/sha256block_amd64.go @@ -0,0 +1,31 @@ +//go:build !noasm && !appengine && gc +// +build !noasm,!appengine,gc + +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sha256 + +func blockArmSha2Go(dig *digest, p []byte) { + panic("blockArmSha2Go called unexpectedly") +} + +//go:noescape +func blockIntelSha(h *[8]uint32, message []uint8) + +func blockIntelShaGo(dig *digest, p []byte) { + blockIntelSha(&dig.h, p) +} diff --git a/pkg/crypto/sha256/sha256block_amd64.s b/pkg/crypto/sha256/sha256block_amd64.s new file mode 100644 index 0000000..c98a1d8 --- /dev/null +++ b/pkg/crypto/sha256/sha256block_amd64.s @@ -0,0 +1,266 @@ +//+build !noasm,!appengine,gc + +// SHA intrinsic version of SHA256 + +// Kristofer Peterson, (C) 2018. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "textflag.h" + +DATA K<>+0x00(SB)/4, $0x428a2f98 +DATA K<>+0x04(SB)/4, $0x71374491 +DATA K<>+0x08(SB)/4, $0xb5c0fbcf +DATA K<>+0x0c(SB)/4, $0xe9b5dba5 +DATA K<>+0x10(SB)/4, $0x3956c25b +DATA K<>+0x14(SB)/4, $0x59f111f1 +DATA K<>+0x18(SB)/4, $0x923f82a4 +DATA K<>+0x1c(SB)/4, $0xab1c5ed5 +DATA K<>+0x20(SB)/4, $0xd807aa98 +DATA K<>+0x24(SB)/4, $0x12835b01 +DATA K<>+0x28(SB)/4, $0x243185be +DATA K<>+0x2c(SB)/4, $0x550c7dc3 +DATA K<>+0x30(SB)/4, $0x72be5d74 +DATA K<>+0x34(SB)/4, $0x80deb1fe +DATA K<>+0x38(SB)/4, $0x9bdc06a7 +DATA K<>+0x3c(SB)/4, $0xc19bf174 +DATA K<>+0x40(SB)/4, $0xe49b69c1 +DATA K<>+0x44(SB)/4, $0xefbe4786 +DATA K<>+0x48(SB)/4, $0x0fc19dc6 +DATA K<>+0x4c(SB)/4, $0x240ca1cc +DATA K<>+0x50(SB)/4, $0x2de92c6f +DATA K<>+0x54(SB)/4, $0x4a7484aa +DATA K<>+0x58(SB)/4, $0x5cb0a9dc +DATA K<>+0x5c(SB)/4, $0x76f988da +DATA K<>+0x60(SB)/4, $0x983e5152 +DATA K<>+0x64(SB)/4, $0xa831c66d +DATA K<>+0x68(SB)/4, $0xb00327c8 +DATA K<>+0x6c(SB)/4, $0xbf597fc7 +DATA K<>+0x70(SB)/4, $0xc6e00bf3 +DATA K<>+0x74(SB)/4, $0xd5a79147 +DATA K<>+0x78(SB)/4, $0x06ca6351 +DATA K<>+0x7c(SB)/4, $0x14292967 +DATA K<>+0x80(SB)/4, $0x27b70a85 +DATA K<>+0x84(SB)/4, $0x2e1b2138 +DATA K<>+0x88(SB)/4, $0x4d2c6dfc +DATA K<>+0x8c(SB)/4, $0x53380d13 +DATA K<>+0x90(SB)/4, $0x650a7354 +DATA K<>+0x94(SB)/4, $0x766a0abb +DATA K<>+0x98(SB)/4, $0x81c2c92e +DATA K<>+0x9c(SB)/4, $0x92722c85 +DATA K<>+0xa0(SB)/4, $0xa2bfe8a1 +DATA K<>+0xa4(SB)/4, $0xa81a664b +DATA K<>+0xa8(SB)/4, $0xc24b8b70 +DATA K<>+0xac(SB)/4, $0xc76c51a3 +DATA K<>+0xb0(SB)/4, $0xd192e819 +DATA K<>+0xb4(SB)/4, $0xd6990624 +DATA K<>+0xb8(SB)/4, $0xf40e3585 +DATA K<>+0xbc(SB)/4, $0x106aa070 +DATA K<>+0xc0(SB)/4, $0x19a4c116 +DATA K<>+0xc4(SB)/4, $0x1e376c08 +DATA K<>+0xc8(SB)/4, $0x2748774c +DATA K<>+0xcc(SB)/4, $0x34b0bcb5 +DATA K<>+0xd0(SB)/4, $0x391c0cb3 +DATA K<>+0xd4(SB)/4, $0x4ed8aa4a +DATA K<>+0xd8(SB)/4, $0x5b9cca4f +DATA K<>+0xdc(SB)/4, $0x682e6ff3 +DATA K<>+0xe0(SB)/4, $0x748f82ee +DATA K<>+0xe4(SB)/4, $0x78a5636f +DATA K<>+0xe8(SB)/4, $0x84c87814 +DATA K<>+0xec(SB)/4, $0x8cc70208 +DATA K<>+0xf0(SB)/4, $0x90befffa +DATA K<>+0xf4(SB)/4, $0xa4506ceb +DATA K<>+0xf8(SB)/4, $0xbef9a3f7 +DATA K<>+0xfc(SB)/4, $0xc67178f2 +GLOBL K<>(SB), RODATA|NOPTR, $256 + +DATA SHUF_MASK<>+0x00(SB)/8, $0x0405060700010203 +DATA SHUF_MASK<>+0x08(SB)/8, $0x0c0d0e0f08090a0b +GLOBL SHUF_MASK<>(SB), RODATA|NOPTR, $16 + +// Register Usage +// BX base address of constant table (constant) +// DX hash_state (constant) +// SI hash_data.data +// DI hash_data.data + hash_data.length - 64 (constant) +// X0 scratch +// X1 scratch +// X2 working hash state // ABEF +// X3 working hash state // CDGH +// X4 first 16 bytes of block +// X5 second 16 bytes of block +// X6 third 16 bytes of block +// X7 fourth 16 bytes of block +// X12 saved hash state // ABEF +// X13 saved hash state // CDGH +// X15 data shuffle mask (constant) + +TEXT ·blockIntelSha(SB), NOSPLIT, $0-32 + MOVQ h+0(FP), DX + MOVQ message_base+8(FP), SI + MOVQ message_len+16(FP), DI + LEAQ -64(SI)(DI*1), DI + MOVOU (DX), X2 + MOVOU 16(DX), X1 + MOVO X2, X3 + PUNPCKLLQ X1, X2 + PUNPCKHLQ X1, X3 + PSHUFD $0x27, X2, X2 + PSHUFD $0x27, X3, X3 + MOVO SHUF_MASK<>(SB), X15 + LEAQ K<>(SB), BX + + JMP TEST + +LOOP: + MOVO X2, X12 + MOVO X3, X13 + + // load block and shuffle + MOVOU (SI), X4 + MOVOU 16(SI), X5 + MOVOU 32(SI), X6 + MOVOU 48(SI), X7 + PSHUFB X15, X4 + PSHUFB X15, X5 + PSHUFB X15, X6 + PSHUFB X15, X7 + +#define ROUND456 \ + PADDL X5, X0 \ + LONG $0xdacb380f \ // SHA256RNDS2 XMM3, XMM2 + MOVO X5, X1 \ + LONG $0x0f3a0f66; WORD $0x04cc \ // PALIGNR XMM1, XMM4, 4 + PADDL X1, X6 \ + LONG $0xf5cd380f \ // SHA256MSG2 XMM6, XMM5 + PSHUFD $0x4e, X0, X0 \ + LONG $0xd3cb380f \ // SHA256RNDS2 XMM2, XMM3 + LONG $0xe5cc380f // SHA256MSG1 XMM4, XMM5 + +#define ROUND567 \ + PADDL X6, X0 \ + LONG $0xdacb380f \ // SHA256RNDS2 XMM3, XMM2 + MOVO X6, X1 \ + LONG $0x0f3a0f66; WORD $0x04cd \ // PALIGNR XMM1, XMM5, 4 + PADDL X1, X7 \ + LONG $0xfecd380f \ // SHA256MSG2 XMM7, XMM6 + PSHUFD $0x4e, X0, X0 \ + LONG $0xd3cb380f \ // SHA256RNDS2 XMM2, XMM3 + LONG $0xeecc380f // SHA256MSG1 XMM5, XMM6 + +#define ROUND674 \ + PADDL X7, X0 \ + LONG $0xdacb380f \ // SHA256RNDS2 XMM3, XMM2 + MOVO X7, X1 \ + LONG $0x0f3a0f66; WORD $0x04ce \ // PALIGNR XMM1, XMM6, 4 + PADDL X1, X4 \ + LONG $0xe7cd380f \ // SHA256MSG2 XMM4, XMM7 + PSHUFD $0x4e, X0, X0 \ + LONG $0xd3cb380f \ // SHA256RNDS2 XMM2, XMM3 + LONG $0xf7cc380f // SHA256MSG1 XMM6, XMM7 + +#define ROUND745 \ + PADDL X4, X0 \ + LONG $0xdacb380f \ // SHA256RNDS2 XMM3, XMM2 + MOVO X4, X1 \ + LONG $0x0f3a0f66; WORD $0x04cf \ // PALIGNR XMM1, XMM7, 4 + PADDL X1, X5 \ + LONG $0xeccd380f \ // SHA256MSG2 XMM5, XMM4 + PSHUFD $0x4e, X0, X0 \ + LONG $0xd3cb380f \ // SHA256RNDS2 XMM2, XMM3 + LONG $0xfccc380f // SHA256MSG1 XMM7, XMM4 + + // rounds 0-3 + MOVO (BX), X0 + PADDL X4, X0 + LONG $0xdacb380f // SHA256RNDS2 XMM3, XMM2 + PSHUFD $0x4e, X0, X0 + LONG $0xd3cb380f // SHA256RNDS2 XMM2, XMM3 + + // rounds 4-7 + MOVO 1*16(BX), X0 + PADDL X5, X0 + LONG $0xdacb380f // SHA256RNDS2 XMM3, XMM2 + PSHUFD $0x4e, X0, X0 + LONG $0xd3cb380f // SHA256RNDS2 XMM2, XMM3 + LONG $0xe5cc380f // SHA256MSG1 XMM4, XMM5 + + // rounds 8-11 + MOVO 2*16(BX), X0 + PADDL X6, X0 + LONG $0xdacb380f // SHA256RNDS2 XMM3, XMM2 + PSHUFD $0x4e, X0, X0 + LONG $0xd3cb380f // SHA256RNDS2 XMM2, XMM3 + LONG $0xeecc380f // SHA256MSG1 XMM5, XMM6 + + MOVO 3*16(BX), X0; ROUND674 // rounds 12-15 + MOVO 4*16(BX), X0; ROUND745 // rounds 16-19 + MOVO 5*16(BX), X0; ROUND456 // rounds 20-23 + MOVO 6*16(BX), X0; ROUND567 // rounds 24-27 + MOVO 7*16(BX), X0; ROUND674 // rounds 28-31 + MOVO 8*16(BX), X0; ROUND745 // rounds 32-35 + MOVO 9*16(BX), X0; ROUND456 // rounds 36-39 + MOVO 10*16(BX), X0; ROUND567 // rounds 40-43 + MOVO 11*16(BX), X0; ROUND674 // rounds 44-47 + MOVO 12*16(BX), X0; ROUND745 // rounds 48-51 + + // rounds 52-55 + MOVO 13*16(BX), X0 + PADDL X5, X0 + LONG $0xdacb380f // SHA256RNDS2 XMM3, XMM2 + MOVO X5, X1 + LONG $0x0f3a0f66; WORD $0x04cc // PALIGNR XMM1, XMM4, 4 + PADDL X1, X6 + LONG $0xf5cd380f // SHA256MSG2 XMM6, XMM5 + PSHUFD $0x4e, X0, X0 + LONG $0xd3cb380f // SHA256RNDS2 XMM2, XMM3 + + // rounds 56-59 + MOVO 14*16(BX), X0 + PADDL X6, X0 + LONG $0xdacb380f // SHA256RNDS2 XMM3, XMM2 + MOVO X6, X1 + LONG $0x0f3a0f66; WORD $0x04cd // PALIGNR XMM1, XMM5, 4 + PADDL X1, X7 + LONG $0xfecd380f // SHA256MSG2 XMM7, XMM6 + PSHUFD $0x4e, X0, X0 + LONG $0xd3cb380f // SHA256RNDS2 XMM2, XMM3 + + // rounds 60-63 + MOVO 15*16(BX), X0 + PADDL X7, X0 + LONG $0xdacb380f // SHA256RNDS2 XMM3, XMM2 + PSHUFD $0x4e, X0, X0 + LONG $0xd3cb380f // SHA256RNDS2 XMM2, XMM3 + + PADDL X12, X2 + PADDL X13, X3 + + ADDQ $64, SI + +TEST: + CMPQ SI, DI + JBE LOOP + + PSHUFD $0x4e, X3, X0 + LONG $0x0e3a0f66; WORD $0xf0c2 // PBLENDW XMM0, XMM2, 0xf0 + PSHUFD $0x4e, X2, X1 + LONG $0x0e3a0f66; WORD $0x0fcb // PBLENDW XMM1, XMM3, 0x0f + PSHUFD $0x1b, X0, X0 + PSHUFD $0x1b, X1, X1 + + MOVOU X0, (DX) + MOVOU X1, 16(DX) + + RET diff --git a/pkg/crypto/sha256/sha256block_amd64_test.go b/pkg/crypto/sha256/sha256block_amd64_test.go new file mode 100644 index 0000000..da3d60c --- /dev/null +++ b/pkg/crypto/sha256/sha256block_amd64_test.go @@ -0,0 +1,78 @@ +//go:build !noasm && !appengine && gc +// +build !noasm,!appengine,gc + +package sha256 + +import ( + "crypto/sha256" + "encoding/binary" + "testing" +) + +func sha256hash(m []byte) (r [32]byte) { + var h [8]uint32 + + h[0] = 0x6a09e667 + h[1] = 0xbb67ae85 + h[2] = 0x3c6ef372 + h[3] = 0xa54ff53a + h[4] = 0x510e527f + h[5] = 0x9b05688c + h[6] = 0x1f83d9ab + h[7] = 0x5be0cd19 + + blockIntelSha(&h, m) + l0 := len(m) + l := l0 & (BlockSize - 1) + m = m[l0-l:] + + var k [64]byte + copy(k[:], m) + + k[l] = 0x80 + + if l >= 56 { + blockIntelSha(&h, k[:]) + binary.LittleEndian.PutUint64(k[0:8], 0) + binary.LittleEndian.PutUint64(k[8:16], 0) + binary.LittleEndian.PutUint64(k[16:24], 0) + binary.LittleEndian.PutUint64(k[24:32], 0) + binary.LittleEndian.PutUint64(k[32:40], 0) + binary.LittleEndian.PutUint64(k[40:48], 0) + binary.LittleEndian.PutUint64(k[48:56], 0) + } + binary.BigEndian.PutUint64(k[56:64], uint64(l0)<<3) + blockIntelSha(&h, k[:]) + + binary.BigEndian.PutUint32(r[0:4], h[0]) + binary.BigEndian.PutUint32(r[4:8], h[1]) + binary.BigEndian.PutUint32(r[8:12], h[2]) + binary.BigEndian.PutUint32(r[12:16], h[3]) + binary.BigEndian.PutUint32(r[16:20], h[4]) + binary.BigEndian.PutUint32(r[20:24], h[5]) + binary.BigEndian.PutUint32(r[24:28], h[6]) + binary.BigEndian.PutUint32(r[28:32], h[7]) + + return +} + +func runTestSha(hashfunc func([]byte) [32]byte) bool { + var m = []byte("This is a message. This is a message. This is a message. This is a message.") + + ar := hashfunc(m) + br := sha256.Sum256(m) + + return ar == br +} + +func TestSha0(t *testing.T) { + if !runTestSha(Sum256) { + t.Errorf("FAILED") + } +} + +func TestSha1(t *testing.T) { + if hasIntelSha && !runTestSha(sha256hash) { + t.Errorf("FAILED") + } +} diff --git a/pkg/crypto/sha256/sha256block_arm64.go b/pkg/crypto/sha256/sha256block_arm64.go new file mode 100644 index 0000000..fceeda2 --- /dev/null +++ b/pkg/crypto/sha256/sha256block_arm64.go @@ -0,0 +1,40 @@ +//go:build !noasm && !appengine && gc +// +build !noasm,!appengine,gc + +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sha256 + +func blockIntelShaGo(dig *digest, p []byte) { + panic("blockIntelShaGo called unexpectedly") +} + +//go:noescape +func blockArmSha2(h []uint32, message []uint8) + +func blockArmSha2Go(dig *digest, p []byte) { + + h := []uint32{ + dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], + dig.h[7], + } + + blockArmSha2(h[:], p[:]) + + dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h[0], h[1], h[2], h[3], h[4], + h[5], h[6], h[7] +} diff --git a/pkg/crypto/sha256/sha256block_arm64.s b/pkg/crypto/sha256/sha256block_arm64.s new file mode 100644 index 0000000..7ab88b1 --- /dev/null +++ b/pkg/crypto/sha256/sha256block_arm64.s @@ -0,0 +1,192 @@ +//+build !noasm,!appengine,gc + +// ARM64 version of SHA256 + +// +// Minio Cloud Storage, (C) 2016 Minio, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// +// Based on implementation as found in https://github.com/jocover/sha256-armv8 +// +// Use github.com/minio/asm2plan9s on this file to assemble ARM instructions to +// their Plan9 equivalents +// + +TEXT ·blockArmSha2(SB), 7, $0 + MOVD h+0(FP), R0 + MOVD message+24(FP), R1 + MOVD message_len+32(FP), R2 // length of message + SUBS $64, R2 + BMI complete + + // Load constants table pointer + MOVD $·constants(SB), R3 + + // Cache constants table in registers v16 - v31 + WORD $0x4cdf2870 // ld1 {v16.4s-v19.4s}, [x3], #64 + WORD $0x4cdf7800 // ld1 {v0.4s}, [x0], #16 + WORD $0x4cdf2874 // ld1 {v20.4s-v23.4s}, [x3], #64 + + WORD $0x4c407801 // ld1 {v1.4s}, [x0] + WORD $0x4cdf2878 // ld1 {v24.4s-v27.4s}, [x3], #64 + WORD $0xd1004000 // sub x0, x0, #0x10 + WORD $0x4cdf287c // ld1 {v28.4s-v31.4s}, [x3], #64 + +loop: + // Main loop + WORD $0x4cdf2025 // ld1 {v5.16b-v8.16b}, [x1], #64 + WORD $0x4ea01c02 // mov v2.16b, v0.16b + WORD $0x4ea11c23 // mov v3.16b, v1.16b + WORD $0x6e2008a5 // rev32 v5.16b, v5.16b + WORD $0x6e2008c6 // rev32 v6.16b, v6.16b + WORD $0x4eb084a9 // add v9.4s, v5.4s, v16.4s + WORD $0x6e2008e7 // rev32 v7.16b, v7.16b + WORD $0x4eb184ca // add v10.4s, v6.4s, v17.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x5e2828c5 // sha256su0 v5.4s, v6.4s + WORD $0x6e200908 // rev32 v8.16b, v8.16b + WORD $0x4eb284e9 // add v9.4s, v7.4s, v18.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x5e2828e6 // sha256su0 v6.4s, v7.4s + WORD $0x5e0860e5 // sha256su1 v5.4s, v7.4s, v8.4s + WORD $0x4eb3850a // add v10.4s, v8.4s, v19.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x5e282907 // sha256su0 v7.4s, v8.4s + WORD $0x5e056106 // sha256su1 v6.4s, v8.4s, v5.4s + WORD $0x4eb484a9 // add v9.4s, v5.4s, v20.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x5e2828a8 // sha256su0 v8.4s, v5.4s + WORD $0x5e0660a7 // sha256su1 v7.4s, v5.4s, v6.4s + WORD $0x4eb584ca // add v10.4s, v6.4s, v21.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x5e2828c5 // sha256su0 v5.4s, v6.4s + WORD $0x5e0760c8 // sha256su1 v8.4s, v6.4s, v7.4s + WORD $0x4eb684e9 // add v9.4s, v7.4s, v22.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x5e2828e6 // sha256su0 v6.4s, v7.4s + WORD $0x5e0860e5 // sha256su1 v5.4s, v7.4s, v8.4s + WORD $0x4eb7850a // add v10.4s, v8.4s, v23.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x5e282907 // sha256su0 v7.4s, v8.4s + WORD $0x5e056106 // sha256su1 v6.4s, v8.4s, v5.4s + WORD $0x4eb884a9 // add v9.4s, v5.4s, v24.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x5e2828a8 // sha256su0 v8.4s, v5.4s + WORD $0x5e0660a7 // sha256su1 v7.4s, v5.4s, v6.4s + WORD $0x4eb984ca // add v10.4s, v6.4s, v25.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x5e2828c5 // sha256su0 v5.4s, v6.4s + WORD $0x5e0760c8 // sha256su1 v8.4s, v6.4s, v7.4s + WORD $0x4eba84e9 // add v9.4s, v7.4s, v26.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x5e2828e6 // sha256su0 v6.4s, v7.4s + WORD $0x5e0860e5 // sha256su1 v5.4s, v7.4s, v8.4s + WORD $0x4ebb850a // add v10.4s, v8.4s, v27.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x5e282907 // sha256su0 v7.4s, v8.4s + WORD $0x5e056106 // sha256su1 v6.4s, v8.4s, v5.4s + WORD $0x4ebc84a9 // add v9.4s, v5.4s, v28.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x5e2828a8 // sha256su0 v8.4s, v5.4s + WORD $0x5e0660a7 // sha256su1 v7.4s, v5.4s, v6.4s + WORD $0x4ebd84ca // add v10.4s, v6.4s, v29.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x5e0760c8 // sha256su1 v8.4s, v6.4s, v7.4s + WORD $0x4ebe84e9 // add v9.4s, v7.4s, v30.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x4ebf850a // add v10.4s, v8.4s, v31.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e094062 // sha256h q2, q3, v9.4s + WORD $0x5e095083 // sha256h2 q3, q4, v9.4s + WORD $0x4ea21c44 // mov v4.16b, v2.16b + WORD $0x5e0a4062 // sha256h q2, q3, v10.4s + WORD $0x5e0a5083 // sha256h2 q3, q4, v10.4s + WORD $0x4ea38421 // add v1.4s, v1.4s, v3.4s + WORD $0x4ea28400 // add v0.4s, v0.4s, v2.4s + + SUBS $64, R2 + BPL loop + + // Store result + WORD $0x4c00a800 // st1 {v0.4s, v1.4s}, [x0] + +complete: + RET + +// Constants table +DATA ·constants+0x0(SB)/8, $0x71374491428a2f98 +DATA ·constants+0x8(SB)/8, $0xe9b5dba5b5c0fbcf +DATA ·constants+0x10(SB)/8, $0x59f111f13956c25b +DATA ·constants+0x18(SB)/8, $0xab1c5ed5923f82a4 +DATA ·constants+0x20(SB)/8, $0x12835b01d807aa98 +DATA ·constants+0x28(SB)/8, $0x550c7dc3243185be +DATA ·constants+0x30(SB)/8, $0x80deb1fe72be5d74 +DATA ·constants+0x38(SB)/8, $0xc19bf1749bdc06a7 +DATA ·constants+0x40(SB)/8, $0xefbe4786e49b69c1 +DATA ·constants+0x48(SB)/8, $0x240ca1cc0fc19dc6 +DATA ·constants+0x50(SB)/8, $0x4a7484aa2de92c6f +DATA ·constants+0x58(SB)/8, $0x76f988da5cb0a9dc +DATA ·constants+0x60(SB)/8, $0xa831c66d983e5152 +DATA ·constants+0x68(SB)/8, $0xbf597fc7b00327c8 +DATA ·constants+0x70(SB)/8, $0xd5a79147c6e00bf3 +DATA ·constants+0x78(SB)/8, $0x1429296706ca6351 +DATA ·constants+0x80(SB)/8, $0x2e1b213827b70a85 +DATA ·constants+0x88(SB)/8, $0x53380d134d2c6dfc +DATA ·constants+0x90(SB)/8, $0x766a0abb650a7354 +DATA ·constants+0x98(SB)/8, $0x92722c8581c2c92e +DATA ·constants+0xa0(SB)/8, $0xa81a664ba2bfe8a1 +DATA ·constants+0xa8(SB)/8, $0xc76c51a3c24b8b70 +DATA ·constants+0xb0(SB)/8, $0xd6990624d192e819 +DATA ·constants+0xb8(SB)/8, $0x106aa070f40e3585 +DATA ·constants+0xc0(SB)/8, $0x1e376c0819a4c116 +DATA ·constants+0xc8(SB)/8, $0x34b0bcb52748774c +DATA ·constants+0xd0(SB)/8, $0x4ed8aa4a391c0cb3 +DATA ·constants+0xd8(SB)/8, $0x682e6ff35b9cca4f +DATA ·constants+0xe0(SB)/8, $0x78a5636f748f82ee +DATA ·constants+0xe8(SB)/8, $0x8cc7020884c87814 +DATA ·constants+0xf0(SB)/8, $0xa4506ceb90befffa +DATA ·constants+0xf8(SB)/8, $0xc67178f2bef9a3f7 + +GLOBL ·constants(SB), 8, $256 + diff --git a/pkg/crypto/sha256/sha256block_other.go b/pkg/crypto/sha256/sha256block_other.go new file mode 100644 index 0000000..94d7eb0 --- /dev/null +++ b/pkg/crypto/sha256/sha256block_other.go @@ -0,0 +1,29 @@ +//go:build appengine || noasm || (!amd64 && !arm64) || !gc +// +build appengine noasm !amd64,!arm64 !gc + +/* + * Minio Cloud Storage, (C) 2019 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sha256 + +func blockIntelShaGo(dig *digest, p []byte) { + panic("blockIntelShaGo called unexpectedly") + +} + +func blockArmSha2Go(dig *digest, p []byte) { + panic("blockArmSha2Go called unexpectedly") +} diff --git a/pkg/crypto/sha256/test-architectures.sh b/pkg/crypto/sha256/test-architectures.sh new file mode 100644 index 0000000..50150ea --- /dev/null +++ b/pkg/crypto/sha256/test-architectures.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +go tool dist list | while IFS=/ read os arch; do + echo "Checking $os/$arch..." + echo " normal" + GOARCH=$arch GOOS=$os go build -o /dev/null ./... + echo " noasm" + GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null ./... + echo " appengine" + GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null ./... + echo " noasm,appengine" + GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null ./... +done diff --git a/pkg/encoders/hex/aliases.go b/pkg/encoders/hex/aliases.go new file mode 100644 index 0000000..0cf3290 --- /dev/null +++ b/pkg/encoders/hex/aliases.go @@ -0,0 +1,50 @@ +// Package hex is a set of aliases and helpers for using the templexxx SIMD hex +// encoder. +package hex + +import ( + "encoding/hex" + + "github.com/templexxx/xhex" + "lol.mleku.dev/chk" + "lol.mleku.dev/errorf" +) + +var Enc = hex.EncodeToString +var EncBytes = hex.Encode +var Dec = hex.DecodeString +var DecBytes = hex.Decode + +// var EncAppend = hex.AppendEncode +// var DecAppend = hex.AppendDecode + +var DecLen = hex.DecodedLen + +type InvalidByteError = hex.InvalidByteError + +// EncAppend uses xhex to encode a sice of bytes and appends it to a provided destination slice. +func EncAppend(dst, src []byte) (b []byte) { + l := len(dst) + dst = append(dst, make([]byte, len(src)*2)...) + xhex.Encode(dst[l:], src) + return dst +} + +// DecAppend decodes a provided encoded hex encoded string and appends the decoded output to a +// provided input slice. +func DecAppend(dst, src []byte) (b []byte, err error) { + if src == nil || len(src) < 2 { + err = errorf.E("nothing to decode") + return + } + if dst == nil { + dst = []byte{} + } + l := len(dst) + b = dst + b = append(b, make([]byte, len(src)/2)...) + if err = xhex.Decode(b[l:], src); chk.T(err) { + return + } + return +} diff --git a/pkg/interfaces/signer/signer.go b/pkg/interfaces/signer/signer.go new file mode 100644 index 0000000..e20e42b --- /dev/null +++ b/pkg/interfaces/signer/signer.go @@ -0,0 +1,42 @@ +// Package signer defines server for management of signatures, used to +// abstract the signature algorithm from the usage. +package signer + +// I is an interface for a key pair for signing, created to abstract between a CGO fast BIP-340 +// signature library and the slower btcec library. +type I interface { + // Generate creates a fresh new key pair from system entropy, and ensures it is even (so + // ECDH works). + Generate() (err error) + // InitSec initialises the secret (signing) key from the raw bytes, and also + // derives the public key because it can. + InitSec(sec []byte) (err error) + // InitPub initializes the public (verification) key from raw bytes, this is + // expected to be an x-only 32 byte pubkey. + InitPub(pub []byte) (err error) + // Sec returns the secret key bytes. + Sec() []byte + // Pub returns the public key bytes (x-only schnorr pubkey). + Pub() []byte + // Sign creates a signature using the stored secret key. + Sign(msg []byte) (sig []byte, err error) + // Verify checks a message hash and signature match the stored public key. + Verify(msg, sig []byte) (valid bool, err error) + // Zero wipes the secret key to prevent memory leaks. + Zero() + // ECDH returns a shared secret derived using Elliptic Curve Diffie-Hellman on + // the I secret and provided pubkey. + ECDH(pub []byte) (secret []byte, err error) +} + +// Gen is an interface for nostr BIP-340 key generation. +type Gen interface { + // Generate gathers entropy and derives pubkey bytes for matching, this returns the 33 byte + // compressed form for checking the oddness of the Y coordinate. + Generate() (pubBytes []byte, err error) + // Negate flips the public key Y coordinate between odd and even. + Negate() + // KeyPairBytes returns the raw bytes of the secret and public key, this returns the 32 byte + // X-only pubkey. + KeyPairBytes() (secBytes, cmprPubBytes []byte) +} diff --git a/pkg/utils/fastequal.go b/pkg/utils/fastequal.go new file mode 100644 index 0000000..42f4665 --- /dev/null +++ b/pkg/utils/fastequal.go @@ -0,0 +1,13 @@ +package utils + +func FastEqual(a, b []byte) (same bool) { + if len(a) != len(b) { + return + } + for i, v := range a { + if v != b[i] { + return + } + } + return true +} diff --git a/readme.adoc b/readme.adoc index 83bd84e..f3cee24 100644 --- a/readme.adoc +++ b/readme.adoc @@ -18,4 +18,6 @@ ORLY is a nostr relay written from the ground up to be performant, low latency, - business deployments and RaaS (Relay as a Service) with a nostr-native NWC client to allow accepting payments through NWC capable lightning nodes - high availability clusters for reliability and/or providing a unified data set across multiple regions -ORLY uses a fast embedded link:https://github.com/hypermodeinc/badger[badger] database with a database designed for high performance querying and event storage. \ No newline at end of file +ORLY uses a fast embedded link:https://github.com/hypermodeinc/badger[badger] database with a database designed for high performance querying and event storage. + +On linux platforms, it uses https://github.com/bitcoin/secp256k1[libsecp256k1]-enabled signature and signature verification (see link:pkg/p256k/README.md[here]). diff --git a/scripts/orly.service b/scripts/orly.service new file mode 100644 index 0000000..3f41049 --- /dev/null +++ b/scripts/orly.service @@ -0,0 +1,16 @@ +# systemd unit to run orly as a service +[Unit] +Description=orly + +[Service] +Type=simple +User=mleku +ExecStart=/home/mleku/.local/bin/orly +Restart=always +Wants=network-online.target +# waits for wireguard service to come up before starting, remove if running it directly on an +# internet routeable connection +After=network.target network-online.target wg-quick@wg0.service + +[Install] +WantedBy=multi-user.target diff --git a/scripts/reload.sh b/scripts/reload.sh new file mode 100755 index 0000000..9d7a710 --- /dev/null +++ b/scripts/reload.sh @@ -0,0 +1,7 @@ +#!/usr/bin/bash +until false; do + echo "Respawning.." >&2 + sleep 1 + reset + go run ./cmd/realy/. +done diff --git a/scripts/runtests.sh b/scripts/runtests.sh new file mode 100644 index 0000000..4a0e704 --- /dev/null +++ b/scripts/runtests.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +go test -v ./... -bench=. -run=xxx -benchmem \ No newline at end of file diff --git a/scripts/ubuntu_install_libsecp256k1.sh b/scripts/ubuntu_install_libsecp256k1.sh new file mode 100755 index 0000000..288df4c --- /dev/null +++ b/scripts/ubuntu_install_libsecp256k1.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +sudo apt -y install build-essential autoconf libtool +cd $SCRIPT_DIR +rm -rf secp256k1 +git clone https://github.com/bitcoin-core/secp256k1.git +cd secp256k1 +git checkout v0.6.0 +git submodule init +git submodule update +./autogen.sh +./configure --enable-module-schnorrsig --enable-module-ecdh --prefix=/usr +make -j1 +sudo make install