mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 21:15:39 +00:00
239 lines
5.9 KiB
Go
239 lines
5.9 KiB
Go
|
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package noise
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"io"
|
||
|
"net"
|
||
|
"testing"
|
||
|
|
||
|
tsnettest "tailscale.com/net/nettest"
|
||
|
"tailscale.com/types/key"
|
||
|
)
|
||
|
|
||
|
// Can a reference Noise IK client talk to our server?
|
||
|
func TestInteropClient(t *testing.T) {
|
||
|
var (
|
||
|
s1, s2 = tsnettest.NewConn("noise", 128000)
|
||
|
controlKey = key.NewPrivate()
|
||
|
machineKey = key.NewPrivate()
|
||
|
serverErr = make(chan error, 2)
|
||
|
serverBytes = make(chan []byte, 1)
|
||
|
c2s = "client>server"
|
||
|
s2c = "server>client"
|
||
|
)
|
||
|
|
||
|
go func() {
|
||
|
server, err := Server(context.Background(), s2, controlKey)
|
||
|
serverErr <- err
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
var buf [1024]byte
|
||
|
_, err = io.ReadFull(server, buf[:len(c2s)])
|
||
|
serverBytes <- buf[:len(c2s)]
|
||
|
if err != nil {
|
||
|
serverErr <- err
|
||
|
return
|
||
|
}
|
||
|
_, err = server.Write([]byte(s2c))
|
||
|
serverErr <- err
|
||
|
}()
|
||
|
|
||
|
gotS2C, err := noiseExplorerClient(s1, controlKey.Public(), machineKey, []byte(c2s))
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed client interop: %v", err)
|
||
|
}
|
||
|
if string(gotS2C) != s2c {
|
||
|
t.Fatalf("server sent unexpected data %q, want %q", string(gotS2C), s2c)
|
||
|
}
|
||
|
|
||
|
if err := <-serverErr; err != nil {
|
||
|
t.Fatalf("server handshake failed: %v", err)
|
||
|
}
|
||
|
if err := <-serverErr; err != nil {
|
||
|
t.Fatalf("server read/write failed: %v", err)
|
||
|
}
|
||
|
if got := string(<-serverBytes); got != c2s {
|
||
|
t.Fatalf("server received %q, want %q", got, c2s)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Can our client talk to a reference Noise IK server?
|
||
|
func TestInteropServer(t *testing.T) {
|
||
|
var (
|
||
|
s1, s2 = tsnettest.NewConn("noise", 128000)
|
||
|
controlKey = key.NewPrivate()
|
||
|
machineKey = key.NewPrivate()
|
||
|
clientErr = make(chan error, 2)
|
||
|
clientBytes = make(chan []byte, 1)
|
||
|
c2s = "client>server"
|
||
|
s2c = "server>client"
|
||
|
)
|
||
|
|
||
|
go func() {
|
||
|
client, err := Client(context.Background(), s1, machineKey, controlKey.Public())
|
||
|
clientErr <- err
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
_, err = client.Write([]byte(c2s))
|
||
|
if err != nil {
|
||
|
clientErr <- err
|
||
|
return
|
||
|
}
|
||
|
var buf [1024]byte
|
||
|
_, err = io.ReadFull(client, buf[:len(s2c)])
|
||
|
clientBytes <- buf[:len(s2c)]
|
||
|
clientErr <- err
|
||
|
}()
|
||
|
|
||
|
gotC2S, err := noiseExplorerServer(s2, controlKey, machineKey.Public(), []byte(s2c))
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed server interop: %v", err)
|
||
|
}
|
||
|
if string(gotC2S) != c2s {
|
||
|
t.Fatalf("server sent unexpected data %q, want %q", string(gotC2S), c2s)
|
||
|
}
|
||
|
|
||
|
if err := <-clientErr; err != nil {
|
||
|
t.Fatalf("client handshake failed: %v", err)
|
||
|
}
|
||
|
if err := <-clientErr; err != nil {
|
||
|
t.Fatalf("client read/write failed: %v", err)
|
||
|
}
|
||
|
if got := string(<-clientBytes); got != s2c {
|
||
|
t.Fatalf("client received %q, want %q", got, s2c)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// noiseExplorerClient uses the Noise Explorer implementation of Noise
|
||
|
// IK to handshake as a Noise client on conn, transmit payload, and
|
||
|
// read+return a payload from the peer.
|
||
|
func noiseExplorerClient(conn net.Conn, controlKey key.Public, machineKey key.Private, payload []byte) ([]byte, error) {
|
||
|
mk := keypair{
|
||
|
private_key: machineKey,
|
||
|
public_key: machineKey.Public(),
|
||
|
}
|
||
|
session := InitSession(true, nil, mk, controlKey)
|
||
|
|
||
|
_, msg1 := SendMessage(&session, nil)
|
||
|
if _, err := conn.Write(msg1.ne[:]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if _, err := conn.Write(msg1.ns); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if _, err := conn.Write(msg1.ciphertext); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var buf [1024]byte
|
||
|
if _, err := io.ReadFull(conn, buf[:48]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
msg2 := messagebuffer{
|
||
|
ciphertext: buf[32:48],
|
||
|
}
|
||
|
copy(msg2.ne[:], buf[:32])
|
||
|
_, p, valid := RecvMessage(&session, &msg2)
|
||
|
if !valid {
|
||
|
return nil, errors.New("handshake failed")
|
||
|
}
|
||
|
if len(p) != 0 {
|
||
|
return nil, errors.New("non-empty payload")
|
||
|
}
|
||
|
|
||
|
_, msg3 := SendMessage(&session, payload)
|
||
|
binary.BigEndian.PutUint16(buf[:2], uint16(len(msg3.ciphertext)))
|
||
|
if _, err := conn.Write(buf[:2]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if _, err := conn.Write(msg3.ciphertext); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
plen := int(binary.BigEndian.Uint16(buf[:2]))
|
||
|
if _, err := io.ReadFull(conn, buf[:plen]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
msg4 := messagebuffer{
|
||
|
ciphertext: buf[:plen],
|
||
|
}
|
||
|
_, p, valid = RecvMessage(&session, &msg4)
|
||
|
if !valid {
|
||
|
return nil, errors.New("transport message decryption failed")
|
||
|
}
|
||
|
|
||
|
return p, nil
|
||
|
}
|
||
|
|
||
|
func noiseExplorerServer(conn net.Conn, controlKey key.Private, wantMachineKey key.Public, payload []byte) ([]byte, error) {
|
||
|
mk := keypair{
|
||
|
private_key: controlKey,
|
||
|
public_key: controlKey.Public(),
|
||
|
}
|
||
|
session := InitSession(false, nil, mk, [32]byte{})
|
||
|
|
||
|
var buf [1024]byte
|
||
|
if _, err := io.ReadFull(conn, buf[:96]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
msg1 := messagebuffer{
|
||
|
ns: buf[32:80],
|
||
|
ciphertext: buf[80:96],
|
||
|
}
|
||
|
copy(msg1.ne[:], buf[:32])
|
||
|
_, p, valid := RecvMessage(&session, &msg1)
|
||
|
if !valid {
|
||
|
return nil, errors.New("handshake failed")
|
||
|
}
|
||
|
if len(p) != 0 {
|
||
|
return nil, errors.New("non-empty payload")
|
||
|
}
|
||
|
|
||
|
_, msg2 := SendMessage(&session, nil)
|
||
|
if _, err := conn.Write(msg2.ne[:]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if _, err := conn.Write(msg2.ciphertext[:]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
plen := int(binary.BigEndian.Uint16(buf[:2]))
|
||
|
if _, err := io.ReadFull(conn, buf[:plen]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
msg3 := messagebuffer{
|
||
|
ciphertext: buf[:plen],
|
||
|
}
|
||
|
_, p, valid = RecvMessage(&session, &msg3)
|
||
|
if !valid {
|
||
|
return nil, errors.New("transport message decryption failed")
|
||
|
}
|
||
|
|
||
|
_, msg4 := SendMessage(&session, payload)
|
||
|
binary.BigEndian.PutUint16(buf[:2], uint16(len(msg4.ciphertext)))
|
||
|
if _, err := conn.Write(buf[:2]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if _, err := conn.Write(msg4.ciphertext); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return p, nil
|
||
|
}
|