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
19 "github.com/flynn/noise"
20 "golang.org/x/crypto/curve25519"
23 // The length of public and private keys as returned by GeneratePrivkey.
27 MsgTypeHandshakeInit
= 1
28 MsgTypeHandshakeResp
= 2
32 // cipherSuite represents 25519_ChaChaPoly_BLAKE2s.
33 var cipherSuite
= noise
.NewCipherSuite(noise
.DH25519
, noise
.CipherChaChaPoly
, noise
.HashBLAKE2s
)
35 func ReadMessageFrom(conn net
.PacketConn
) (byte, []byte, net
.Addr
, error
) {
38 n
, addr
, err
:= conn
.ReadFrom(buf
[:])
40 return 0, nil, nil, err
43 return buf
[0], buf
[1:n
], addr
, nil
48 // readMessage reads a length-prefixed message from r. It returns a nil error
49 // only when a complete message was read. It returns io.EOF only when there were
50 // 0 bytes remaining to read from r. It returns io.ErrUnexpectedEOF when EOF
51 // occurs in the middle of an encoded message.
52 func readMessage(r io
.Reader
) ([]byte, error
) {
54 err
:= binary
.Read(r
, binary
.BigEndian
, &length
)
56 // We may return a real io.EOF only here.
59 msg
:= make([]byte, int(length
))
60 _
, err
= io
.ReadFull(r
, msg
)
61 // Here we must change io.EOF to io.ErrUnexpectedEOF.
63 err
= io
.ErrUnexpectedEOF
68 // writeMessage writes msg as a length-prefixed message to w. It panics if the
69 // length of msg cannot be represented in 16 bits.
70 func writeMessage(w io
.Writer
, msg
[]byte) error
{
71 length
:= uint16(len(msg
))
72 if int(length
) != len(msg
) {
75 err
:= binary
.Write(w
, binary
.BigEndian
, length
)
83 // socket is the internal type that represents a Noise-wrapped
84 // io.ReadWriteCloser.
86 recvPipe
*io
.PipeReader
87 sendCipher
*noise
.CipherState
91 func newSocket(rwc io
.ReadWriteCloser
, recvCipher
, sendCipher
*noise
.CipherState
) *socket
{
93 // This loop calls readMessage, decrypts the messages, and feeds them
94 // into recvPipe where they will be returned from Read.
95 go func() (err error
) {
97 pw
.CloseWithError(err
)
100 msg
, err
:= readMessage(rwc
)
104 p
, err
:= recvCipher
.Decrypt(nil, nil, msg
)
115 sendCipher
: sendCipher
,
117 ReadWriteCloser
: rwc
,
121 // Read reads decrypted data from the wrapped io.Reader.
122 func (s
*socket
) Read(p
[]byte) (int, error
) {
123 return s
.recvPipe
.Read(p
)
126 // Write writes encrypted data from the wrapped io.Writer.
127 func (s
*socket
) Write(p
[]byte) (int, error
) {
134 msg
, err
:= s
.sendCipher
.Encrypt(nil, nil, p
[:n
])
138 err
= writeMessage(s
.ReadWriteCloser
, msg
)
148 // newConfig instantiates configuration settings that are common to clients and
150 func newConfig() noise
.Config
{
152 CipherSuite
: cipherSuite
,
153 Pattern
: noise
.HandshakeNK
,
154 Prologue
: []byte("Champa 2021-06-17"),
158 // NewClient wraps an io.ReadWriteCloser in a Noise protocol as a client, and
159 // returns after completing the handshake. It returns a non-nil error if there
160 // is an error during the handshake.
161 func NewClient(rwc io
.ReadWriteCloser
, serverPubkey
[]byte) (io
.ReadWriteCloser
, error
) {
162 config
:= newConfig()
163 config
.Initiator
= true
164 config
.PeerStatic
= serverPubkey
165 handshakeState
, err
:= noise
.NewHandshakeState(config
)
171 msg
, _
, _
, err
:= handshakeState
.WriteMessage(nil, nil)
175 err
= writeMessage(rwc
, msg
)
181 msg
, err
= readMessage(rwc
)
185 payload
, sendCipher
, recvCipher
, err
:= handshakeState
.ReadMessage(nil, msg
)
189 if len(payload
) != 0 {
190 return nil, errors
.New("unexpected server payload")
193 return newSocket(rwc
, recvCipher
, sendCipher
), nil
196 // NewClient wraps an io.ReadWriteCloser in a Noise protocol as a server, and
197 // returns after completing the handshake. It returns a non-nil error if there
198 // is an error during the handshake.
199 func NewServer(rwc io
.ReadWriteCloser
, serverPrivkey
[]byte) (io
.ReadWriteCloser
, error
) {
200 config
:= newConfig()
201 config
.Initiator
= false
202 config
.StaticKeypair
= noise
.DHKey
{
203 Private
: serverPrivkey
,
204 Public
: PubkeyFromPrivkey(serverPrivkey
),
206 handshakeState
, err
:= noise
.NewHandshakeState(config
)
212 msg
, err
:= readMessage(rwc
)
216 payload
, _
, _
, err
:= handshakeState
.ReadMessage(nil, msg
)
220 if len(payload
) != 0 {
221 return nil, errors
.New("unexpected server payload")
225 msg
, recvCipher
, sendCipher
, err
:= handshakeState
.WriteMessage(nil, nil)
229 err
= writeMessage(rwc
, msg
)
234 return newSocket(rwc
, recvCipher
, sendCipher
), nil
237 // GeneratePrivkey generates a private key. The corresponding private key can be
238 // generated using PubkeyFromPrivkey.
239 func GeneratePrivkey() ([]byte, error
) {
240 pair
, err
:= noise
.DH25519
.GenerateKeypair(rand
.Reader
)
241 return pair
.Private
, err
244 // PubkeyFromPrivkey returns the public key that corresponds to privkey.
245 func PubkeyFromPrivkey(privkey
[]byte) []byte {
246 pubkey
, err
:= curve25519
.X25519(privkey
, curve25519
.Basepoint
)
253 // ReadKey reads a hex-encoded key from r. r must consist of a single line, with
254 // or without a '\n' line terminator. The line must consist of KeyLen
255 // hex-encoded bytes.
256 func ReadKey(r io
.Reader
) ([]byte, error
) {
257 br
:= bufio
.NewReader(io
.LimitReader(r
, 100))
258 line
, err
:= br
.ReadString('\n')
263 // Check that we're at EOF.
264 _
, err
= br
.ReadByte()
267 } else if err
== nil {
268 err
= fmt
.Errorf("file contains more than one line")
274 line
= strings
.TrimSuffix(line
, "\n")
275 return DecodeKey(line
)
278 // WriteKey writes the hex-encoded key in a single line to w.
279 func WriteKey(w io
.Writer
, key
[]byte) error
{
280 _
, err
:= fmt
.Fprintf(w
, "%x\n", key
)
284 // DecodeKey decodes a hex-encoded private or public key.
285 func DecodeKey(s
string) ([]byte, error
) {
286 key
, err
:= hex
.DecodeString(s
)
287 if err
== nil && len(key
) != KeyLen
{
288 err
= fmt
.Errorf("length is %d, expected %d", len(key
), KeyLen
)
293 // EncodeKey encodes a hex-encoded private or public key.
294 func EncodeKey(key
[]byte) string {
295 return hex
.EncodeToString(key
)