/
noise.go
137 lines (125 loc) · 3.56 KB
/
noise.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
// Package noise integrates keys with the Noise protocol.
package noise
import (
"github.com/flynn/noise"
"github.com/keys-pub/keys"
"github.com/pkg/errors"
)
// Cipher provides symmetric encryption and decryption after a successful
// handshake.
type Cipher interface {
Encrypt(out, ad, plaintext []byte) ([]byte, error)
Decrypt(out, ad, ciphertext []byte) ([]byte, error)
}
// Handshake using noise protocol.
// See http://www.noiseprotocol.org/.
type Handshake struct {
initiator bool
state *noise.HandshakeState
csI0 *noise.CipherState
csI1 *noise.CipherState
csR0 *noise.CipherState
csR1 *noise.CipherState
}
// NewHandshake returns a Handshake for X25519Key sender and recipient.
//
// The cipher suite used is:
// Curve25519 ECDH, ChaCha20-Poly1305 AEAD, BLAKE2b hash.
//
// The handshake uses the KK pattern:
// - K = Static key for initiator Known to responder
// - K = Static key for responder Known to initiator
//
// One of the Noise participants should be the initiator.
//
// The order of the handshake writes/reads should be:
// - (1) Initiator: Write
// - (2) Responder: Read
// - (3) Initiator: Read
// - (4) Responder: Write
//
// When the handshake is complete, use the Cipher to Encrypt/Decrypt.
//
func NewHandshake(sender *keys.X25519Key, recipient *keys.X25519PublicKey, initiator bool) (*Handshake, error) {
dhKey := noise.DHKey{
Private: sender.PrivateKey()[:],
Public: sender.PublicKey().Bytes(),
}
pk := recipient.Bytes()
cs := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2b)
config := noise.Config{
CipherSuite: cs,
Pattern: noise.HandshakeKK,
Initiator: initiator,
Prologue: []byte("keys.pub/1.0"),
StaticKeypair: dhKey,
PeerStatic: pk,
}
state, err := noise.NewHandshakeState(config)
if err != nil {
return nil, err
}
return &Handshake{
initiator: initiator,
state: state,
}, nil
}
// Write performs handshake write.
// You can include optional payload bytes, as the pattern allows zero-RTT
// encryption, meaning the initiator can encrypt the first handshake payload.
//
// The order of the handshake writes/reads should be:
// (1) Initiator: Write
// (2) Responder: Read
// (3) Initiator: Read
// (4) Responder: Write
func (n *Handshake) Write(payload []byte) ([]byte, error) {
if n.Complete() {
return nil, errors.Errorf("handshake already complete")
}
out, csR0, csR1, err := n.state.WriteMessage(nil, payload)
if err != nil {
return nil, err
}
if !n.initiator {
n.csR0 = csR0
n.csR1 = csR1
}
return out, nil
}
// Read performs handshake read, returning optional payload if it was included
// in the Write, as the pattern allows zero-RTT encryption, meaning the
// initiator can encrypt the first handshake payload.
//
// The order of the handshake writes/reads should be:
// (1) Initiator: Write
// (2) Responder: Read
// (3) Initiator: Read
// (4) Responder: Write
func (n *Handshake) Read(b []byte) ([]byte, error) {
if n.Complete() {
return nil, errors.Errorf("handshake already complete")
}
out, csI0, csI1, err := n.state.ReadMessage(nil, b)
if err != nil {
return nil, err
}
if n.initiator {
n.csI0 = csI0
n.csI1 = csI1
}
return out, nil
}
// Complete returns true if handshake is complete and Encrypt/Decrypt
// are available.
func (n *Handshake) Complete() bool {
return n.csI0 != nil || n.csR0 != nil
}
// Cipher provides symmetric encryption and decryption after a successful
// handshake.
func (n *Handshake) Cipher() (Cipher, error) {
if !n.Complete() {
return nil, errors.Errorf("handshake not complete")
}
return newCipherState(n), nil
}