Files
weave/crypto.go
T
2014-08-18 17:55:26 +01:00

450 lines
13 KiB
Go

package weave
import (
"bytes"
"code.google.com/p/go-bit/bit"
"code.google.com/p/go.crypto/nacl/box"
"code.google.com/p/go.crypto/nacl/secretbox"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"encoding/gob"
"fmt"
"log"
)
func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
return box.GenerateKey(rand.Reader)
}
func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey *[]byte) *[32]byte {
var sharedKey [32]byte
box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
sharedKeySlice := sharedKey[:]
sharedKeySlice = append(sharedKeySlice, *secretKey...)
sessionKey := sha256.Sum256(sharedKeySlice)
return &sessionKey
}
func GenerateRandomNonce() ([24]byte, error) {
var nonce [24]byte
n, err := rand.Read(nonce[:])
if err != nil {
return nonce, err
}
if n != 24 {
return nonce, fmt.Errorf("Did not read enough - wanted 24, got %v", n)
}
return nonce, nil
}
func EncryptPrefixNonce(plaintxt []byte, nonce *[24]byte, secret *[32]byte) []byte {
buf := make([]byte, 24, 24+len(plaintxt)+secretbox.Overhead)
copy(buf, nonce[:])
// Seal *appends* to buf
return secretbox.Seal(buf, plaintxt, nonce, secret)
}
func DecryptPrefixNonce(ciphertxt []byte, secret *[32]byte) ([]byte, bool) {
if len(ciphertxt) < secretbox.Overhead+24 {
return nil, false
}
// There is no way to nicely convert from a slice to an
// array. So have to used the following loop.
var nonce [24]byte
for idx, e := range ciphertxt[0:24] {
nonce[idx] = e
}
ciphertxt = ciphertxt[24:]
return secretbox.Open(nil, ciphertxt, &nonce, secret)
}
func SetNonceLow15Bits(nonce *[24]byte, offset uint16) {
// ensure top bit of offset is 0
offset = offset & ((1 << 15) - 1)
// grab top bit of nonce[22:24] (and clear out lower bits)
nonceBits := binary.BigEndian.Uint16(nonce[22:24]) & (1 << 15)
// Big endian => the MSB is stored at the *lowest* address. So
// that top bit in nonce[22] should stay as the top bit in
// nonce[22]
binary.BigEndian.PutUint16(nonce[22:24], nonceBits+offset)
}
// Nonce encoding/decoding
func EncodeNonce(df bool) (*[24]byte, []byte, error) {
nonce, err := GenerateRandomNonce()
if err != nil {
return nil, []byte{}, err
}
// wipe out lowest 15 bits, but encode the df right at the bottom
flags := uint16(0)
if df {
flags = flags | 1
}
SetNonceLow15Bits(&nonce, flags)
return &nonce, Concat(ProtocolNonceByte, nonce[:]), nil
}
func DecodeNonce(msg []byte) (bool, *[24]byte) {
flags := uint16(msg[23])
df := 0 != (flags & 1)
nonce := [24]byte{}
// upper bound is exclusive so this avoids copying the flags
for idx, elem := range msg[:23] {
nonce[idx] = elem
}
return df, &nonce
}
// Frame Encryptors
func NewNonEncryptor(prefix []byte) *NonEncryptor {
buf := make([]byte, MaxUDPPacketSize)
prefixLen := copy(buf, prefix)
return &NonEncryptor{
buf: buf,
bufTail: buf[prefixLen:],
buffered: prefixLen,
prefixLen: prefixLen}
}
func (ne *NonEncryptor) PacketOverhead() int {
return ne.prefixLen
}
func (ne *NonEncryptor) FrameOverhead() int {
return NameSize + NameSize + 2
}
func (ne *NonEncryptor) IsEmpty() bool {
return ne.buffered == ne.prefixLen
}
func (ne *NonEncryptor) Bytes() []byte {
buf := ne.buf[:ne.buffered]
ne.buffered = ne.prefixLen
ne.bufTail = ne.buf[ne.prefixLen:]
return buf
}
func (ne *NonEncryptor) AppendFrame(frame *ForwardedFrame) {
bufTail := ne.bufTail
srcLen := copy(bufTail, frame.srcPeer.NameByte)
bufTail = bufTail[srcLen:]
dstLen := copy(bufTail, frame.dstPeer.NameByte)
bufTail = bufTail[dstLen:]
frameLen := len(frame.frame)
binary.BigEndian.PutUint16(bufTail, uint16(frameLen))
bufTail = bufTail[2:]
copy(bufTail, frame.frame)
ne.bufTail = bufTail[frameLen:]
ne.buffered = ne.buffered + srcLen + dstLen + 2 + frameLen
}
func (ne *NonEncryptor) TotalLen() int {
return ne.buffered
}
func NewNaClEncryptor(prefix []byte, conn *LocalConnection, df bool) *NaClEncryptor {
buf := make([]byte, MaxUDPPacketSize)
prefixLen := copy(buf, prefix)
flags := uint16(0)
if df {
flags = flags | (1 << 15)
}
return &NaClEncryptor{
NonEncryptor: *NewNonEncryptor([]byte{}),
buf: buf,
offset: 0,
nonce: nil,
nonceChan: make(chan *[24]byte, ChannelSize),
flags: flags,
prefixLen: prefixLen,
conn: conn,
df: df}
}
func (ne *NaClEncryptor) Bytes() []byte {
plaintext := ne.NonEncryptor.Bytes()
offsetFlags := ne.offset | ne.flags
ciphertext := ne.buf
binary.BigEndian.PutUint16(ciphertext[ne.prefixLen:], offsetFlags)
nonce := ne.nonce
if nonce == nil {
freshNonce, encodedNonce, err := EncodeNonce(ne.df)
if err = ne.conn.CheckFatal(err); err != nil {
return []byte{}
}
ne.conn.SendTCP(encodedNonce)
ne.nonce = freshNonce
nonce = freshNonce
}
offset := ne.offset
SetNonceLow15Bits(nonce, offset)
// Seal *appends* to ciphertext
ciphertext = secretbox.Seal(ciphertext[:ne.prefixLen+2], plaintext, nonce, ne.conn.SessionKey)
offset = (offset + 1) & ((1 << 15) - 1)
if offset == 0 {
// need a new nonce please
ne.nonce = <-ne.nonceChan
} else if offset == 1<<14 { // half way through range, send new nonce
nonce, encodedNonce, err := EncodeNonce(ne.df)
if err = ne.conn.CheckFatal(err); err != nil {
return []byte{}
}
ne.nonceChan <- nonce
ne.conn.SendTCP(encodedNonce)
}
ne.offset = offset
return ciphertext
}
func (ne *NaClEncryptor) PacketOverhead() int {
return ne.prefixLen + 2 + secretbox.Overhead + ne.NonEncryptor.PacketOverhead()
}
func (ne *NaClEncryptor) TotalLen() int {
return ne.PacketOverhead() + ne.NonEncryptor.TotalLen()
}
// Frame Decryptors
func NewNonDecryptor(conn *LocalConnection) *NonDecryptor {
return &NonDecryptor{conn: conn}
}
func (nd *NonDecryptor) IterateFrames(fun FrameConsumer, packet *UDPPacket) error {
buf := packet.Packet
for len(buf) >= (2 + NameSize + NameSize) {
srcNameByte := buf[:NameSize]
buf = buf[NameSize:]
dstNameByte := buf[:NameSize]
buf = buf[NameSize:]
length := binary.BigEndian.Uint16(buf[:2])
frame := buf[2 : 2+length]
buf = buf[2+length:]
err := fun(nd.conn, packet.Sender, srcNameByte, dstNameByte, length, frame)
if err != nil {
return err
}
}
return nil
}
func (nd *NonDecryptor) Shutdown() {
}
func (nd *NonDecryptor) ReceiveNonce(msg []byte) {
log.Println("Received Nonce on non-encrypted channel. Ignoring.")
}
func NewNaClDecryptor(conn *LocalConnection) *NaClDecryptor {
inst := NaClDecryptorInstance{
nonce: nil,
previousNonce: nil,
usedOffsets: bit.New(),
previousUsedOffsets: nil,
highestOffsetSeen: 0,
nonceChan: make(chan *[24]byte, ChannelSize)}
instDF := NaClDecryptorInstance{
nonce: nil,
previousNonce: nil,
usedOffsets: bit.New(),
previousUsedOffsets: nil,
highestOffsetSeen: 0,
nonceChan: make(chan *[24]byte, ChannelSize)}
return &NaClDecryptor{
NonDecryptor: *NewNonDecryptor(conn),
instance: &inst,
instanceDF: &instDF}
}
func (nd *NaClDecryptor) Shutdown() {
close(nd.instance.nonceChan)
close(nd.instanceDF.nonceChan)
}
func (nd *NaClDecryptor) ReceiveNonce(msg []byte) {
df, nonce := DecodeNonce(msg)
if df {
nd.instanceDF.nonceChan <- nonce
} else {
nd.instance.nonceChan <- nonce
}
}
func (nd *NaClDecryptor) IterateFrames(fun FrameConsumer, packet *UDPPacket) error {
buf, err := nd.decrypt(packet.Packet)
if err != nil {
return err
}
packet.Packet = buf
return nd.NonDecryptor.IterateFrames(fun, packet)
}
func (nd *NaClDecryptor) decrypt(buf []byte) ([]byte, error) {
offset := binary.BigEndian.Uint16(buf[:2])
df := (offset & (1 << 15)) != 0
offsetNoFlags := offset & ((1 << 15) - 1)
var decState *NaClDecryptorInstance
if df {
decState = nd.instanceDF
} else {
decState = nd.instance
}
var nonce *[24]byte
var usedOffsets *bit.Set
var ok bool
if decState.nonce == nil {
if offsetNoFlags > (1 << 13) {
// offset is already beyond the first quarter and it's the
// first thing we've seen?! I don't think so.
return nil, fmt.Errorf("Unexpected offset when decrypting UDP packet")
}
decState.nonce, ok = <-decState.nonceChan
if !ok {
return nil, fmt.Errorf("Nonce chan closed")
}
nonce = decState.nonce
usedOffsets = decState.usedOffsets
decState.highestOffsetSeen = offsetNoFlags
} else {
highestOffsetSeen := decState.highestOffsetSeen
if offsetNoFlags < (1<<13) && highestOffsetSeen > ((1<<14)+(1<<13)) &&
(highestOffsetSeen-offsetNoFlags) > ((1<<14)+(1<<13)) {
// offset is in the first quarter, highestOffsetSeen is in
// the top quarter and under a quarter behind us. We
// interpret this as we need to move to the next nonce
decState.previousUsedOffsets = decState.usedOffsets
decState.usedOffsets = bit.New()
decState.previousNonce = decState.nonce
decState.nonce, ok = <-decState.nonceChan
if !ok {
return nil, fmt.Errorf("Nonce chan closed")
}
decState.highestOffsetSeen = offsetNoFlags
nonce = decState.nonce
usedOffsets = decState.usedOffsets
} else if offsetNoFlags > highestOffsetSeen &&
(offsetNoFlags-highestOffsetSeen) < (1<<13) {
// offset is under a quarter above highestOffsetSeen. This
// is ok - maybe some packet loss
decState.highestOffsetSeen = offsetNoFlags
nonce = decState.nonce
usedOffsets = decState.usedOffsets
} else if offsetNoFlags <= highestOffsetSeen &&
(highestOffsetSeen-offsetNoFlags) < (1<<13) {
// offset is within a quarter of the highest we've
// seen. This is ok - just assuming some out-of-order
// delivery.
nonce = decState.nonce
usedOffsets = decState.usedOffsets
} else if highestOffsetSeen < (1<<13) && offsetNoFlags > ((1<<14)+(1<<13)) &&
(offsetNoFlags-highestOffsetSeen) > ((1<<14)+(1<<13)) {
// offset is in the last quarter, highestOffsetSeen is in
// the first quarter, and offset is under a quarter behind
// us. This is ok - as above, just some out of order. But
// here it means we're dealing with the previous nonce
nonce = decState.previousNonce
usedOffsets = decState.previousUsedOffsets
} else {
return nil, fmt.Errorf("Unexpected offset when decrypting UDP packet")
}
}
offsetNoFlagsInt := int(offsetNoFlags)
if usedOffsets.Contains(offsetNoFlagsInt) {
return nil, fmt.Errorf("Suspected replay attack detected when decrypting UDP packet")
}
SetNonceLow15Bits(nonce, offsetNoFlags)
result, success := secretbox.Open(nil, buf[2:], nonce, nd.conn.SessionKey)
if success {
usedOffsets.Add(offsetNoFlagsInt)
return result, nil
} else {
return nil, fmt.Errorf("Unable to decrypt msg via UDP: %v", buf)
}
}
// TCP Senders
func NewSimpleTCPSender(encoder *gob.Encoder) *SimpleTCPSender {
return &SimpleTCPSender{encoder: encoder}
}
func (sender *SimpleTCPSender) Send(msg []byte) error {
return sender.encoder.Encode(msg)
}
func NewEncryptedTCPSender(encoder *gob.Encoder, conn *LocalConnection) *EncryptedTCPSender {
buffer := new(bytes.Buffer)
return &EncryptedTCPSender{
outerEncoder: encoder,
innerEncoder: gob.NewEncoder(buffer),
buffer: buffer,
conn: conn,
msgCount: 0}
}
func (sender *EncryptedTCPSender) Send(msg []byte) error {
nonce, err := GenerateRandomNonce()
if err != nil {
return err
}
sender.Lock()
defer sender.Unlock()
wrappedMsg := EncryptedTCPMessage{Number: sender.msgCount, Body: msg}
buffer := sender.buffer
buffer.Reset()
err = sender.innerEncoder.Encode(wrappedMsg)
if err != nil {
return err
}
sender.msgCount = sender.msgCount + 1
return sender.outerEncoder.Encode(
EncryptPrefixNonce(buffer.Bytes(), &nonce, sender.conn.SessionKey))
}
// TCP Receivers
func NewSimpleTCPReceiver() *SimpleTCPReceiver {
return &SimpleTCPReceiver{}
}
func (receiver *SimpleTCPReceiver) Decode(msg []byte) ([]byte, error) {
return msg, nil
}
func NewEncryptedTCPReceiver(conn *LocalConnection) *EncryptedTCPReceiver {
buffer := new(bytes.Buffer)
return &EncryptedTCPReceiver{
conn: conn,
decoder: gob.NewDecoder(buffer),
buffer: buffer,
msgCount: 0}
}
func (receiver *EncryptedTCPReceiver) Decode(msg []byte) ([]byte, error) {
plaintext, success := DecryptPrefixNonce(msg, receiver.conn.SessionKey)
if !success {
return msg, fmt.Errorf("Unable to decrypt msg via TCP:\n %X", msg)
}
receiver.buffer.Reset()
_, err := receiver.buffer.Write(plaintext)
if err != nil {
return msg, err
}
wrappedMsg := new(EncryptedTCPMessage)
err = receiver.decoder.Decode(wrappedMsg)
if err != nil {
return msg, err
}
if wrappedMsg.Number != receiver.msgCount {
return msg, fmt.Errorf("Received TCP message with wrong sequence number; possible replay attack")
}
receiver.msgCount = receiver.msgCount + 1
return wrappedMsg.Body, nil
}