Sliding replay window.
[champa.git] / noise / noise.go
blob429d1d435663ce90ec0164255bb34d4ba938513c
1 // Package noise provides a net.Conn-like interface for a
2 // Noise_NK_25519_ChaChaPoly_BLAKE2s. It encodes Noise messages onto a reliable
3 // stream using 16-bit length prefixes.
4 //
5 // https://noiseprotocol.org/noise.html
6 package noise
8 import (
9 "bufio"
10 "crypto/rand"
11 "encoding/binary"
12 "encoding/hex"
13 "errors"
14 "fmt"
15 "io"
16 "strings"
18 "github.com/flynn/noise"
19 "golang.org/x/crypto/curve25519"
22 // The length of public and private keys as returned by GeneratePrivkey.
23 const KeyLen = 32
25 // cipherSuite represents 25519_ChaChaPoly_BLAKE2s.
26 var cipherSuite = noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2s)
28 // readMessage reads a length-prefixed message from r. It returns a nil error
29 // only when a complete message was read. It returns io.EOF only when there were
30 // 0 bytes remaining to read from r. It returns io.ErrUnexpectedEOF when EOF
31 // occurs in the middle of an encoded message.
32 func readMessage(r io.Reader) ([]byte, error) {
33 var length uint16
34 err := binary.Read(r, binary.BigEndian, &length)
35 if err != nil {
36 // We may return a real io.EOF only here.
37 return nil, err
39 msg := make([]byte, int(length))
40 _, err = io.ReadFull(r, msg)
41 // Here we must change io.EOF to io.ErrUnexpectedEOF.
42 if err == io.EOF {
43 err = io.ErrUnexpectedEOF
45 return msg, err
48 // writeMessage writes msg as a length-prefixed message to w. It panics if the
49 // length of msg cannot be represented in 16 bits.
50 func writeMessage(w io.Writer, msg []byte) error {
51 length := uint16(len(msg))
52 if int(length) != len(msg) {
53 panic(len(msg))
55 err := binary.Write(w, binary.BigEndian, length)
56 if err != nil {
57 return err
59 _, err = w.Write(msg)
60 return err
63 // socket is the internal type that represents a Noise-wrapped
64 // io.ReadWriteCloser.
65 type socket struct {
66 recvPipe *io.PipeReader
67 sendCipher *noise.CipherState
68 io.ReadWriteCloser
71 func newSocket(rwc io.ReadWriteCloser, recvCipher, sendCipher *noise.CipherState) *socket {
72 pr, pw := io.Pipe()
73 // This loop calls readMessage, decrypts the messages, and feeds them
74 // into recvPipe where they will be returned from Read.
75 go func() (err error) {
76 defer func() {
77 pw.CloseWithError(err)
78 }()
79 for {
80 msg, err := readMessage(rwc)
81 if err != nil {
82 return err
84 p, err := recvCipher.Decrypt(nil, nil, msg)
85 if err != nil {
86 return err
88 _, err = pw.Write(p)
89 if err != nil {
90 return err
93 }()
94 return &socket{
95 sendCipher: sendCipher,
96 recvPipe: pr,
97 ReadWriteCloser: rwc,
101 // Read reads decrypted data from the wrapped io.Reader.
102 func (s *socket) Read(p []byte) (int, error) {
103 return s.recvPipe.Read(p)
106 // Write writes encrypted data from the wrapped io.Writer.
107 func (s *socket) Write(p []byte) (int, error) {
108 total := 0
109 for len(p) > 0 {
110 n := len(p)
111 if n > 4096 {
112 n = 4096
114 msg, err := s.sendCipher.Encrypt(nil, nil, p[:n])
115 if err != nil {
116 return total, err
118 err = writeMessage(s.ReadWriteCloser, msg)
119 if err != nil {
120 return total, err
122 total += n
123 p = p[n:]
125 return total, nil
128 // newConfig instantiates configuration settings that are common to clients and
129 // servers.
130 func newConfig() noise.Config {
131 return noise.Config{
132 CipherSuite: cipherSuite,
133 Pattern: noise.HandshakeNK,
134 Prologue: []byte("Champa 2021-06-17"),
138 // NewClient wraps an io.ReadWriteCloser in a Noise protocol as a client, and
139 // returns after completing the handshake. It returns a non-nil error if there
140 // is an error during the handshake.
141 func NewClient(rwc io.ReadWriteCloser, serverPubkey []byte) (io.ReadWriteCloser, error) {
142 config := newConfig()
143 config.Initiator = true
144 config.PeerStatic = serverPubkey
145 handshakeState, err := noise.NewHandshakeState(config)
146 if err != nil {
147 return nil, err
150 // -> e, es
151 msg, _, _, err := handshakeState.WriteMessage(nil, nil)
152 if err != nil {
153 return nil, err
155 err = writeMessage(rwc, msg)
156 if err != nil {
157 return nil, err
160 // <- e, es
161 msg, err = readMessage(rwc)
162 if err != nil {
163 return nil, err
165 payload, sendCipher, recvCipher, err := handshakeState.ReadMessage(nil, msg)
166 if err != nil {
167 return nil, err
169 if len(payload) != 0 {
170 return nil, errors.New("unexpected server payload")
173 return newSocket(rwc, recvCipher, sendCipher), nil
176 // NewClient wraps an io.ReadWriteCloser in a Noise protocol as a server, and
177 // returns after completing the handshake. It returns a non-nil error if there
178 // is an error during the handshake.
179 func NewServer(rwc io.ReadWriteCloser, serverPrivkey []byte) (io.ReadWriteCloser, error) {
180 config := newConfig()
181 config.Initiator = false
182 config.StaticKeypair = noise.DHKey{
183 Private: serverPrivkey,
184 Public: PubkeyFromPrivkey(serverPrivkey),
186 handshakeState, err := noise.NewHandshakeState(config)
187 if err != nil {
188 return nil, err
191 // -> e, es
192 msg, err := readMessage(rwc)
193 if err != nil {
194 return nil, err
196 payload, _, _, err := handshakeState.ReadMessage(nil, msg)
197 if err != nil {
198 return nil, err
200 if len(payload) != 0 {
201 return nil, errors.New("unexpected server payload")
204 // <- e, es
205 msg, recvCipher, sendCipher, err := handshakeState.WriteMessage(nil, nil)
206 if err != nil {
207 return nil, err
209 err = writeMessage(rwc, msg)
210 if err != nil {
211 return nil, err
214 return newSocket(rwc, recvCipher, sendCipher), nil
217 // GeneratePrivkey generates a private key. The corresponding private key can be
218 // generated using PubkeyFromPrivkey.
219 func GeneratePrivkey() ([]byte, error) {
220 pair, err := noise.DH25519.GenerateKeypair(rand.Reader)
221 return pair.Private, err
224 // PubkeyFromPrivkey returns the public key that corresponds to privkey.
225 func PubkeyFromPrivkey(privkey []byte) []byte {
226 pubkey, err := curve25519.X25519(privkey, curve25519.Basepoint)
227 if err != nil {
228 panic(err)
230 return pubkey
233 // ReadKey reads a hex-encoded key from r. r must consist of a single line, with
234 // or without a '\n' line terminator. The line must consist of KeyLen
235 // hex-encoded bytes.
236 func ReadKey(r io.Reader) ([]byte, error) {
237 br := bufio.NewReader(io.LimitReader(r, 100))
238 line, err := br.ReadString('\n')
239 if err == io.EOF {
240 err = nil
242 if err == nil {
243 // Check that we're at EOF.
244 _, err = br.ReadByte()
245 if err == io.EOF {
246 err = nil
247 } else if err == nil {
248 err = fmt.Errorf("file contains more than one line")
251 if err != nil {
252 return nil, err
254 line = strings.TrimSuffix(line, "\n")
255 return DecodeKey(line)
258 // WriteKey writes the hex-encoded key in a single line to w.
259 func WriteKey(w io.Writer, key []byte) error {
260 _, err := fmt.Fprintf(w, "%x\n", key)
261 return err
264 // DecodeKey decodes a hex-encoded private or public key.
265 func DecodeKey(s string) ([]byte, error) {
266 key, err := hex.DecodeString(s)
267 if err == nil && len(key) != KeyLen {
268 err = fmt.Errorf("length is %d, expected %d", len(key), KeyLen)
270 return key, err
273 // EncodeKey encodes a hex-encoded private or public key.
274 func EncodeKey(key []byte) string {
275 return hex.EncodeToString(key)