From 91e14497bb334523f0a68864290bd6e99296c499 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 1 Aug 2017 17:17:55 -0700 Subject: [PATCH] brontide: add a test case to exercise all BOLT-0008 test vectors --- brontide/listener.go | 4 +- brontide/noise_test.go | 164 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) diff --git a/brontide/listener.go b/brontide/listener.go index 27dca9e6..9a2ca498 100644 --- a/brontide/listener.go +++ b/brontide/listener.go @@ -8,7 +8,7 @@ import ( ) // Listener is an implementation of a net.Conn which executes an authenticated -// key exchange and message encryption protocol dubeed "Machine" after +// key exchange and message encryption protocol dubbed "Machine" after // initial connection acceptance. See the Machine struct for additional // details w.r.t the handshake and encryption scheme used within the // connection. @@ -43,7 +43,7 @@ func NewListener(localStatic *btcec.PrivateKey, listenAddr string) (*Listener, // Accept waits for and returns the next connection to the listener. All // incoming connections are authenticated via the three act Brontide -// key-exchange scheme. This funciton will fail with a non-nil error in the +// key-exchange scheme. This function will fail with a non-nil error in the // case that either the handshake breaks down, or the remote peer doesn't know // our static public key. // diff --git a/brontide/noise_test.go b/brontide/noise_test.go index c94b56ef..fa4a1924 100644 --- a/brontide/noise_test.go +++ b/brontide/noise_test.go @@ -2,6 +2,7 @@ package brontide import ( "bytes" + "encoding/hex" "io" "math" "net" @@ -204,3 +205,166 @@ func TestWriteMessageChunking(t *testing.T) { t.Fatalf("bytes don't match") } } + +// TestBolt0008TestVectors ensures that our implementation of brontide exactly +// matches the test vectors within the specification. +func TestBolt0008TestVectors(t *testing.T) { + t.Parallel() + + // First, we'll generate the state of the initiator from the test + // vectors at the appendix of BOLT-0008 + initiatorKeyBytes, err := hex.DecodeString("1111111111111111111111" + + "111111111111111111111111111111111111111111") + if err != nil { + t.Fatalf("unable to decode hex: %v", err) + } + initiatorPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(), + initiatorKeyBytes) + + // We'll then do the same for the responder. + responderKeyBytes, err := hex.DecodeString("212121212121212121212121" + + "2121212121212121212121212121212121212121") + if err != nil { + t.Fatalf("unable to decode hex: %v", err) + } + responderPriv, responderPub := btcec.PrivKeyFromBytes(btcec.S256(), + responderKeyBytes) + + // With the initiator's key data parsed, we'll now define a custom + // EphemeralGenerator function for the state machine to ensure that the + // initiator and responder both generate the ephemeral public key + // defined within the test vectors. + initiatorEphemeral := EphemeralGenerator(func() (*btcec.PrivateKey, error) { + e := "121212121212121212121212121212121212121212121212121212" + + "1212121212" + eBytes, err := hex.DecodeString(e) + if err != nil { + return nil, err + } + + priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), eBytes) + return priv, nil + }) + responderEphemeral := EphemeralGenerator(func() (*btcec.PrivateKey, error) { + e := "222222222222222222222222222222222222222222222222222" + + "2222222222222" + eBytes, err := hex.DecodeString(e) + if err != nil { + return nil, err + } + + priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), eBytes) + return priv, nil + }) + + // Finally, we'll create both brontide state machines, so we can begin + // our test. + initiator := NewBrontideMachine(true, initiatorPriv, responderPub, + initiatorEphemeral) + responder := NewBrontideMachine(false, responderPriv, nil, + responderEphemeral) + + // We'll start with the initiator generating the initial payload for + // act one. This should consist of exactly 50 bytes. We'll assert that + // the payload return is _exactly_ the same as what's specified within + // the test vectors. + actOne, err := initiator.GenActOne() + if err != nil { + t.Fatalf("unable to generate act one: %v", err) + } + expectedActOne, err := hex.DecodeString("00036360e856310ce5d294e" + + "8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df608655115" + + "1f58b8afe6c195782c6a") + if err != nil { + t.Fatalf("unable to parse expected act one: %v", err) + } + if !bytes.Equal(expectedActOne, actOne[:]) { + t.Fatalf("act one mismatch: expected %x, got %x", + expectedActOne, actOne) + } + + // With the assertion above passed, we'll now process the act one + // payload with the responder of the crypto handshake. + if err := responder.RecvActOne(actOne); err != nil { + t.Fatalf("responder unable to process act one: %v", err) + } + + // Next, we'll start the second act by having the responder generate + // its contribution to the crypto handshake. We'll also verify that we + // produce the _exact_ same byte stream as advertised within the spec's + // test vectors. + actTwo, err := responder.GenActTwo() + if err != nil { + t.Fatalf("unable to generate act two: %v", err) + } + expectedActTwo, err := hex.DecodeString("0002466d7fcae563e5cb09a0" + + "d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac58" + + "3c9ef6eafca3f730ae") + if err != nil { + t.Fatalf("unable to parse expected act two: %v", err) + } + if !bytes.Equal(expectedActTwo, actTwo[:]) { + t.Fatalf("act two mismatch: expected %x, got %x", + expectedActTwo, actTwo) + } + + // Moving the handshake along, we'll also ensure that the initiator + // accepts the act two payload. + if err := initiator.RecvActTwo(actTwo); err != nil { + t.Fatalf("initiator unable to process act two: %v", err) + } + + // At the final step, we'll generate the last act from the initiator + // and once again verify that it properly matches the test vectors. + actThree, err := initiator.GenActThree() + if err != nil { + t.Fatalf("unable to generate act three: %v", err) + } + expectedActThree, err := hex.DecodeString("00b9e3a702e93e3a9948c2e" + + "d6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8f" + + "c28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba") + if err != nil { + t.Fatalf("unable to parse expected act three: %v", err) + } + if !bytes.Equal(expectedActThree, actThree[:]) { + t.Fatalf("act three mismatch: expected %x, got %x", + expectedActThree, actThree) + } + + // Finally, we'll ensure that the responder itself also properly parses + // the last payload in the crypto handshake. + if err := responder.RecvActThree(actThree); err != nil { + t.Fatalf("responder unable to process act three: %v", err) + } + + // As a final assertion, we'll ensure that both sides have derived the + // proper symmetric encryption keys. + sendingKey, err := hex.DecodeString("969ab31b4d288cedf6218839b27a3e2" + + "140827047f2c0f01bf5c04435d43511a9") + if err != nil { + t.Fatalf("unable to parse sending key: %v", err) + } + recvKey, err := hex.DecodeString("bb9020b8965f4df047e07f955f3c4b884" + + "18984aadc5cdb35096b9ea8fa5c3442") + if err != nil { + t.Fatalf("unable to parse recv'ing key: %v", err) + } + + if !bytes.Equal(initiator.sendCipher.secretKey[:], sendingKey) { + t.Fatalf("sending key mismatch: expected %x, got %x", + initiator.sendCipher.secretKey[:], sendingKey) + } + if !bytes.Equal(initiator.recvCipher.secretKey[:], recvKey) { + t.Fatalf("sending key mismatch: expected %x, got %x", + initiator.sendCipher.secretKey[:], recvKey) + } + + if !bytes.Equal(responder.sendCipher.secretKey[:], recvKey) { + t.Fatalf("sending key mismatch: expected %x, got %x", + responder.sendCipher.secretKey[:], recvKey) + } + if !bytes.Equal(responder.recvCipher.secretKey[:], sendingKey) { + t.Fatalf("sending key mismatch: expected %x, got %x", + responder.sendCipher.secretKey[:], sendingKey) + } +}