blob: 28834f81895d67e2f491ed1db411b837de672b6e [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package grpc
import (
"bytes"
_ "encoding/binary"
"errors"
"fmt"
"io"
_ "io/ioutil"
"log"
"net"
"runtime/debug"
"sync"
"time"
"golang.org/x/crypto/nacl/box"
)
// Implements net.Conn. Encrypts messages using keys negotioated via NaCl.
// TODO: blessings
// TODO: discharges and caveats
// TODO: ensuer that all 5 of these are set. Constructor?
// TODO: do I need to check the message size as in box_coipher.Seal?
// TODO: make all these keys not pointers?
// TODO: should I have locks here?
// TODO: what's all this salsa stuff?
type conn struct {
rawConn net.Conn
publicKey *[32]byte
secretKey *[32]byte
sharedKey *[32]byte
binding []byte
nonce [24]byte // TODO: do I need a second nonce? Does this need to be random?
counter uint64 // Why is this so weirdly handled in advanceNonce?
mu sync.Mutex
}
func (c *conn) Read(b []byte) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
log.Printf("Beginning to Read.\n")
resBuf := make([]byte, 4096*4096) // TODO better (dynamic) size or way of reading?
bytesRead, err := c.rawConn.Read(resBuf)
log.Printf("Read %d bytes: %v\n", bytesRead, resBuf[:bytesRead])
if err != nil {
log.Printf("Failed to read.\n")
log.Fatal(err)
return bytesRead, err
}
// tmp := make([]byte, 0, bytesRead-box.Overhead) // TODO: is this enough? Also, why do we need both of tmp and out?
out, ok := box.OpenAfterPrecomputation(nil, resBuf[:bytesRead], c.currentNonce(), c.sharedKey)
c.advanceNonce() // TODO: defer? Is there harm in advancing often than necessary (i.e. on error)?
if !ok {
log.Printf("Failed to decrypt.\n")
//log.Fatal(err)
return 0, err
}
copy(b, out)
// should we return bytesRead or len(b)?
log.Printf("cap(b): %d, len(b) %d, \ncap(out): %d, len(out): %d\n", cap(b), len(b), cap(out), len(out))
log.Printf("Succeeded in reading.\n\n")
// return bytesRead, nil
return len(b), nil
}
// TODO: all this casting is gross
func (c *conn) Write(b []byte) (n int, err error) {
// log.Printf("Write called:\n%v\n", string(debug.Stack()))
c.mu.Lock()
defer c.mu.Unlock()
log.Printf("Beginning to write.\n")
tmp := make([]byte, 3, 3+len(b)+box.Overhead) // TODO: is this enough? Also, why do we need both of tmp and out?
tmp[0] = 5
tmp[1] = 7
tmp[2] = 14
out := box.SealAfterPrecomputation(tmp, b, c.currentNonce(), c.sharedKey)
log.Printf("tmp: %v", tmp)
log.Printf("out: %v", out)
log.Printf("tmp[0]: %p", &tmp[0])
log.Printf("tmp[1]: %p", &tmp[1])
log.Printf("tmp[2]: %p", &tmp[2])
log.Printf("out[0]: %p", &out[0])
c.advanceNonce()
bytesCopied, err := io.Copy(c.rawConn, bytes.NewReader(out))
log.Printf("Wrote %d bytes.\n", bytesCopied)
if err != nil {
log.Printf("Failed to copy to rawConn.\n")
log.Fatal(err)
return int(bytesCopied), err
}
if bytesCopied != int64(len(out)) {
errMsg := fmt.Sprintf("Did not write entire message. Expected to write %d bytes but wrote %d.", len(out), bytesCopied)
log.Printf("Did not write entire message. Expected to write %d bytes but wrote %d.", len(out), bytesCopied)
log.Printf("Failed to write.\n")
log.Fatal(err)
return -1, errors.New(errMsg)
}
log.Printf("cap(b): %d, len(b) %d, cap(out): %d, len(out): %d\n", cap(b), len(b), cap(out), len(out))
log.Printf("Succeeded in writing!\n\n")
// return int(bytesCopied), nil
return len(b), nil
}
// TODO: understand this stuff
const maxPacketSize = 0xffffff
func write3ByteUint(dst []byte, n int) error {
if n > maxPacketSize || n < 0 {
// return NewErrLargerThan3ByteUInt(nil)
return errors.New("TOOO BIG")
}
n = maxPacketSize - n
dst[0] = byte((n & 0xff0000) >> 16)
dst[1] = byte((n & 0x00ff00) >> 8)
dst[2] = byte(n & 0x0000ff)
return nil
}
func read3ByteUint(src [3]byte) int {
return maxPacketSize - (int(src[0])<<16 | int(src[1])<<8 | int(src[2]))
}
// TODO: cover these up with an interface?
// TODO: remove all these useless mutex locks and unlocks
func (c *conn) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
debug.PrintStack()
// log.Fatalf("DON'T CLOSE THE CONNETION\n")
return c.rawConn.Close()
}
func (c *conn) LocalAddr() net.Addr {
c.mu.Lock()
defer c.mu.Unlock()
return c.rawConn.LocalAddr()
}
func (c *conn) RemoteAddr() net.Addr {
c.mu.Lock()
defer c.mu.Unlock()
return c.rawConn.RemoteAddr()
}
func (c *conn) SetDeadline(t time.Time) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.rawConn.SetDeadline(t)
}
func (c *conn) SetReadDeadline(t time.Time) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.rawConn.SetReadDeadline(t)
}
func (c *conn) SetWriteDeadline(t time.Time) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.rawConn.SetWriteDeadline(t)
}
// TODO: should I just collapse current and advance?
func (c *conn) currentNonce() *[24]byte {
return &c.nonce
}
// TODO: re enable.
func (c *conn) advanceNonce() {
// c.counter++
// binary.LittleEndian.PutUint64(c.nonce[:], c.counter)
}