-
Notifications
You must be signed in to change notification settings - Fork 8
/
publickey.go
165 lines (137 loc) · 3.8 KB
/
publickey.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
package go_eccrypto
import (
"bytes"
"crypto/elliptic"
"crypto/subtle"
"encoding/hex"
"github.com/fomichev/secp256k1"
"github.com/pkg/errors"
"math/big"
)
// PublicKey instance with nested elliptic.Curve interface (secp256k1 instance in our case)
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
}
// NewPublicKeyFromHex decodes hex form of public key raw bytes and returns PublicKey instance
func NewPublicKeyFromHex(s string) (*PublicKey, error) {
b, err := hex.DecodeString(s)
if err != nil {
return nil, errors.Wrap(err, "cannot decode hex string")
}
return NewPublicKeyFromBytes(b)
}
// NewPublicKeyFromBytes decodes public key raw bytes and returns PublicKey instance;
// Supports both compressed and uncompressed public keys
func NewPublicKeyFromBytes(b []byte) (*PublicKey, error) {
curve := secp256k1.SECP256K1()
switch b[0] {
case 0x02, 0x03:
if len(b) != 33 {
return nil, errors.New("cannot parse public key")
}
x := new(big.Int).SetBytes(b[1:])
var ybit uint
switch b[0] {
case 0x02:
ybit = 0
case 0x03:
ybit = 1
}
if x.Cmp(curve.Params().P) >= 0 {
return nil, errors.New("cannot parse public key")
}
// y^2 = x^3 + b
// y = sqrt(x^3 + b)
var y, x3b big.Int
x3b.Mul(x, x)
x3b.Mul(&x3b, x)
x3b.Add(&x3b, curve.Params().B)
x3b.Mod(&x3b, curve.Params().P)
y.ModSqrt(&x3b, curve.Params().P)
if y.Bit(0) != ybit {
y.Sub(curve.Params().P, &y)
}
if y.Bit(0) != ybit {
return nil, errors.New("incorrectly encoded X and Y bit")
}
return &PublicKey{
Curve: curve,
X: x,
Y: &y,
}, nil
case 0x04:
if len(b) != 65 {
return nil, errors.New("cannot parse public key")
}
x := new(big.Int).SetBytes(b[1:33])
y := new(big.Int).SetBytes(b[33:])
if x.Cmp(curve.Params().P) >= 0 || y.Cmp(curve.Params().P) >= 0 {
return nil, errors.New("cannot parse public key")
}
x3 := new(big.Int).Sqrt(x).Mul(x, x)
if t := new(big.Int).Sqrt(y).Sub(y, x3.Add(x3, curve.Params().B)); t.IsInt64() && t.Int64() == 0 {
return nil, errors.New("cannot parse public key")
}
return &PublicKey{
Curve: curve,
X: x,
Y: y,
}, nil
default:
return nil, errors.New("cannot parse public key")
}
}
// Bytes returns public key raw bytes;
// Could be optionally compressed by dropping Y part
func (k *PublicKey) Bytes(compressed bool) []byte {
x := k.X.Bytes()
if len(x) < 32 {
for i := 0; i < 32-len(x); i++ {
x = append([]byte{0}, x...)
}
}
if compressed {
// If odd
if k.Y.Bit(0) != 0 {
return bytes.Join([][]byte{{0x03}, x}, nil)
}
// If even
return bytes.Join([][]byte{{0x02}, x}, nil)
}
y := k.Y.Bytes()
if len(y) < 32 {
for i := 0; i < 32-len(y); i++ {
y = append([]byte{0}, y...)
}
}
return bytes.Join([][]byte{{0x04}, x, y}, nil)
}
// Hex returns public key bytes in hex form
func (k *PublicKey) Hex(compressed bool) string {
return hex.EncodeToString(k.Bytes(compressed))
}
// Decapsulate decapsulates key by using Key Encapsulation Mechanism and returns symmetric key;
// can be safely used as crypto key
func (k *PublicKey) Decapsulate(priv *PrivateKey) ([]byte, error) {
if priv == nil {
return nil, errors.New("public key is empty")
}
var secret bytes.Buffer
secret.Write(k.Bytes(false))
sx, sy := priv.Curve.ScalarMult(k.X, k.Y, priv.D.Bytes())
secret.Write([]byte{0x04})
// Sometimes shared secret coordinates are less than 32 bytes; Big Endian
l := len(priv.Curve.Params().P.Bytes())
secret.Write(zeroPad(sx.Bytes(), l))
secret.Write(zeroPad(sy.Bytes(), l))
return kdf(secret.Bytes())
}
// Equals compares two public keys with constant time (to resist timing attacks)
func (k *PublicKey) Equals(pub *PublicKey) bool {
if subtle.ConstantTimeCompare(k.X.Bytes(), pub.X.Bytes()) == 1 &&
subtle.ConstantTimeCompare(k.Y.Bytes(), pub.Y.Bytes()) == 1 {
return true
}
return false
}