-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
exchange.go
183 lines (159 loc) · 4.54 KB
/
exchange.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
package crypto
import (
"crypto/ecdh"
"crypto/rand"
"github.com/Laisky/errors/v2"
"github.com/monnand/dhkx"
)
var (
_ KeyExchange = new(DHKX)
_ KeyExchange = new(ECDH)
)
// KeyExchange key agreement interface
type KeyExchange interface {
// PublicKey return public key bytes
//
// send public key to peer, and get peer's public key
// every side of the exchange peers will generate the same key
PublicKey() ([]byte, error)
// GenerateKey generate new key by peer's public key
GenerateKey(peerPubKey []byte) ([]byte, error)
}
// Diffie Hellman Key-exchange algorithm
//
// https://pkg.go.dev/github.com/monnand/dhkx
//
// # Example
//
// alice, _ := NewDHKX()
// bob, _ := NewDHKX()
//
// alicePub := alice.PublicKey()
// bobPub := bob.PublicKey()
//
// aliceKey, _ := alice.GenerateKey(bobPub)
// bobKey, _ := bob.GenerateKey(alicePub)
//
// aliceKey == bobKey
//
// Note: recommoend to use ECDH instead of DHKX
type DHKX struct {
g *dhkx.DHGroup
priv *dhkx.DHKey
}
type dhkxOption struct {
group int
}
func (o *dhkxOption) fillDefault() *dhkxOption {
return o
}
func (o *dhkxOption) applyOpts(opts ...DHKXOptionFunc) (*dhkxOption, error) {
for _, opt := range opts {
if err := opt(o); err != nil {
return nil, err
}
}
return o, nil
}
// DHKXOptionFunc optional func to set dhkx option
type DHKXOptionFunc func(*dhkxOption) error
// NewDHKX create a new DHKX instance
//
// each DHKX instance has it's unique group and private key
//
// Note: recommoend to use ECDH instead of DHKX
func NewDHKX(optfs ...DHKXOptionFunc) (d *DHKX, err error) {
opt, err := new(dhkxOption).fillDefault().applyOpts(optfs...)
if err != nil {
return nil, err
}
d = new(DHKX)
if d.g, err = dhkx.GetGroup(opt.group); err != nil {
return nil, errors.Wrap(err, "get group")
}
if d.priv, err = d.g.GeneratePrivateKey(nil); err != nil {
return nil, errors.Wrap(err, "generate key")
}
return d, nil
}
// PublicKey return public key bytes
func (d *DHKX) PublicKey() ([]byte, error) {
return d.priv.Bytes(), nil
}
// GenerateKey generate new key by peer's public key
//
// each side of the DHKX exchange peers will generate the same key
//
// key like:
//
// 60a425ca3a4cc313db9c113a0526f3809725305afc68e1accd0e653ae8d0182c6eb05557f4b5d094
// f015972b9fda7d60c1b64d79f50baea7365d858ede0fb7a6571403d4b95f682144b56fa17ffcbe9e
// 70de69dc0045672696e683c423c5b3dfc02a6916be1e50c74e60353ec08a465cc124e8ca88337fb7
// 4a0370e17a7cedb0b1e76733f43ad3db9e3d29ab43c75686a8bc4a88ee46addbd1590c8277d1b1ef
// 42aded6cc0bfe0a7ff8933861dae772c755087f2a41021f4ca53867ba49797d111ef21b381cb6441
// 178f4ccd3748f8e7b1a12ec3799571a49fc0aa793c05ab6e228b559f1fda2912542d7246388ccec1
// 38b4d8ce9df4a32c198891c4e33b5034
func (d *DHKX) GenerateKey(peerPubKey []byte) ([]byte, error) {
k, err := d.g.ComputeKey(dhkx.NewPublicKey(peerPubKey), d.priv)
if err != nil {
return nil, errors.Wrap(err, "compute key")
}
return k.Bytes(), nil
}
// ECDH Elliptic Curve Diffie-Hellman
type ECDH struct {
priv *ecdh.PrivateKey
}
// NewEcdh create a new ECDH instance
func NewEcdh(curve ECDSACurve) (ins *ECDH, err error) {
ins = new(ECDH)
switch curve {
case ECDSACurveP256:
ins.priv, err = ecdh.P256().GenerateKey(rand.Reader)
case ECDSACurveP384:
ins.priv, err = ecdh.P384().GenerateKey(rand.Reader)
case ECDSACurveP521:
ins.priv, err = ecdh.P521().GenerateKey(rand.Reader)
default:
return nil, errors.Errorf("unsupport curve %s", curve)
}
if err != nil {
return nil, errors.Wrap(err, "generate key")
}
return ins, nil
}
// PublicKey return public key bytes
func (e *ECDH) PublicKey() ([]byte, error) {
switch e.priv.Curve() {
case ecdh.P256():
return append([]byte{byte(1)}, e.priv.PublicKey().Bytes()...), nil
case ecdh.P384():
return append([]byte{byte(2)}, e.priv.PublicKey().Bytes()...), nil
case ecdh.P521():
return append([]byte{byte(3)}, e.priv.PublicKey().Bytes()...), nil
default:
return nil, errors.Errorf("unsupport curve %s", e.priv.Curve())
}
}
// GenerateKey generate new key by peer's public key
func (e *ECDH) GenerateKey(peerPubKey []byte) (sharekey []byte, err error) {
var pubkey *ecdh.PublicKey
switch peerPubKey[0] {
case 1:
pubkey, err = ecdh.P256().NewPublicKey(peerPubKey[1:])
case 2:
pubkey, err = ecdh.P384().NewPublicKey(peerPubKey[1:])
case 3:
pubkey, err = ecdh.P521().NewPublicKey(peerPubKey[1:])
default:
return nil, errors.Errorf("unsupport curve %d", peerPubKey[0])
}
if err != nil {
return nil, errors.Wrap(err, "new public key")
}
sharekey, err = e.priv.ECDH(pubkey)
if err != nil {
return nil, errors.Wrap(err, "ecdh")
}
return sharekey, nil
}