Handshake negotiation functioning
This commit is contained in:
		
							parent
							
								
									a4cc0a30fa
								
							
						
					
					
						commit
						1e620427bd
					
				
					 10 changed files with 512 additions and 82 deletions
				
			
		|  | @ -18,4 +18,8 @@ const ( | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	QueueOutboundSize      = 1024 | 	QueueOutboundSize      = 1024 | ||||||
|  | 	QueueInboundSize       = 1024 | ||||||
|  | 	QueueHandshakeSize     = 1024 | ||||||
|  | 	QueueHandshakeBusySize = QueueHandshakeSize / 8 | ||||||
|  | 	MinMessageSize         = MessageTransportSize | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -23,7 +23,13 @@ type Device struct { | ||||||
| 	routingTable RoutingTable | 	routingTable RoutingTable | ||||||
| 	indices      IndexTable | 	indices      IndexTable | ||||||
| 	queue        struct { | 	queue        struct { | ||||||
| 		encryption chan *QueueOutboundElement // parallel work queue
 | 		encryption chan *QueueOutboundElement | ||||||
|  | 		decryption chan *QueueInboundElement | ||||||
|  | 		handshake  chan QueueHandshakeElement | ||||||
|  | 		inbound    chan []byte // inbound queue for TUN
 | ||||||
|  | 	} | ||||||
|  | 	signal struct { | ||||||
|  | 		stop chan struct{} | ||||||
| 	} | 	} | ||||||
| 	peers map[NoisePublicKey]*Peer | 	peers map[NoisePublicKey]*Peer | ||||||
| 	mac   MacStateDevice | 	mac   MacStateDevice | ||||||
|  | @ -56,6 +62,7 @@ func NewDevice(tun TUNDevice, logLevel int) *Device { | ||||||
| 	defer device.mutex.Unlock() | 	defer device.mutex.Unlock() | ||||||
| 
 | 
 | ||||||
| 	device.log = NewLogger(logLevel) | 	device.log = NewLogger(logLevel) | ||||||
|  | 	device.mtu = tun.MTU() | ||||||
| 	device.peers = make(map[NoisePublicKey]*Peer) | 	device.peers = make(map[NoisePublicKey]*Peer) | ||||||
| 	device.indices.Init() | 	device.indices.Init() | ||||||
| 	device.routingTable.Reset() | 	device.routingTable.Reset() | ||||||
|  | @ -71,13 +78,22 @@ func NewDevice(tun TUNDevice, logLevel int) *Device { | ||||||
| 	// create queues
 | 	// create queues
 | ||||||
| 
 | 
 | ||||||
| 	device.queue.encryption = make(chan *QueueOutboundElement, QueueOutboundSize) | 	device.queue.encryption = make(chan *QueueOutboundElement, QueueOutboundSize) | ||||||
|  | 	device.queue.handshake = make(chan QueueHandshakeElement, QueueHandshakeSize) | ||||||
|  | 	device.queue.decryption = make(chan *QueueInboundElement, QueueInboundSize) | ||||||
|  | 
 | ||||||
|  | 	// prepare signals
 | ||||||
|  | 
 | ||||||
|  | 	device.signal.stop = make(chan struct{}) | ||||||
| 
 | 
 | ||||||
| 	// start workers
 | 	// start workers
 | ||||||
| 
 | 
 | ||||||
| 	for i := 0; i < runtime.NumCPU(); i += 1 { | 	for i := 0; i < runtime.NumCPU(); i += 1 { | ||||||
| 		go device.RoutineEncryption() | 		go device.RoutineEncryption() | ||||||
|  | 		go device.RoutineDecryption() | ||||||
|  | 		go device.RoutineHandshake() | ||||||
| 	} | 	} | ||||||
| 	go device.RoutineReadFromTUN(tun) | 	go device.RoutineReadFromTUN(tun) | ||||||
|  | 	go device.RoutineReceiveIncomming() | ||||||
| 	return device | 	return device | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -115,5 +131,6 @@ func (device *Device) RemoveAllPeers() { | ||||||
| 
 | 
 | ||||||
| func (device *Device) Close() { | func (device *Device) Close() { | ||||||
| 	device.RemoveAllPeers() | 	device.RemoveAllPeers() | ||||||
|  | 	close(device.signal.stop) | ||||||
| 	close(device.queue.encryption) | 	close(device.queue.encryption) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ package main | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"encoding/binary" | 	"encoding/binary" | ||||||
| 	"net" |  | ||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  | @ -24,14 +23,6 @@ func (peer *Peer) SendKeepAlive() bool { | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func StoppedTimer() *time.Timer { |  | ||||||
| 	timer := time.NewTimer(time.Hour) |  | ||||||
| 	if !timer.Stop() { |  | ||||||
| 		<-timer.C |  | ||||||
| 	} |  | ||||||
| 	return timer |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* Called when a new authenticated message has been send | /* Called when a new authenticated message has been send | ||||||
|  * |  * | ||||||
|  * TODO: This might be done in a faster way |  * TODO: This might be done in a faster way | ||||||
|  | @ -71,7 +62,7 @@ func (peer *Peer) RoutineHandshakeInitiator() { | ||||||
| 	device := peer.device | 	device := peer.device | ||||||
| 	buffer := make([]byte, 1024) | 	buffer := make([]byte, 1024) | ||||||
| 	logger := device.log.Debug | 	logger := device.log.Debug | ||||||
| 	timeout := time.NewTimer(time.Hour) | 	timeout := stoppedTimer() | ||||||
| 
 | 
 | ||||||
| 	var work *QueueOutboundElement | 	var work *QueueOutboundElement | ||||||
| 
 | 
 | ||||||
|  | @ -129,13 +120,8 @@ func (peer *Peer) RoutineHandshakeInitiator() { | ||||||
| 
 | 
 | ||||||
| 				// set timeout
 | 				// set timeout
 | ||||||
| 
 | 
 | ||||||
| 				if !timeout.Stop() { |  | ||||||
| 					select { |  | ||||||
| 					case <-timeout.C: |  | ||||||
| 					default: |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				attempts += 1 | 				attempts += 1 | ||||||
|  | 				stopTimer(timeout) | ||||||
| 				timeout.Reset(RekeyTimeout) | 				timeout.Reset(RekeyTimeout) | ||||||
| 				device.log.Debug.Println("Handshake initiation attempt", attempts, "queued for peer", peer.id) | 				device.log.Debug.Println("Handshake initiation attempt", attempts, "queued for peer", peer.id) | ||||||
| 
 | 
 | ||||||
|  | @ -163,45 +149,3 @@ func (peer *Peer) RoutineHandshakeInitiator() { | ||||||
| 
 | 
 | ||||||
| 	logger.Println("Routine, handshake initator, stopped for peer", peer.id) | 	logger.Println("Routine, handshake initator, stopped for peer", peer.id) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| /* Handles incomming packets related to handshake |  | ||||||
|  * |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
| func (device *Device) HandshakeWorker(queue chan struct { |  | ||||||
| 	msg     []byte |  | ||||||
| 	msgType uint32 |  | ||||||
| 	addr    *net.UDPAddr |  | ||||||
| }) { |  | ||||||
| 	for { |  | ||||||
| 		elem := <-queue |  | ||||||
| 
 |  | ||||||
| 		switch elem.msgType { |  | ||||||
| 		case MessageInitiationType: |  | ||||||
| 			if len(elem.msg) != MessageInitiationSize { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// check for cookie
 |  | ||||||
| 
 |  | ||||||
| 			var msg MessageInitiation |  | ||||||
| 
 |  | ||||||
| 			binary.Read(nil, binary.LittleEndian, &msg) |  | ||||||
| 
 |  | ||||||
| 		case MessageResponseType: |  | ||||||
| 			if len(elem.msg) != MessageResponseSize { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// check for cookie
 |  | ||||||
| 
 |  | ||||||
| 		case MessageCookieReplyType: |  | ||||||
| 			if len(elem.msg) != MessageCookieReplySize { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		default: |  | ||||||
| 			device.log.Error.Println("Invalid message type in handshake queue") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										19
									
								
								src/misc.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/misc.go
									
									
									
									
									
								
							|  | @ -1,5 +1,9 @@ | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| func min(a uint, b uint) uint { | func min(a uint, b uint) uint { | ||||||
| 	if a > b { | 	if a > b { | ||||||
| 		return b | 		return b | ||||||
|  | @ -13,3 +17,18 @@ func sendSignal(c chan struct{}) { | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func stopTimer(timer *time.Timer) { | ||||||
|  | 	if !timer.Stop() { | ||||||
|  | 		select { | ||||||
|  | 		case <-timer.C: | ||||||
|  | 		default: | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func stoppedTimer() *time.Timer { | ||||||
|  | 	timer := time.NewTimer(time.Hour) | ||||||
|  | 	stopTimer(timer) | ||||||
|  | 	return timer | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import ( | ||||||
| 	"golang.org/x/crypto/chacha20poly1305" | 	"golang.org/x/crypto/chacha20poly1305" | ||||||
| 	"golang.org/x/crypto/poly1305" | 	"golang.org/x/crypto/poly1305" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | @ -34,6 +35,13 @@ const ( | ||||||
| 	MessageInitiationSize  = 148 | 	MessageInitiationSize  = 148 | ||||||
| 	MessageResponseSize    = 92 | 	MessageResponseSize    = 92 | ||||||
| 	MessageCookieReplySize = 64 | 	MessageCookieReplySize = 64 | ||||||
|  | 	MessageTransportSize   = 16 + poly1305.TagSize // size of empty transport
 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	MessageTransportOffsetReceiver = 4 | ||||||
|  | 	MessageTransportOffsetCounter  = 8 | ||||||
|  | 	MessageTransportOffsetContent  = 16 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| /* Type is an 8-bit field, followed by 3 nul bytes, | /* Type is an 8-bit field, followed by 3 nul bytes, | ||||||
|  | @ -55,7 +63,7 @@ type MessageInitiation struct { | ||||||
| type MessageResponse struct { | type MessageResponse struct { | ||||||
| 	Type      uint32 | 	Type      uint32 | ||||||
| 	Sender    uint32 | 	Sender    uint32 | ||||||
| 	Reciever  uint32 | 	Receiver  uint32 | ||||||
| 	Ephemeral NoisePublicKey | 	Ephemeral NoisePublicKey | ||||||
| 	Empty     [poly1305.TagSize]byte | 	Empty     [poly1305.TagSize]byte | ||||||
| 	Mac1      [blake2s.Size128]byte | 	Mac1      [blake2s.Size128]byte | ||||||
|  | @ -64,7 +72,7 @@ type MessageResponse struct { | ||||||
| 
 | 
 | ||||||
| type MessageTransport struct { | type MessageTransport struct { | ||||||
| 	Type     uint32 | 	Type     uint32 | ||||||
| 	Reciever uint32 | 	Receiver uint32 | ||||||
| 	Counter  uint64 | 	Counter  uint64 | ||||||
| 	Content  []byte | 	Content  []byte | ||||||
| } | } | ||||||
|  | @ -292,7 +300,7 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error | ||||||
| 	var msg MessageResponse | 	var msg MessageResponse | ||||||
| 	msg.Type = MessageResponseType | 	msg.Type = MessageResponseType | ||||||
| 	msg.Sender = handshake.localIndex | 	msg.Sender = handshake.localIndex | ||||||
| 	msg.Reciever = handshake.remoteIndex | 	msg.Receiver = handshake.remoteIndex | ||||||
| 
 | 
 | ||||||
| 	// create ephemeral key
 | 	// create ephemeral key
 | ||||||
| 
 | 
 | ||||||
|  | @ -302,6 +310,7 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error | ||||||
| 	} | 	} | ||||||
| 	msg.Ephemeral = handshake.localEphemeral.publicKey() | 	msg.Ephemeral = handshake.localEphemeral.publicKey() | ||||||
| 	handshake.mixHash(msg.Ephemeral[:]) | 	handshake.mixHash(msg.Ephemeral[:]) | ||||||
|  | 	handshake.mixKey(msg.Ephemeral[:]) | ||||||
| 
 | 
 | ||||||
| 	func() { | 	func() { | ||||||
| 		ss := handshake.localEphemeral.sharedSecret(handshake.remoteEphemeral) | 		ss := handshake.localEphemeral.sharedSecret(handshake.remoteEphemeral) | ||||||
|  | @ -334,7 +343,7 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer { | ||||||
| 
 | 
 | ||||||
| 	// lookup handshake by reciever
 | 	// lookup handshake by reciever
 | ||||||
| 
 | 
 | ||||||
| 	lookup := device.indices.Lookup(msg.Reciever) | 	lookup := device.indices.Lookup(msg.Receiver) | ||||||
| 	handshake := lookup.handshake | 	handshake := lookup.handshake | ||||||
| 	if handshake == nil { | 	if handshake == nil { | ||||||
| 		return nil | 		return nil | ||||||
|  | @ -359,7 +368,7 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer { | ||||||
| 		// finish 3-way DH
 | 		// finish 3-way DH
 | ||||||
| 
 | 
 | ||||||
| 		hash = mixHash(handshake.hash, msg.Ephemeral[:]) | 		hash = mixHash(handshake.hash, msg.Ephemeral[:]) | ||||||
| 		chainKey = handshake.chainKey | 		chainKey = mixKey(handshake.chainKey, msg.Ephemeral[:]) | ||||||
| 
 | 
 | ||||||
| 		func() { | 		func() { | ||||||
| 			ss := handshake.localEphemeral.sharedSecret(msg.Ephemeral) | 			ss := handshake.localEphemeral.sharedSecret(msg.Ephemeral) | ||||||
|  | @ -380,6 +389,7 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer { | ||||||
| 		aead, _ := chacha20poly1305.New(key[:]) | 		aead, _ := chacha20poly1305.New(key[:]) | ||||||
| 		_, err := aead.Open(nil, ZeroNonce[:], msg.Empty[:], hash[:]) | 		_, err := aead.Open(nil, ZeroNonce[:], msg.Empty[:], hash[:]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | 			device.log.Debug.Println("failed to open") | ||||||
| 			return false | 			return false | ||||||
| 		} | 		} | ||||||
| 		hash = mixHash(hash, msg.Empty[:]) | 		hash = mixHash(hash, msg.Empty[:]) | ||||||
|  | @ -438,6 +448,7 @@ func (peer *Peer) NewKeyPair() *KeyPair { | ||||||
| 	keyPair.recv, _ = chacha20poly1305.New(recvKey[:]) | 	keyPair.recv, _ = chacha20poly1305.New(recvKey[:]) | ||||||
| 	keyPair.sendNonce = 0 | 	keyPair.sendNonce = 0 | ||||||
| 	keyPair.recvNonce = 0 | 	keyPair.recvNonce = 0 | ||||||
|  | 	keyPair.created = time.Now() | ||||||
| 
 | 
 | ||||||
| 	// remap index
 | 	// remap index
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								src/peer.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/peer.go
									
									
									
									
									
								
							|  | @ -37,6 +37,7 @@ type Peer struct { | ||||||
| 	queue struct { | 	queue struct { | ||||||
| 		nonce    chan []byte                // nonce / pre-handshake queue
 | 		nonce    chan []byte                // nonce / pre-handshake queue
 | ||||||
| 		outbound chan *QueueOutboundElement // sequential ordering of work
 | 		outbound chan *QueueOutboundElement // sequential ordering of work
 | ||||||
|  | 		inbound  chan *QueueInboundElement  // sequential ordering of work
 | ||||||
| 	} | 	} | ||||||
| 	mac MacStatePeer | 	mac MacStatePeer | ||||||
| } | } | ||||||
|  | @ -47,11 +48,10 @@ func (device *Device) NewPeer(pk NoisePublicKey) *Peer { | ||||||
| 	peer := new(Peer) | 	peer := new(Peer) | ||||||
| 	peer.mutex.Lock() | 	peer.mutex.Lock() | ||||||
| 	defer peer.mutex.Unlock() | 	defer peer.mutex.Unlock() | ||||||
| 	peer.device = device | 
 | ||||||
| 	peer.mac.Init(pk) | 	peer.mac.Init(pk) | ||||||
| 	peer.queue.outbound = make(chan *QueueOutboundElement, QueueOutboundSize) | 	peer.device = device | ||||||
| 	peer.queue.nonce = make(chan []byte, QueueOutboundSize) | 	peer.timer.sendKeepalive = stoppedTimer() | ||||||
| 	peer.timer.sendKeepalive = StoppedTimer() |  | ||||||
| 
 | 
 | ||||||
| 	// assign id for debugging
 | 	// assign id for debugging
 | ||||||
| 
 | 
 | ||||||
|  | @ -76,6 +76,12 @@ func (device *Device) NewPeer(pk NoisePublicKey) *Peer { | ||||||
| 	handshake.precomputedStaticStatic = device.privateKey.sharedSecret(handshake.remoteStatic) | 	handshake.precomputedStaticStatic = device.privateKey.sharedSecret(handshake.remoteStatic) | ||||||
| 	handshake.mutex.Unlock() | 	handshake.mutex.Unlock() | ||||||
| 
 | 
 | ||||||
|  | 	// prepare queuing
 | ||||||
|  | 
 | ||||||
|  | 	peer.queue.nonce = make(chan []byte, QueueOutboundSize) | ||||||
|  | 	peer.queue.inbound = make(chan *QueueInboundElement, QueueInboundSize) | ||||||
|  | 	peer.queue.outbound = make(chan *QueueOutboundElement, QueueOutboundSize) | ||||||
|  | 
 | ||||||
| 	// prepare signaling
 | 	// prepare signaling
 | ||||||
| 
 | 
 | ||||||
| 	peer.signal.stop = make(chan struct{}) | 	peer.signal.stop = make(chan struct{}) | ||||||
|  | @ -89,6 +95,7 @@ func (device *Device) NewPeer(pk NoisePublicKey) *Peer { | ||||||
| 	go peer.RoutineNonce() | 	go peer.RoutineNonce() | ||||||
| 	go peer.RoutineHandshakeInitiator() | 	go peer.RoutineHandshakeInitiator() | ||||||
| 	go peer.RoutineSequentialSender() | 	go peer.RoutineSequentialSender() | ||||||
|  | 	go peer.RoutineSequentialReceiver() | ||||||
| 
 | 
 | ||||||
| 	return peer | 	return peer | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										404
									
								
								src/receive.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								src/receive.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,404 @@ | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/binary" | ||||||
|  | 	"golang.org/x/crypto/chacha20poly1305" | ||||||
|  | 	"net" | ||||||
|  | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	ElementStateOkay = iota | ||||||
|  | 	ElementStateDropped | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type QueueHandshakeElement struct { | ||||||
|  | 	msgType uint32 | ||||||
|  | 	packet  []byte | ||||||
|  | 	source  *net.UDPAddr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type QueueInboundElement struct { | ||||||
|  | 	state   uint32 | ||||||
|  | 	mutex   sync.Mutex | ||||||
|  | 	packet  []byte | ||||||
|  | 	counter uint64 | ||||||
|  | 	keyPair *KeyPair | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (elem *QueueInboundElement) Drop() { | ||||||
|  | 	atomic.StoreUint32(&elem.state, ElementStateDropped) | ||||||
|  | 	elem.mutex.Unlock() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (device *Device) RoutineReceiveIncomming() { | ||||||
|  | 	var packet []byte | ||||||
|  | 
 | ||||||
|  | 	debugLog := device.log.Debug | ||||||
|  | 	debugLog.Println("Routine, receive incomming, started") | ||||||
|  | 
 | ||||||
|  | 	errorLog := device.log.Error | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 
 | ||||||
|  | 		// check if stopped
 | ||||||
|  | 
 | ||||||
|  | 		select { | ||||||
|  | 		case <-device.signal.stop: | ||||||
|  | 			return | ||||||
|  | 		default: | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// read next datagram
 | ||||||
|  | 
 | ||||||
|  | 		if packet == nil { | ||||||
|  | 			packet = make([]byte, 1<<16) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		device.net.mutex.RLock() | ||||||
|  | 		conn := device.net.conn | ||||||
|  | 		device.net.mutex.RUnlock() | ||||||
|  | 
 | ||||||
|  | 		conn.SetReadDeadline(time.Now().Add(time.Second)) | ||||||
|  | 
 | ||||||
|  | 		size, raddr, err := conn.ReadFromUDP(packet) | ||||||
|  | 		if err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if size < MinMessageSize { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// handle packet
 | ||||||
|  | 
 | ||||||
|  | 		packet = packet[:size] | ||||||
|  | 		msgType := binary.LittleEndian.Uint32(packet[:4]) | ||||||
|  | 
 | ||||||
|  | 		func() { | ||||||
|  | 			switch msgType { | ||||||
|  | 
 | ||||||
|  | 			case MessageInitiationType, MessageResponseType: | ||||||
|  | 
 | ||||||
|  | 				// verify mac1
 | ||||||
|  | 
 | ||||||
|  | 				if !device.mac.CheckMAC1(packet) { | ||||||
|  | 					debugLog.Println("Received packet with invalid mac1") | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// check if busy, TODO: refine definition of "busy"
 | ||||||
|  | 
 | ||||||
|  | 				busy := len(device.queue.handshake) > QueueHandshakeBusySize | ||||||
|  | 				if busy && !device.mac.CheckMAC2(packet, raddr) { | ||||||
|  | 					sender := binary.LittleEndian.Uint32(packet[4:8]) // "sender" follows "type"
 | ||||||
|  | 					reply, err := device.CreateMessageCookieReply(packet, sender, raddr) | ||||||
|  | 					if err != nil { | ||||||
|  | 						errorLog.Println("Failed to create cookie reply:", err) | ||||||
|  | 						return | ||||||
|  | 					} | ||||||
|  | 					writer := bytes.NewBuffer(packet[:0]) | ||||||
|  | 					binary.Write(writer, binary.LittleEndian, reply) | ||||||
|  | 					packet = writer.Bytes() | ||||||
|  | 					_, err = device.net.conn.WriteToUDP(packet, raddr) | ||||||
|  | 					if err != nil { | ||||||
|  | 						debugLog.Println("Failed to send cookie reply:", err) | ||||||
|  | 					} | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// add to handshake queue
 | ||||||
|  | 
 | ||||||
|  | 				device.queue.handshake <- QueueHandshakeElement{ | ||||||
|  | 					msgType: msgType, | ||||||
|  | 					packet:  packet, | ||||||
|  | 					source:  raddr, | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 			case MessageCookieReplyType: | ||||||
|  | 
 | ||||||
|  | 				// verify and update peer cookie state
 | ||||||
|  | 
 | ||||||
|  | 				if len(packet) != MessageCookieReplySize { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				var reply MessageCookieReply | ||||||
|  | 				reader := bytes.NewReader(packet) | ||||||
|  | 				err := binary.Read(reader, binary.LittleEndian, &reply) | ||||||
|  | 				if err != nil { | ||||||
|  | 					debugLog.Println("Failed to decode cookie reply") | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				device.ConsumeMessageCookieReply(&reply) | ||||||
|  | 
 | ||||||
|  | 			case MessageTransportType: | ||||||
|  | 
 | ||||||
|  | 				debugLog.Println("DEBUG: Got transport") | ||||||
|  | 
 | ||||||
|  | 				// lookup key pair
 | ||||||
|  | 
 | ||||||
|  | 				if len(packet) < MessageTransportSize { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				receiver := binary.LittleEndian.Uint32( | ||||||
|  | 					packet[MessageTransportOffsetReceiver:MessageTransportOffsetCounter], | ||||||
|  | 				) | ||||||
|  | 				value := device.indices.Lookup(receiver) | ||||||
|  | 				keyPair := value.keyPair | ||||||
|  | 				if keyPair == nil { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// check key-pair expiry
 | ||||||
|  | 
 | ||||||
|  | 				if keyPair.created.Add(RejectAfterTime).Before(time.Now()) { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// add to peer queue
 | ||||||
|  | 
 | ||||||
|  | 				peer := value.peer | ||||||
|  | 				work := new(QueueInboundElement) | ||||||
|  | 				work.packet = packet | ||||||
|  | 				work.keyPair = keyPair | ||||||
|  | 				work.state = ElementStateOkay | ||||||
|  | 				work.mutex.Lock() | ||||||
|  | 
 | ||||||
|  | 				// add to parallel decryption queue
 | ||||||
|  | 
 | ||||||
|  | 				func() { | ||||||
|  | 					for { | ||||||
|  | 						select { | ||||||
|  | 						case device.queue.decryption <- work: | ||||||
|  | 							return | ||||||
|  | 						default: | ||||||
|  | 							select { | ||||||
|  | 							case elem := <-device.queue.decryption: | ||||||
|  | 								elem.Drop() | ||||||
|  | 							default: | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				// add to sequential inbound queue
 | ||||||
|  | 
 | ||||||
|  | 				func() { | ||||||
|  | 					for { | ||||||
|  | 						select { | ||||||
|  | 						case peer.queue.inbound <- work: | ||||||
|  | 							break | ||||||
|  | 						default: | ||||||
|  | 							select { | ||||||
|  | 							case elem := <-peer.queue.inbound: | ||||||
|  | 								elem.Drop() | ||||||
|  | 							default: | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 			default: | ||||||
|  | 				// unknown message type
 | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (device *Device) RoutineDecryption() { | ||||||
|  | 	var elem *QueueInboundElement | ||||||
|  | 	var nonce [chacha20poly1305.NonceSize]byte | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case elem = <-device.queue.decryption: | ||||||
|  | 		case <-device.signal.stop: | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// check if dropped
 | ||||||
|  | 
 | ||||||
|  | 		state := atomic.LoadUint32(&elem.state) | ||||||
|  | 		if state != ElementStateOkay { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// split message into fields
 | ||||||
|  | 
 | ||||||
|  | 		counter := binary.LittleEndian.Uint64( | ||||||
|  | 			elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent], | ||||||
|  | 		) | ||||||
|  | 		content := elem.packet[MessageTransportOffsetContent:] | ||||||
|  | 
 | ||||||
|  | 		// decrypt with key-pair
 | ||||||
|  | 
 | ||||||
|  | 		var err error | ||||||
|  | 		binary.LittleEndian.PutUint64(nonce[4:], counter) | ||||||
|  | 		elem.packet, err = elem.keyPair.recv.Open(elem.packet[:0], nonce[:], content, nil) | ||||||
|  | 		if err != nil { | ||||||
|  | 			elem.Drop() | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// release to consumer
 | ||||||
|  | 
 | ||||||
|  | 		elem.counter = counter | ||||||
|  | 		elem.mutex.Unlock() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Handles incomming packets related to handshake | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | func (device *Device) RoutineHandshake() { | ||||||
|  | 
 | ||||||
|  | 	logInfo := device.log.Info | ||||||
|  | 	logError := device.log.Error | ||||||
|  | 	logDebug := device.log.Debug | ||||||
|  | 
 | ||||||
|  | 	var elem QueueHandshakeElement | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case elem = <-device.queue.handshake: | ||||||
|  | 		case <-device.signal.stop: | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		func() { | ||||||
|  | 
 | ||||||
|  | 			switch elem.msgType { | ||||||
|  | 			case MessageInitiationType: | ||||||
|  | 
 | ||||||
|  | 				// unmarshal
 | ||||||
|  | 
 | ||||||
|  | 				if len(elem.packet) != MessageInitiationSize { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				var msg MessageInitiation | ||||||
|  | 				reader := bytes.NewReader(elem.packet) | ||||||
|  | 				err := binary.Read(reader, binary.LittleEndian, &msg) | ||||||
|  | 				if err != nil { | ||||||
|  | 					logError.Println("Failed to decode initiation message") | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// consume initiation
 | ||||||
|  | 
 | ||||||
|  | 				peer := device.ConsumeMessageInitiation(&msg) | ||||||
|  | 				if peer == nil { | ||||||
|  | 					logInfo.Println( | ||||||
|  | 						"Recieved invalid initiation message from", | ||||||
|  | 						elem.source.IP.String(), | ||||||
|  | 						elem.source.Port, | ||||||
|  | 					) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				logDebug.Println("Recieved valid initiation message for peer", peer.id) | ||||||
|  | 
 | ||||||
|  | 			case MessageResponseType: | ||||||
|  | 
 | ||||||
|  | 				// unmarshal
 | ||||||
|  | 
 | ||||||
|  | 				if len(elem.packet) != MessageResponseSize { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				var msg MessageResponse | ||||||
|  | 				reader := bytes.NewReader(elem.packet) | ||||||
|  | 				err := binary.Read(reader, binary.LittleEndian, &msg) | ||||||
|  | 				if err != nil { | ||||||
|  | 					logError.Println("Failed to decode response message") | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// consume response
 | ||||||
|  | 
 | ||||||
|  | 				peer := device.ConsumeMessageResponse(&msg) | ||||||
|  | 				if peer == nil { | ||||||
|  | 					logInfo.Println( | ||||||
|  | 						"Recieved invalid response message from", | ||||||
|  | 						elem.source.IP.String(), | ||||||
|  | 						elem.source.Port, | ||||||
|  | 					) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				sendSignal(peer.signal.handshakeCompleted) | ||||||
|  | 				logDebug.Println("Recieved valid response message for peer", peer.id) | ||||||
|  | 				peer.NewKeyPair() | ||||||
|  | 				peer.SendKeepAlive() | ||||||
|  | 
 | ||||||
|  | 			default: | ||||||
|  | 				device.log.Error.Println("Invalid message type in handshake queue") | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (peer *Peer) RoutineSequentialReceiver() { | ||||||
|  | 	var elem *QueueInboundElement | ||||||
|  | 
 | ||||||
|  | 	device := peer.device | ||||||
|  | 	logDebug := device.log.Debug | ||||||
|  | 
 | ||||||
|  | 	logDebug.Println("Routine, sequential receiver, started for peer", peer.id) | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		// wait for decryption
 | ||||||
|  | 
 | ||||||
|  | 		select { | ||||||
|  | 		case <-peer.signal.stop: | ||||||
|  | 			return | ||||||
|  | 		case elem = <-peer.queue.inbound: | ||||||
|  | 		} | ||||||
|  | 		elem.mutex.Lock() | ||||||
|  | 
 | ||||||
|  | 		// check if dropped
 | ||||||
|  | 
 | ||||||
|  | 		logDebug.Println("MESSSAGE:", elem) | ||||||
|  | 
 | ||||||
|  | 		state := atomic.LoadUint32(&elem.state) | ||||||
|  | 		if state != ElementStateOkay { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// check for replay
 | ||||||
|  | 
 | ||||||
|  | 		// check for keep-alive
 | ||||||
|  | 
 | ||||||
|  | 		if len(elem.packet) == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// insert into inbound TUN queue
 | ||||||
|  | 
 | ||||||
|  | 		device.queue.inbound <- elem.packet | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (device *Device) RoutineWriteToTUN(tun TUNDevice) { | ||||||
|  | 	for { | ||||||
|  | 		var packet []byte | ||||||
|  | 
 | ||||||
|  | 		select { | ||||||
|  | 		case <-device.signal.stop: | ||||||
|  | 		case packet = <-device.queue.inbound: | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		device.log.Debug.Println("GOT:", packet) | ||||||
|  | 
 | ||||||
|  | 		size, err := tun.Write(packet) | ||||||
|  | 		device.log.Debug.Println("DEBUG:", size, err) | ||||||
|  | 		if err != nil { | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								src/send.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/send.go
									
									
									
									
									
								
							|  | @ -27,6 +27,7 @@ import ( | ||||||
|  * workers release lock when they have completed work on the packet. |  * workers release lock when they have completed work on the packet. | ||||||
|  */ |  */ | ||||||
| type QueueOutboundElement struct { | type QueueOutboundElement struct { | ||||||
|  | 	state   uint32 | ||||||
| 	mutex   sync.Mutex | 	mutex   sync.Mutex | ||||||
| 	packet  []byte | 	packet  []byte | ||||||
| 	nonce   uint64 | 	nonce   uint64 | ||||||
|  | @ -59,6 +60,14 @@ func (peer *Peer) InsertOutbound(elem *QueueOutboundElement) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (elem *QueueOutboundElement) Drop() { | ||||||
|  | 	atomic.StoreUint32(&elem.state, ElementStateDropped) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (elem *QueueOutboundElement) IsDropped() bool { | ||||||
|  | 	return atomic.LoadUint32(&elem.state) == ElementStateDropped | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Reads packets from the TUN and inserts | /* Reads packets from the TUN and inserts | ||||||
|  * into nonce queue for peer |  * into nonce queue for peer | ||||||
|  * |  * | ||||||
|  | @ -162,6 +171,8 @@ func (peer *Peer) RoutineNonce() { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			logger.Println("PACKET:", packet) | ||||||
|  | 
 | ||||||
| 			// wait for key pair
 | 			// wait for key pair
 | ||||||
| 
 | 
 | ||||||
| 			for { | 			for { | ||||||
|  | @ -176,6 +187,7 @@ func (peer *Peer) RoutineNonce() { | ||||||
| 						break | 						break | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | 				logger.Println("Key pair:", keyPair) | ||||||
| 
 | 
 | ||||||
| 				sendSignal(peer.signal.handshakeBegin) | 				sendSignal(peer.signal.handshakeBegin) | ||||||
| 				logger.Println("Waiting for key-pair, peer", peer.id) | 				logger.Println("Waiting for key-pair, peer", peer.id) | ||||||
|  | @ -205,10 +217,12 @@ func (peer *Peer) RoutineNonce() { | ||||||
| 				work := new(QueueOutboundElement) // TODO: profile, maybe use pool
 | 				work := new(QueueOutboundElement) // TODO: profile, maybe use pool
 | ||||||
| 				work.keyPair = keyPair | 				work.keyPair = keyPair | ||||||
| 				work.packet = packet | 				work.packet = packet | ||||||
| 				work.nonce = atomic.AddUint64(&keyPair.sendNonce, 1) | 				work.nonce = atomic.AddUint64(&keyPair.sendNonce, 1) - 1 | ||||||
| 				work.peer = peer | 				work.peer = peer | ||||||
| 				work.mutex.Lock() | 				work.mutex.Lock() | ||||||
| 
 | 
 | ||||||
|  | 				logger.Println("WORK:", work) | ||||||
|  | 
 | ||||||
| 				packet = nil | 				packet = nil | ||||||
| 
 | 
 | ||||||
| 				// drop packets until there is space
 | 				// drop packets until there is space
 | ||||||
|  | @ -219,9 +233,11 @@ func (peer *Peer) RoutineNonce() { | ||||||
| 						case peer.device.queue.encryption <- work: | 						case peer.device.queue.encryption <- work: | ||||||
| 							return | 							return | ||||||
| 						default: | 						default: | ||||||
| 							drop := <-peer.device.queue.encryption | 							select { | ||||||
| 							drop.packet = nil | 							case elem := <-peer.device.queue.encryption: | ||||||
| 							drop.mutex.Unlock() | 								elem.Drop() | ||||||
|  | 							default: | ||||||
|  | 							} | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				}() | 				}() | ||||||
|  | @ -241,18 +257,22 @@ func (peer *Peer) RoutineNonce() { | ||||||
| func (device *Device) RoutineEncryption() { | func (device *Device) RoutineEncryption() { | ||||||
| 	var nonce [chacha20poly1305.NonceSize]byte | 	var nonce [chacha20poly1305.NonceSize]byte | ||||||
| 	for work := range device.queue.encryption { | 	for work := range device.queue.encryption { | ||||||
|  | 		if work.IsDropped() { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// pad packet
 | 		// pad packet
 | ||||||
| 
 | 
 | ||||||
| 		padding := device.mtu - len(work.packet) | 		padding := device.mtu - len(work.packet) | ||||||
| 		if padding < 0 { | 		if padding < 0 { | ||||||
| 			// drop
 | 			work.Drop() | ||||||
| 			work.packet = nil | 			continue | ||||||
| 			work.mutex.Unlock() |  | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
| 		for n := 0; n < padding; n += 1 { | 		for n := 0; n < padding; n += 1 { | ||||||
| 			work.packet = append(work.packet, 0) | 			work.packet = append(work.packet, 0) | ||||||
| 		} | 		} | ||||||
|  | 		device.log.Debug.Println(work.packet) | ||||||
| 
 | 
 | ||||||
| 		// encrypt
 | 		// encrypt
 | ||||||
| 
 | 
 | ||||||
|  | @ -288,6 +308,9 @@ func (peer *Peer) RoutineSequentialSender() { | ||||||
| 			logger.Println("Routine, sequential sender, stopped for peer", peer.id) | 			logger.Println("Routine, sequential sender, stopped for peer", peer.id) | ||||||
| 			return | 			return | ||||||
| 		case work := <-peer.queue.outbound: | 		case work := <-peer.queue.outbound: | ||||||
|  | 			if work.IsDropped() { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
| 			work.mutex.Lock() | 			work.mutex.Lock() | ||||||
| 			func() { | 			func() { | ||||||
| 				if work.packet == nil { | 				if work.packet == nil { | ||||||
|  | @ -310,10 +333,12 @@ func (peer *Peer) RoutineSequentialSender() { | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				logger.Println("Sending packet for peer", peer.id, work.packet) | 				logger.Println(work.packet) | ||||||
| 
 | 
 | ||||||
| 				_, err := device.net.conn.WriteToUDP(work.packet, peer.endpoint) | 				_, err := device.net.conn.WriteToUDP(work.packet, peer.endpoint) | ||||||
| 				logger.Println("SEND:", peer.endpoint, err) | 				if err != nil { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
| 				atomic.AddUint64(&peer.tx_bytes, uint64(len(work.packet))) | 				atomic.AddUint64(&peer.tx_bytes, uint64(len(work.packet))) | ||||||
| 
 | 
 | ||||||
| 				// shift keep-alive timer
 | 				// shift keep-alive timer
 | ||||||
|  | @ -323,7 +348,6 @@ func (peer *Peer) RoutineSequentialSender() { | ||||||
| 					peer.timer.sendKeepalive.Reset(interval) | 					peer.timer.sendKeepalive.Reset(interval) | ||||||
| 				} | 				} | ||||||
| 			}() | 			}() | ||||||
| 			work.mutex.Unlock() |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,5 +4,5 @@ type TUNDevice interface { | ||||||
| 	Read([]byte) (int, error) | 	Read([]byte) (int, error) | ||||||
| 	Write([]byte) (int, error) | 	Write([]byte) (int, error) | ||||||
| 	Name() string | 	Name() string | ||||||
| 	MTU() uint | 	MTU() int | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,14 +24,14 @@ const ( | ||||||
| type NativeTun struct { | type NativeTun struct { | ||||||
| 	fd   *os.File | 	fd   *os.File | ||||||
| 	name string | 	name string | ||||||
| 	mtu  uint | 	mtu  int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (tun *NativeTun) Name() string { | func (tun *NativeTun) Name() string { | ||||||
| 	return tun.name | 	return tun.name | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (tun *NativeTun) MTU() uint { | func (tun *NativeTun) MTU() int { | ||||||
| 	return tun.mtu | 	return tun.mtu | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue