veyron/runtimes/google/ipc/stream: Implement control-channel encryption.

For VIFs, perform versioning, authentication, and encrypt the control
data (VC data is already encrypted).

In this implementation, authentication/encryption is initiated by the
client.

  - The VIF starts off using the standard unencrypted protocol.

  - The client calculates the expected IPC version by intersecting
    its own supported version range with the server's range that
    is published via the endpoint.  If the version suports
    encryption, the client immediately begins negotiating
    with the server.

Negotiation is performed using Encrypt control messages to set up a
bidirectional flow between the client and server.  This is like a VC,
but it is hop-by-hop, instead of end-to-end.

  - Each party sends its acceptable verson ranges together with
    public data for initiating encryption.  There is only one
    supported encryption scheme now, but the options should
    eventually include data for each of the encryption protocols
    the party is willing to support.

  - Currently the only encryption protocol is based on
    code.google.com/p/go.crypto/nacl/box,
    which uses Curve25519, XSalsa20 and Poly1305 to encrypt and
    authenticate messages.  The parties exchange public keys
    to create a box cipher.

  - Perform the standard Veyron handshake using the cipher for
    encryption.

  - On success, use the cipher to encrypt the control data.

RULES:

  - VIF message formats never change, except in compatible ways.
    New developments can only change the VIF protocol by adding
    more message types.

  - The format of the public data header in the Encrypt
    negotiation never changes for all time.

A VIF frame has two parts, a (Type + PayloadSize) header word, and
some payload.  The Type+PayloadSize is encrypted with the cipher's
XORKeyStream.  The rest of the control data is encrypted with the
cipher's Seal method.  This means that none of the data is observable
by an adversary, but the type and length are subject to corruption
(the rest of the data is not).  This doesn't matter -- if the Type
or PayloadSize is corrupted by an adversary, the payload will be
misread, and will fail to validate.

We could potentially pass the Type and PayloadSize in the clear,
but then the framing would be observable, a (probably minor)
information leak.  There is no reason to do so, we encrypt everything.

TIMES:
$ veyron go test veyron.io/veyron/veyron/runtimes/google/ipc/stream/benchmark -bench=Flow -run=X

Before:
BenchmarkFlow_1VIF_1VC_1Flow	   50000	     45029 ns/op	1137.02 MB/s
BenchmarkFlow_1VIF_1VC_2Flow	   50000	     39475 ns/op	1296.99 MB/s
BenchmarkFlow_1VIF_1VC_8Flow	   50000	     42355 ns/op	1208.82 MB/s
BenchmarkFlow_1VIF_2VC_2Flow	   50000	     40518 ns/op	1263.62 MB/s
BenchmarkFlow_1VIF_2VC_8Flow	   50000	     42861 ns/op	1194.55 MB/s
BenchmarkFlow_2VIF_4VC_8Flow	   50000	     43486 ns/op	1177.38 MB/s
BenchmarkFlow_1VIF_1VC_1FlowTLS	    5000	    521504 ns/op	  98.18 MB/s
BenchmarkFlow_1VIF_1VC_2FlowTLS	    5000	    503743 ns/op	 101.64 MB/s
BenchmarkFlow_1VIF_1VC_8FlowTLS	    5000	    508953 ns/op	 100.60 MB/s
BenchmarkFlow_1VIF_2VC_2FlowTLS	    5000	    503255 ns/op	 101.74 MB/s
BenchmarkFlow_1VIF_2VC_8FlowTLS	    5000	    510379 ns/op	 100.32 MB/s
BenchmarkFlow_2VIF_4VC_8FlowTLS	    5000	    511113 ns/op	 100.17 MB/s
ok  	veyron.io/veyron/veyron/runtimes/google/ipc/stream/benchmark	32.253s

After:
BenchmarkFlow_1VIF_1VC_1Flow	   50000	     43946 ns/op	1165.05 MB/s
BenchmarkFlow_1VIF_1VC_2Flow	   50000	     39394 ns/op	1299.67 MB/s
BenchmarkFlow_1VIF_1VC_8Flow	   50000	     43537 ns/op	1175.98 MB/s
BenchmarkFlow_1VIF_2VC_2Flow	   50000	     39945 ns/op	1281.74 MB/s
BenchmarkFlow_1VIF_2VC_8Flow	   50000	     41531 ns/op	1232.80 MB/s
BenchmarkFlow_2VIF_4VC_8Flow	   50000	     43568 ns/op	1175.17 MB/s
BenchmarkFlow_1VIF_1VC_1FlowTLS	    5000	    538491 ns/op	  95.08 MB/s
BenchmarkFlow_1VIF_1VC_2FlowTLS	    5000	    522869 ns/op	  97.92 MB/s
BenchmarkFlow_1VIF_1VC_8FlowTLS	    5000	    526982 ns/op	  97.16 MB/s
BenchmarkFlow_1VIF_2VC_2FlowTLS	    5000	    522381 ns/op	  98.01 MB/s
BenchmarkFlow_1VIF_2VC_8FlowTLS	    5000	    528707 ns/op	  96.84 MB/s
BenchmarkFlow_2VIF_4VC_8FlowTLS	    5000	    530732 ns/op	  96.47 MB/s
ok  	veyron.io/veyron/veyron/runtimes/google/ipc/stream/benchmark	33.302s

Change-Id: I3fcb0346dd23f77d549744fe061695a2dd606215
diff --git a/runtimes/google/ipc/stream/vif/auth.go b/runtimes/google/ipc/stream/vif/auth.go
new file mode 100644
index 0000000..6a05496
--- /dev/null
+++ b/runtimes/google/ipc/stream/vif/auth.go
@@ -0,0 +1,265 @@
+package vif
+
+import (
+	"crypto/rand"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+
+	"code.google.com/p/go.crypto/nacl/box"
+
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/crypto"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/message"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/version"
+	"veyron.io/veyron/veyron/runtimes/google/lib/iobuf"
+	"veyron.io/veyron/veyron2/ipc/stream"
+	ipcversion "veyron.io/veyron/veyron2/ipc/version"
+	"veyron.io/veyron/veyron2/options"
+	"veyron.io/veyron/veyron2/security"
+)
+
+var (
+	errUnsupportedEncryptVersion = errors.New("unsupported encryption version")
+	errVersionNegotiationFailed  = errors.New("encryption version negotiation failed")
+	nullCipher                   crypto.NullControlCipher
+)
+
+// privateData includes secret data we need for encryption.
+type privateData struct {
+	naclBoxPrivateKey [32]byte
+}
+
+// AuthenticateAsClient sends a HopSetup message if possible.  If so, it chooses
+// encryption based on the max supported version.
+//
+// The sequence is initiated by the client.
+//
+//    - If the versions include IPCVersion6 or greater, the client sends a
+//      HopSetup message to the server, containing the client's supported
+//      versions, and the client's crypto options.  The HopSetup message
+//      is sent in the clear.
+//
+//    - When the server receives the HopSetup message, it calls
+//      AuthenticateAsServer, which constructs a response HopSetup containing
+//      the server's version range, and any crypto options.
+//
+//    - For IPCVersion6, the client and server generate fresh public/private key
+//      pairs, sending the public key to the peer as a crypto option.  The
+//      remainder of the communication is encrypted as HopSetupStream messages
+//      using NewControlCipherIPC6, which is based on
+//      code.google.com/p/go.crypto/nacl/box.
+//
+//    - Once the encrypted HopSetupStream channel is setup, the client and
+//      server authenticate using the vc.AuthenticateAs{Client,Server} protocol.
+//
+// Note that the HopSetup messages are sent in the clear, so they are subject to
+// modification by a man-in-the-middle, which can currently force a downgrade by
+// modifying the acceptable version ranges downward.  This can be addressed by
+// including a hash of the HopSetup message in the encrypted stream.  It is
+// likely that this will be addressed in subsequent protocol versions (or it may
+// not be addressed at all if IPCVersion6 becomes the only supported version).
+func AuthenticateAsClient(conn net.Conn, versions *version.Range, principal security.Principal, dc vc.DischargeClient) (crypto.ControlCipher, error) {
+	if versions == nil {
+		versions = version.SupportedRange
+	}
+	if principal == nil {
+		// If there is no principal, we do not support encryption/authentication.
+		var err error
+		versions, err = versions.Intersect(&version.Range{Min: 0, Max: ipcversion.IPCVersion5})
+		if err != nil {
+			return nil, err
+		}
+	}
+	if versions.Max < ipcversion.IPCVersion6 {
+		return nullCipher, nil
+	}
+
+	// The client has not yet sent its public data.  Construct it and send it.
+	pvt, pub, err := makeHopSetup(versions)
+	if err != nil {
+		return nil, err
+	}
+	if err := message.WriteTo(conn, &pub, nullCipher); err != nil {
+		return nil, err
+	}
+
+	// Read the server's public data.
+	pool := iobuf.NewPool(0)
+	reader := iobuf.NewReader(pool, conn)
+	pmsg, err := message.ReadFrom(reader, nullCipher)
+	if err != nil {
+		return nil, err
+	}
+	ppub, ok := pmsg.(*message.HopSetup)
+	if !ok {
+		return nil, errVersionNegotiationFailed
+	}
+
+	// Choose the max version in the intersection.
+	vrange, err := pub.Versions.Intersect(&ppub.Versions)
+	if err != nil {
+		return nil, err
+	}
+	v := vrange.Max
+	if v < ipcversion.IPCVersion6 {
+		return nullCipher, nil
+	}
+
+	// Perform the authentication.
+	switch v {
+	case ipcversion.IPCVersion6:
+		return authenticateAsClientIPC6(conn, reader, principal, dc, &pvt, &pub, ppub)
+	default:
+		return nil, errUnsupportedEncryptVersion
+	}
+}
+
+func authenticateAsClientIPC6(writer io.Writer, reader *iobuf.Reader, principal security.Principal, dc vc.DischargeClient,
+	pvt *privateData, pub, ppub *message.HopSetup) (crypto.ControlCipher, error) {
+	pbox := ppub.NaclBox()
+	if pbox == nil {
+		return nil, errVersionNegotiationFailed
+	}
+	c := crypto.NewControlCipherIPC6(&pbox.PublicKey, &pvt.naclBoxPrivateKey, false)
+	sconn := newSetupConn(writer, reader, c)
+	// TODO(jyh): act upon the authentication results.
+	_, _, _, err := vc.AuthenticateAsClient(sconn, principal, dc, crypto.NewNullCrypter(), ipcversion.IPCVersion6)
+	if err != nil {
+		return nil, err
+	}
+	return c, nil
+}
+
+// AuthenticateAsServer handles a HopSetup message, choosing authentication
+// based on the max common version.
+//
+// See AuthenticateAsClient for a description of the negotiation.
+func AuthenticateAsServer(conn net.Conn, versions *version.Range, principal security.Principal, lBlessings security.Blessings,
+	dc vc.DischargeClient, ppub *message.HopSetup) (crypto.ControlCipher, error) {
+	var err error
+	if versions == nil {
+		versions = version.SupportedRange
+	}
+	if principal == nil {
+		// If there is no principal, we don't support encryption/authentication.
+		versions, err = versions.Intersect(&version.Range{Min: 0, Max: ipcversion.IPCVersion5})
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// Create our public data and send it to the client.
+	pvt, pub, err := makeHopSetup(versions)
+	if err != nil {
+		return nil, err
+	}
+	if err := message.WriteTo(conn, &pub, nullCipher); err != nil {
+		return nil, err
+	}
+
+	// Choose the max version in common.
+	vrange, err := pub.Versions.Intersect(&ppub.Versions)
+	if err != nil {
+		return nil, err
+	}
+	v := vrange.Max
+	if v < ipcversion.IPCVersion6 {
+		return nullCipher, nil
+	}
+
+	// Perform authentication.
+	switch v {
+	case ipcversion.IPCVersion6:
+		return authenticateAsServerIPC6(conn, principal, lBlessings, dc, &pvt, &pub, ppub)
+	default:
+		return nil, errUnsupportedEncryptVersion
+	}
+}
+
+func authenticateAsServerIPC6(conn net.Conn, principal security.Principal, lBlessings security.Blessings, dc vc.DischargeClient,
+	pvt *privateData, pub, ppub *message.HopSetup) (crypto.ControlCipher, error) {
+	box := ppub.NaclBox()
+	if box == nil {
+		return nil, errVersionNegotiationFailed
+	}
+	c := crypto.NewControlCipherIPC6(&box.PublicKey, &pvt.naclBoxPrivateKey, true)
+	pool := iobuf.NewPool(0)
+	reader := iobuf.NewReader(pool, conn)
+	sconn := newSetupConn(conn, reader, c)
+	// TODO(jyh): act upon authentication results.
+	_, _, err := vc.AuthenticateAsServer(sconn, principal, lBlessings, dc, crypto.NewNullCrypter(), ipcversion.IPCVersion6)
+	if err != nil {
+		return nil, err
+	}
+	return c, nil
+}
+
+// serverAuthOptions extracts the Principal from the options list.
+func serverAuthOptions(lopts []stream.ListenerOpt) (principal security.Principal, lBlessings security.Blessings, dischargeClient vc.DischargeClient, err error) {
+	var securityLevel options.VCSecurityLevel
+	for _, o := range lopts {
+		switch v := o.(type) {
+		case vc.DischargeClient:
+			dischargeClient = v
+		case vc.LocalPrincipal:
+			principal = v.Principal
+		case options.VCSecurityLevel:
+			securityLevel = v
+		case options.ServerBlessings:
+			lBlessings = v.Blessings
+		}
+	}
+	switch securityLevel {
+	case options.VCSecurityConfidential:
+		if principal == nil {
+			principal = vc.AnonymousPrincipal
+		}
+		if lBlessings == nil {
+			lBlessings = principal.BlessingStore().Default()
+		}
+	case options.VCSecurityNone:
+		principal = nil
+	default:
+		err = fmt.Errorf("unrecognized VC security level: %v", securityLevel)
+	}
+	return
+}
+
+// clientAuthOptions extracts the client authentication options from the options
+// list.
+func clientAuthOptions(lopts []stream.VCOpt) (principal security.Principal, dischargeClient vc.DischargeClient, err error) {
+	var securityLevel options.VCSecurityLevel
+	for _, o := range lopts {
+		switch v := o.(type) {
+		case vc.DischargeClient:
+			dischargeClient = v
+		case vc.LocalPrincipal:
+			principal = v.Principal
+		case options.VCSecurityLevel:
+			securityLevel = v
+		}
+	}
+	switch securityLevel {
+	case options.VCSecurityConfidential:
+		if principal == nil {
+			principal = vc.AnonymousPrincipal
+		}
+	case options.VCSecurityNone:
+		principal = nil
+	default:
+		err = fmt.Errorf("unrecognized VC security level: %v", securityLevel)
+	}
+	return
+}
+
+// makeHopSetup constructs the options that this process can support.
+func makeHopSetup(versions *version.Range) (pvt privateData, pub message.HopSetup, err error) {
+	pub.Versions = *versions
+	var pubKey, pvtKey *[32]byte
+	pubKey, pvtKey, err = box.GenerateKey(rand.Reader)
+	pub.Options = append(pub.Options, &message.NaclBox{PublicKey: *pubKey})
+	pvt.naclBoxPrivateKey = *pvtKey
+	return
+}