Revert "Revert "veyron/runtimes/google/ipc/stream/crypto: use nacl/crypto_box""
This reverts commit bcab01884738b1ad598cb744c66ba4a2fb9ca093.
Change-Id: I0a15287993732465f0f514b747209ab850a3cde0
diff --git a/runtimes/google/ipc/stream/crypto/box.go b/runtimes/google/ipc/stream/crypto/box.go
new file mode 100644
index 0000000..3ab2c33
--- /dev/null
+++ b/runtimes/google/ipc/stream/crypto/box.go
@@ -0,0 +1,87 @@
+package crypto
+
+import (
+ "bytes"
+ "code.google.com/p/go.crypto/nacl/box"
+ "crypto/rand"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "veyron/runtimes/google/lib/iobuf"
+)
+
+type boxcrypter struct {
+ conn net.Conn
+ alloc *iobuf.Allocator
+ sharedKey [32]byte
+ writeNonce, readNonce uint64
+}
+
+// NewBoxCrypter uses Curve25519, XSalsa20 and Poly1305 to encrypt and
+// authenticate messages (as defined in http://nacl.cr.yp.to/box.html).
+// An ephemeral Diffie-Hellman key exchange is performed per invocation
+// of NewBoxCrypter; the data sent has forward security with connection
+// granularity. One round-trip is required before any data can be sent.
+// BoxCrypter does NOT do anything to verify the identity of the peer.
+func NewBoxCrypter(conn net.Conn, pool *iobuf.Pool) (Crypter, error) {
+ pk, sk, err := box.GenerateKey(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+ var theirPK [32]byte
+ errChan := make(chan error)
+ defer close(errChan)
+ go func() { _, err := conn.Write(pk[:]); errChan <- err }()
+ go func() { _, err := io.ReadFull(conn, theirPK[:]); errChan <- err }()
+ if err := <-errChan; err != nil {
+ return nil, err
+ }
+ if err := <-errChan; err != nil {
+ return nil, err
+ }
+ ret := &boxcrypter{conn: conn, alloc: iobuf.NewAllocator(pool, 0)}
+ box.Precompute(&ret.sharedKey, &theirPK, sk)
+ // Distinct messages between the same {sender, receiver} set are required
+ // to have distinct nonces. The server with the lexicographically smaller
+ // public key will be sending messages with 0, 2, 4... and the other will
+ // be using 1, 3, 5...
+ if bytes.Compare(pk[:], theirPK[:]) < 0 {
+ ret.writeNonce, ret.readNonce = 0, 1
+ } else {
+ ret.writeNonce, ret.readNonce = 1, 0
+ }
+ return ret, nil
+}
+
+func (c *boxcrypter) Encrypt(src *iobuf.Slice) (*iobuf.Slice, error) {
+ defer src.Release()
+ var nonce [24]byte
+ binary.LittleEndian.PutUint64(nonce[:], c.writeNonce)
+ c.writeNonce += 2
+ ret := c.alloc.Alloc(uint(len(src.Contents) + box.Overhead))
+ ret.Contents = box.SealAfterPrecomputation(ret.Contents[:0], src.Contents, &nonce, &c.sharedKey)
+ return ret, nil
+}
+
+func (c *boxcrypter) Decrypt(src *iobuf.Slice) (*iobuf.Slice, error) {
+ defer src.Release()
+ var nonce [24]byte
+ binary.LittleEndian.PutUint64(nonce[:], c.readNonce)
+ c.readNonce += 2
+ retLen := len(src.Contents) - box.Overhead
+ if retLen < 0 {
+ return nil, fmt.Errorf("ciphertext too short")
+ }
+ ret := c.alloc.Alloc(uint(retLen))
+ var ok bool
+ ret.Contents, ok = box.OpenAfterPrecomputation(ret.Contents[:0], src.Contents, &nonce, &c.sharedKey)
+ if !ok {
+ return nil, fmt.Errorf("message authentication failed")
+ }
+ return ret, nil
+}
+
+func (c *boxcrypter) String() string {
+ return fmt.Sprintf("%#v", *c)
+}
diff --git a/runtimes/google/ipc/stream/crypto/crypto_test.go b/runtimes/google/ipc/stream/crypto/crypto_test.go
index 063d222..4bbdd45 100644
--- a/runtimes/google/ipc/stream/crypto/crypto_test.go
+++ b/runtimes/google/ipc/stream/crypto/crypto_test.go
@@ -37,8 +37,8 @@
crypter.String() // Only to test that String does not crash.
}
-func TestTLS(t *testing.T) {
- c1, c2 := tlsCrypters(t)
+func testSimple(t *testing.T, crypters func(testing.TB) (Crypter, Crypter)) {
+ c1, c2 := crypters(t)
// Execute String just to check that it does not crash.
c1.String()
c2.String()
@@ -63,6 +63,9 @@
t.Logf("Byte overhead of encryption: %v", overhead)
}
+func TestTLS(t *testing.T) { testSimple(t, tlsCrypters) }
+func TestBox(t *testing.T) { testSimple(t, boxCrypters) }
+
func TestTLSNil(t *testing.T) {
c1, c2 := tlsCrypters(t)
if t.Failed() {
@@ -123,12 +126,27 @@
return c1, c2
}
-func benchmarkEncrypt(b *testing.B, size int) {
+func boxCrypters(t testing.TB) (Crypter, Crypter) {
+ serverConn, clientConn := net.Pipe()
+ crypters := make(chan Crypter)
+ for _, conn := range []net.Conn{serverConn, clientConn} {
+ go func(conn net.Conn) {
+ crypter, err := NewBoxCrypter(conn, iobuf.NewPool(0))
+ if err != nil {
+ t.Fatal(err)
+ }
+ crypters <- crypter
+ }(conn)
+ }
+ return <-crypters, <-crypters
+}
+
+func benchmarkEncrypt(b *testing.B, crypters func(testing.TB) (Crypter, Crypter), size int) {
plaintext := make([]byte, size)
if _, err := rand.Read(plaintext); err != nil {
b.Fatal(err)
}
- e, _ := tlsCrypters(b)
+ e, _ := crypters(b)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -140,13 +158,19 @@
}
}
-func BenchmarkEncrypt_1B(b *testing.B) { benchmarkEncrypt(b, 1) }
-func BenchmarkEncrypt_1K(b *testing.B) { benchmarkEncrypt(b, 1<<10) }
-func BenchmarkEncrypt_10K(b *testing.B) { benchmarkEncrypt(b, 10<<10) }
-func BenchmarkEncrypt_1M(b *testing.B) { benchmarkEncrypt(b, 1<<20) }
-func BenchmarkEncrypt_5M(b *testing.B) { benchmarkEncrypt(b, 5<<20) }
+func BenchmarkTLSEncrypt_1B(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 1) }
+func BenchmarkTLSEncrypt_1K(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 1<<10) }
+func BenchmarkTLSEncrypt_10K(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 10<<10) }
+func BenchmarkTLSEncrypt_1M(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 1<<20) }
+func BenchmarkTLSEncrypt_5M(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 5<<20) }
-func benchmarkRoundTrip(b *testing.B, size int) {
+func BenchmarkBoxEncrypt_1B(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 1) }
+func BenchmarkBoxEncrypt_1K(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 1<<10) }
+func BenchmarkBoxEncrypt_10K(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 10<<10) }
+func BenchmarkBoxEncrypt_1M(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 1<<20) }
+func BenchmarkBoxEncrypt_5M(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 5<<20) }
+
+func benchmarkRoundTrip(b *testing.B, crypters func(testing.TB) (Crypter, Crypter), size int) {
plaintext := make([]byte, size)
if _, err := rand.Read(plaintext); err != nil {
b.Fatal(err)
@@ -166,8 +190,14 @@
plainslice.Release()
}
}
-func BenchmarkRoundTrip_1B(b *testing.B) { benchmarkRoundTrip(b, 1) }
-func BenchmarkRoundTrip_1K(b *testing.B) { benchmarkRoundTrip(b, 1<<10) }
-func BenchmarkRoundTrip_10K(b *testing.B) { benchmarkRoundTrip(b, 10<<10) }
-func BenchmarkRoundTrip_1M(b *testing.B) { benchmarkRoundTrip(b, 1<<20) }
-func BenchmarkRoundTrip_5M(b *testing.B) { benchmarkRoundTrip(b, 5<<20) }
+func BenchmarkTLSRoundTrip_1B(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 1) }
+func BenchmarkTLSRoundTrip_1K(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 1<<10) }
+func BenchmarkTLSRoundTrip_10K(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 10<<10) }
+func BenchmarkTLSRoundTrip_1M(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 1<<20) }
+func BenchmarkTLSRoundTrip_5M(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 5<<20) }
+
+func BenchmarkBoxRoundTrip_1B(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 1) }
+func BenchmarkBoxRoundTrip_1K(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 1<<10) }
+func BenchmarkBoxRoundTrip_10K(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 10<<10) }
+func BenchmarkBoxRoundTrip_1M(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 1<<20) }
+func BenchmarkBoxRoundTrip_5M(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 5<<20) }