Initial commit - Tutus RFC6979

This commit is contained in:
Admin 2025-12-21 05:57:03 -05:00
commit 247633aaaf
15 changed files with 1234 additions and 0 deletions

9
.github/workflows/dco.yml vendored Normal file
View File

@ -0,0 +1,9 @@
name: DCO check
on:
pull_request:
branches: [ "master" ]
jobs:
dco:
uses: nspcc-dev/.github/.github/workflows/dco.yml@master

116
.github/workflows/run_tests.yml vendored Normal file
View File

@ -0,0 +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

23
.gitignore vendored Normal file
View File

@ -0,0 +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

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: go
go:
- 1.12.x
- 1.13.x

21
LICENSE Normal file
View File

@ -0,0 +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.

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# Tutus RFC6979
RFC6979 deterministic ECDSA signature implementation for the Tutus blockchain.
## Overview
Provides deterministic ECDSA signatures per RFC6979 for cryptographic operations.
## Installation
```go
import "github.com/tutus-one/tutus-rfc6979"
```
## License
MIT License
---
Part of the [Tutus](https://github.com/tutus-one/tutus-chain) blockchain infrastructure.

29
bench_test.go Normal file
View File

@ -0,0 +1,29 @@
package rfc6979_test
import (
"crypto"
"crypto/sha256"
"testing"
"github.com/tutus-one/tutus-rfc6979"
)
func BenchmarkECDSASign(b *testing.B) {
const msg = "Hello world!"
h := sha256.Sum256([]byte(msg))
for b.Loop() {
_, _ = rfc6979.SignECDSA(p256.key, h[:], sha256.New)
}
}
func BenchmarkECDSACore(b *testing.B) {
const msg = "Hello world!"
h := sha256.Sum256([]byte(msg))
for b.Loop() {
_, _ = p256.key.Sign(nil, h[:], crypto.SHA256)
}
}

42
dsa.go Normal file
View File

@ -0,0 +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
}

270
dsa_test.go Normal file
View File

@ -0,0 +1,270 @@
package rfc6979_test
import (
"crypto/dsa" //nolint:staticcheck
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"hash"
"math/big"
"testing"
"github.com/tutus-one/tutus-rfc6979"
)
type dsaFixture struct {
name string
key *dsaKey
alg func() hash.Hash
message string
r, s string
}
type dsaKey struct {
key *dsa.PrivateKey
subgroup int
}
var dsa1024 = &dsaKey{
key: &dsa.PrivateKey{
PublicKey: dsa.PublicKey{
Parameters: dsa.Parameters{
P: dsaLoadInt("86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED8873ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779"),
Q: dsaLoadInt("996F967F6C8E388D9E28D01E205FBA957A5698B1"),
G: dsaLoadInt("07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA417BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD"),
},
Y: dsaLoadInt("5DF5E01DED31D0297E274E1691C192FE5868FEF9E19A84776454B100CF16F65392195A38B90523E2542EE61871C0440CB87C322FC4B4D2EC5E1E7EC766E1BE8D4CE935437DC11C3C8FD426338933EBFE739CB3465F4D3668C5E473508253B1E682F65CBDC4FAE93C2EA212390E54905A86E2223170B44EAA7DA5DD9FFCFB7F3B"),
},
X: dsaLoadInt("411602CB19A6CCC34494D79D98EF1E7ED5AF25F7"),
},
subgroup: 160,
}
var dsa2048 = &dsaKey{
key: &dsa.PrivateKey{
PublicKey: dsa.PublicKey{
Parameters: dsa.Parameters{
P: dsaLoadInt("9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B"),
Q: dsaLoadInt("F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F"),
G: dsaLoadInt("5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7"),
},
Y: dsaLoadInt("667098C654426C78D7F8201EAC6C203EF030D43605032C2F1FA937E5237DBD949F34A0A2564FE126DC8B715C5141802CE0979C8246463C40E6B6BDAA2513FA611728716C2E4FD53BC95B89E69949D96512E873B9C8F8DFD499CC312882561ADECB31F658E934C0C197F2C4D96B05CBAD67381E7B768891E4DA3843D24D94CDFB5126E9B8BF21E8358EE0E0A30EF13FD6A664C0DCE3731F7FB49A4845A4FD8254687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D123AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF"),
},
X: dsaLoadInt("69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC"),
},
subgroup: 256,
}
var dsaFixtures = []dsaFixture{
// DSA, 1024 Bits
// https://tools.ietf.org/html/rfc6979#appendix-A.2.1
{
name: "1024/SHA-1 #1",
key: dsa1024,
alg: sha1.New,
message: "sample",
r: "2E1A0C2562B2912CAAF89186FB0F42001585DA55",
s: "29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5",
},
{
name: "1024/SHA-224 #1",
key: dsa1024,
alg: sha256.New224,
message: "sample",
r: "4BC3B686AEA70145856814A6F1BB53346F02101E",
s: "410697B92295D994D21EDD2F4ADA85566F6F94C1",
},
{
name: "1024/SHA-256 #1",
key: dsa1024,
alg: sha256.New,
message: "sample",
r: "81F2F5850BE5BC123C43F71A3033E9384611C545",
s: "4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89",
},
{
name: "1024/SHA-384 #1",
key: dsa1024,
alg: sha512.New384,
message: "sample",
r: "07F2108557EE0E3921BC1774F1CA9B410B4CE65A",
s: "54DF70456C86FAC10FAB47C1949AB83F2C6F7595",
},
{
name: "1024/SHA-512 #1",
key: dsa1024,
alg: sha512.New,
message: "sample",
r: "16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B",
s: "02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C",
},
{
name: "1024/SHA-1 #2",
key: dsa1024,
alg: sha1.New,
message: "test",
r: "42AB2052FD43E123F0607F115052A67DCD9C5C77",
s: "183916B0230D45B9931491D4C6B0BD2FB4AAF088",
},
{
name: "1024/SHA-224 #2",
key: dsa1024,
alg: sha256.New224,
message: "test",
r: "6868E9964E36C1689F6037F91F28D5F2C30610F2",
s: "49CEC3ACDC83018C5BD2674ECAAD35B8CD22940F",
},
{
name: "1024/SHA-256 #2",
key: dsa1024,
alg: sha256.New,
message: "test",
r: "22518C127299B0F6FDC9872B282B9E70D0790812",
s: "6837EC18F150D55DE95B5E29BE7AF5D01E4FE160",
},
{
name: "1024/SHA-384 #2",
key: dsa1024,
alg: sha512.New384,
message: "test",
r: "854CF929B58D73C3CBFDC421E8D5430CD6DB5E66",
s: "91D0E0F53E22F898D158380676A871A157CDA622",
},
{
name: "1024/SHA-512 #2",
key: dsa1024,
alg: sha512.New,
message: "test",
r: "8EA47E475BA8AC6F2D821DA3BD212D11A3DEB9A0",
s: "7C670C7AD72B6C050C109E1790008097125433E8",
},
// DSA, 2048 Bits
// https://tools.ietf.org/html/rfc6979#appendix-A.2.2
{
name: "2048/SHA-1 #1",
key: dsa2048,
alg: sha1.New,
message: "sample",
r: "3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A",
s: "D26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF",
},
{
name: "2048/SHA-224 #1",
key: dsa2048,
alg: sha256.New224,
message: "sample",
r: "DC9F4DEADA8D8FF588E98FED0AB690FFCE858DC8C79376450EB6B76C24537E2C",
s: "A65A9C3BC7BABE286B195D5DA68616DA8D47FA0097F36DD19F517327DC848CEC",
},
{
name: "2048/SHA-256 #1",
key: dsa2048,
alg: sha256.New,
message: "sample",
r: "EACE8BDBBE353C432A795D9EC556C6D021F7A03F42C36E9BC87E4AC7932CC809",
s: "7081E175455F9247B812B74583E9E94F9EA79BD640DC962533B0680793A38D53",
},
{
name: "2048/SHA-384 #1",
key: dsa2048,
alg: sha512.New384,
message: "sample",
r: "B2DA945E91858834FD9BF616EBAC151EDBC4B45D27D0DD4A7F6A22739F45C00B",
s: "19048B63D9FD6BCA1D9BAE3664E1BCB97F7276C306130969F63F38FA8319021B",
},
{
name: "2048/SHA-512 #1",
key: dsa2048,
alg: sha512.New,
message: "sample",
r: "2016ED092DC5FB669B8EFB3D1F31A91EECB199879BE0CF78F02BA062CB4C942E",
s: "D0C76F84B5F091E141572A639A4FB8C230807EEA7D55C8A154A224400AFF2351",
},
{
name: "2048/SHA-1 #2",
key: dsa2048,
alg: sha1.New,
message: "test",
r: "C18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0",
s: "414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA",
},
{
name: "2048/SHA-224 #2",
key: dsa2048,
alg: sha256.New224,
message: "test",
r: "272ABA31572F6CC55E30BF616B7A265312018DD325BE031BE0CC82AA17870EA3",
s: "E9CC286A52CCE201586722D36D1E917EB96A4EBDB47932F9576AC645B3A60806",
},
{
name: "2048/SHA-256 #2",
key: dsa2048,
alg: sha256.New,
message: "test",
r: "8190012A1969F9957D56FCCAAD223186F423398D58EF5B3CEFD5A4146A4476F0",
s: "7452A53F7075D417B4B013B278D1BB8BBD21863F5E7B1CEE679CF2188E1AB19E",
},
{
name: "2048/SHA-384 #2",
key: dsa2048,
alg: sha512.New384,
message: "test",
r: "239E66DDBE8F8C230A3D071D601B6FFBDFB5901F94D444C6AF56F732BEB954BE",
s: "6BD737513D5E72FE85D1C750E0F73921FE299B945AAD1C802F15C26A43D34961",
},
{
name: "2048/SHA-512 #2",
key: dsa2048,
alg: sha512.New,
message: "test",
r: "89EC4BB1400ECCFF8E7D9AA515CD1DE7803F2DAFF09693EE7FD1353E90A68307",
s: "C9F0BDABCC0D880BB137A994CC7F3980CE91CC10FAF529FC46565B15CEA854E1",
},
}
func TestDSASignatures(t *testing.T) {
for _, f := range dsaFixtures {
testDsaFixture(&f, t)
}
}
func testDsaFixture(f *dsaFixture, t *testing.T) {
t.Logf("Testing %s", f.name)
h := f.alg()
h.Write([]byte(f.message))
digest := h.Sum(nil)
g := f.key.subgroup / 8
if len(digest) > g {
digest = digest[0:g]
}
r, s, err := rfc6979.SignDSA(f.key.key, digest, f.alg)
if err != nil {
t.Error(err)
return
}
expectedR := dsaLoadInt(f.r)
expectedS := dsaLoadInt(f.s)
if r.Cmp(expectedR) != 0 {
t.Errorf("%s: Expected R of %X, got %X", f.name, expectedR, r)
}
if s.Cmp(expectedS) != 0 {
t.Errorf("%s: Expected S of %X, got %X", f.name, expectedS, s)
}
}
func dsaLoadInt(s string) *big.Int {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return new(big.Int).SetBytes(b)
}

38
ecdsa.go Normal file
View File

@ -0,0 +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
}

442
ecdsa_test.go Normal file
View File

@ -0,0 +1,442 @@
package rfc6979_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"hash"
"math/big"
"testing"
"github.com/tutus-one/tutus-rfc6979"
)
type ecdsaFixture struct {
name string
key *ecdsaKey
alg func() hash.Hash
message string
r, s string
}
type ecdsaKey struct {
key *ecdsa.PrivateKey
subgroup int
}
var p224 = &ecdsaKey{
key: &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P224(),
X: ecdsaLoadInt("00CF08DA5AD719E42707FA431292DEA11244D64FC51610D94B130D6C"),
Y: ecdsaLoadInt("EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A"),
},
D: ecdsaLoadInt("F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1"),
},
subgroup: 224,
}
var p256 = &ecdsaKey{
key: &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P256(),
X: ecdsaLoadInt("60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6"),
Y: ecdsaLoadInt("7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299"),
},
D: ecdsaLoadInt("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721"),
},
subgroup: 256,
}
var p384 = &ecdsaKey{
key: &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P384(),
X: ecdsaLoadInt("EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13"),
Y: ecdsaLoadInt("8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720"),
},
D: ecdsaLoadInt("6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5"),
},
subgroup: 384,
}
var p521 = &ecdsaKey{
key: &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P521(),
X: ecdsaLoadInt("1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4"),
Y: ecdsaLoadInt("0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5"),
},
D: ecdsaLoadInt("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538"),
},
subgroup: 521,
}
var fixtures = []ecdsaFixture{
// ECDSA, 224 Bits (Prime Field)
// https://tools.ietf.org/html/rfc6979#appendix-A.2.4
{
name: "P224/SHA-1 #1",
key: p224,
alg: sha1.New,
message: "sample",
r: "22226F9D40A96E19C4A301CE5B74B115303C0F3A4FD30FC257FB57AC",
s: "66D1CDD83E3AF75605DD6E2FEFF196D30AA7ED7A2EDF7AF475403D69",
},
{
name: "P224/SHA-224 #1",
key: p224,
alg: sha256.New224,
message: "sample",
r: "1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E",
s: "A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC",
},
{
name: "P224/SHA-256 #1",
key: p224,
alg: sha256.New,
message: "sample",
r: "61AA3DA010E8E8406C656BC477A7A7189895E7E840CDFE8FF42307BA",
s: "BC814050DAB5D23770879494F9E0A680DC1AF7161991BDE692B10101",
},
{
name: "P224/SHA-384 #1",
key: p224,
alg: sha512.New384,
message: "sample",
r: "0B115E5E36F0F9EC81F1325A5952878D745E19D7BB3EABFABA77E953",
s: "830F34CCDFE826CCFDC81EB4129772E20E122348A2BBD889A1B1AF1D",
},
{
name: "P224/SHA-512 #1",
key: p224,
alg: sha512.New,
message: "sample",
r: "074BD1D979D5F32BF958DDC61E4FB4872ADCAFEB2256497CDAC30397",
s: "A4CECA196C3D5A1FF31027B33185DC8EE43F288B21AB342E5D8EB084",
},
{
name: "P224/SHA-1 #2",
key: p224,
alg: sha1.New,
message: "test",
r: "DEAA646EC2AF2EA8AD53ED66B2E2DDAA49A12EFD8356561451F3E21C",
s: "95987796F6CF2062AB8135271DE56AE55366C045F6D9593F53787BD2",
},
{
name: "P224/SHA-224 #2",
key: p224,
alg: sha256.New224,
message: "test",
r: "C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019",
s: "902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4",
},
{
name: "P224/SHA-256 #2",
key: p224,
alg: sha256.New,
message: "test",
r: "AD04DDE87B84747A243A631EA47A1BA6D1FAA059149AD2440DE6FBA6",
s: "178D49B1AE90E3D8B629BE3DB5683915F4E8C99FDF6E666CF37ADCFD",
},
{
name: "P224/SHA-384 #2",
key: p224,
alg: sha512.New384,
message: "test",
r: "389B92682E399B26518A95506B52C03BC9379A9DADF3391A21FB0EA4",
s: "414A718ED3249FF6DBC5B50C27F71F01F070944DA22AB1F78F559AAB",
},
{
name: "P224/SHA-512 #2",
key: p224,
alg: sha512.New,
message: "test",
r: "049F050477C5ADD858CAC56208394B5A55BAEBBE887FDF765047C17C",
s: "077EB13E7005929CEFA3CD0403C7CDCC077ADF4E44F3C41B2F60ECFF",
},
// ECDSA, 256 Bits (Prime Field)
// https://tools.ietf.org/html/rfc6979#appendix-A.2.5
{
name: "P256/SHA-1 #1",
key: p256,
alg: sha1.New,
message: "sample",
r: "61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D32",
s: "6D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB",
},
{
name: "P256/SHA-224 #1",
key: p256,
alg: sha256.New224,
message: "sample",
r: "53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F",
s: "B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C",
},
{
name: "P256/SHA-256 #1",
key: p256,
alg: sha256.New,
message: "sample",
r: "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716",
s: "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8",
},
{
name: "P256/SHA-384 #1",
key: p256,
alg: sha512.New384,
message: "sample",
r: "0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF7719",
s: "4861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954",
},
{
name: "P256/SHA-512 #1",
key: p256,
alg: sha512.New,
message: "sample",
r: "8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F00",
s: "2362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE",
},
{
name: "P256/SHA-1 #2",
key: p256,
alg: sha1.New,
message: "test",
r: "0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89",
s: "01B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1",
},
{
name: "P256/SHA-224 #2",
key: p256,
alg: sha256.New224,
message: "test",
r: "C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692",
s: "C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D",
},
{
name: "P256/SHA-256 #2",
key: p256,
alg: sha256.New,
message: "test",
r: "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367",
s: "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083",
},
{
name: "P256/SHA-384 #2",
key: p256,
alg: sha512.New384,
message: "test",
r: "83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB6",
s: "8DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C",
},
{
name: "P256/SHA-512 #2",
key: p256,
alg: sha512.New,
message: "test",
r: "461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04",
s: "39AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55",
},
// ECDSA, 384 Bits (Prime Field)
// https://tools.ietf.org/html/rfc6979#appendix-A.2.6
{
name: "P384/SHA-1 #1",
key: p384,
alg: sha1.New,
message: "sample",
r: "EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA37B9BA002899F6FDA3A4A9386790D4EB2",
s: "A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF26F49CA031D4857570CCB5CA4424A443",
},
{
name: "P384/SHA-224 #1",
key: p384,
alg: sha256.New224,
message: "sample",
r: "42356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366450F76EE3DE43F5A125333A6BE060122",
s: "9DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E4834C082C03D83028EFBF93A3C23940CA8D",
},
{
name: "P384/SHA-256 #1",
key: p384,
alg: sha256.New,
message: "sample",
r: "21B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33BDE1E888E63355D92FA2B3C36D8FB2CD",
s: "F3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEBEFDC63ECCD1AC42EC0CB8668A4FA0AB0",
},
{
name: "P384/SHA-384 #1",
key: p384,
alg: sha512.New384,
message: "sample",
r: "94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46",
s: "99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8",
},
{
name: "P384/SHA-512 #1",
key: p384,
alg: sha512.New,
message: "sample",
r: "ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799CFE30F35CC900056D7C99CD7882433709",
s: "512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112DC7CC3EF3446DEFCEB01A45C2667FDD5",
},
{
name: "P384/SHA-1 #2",
key: p384,
alg: sha1.New,
message: "test",
r: "4BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678ACD9D29876DAF46638645F7F404B11C7",
s: "D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A2991695BA1C84541327E966FA7B50F7382282",
},
{
name: "P384/SHA-224 #2",
key: p384,
alg: sha256.New224,
message: "test",
r: "E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E62464A9A817C47FF78B8C11066B24080E72",
s: "07041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C6141C53EA5ABEF0D8231077A04540A96B66",
},
{
name: "P384/SHA-256 #2",
key: p384,
alg: sha256.New,
message: "test",
r: "6D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559F918EEDAF2293BE5B475CC8F0188636B",
s: "2D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D51AB373F9845C0514EEFB14024787265",
},
{
name: "P384/SHA-384 #2",
key: p384,
alg: sha512.New384,
message: "test",
r: "8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB",
s: "DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5",
},
{
name: "P384/SHA-512 #2",
key: p384,
alg: sha512.New,
message: "test",
r: "A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D06FB6495CD21B4B6E340FC236584FB277",
s: "976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B224634A2092CD3792E0159AD9CEE37659C736",
},
// ECDSA, 521 Bits (Prime Field)
// https://tools.ietf.org/html/rfc6979#appendix-A.2.7
{
name: "P521/SHA-1 #1",
key: p521,
alg: sha1.New,
message: "sample",
r: "0343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D75D",
s: "0E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5D16",
},
{
name: "P521/SHA-224 #1",
key: p521,
alg: sha256.New224,
message: "sample",
r: "1776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A30715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2ED2E",
s: "050CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17BA41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B41F",
},
{
name: "P521/SHA-256 #1",
key: p521,
alg: sha256.New,
message: "sample",
r: "1511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E1A7",
s: "04A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7ECFC",
},
{
name: "P521/SHA-384 #1",
key: p521,
alg: sha512.New384,
message: "sample",
r: "1EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C67451",
s: "1F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65D61",
},
{
name: "P521/SHA-512 #1",
key: p521,
alg: sha512.New,
message: "sample",
r: "0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA",
s: "0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A",
},
{
name: "P521/SHA-1 #2",
key: p521,
alg: sha1.New,
message: "test",
r: "13BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D0367",
s: "1E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC916797FF",
},
{
name: "P521/SHA-224 #2",
key: p521,
alg: sha256.New224,
message: "test",
r: "1C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE17FB",
s: "177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD519A4",
},
{
name: "P521/SHA-256 #2",
key: p521,
alg: sha256.New,
message: "test",
r: "00E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D8071042EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656AA8",
s: "0CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694E86",
},
{
name: "P521/SHA-384 #2",
key: p521,
alg: sha512.New384,
message: "test",
r: "14BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF6075578C",
s: "133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0ED94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B979",
},
{
name: "P521/SHA-512 #2",
key: p521,
alg: sha512.New,
message: "test",
r: "13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D",
s: "1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3",
},
}
func TestECDSA(t *testing.T) {
for _, f := range fixtures {
testEcsaFixture(&f, t)
}
}
func ecdsaLoadInt(s string) (n *big.Int) {
n, _ = new(big.Int).SetString(s, 16)
return
}
func testEcsaFixture(f *ecdsaFixture, t *testing.T) {
t.Logf("Testing %s", f.name)
h := f.alg()
h.Write([]byte(f.message))
digest := h.Sum(nil)
g := f.key.subgroup / 8
if len(digest) > g {
digest = digest[0:g]
}
r, s := rfc6979.SignECDSA(f.key.key, digest, f.alg)
expectedR := ecdsaLoadInt(f.r)
expectedS := ecdsaLoadInt(f.s)
if r.Cmp(expectedR) != 0 {
t.Errorf("%s: Expected R of %X, got %X", f.name, expectedR, r)
}
if s.Cmp(expectedS) != 0 {
t.Errorf("%s: Expected S of %X, got %X", f.name, expectedS, s)
}
}

74
example_test.go Normal file
View File

@ -0,0 +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:
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/tutus-one/tutus-rfc6979
go 1.24

114
rfc6979.go Normal file
View File

@ -0,0 +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)
}
}

28
rfc6979_test.go Normal file
View File

@ -0,0 +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)
}
}