ab97d9693f
This commit introduces Brontide: an authenticated key agreement protocol in three acts. Brontide is the successor to lndc within lnd, and ultimately within the greater Lighting Network. Brontide uses the Noise_XK handshake for initial key agreement, then implements an AEAD scheme which encrypts+authenticates both packets, and the lengths of the packets on the wire. The initial authentication handshake preserves the responder’s identity by never transmitting it to the initiator and performing mutual authentication via an incremental Triple-DH based on ECDH of secp256k1 and an HKDF which uses SHA-256. Bronzed isn’t yet integrated within the wider daemon yet. Full integration will land in a future pull request.
99 lines
2.6 KiB
Go
99 lines
2.6 KiB
Go
package brontide
|
|
|
|
import (
|
|
"bytes"
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/roasbeef/btcd/btcec"
|
|
)
|
|
|
|
func TestConnectionCorrectness(t *testing.T) {
|
|
// First, generate the long-term private keys both ends of the connection
|
|
// within our test.
|
|
localPriv, err := btcec.NewPrivateKey(btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to generate local priv key: %v", err)
|
|
}
|
|
remotePriv, err := btcec.NewPrivateKey(btcec.S256())
|
|
if err != nil {
|
|
t.Fatalf("unable to generate remote priv key: %v", err)
|
|
}
|
|
|
|
// Having a port of ":0" means a random port, and interface will be
|
|
// chosen for our listener.
|
|
addr := ":0"
|
|
|
|
// Our listener will be local, and the connection remote.
|
|
listener, err := NewListener(localPriv, addr)
|
|
if err != nil {
|
|
t.Fatalf("unable to create listener: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
listenAddr := listener.Addr().String()
|
|
|
|
// Initiate a connection with a separate goroutine, and listen with our
|
|
// main one. If both errors are nil, then encryption+auth was succesful.
|
|
errChan := make(chan error)
|
|
connChan := make(chan net.Conn)
|
|
go func() {
|
|
conn, err := Dial(remotePriv, localPriv.PubKey(), listenAddr)
|
|
errChan <- err
|
|
connChan <- conn
|
|
}()
|
|
|
|
localConn, listenErr := listener.Accept()
|
|
if listenErr != nil {
|
|
t.Fatalf("unable to accept connection: %v", listenErr)
|
|
}
|
|
|
|
if dialErr := <-errChan; err != nil {
|
|
t.Fatalf("unable to establish connection: %v", dialErr)
|
|
}
|
|
conn := <-connChan
|
|
|
|
// Test out some message full-message reads.
|
|
for i := 0; i < 10; i++ {
|
|
msg := []byte("hello" + string(i))
|
|
|
|
if _, err := conn.Write(msg); err != nil {
|
|
t.Fatalf("remote conn failed to write: %v", err)
|
|
}
|
|
|
|
readBuf := make([]byte, len(msg))
|
|
if _, err := localConn.Read(readBuf); err != nil {
|
|
t.Fatalf("local conn failed to read: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(readBuf, msg) {
|
|
t.Fatalf("messages don't match, %v vs %v",
|
|
string(readBuf), string(msg))
|
|
}
|
|
}
|
|
|
|
// Now try incremental message reads. This simulates first writing a
|
|
// message header, then a message body.
|
|
outMsg := []byte("hello world")
|
|
if _, err := conn.Write(outMsg); err != nil {
|
|
t.Fatalf("remote conn failed to write: %v", err)
|
|
}
|
|
|
|
readBuf := make([]byte, len(outMsg))
|
|
if _, err := localConn.Read(readBuf[:len(outMsg)/2]); err != nil {
|
|
t.Fatalf("local conn failed to read: %v", err)
|
|
}
|
|
if _, err := localConn.Read(readBuf[len(outMsg)/2:]); err != nil {
|
|
t.Fatalf("local conn failed to read: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(outMsg, readBuf) {
|
|
t.Fatalf("messages don't match, %v vs %v",
|
|
string(readBuf), string(outMsg))
|
|
}
|
|
}
|
|
|
|
func TestNoiseIdentityHiding(t *testing.T) {
|
|
// TODO(roasbeef): fin
|
|
}
|