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.
5 // https://noiseprotocol.org/noise.html
18 "github.com/flynn/noise"
19 "golang.org/x/crypto/curve25519"
22 // The length of public and private keys as returned by GeneratePrivkey.
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
) {
34 err
:= binary
.Read(r
, binary
.BigEndian
, &length
)
36 // We may return a real io.EOF only here.
39 msg
:= make([]byte, int(length
))
40 _
, err
= io
.ReadFull(r
, msg
)
41 // Here we must change io.EOF to io.ErrUnexpectedEOF.
43 err
= io
.ErrUnexpectedEOF
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
) {
55 err
:= binary
.Write(w
, binary
.BigEndian
, length
)
63 // socket is the internal type that represents a Noise-wrapped
64 // io.ReadWriteCloser.
66 recvPipe
*io
.PipeReader
67 sendCipher
*noise
.CipherState
71 func newSocket(rwc io
.ReadWriteCloser
, recvCipher
, sendCipher
*noise
.CipherState
) *socket
{
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
) {
77 pw
.CloseWithError(err
)
80 msg
, err
:= readMessage(rwc
)
84 p
, err
:= recvCipher
.Decrypt(nil, nil, msg
)
95 sendCipher
: sendCipher
,
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
) {
114 msg
, err
:= s
.sendCipher
.Encrypt(nil, nil, p
[:n
])
118 err
= writeMessage(s
.ReadWriteCloser
, msg
)
128 // newConfig instantiates configuration settings that are common to clients and
130 func newConfig() 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
)
151 msg
, _
, _
, err
:= handshakeState
.WriteMessage(nil, nil)
155 err
= writeMessage(rwc
, msg
)
161 msg
, err
= readMessage(rwc
)
165 payload
, sendCipher
, recvCipher
, err
:= handshakeState
.ReadMessage(nil, msg
)
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
)
192 msg
, err
:= readMessage(rwc
)
196 payload
, _
, _
, err
:= handshakeState
.ReadMessage(nil, msg
)
200 if len(payload
) != 0 {
201 return nil, errors
.New("unexpected server payload")
205 msg
, recvCipher
, sendCipher
, err
:= handshakeState
.WriteMessage(nil, nil)
209 err
= writeMessage(rwc
, msg
)
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
)
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')
243 // Check that we're at EOF.
244 _
, err
= br
.ReadByte()
247 } else if err
== nil {
248 err
= fmt
.Errorf("file contains more than one line")
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
)
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
)
273 // EncodeKey encodes a hex-encoded private or public key.
274 func EncodeKey(key
[]byte) string {
275 return hex
.EncodeToString(key
)