From 3e594d4520c601c991c9121481bd7941cc142b11 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Sat, 16 Oct 2021 00:42:48 -0600 Subject: [PATCH] Remove noise.socket, now unused. --- noise/noise.go | 411 +++++++++++++++------------------------------------- noise/noise_test.go | 65 --------- 2 files changed, 115 insertions(+), 361 deletions(-) rewrite noise/noise.go (60%) diff --git a/noise/noise.go b/noise/noise.go dissimilarity index 60% index 056903e..49c76d8 100644 --- a/noise/noise.go +++ b/noise/noise.go @@ -1,296 +1,115 @@ -// Package noise provides a net.Conn-like interface for a -// Noise_NK_25519_ChaChaPoly_BLAKE2s. It encodes Noise messages onto a reliable -// stream using 16-bit length prefixes. -// -// https://noiseprotocol.org/noise.html -package noise - -import ( - "bufio" - "crypto/rand" - "encoding/binary" - "encoding/hex" - "errors" - "fmt" - "io" - "net" - "strings" - - "github.com/flynn/noise" - "golang.org/x/crypto/curve25519" -) - -// The length of public and private keys as returned by GeneratePrivkey. -const KeyLen = 32 - -const ( - MsgTypeHandshakeInit = 1 - MsgTypeHandshakeResp = 2 - MsgTypeTransport = 4 -) - -// cipherSuite represents 25519_ChaChaPoly_BLAKE2s. -var cipherSuite = noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2s) - -func ReadMessageFrom(conn net.PacketConn) (byte, []byte, net.Addr, error) { - var buf [1500]byte - for { - n, addr, err := conn.ReadFrom(buf[:]) - if err != nil { - return 0, nil, nil, err - } - if n >= 1 { - return buf[0], buf[1:n], addr, nil - } - } -} - -// readMessage reads a length-prefixed message from r. It returns a nil error -// only when a complete message was read. It returns io.EOF only when there were -// 0 bytes remaining to read from r. It returns io.ErrUnexpectedEOF when EOF -// occurs in the middle of an encoded message. -func readMessage(r io.Reader) ([]byte, error) { - var length uint16 - err := binary.Read(r, binary.BigEndian, &length) - if err != nil { - // We may return a real io.EOF only here. - return nil, err - } - msg := make([]byte, int(length)) - _, err = io.ReadFull(r, msg) - // Here we must change io.EOF to io.ErrUnexpectedEOF. - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - return msg, err -} - -// writeMessage writes msg as a length-prefixed message to w. It panics if the -// length of msg cannot be represented in 16 bits. -func writeMessage(w io.Writer, msg []byte) error { - length := uint16(len(msg)) - if int(length) != len(msg) { - panic(len(msg)) - } - err := binary.Write(w, binary.BigEndian, length) - if err != nil { - return err - } - _, err = w.Write(msg) - return err -} - -// socket is the internal type that represents a Noise-wrapped -// io.ReadWriteCloser. -type socket struct { - recvPipe *io.PipeReader - sendCipher *noise.CipherState - io.ReadWriteCloser -} - -func newSocket(rwc io.ReadWriteCloser, recvCipher, sendCipher *noise.CipherState) *socket { - pr, pw := io.Pipe() - // This loop calls readMessage, decrypts the messages, and feeds them - // into recvPipe where they will be returned from Read. - go func() (err error) { - defer func() { - pw.CloseWithError(err) - }() - for { - msg, err := readMessage(rwc) - if err != nil { - return err - } - p, err := recvCipher.Decrypt(nil, nil, msg) - if err != nil { - return err - } - _, err = pw.Write(p) - if err != nil { - return err - } - } - }() - return &socket{ - sendCipher: sendCipher, - recvPipe: pr, - ReadWriteCloser: rwc, - } -} - -// Read reads decrypted data from the wrapped io.Reader. -func (s *socket) Read(p []byte) (int, error) { - return s.recvPipe.Read(p) -} - -// Write writes encrypted data from the wrapped io.Writer. -func (s *socket) Write(p []byte) (int, error) { - total := 0 - for len(p) > 0 { - n := len(p) - if n > 4096 { - n = 4096 - } - msg, err := s.sendCipher.Encrypt(nil, nil, p[:n]) - if err != nil { - return total, err - } - err = writeMessage(s.ReadWriteCloser, msg) - if err != nil { - return total, err - } - total += n - p = p[n:] - } - return total, nil -} - -// newConfig instantiates configuration settings that are common to clients and -// servers. -func newConfig() noise.Config { - return noise.Config{ - CipherSuite: cipherSuite, - Pattern: noise.HandshakeNK, - Prologue: []byte("Champa 2021-06-17"), - } -} - -// NewClient wraps an io.ReadWriteCloser in a Noise protocol as a client, and -// returns after completing the handshake. It returns a non-nil error if there -// is an error during the handshake. -func NewClient(rwc io.ReadWriteCloser, serverPubkey []byte) (io.ReadWriteCloser, error) { - config := newConfig() - config.Initiator = true - config.PeerStatic = serverPubkey - handshakeState, err := noise.NewHandshakeState(config) - if err != nil { - return nil, err - } - - // -> e, es - msg, _, _, err := handshakeState.WriteMessage(nil, nil) - if err != nil { - return nil, err - } - err = writeMessage(rwc, msg) - if err != nil { - return nil, err - } - - // <- e, es - msg, err = readMessage(rwc) - if err != nil { - return nil, err - } - payload, sendCipher, recvCipher, err := handshakeState.ReadMessage(nil, msg) - if err != nil { - return nil, err - } - if len(payload) != 0 { - return nil, errors.New("unexpected server payload") - } - - return newSocket(rwc, recvCipher, sendCipher), nil -} - -// NewClient wraps an io.ReadWriteCloser in a Noise protocol as a server, and -// returns after completing the handshake. It returns a non-nil error if there -// is an error during the handshake. -func NewServer(rwc io.ReadWriteCloser, serverPrivkey []byte) (io.ReadWriteCloser, error) { - config := newConfig() - config.Initiator = false - config.StaticKeypair = noise.DHKey{ - Private: serverPrivkey, - Public: PubkeyFromPrivkey(serverPrivkey), - } - handshakeState, err := noise.NewHandshakeState(config) - if err != nil { - return nil, err - } - - // -> e, es - msg, err := readMessage(rwc) - if err != nil { - return nil, err - } - payload, _, _, err := handshakeState.ReadMessage(nil, msg) - if err != nil { - return nil, err - } - if len(payload) != 0 { - return nil, errors.New("unexpected server payload") - } - - // <- e, es - msg, recvCipher, sendCipher, err := handshakeState.WriteMessage(nil, nil) - if err != nil { - return nil, err - } - err = writeMessage(rwc, msg) - if err != nil { - return nil, err - } - - return newSocket(rwc, recvCipher, sendCipher), nil -} - -// GeneratePrivkey generates a private key. The corresponding private key can be -// generated using PubkeyFromPrivkey. -func GeneratePrivkey() ([]byte, error) { - pair, err := noise.DH25519.GenerateKeypair(rand.Reader) - return pair.Private, err -} - -// PubkeyFromPrivkey returns the public key that corresponds to privkey. -func PubkeyFromPrivkey(privkey []byte) []byte { - pubkey, err := curve25519.X25519(privkey, curve25519.Basepoint) - if err != nil { - panic(err) - } - return pubkey -} - -// ReadKey reads a hex-encoded key from r. r must consist of a single line, with -// or without a '\n' line terminator. The line must consist of KeyLen -// hex-encoded bytes. -func ReadKey(r io.Reader) ([]byte, error) { - br := bufio.NewReader(io.LimitReader(r, 100)) - line, err := br.ReadString('\n') - if err == io.EOF { - err = nil - } - if err == nil { - // Check that we're at EOF. - _, err = br.ReadByte() - if err == io.EOF { - err = nil - } else if err == nil { - err = fmt.Errorf("file contains more than one line") - } - } - if err != nil { - return nil, err - } - line = strings.TrimSuffix(line, "\n") - return DecodeKey(line) -} - -// WriteKey writes the hex-encoded key in a single line to w. -func WriteKey(w io.Writer, key []byte) error { - _, err := fmt.Fprintf(w, "%x\n", key) - return err -} - -// DecodeKey decodes a hex-encoded private or public key. -func DecodeKey(s string) ([]byte, error) { - key, err := hex.DecodeString(s) - if err == nil && len(key) != KeyLen { - err = fmt.Errorf("length is %d, expected %d", len(key), KeyLen) - } - return key, err -} - -// EncodeKey encodes a hex-encoded private or public key. -func EncodeKey(key []byte) string { - return hex.EncodeToString(key) -} +// Package noise provides a net.Conn-like interface for a +// Noise_NK_25519_ChaChaPoly_BLAKE2s. It encodes Noise messages onto a reliable +// stream using 16-bit length prefixes. +// +// https://noiseprotocol.org/noise.html +package noise + +import ( + "bufio" + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "net" + "strings" + + "github.com/flynn/noise" + "golang.org/x/crypto/curve25519" +) + +// The length of public and private keys as returned by GeneratePrivkey. +const KeyLen = 32 + +const ( + MsgTypeHandshakeInit = 1 + MsgTypeHandshakeResp = 2 + MsgTypeTransport = 4 +) + +// cipherSuite represents 25519_ChaChaPoly_BLAKE2s. +var cipherSuite = noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2s) + +func ReadMessageFrom(conn net.PacketConn) (byte, []byte, net.Addr, error) { + var buf [1500]byte + for { + n, addr, err := conn.ReadFrom(buf[:]) + if err != nil { + return 0, nil, nil, err + } + if n >= 1 { + return buf[0], buf[1:n], addr, nil + } + } +} + +// newConfig instantiates configuration settings that are common to clients and +// servers. +func newConfig() noise.Config { + return noise.Config{ + CipherSuite: cipherSuite, + Pattern: noise.HandshakeNK, + Prologue: []byte("Champa 2021-06-17"), + } +} + +// GeneratePrivkey generates a private key. The corresponding private key can be +// generated using PubkeyFromPrivkey. +func GeneratePrivkey() ([]byte, error) { + pair, err := noise.DH25519.GenerateKeypair(rand.Reader) + return pair.Private, err +} + +// PubkeyFromPrivkey returns the public key that corresponds to privkey. +func PubkeyFromPrivkey(privkey []byte) []byte { + pubkey, err := curve25519.X25519(privkey, curve25519.Basepoint) + if err != nil { + panic(err) + } + return pubkey +} + +// ReadKey reads a hex-encoded key from r. r must consist of a single line, with +// or without a '\n' line terminator. The line must consist of KeyLen +// hex-encoded bytes. +func ReadKey(r io.Reader) ([]byte, error) { + br := bufio.NewReader(io.LimitReader(r, 100)) + line, err := br.ReadString('\n') + if err == io.EOF { + err = nil + } + if err == nil { + // Check that we're at EOF. + _, err = br.ReadByte() + if err == io.EOF { + err = nil + } else if err == nil { + err = fmt.Errorf("file contains more than one line") + } + } + if err != nil { + return nil, err + } + line = strings.TrimSuffix(line, "\n") + return DecodeKey(line) +} + +// WriteKey writes the hex-encoded key in a single line to w. +func WriteKey(w io.Writer, key []byte) error { + _, err := fmt.Fprintf(w, "%x\n", key) + return err +} + +// DecodeKey decodes a hex-encoded private or public key. +func DecodeKey(s string) ([]byte, error) { + key, err := hex.DecodeString(s) + if err == nil && len(key) != KeyLen { + err = fmt.Errorf("length is %d, expected %d", len(key), KeyLen) + } + return key, err +} + +// EncodeKey encodes a hex-encoded private or public key. +func EncodeKey(key []byte) string { + return hex.EncodeToString(key) +} diff --git a/noise/noise_test.go b/noise/noise_test.go index 66a2e4f..dac100d 100644 --- a/noise/noise_test.go +++ b/noise/noise_test.go @@ -2,74 +2,9 @@ package noise import ( "bytes" - "io" "testing" ) -func allMessages(buf []byte) ([][]byte, error) { - var messages [][]byte - r := bytes.NewReader(buf) - for { - msg, err := readMessage(r) - if err != nil { - return messages, err - } - messages = append(messages, msg) - } -} - -func messagesEqual(a, b [][]byte) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if !bytes.Equal(a[i], b[i]) { - return false - } - } - return true -} - -func TestReadMessage(t *testing.T) { - for _, test := range []struct { - input string - messages [][]byte - err error - }{ - {"", [][]byte{}, io.EOF}, - {"\x00", [][]byte{}, io.ErrUnexpectedEOF}, - {"\x00\x00", [][]byte{{}}, io.EOF}, - {"\x00\x00\x00", [][]byte{{}}, io.ErrUnexpectedEOF}, - {"\x00\x01", [][]byte{}, io.ErrUnexpectedEOF}, - {"\x00\x05hello\x00\x05world", [][]byte{[]byte("hello"), []byte("world")}, io.EOF}, - } { - packets, err := allMessages([]byte(test.input)) - if !messagesEqual(packets, test.messages) || err != test.err { - t.Errorf("%x\nreturned %x %v\nexpected %x %v", - test.input, packets, err, test.messages, test.err) - } - } -} - -func TestMessageRoundTrip(t *testing.T) { - for _, messages := range [][][]byte{ - {}, - } { - var buf bytes.Buffer - for _, msg := range messages { - err := writeMessage(&buf, msg) - if err != nil { - panic(err) - } - } - output, err := allMessages(buf.Bytes()) - if !messagesEqual(output, messages) || err != io.EOF { - t.Errorf("%x roundtripped to %x %v", - messages, output, err) - } - } -} - func TestReadKey(t *testing.T) { for _, test := range []struct { input string -- 2.11.4.GIT