forked from taurushq-io/multi-party-sig
-
Notifications
You must be signed in to change notification settings - Fork 0
/
signature.go
202 lines (173 loc) · 5.48 KB
/
signature.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package taproot
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"fmt"
"io"
"sync/atomic"
"github.com/taurusgroup/multi-party-sig/pkg/math/curve"
)
// TaggedHash addes some domain separation to SHA-256.
//
// This is the hash_tag function mentioned in BIP-340.
//
// See: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#specification
func TaggedHash(tag string, datas ...[]byte) []byte {
tagSum := sha256.Sum256([]byte(tag))
h := sha256.New()
h.Write(tagSum[:])
h.Write(tagSum[:])
for _, data := range datas {
h.Write(data)
}
return h.Sum(nil)
}
// SecretKeyLength is the number of bytes in a SecretKey.
const SecretKeyLength = 32
// SecretKey represents a secret key for BIP-340 signatures.
//
// This is simply an array of 32 bytes.
type SecretKey []byte
// PublicKey represents a public key for BIP-340 signatures.
//
// This key allows verifying signatures produced with the corresponding secret key.
//
// This is simply an array of 32 bytes.
type PublicKey []byte
// Public calculates the public key corresponding to a given secret key.
//
// This will return an error if the secret key is invalid.
//
// See: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#public-key-generation
func (s SecretKey) Public() (PublicKey, error) {
scalar := new(curve.Secp256k1Scalar)
if err := scalar.UnmarshalBinary(s); err != nil || scalar.IsZero() {
return nil, fmt.Errorf("invalid secret key")
}
point := scalar.ActOnBase().(*curve.Secp256k1Point)
return PublicKey(point.XBytes()), nil
}
// GenKey generates a new key-pair, from a source of randomness.
//
// Errors returned by this function will only come from the reader. If you know
// that the reader will never return errors, you can rest assured that this
// function won't either.
func GenKey(rand io.Reader) (SecretKey, PublicKey, error) {
for {
secret := SecretKey(make([]byte, SecretKeyLength))
if _, err := io.ReadFull(rand, secret); err != nil {
return nil, nil, err
}
if public, err := secret.Public(); err == nil {
return secret, public, nil
}
}
}
// SignatureLen is the number of bytes in a Signature.
const SignatureLen = 64
// Signature represents a signature according to BIP-340.
//
// This should exactly SignatureLen = 64 bytes long.
//
// This can only be produced using a secret key, but anyone with a public key
// can verify the integrity of the signature.
type Signature []byte
// signatureCounter is an atomic counter used to add some fault
// resistance in case we don't use a source of randomness for Sign
var signatureCounter uint64
// Sign uses a secret key to create a new signature.
//
// Note that m should be the hash of a message, and not the actual message.
//
// This accepts a source of randomness, but nil can be passed to use entirely
// deterministic signatures. Adding randomness hardens the implementation against
// fault attacks, but isn't strictly necessary for security.
//
// Without randomness, an atomic counter is used to also hedge against attacks.
//
// Errors will be returned if the source of randomness produces an error,
// or if the secret key is invalid.
func (sk SecretKey) Sign(rand io.Reader, m []byte) (Signature, error) {
// See: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#default-signing
d := new(curve.Secp256k1Scalar)
if err := d.UnmarshalBinary(sk); err != nil || d.IsZero() {
return nil, fmt.Errorf("invalid secret key")
}
P := d.ActOnBase().(*curve.Secp256k1Point)
PBytes := P.XBytes()
if !P.HasEvenY() {
d.Negate()
}
a := make([]byte, 32)
k := new(curve.Secp256k1Scalar)
for k.IsZero() {
// Either read new random bytes into a, or increment a global counter.
//
// Either way, the probability of us not finding a valid nonce
// is negligeable.
if rand != nil {
if _, err := io.ReadFull(rand, a); err != nil {
return nil, err
}
} else {
// Need to use atomics, because of potential multi-threading
ctr := atomic.AddUint64(&signatureCounter, 1)
binary.BigEndian.PutUint64(a, ctr)
}
t, _ := d.MarshalBinary()
aHash := TaggedHash("BIP0340/aux", a)
for i := 0; i < 32; i++ {
t[i] ^= aHash[i]
}
randHash := TaggedHash("BIP0340/nonce", t[:], PBytes, m)
_ = k.UnmarshalBinary(randHash)
if k.IsZero() {
return nil, fmt.Errorf("invalid nonce")
}
}
R := k.ActOnBase().(*curve.Secp256k1Point)
if !R.HasEvenY() {
k.Negate()
}
RBytes := R.XBytes()[:]
eHash := TaggedHash("BIP0340/challenge", RBytes, PBytes, m)
e := new(curve.Secp256k1Scalar)
_ = e.UnmarshalBinary(eHash)
z := e.Mul(d).Add(k)
zBytes, _ := z.MarshalBinary()
sig := make([]byte, 0, SignatureLen)
sig = append(sig, RBytes...)
sig = append(sig, zBytes[:]...)
return Signature(sig), nil
}
// Verify checks the integrity of a signature, using a public key.
//
// Note that m is the hash of a message, and not the message itself.
func (pk PublicKey) Verify(sig Signature, m []byte) bool {
// See: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#verification
if len(sig) != SignatureLen {
return false
}
P, err := curve.Secp256k1{}.LiftX(pk)
if err != nil {
return false
}
s := new(curve.Secp256k1Scalar)
if err := s.UnmarshalBinary(sig[32:]); err != nil {
return false
}
eHash := TaggedHash("BIP0340/challenge", sig[:32], pk, m)
e := new(curve.Secp256k1Scalar)
_ = e.UnmarshalBinary(eHash)
R := s.ActOnBase()
check2 := R.Sub(e.Act(P))
check := check2.(*curve.Secp256k1Point)
if check.IsIdentity() {
return false
}
if !check.HasEvenY() {
return false
}
return bytes.Equal(check.XBytes(), sig[:32])
}