-
Notifications
You must be signed in to change notification settings - Fork 26
/
handshake.go
163 lines (138 loc) · 4.56 KB
/
handshake.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
package handshake
import (
crand "crypto/rand"
"encoding/base64"
ggio "github.com/gogo/protobuf/io"
p2pcrypto "github.com/libp2p/go-libp2p/core/crypto"
"golang.org/x/crypto/nacl/box"
"berty.tech/weshnet/pkg/cryptoutil"
"berty.tech/weshnet/pkg/errcode"
"berty.tech/weshnet/pkg/tyber"
)
// Constant nonces
var (
nonceRequesterAuthenticate = [cryptoutil.NonceSize]byte{1}
nonceResponderAccept = [cryptoutil.NonceSize]byte{2}
)
// Common struct and methods
type handshakeContext struct {
reader ggio.Reader
writer ggio.Writer
ownAccountID p2pcrypto.PrivKey
peerAccountID p2pcrypto.PubKey
ownEphemeral *[cryptoutil.KeySize]byte
peerEphemeral *[cryptoutil.KeySize]byte
sharedEphemeral *[cryptoutil.KeySize]byte
}
func (hc *handshakeContext) toTyberStepMutator() tyber.StepMutator {
return func(s tyber.Step) tyber.Step {
if hc == nil {
return s
}
if hc.peerAccountID != nil {
if cpkb, err := hc.peerAccountID.Raw(); err == nil {
s.Details = append(s.Details, tyber.Detail{Name: "ContactPublicKey", Description: base64.RawURLEncoding.EncodeToString(cpkb)})
}
}
for key, val := range map[string]*[cryptoutil.KeySize]byte{
"OwnEphemeral": hc.ownEphemeral,
"PeerEphemeral": hc.peerEphemeral,
"SharedEphemeral": hc.sharedEphemeral,
} {
if val != nil {
s.Details = append(s.Details, tyber.Detail{
Name: key,
Description: base64.RawURLEncoding.EncodeToString(val[:]),
})
}
}
return s
}
}
// Generates own Ephemeral key pair and send pub key to peer
func (hc *handshakeContext) generateOwnEphemeralAndSendPubKey() error {
// Generate own Ephemeral key pair
ownEphemeralPub, ownEphemeralPriv, err := box.GenerateKey(crand.Reader)
if err != nil {
return errcode.ErrCryptoKeyGeneration.Wrap(err)
}
// Set own Ephemeral priv key in Handshake Context
hc.ownEphemeral = ownEphemeralPriv
// Send own Ephemeral pub key to peer
hello := HelloPayload{EphemeralPubKey: ownEphemeralPub[:]}
if err := hc.writer.WriteMsg(&hello); err != nil {
return errcode.ErrStreamWrite.Wrap(err)
}
return nil
}
// Receives peer's Ephemeral pub key
func (hc *handshakeContext) receivePeerEphemeralPubKey() error {
var err error
// Receive peer's Ephemeral pub key
hello := HelloPayload{}
if err := hc.reader.ReadMsg(&hello); err != nil {
return errcode.ErrStreamRead.Wrap(err)
}
// Set peer's Ephemeral pub key in Handshake Context
hc.peerEphemeral, err = cryptoutil.KeySliceToArray(hello.EphemeralPubKey)
if err != nil {
return errcode.ErrSerialization.Wrap(err)
}
return nil
}
// Computes box key for step 3 (Requester Authenticate): box[a.b|a.B]
func (hc *handshakeContext) computeRequesterAuthenticateBoxKey(asRequester bool) (*[cryptoutil.KeySize]byte, error) {
var sharedReqEphemeralRespAccountID [cryptoutil.KeySize]byte
// If this function was called by the requester
if asRequester {
// Convert Ed25519 peer's AccountID key to X25519 key
mongPeerAccountID, err := cryptoutil.EdwardsToMontgomeryPub(hc.peerAccountID)
if err != nil {
return nil, errcode.ErrCryptoKeyConversion.Wrap(err)
}
// Compute shared key from own Ephemeral key and peer's AccountID key
box.Precompute(
&sharedReqEphemeralRespAccountID,
mongPeerAccountID,
hc.ownEphemeral,
)
} else {
// Convert Ed25519 own AccountID key to X25519 key
mongOwnAccountID, err := cryptoutil.EdwardsToMontgomeryPriv(hc.ownAccountID)
if err != nil {
return nil, errcode.ErrCryptoKeyConversion.Wrap(err)
}
// Compute shared key from peer's Ephemeral key and own AccountID key
box.Precompute(
&sharedReqEphemeralRespAccountID,
hc.peerEphemeral,
mongOwnAccountID,
)
}
// Concatenate both shared keys and hash them using sha256
boxKey := cryptoutil.ConcatAndHashSha256(
hc.sharedEphemeral[:],
sharedReqEphemeralRespAccountID[:],
)
return boxKey, nil
}
// Computes box key for step 4 (Responder Accept): box[a.b|A.B]
func (hc *handshakeContext) computeResponderAcceptBoxKey() (*[cryptoutil.KeySize]byte, error) {
var sharedAccountID [cryptoutil.KeySize]byte
// Convert Ed25519 AccountID keys to X25519 keys
mongOwnAccountID, mongPeerAccountID, err := cryptoutil.EdwardsToMontgomery(
hc.ownAccountID,
hc.peerAccountID,
)
if err != nil {
return nil, errcode.ErrCryptoKeyConversion.Wrap(err)
}
// Compute shared key from AccountID keys (X25519 converted)
box.Precompute(&sharedAccountID, mongPeerAccountID, mongOwnAccountID)
// Concatenate both shared keys and hash them using sha256
boxKey := cryptoutil.ConcatAndHashSha256(
hc.sharedEphemeral[:],
sharedAccountID[:],
)
return boxKey, nil
}