-package main\r
-\r
-import (\r
- "bytes"\r
- "encoding/binary"\r
- "math/rand"\r
- "sync/atomic"\r
- "time"\r
-)\r
-\r
-/* NOTE:\r
- * Notion of validity\r
- *\r
- *\r
- */\r
-\r
-/* Called when a new authenticated message has been send\r
- *\r
- */\r
-func (peer *Peer) KeepKeyFreshSending() {\r
- kp := peer.keyPairs.Current()\r
- if kp == nil {\r
- return\r
- }\r
- nonce := atomic.LoadUint64(&kp.sendNonce)\r
- if nonce > RekeyAfterMessages {\r
- peer.signal.handshakeBegin.Send()\r
- }\r
- if kp.isInitiator && time.Now().Sub(kp.created) > RekeyAfterTime {\r
- peer.signal.handshakeBegin.Send()\r
- }\r
-}\r
-\r
-/* Called when a new authenticated message has been received\r
- *\r
- * NOTE: Not thread safe, but called by sequential receiver!\r
- */\r
-func (peer *Peer) KeepKeyFreshReceiving() {\r
- if peer.timer.sendLastMinuteHandshake {\r
- return\r
- }\r
- kp := peer.keyPairs.Current()\r
- if kp == nil {\r
- return\r
- }\r
- if !kp.isInitiator {\r
- return\r
- }\r
- nonce := atomic.LoadUint64(&kp.sendNonce)\r
- send := nonce > RekeyAfterMessages || time.Now().Sub(kp.created) > RekeyAfterTimeReceiving\r
- if send {\r
- // do a last minute attempt at initiating a new handshake\r
- peer.timer.sendLastMinuteHandshake = true\r
- peer.signal.handshakeBegin.Send()\r
- }\r
-}\r
-\r
-/* Queues a keep-alive if no packets are queued for peer\r
- */\r
-func (peer *Peer) SendKeepAlive() bool {\r
- if len(peer.queue.nonce) != 0 {\r
- return false\r
- }\r
- elem := peer.device.NewOutboundElement()\r
- elem.packet = nil\r
- select {\r
- case peer.queue.nonce <- elem:\r
- return true\r
- default:\r
- return false\r
- }\r
-}\r
-\r
-/* Event:\r
- * Sent non-empty (authenticated) transport message\r
- */\r
-func (peer *Peer) TimerDataSent() {\r
- peer.timer.keepalivePassive.Stop()\r
- peer.timer.handshakeNew.Start(NewHandshakeTime)\r
-}\r
-\r
-/* Event:\r
- * Received non-empty (authenticated) transport message\r
- *\r
- * Action:\r
- * Set a timer to confirm the message using a keep-alive (if not already set)\r
- */\r
-func (peer *Peer) TimerDataReceived() {\r
- if !peer.timer.keepalivePassive.Start(KeepaliveTimeout) {\r
- peer.timer.needAnotherKeepalive = true\r
- }\r
-}\r
-\r
-/* Event:\r
- * Any (authenticated) packet received\r
- */\r
-func (peer *Peer) TimerAnyAuthenticatedPacketReceived() {\r
- peer.timer.handshakeNew.Stop()\r
-}\r
-\r
-/* Event:\r
- * Any authenticated packet send / received.\r
- *\r
- * Action:\r
- * Push persistent keep-alive into the future\r
- */\r
-func (peer *Peer) TimerAnyAuthenticatedPacketTraversal() {\r
- interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)\r
- if interval > 0 {\r
- duration := time.Duration(interval) * time.Second\r
- peer.timer.keepalivePersistent.Reset(duration)\r
- }\r
-}\r
-\r
-/* Called after successfully completing a handshake.\r
- * i.e. after:\r
- *\r
- * - Valid handshake response\r
- * - First transport message under the "next" key\r
- */\r
-func (peer *Peer) TimerHandshakeComplete() {\r
- peer.signal.handshakeCompleted.Send()\r
- peer.device.log.Info.Println("Negotiated new handshake for", peer.String())\r
-}\r
-\r
-/* Event:\r
- * An ephemeral key is generated\r
- *\r
- * i.e. after:\r
- *\r
- * CreateMessageInitiation\r
- * CreateMessageResponse\r
- *\r
- * Action:\r
- * Schedule the deletion of all key material\r
- * upon failure to complete a handshake\r
- */\r
-func (peer *Peer) TimerEphemeralKeyCreated() {\r
- peer.timer.zeroAllKeys.Reset(RejectAfterTime * 3)\r
-}\r
-\r
-/* Sends a new handshake initiation message to the peer (endpoint)\r
- */\r
-func (peer *Peer) sendNewHandshake() error {\r
-\r
- // temporarily disable the handshake complete signal\r
-\r
- peer.signal.handshakeCompleted.Disable()\r
-\r
- // create initiation message\r
-\r
- msg, err := peer.device.CreateMessageInitiation(peer)\r
- if err != nil {\r
- return err\r
- }\r
-\r
- // marshal handshake message\r
-\r
- var buff [MessageInitiationSize]byte\r
- writer := bytes.NewBuffer(buff[:0])\r
- binary.Write(writer, binary.LittleEndian, msg)\r
- packet := writer.Bytes()\r
- peer.mac.AddMacs(packet)\r
-\r
- // send to endpoint\r
-\r
- peer.TimerAnyAuthenticatedPacketTraversal()\r
-\r
- err = peer.SendBuffer(packet)\r
- if err == nil {\r
- peer.signal.handshakeCompleted.Enable()\r
- }\r
-\r
- // set timeout\r
-\r
- jitter := time.Millisecond * time.Duration(rand.Uint32()%334)\r
-\r
- peer.timer.keepalivePassive.Stop()\r
- peer.timer.handshakeTimeout.Reset(RekeyTimeout + jitter)\r
-\r
- return err\r
-}\r
-\r
-func (peer *Peer) RoutineTimerHandler() {\r
-\r
- defer peer.routines.stopping.Done()\r
-\r
- device := peer.device\r
-\r
- logInfo := device.log.Info\r
- logDebug := device.log.Debug\r
- logDebug.Println("Routine, timer handler, started for peer", peer.String())\r
-\r
- // reset all timers\r
-\r
- peer.timer.keepalivePassive.Stop()\r
- peer.timer.handshakeDeadline.Stop()\r
- peer.timer.handshakeTimeout.Stop()\r
- peer.timer.handshakeNew.Stop()\r
- peer.timer.zeroAllKeys.Stop()\r
-\r
- interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)\r
- if interval > 0 {\r
- duration := time.Duration(interval) * time.Second\r
- peer.timer.keepalivePersistent.Reset(duration)\r
- }\r
-\r
- // signal synchronised setup complete\r
-\r
- peer.routines.starting.Done()\r
-\r
- // handle timer events\r
-\r
- for {\r
- select {\r
-\r
- /* stopping */\r
-\r
- case <-peer.routines.stop.Wait():\r
- return\r
-\r
- /* timers */\r
-\r
- // keep-alive\r
-\r
- case <-peer.timer.keepalivePersistent.Wait():\r
-\r
- interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)\r
- if interval > 0 {\r
- logDebug.Println(peer.String(), ": Send keep-alive (persistent)")\r
- peer.timer.keepalivePassive.Stop()\r
- peer.SendKeepAlive()\r
- }\r
-\r
- case <-peer.timer.keepalivePassive.Wait():\r
-\r
- logDebug.Println(peer.String(), ": Send keep-alive (passive)")\r
-\r
- peer.SendKeepAlive()\r
-\r
- if peer.timer.needAnotherKeepalive {\r
- peer.timer.needAnotherKeepalive = false\r
- peer.timer.keepalivePassive.Reset(KeepaliveTimeout)\r
- }\r
-\r
- // clear key material timer\r
-\r
- case <-peer.timer.zeroAllKeys.Wait():\r
-\r
- logDebug.Println(peer.String(), ": Clear all key-material (timer event)")\r
-\r
- hs := &peer.handshake\r
- hs.mutex.Lock()\r
-\r
- kp := &peer.keyPairs\r
- kp.mutex.Lock()\r
-\r
- // remove key-pairs\r
-\r
- if kp.previous != nil {\r
- device.DeleteKeyPair(kp.previous)\r
- kp.previous = nil\r
- }\r
- if kp.current != nil {\r
- device.DeleteKeyPair(kp.current)\r
- kp.current = nil\r
- }\r
- if kp.next != nil {\r
- device.DeleteKeyPair(kp.next)\r
- kp.next = nil\r
- }\r
- kp.mutex.Unlock()\r
-\r
- // zero out handshake\r
-\r
- device.indices.Delete(hs.localIndex)\r
- hs.Clear()\r
- hs.mutex.Unlock()\r
-\r
- // handshake timers\r
-\r
- case <-peer.timer.handshakeNew.Wait():\r
- logInfo.Println(peer.String(), ": Retrying handshake (timer event)")\r
- peer.signal.handshakeBegin.Send()\r
-\r
- case <-peer.timer.handshakeTimeout.Wait():\r
-\r
- // clear source (in case this is causing problems)\r
-\r
- peer.mutex.Lock()\r
- if peer.endpoint != nil {\r
- peer.endpoint.ClearSrc()\r
- }\r
- peer.mutex.Unlock()\r
-\r
- // send new handshake\r
-\r
- err := peer.sendNewHandshake()\r
-\r
- if err != nil {\r
- logInfo.Println(peer.String(), ": Failed to send handshake initiation", err)\r
- } else {\r
- logDebug.Println(peer.String(), ": Send handshake initiation (subsequent)")\r
- }\r
-\r
- case <-peer.timer.handshakeDeadline.Wait():\r
-\r
- // clear all queued packets and stop keep-alive\r
-\r
- logInfo.Println(peer.String(), ": Handshake negotiation timed-out")\r
-\r
- peer.signal.flushNonceQueue.Send()\r
- peer.timer.keepalivePersistent.Stop()\r
- peer.signal.handshakeBegin.Enable()\r
-\r
- /* signals */\r
-\r
- case <-peer.signal.handshakeBegin.Wait():\r
-\r
- peer.signal.handshakeBegin.Disable()\r
-\r
- err := peer.sendNewHandshake()\r
-\r
- if err != nil {\r
- logInfo.Println(peer.String(), ": Failed to send handshake initiation", err)\r
- } else {\r
- logDebug.Println(peer.String(), ": Send handshake initiation (initial)")\r
- }\r
-\r
- peer.timer.handshakeDeadline.Reset(RekeyAttemptTime)\r
-\r
- case <-peer.signal.handshakeCompleted.Wait():\r
-\r
- logInfo.Println(peer.String(), ": Handshake completed")\r
-\r
- atomic.StoreInt64(\r
- &peer.stats.lastHandshakeNano,\r
- time.Now().UnixNano(),\r
- )\r
-\r
- peer.timer.handshakeTimeout.Stop()\r
- peer.timer.handshakeDeadline.Stop()\r
- peer.signal.handshakeBegin.Enable()\r
-\r
- peer.timer.sendLastMinuteHandshake = false\r
- }\r
- }\r
-}\r
+package main
+
+import (
+ "bytes"
+ "encoding/binary"
+ "math/rand"
+ "sync/atomic"
+ "time"
+)
+
+/* NOTE:
+ * Notion of validity
+ *
+ *
+ */
+
+/* Called when a new authenticated message has been send
+ *
+ */
+func (peer *Peer) KeepKeyFreshSending() {
+ kp := peer.keyPairs.Current()
+ if kp == nil {
+ return
+ }
+ nonce := atomic.LoadUint64(&kp.sendNonce)
+ if nonce > RekeyAfterMessages {
+ peer.signal.handshakeBegin.Send()
+ }
+ if kp.isInitiator && time.Now().Sub(kp.created) > RekeyAfterTime {
+ peer.signal.handshakeBegin.Send()
+ }
+}
+
+/* Called when a new authenticated message has been received
+ *
+ * NOTE: Not thread safe, but called by sequential receiver!
+ */
+func (peer *Peer) KeepKeyFreshReceiving() {
+ if peer.timer.sendLastMinuteHandshake {
+ return
+ }
+ kp := peer.keyPairs.Current()
+ if kp == nil {
+ return
+ }
+ if !kp.isInitiator {
+ return
+ }
+ nonce := atomic.LoadUint64(&kp.sendNonce)
+ send := nonce > RekeyAfterMessages || time.Now().Sub(kp.created) > RekeyAfterTimeReceiving
+ if send {
+ // do a last minute attempt at initiating a new handshake
+ peer.timer.sendLastMinuteHandshake = true
+ peer.signal.handshakeBegin.Send()
+ }
+}
+
+/* Queues a keep-alive if no packets are queued for peer
+ */
+func (peer *Peer) SendKeepAlive() bool {
+ if len(peer.queue.nonce) != 0 {
+ return false
+ }
+ elem := peer.device.NewOutboundElement()
+ elem.packet = nil
+ select {
+ case peer.queue.nonce <- elem:
+ return true
+ default:
+ return false
+ }
+}
+
+/* Event:
+ * Sent non-empty (authenticated) transport message
+ */
+func (peer *Peer) TimerDataSent() {
+ peer.timer.keepalivePassive.Stop()
+ peer.timer.handshakeNew.Start(NewHandshakeTime)
+}
+
+/* Event:
+ * Received non-empty (authenticated) transport message
+ *
+ * Action:
+ * Set a timer to confirm the message using a keep-alive (if not already set)
+ */
+func (peer *Peer) TimerDataReceived() {
+ if !peer.timer.keepalivePassive.Start(KeepaliveTimeout) {
+ peer.timer.needAnotherKeepalive = true
+ }
+}
+
+/* Event:
+ * Any (authenticated) packet received
+ */
+func (peer *Peer) TimerAnyAuthenticatedPacketReceived() {
+ peer.timer.handshakeNew.Stop()
+}
+
+/* Event:
+ * Any authenticated packet send / received.
+ *
+ * Action:
+ * Push persistent keep-alive into the future
+ */
+func (peer *Peer) TimerAnyAuthenticatedPacketTraversal() {
+ interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)
+ if interval > 0 {
+ duration := time.Duration(interval) * time.Second
+ peer.timer.keepalivePersistent.Reset(duration)
+ }
+}
+
+/* Called after successfully completing a handshake.
+ * i.e. after:
+ *
+ * - Valid handshake response
+ * - First transport message under the "next" key
+ */
+func (peer *Peer) TimerHandshakeComplete() {
+ peer.signal.handshakeCompleted.Send()
+ peer.device.log.Info.Println("Negotiated new handshake for", peer.String())
+}
+
+/* Event:
+ * An ephemeral key is generated
+ *
+ * i.e. after:
+ *
+ * CreateMessageInitiation
+ * CreateMessageResponse
+ *
+ * Action:
+ * Schedule the deletion of all key material
+ * upon failure to complete a handshake
+ */
+func (peer *Peer) TimerEphemeralKeyCreated() {
+ peer.timer.zeroAllKeys.Reset(RejectAfterTime * 3)
+}
+
+/* Sends a new handshake initiation message to the peer (endpoint)
+ */
+func (peer *Peer) sendNewHandshake() error {
+
+ // temporarily disable the handshake complete signal
+
+ peer.signal.handshakeCompleted.Disable()
+
+ // create initiation message
+
+ msg, err := peer.device.CreateMessageInitiation(peer)
+ if err != nil {
+ return err
+ }
+
+ // marshal handshake message
+
+ var buff [MessageInitiationSize]byte
+ writer := bytes.NewBuffer(buff[:0])
+ binary.Write(writer, binary.LittleEndian, msg)
+ packet := writer.Bytes()
+ peer.mac.AddMacs(packet)
+
+ // send to endpoint
+
+ peer.TimerAnyAuthenticatedPacketTraversal()
+
+ err = peer.SendBuffer(packet)
+ if err == nil {
+ peer.signal.handshakeCompleted.Enable()
+ }
+
+ // set timeout
+
+ jitter := time.Millisecond * time.Duration(rand.Uint32()%334)
+
+ peer.timer.keepalivePassive.Stop()
+ peer.timer.handshakeTimeout.Reset(RekeyTimeout + jitter)
+
+ return err
+}
+
+func (peer *Peer) RoutineTimerHandler() {
+
+ defer peer.routines.stopping.Done()
+
+ device := peer.device
+
+ logInfo := device.log.Info
+ logDebug := device.log.Debug
+ logDebug.Println("Routine, timer handler, started for peer", peer.String())
+
+ // reset all timers
+
+ peer.timer.keepalivePassive.Stop()
+ peer.timer.handshakeDeadline.Stop()
+ peer.timer.handshakeTimeout.Stop()
+ peer.timer.handshakeNew.Stop()
+ peer.timer.zeroAllKeys.Stop()
+
+ interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)
+ if interval > 0 {
+ duration := time.Duration(interval) * time.Second
+ peer.timer.keepalivePersistent.Reset(duration)
+ }
+
+ // signal synchronised setup complete
+
+ peer.routines.starting.Done()
+
+ // handle timer events
+
+ for {
+ select {
+
+ /* stopping */
+
+ case <-peer.routines.stop.Wait():
+ return
+
+ /* timers */
+
+ // keep-alive
+
+ case <-peer.timer.keepalivePersistent.Wait():
+
+ interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)
+ if interval > 0 {
+ logDebug.Println(peer.String(), ": Send keep-alive (persistent)")
+ peer.timer.keepalivePassive.Stop()
+ peer.SendKeepAlive()
+ }
+
+ case <-peer.timer.keepalivePassive.Wait():
+
+ logDebug.Println(peer.String(), ": Send keep-alive (passive)")
+
+ peer.SendKeepAlive()
+
+ if peer.timer.needAnotherKeepalive {
+ peer.timer.needAnotherKeepalive = false
+ peer.timer.keepalivePassive.Reset(KeepaliveTimeout)
+ }
+
+ // clear key material timer
+
+ case <-peer.timer.zeroAllKeys.Wait():
+
+ logDebug.Println(peer.String(), ": Clear all key-material (timer event)")
+
+ hs := &peer.handshake
+ hs.mutex.Lock()
+
+ kp := &peer.keyPairs
+ kp.mutex.Lock()
+
+ // remove key-pairs
+
+ if kp.previous != nil {
+ device.DeleteKeyPair(kp.previous)
+ kp.previous = nil
+ }
+ if kp.current != nil {
+ device.DeleteKeyPair(kp.current)
+ kp.current = nil
+ }
+ if kp.next != nil {
+ device.DeleteKeyPair(kp.next)
+ kp.next = nil
+ }
+ kp.mutex.Unlock()
+
+ // zero out handshake
+
+ device.indices.Delete(hs.localIndex)
+ hs.Clear()
+ hs.mutex.Unlock()
+
+ // handshake timers
+
+ case <-peer.timer.handshakeNew.Wait():
+ logInfo.Println(peer.String(), ": Retrying handshake (timer event)")
+ peer.signal.handshakeBegin.Send()
+
+ case <-peer.timer.handshakeTimeout.Wait():
+
+ // clear source (in case this is causing problems)
+
+ peer.mutex.Lock()
+ if peer.endpoint != nil {
+ peer.endpoint.ClearSrc()
+ }
+ peer.mutex.Unlock()
+
+ // send new handshake
+
+ err := peer.sendNewHandshake()
+
+ if err != nil {
+ logInfo.Println(peer.String(), ": Failed to send handshake initiation", err)
+ } else {
+ logDebug.Println(peer.String(), ": Send handshake initiation (subsequent)")
+ }
+
+ case <-peer.timer.handshakeDeadline.Wait():
+
+ // clear all queued packets and stop keep-alive
+
+ logInfo.Println(peer.String(), ": Handshake negotiation timed-out")
+
+ peer.signal.flushNonceQueue.Send()
+ peer.timer.keepalivePersistent.Stop()
+ peer.signal.handshakeBegin.Enable()
+
+ /* signals */
+
+ case <-peer.signal.handshakeBegin.Wait():
+
+ peer.signal.handshakeBegin.Disable()
+
+ err := peer.sendNewHandshake()
+
+ if err != nil {
+ logInfo.Println(peer.String(), ": Failed to send handshake initiation", err)
+ } else {
+ logDebug.Println(peer.String(), ": Send handshake initiation (initial)")
+ }
+
+ peer.timer.handshakeDeadline.Reset(RekeyAttemptTime)
+
+ case <-peer.signal.handshakeCompleted.Wait():
+
+ logInfo.Println(peer.String(), ": Handshake completed")
+
+ atomic.StoreInt64(
+ &peer.stats.lastHandshakeNano,
+ time.Now().UnixNano(),
+ )
+
+ peer.timer.handshakeTimeout.Stop()
+ peer.timer.handshakeDeadline.Stop()
+ peer.signal.handshakeBegin.Enable()
+
+ peer.timer.sendLastMinuteHandshake = false
+ }
+ }
+}