From 24720bf5a632a26c6466e2addf842674d87420c6 Mon Sep 17 00:00:00 2001 From: Tutus Development Date: Sat, 27 Dec 2025 15:45:25 +0000 Subject: [PATCH] Rebrand to Tutus - update license, workflows, and dependencies --- .github/workflows/dco.yml | 18 +-- .github/workflows/run_tests.yml | 232 ++++++++++++++++---------------- .gitignore | 46 +++---- .travis.yml | 8 +- LICENSE | 42 +++--- dsa.go | 84 ++++++------ ecdsa.go | 76 +++++------ example_test.go | 148 ++++++++++---------- rfc6979.go | 228 +++++++++++++++---------------- rfc6979_test.go | 56 ++++---- 10 files changed, 469 insertions(+), 469 deletions(-) diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml index 9850cbd..56a7bf1 100644 --- a/.github/workflows/dco.yml +++ b/.github/workflows/dco.yml @@ -1,9 +1,9 @@ -name: DCO check - -on: - pull_request: - branches: [ "master" ] - -jobs: - dco: - uses: nspcc-dev/.github/.github/workflows/dco.yml@master +name: DCO check + +on: + pull_request: + branches: [ "master" ] + +jobs: + dco: + uses: nspcc-dev/.github/.github/workflows/dco.yml@master diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 3410deb..d7990d1 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -1,116 +1,116 @@ -name: Tests - -on: - pull_request: - branches: - - master - types: [ opened, synchronize ] - workflow_dispatch: - -jobs: - lint: - name: Lint - uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master - - test_cover: - name: Coverage - runs-on: ubuntu-latest - - env: - CGO_ENABLED: 0 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.25' - cache: true - - - name: Write coverage profile - run: go test ./... -coverprofile=./coverage.txt -covermode=atomic - - - name: Upload coverage results to Codecov - uses: codecov/codecov-action@v4 - with: - fail_ci_if_error: false - slug: nspcc-dev/rfc6979 - token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.txt - verbose: true - - tests: - name: Run tests - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - go_versions: [ '1.24', '1.25' ] - exclude: - - os: macos-latest - go_versions: '1.24' - - os: windows-latest - go_versions: '1.24' - - os: ubuntu-latest - go_versions: '1.25' - fail-fast: false - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '${{ matrix.go_versions }}' - cache: true - - - name: Run tests - run: go test -race ./... - - codeql: - name: CodeQL - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 +name: Tests + +on: + pull_request: + branches: + - master + types: [ opened, synchronize ] + workflow_dispatch: + +jobs: + lint: + name: Lint + uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master + + test_cover: + name: Coverage + runs-on: ubuntu-latest + + env: + CGO_ENABLED: 0 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.25' + cache: true + + - name: Write coverage profile + run: go test ./... -coverprofile=./coverage.txt -covermode=atomic + + - name: Upload coverage results to Codecov + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: false + slug: nspcc-dev/rfc6979 + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.txt + verbose: true + + tests: + name: Run tests + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, windows-latest, macos-latest ] + go_versions: [ '1.24', '1.25' ] + exclude: + - os: macos-latest + go_versions: '1.24' + - os: windows-latest + go_versions: '1.24' + - os: ubuntu-latest + go_versions: '1.25' + fail-fast: false + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '${{ matrix.go_versions }}' + cache: true + + - name: Run tests + run: go test -race ./... + + codeql: + name: CodeQL + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore index 11d7a2c..5050f35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,23 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -.golangci.yml +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +.golangci.yml diff --git a/.travis.yml b/.travis.yml index f77ad20..1fa93c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -language: go -go: - - 1.12.x - - 1.13.x +language: go +go: + - 1.12.x + - 1.13.x diff --git a/LICENSE b/LICENSE index f9835c2..f6f8ae3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -The MIT License (MIT) - -Copyright (c) 2014 Coda Hale - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +The MIT License (MIT) + +Copyright (c) 2014 Coda Hale + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dsa.go b/dsa.go index 9a4d67a..3f6acfa 100644 --- a/dsa.go +++ b/dsa.go @@ -1,42 +1,42 @@ -package rfc6979 - -import ( - "crypto/dsa" //nolint:staticcheck - "hash" - "math/big" -) - -// SignDSA signs an arbitrary length hash (which should be the result of hashing -// a larger message) using the private key, priv. It returns the signature as a -// pair of integers. -// -// Deprecated: crypto/dsa package is deprecated in Go, so please swtich to ECDSA. -// This method can be removed in future versions. -func SignDSA(priv *dsa.PrivateKey, hash []byte, alg func() hash.Hash) (r, s *big.Int, err error) { - n := priv.Q.BitLen() - if n&7 != 0 { - err = dsa.ErrInvalidPublicKey - return - } - - generateSecret(priv.Q, priv.X, alg, hash, func(k *big.Int, z *big.Int, _ []byte) bool { - r = new(big.Int).Exp(priv.G, k, priv.P) - r.Mod(r, priv.Q) - - if r.Sign() == 0 { - return false - } - - inv := k.ModInverse(k, priv.Q) - - s = new(big.Int).Mul(priv.X, r) - s.Add(s, z) - s.Mod(s, priv.Q) - s.Mul(s, inv) - s.Mod(s, priv.Q) - - return s.Sign() != 0 - }) - - return -} +package rfc6979 + +import ( + "crypto/dsa" //nolint:staticcheck + "hash" + "math/big" +) + +// SignDSA signs an arbitrary length hash (which should be the result of hashing +// a larger message) using the private key, priv. It returns the signature as a +// pair of integers. +// +// Deprecated: crypto/dsa package is deprecated in Go, so please swtich to ECDSA. +// This method can be removed in future versions. +func SignDSA(priv *dsa.PrivateKey, hash []byte, alg func() hash.Hash) (r, s *big.Int, err error) { + n := priv.Q.BitLen() + if n&7 != 0 { + err = dsa.ErrInvalidPublicKey + return + } + + generateSecret(priv.Q, priv.X, alg, hash, func(k *big.Int, z *big.Int, _ []byte) bool { + r = new(big.Int).Exp(priv.G, k, priv.P) + r.Mod(r, priv.Q) + + if r.Sign() == 0 { + return false + } + + inv := k.ModInverse(k, priv.Q) + + s = new(big.Int).Mul(priv.X, r) + s.Add(s, z) + s.Mod(s, priv.Q) + s.Mul(s, inv) + s.Mod(s, priv.Q) + + return s.Sign() != 0 + }) + + return +} diff --git a/ecdsa.go b/ecdsa.go index 769e2e9..def9653 100644 --- a/ecdsa.go +++ b/ecdsa.go @@ -1,38 +1,38 @@ -package rfc6979 - -import ( - "crypto/ecdsa" - "hash" - "math/big" -) - -// SignECDSA signs an arbitrary length hash (which should be the result of -// hashing a larger message) using the private key, priv. It returns the -// signature as a pair of integers. -// -// Will panic if invalid private key (>N for the curve) is passed. -func SignECDSA(priv *ecdsa.PrivateKey, hash []byte, alg func() hash.Hash) (r, s *big.Int) { - c := priv.Curve - N := c.Params().N - - generateSecret(N, priv.D, alg, hash, func(k *big.Int, e *big.Int, t []byte) bool { - k.FillBytes(t) - r, _ = priv.ScalarBaseMult(t) - r.Mod(r, N) - - if r.Sign() == 0 { - return false - } - - inv := k.ModInverse(k, N) - - s = new(big.Int).Mul(priv.D, r) - s.Add(s, e) - s.Mul(s, inv) - s.Mod(s, N) - - return s.Sign() != 0 - }) - - return -} +package rfc6979 + +import ( + "crypto/ecdsa" + "hash" + "math/big" +) + +// SignECDSA signs an arbitrary length hash (which should be the result of +// hashing a larger message) using the private key, priv. It returns the +// signature as a pair of integers. +// +// Will panic if invalid private key (>N for the curve) is passed. +func SignECDSA(priv *ecdsa.PrivateKey, hash []byte, alg func() hash.Hash) (r, s *big.Int) { + c := priv.Curve + N := c.Params().N + + generateSecret(N, priv.D, alg, hash, func(k *big.Int, e *big.Int, t []byte) bool { + k.FillBytes(t) + r, _ = priv.ScalarBaseMult(t) + r.Mod(r, N) + + if r.Sign() == 0 { + return false + } + + inv := k.ModInverse(k, N) + + s = new(big.Int).Mul(priv.D, r) + s.Add(s, e) + s.Mul(s, inv) + s.Mod(s, N) + + return s.Sign() != 0 + }) + + return +} diff --git a/example_test.go b/example_test.go index b6ff7e9..1b43f45 100644 --- a/example_test.go +++ b/example_test.go @@ -1,74 +1,74 @@ -package rfc6979 - -import ( - "crypto/dsa" //nolint:staticcheck - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha1" - "crypto/sha512" - "fmt" -) - -// Generates a 521-bit ECDSA key, uses SHA-512 to sign a message, then verifies -// it. -func ExampleSignECDSA() { - // Generate a key pair. - // You need a high-quality PRNG for this. - k, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - if err != nil { - fmt.Println(err) - return - } - - // Hash a message. - alg := sha512.New() - alg.Write([]byte("I am a potato.")) - hash := alg.Sum(nil) - - // Sign the message. You don't need a PRNG for this. - r, s := SignECDSA(k, hash, sha512.New) - - if !ecdsa.Verify(&k.PublicKey, hash, r, s) { - fmt.Println("Invalid signature!") - } - - // Output: -} - -// Generates a 1024-bit DSA key, uses SHA-1 to sign a message, then verifies it. -func ExampleSignDSA() { - // Here I'm generating some DSA params, but you should really pre-generate - // these and re-use them, since this takes a long time and isn't necessary. - k := new(dsa.PrivateKey) - if err := dsa.GenerateParameters(&k.Parameters, rand.Reader, dsa.L1024N160); err != nil { - fmt.Println(err) - return - } - - // Generate a key pair. - // You need a high-quality PRNG for this. - err := dsa.GenerateKey(k, rand.Reader) - if err != nil { - fmt.Println(err) - return - } - - // Hash a message. - alg := sha1.New() - alg.Write([]byte("I am a potato.")) - hash := alg.Sum(nil) - - // Sign the message. You don't need a PRNG for this. - r, s, err := SignDSA(k, hash, sha1.New) - if err != nil { - fmt.Println(err) - return - } - - if !dsa.Verify(&k.PublicKey, hash, r, s) { - fmt.Println("Invalid signature!") - } - - // Output: -} +package rfc6979 + +import ( + "crypto/dsa" //nolint:staticcheck + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha1" + "crypto/sha512" + "fmt" +) + +// Generates a 521-bit ECDSA key, uses SHA-512 to sign a message, then verifies +// it. +func ExampleSignECDSA() { + // Generate a key pair. + // You need a high-quality PRNG for this. + k, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + if err != nil { + fmt.Println(err) + return + } + + // Hash a message. + alg := sha512.New() + alg.Write([]byte("I am a potato.")) + hash := alg.Sum(nil) + + // Sign the message. You don't need a PRNG for this. + r, s := SignECDSA(k, hash, sha512.New) + + if !ecdsa.Verify(&k.PublicKey, hash, r, s) { + fmt.Println("Invalid signature!") + } + + // Output: +} + +// Generates a 1024-bit DSA key, uses SHA-1 to sign a message, then verifies it. +func ExampleSignDSA() { + // Here I'm generating some DSA params, but you should really pre-generate + // these and re-use them, since this takes a long time and isn't necessary. + k := new(dsa.PrivateKey) + if err := dsa.GenerateParameters(&k.Parameters, rand.Reader, dsa.L1024N160); err != nil { + fmt.Println(err) + return + } + + // Generate a key pair. + // You need a high-quality PRNG for this. + err := dsa.GenerateKey(k, rand.Reader) + if err != nil { + fmt.Println(err) + return + } + + // Hash a message. + alg := sha1.New() + alg.Write([]byte("I am a potato.")) + hash := alg.Sum(nil) + + // Sign the message. You don't need a PRNG for this. + r, s, err := SignDSA(k, hash, sha1.New) + if err != nil { + fmt.Println(err) + return + } + + if !dsa.Verify(&k.PublicKey, hash, r, s) { + fmt.Println("Invalid signature!") + } + + // Output: +} diff --git a/rfc6979.go b/rfc6979.go index 7b0c8af..f4f8b27 100644 --- a/rfc6979.go +++ b/rfc6979.go @@ -1,114 +1,114 @@ -/* -Package rfc6979 is an implementation of RFC 6979's deterministic DSA. - - Such signatures are compatible with standard Digital Signature Algorithm - (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA) digital - signatures and can be processed with unmodified verifiers, which need not be - aware of the procedure described therein. Deterministic signatures retain - the cryptographic security features associated with digital signatures but - can be more easily implemented in various environments, since they do not - need access to a source of high-quality randomness. - -(https://tools.ietf.org/html/rfc6979) - -Provides functions similar to crypto/dsa and crypto/ecdsa. -*/ -package rfc6979 - -import ( - "crypto/hmac" - "hash" - "math/big" -) - -// mac returns an HMAC result for the given key and message as well as -// hmac hash instance itself (that can be reused for the same key after reset). -func mac(alg func() hash.Hash, k, m, buf []byte) ([]byte, hash.Hash) { - h := hmac.New(alg, k) - h.Write(m) - return h.Sum(buf[:0]), h -} - -// macReuse allows to reuse already initialized hmac for the next -// message using the same key. -func macReuse(h hash.Hash, m, buf []byte) []byte { - h.Reset() - h.Write(m) - return h.Sum(buf[:0]) -} - -// https://tools.ietf.org/html/rfc6979#section-2.3.2 -func bits2int(in []byte, qlen int) *big.Int { - vlen := len(in) * 8 - v := new(big.Int).SetBytes(in) - if vlen > qlen { - v.Rsh(v, uint(vlen-qlen)) - } - return v -} - -// bits2IntModQ implements an integer part of bits2octets defined -// in https://tools.ietf.org/html/rfc6979#section-2.3.4 -func bits2IntModQ(in []byte, q *big.Int, qlen int) *big.Int { - z1 := bits2int(in, qlen) - if z1.Cmp(q) < 0 { - return z1 - } - return z1.Sub(z1, q) -} - -var one = big.NewInt(1) - -// https://tools.ietf.org/html/rfc6979#section-3.2 -func generateSecret(q, x *big.Int, alg func() hash.Hash, hash []byte, test func(*big.Int, *big.Int, []byte) bool) { - qlen := q.BitLen() - holen := alg().Size() - rolen := (qlen + 7) >> 3 - - var bx = make([]byte, 2*rolen) - x.FillBytes(bx[:rolen]) // int2octets per https://tools.ietf.org/html/rfc6979#section-2.3.3 - - var hashInt = bits2IntModQ(hash, q, qlen) - hashInt.FillBytes(bx[rolen:]) // int2octets per https://tools.ietf.org/html/rfc6979#section-2.3.3 - - // Step B - var v = make([]byte, holen, holen+1+len(bx)) // see appends below - for i := range holen { - v[i] = 0x01 - } - - // Step C - k := make([]byte, holen) - - // Step D - k, _ = mac(alg, k, append(append(v, 0x00), bx...), k) - - // Step E - v, h := mac(alg, k, v, v) - - // Step F - k = macReuse(h, append(append(v, 0x01), bx...), k) - - // Step G - v, h = mac(alg, k, v, v) - - // Step H - var t = make([]byte, qlen/8) - for { - // Step H1 - t = t[:0] - // Step H2 - for len(t) < qlen/8 { - v = macReuse(h, v, v) - t = append(t, v...) - } - - // Step H3 - secret := bits2int(t, qlen) - if secret.Cmp(one) >= 0 && secret.Cmp(q) < 0 && test(secret, hashInt, t) { - return - } - k, _ = mac(alg, k, append(v, 0x00), k) - v, h = mac(alg, k, v, v) - } -} +/* +Package rfc6979 is an implementation of RFC 6979's deterministic DSA. + + Such signatures are compatible with standard Digital Signature Algorithm + (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA) digital + signatures and can be processed with unmodified verifiers, which need not be + aware of the procedure described therein. Deterministic signatures retain + the cryptographic security features associated with digital signatures but + can be more easily implemented in various environments, since they do not + need access to a source of high-quality randomness. + +(https://tools.ietf.org/html/rfc6979) + +Provides functions similar to crypto/dsa and crypto/ecdsa. +*/ +package rfc6979 + +import ( + "crypto/hmac" + "hash" + "math/big" +) + +// mac returns an HMAC result for the given key and message as well as +// hmac hash instance itself (that can be reused for the same key after reset). +func mac(alg func() hash.Hash, k, m, buf []byte) ([]byte, hash.Hash) { + h := hmac.New(alg, k) + h.Write(m) + return h.Sum(buf[:0]), h +} + +// macReuse allows to reuse already initialized hmac for the next +// message using the same key. +func macReuse(h hash.Hash, m, buf []byte) []byte { + h.Reset() + h.Write(m) + return h.Sum(buf[:0]) +} + +// https://tools.ietf.org/html/rfc6979#section-2.3.2 +func bits2int(in []byte, qlen int) *big.Int { + vlen := len(in) * 8 + v := new(big.Int).SetBytes(in) + if vlen > qlen { + v.Rsh(v, uint(vlen-qlen)) + } + return v +} + +// bits2IntModQ implements an integer part of bits2octets defined +// in https://tools.ietf.org/html/rfc6979#section-2.3.4 +func bits2IntModQ(in []byte, q *big.Int, qlen int) *big.Int { + z1 := bits2int(in, qlen) + if z1.Cmp(q) < 0 { + return z1 + } + return z1.Sub(z1, q) +} + +var one = big.NewInt(1) + +// https://tools.ietf.org/html/rfc6979#section-3.2 +func generateSecret(q, x *big.Int, alg func() hash.Hash, hash []byte, test func(*big.Int, *big.Int, []byte) bool) { + qlen := q.BitLen() + holen := alg().Size() + rolen := (qlen + 7) >> 3 + + var bx = make([]byte, 2*rolen) + x.FillBytes(bx[:rolen]) // int2octets per https://tools.ietf.org/html/rfc6979#section-2.3.3 + + var hashInt = bits2IntModQ(hash, q, qlen) + hashInt.FillBytes(bx[rolen:]) // int2octets per https://tools.ietf.org/html/rfc6979#section-2.3.3 + + // Step B + var v = make([]byte, holen, holen+1+len(bx)) // see appends below + for i := range holen { + v[i] = 0x01 + } + + // Step C + k := make([]byte, holen) + + // Step D + k, _ = mac(alg, k, append(append(v, 0x00), bx...), k) + + // Step E + v, h := mac(alg, k, v, v) + + // Step F + k = macReuse(h, append(append(v, 0x01), bx...), k) + + // Step G + v, h = mac(alg, k, v, v) + + // Step H + var t = make([]byte, qlen/8) + for { + // Step H1 + t = t[:0] + // Step H2 + for len(t) < qlen/8 { + v = macReuse(h, v, v) + t = append(t, v...) + } + + // Step H3 + secret := bits2int(t, qlen) + if secret.Cmp(one) >= 0 && secret.Cmp(q) < 0 && test(secret, hashInt, t) { + return + } + k, _ = mac(alg, k, append(v, 0x00), k) + v, h = mac(alg, k, v, v) + } +} diff --git a/rfc6979_test.go b/rfc6979_test.go index 18e9e52..aa80fce 100644 --- a/rfc6979_test.go +++ b/rfc6979_test.go @@ -1,28 +1,28 @@ -package rfc6979 - -import ( - "crypto/sha256" - "encoding/hex" - "math/big" - "testing" -) - -// https://tools.ietf.org/html/rfc6979#appendix-A.1 -func TestGenerateSecret(t *testing.T) { - q, _ := new(big.Int).SetString("4000000000000000000020108A2E0CC0D99F8A5EF", 16) - - x, _ := new(big.Int).SetString("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16) - - hash, _ := hex.DecodeString("AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF") - - expected, _ := new(big.Int).SetString("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16) - var actual *big.Int - generateSecret(q, x, sha256.New, hash, func(k *big.Int, _ *big.Int, _ []byte) bool { - actual = k - return true - }) - - if actual.Cmp(expected) != 0 { - t.Errorf("Expected %x, got %x", expected, actual) - } -} +package rfc6979 + +import ( + "crypto/sha256" + "encoding/hex" + "math/big" + "testing" +) + +// https://tools.ietf.org/html/rfc6979#appendix-A.1 +func TestGenerateSecret(t *testing.T) { + q, _ := new(big.Int).SetString("4000000000000000000020108A2E0CC0D99F8A5EF", 16) + + x, _ := new(big.Int).SetString("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16) + + hash, _ := hex.DecodeString("AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF") + + expected, _ := new(big.Int).SetString("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16) + var actual *big.Int + generateSecret(q, x, sha256.New, hash, func(k *big.Int, _ *big.Int, _ []byte) bool { + actual = k + return true + }) + + if actual.Cmp(expected) != 0 { + t.Errorf("Expected %x, got %x", expected, actual) + } +}