Compare commits
3 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
2c55dd0785 | |
|
|
24720bf5a6 | |
|
|
6724b49798 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
language: go
|
||||
go:
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
|
|
|
|||
42
LICENSE
42
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.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"testing"
|
||||
|
||||
"github.com/tutus-one/tutus-rfc6979"
|
||||
"git.marketally.com/tutus-one/tutus-rfc6979"
|
||||
)
|
||||
|
||||
func BenchmarkECDSASign(b *testing.B) {
|
||||
|
|
|
|||
84
dsa.go
84
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/tutus-one/tutus-rfc6979"
|
||||
"git.marketally.com/tutus-one/tutus-rfc6979"
|
||||
)
|
||||
|
||||
type dsaFixture struct {
|
||||
|
|
|
|||
76
ecdsa.go
76
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/tutus-one/tutus-rfc6979"
|
||||
"git.marketally.com/tutus-one/tutus-rfc6979"
|
||||
)
|
||||
|
||||
type ecdsaFixture struct {
|
||||
|
|
|
|||
148
example_test.go
148
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:
|
||||
}
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -1,3 +1,3 @@
|
|||
module github.com/tutus-one/tutus-rfc6979
|
||||
module git.marketally.com/tutus-one/tutus-rfc6979
|
||||
|
||||
go 1.24
|
||||
|
|
|
|||
228
rfc6979.go
228
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue