veyron/runtimes/google/ipc/stream/vc: channel binding authentication

This CL depends on a recent change in the environment repository:
https://veyron-review.googlesource.com/#/c/3832/

Change-Id: I17027bd77ff3af4096dcf933c18853173fdb9299
diff --git a/runtimes/google/ipc/stream/crypto/box.go b/runtimes/google/ipc/stream/crypto/box.go
index 3ab2c33..10a9cea 100644
--- a/runtimes/google/ipc/stream/crypto/box.go
+++ b/runtimes/google/ipc/stream/crypto/box.go
@@ -15,6 +15,7 @@
 	conn                  net.Conn
 	alloc                 *iobuf.Allocator
 	sharedKey             [32]byte
+	sortedPubkeys         []byte
 	writeNonce, readNonce uint64
 }
 
@@ -48,8 +49,10 @@
 	// be using 1, 3, 5...
 	if bytes.Compare(pk[:], theirPK[:]) < 0 {
 		ret.writeNonce, ret.readNonce = 0, 1
+		ret.sortedPubkeys = append(pk[:], theirPK[:]...)
 	} else {
 		ret.writeNonce, ret.readNonce = 1, 0
+		ret.sortedPubkeys = append(theirPK[:], pk[:]...)
 	}
 	return ret, nil
 }
@@ -82,6 +85,10 @@
 	return ret, nil
 }
 
+func (c *boxcrypter) ChannelBinding() []byte {
+	return c.sortedPubkeys
+}
+
 func (c *boxcrypter) String() string {
 	return fmt.Sprintf("%#v", *c)
 }
diff --git a/runtimes/google/ipc/stream/crypto/crypto.go b/runtimes/google/ipc/stream/crypto/crypto.go
index 6f6a2c3..d881309 100644
--- a/runtimes/google/ipc/stream/crypto/crypto.go
+++ b/runtimes/google/ipc/stream/crypto/crypto.go
@@ -25,5 +25,11 @@
 type Crypter interface {
 	Encrypter
 	Decrypter
+	// ChannelBinding Returns a byte slice that is unique for the the
+	// particular crypter (and the parties between which it is operationg).
+	// Having both parties assert out of the band that they are indeed
+	// participating in a connection with that channel binding value is
+	// sufficient to authenticate the data received through the crypter.
+	ChannelBinding() []byte
 	String() string
 }
diff --git a/runtimes/google/ipc/stream/crypto/null.go b/runtimes/google/ipc/stream/crypto/null.go
index 7b07ce6..0401076 100644
--- a/runtimes/google/ipc/stream/crypto/null.go
+++ b/runtimes/google/ipc/stream/crypto/null.go
@@ -7,6 +7,7 @@
 
 type null struct{}
 
-func (c null) Encrypt(src *iobuf.Slice) (*iobuf.Slice, error) { return src, nil }
-func (c null) Decrypt(src *iobuf.Slice) (*iobuf.Slice, error) { return src, nil }
-func (c null) String() string                                 { return "Null" }
+func (null) Encrypt(src *iobuf.Slice) (*iobuf.Slice, error) { return src, nil }
+func (null) Decrypt(src *iobuf.Slice) (*iobuf.Slice, error) { return src, nil }
+func (null) String() string                                 { return "Null" }
+func (null) ChannelBinding() []byte                         { return nil }
diff --git a/runtimes/google/ipc/stream/crypto/tls.go b/runtimes/google/ipc/stream/crypto/tls.go
index 8dfb09d..8c62afe 100644
--- a/runtimes/google/ipc/stream/crypto/tls.go
+++ b/runtimes/google/ipc/stream/crypto/tls.go
@@ -24,6 +24,8 @@
 // handshaker was initiated by a client.
 func NewTLSClient(handshaker net.Conn, sessionCache tls.ClientSessionCache, pool *iobuf.Pool) (Crypter, error) {
 	var config tls.Config
+	// TLS + resumption + channel bindings is broken: <https://secure-resumption.com/#channelbindings>.
+	config.SessionTicketsDisabled = true
 	config.InsecureSkipVerify = true
 	config.ClientSessionCache = sessionCache
 	return newTLSCrypter(handshaker, &config, pool, false)
@@ -192,9 +194,10 @@
 		panic(err)
 	}
 	return &tls.Config{
-		Certificates:       []tls.Certificate{c},
-		InsecureSkipVerify: true,
-		ClientAuth:         tls.NoClientCert,
+		// TLS + resumption + channel bindings is broken: <https://secure-resumption.com/#channelbindings>.
+		SessionTicketsDisabled: true,
+		Certificates:           []tls.Certificate{c},
+		InsecureSkipVerify:     true,
 		// RC4_128_SHA is 4-5X faster compared to the other cipher suites
 		// and is what google.com seems to use.
 		// Allowing ECDHE_RSA for the key exchange since some older binaries
@@ -203,6 +206,10 @@
 	}
 }
 
+func (c *tlsCrypter) ChannelBinding() []byte {
+	return c.tls.ConnectionState().TLSUnique
+}
+
 // TODO(ashankar): Get rid of TLS certificates completely after implementing an
 // anonymous key-exchange mechanism. See F.1.1.1 in RFC 5246.
 //
diff --git a/runtimes/google/ipc/stream/manager/manager_test.go b/runtimes/google/ipc/stream/manager/manager_test.go
index 84a78ee..ff9a4dc 100644
--- a/runtimes/google/ipc/stream/manager/manager_test.go
+++ b/runtimes/google/ipc/stream/manager/manager_test.go
@@ -293,6 +293,7 @@
 	}
 }
 
+/* TLS + resumption + channel bindings is broken: <https://secure-resumption.com/#channelbindings>.
 func TestSessionTicketCache(t *testing.T) {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
 	_, ep, err := server.Listen("tcp", "localhost:0", vc.FixedLocalID(newID("server")))
@@ -309,6 +310,7 @@
 		t.Fatalf("SessionTicket from TLS handshake not cached")
 	}
 }
+*/
 
 func TestMultipleVCs(t *testing.T) {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
diff --git a/runtimes/google/ipc/stream/vc/auth.go b/runtimes/google/ipc/stream/vc/auth.go
index 76e7118..28dfa8c 100644
--- a/runtimes/google/ipc/stream/vc/auth.go
+++ b/runtimes/google/ipc/stream/vc/auth.go
@@ -21,6 +21,11 @@
 	serverChannelEnd = "server"
 )
 
+var (
+	authServerContextTag = []byte("VCauthS\x00")
+	authClientContextTag = []byte("VCauthC\x00")
+)
+
 // authenticateAsServer executes the authentication protocol at the server and
 // returns the identity of the client and server.
 func authenticateAsServer(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter, v version.IPCVersion) (clientID, serverID security.PublicID, err error) {
@@ -30,10 +35,10 @@
 	if serverID, err = localID.AsServer(); err != nil {
 		return
 	}
-	if err = writeIdentity(conn, serverChannelEnd, crypter, localID, serverID, v); err != nil {
+	if err = writeIdentity(conn, serverChannelEnd, authServerContextTag, crypter, localID, serverID, v); err != nil {
 		return
 	}
-	clientID, err = readIdentity(conn, clientChannelEnd, crypter, v)
+	clientID, err = readIdentity(conn, clientChannelEnd, authClientContextTag, crypter, v)
 	return
 }
 
@@ -44,7 +49,7 @@
 // successfull iff the server identity matches the provided regular expression.
 func authenticateAsClient(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter, v version.IPCVersion) (serverID, clientID security.PublicID, err error) {
 	defer conn.Close()
-	if serverID, err = readIdentity(conn, serverChannelEnd, crypter, v); err != nil {
+	if serverID, err = readIdentity(conn, serverChannelEnd, authServerContextTag, crypter, v); err != nil {
 		return
 	}
 	// TODO(ashankar,ataly): Have the ability to avoid talking to a server we do not want to.
@@ -52,7 +57,7 @@
 	if clientID, err = localID.AsClient(serverID); err != nil {
 		return
 	}
-	err = writeIdentity(conn, clientChannelEnd, crypter, localID, clientID, v)
+	err = writeIdentity(conn, clientChannelEnd, authClientContextTag, crypter, localID, clientID, v)
 	return
 }
 
@@ -76,125 +81,170 @@
 	errSingleCertificateRequired = errors.New("exactly one X.509 certificate chain with exactly one certificate is required")
 )
 
-func writeIdentity(w io.Writer, chEnd string, enc crypto.Encrypter, id LocalID, pub security.PublicID, v version.IPCVersion) error {
-	// Compute channel id - encrypted chEnd string
-	chid, err := enc.Encrypt(iobuf.NewSlice([]byte(chEnd)))
+func writeIdentity(w io.Writer, chEnd string, contextTag []byte, crypter crypto.Crypter, id LocalID, pub security.PublicID, v version.IPCVersion) error {
+	// TODO(ashankar): Remove references to protocol V1 and V2 before release
+	if v == version.IPCVersion1 || v == version.IPCVersion2 {
+		// Compute channel id - encrypted chEnd string
+		chid, err := crypter.Encrypt(iobuf.NewSlice([]byte(chEnd)))
+		if err != nil {
+			return err
+		}
+		defer chid.Release()
+
+		// VOM-encode and encrypt the (public) identity.
+		var buf bytes.Buffer
+		if err := vom.NewEncoder(&buf).Encode(pub); err != nil {
+			return err
+		}
+		eid, err := crypter.Encrypt(iobuf.NewSlice(buf.Bytes()))
+		if err != nil {
+			return err
+		}
+		defer eid.Release()
+
+		// Sign the channel ID
+		signature, err := id.Sign(chid.Contents)
+		if err != nil {
+			return err
+		}
+		msg := identityMessage{
+			ID:        eid.Contents,
+			ChannelID: chid.Contents,
+		}
+		if v == version.IPCVersion1 {
+			er, err := crypter.Encrypt(iobuf.NewSlice(signature.R))
+			if err != nil {
+				return err
+			}
+			defer er.Release()
+			es, err := crypter.Encrypt(iobuf.NewSlice(signature.S))
+			if err != nil {
+				return err
+			}
+			defer es.Release()
+			msg.SignR, msg.SignS = er.Contents, es.Contents
+		} else { // v == version.IPCVersion2
+			buf = bytes.Buffer{}
+			if err := vom.NewEncoder(&buf).Encode(signature); err != nil {
+				return err
+			}
+			esig, err := crypter.Encrypt(iobuf.NewSlice(buf.Bytes()))
+			if err != nil {
+				return err
+			}
+			defer esig.Release()
+			msg.Signature = esig.Contents
+		}
+		// Write the message out.
+		return vom.NewEncoder(w).Encode(msg)
+	}
+	// v == version.IPCVersion3
+	signature, err := id.Sign(append(contextTag, crypter.ChannelBinding()...))
 	if err != nil {
 		return err
 	}
-	defer chid.Release()
-
-	// VOM-encode and encrypt the (public) identity.
 	var buf bytes.Buffer
-	if err := vom.NewEncoder(&buf).Encode(pub); err != nil {
+	ve := vom.NewEncoder(&buf)
+	if err := ve.Encode(pub); err != nil {
 		return err
 	}
-	eid, err := enc.Encrypt(iobuf.NewSlice(buf.Bytes()))
+	if err := ve.Encode(signature); err != nil {
+		return err
+	}
+	msg, err := crypter.Encrypt(iobuf.NewSlice(buf.Bytes()))
 	if err != nil {
 		return err
 	}
-	defer eid.Release()
-
-	// Sign the channel ID
-	signature, err := id.Sign(chid.Contents)
-	if err != nil {
-		return err
-	}
-	msg := identityMessage{
-		ID:        eid.Contents,
-		ChannelID: chid.Contents,
-	}
-	if v == version.IPCVersion1 {
-		er, err := enc.Encrypt(iobuf.NewSlice(signature.R))
-		if err != nil {
-			return err
-		}
-		defer er.Release()
-		es, err := enc.Encrypt(iobuf.NewSlice(signature.S))
-		if err != nil {
-			return err
-		}
-		defer es.Release()
-		msg.SignR, msg.SignS = er.Contents, es.Contents
-	} else {
-		buf = bytes.Buffer{}
-		if err := vom.NewEncoder(&buf).Encode(signature); err != nil {
-			return err
-		}
-		esig, err := enc.Encrypt(iobuf.NewSlice(buf.Bytes()))
-		if err != nil {
-			return err
-		}
-		defer esig.Release()
-		msg.Signature = esig.Contents
-	}
-
-	// Write the message out.
-	return vom.NewEncoder(w).Encode(msg)
+	defer msg.Release()
+	return vom.NewEncoder(w).Encode(msg.Contents)
 }
 
-func readIdentity(reader io.Reader, expectedChEnd string, dec crypto.Decrypter, v version.IPCVersion) (security.PublicID, error) {
-	// Read the message.
-	var msg identityMessage
-	if err := vom.NewDecoder(reader).Decode(&msg); err != nil {
-		return nil, fmt.Errorf("failed to decode identityMessage: %v", err)
-	}
+func readIdentity(reader io.Reader, expectedChEnd string, contextTag []byte, crypter crypto.Crypter, v version.IPCVersion) (security.PublicID, error) {
+	if v == version.IPCVersion1 || v == version.IPCVersion2 {
+		// Read the message.
+		var msg identityMessage
+		if err := vom.NewDecoder(reader).Decode(&msg); err != nil {
+			return nil, fmt.Errorf("failed to decode identityMessage: %v", err)
+		}
 
-	// Decrypt and authenticate the channel id
-	chEnd, err := dec.Decrypt(iobuf.NewSlice(msg.ChannelID))
-	if err != nil {
-		return nil, fmt.Errorf("failed to decrypt ChannelID: %v", err)
-	}
-	defer chEnd.Release()
-	if bytes.Compare([]byte(expectedChEnd), chEnd.Contents) != 0 {
-		return nil, errChannelIDMismatch
-	}
+		// Decrypt and authenticate the channel id
+		chEnd, err := crypter.Decrypt(iobuf.NewSlice(msg.ChannelID))
+		if err != nil {
+			return nil, fmt.Errorf("failed to decrypt ChannelID: %v", err)
+		}
+		defer chEnd.Release()
+		if bytes.Compare([]byte(expectedChEnd), chEnd.Contents) != 0 {
+			return nil, errChannelIDMismatch
+		}
 
-	// Decrypt and VOM-decode the identity
-	idbytes, err := dec.Decrypt(iobuf.NewSlice(msg.ID))
-	if err != nil {
-		return nil, fmt.Errorf("failed to decrypt security.PublicID: %v", err)
+		// Decrypt and VOM-decode the identity
+		idbytes, err := crypter.Decrypt(iobuf.NewSlice(msg.ID))
+		if err != nil {
+			return nil, fmt.Errorf("failed to decrypt security.PublicID: %v", err)
+		}
+		defer idbytes.Release()
+		var id security.PublicID
+		if err := vom.NewDecoder(bytes.NewBuffer(idbytes.Contents)).Decode(&id); err != nil {
+			return nil, fmt.Errorf("failed to decode security.PublicID: %v", err)
+		}
+		if id == nil || id.PublicKey() == nil {
+			return nil, errInvalidIdentityInMessage
+		}
+
+		// Decrypt the signature
+		if v == version.IPCVersion1 {
+			r, err := crypter.Decrypt(iobuf.NewSlice(msg.SignR))
+			if err != nil {
+				return nil, fmt.Errorf("failed to decrypt SignR: %v", err)
+			}
+			defer r.Release()
+			s, err := crypter.Decrypt(iobuf.NewSlice(msg.SignS))
+			if err != nil {
+				return nil, fmt.Errorf("failed to decrypt SignS: %v", err)
+			}
+			defer s.Release()
+			if !ecdsa.Verify(id.PublicKey(), msg.ChannelID, bigInt(r), bigInt(s)) {
+				return nil, errInvalidSignatureInMessage
+			}
+		} else {
+			sigbytes, err := crypter.Decrypt(iobuf.NewSlice(msg.Signature))
+			if err != nil {
+				return nil, fmt.Errorf("failed to decrypt Signature: %v", err)
+			}
+			defer sigbytes.Release()
+			var sig security.Signature
+			if err = vom.NewDecoder(bytes.NewBuffer(sigbytes.Contents)).Decode(&sig); err != nil {
+				return nil, fmt.Errorf("failed to decode security.Signature: %v", err)
+			}
+			// Validate the signature
+			if !sig.Verify(id.PublicKey(), msg.ChannelID) {
+				return nil, errInvalidSignatureInMessage
+			}
+		}
+		return id, nil
 	}
-	defer idbytes.Release()
+	// v == version.IPCVersion3
+	var encryptedHandshake []byte
+	if err := vom.NewDecoder(reader).Decode(&encryptedHandshake); err != nil {
+		return nil, fmt.Errorf("failed to read handshake: %v", err)
+	}
+	handshake, err := crypter.Decrypt(iobuf.NewSlice(encryptedHandshake))
+	if err != nil {
+		return nil, fmt.Errorf("failed to decrypt handshake: %v", err)
+	}
+	defer handshake.Release()
+	vd := vom.NewDecoder(bytes.NewBuffer(handshake.Contents))
 	var id security.PublicID
-	if err := vom.NewDecoder(bytes.NewBuffer(idbytes.Contents)).Decode(&id); err != nil {
+	if err := vd.Decode(&id); err != nil {
 		return nil, fmt.Errorf("failed to decode security.PublicID: %v", err)
 	}
-	if id == nil || id.PublicKey() == nil {
-		return nil, errInvalidIdentityInMessage
+	var signature security.Signature
+	if err := vd.Decode(&signature); err != nil {
+		return nil, fmt.Errorf("failed to decode security.Signature: %v", err)
 	}
-
-	// Decrypt the signature
-	if v == version.IPCVersion1 {
-		r, err := dec.Decrypt(iobuf.NewSlice(msg.SignR))
-		if err != nil {
-			return nil, fmt.Errorf("failed to decrypt SignR: %v", err)
-		}
-		defer r.Release()
-		s, err := dec.Decrypt(iobuf.NewSlice(msg.SignS))
-		if err != nil {
-			return nil, fmt.Errorf("failed to decrypt SignS: %v", err)
-		}
-		defer s.Release()
-		if !ecdsa.Verify(id.PublicKey(), msg.ChannelID, bigInt(r), bigInt(s)) {
-			return nil, errInvalidSignatureInMessage
-		}
-	} else {
-		sigbytes, err := dec.Decrypt(iobuf.NewSlice(msg.Signature))
-		if err != nil {
-			return nil, fmt.Errorf("failed to decrypt Signature: %v", err)
-		}
-		defer sigbytes.Release()
-		var sig security.Signature
-		if err = vom.NewDecoder(bytes.NewBuffer(sigbytes.Contents)).Decode(&sig); err != nil {
-			return nil, fmt.Errorf("failed to decode security.Signature: %v", err)
-		}
-		// Validate the signature
-		if !sig.Verify(id.PublicKey(), msg.ChannelID) {
-			return nil, errInvalidSignatureInMessage
-		}
+	if !signature.Verify(id.PublicKey(), append(contextTag, crypter.ChannelBinding()...)) {
+		return nil, errInvalidSignatureInMessage
 	}
-
 	return id, nil
 }