about to stare actually using the connection wrapper

Change-Id: If0f0eb71b4adf43cc6fce05df2f9306ceb9affec
diff --git a/runtime/internal/flow/conn/grpc/conn.go b/runtime/internal/flow/conn/grpc/conn.go
index 5ce7b6a..08fa993 100644
--- a/runtime/internal/flow/conn/grpc/conn.go
+++ b/runtime/internal/flow/conn/grpc/conn.go
@@ -28,12 +28,26 @@
 	secretKey *[32]byte
 	sharedKey *[32]byte
 	binding   []byte
-	nonce     [24]byte
-	counter   uint64 // Why is this so weirdly handled in advanceNonce?
+	nonce     [24]byte // TODO: do I need a second nonce?
+	counter   uint64   // Why is this so weirdly handled in advanceNonce?
 }
 
 func (c *conn) Read(b []byte) (n int, err error) {
-	return 0, nil
+	resBuf := make([]byte, 4096) // TODO better (dynamic) size or way of reading?
+	bytesRead, err := c.rawConn.Read(resBuf)
+	if err != nil {
+		return bytesRead, err
+	}
+
+	tmp := make([]byte, bytesRead+box.Overhead) // TODO: is this enough? Also, why do we need both of tmp and out?
+	out, ok := box.OpenAfterPrecomputation(tmp, resBuf[:bytesRead], c.currentNonce(), c.sharedKey)
+	c.advanceNonce() // TODO: defer? Is there harm in advancing often than necessary (i.e. on error)?
+	if !ok {
+		return -1, err
+	}
+	copy(b, out)
+	// should we return bytesRead or len(b)?
+	return bytesRead, errors.New("Wait, did this work?")
 }
 
 // TODO: all this casting is gross