]> Cypherpunks.ru repositories - govpn.git/blobdiff - govpn.go
Obfuscate/randomize message nonces
[govpn.git] / govpn.go
index c22a1e6e517b8d930836da2b5a709ecfc0c073d3..6f662c60ca208d5d4a8bce9d69ce1dbc0382ae65 100644 (file)
--- a/govpn.go
+++ b/govpn.go
@@ -1,6 +1,6 @@
 /*
-govpn -- high-performance secure virtual private network daemon
-Copyright (C) 2014 Sergey Matveev <stargrave@stargrave.org>
+govpn -- simple secure virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// High-performance secure virtual private network daemon
+// Simple secure virtual private network daemon
 package main
 
 import (
@@ -34,8 +34,9 @@ import (
        "os/signal"
        "time"
 
-       "code.google.com/p/go.crypto/poly1305"
-       "code.google.com/p/go.crypto/salsa20"
+       "golang.org/x/crypto/poly1305"
+       "golang.org/x/crypto/salsa20"
+       "golang.org/x/crypto/xtea"
 )
 
 var (
@@ -46,6 +47,7 @@ var (
        upPath     = flag.String("up", "", "Path to up-script")
        downPath   = flag.String("down", "", "Path to down-script")
        mtu        = flag.Int("mtu", 1500, "MTU")
+       nonceDiff  = flag.Int("noncediff", 1, "Allow nonce difference")
        timeoutP   = flag.Int("timeout", 60, "Timeout seconds")
        verboseP   = flag.Bool("v", false, "Increase verbosity")
 )
@@ -67,10 +69,11 @@ type TAP interface {
 }
 
 type Peer struct {
-       addr      *net.UDPAddr
-       key       *[KeySize]byte // encryption key
-       nonceOur  uint64         // nonce for our messages
-       nonceRecv uint64         // latest received nonce from remote peer
+       addr        *net.UDPAddr
+       key         *[KeySize]byte // encryption key
+       nonceOur    uint64         // nonce for our messages
+       nonceRecv   uint64         // latest received nonce from remote peer
+       nonceCipher *xtea.Cipher   // nonce cipher
 }
 
 type UDPPkt struct {
@@ -94,6 +97,8 @@ func main() {
        flag.Parse()
        timeout := *timeoutP
        verbose := *verboseP
+       noncediff := uint64(*nonceDiff)
+       var err error
        log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
 
        // Key decoding
@@ -121,9 +126,11 @@ func main() {
        ethSink := make(chan int)
        ethSinkReady := make(chan bool)
        go func() {
+               var n int
+               var err error
                for {
                        <-ethSinkReady
-                       n, err := iface.Read(ethBuf)
+                       n, err = iface.Read(ethBuf)
                        if err != nil {
                                panic(err)
                        }
@@ -167,10 +174,13 @@ func main() {
        udpSink := make(chan *UDPPkt)
        udpSinkReady := make(chan bool)
        go func(conn *net.UDPConn) {
+               var n int
+               var addr *net.UDPAddr
+               var err error
                for {
                        <-udpSinkReady
                        conn.SetReadDeadline(time.Now().Add(time.Second))
-                       n, addr, err := conn.ReadFromUDP(udpBuf)
+                       n, addr, err = conn.ReadFromUDP(udpBuf)
                        if err != nil {
                                if verbose {
                                        fmt.Print("B")
@@ -188,9 +198,11 @@ func main() {
        var udpPktData []byte
        var ethPktSize int
        var frame []byte
+       var dataToSend []byte
        var addr string
        var peer *Peer
        var p *Peer
+       var nonceRecv uint64
 
        timeouts := 0
        bytes := 0
@@ -244,14 +256,14 @@ func main() {
                                                state = &Handshake{addr: udpPkt.addr}
                                                states[addr] = state
                                        }
-                                       p = state.Server(conn, key, udpPktData)
+                                       p = state.Server(noncediff, conn, key, udpPktData)
                                } else {
                                        if !exists {
                                                fmt.Print("[HS?]")
                                                udpSinkReady <- true
                                                continue
                                        }
-                                       p = state.Client(conn, key, udpPktData)
+                                       p = state.Client(noncediff, conn, key, udpPktData)
                                }
                                if p != nil {
                                        fmt.Print("[HS-OK]")
@@ -268,12 +280,6 @@ func main() {
                                udpSinkReady <- true
                                continue
                        }
-                       nonceRecv, _ := binary.Uvarint(udpPktData[:8])
-                       if peer.nonceRecv >= nonceRecv {
-                               fmt.Print("R")
-                               udpSinkReady <- true
-                               continue
-                       }
                        copy(buf[:KeySize], emptyKey)
                        copy(tag[:], udpPktData[udpPkt.size-poly1305.TagSize:])
                        copy(buf[S20BS:], udpPktData[NonceSize:udpPkt.size-poly1305.TagSize])
@@ -289,6 +295,13 @@ func main() {
                                fmt.Print("T")
                                continue
                        }
+                       peer.nonceCipher.Decrypt(buf, udpPktData[:NonceSize])
+                       nonceRecv, _ = binary.Uvarint(buf[:NonceSize])
+                       if nonceRecv < peer.nonceRecv-noncediff {
+                               fmt.Print("R")
+                               udpSinkReady <- true
+                               continue
+                       }
                        udpSinkReady <- true
                        peer.nonceRecv = nonceRecv
                        timeouts = 0
@@ -297,7 +310,7 @@ func main() {
                        if string(frame[0:HeartBeatSize]) == HeartBeatMark {
                                continue
                        }
-                       if _, err := iface.Write(frame); err != nil {
+                       if _, err = iface.Write(frame); err != nil {
                                log.Println("Error writing to iface: ", err)
                        }
                        if verbose {
@@ -311,8 +324,14 @@ func main() {
                                ethSinkReady <- true
                                continue
                        }
+
                        peer.nonceOur = peer.nonceOur + 2
+                       for i := 0; i < NonceSize; i++ {
+                               nonce[i] = '\x00'
+                       }
                        binary.PutUvarint(nonce, peer.nonceOur)
+                       peer.nonceCipher.Encrypt(nonce, nonce)
+
                        copy(buf[:KeySize], emptyKey)
                        if ethPktSize > -1 {
                                copy(buf[S20BS:], ethBuf[:ethPktSize])
@@ -324,10 +343,10 @@ func main() {
                        salsa20.XORKeyStream(buf, buf, nonce, peer.key)
                        copy(buf[S20BS-NonceSize:S20BS], nonce)
                        copy(keyAuth[:], buf[:KeySize])
-                       dataToSend := buf[S20BS-NonceSize : S20BS+ethPktSize]
+                       dataToSend = buf[S20BS-NonceSize : S20BS+ethPktSize]
                        poly1305.Sum(tag, dataToSend, keyAuth)
                        bytes += len(dataToSend)
-                       if _, err := conn.WriteTo(append(dataToSend, tag[:]...), peer.addr); err != nil {
+                       if _, err = conn.WriteTo(append(dataToSend, tag[:]...), peer.addr); err != nil {
                                log.Println("Error sending UDP", err)
                        }
                        if verbose {