-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathecdsa_recover_test.go
127 lines (111 loc) · 2.81 KB
/
ecdsa_recover_test.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
// Copyright (c) 2021 dustinxie. All rights reserved.
//
// Use of this source code is governed by MIT license
// that can be found in the LICENSE file.
package ecc
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"math/big"
"testing"
)
func TestRecoverPubkey(t *testing.T) {
for _, curve := range []elliptic.Curve{
elliptic.P224(),
elliptic.P256(),
elliptic.P384(),
elliptic.P521(),
P256k1(),
} {
privKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
panic(err)
}
for _, hashed := range [][]byte{
make([]byte, 64),
[]byte("testing"),
} {
for _, flag := range []byte{
Normal,
LowerS,
RecID,
LowerS | RecID,
} {
b, err := SignBytes(privKey, hashed, flag)
if err != nil {
t.Errorf("SignBytes failed for %T", curve)
}
k := testRecoverPubkey(t, curve.Params(), hashed, b, flag)
if k != nil {
if !privKey.PublicKey.Equal(k) {
t.Errorf("Recovered pubkey %v not equal %v", k, privKey.PublicKey)
}
}
}
}
}
}
// returns 2 pubkeys, first is the correct one, second is a key with tampered r
func testRecoverPubkey(t *testing.T, param *elliptic.CurveParams, hash, sig []byte, flag byte) *ecdsa.PublicKey {
var (
k, k1 *ecdsa.PublicKey
err error
)
k, err = RecoverPubkey(param.Name, hash, sig)
if flag&RecID == 0 {
if err == nil {
t.Error("RecoverPubkey pass w/o recovery id")
}
return nil
}
if err != nil {
t.Error(err.Error())
return nil
}
if k == nil {
t.Error("RecoverPubkey returns nil")
return nil
}
if !VerifyBytes(k, hash, sig, flag) {
t.Error("Recovered pubkey failed verification")
}
// invalid recovery id
size := len(sig)
v := sig[size-1]
sig[size-1] = 4
if _, err = RecoverPubkey(param.Name, hash, sig); err == nil {
t.Error("RecoverPubkey pass with invalid recovery id")
}
// add N to r fails the recovery
if v <= 1 {
sig[size-1] = v + 2
if _, err = RecoverPubkey(param.Name, hash, sig); err == nil {
t.Error("RecoverPubkey pass with r+N")
}
}
// flipping the v, that is (r, s, v^1) will generate a different key
sig[size-1] = v ^ 1
if k1, err = RecoverPubkey(param.Name, hash, sig); err != nil {
t.Error("RecoverPubkey fail flipping")
}
// this key can pass verification as well
if !VerifyBytes(k1, hash, sig, flag) {
t.Error("Flipped pubkey failed verification")
}
// but not equal to correct key
if k1.Equal(k) {
t.Errorf("Flipped pubkey %v equal %v", k1, k)
}
// ECDSA signature malleability: (r, N-s, v^1) is also a valid signature
rSize := (size - 1) / 2
s := new(big.Int).SetBytes(sig[rSize : 2*rSize])
s.Sub(param.N, s).FillBytes(sig[rSize : 2*rSize])
if k1, err = RecoverPubkey(param.Name, hash, sig); err != nil {
t.Error("RecoverPubkey fail flipping")
}
if !k.Equal(k1) {
t.Errorf("Flipped pubkey %v not equal %v", k, k1)
}
return k
}