]> Cypherpunks.ru repositories - govpn.git/commitdiff
Merge branch 'develop' 5.8
authorSergey Matveev <stargrave@stargrave.org>
Tue, 10 May 2016 08:21:22 +0000 (11:21 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 10 May 2016 08:21:22 +0000 (11:21 +0300)
39 files changed:
TODO
VERSION
common.mk
doc/about.ru.texi
doc/about.texi
doc/client.texi
doc/developer.texi
doc/download.texi
doc/encless.texi
doc/glossary.texi
doc/handshake.texi
doc/handshake.txt
doc/media.texi
doc/news.ru.texi
doc/news.texi
doc/server.texi
doc/syslog.texi [new file with mode: 0644]
doc/transport.texi
src/cypherpunks.ru/govpn/aont/aont_test.go
src/cypherpunks.ru/govpn/cmd/govpn-client/main.go
src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go
src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go
src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go
src/cypherpunks.ru/govpn/cmd/govpn-server/common.go
src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go
src/cypherpunks.ru/govpn/cmd/govpn-server/main.go
src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go
src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go
src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go
src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go
src/cypherpunks.ru/govpn/cnw/cnw_test.go
src/cypherpunks.ru/govpn/egd.go
src/cypherpunks.ru/govpn/encless.go
src/cypherpunks.ru/govpn/encless_test.go
src/cypherpunks.ru/govpn/handshake.go
src/cypherpunks.ru/govpn/identity.go
src/cypherpunks.ru/govpn/logger.go [new file with mode: 0644]
src/cypherpunks.ru/govpn/peer.go
src/cypherpunks.ru/govpn/peer_test.go

diff --git a/TODO b/TODO
index 2ecafb04518cb20354d7c44ac67daf0579a20fbf..3c1c91bc9520f552866ce7dee3c19fd86e5073f9 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1 +1,3 @@
+* When govpn-server opens TAP files, then it won't release them until
+  daemon itself is exited
 * Randomize ports usage
diff --git a/VERSION b/VERSION
index 760606e1ffff82566e95fa1bfa840ca7d1024dd5..3659ea2fa3a7771c8641de0cdbf8c0a02e47e49c 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.7
+5.8
index bd2f3a42bbfc8e162877434ec2db7aa7dda31a50..99e1c0d1b6c9eb1247020f598b0fa2b8240ce666 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -43,3 +43,6 @@ install: all doc
 
 install-strip: install
        strip $(BINDIR)/govpn-client $(BINDIR)/govpn-server $(BINDIR)/govpn-verifier
+
+dist:
+       ./utils/makedist.sh $(VERSION)
index 5bb7995e03967ecae194ef37d9486f45b563ed60..aa89817786476746f1ad18f255e7fff16991c017 100644 (file)
@@ -88,6 +88,9 @@ A-EKE (Diffie-Hellman Augmented Encrypted Key Exchange)).
 @item
 Сервер конфигурируется используя @url{http://yaml.org/, YAML} файл.
 
+@item
+Возможность использовать syslog для журналирования.
+
 @item
 Написан на языке @url{https://golang.org/, Go} с простым кодом,
 ориентированным на лёгкость чтения и анализа.
index a3ed945f20c1037a72260c9a641594736a766c1d..833c51696b8bd41286b96dc58b9bd827b29b0ed8 100644 (file)
@@ -84,6 +84,9 @@ Optional built-in HTTP-server for retrieving real-time
 @item
 Server is configured through the @url{http://yaml.org/, YAML} file.
 
+@item
+Ability to use syslog for logging.
+
 @item
 Written on @url{https://golang.org/, Go} programming language with
 simple code that can be read and reviewed.
index b6d8aab2a544863e63c7ef6f7cff7af539172b7d..d3a7f418bb73bb807bc8ea4bc945861bfecbb600 100644 (file)
@@ -1,8 +1,8 @@
 @node Client
 @section Client part
 
-Except for common @ref{Stats, -stats}, @ref{EGD, -egd} options client
-has the following ones:
+Except for common @ref{Stats, -stats}, @ref{EGD, -egd}, @ref{Syslog, -syslog}
+options client has the following ones:
 
 @table @option
 
index 4293f800c06d8084790c564e26768c911d4509a4..f2fe00ec5ba392b87a26ee14c99ddffab48f1606 100644 (file)
@@ -4,12 +4,12 @@
 Pay attention how to get @ref{Sources, development source code}.
 
 @table @asis
-@item Nonce and identity encryption
-    @url{http://www.cix.co.uk/~klockstone/xtea.pdf, XTEA}.
 @item Data encryption
     @url{http://cr.yp.to/snuffle.html, Salsa20}.
 @item Message authentication
     @url{http://cr.yp.to/mac.html, Poly1305}.
+@item Nonce and identity obfuscation
+    @url{https://blake2.net/, BLAKE2b-MAC}.
 @item Password authenticated key agreement
     DH-A-EKE powered by @url{http://cr.yp.to/ecdh.html, Curve25519}
     and @url{http://ed25519.cr.yp.to/, Ed25519}.
@@ -24,7 +24,7 @@ Pay attention how to get @ref{Sources, development source code}.
     @url{http://theory.lcs.mit.edu/~cis/pubs/rivest/fusion.ps,
     All-Or-Nothing-Transformed} (based on
     @url{http://cseweb.ucsd.edu/~mihir/papers/oaep.html, OAEP} using
-    Salsa20 with @url{https://blake2.net/, BLAKE2b-256} based
+    Salsa20 with BLAKE2b-256 based
     @url{http://crypto.stanford.edu/~dabo/abstracts/saep.html, SAEP+}
     checksums) data with 128-bits of feeded random.
 @item Packet overhead
index aeab4810cb2280208cb2854f44ff6f42a952e710..721e4f9a561b996cc1e651ba7c506fd77acf55ad 100644 (file)
@@ -6,6 +6,10 @@ You can obtain releases source code prepared tarballs from the links below:
 @multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
 @headitem Version @tab Size @tab Tarball @tab SHA256 checksum
 
+@item @ref{Release 5.7, 5.7} @tab 312 KiB
+@tab @url{download/govpn-5.7.tar.xz, link} @url{download/govpn-5.7.tar.xz.sig, sign}
+@tab @code{17a8a223e2d9d4fd537f8de802bc6c72f16ebf8a8c5430e3fbf045c304f9dfec}
+
 @item @ref{Release 5.6, 5.6} @tab 311 KiB
 @tab @url{download/govpn-5.6.tar.xz, link} @url{download/govpn-5.6.tar.xz.sig, sign}
 @tab @code{d46b8f742f1e2bf17236868512f1ea5ad80f59c3bac753a56ce41a1f465282a8}
index 6d44191ebdc1c5d492ffc321568e146f8d2acada..6d694b2a984217740f0dff2c321aaa088c28d4c3 100644 (file)
@@ -25,12 +25,5 @@ signature algorithms. No encryption and steganography involved.
 In this mode each outgoing packet became larger on 4128 bytes and
 @ref{Noise, noise} is forcefully enabled. So this is resource hungry mode!
 
-@strong{Beware}: by default packet serial numbers are still processed
-through the XTEA encryption. It is not required for confidentiality and
-security, but for randomizing some parts of the traffic to make it
-indistinguishable from the noise, for making it more DPI-proof. It
-safely can be disabled, turned off or maybe its keys even can be
-revealed without security and forward secrecy loss.
-
 See @code{govpn/cnw} and @code{govpn/aont} packages for details of AONT
 and chaffing operations.
index 8fed1674d4070f9c0dd4da1ddb9d9dd915427565..3cf8d901e021f8ffe58f117d40ab6f80d3304f8a 100644 (file)
@@ -15,6 +15,7 @@
 * Noise::
 * Constant Packet Rate: CPR.
 * Encryptionless mode: Encless.
+* Syslog::
 * Verifier::
 @end menu
 
@@ -31,4 +32,5 @@
 @include noise.texi
 @include cpr.texi
 @include encless.texi
+@include syslog.texi
 @include verifier.texi
index f19fde0c153d9f0b7afe9bd2411aee9916d233a4..75a65085f712082136675d6b34dc54860db1cd54 100644 (file)
@@ -3,10 +3,10 @@
 
 @verbatiminclude handshake.utxt
 
-Each handshake message ends with so called @code{IDtag}: it is an XTEA
-encrypted first 64 bits of each message with client's @ref{Identity} as
-a key. It is used to transmit identity and to mark packet as handshake
-message.
+Each handshake message ends with so called @code{IDtag}: it is
+BLAKE2b-MAC of the first 64 bits of the handshake message, with client's
+@ref{Identity} used as a key. It is used to transmit identity and to
+mark packet as handshake message.
 
 If @ref{Noise, noise} is enabled, then data is padded to fill up packet
 to MTU's size.
index 43ed4ff56546cb5195f5daa7f89a5cad8bcbba95..50b479a56293daefb44b1daccda4e1009485598a 100644 (file)
@@ -3,26 +3,33 @@ hide footbox
 participant Client
 participant Server
 
-== Preparation ==
-Client -> Client : R=rand(64bit)
-Client -> Client : CDHPriv=rand(256bit)
-
-== Interaction ==
 Client -> Server : R, enc(H(DSAPub), R, El(CDHPub))
-Server -> Server : SDHPriv=rand(256bit)
-Server -> Server : K=H(DH(SDHPriv, CDHPub))
-Server -> Server : RS=rand(64bit)
-Server -> Server : SS=rand(256bit)
-Server -> Client : enc(H(DSAPub), R+1, El(SDHPub)); enc(K, R, RS+SS)
-Client -> Client : K=H(DH(CDHPriv, SDHPub))
-Client -> Client : RC=rand(64bit); SC=rand(256bit)
+note right
+R=rand(64bit)
+CDHPriv=rand(256bit)
+end note
+
+Server -> Client : enc(H(DSAPub), R+1, El(SDHPub))\nenc(K, R, RS+SS)
+note right
+SDHPriv=rand(256bit)
+K=H(DH(SDHPriv, CDHPub))
+RS=rand(64bit)
+SS=rand(256bit)
+end note
+
 Client -> Server : enc(K, R+1, RS+RC+SC+Sign(DSAPriv, K))
-Server -> Server : compare(RS)
-Server -> Server : Verify(DSAPub, Sign(DSAPriv, K), K)
+note right
+K=H(DH(CDHPriv, SDHPub))
+RC=rand(64bit)
+SC=rand(256bit)
+end note
+
 Server -> Client : enc(K, R+2, RC)
+note right
+compare(RS)
+compare(RC)
+Verify(DSAPub, Sign(DSAPriv, K), K)
+MasterKey=SS XOR SC
+end note
 
-== Finalizing ==
-Client -> Client : compare(RC)
-Client -> Client : MasterKey=SS XOR SC
-Server -> Server : MasterKey=SS XOR SC
 @enduml
index b129c0dd1b69c40f679e4e433ba72513bb97cbe4..5c30def83d03ce88135099faa02c6ba12ff7e436 100644 (file)
@@ -2,6 +2,7 @@
 @unnumbered In the media
 
 @itemize
+@item @url{http://www.stargrave.org/GoVPN.html, GoVPN: secure censorship resistant VPN daemon history and implementation decisions}
 @item @url{http://habrahabr.ru/company/ivi/blog/256365/, Реализуем безопасный VPN-протокол} (on russian)
 @item @url{http://habrahabr.ru/company/ivi/blog/257431/, Реализуем ещё более безопасный VPN-протокол} (on russian)
 @item @url{http://www.linuxspace.org/archives/9449, Установка и настройка безопасного VPN-демона GoVPN 3.2} (on russian)
index 362ada0d5fd13824ca62cb0005732b3c3b7333c7..6ea418170abf2d2f22d739fbdfae762544ea6b72 100644 (file)
@@ -1,6 +1,17 @@
 @node Новости
 @section Новости
 
+@node Релиз 5.8
+@subsection Релиз 5.8
+@itemize
+@item Опциональная возможность использовать syslog для журналирования,
+с @url{https://tools.ietf.org/html/rfc5424, RFC 5424}-похожими
+структурированными записями.
+@item Вместо XTEA алгоритма для обфускации nonce используется
+BLAKE2b-MAC. Теперь нешифрованный режим действительно не зависит от
+алгоритмов шифрования.
+@end itemize
+
 @node Релиз 5.7
 @subsection Релиз 5.7
 @itemize
index 195673066d58143ba802834900c0add6f356b7ec..f363b846307e944ba69d94cdda795c0156adde26 100644 (file)
@@ -3,6 +3,17 @@
 
 See also this page @ref{Новости, on russian}.
 
+@node Release 5.8
+@section Release 5.8
+@itemize
+@item Optional ability to use syslog for logging, with
+@url{https://tools.ietf.org/html/rfc5424, RFC 5424}-like
+structured records.
+@item XTEA algorithm is not used anymore for nonce obfuscation, but
+BLAKE2b-MAC instead. Encryptionless mode now really does not depend on
+encryption functions.
+@end itemize
+
 @node Release 5.7
 @section Release 5.7
 @itemize
index b3c641e00fe7b2234978926eec7d0326aa57ce98..63f3239fb994aff3315ba24ec9126ecf1afc876b 100644 (file)
@@ -1,8 +1,8 @@
 @node Server
 @section Server part
 
-Except for common @ref{Stats, -stats}, @ref{EGD, -egd} options client
-has the following ones:
+Except for common @ref{Stats, -stats}, @ref{EGD, -egd}, @ref{Syslog, -syslog}
+options server has the following ones:
 
 @table @option
 
diff --git a/doc/syslog.texi b/doc/syslog.texi
new file mode 100644 (file)
index 0000000..82ba7b3
--- /dev/null
@@ -0,0 +1,6 @@
+@node Syslog
+@subsection Syslog
+
+You can enable logging to syslog instead of default stdout using
+@option{-syslog} option. All informational messages during the work
+will be sent with @emph{INFO} level.
index 4b8413b23e470b0cb58543ecaa4c92c89d572731..1518d6f95fa59d02324409e9818234416827e5c0 100644 (file)
@@ -2,42 +2,22 @@
 @section Transport protocol
 
 @verbatim
-TAG || ENCRYPTED || NONCE --> PACKET
- ^         ^          ^
- |         |          |
- |         |          +-------------+
- |         |                        |
- |         +-------------+          |
- |                       |          |
- +--< AUTH(AUTH_KEY, ENCRYPTED || NONCE)
-                         ^          ^
-                         |          |
-+------------------------+          |
-|                                   |
-|                   +---------------+
-|                   |
-+--< ENCRYPT(KEY, NONCE, PAYLOAD)
-                    ^       ^
-                    |       |
-                    |       +--< DATA || PAD [|| ZEROS]
-                    |
-                    +--< PRP(PRP_KEY, SERIAL)
+     NONCE = 64bit(MAC(MAC_KEY, SERIAL))
+   PAYLOAD = DATA || PAD [|| ZEROS]
+CIPHERTEXT = ENCRYPT(KEY, NONCE, PAYLOAD)
+       TAG = AUTH(AUTH_KEY, CIPHERTEXT || NONCE)
+   MESSAGE = TAG || CIPHERTEXT || NONCE
 @end verbatim
 
 @code{SERIAL} is message's serial number. Odds are reserved for
 client (to server) messages, evens for server (to client) messages.
 
-@code{PRP} is XTEA block cipher algorithm used here as PRP (pseudo
-random permutation function) to obfuscate @code{SERIAL}. Plaintext
-@code{SERIAL} state is kept in peers internal state, but encrypted
-before transmission.
-
-XTEA's encryption key @code{PRP_KEY} is the first 128-bit of Salsa20's
-output with established common key and zero nonce (message nonces start
-from 1).
+@code{MAC} is BLAKE2b-MAC used to obfuscate @code{SERIAL}. MAC's key
+@code{MAC_KEY} is the first 256-bit of Salsa20's output with established
+common key and zero nonce (message nonces start from 1).
 
 @verbatim
-PRP_KEY = 128bit(ENCRYPT(KEY, 0))
+MAC_KEY = 256bit(ENCRYPT(KEY, 0))
 @end verbatim
 
 @code{ENCRYPT} is Salsa20 stream cipher, with established session
@@ -61,9 +41,9 @@ drop when receiving duplicate ones.
 In @ref{Encless, encryptionless mode} this scheme is slightly different:
 
 @verbatim
- PACKET = ENCODED || NONCE
+  NONCE = MAC(MAC_KEY, SERIAL)
 ENCODED = ENCLESS(DATA || PAD || ZEROS)
-  NONCE = PRP(PRP_KEY, SERIAL)
+ PACKET = ENCODED || NONCE
 @end verbatim
 
 @code{ENCLESS} is AONT and chaffing function. There is no need in
index 93b7db51afbaf3b3f667c684735b3d01d1ab46fb..0ef140c2c407726531a21b951875a4ab0b2b5aa5 100644 (file)
@@ -21,6 +21,7 @@ package aont
 import (
        "bytes"
        "crypto/rand"
+       "io"
        "testing"
        "testing/quick"
 )
@@ -30,7 +31,7 @@ var (
 )
 
 func init() {
-       rand.Read(testKey[:])
+       io.ReadFull(rand.Reader, testKey[:])
 }
 
 func TestSymmetric(t *testing.T) {
@@ -80,7 +81,7 @@ func TestTampered(t *testing.T) {
 
 func BenchmarkEncode(b *testing.B) {
        data := make([]byte, 128)
-       rand.Read(data)
+       io.ReadFull(rand.Reader, data)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                Encode(testKey, data)
@@ -89,7 +90,7 @@ func BenchmarkEncode(b *testing.B) {
 
 func BenchmarkDecode(b *testing.B) {
        data := make([]byte, 128)
-       rand.Read(data)
+       io.ReadFull(rand.Reader, data)
        encoded, _ := Encode(testKey, data)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
index 58d5ce7d8597022df5a0a9e9197cf71f1dce0cdf..3d8cbe65953fd935f480a9aefde3efec28ed61d8 100644 (file)
@@ -49,6 +49,7 @@ var (
        encless     = flag.Bool("encless", false, "Encryptionless mode")
        cpr         = flag.Int("cpr", 0, "Enable constant KiB/sec out traffic rate")
        egdPath     = flag.String("egd", "", "Optional path to EGD socket")
+       syslog      = flag.Bool("syslog", false, "Enable logging to syslog")
        warranty    = flag.Bool("warranty", false, "Print warranty information")
 
        conf        *govpn.PeerConf
@@ -56,7 +57,7 @@ var (
        timeout     int
        firstUpCall bool = true
        knownPeers  govpn.KnownPeers
-       idsCache    *govpn.CipherCache
+       idsCache    *govpn.MACCache
 )
 
 func main() {
@@ -107,7 +108,7 @@ func main() {
                Verifier: verifier,
                DSAPriv:  priv,
        }
-       idsCache = govpn.NewCipherCache()
+       idsCache = govpn.NewMACCache()
        confs := map[govpn.PeerId]*govpn.PeerConf{*verifier.Id: conf}
        idsCache.Update(&confs)
        log.Println(govpn.VersionGet())
@@ -126,6 +127,10 @@ func main() {
                go govpn.StatsProcessor(statsPort, &knownPeers)
        }
 
+       if *syslog {
+               govpn.SyslogEnable()
+       }
+
        termSignal := make(chan os.Signal, 1)
        signal.Notify(termSignal, os.Interrupt, os.Kill)
 
@@ -151,7 +156,7 @@ MainCycle:
                }
                select {
                case <-termSignal:
-                       log.Fatalln("Finishing")
+                       govpn.BothPrintf(`[finish remote="%s"]`, *remoteAddr)
                        termination <- struct{}{}
                        break MainCycle
                case <-timeouted:
index a4259f39c2840d5f57b61084ca9f1dc8738f6f35..0a6729c1dd7ca99ae0ce8f66bc4793ace0bd8a3e 100644 (file)
@@ -24,6 +24,8 @@ import (
        "log"
        "net"
        "net/http"
+
+       "cypherpunks.ru/govpn"
 )
 
 func proxyTCP(timeouted, rehandshaking, termination chan struct{}) {
@@ -50,6 +52,6 @@ func proxyTCP(timeouted, rehandshaking, termination chan struct{}) {
        if err != nil || resp.StatusCode != http.StatusOK {
                log.Fatalln("Unexpected response from proxy")
        }
-       log.Println("Connected to proxy:", *proxyAddr)
+       govpn.Printf(`[proxy-connected remote="%s" addr="%s"]`, *remoteAddr, *proxyAddr)
        go handleTCP(conn, timeouted, rehandshaking, termination)
 }
index 4b11eac1091f8c5cca3bd458d95230fc1ba89b7e..57a412d31d687772c691fa2dc07cb7a65ddf8946 100644 (file)
@@ -37,7 +37,7 @@ func startTCP(timeouted, rehandshaking, termination chan struct{}) {
        if err != nil {
                log.Fatalln("Can not connect to address:", err)
        }
-       log.Println("Connected to TCP:" + *remoteAddr)
+       govpn.Printf(`[connected remote="%s"]`, *remoteAddr)
        handleTCP(conn, timeouted, rehandshaking, termination)
 }
 
@@ -57,7 +57,7 @@ HandshakeCycle:
                default:
                }
                if prev == len(buf) {
-                       log.Println("Timeouted waiting for the packet")
+                       govpn.Printf(`[packet-timeouted remote="%s"]`, *remoteAddr)
                        timeouted <- struct{}{}
                        break HandshakeCycle
                }
@@ -65,7 +65,7 @@ HandshakeCycle:
                conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
                n, err = conn.Read(buf[prev:])
                if err != nil {
-                       log.Println("Connection timeouted")
+                       govpn.Printf(`[connection-timeouted remote="%s"]`, *remoteAddr)
                        timeouted <- struct{}{}
                        break HandshakeCycle
                }
@@ -80,7 +80,7 @@ HandshakeCycle:
                if peer == nil {
                        continue
                }
-               log.Println("Handshake completed")
+               govpn.Printf(`[handshake-completed remote="%s"]`, *remoteAddr)
                knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer})
                if firstUpCall {
                        go govpn.ScriptCall(*upPath, *ifaceName, *remoteAddr)
@@ -88,23 +88,7 @@ HandshakeCycle:
                }
                hs.Zero()
                terminator = make(chan struct{})
-               go func() {
-                       heartbeat := time.NewTicker(peer.Timeout)
-                       var data []byte
-               Processor:
-                       for {
-                               select {
-                               case <-heartbeat.C:
-                                       peer.EthProcess(nil)
-                               case <-terminator:
-                                       break Processor
-                               case data = <-tap.Sink:
-                                       peer.EthProcess(data)
-                               }
-                       }
-                       heartbeat.Stop()
-                       peer.Zero()
-               }()
+               go govpn.PeerTapProcessor(peer, tap, terminator)
                break HandshakeCycle
        }
        if hs != nil {
@@ -114,8 +98,6 @@ HandshakeCycle:
                return
        }
 
-       nonceExpectation := make([]byte, govpn.NonceSize)
-       peer.NonceExpectation(nonceExpectation)
        prev = 0
        var i int
 TransportCycle:
@@ -126,14 +108,14 @@ TransportCycle:
                default:
                }
                if prev == len(buf) {
-                       log.Println("Timeouted waiting for the packet")
+                       govpn.Printf(`[packet-timeouted remote="%s"]`, *remoteAddr)
                        timeouted <- struct{}{}
                        break TransportCycle
                }
                conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
                n, err = conn.Read(buf[prev:])
                if err != nil {
-                       log.Println("Connection timeouted")
+                       govpn.Printf(`[connection-timeouted remote="%s"]`, *remoteAddr)
                        timeouted <- struct{}{}
                        break TransportCycle
                }
@@ -142,21 +124,20 @@ TransportCycle:
                if prev < govpn.MinPktLength {
                        continue
                }
-               i = bytes.Index(buf[:prev], nonceExpectation)
+               i = bytes.Index(buf[:prev], peer.NonceExpect)
                if i == -1 {
                        continue
                }
                if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
-                       log.Println("Unauthenticated packet, dropping connection")
+                       govpn.Printf(`[packet-unauthenticated remote="%s"]`, *remoteAddr)
                        timeouted <- struct{}{}
                        break TransportCycle
                }
                if atomic.LoadUint64(&peer.BytesIn)+atomic.LoadUint64(&peer.BytesOut) > govpn.MaxBytesPerKey {
-                       log.Println("Need rehandshake")
+                       govpn.Printf(`[rehandshake-required remote="%s"]`, *remoteAddr)
                        rehandshaking <- struct{}{}
                        break TransportCycle
                }
-               peer.NonceExpectation(nonceExpectation)
                copy(buf, buf[i+govpn.NonceSize:prev])
                prev = prev - i - govpn.NonceSize
                goto CheckMore
index acacbd15473b435abbada466ec37f8030e66ff85..96dbfba418822b9833c40ffd7154070a11c3c8b6 100644 (file)
@@ -36,7 +36,7 @@ func startUDP(timeouted, rehandshaking, termination chan struct{}) {
        if err != nil {
                log.Fatalln("Can not listen on UDP:", err)
        }
-       log.Println("Connected to UDP:" + *remoteAddr)
+       govpn.Printf(`[connected remote="%s"]`, *remoteAddr)
 
        hs := govpn.HandshakeStart(*remoteAddr, conn, conf)
        buf := make([]byte, *mtu*2)
@@ -55,7 +55,7 @@ MainCycle:
                conn.SetReadDeadline(time.Now().Add(time.Second))
                n, err = conn.Read(buf)
                if timeouts == timeout {
-                       log.Println("Timeouted")
+                       govpn.Printf(`[connection-timeouted remote="%s"]`, *remoteAddr)
                        timeouted <- struct{}{}
                        break
                }
@@ -67,18 +67,18 @@ MainCycle:
                        if peer.PktProcess(buf[:n], tap, true) {
                                timeouts = 0
                        } else {
-                               log.Println("Unauthenticated packet")
+                               govpn.Printf(`[packet-unauthenticated remote="%s"]`, *remoteAddr)
                                timeouts++
                        }
                        if atomic.LoadUint64(&peer.BytesIn)+atomic.LoadUint64(&peer.BytesOut) > govpn.MaxBytesPerKey {
-                               log.Println("Need rehandshake")
+                               govpn.Printf(`[rehandshake-required remote="%s"]`, *remoteAddr)
                                rehandshaking <- struct{}{}
                                break MainCycle
                        }
                        continue
                }
                if idsCache.Find(buf[:n]) == nil {
-                       log.Println("Invalid identity in handshake packet")
+                       govpn.Printf(`[identity-invalid remote="%s"]`, *remoteAddr)
                        continue
                }
                timeouts = 0
@@ -86,7 +86,7 @@ MainCycle:
                if peer == nil {
                        continue
                }
-               log.Println("Handshake completed")
+               govpn.Printf(`[handshake-completed remote="%s"]`, *remoteAddr)
                knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer})
                if firstUpCall {
                        go govpn.ScriptCall(*upPath, *ifaceName, *remoteAddr)
@@ -94,23 +94,7 @@ MainCycle:
                }
                hs.Zero()
                terminator = make(chan struct{})
-               go func() {
-                       heartbeat := time.NewTicker(peer.Timeout)
-                       var data []byte
-               Processor:
-                       for {
-                               select {
-                               case <-heartbeat.C:
-                                       peer.EthProcess(nil)
-                               case <-terminator:
-                                       break Processor
-                               case data = <-tap.Sink:
-                                       peer.EthProcess(data)
-                               }
-                       }
-                       heartbeat.Stop()
-                       peer.Zero()
-               }()
+               go govpn.PeerTapProcessor(peer, tap, terminator)
        }
        if terminator != nil {
                terminator <- struct{}{}
index f7435dddcd766a63337e23e9762fd20d5ec5cfdb..7c2dd41e7602a42a7225e8fd74331dcc2df0ffaf 100644 (file)
@@ -20,9 +20,7 @@ package main
 
 import (
        "bytes"
-       "log"
        "sync"
-       "time"
 
        "cypherpunks.ru/govpn"
 )
@@ -47,31 +45,12 @@ var (
        kpLock     sync.RWMutex
 )
 
-func peerReady(ps PeerState) {
-       var data []byte
-       heartbeat := time.NewTicker(ps.peer.Timeout)
-Processor:
-       for {
-               select {
-               case <-heartbeat.C:
-                       ps.peer.EthProcess(nil)
-               case <-ps.terminator:
-                       break Processor
-               case data = <-ps.tap.Sink:
-                       ps.peer.EthProcess(data)
-               }
-       }
-       close(ps.terminator)
-       ps.peer.Zero()
-       heartbeat.Stop()
-}
-
 func callUp(peerId *govpn.PeerId, remoteAddr string) (string, error) {
        ifaceName := confs[*peerId].Iface
        if confs[*peerId].Up != "" {
                result, err := govpn.ScriptCall(confs[*peerId].Up, ifaceName, remoteAddr)
                if err != nil {
-                       log.Println("Script", confs[*peerId].Up, "call failed", err)
+                       govpn.Printf(`[script-failed bind="%s" path="%s" err="%s"]`, *bindAddr, confs[*peerId].Up, err)
                        return "", err
                }
                if ifaceName == "" {
@@ -83,7 +62,7 @@ func callUp(peerId *govpn.PeerId, remoteAddr string) (string, error) {
                }
        }
        if ifaceName == "" {
-               log.Println("Can not obtain interface name for", *peerId)
+               govpn.Printf(`[tap-failed bind="%s" peer="%s"]`, *bindAddr, *peerId)
        }
        return ifaceName, nil
 }
index e486ec97b12a92b37f03a9b2846612bf70903701..ea61c813f22494be3b469b9736f24c99ef436df6 100644 (file)
@@ -35,7 +35,7 @@ const (
 
 var (
        confs    map[govpn.PeerId]*govpn.PeerConf
-       idsCache *govpn.CipherCache
+       idsCache *govpn.MACCache
 )
 
 func confRead() (*map[govpn.PeerId]*govpn.PeerConf, error) {
@@ -62,7 +62,7 @@ func confRead() (*map[govpn.PeerId]*govpn.PeerConf, error) {
                        pc.MTU = govpn.MTUDefault
                }
                if pc.MTU > govpn.MTUMax {
-                       log.Println("MTU value", pc.MTU, "is too high, overriding to", govpn.MTUMax)
+                       govpn.Printf(`[mtu-high bind="%s" value="%d" overriden="%d"]`, *bindAddr, pc.MTU, govpn.MTUMax)
                        pc.MTU = govpn.MTUMax
                }
                conf := govpn.PeerConf{
@@ -90,7 +90,7 @@ func confRead() (*map[govpn.PeerId]*govpn.PeerConf, error) {
 func confRefresh() error {
        newConfs, err := confRead()
        if err != nil {
-               log.Println("Unable to parse peers configuration:", err)
+               govpn.Printf(`[conf-parse-failed bind="%s" err="%s"]`, *bindAddr, err)
                return err
        }
        confs = *newConfs
@@ -99,7 +99,7 @@ func confRefresh() error {
 }
 
 func confInit() {
-       idsCache = govpn.NewCipherCache()
+       idsCache = govpn.NewMACCache()
        if err := confRefresh(); err != nil {
                log.Fatalln(err)
        }
index 606caf0ede5ccbb32c0e0e267dbadfc9d6f59793..c70e82a3f1da9d22f078ec2187a14eaf30428de6 100644 (file)
@@ -38,6 +38,7 @@ var (
        stats    = flag.String("stats", "", "Enable stats retrieving on host:port")
        proxy    = flag.String("proxy", "", "Enable HTTP proxy on host:port")
        egdPath  = flag.String("egd", "", "Optional path to EGD socket")
+       syslog   = flag.Bool("syslog", false, "Enable logging to syslog")
        warranty = flag.Bool("warranty", false, "Print warranty information")
 )
 
@@ -59,6 +60,10 @@ func main() {
                govpn.EGDInit(*egdPath)
        }
 
+       if *syslog {
+               govpn.SyslogEnable()
+       }
+
        switch *proto {
        case "udp":
                startUDP()
@@ -88,14 +93,14 @@ func main() {
        if *proxy != "" {
                go proxyStart()
        }
-       log.Println("Server started")
+       govpn.BothPrintf(`[started bind="%s"]`, *bindAddr)
 
        var needsDeletion bool
 MainCycle:
        for {
                select {
                case <-termSignal:
-                       log.Println("Terminating")
+                       govpn.BothPrintf(`[terminating bind="%s"]`, *bindAddr)
                        for _, ps := range peers {
                                govpn.ScriptCall(
                                        confs[*ps.peer.Id].Down,
@@ -109,7 +114,7 @@ MainCycle:
                        hsLock.Lock()
                        for addr, hs := range handshakes {
                                if hs.LastPing.Add(timeout).Before(now) {
-                                       log.Println("Deleting handshake state", addr)
+                                       govpn.Printf(`[handshake-delete bind="%s" addr="%s"]`, *bindAddr, addr)
                                        hs.Zero()
                                        delete(handshakes, addr)
                                }
@@ -122,7 +127,7 @@ MainCycle:
                                needsDeletion = ps.peer.LastPing.Add(timeout).Before(now)
                                ps.peer.BusyR.Unlock()
                                if needsDeletion {
-                                       log.Println("Deleting peer", ps.peer)
+                                       govpn.Printf(`[peer-delete bind="%s" peer="%s"]`, *bindAddr, ps.peer)
                                        delete(peers, addr)
                                        delete(knownPeers, addr)
                                        delete(peersById, *ps.peer.Id)
index f1f5e09d37aeec0558de8033cd361e2e595e9b62..02c1f0f53addb30708cdace82e756c8edc71ed26 100644 (file)
@@ -19,8 +19,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
-       "log"
        "net/http"
+
+       "cypherpunks.ru/govpn"
 )
 
 type proxyHandler struct{}
@@ -28,7 +29,7 @@ type proxyHandler struct{}
 func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        conn, _, err := w.(http.Hijacker).Hijack()
        if err != nil {
-               log.Println("Hijacking failed:", err.Error())
+               govpn.Printf(`[proxy-hijack-failed bind="%s" err="%s"]`, *bindAddr, err)
                return
        }
        conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
@@ -36,10 +37,10 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 }
 
 func proxyStart() {
-       log.Println("HTTP proxy listening on:" + *proxy)
+       govpn.BothPrintf(`[proxy-listen bind="%s" addr="%s"]`, *bindAddr, *proxy)
        s := &http.Server{
                Addr:    *proxy,
                Handler: proxyHandler{},
        }
-       log.Println("HTTP proxy result:", s.ListenAndServe())
+       govpn.BothPrintf(`[proxy-finished bind="%s" result="%s"]`, *bindAddr, s.ListenAndServe())
 }
index 6ca6967f60af4c892b50d59242ddf273a17d7154..bfb4709771e5cdc8680aea9ada107a5bae908c69 100644 (file)
@@ -36,12 +36,12 @@ func startTCP() {
        if err != nil {
                log.Fatalln("Can not listen on TCP:", err)
        }
-       log.Println("Listening on TCP:" + *bindAddr)
+       govpn.BothPrintf(`[tcp-listen bind="%s"]`, *bindAddr)
        go func() {
                for {
                        conn, err := listener.AcceptTCP()
                        if err != nil {
-                               log.Println("Error accepting TCP:", err)
+                               govpn.Printf(`[tcp-accept-failed bind="%s" err="%s"]`, *bindAddr, err)
                                continue
                        }
                        go handleTCP(conn)
@@ -78,7 +78,10 @@ func handleTCP(conn net.Conn) {
                if hs == nil {
                        conf = confs[*peerId]
                        if conf == nil {
-                               log.Println("Can not get peer configuration:", peerId.String())
+                               govpn.Printf(
+                                       `[conf-get-failed bind="%s" peer="%s"]`,
+                                       *bindAddr, peerId.String(),
+                               )
                                break
                        }
                        hs = govpn.NewHandshake(addr, conn, conf)
@@ -89,7 +92,10 @@ func handleTCP(conn net.Conn) {
                        continue
                }
                hs.Zero()
-               log.Println("Peer handshake finished:", addr, peer.Id.String())
+               govpn.Printf(
+                       `[handshake-completed bind="%s" addr="%s" peer="%s"]`,
+                       *bindAddr, addr, peerId.String(),
+               )
                peersByIdLock.RLock()
                addrPrev, exists := peersById[*peer.Id]
                peersByIdLock.RUnlock()
@@ -102,7 +108,7 @@ func handleTCP(conn net.Conn) {
                                tap:        tap,
                                terminator: make(chan struct{}),
                        }
-                       go peerReady(*ps)
+                       go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
                        peersByIdLock.Lock()
                        kpLock.Lock()
                        delete(peers, addrPrev)
@@ -113,7 +119,10 @@ func handleTCP(conn net.Conn) {
                        peersLock.Unlock()
                        peersByIdLock.Unlock()
                        kpLock.Unlock()
-                       log.Println("Rehandshake processed:", peer.Id.String())
+                       govpn.Printf(
+                               `[rehandshake-completed bind="%s" peer="%s"]`,
+                               *bindAddr, peerId.String(),
+                       )
                } else {
                        ifaceName, err := callUp(peer.Id, peer.Addr)
                        if err != nil {
@@ -122,7 +131,10 @@ func handleTCP(conn net.Conn) {
                        }
                        tap, err = govpn.TAPListen(ifaceName, peer.MTU)
                        if err != nil {
-                               log.Println("Unable to create TAP:", err)
+                               govpn.Printf(
+                                       `[tap-failed bind="%s" peer="%s" err="%s"]`,
+                                       *bindAddr, peerId.String(), err,
+                               )
                                peer = nil
                                break
                        }
@@ -131,7 +143,7 @@ func handleTCP(conn net.Conn) {
                                tap:        tap,
                                terminator: make(chan struct{}, 1),
                        }
-                       go peerReady(*ps)
+                       go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
                        peersLock.Lock()
                        peersByIdLock.Lock()
                        kpLock.Lock()
@@ -141,7 +153,7 @@ func handleTCP(conn net.Conn) {
                        peersLock.Unlock()
                        peersByIdLock.Unlock()
                        kpLock.Unlock()
-                       log.Println("Peer created:", peer.Id.String())
+                       govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peerId.String())
                }
                break
        }
@@ -152,8 +164,6 @@ func handleTCP(conn net.Conn) {
                return
        }
 
-       nonceExpectation := make([]byte, govpn.NonceSize)
-       peer.NonceExpectation(nonceExpectation)
        prev = 0
        var i int
        for {
@@ -171,18 +181,17 @@ func handleTCP(conn net.Conn) {
                if prev < govpn.MinPktLength {
                        continue
                }
-               i = bytes.Index(buf[:prev], nonceExpectation)
+               i = bytes.Index(buf[:prev], peer.NonceExpect)
                if i == -1 {
                        continue
                }
                if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
-                       log.Println(
-                               "Unauthenticated packet, dropping connection",
-                               addr, peer.Id.String(),
+                       govpn.Printf(
+                               `[packet-unauthenticated bind="%s" addr="%s" peer="%s"]`,
+                               *bindAddr, addr, peer.Id.String(),
                        )
                        break
                }
-               peer.NonceExpectation(nonceExpectation)
                copy(buf, buf[i+govpn.NonceSize:prev])
                prev = prev - i - govpn.NonceSize
                goto CheckMore
index d9197cfbbe0e52c8721ffb27bf4f7b818b1abe0c..98993d30a18e5204a2f8c733c9cf8ddeea5d07c9 100644 (file)
@@ -48,7 +48,7 @@ func startUDP() {
        if err != nil {
                log.Fatalln("Can not listen on UDP:", err)
        }
-       log.Println("Listening on UDP:" + *bindAddr)
+       govpn.BothPrintf(`[udp-listen bind="%s"]`, *bindAddr)
 
        udpBufs <- make([]byte, govpn.MTUMax)
        go func() {
@@ -68,7 +68,7 @@ func startUDP() {
                        buf = <-udpBufs
                        n, raddr, err = conn.ReadFromUDP(buf)
                        if err != nil {
-                               log.Println("Unexpected error when receiving", err)
+                               govpn.Printf(`[receive-failed bind="%s" err="%s"]`, *bindAddr, err)
                                break
                        }
                        addr = raddr.String()
@@ -96,7 +96,10 @@ func startUDP() {
                                goto Finished
                        }
 
-                       log.Println("Peer handshake finished:", addr, peer.Id.String())
+                       govpn.Printf(
+                               `[handshake-completed bind="%s" addr="%s" peer="%s"]`,
+                               *bindAddr, addr, peerId.String(),
+                       )
                        hs.Zero()
                        hsLock.Lock()
                        delete(handshakes, addr)
@@ -118,7 +121,7 @@ func startUDP() {
                                        terminator: make(chan struct{}),
                                }
                                go func(ps PeerState) {
-                                       peerReady(ps)
+                                       govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
                                        <-udpBufs
                                        <-udpBufs
                                }(*ps)
@@ -132,7 +135,10 @@ func startUDP() {
                                peersLock.Unlock()
                                peersByIdLock.Unlock()
                                kpLock.Unlock()
-                               log.Println("Rehandshake processed:", peer.Id.String())
+                               govpn.Printf(
+                                       `[rehandshake-completed bind="%s" peer="%s"]`,
+                                       *bindAddr, peer.Id.String(),
+                               )
                        } else {
                                go func(addr string, peer *govpn.Peer) {
                                        ifaceName, err := callUp(peer.Id, peer.Addr)
@@ -141,7 +147,10 @@ func startUDP() {
                                        }
                                        tap, err := govpn.TAPListen(ifaceName, peer.MTU)
                                        if err != nil {
-                                               log.Println("Unable to create TAP:", err)
+                                               govpn.Printf(
+                                                       `[tap-failed bind="%s" peer="%s" err="%s"]`,
+                                                       *bindAddr, peer.Id.String(), err,
+                                               )
                                                return
                                        }
                                        ps = &PeerState{
@@ -150,7 +159,7 @@ func startUDP() {
                                                terminator: make(chan struct{}),
                                        }
                                        go func(ps PeerState) {
-                                               peerReady(ps)
+                                               govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
                                                <-udpBufs
                                                <-udpBufs
                                        }(*ps)
@@ -163,19 +172,22 @@ func startUDP() {
                                        peersLock.Unlock()
                                        peersByIdLock.Unlock()
                                        kpLock.Unlock()
-                                       log.Println("Peer created:", peer.Id.String())
+                                       govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peer.Id.String())
                                }(addr, peer)
                        }
                        goto Finished
                CheckID:
                        peerId = idsCache.Find(buf[:n])
                        if peerId == nil {
-                               log.Println("Unknown identity from:", addr)
+                               govpn.Printf(`[identity-unknown bind="%s" addr="%s"]`, *bindAddr, addr)
                                goto Finished
                        }
                        conf = confs[*peerId]
                        if conf == nil {
-                               log.Println("Unable to get peer configuration:", peerId.String())
+                               govpn.Printf(
+                                       `[conf-get-failed bind="%s" peer="%s"]`,
+                                       *bindAddr, peerId.String(),
+                               )
                                goto Finished
                        }
                        hs = govpn.NewHandshake(
index bdea0791f3d165101ece4219997d1edc21d23665..d5dcfa584d4686fd20c74f2f6b1dae421fbab4b8 100644 (file)
@@ -23,6 +23,7 @@ import (
        "bytes"
        "flag"
        "fmt"
+       "io"
        "log"
 
        "cypherpunks.ru/govpn"
@@ -53,7 +54,7 @@ func main() {
        }
        if *verifier == "" {
                id := new([govpn.IDSize]byte)
-               if _, err := govpn.Rand.Read(id[:]); err != nil {
+               if _, err = io.ReadFull(govpn.Rand, id[:]); err != nil {
                        log.Fatalln(err)
                }
                pid := govpn.PeerId(*id)
index 24cf4524f4eb19be623bbbbbe53565704aa44452..cbb28e98efa1d325c2222ed47b64280ef2881aa1 100644 (file)
@@ -22,6 +22,7 @@ import (
        "bytes"
        "crypto/rand"
        "encoding/binary"
+       "io"
        "testing"
        "testing/quick"
 )
@@ -31,7 +32,7 @@ var (
 )
 
 func init() {
-       rand.Read(testKey[:])
+       io.ReadFull(rand.Reader, testKey[:])
 }
 
 func TestSymmetric(t *testing.T) {
@@ -66,8 +67,8 @@ func TestSmallSize(t *testing.T) {
 func BenchmarkChaff(b *testing.B) {
        nonce := make([]byte, 8)
        data := make([]byte, 16)
-       rand.Read(nonce)
-       rand.Read(data)
+       io.ReadFull(rand.Reader, nonce)
+       io.ReadFull(rand.Reader, data)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                Chaff(testKey, nonce, data)
@@ -77,8 +78,8 @@ func BenchmarkChaff(b *testing.B) {
 func BenchmarkWinnow(b *testing.B) {
        nonce := make([]byte, 8)
        data := make([]byte, 16)
-       rand.Read(nonce)
-       rand.Read(data)
+       io.ReadFull(rand.Reader, nonce)
+       io.ReadFull(rand.Reader, data)
        chaffed := Chaff(testKey, nonce, data)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
index 04ec87b9678cf2dfa10c576c84bfc66bbe4ef493..3d0b7918b26044353c83a408fbdb1b7d4c0750a8 100644 (file)
@@ -20,7 +20,6 @@ package govpn
 
 import (
        "crypto/rand"
-       "errors"
        "io"
        "net"
 )
@@ -37,18 +36,9 @@ func (egdPath EGDRand) Read(b []byte) (int, error) {
        if err != nil {
                return 0, err
        }
+       defer conn.Close()
        conn.Write([]byte{0x02, byte(len(b))})
-       read, err := conn.Read(b)
-       if err != nil {
-               conn.Close()
-               return read, err
-       }
-       if read != len(b) {
-               conn.Close()
-               return read, errors.New("Got less bytes than expected from EGD")
-       }
-       conn.Close()
-       return read, nil
+       return io.ReadFull(conn, b)
 }
 
 func EGDInit(path string) {
index 30eb4a5e33d9446f07c646c03bede769c4043339..f9d9bbfe075e587e6fd16fd9e023a62a5f343537 100644 (file)
@@ -19,6 +19,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package govpn
 
 import (
+       "io"
+
        "cypherpunks.ru/govpn/aont"
        "cypherpunks.ru/govpn/cnw"
 )
@@ -36,7 +38,7 @@ const (
 func EnclessEncode(authKey *[32]byte, nonce, in []byte) ([]byte, error) {
        r := new([aont.RSize]byte)
        var err error
-       if _, err = Rand.Read(r[:]); err != nil {
+       if _, err = io.ReadFull(Rand, r[:]); err != nil {
                return nil, err
        }
        aonted, err := aont.Encode(r, in)
index 20b0df63281e481a254eee1a25bf86a81bf8f0a6..1f9fece30e3207999ec3a01a6d779888299d15e6 100644 (file)
@@ -20,8 +20,8 @@ package govpn
 
 import (
        "bytes"
-       "crypto/rand"
        "encoding/binary"
+       "io"
        "testing"
        "testing/quick"
 )
@@ -31,7 +31,7 @@ var (
 )
 
 func init() {
-       rand.Read(testKey[:])
+       io.ReadFull(Rand, testKey[:])
 }
 
 func TestEnclessSymmetric(t *testing.T) {
@@ -56,8 +56,8 @@ func TestEnclessSymmetric(t *testing.T) {
 func BenchmarkEnclessEncode(b *testing.B) {
        nonce := make([]byte, 8)
        data := make([]byte, 128)
-       rand.Read(nonce)
-       rand.Read(data)
+       io.ReadFull(Rand, nonce)
+       io.ReadFull(Rand, data)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                EnclessEncode(testKey, nonce, data)
@@ -67,8 +67,8 @@ func BenchmarkEnclessEncode(b *testing.B) {
 func BenchmarkEnclessDecode(b *testing.B) {
        nonce := make([]byte, 8)
        data := make([]byte, 128)
-       rand.Read(nonce)
-       rand.Read(data)
+       io.ReadFull(Rand, nonce)
+       io.ReadFull(Rand, data)
        encoded, _ := EnclessEncode(testKey, nonce, data)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
index 4f080eb34647c1494c3b78f6b2e9bdabf082177f..9c536b329371fe7a5a946229407285214dbd5e30 100644 (file)
@@ -30,7 +30,6 @@ import (
        "github.com/dchest/blake2b"
        "golang.org/x/crypto/curve25519"
        "golang.org/x/crypto/salsa20"
-       "golang.org/x/crypto/xtea"
 )
 
 const (
@@ -102,7 +101,7 @@ func dhKeypairGen() (*[32]byte, *[32]byte) {
        repr := new([32]byte)
        reprFound := false
        for !reprFound {
-               if _, err := Rand.Read(priv[:]); err != nil {
+               if _, err := io.ReadFull(Rand, priv[:]); err != nil {
                        log.Fatalln("Error reading random for DH private key:", err)
                }
                reprFound = extra25519.ScalarBaseMult(pub, repr, priv)
@@ -134,14 +133,12 @@ func NewHandshake(addr string, conn io.Writer, conf *PeerConf) *Handshake {
 
 // Generate ID tag from client identification and data.
 func idTag(id *PeerId, timeSync int, data []byte) []byte {
-       ciph, err := xtea.NewCipher(id[:])
-       if err != nil {
-               panic(err)
-       }
-       enc := make([]byte, xtea.BlockSize)
+       enc := make([]byte, 8)
        copy(enc, data)
        AddTimeSync(timeSync, enc)
-       ciph.Encrypt(enc, enc)
+       mac := blake2b.NewMAC(8, id[:])
+       mac.Write(enc)
+       mac.Sum(enc[:0])
        return enc
 }
 
@@ -154,12 +151,12 @@ func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake {
        state.dhPriv, dhPubRepr = dhKeypairGen()
 
        state.rNonce = new([RSize]byte)
-       if _, err := Rand.Read(state.rNonce[:]); err != nil {
+       if _, err := io.ReadFull(Rand, state.rNonce[:]); err != nil {
                log.Fatalln("Error reading random for nonce:", err)
        }
        var enc []byte
        if conf.Noise {
-               enc = make([]byte, conf.MTU-xtea.BlockSize-RSize)
+               enc = make([]byte, conf.MTU-8-RSize)
        } else {
                enc = make([]byte, 32)
        }
@@ -197,7 +194,7 @@ func (h *Handshake) Server(data []byte) *Peer {
                        out, err := EnclessDecode(
                                h.dsaPubH,
                                h.rNonce[:],
-                               data[RSize:len(data)-xtea.BlockSize],
+                               data[RSize:len(data)-8],
                        )
                        if err != nil {
                                log.Println("Unable to decode packet from", h.addr, err)
@@ -205,12 +202,7 @@ func (h *Handshake) Server(data []byte) *Peer {
                        }
                        copy(cDHRepr[:], out)
                } else {
-                       salsa20.XORKeyStream(
-                               cDHRepr[:],
-                               data[RSize:RSize+32],
-                               h.rNonce[:],
-                               h.dsaPubH,
-                       )
+                       salsa20.XORKeyStream(cDHRepr[:], data[RSize:RSize+32], h.rNonce[:], h.dsaPubH)
                }
 
                // Generate DH keypair
@@ -238,18 +230,18 @@ func (h *Handshake) Server(data []byte) *Peer {
 
                // Generate R* and encrypt them
                h.rServer = new([RSize]byte)
-               if _, err = Rand.Read(h.rServer[:]); err != nil {
+               if _, err = io.ReadFull(Rand, h.rServer[:]); err != nil {
                        log.Fatalln("Error reading random for R:", err)
                }
                h.sServer = new([SSize]byte)
-               if _, err = Rand.Read(h.sServer[:]); err != nil {
+               if _, err = io.ReadFull(Rand, h.sServer[:]); err != nil {
                        log.Fatalln("Error reading random for S:", err)
                }
                var encRs []byte
                if h.Conf.Noise && !h.Conf.Encless {
-                       encRs = make([]byte, h.Conf.MTU-len(encPub)-xtea.BlockSize)
+                       encRs = make([]byte, h.Conf.MTU-len(encPub)-8)
                } else if h.Conf.Encless {
-                       encRs = make([]byte, h.Conf.MTU-xtea.BlockSize)
+                       encRs = make([]byte, h.Conf.MTU-8)
                } else {
                        encRs = make([]byte, RSize+SSize)
                }
@@ -278,7 +270,7 @@ func (h *Handshake) Server(data []byte) *Peer {
                        dec, err = EnclessDecode(
                                h.key,
                                h.rNonceNext(1),
-                               data[:len(data)-xtea.BlockSize],
+                               data[:len(data)-8],
                        )
                        if err != nil {
                                log.Println("Unable to decode packet from", h.addr, err)
@@ -308,7 +300,7 @@ func (h *Handshake) Server(data []byte) *Peer {
                // Send final answer to client
                var enc []byte
                if h.Conf.Noise {
-                       enc = make([]byte, h.Conf.MTU-xtea.BlockSize)
+                       enc = make([]byte, h.Conf.MTU-8)
                } else {
                        enc = make([]byte, RSize)
                }
@@ -364,12 +356,7 @@ func (h *Handshake) Client(data []byte) *Peer {
                        }
                        copy(sDHRepr[:], tmp[:32])
                } else {
-                       salsa20.XORKeyStream(
-                               sDHRepr[:],
-                               data[:32],
-                               h.rNonceNext(1),
-                               h.dsaPubH,
-                       )
+                       salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH)
                }
 
                // Compute shared key
@@ -384,7 +371,7 @@ func (h *Handshake) Client(data []byte) *Peer {
                        tmp, err = EnclessDecode(
                                h.key,
                                h.rNonce[:],
-                               data[len(data)/2:len(data)-xtea.BlockSize],
+                               data[len(data)/2:len(data)-8],
                        )
                        if err != nil {
                                log.Println("Unable to decode packet from", h.addr, err)
@@ -394,30 +381,25 @@ func (h *Handshake) Client(data []byte) *Peer {
                        copy(h.sServer[:], tmp[RSize:RSize+SSize])
                } else {
                        decRs := make([]byte, RSize+SSize)
-                       salsa20.XORKeyStream(
-                               decRs,
-                               data[SSize:SSize+RSize+SSize],
-                               h.rNonce[:],
-                               h.key,
-                       )
+                       salsa20.XORKeyStream(decRs, data[SSize:SSize+RSize+SSize], h.rNonce[:], h.key)
                        copy(h.rServer[:], decRs[:RSize])
                        copy(h.sServer[:], decRs[RSize:])
                }
 
                // Generate R* and signature and encrypt them
                h.rClient = new([RSize]byte)
-               if _, err = Rand.Read(h.rClient[:]); err != nil {
+               if _, err = io.ReadFull(Rand, h.rClient[:]); err != nil {
                        log.Fatalln("Error reading random for R:", err)
                }
                h.sClient = new([SSize]byte)
-               if _, err = Rand.Read(h.sClient[:]); err != nil {
+               if _, err = io.ReadFull(Rand, h.sClient[:]); err != nil {
                        log.Fatalln("Error reading random for S:", err)
                }
                sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:])
 
                var enc []byte
                if h.Conf.Noise {
-                       enc = make([]byte, h.Conf.MTU-xtea.BlockSize)
+                       enc = make([]byte, h.Conf.MTU-8)
                } else {
                        enc = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
                }
@@ -445,11 +427,7 @@ func (h *Handshake) Client(data []byte) *Peer {
                // Decrypt rClient
                var dec []byte
                if h.Conf.Encless {
-                       dec, err = EnclessDecode(
-                               h.key,
-                               h.rNonceNext(2),
-                               data[:len(data)-xtea.BlockSize],
-                       )
+                       dec, err = EnclessDecode(h.key, h.rNonceNext(2), data[:len(data)-8])
                        if err != nil {
                                log.Println("Unable to decode packet from", h.addr, err)
                                return nil
index f85631b01839639b8bf981572f4c8b6db106c984..4dc74eed0e330f4a3ec73570e0c9d73b2503baee 100644 (file)
@@ -22,11 +22,12 @@ import (
        "crypto/subtle"
        "encoding/base64"
        "encoding/binary"
+       "hash"
        "log"
        "sync"
        "time"
 
-       "golang.org/x/crypto/xtea"
+       "github.com/dchest/blake2b"
 )
 
 const (
@@ -43,42 +44,42 @@ func (id PeerId) MarshalJSON() ([]byte, error) {
        return []byte(`"` + id.String() + `"`), nil
 }
 
-type CipherAndTimeSync struct {
-       c *xtea.Cipher
-       t int
+type MACAndTimeSync struct {
+       mac hash.Hash
+       ts  int
+       l   sync.Mutex
 }
 
-type CipherCache struct {
-       c map[PeerId]*CipherAndTimeSync
-       l sync.RWMutex
+type MACCache struct {
+       cache map[PeerId]*MACAndTimeSync
+       l     sync.RWMutex
 }
 
-func NewCipherCache() *CipherCache {
-       return &CipherCache{c: make(map[PeerId]*CipherAndTimeSync)}
+func NewMACCache() *MACCache {
+       return &MACCache{cache: make(map[PeerId]*MACAndTimeSync)}
 }
 
-// Remove disappeared keys, add missing ones with initialized ciphers.
-func (cc *CipherCache) Update(peers *map[PeerId]*PeerConf) {
-       cc.l.Lock()
-       for pid, _ := range cc.c {
+// Remove disappeared keys, add missing ones with initialized MACs.
+func (mc *MACCache) Update(peers *map[PeerId]*PeerConf) {
+       mc.l.Lock()
+       for pid, _ := range mc.cache {
                if _, exists := (*peers)[pid]; !exists {
                        log.Println("Cleaning key:", pid)
-                       delete(cc.c, pid)
+                       delete(mc.cache, pid)
                }
        }
        for pid, pc := range *peers {
-               if _, exists := cc.c[pid]; exists {
-                       cc.c[pid].t = pc.TimeSync
+               if _, exists := mc.cache[pid]; exists {
+                       mc.cache[pid].ts = pc.TimeSync
                } else {
                        log.Println("Adding key", pid)
-                       cipher, err := xtea.NewCipher(pid[:])
-                       if err != nil {
-                               panic(err)
+                       mc.cache[pid] = &MACAndTimeSync{
+                               mac: blake2b.NewMAC(8, pid[:]),
+                               ts:  pc.TimeSync,
                        }
-                       cc.c[pid] = &CipherAndTimeSync{cipher, pc.TimeSync}
                }
        }
-       cc.l.Unlock()
+       mc.l.Unlock()
 }
 
 // If timeSync > 0, then XOR timestamp with the data.
@@ -93,24 +94,29 @@ func AddTimeSync(ts int, data []byte) {
        }
 }
 
-// Try to find peer's identity (that equals to an encryption key)
+// Try to find peer's identity (that equals to MAC)
 // by taking first blocksize sized bytes from data at the beginning
 // as plaintext and last bytes as cyphertext.
-func (cc *CipherCache) Find(data []byte) *PeerId {
-       if len(data) < xtea.BlockSize*2 {
+func (mc *MACCache) Find(data []byte) *PeerId {
+       if len(data) < 8*2 {
                return nil
        }
-       buf := make([]byte, xtea.BlockSize)
-       cc.l.RLock()
-       for pid, ct := range cc.c {
-               ct.c.Decrypt(buf, data[len(data)-xtea.BlockSize:])
-               AddTimeSync(ct.t, buf)
-               if subtle.ConstantTimeCompare(buf, data[:xtea.BlockSize]) == 1 {
+       buf := make([]byte, 8)
+       mc.l.RLock()
+       for pid, mt := range mc.cache {
+               copy(buf, data)
+               AddTimeSync(mt.ts, buf)
+               mt.l.Lock()
+               mt.mac.Reset()
+               mt.mac.Write(buf)
+               mt.mac.Sum(buf[:0])
+               mt.l.Unlock()
+               if subtle.ConstantTimeCompare(buf, data[len(data)-8:]) == 1 {
                        ppid := PeerId(pid)
-                       cc.l.RUnlock()
+                       mc.l.RUnlock()
                        return &ppid
                }
        }
-       cc.l.RUnlock()
+       mc.l.RUnlock()
        return nil
 }
diff --git a/src/cypherpunks.ru/govpn/logger.go b/src/cypherpunks.ru/govpn/logger.go
new file mode 100644 (file)
index 0000000..b110ce3
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package govpn
+
+import (
+       "log"
+       "log/syslog"
+)
+
+var (
+       sysloger *log.Logger
+)
+
+// Enable logging to syslog, instead of default stdout log.
+func SyslogEnable() {
+       var err error
+       sysloger, err = syslog.NewLogger(syslog.LOG_INFO, 0)
+       if err != nil {
+               log.Fatalln(err)
+       }
+}
+
+// Call either syslog-related logger.Printf if SyslogEnabled,
+// default log.Printf otherwise.
+func Printf(f string, v ...interface{}) {
+       if sysloger == nil {
+               log.Printf(f, v...)
+       } else {
+               sysloger.Printf(f, v...)
+       }
+}
+
+// Call both default log.Printf and syslog-related one.
+func BothPrintf(f string, v ...interface{}) {
+       log.Printf(f, v...)
+       if sysloger != nil {
+               sysloger.Printf(f, v...)
+       }
+}
index 5160bd4cc6b2f7b172d3441b90762faf73f71ee1..fc361790237df8cfa17876864089757018c0faa7 100644 (file)
@@ -20,6 +20,7 @@ package govpn
 
 import (
        "bytes"
+       "crypto/subtle"
        "encoding/binary"
        "io"
        "log"
@@ -27,14 +28,14 @@ import (
        "sync/atomic"
        "time"
 
+       "github.com/dchest/blake2b"
        "golang.org/x/crypto/poly1305"
        "golang.org/x/crypto/salsa20"
-       "golang.org/x/crypto/xtea"
 )
 
 const (
        NonceSize       = 8
-       NonceBucketSize = 128
+       NonceBucketSize = 256
        TagSize         = poly1305.TagSize
        // S20BS is Salsa20's internal blocksize in bytes
        S20BS = 64
@@ -48,19 +49,23 @@ const (
        PadByte = byte(0x80)
 )
 
-func newNonceCipher(key *[32]byte) *xtea.Cipher {
-       nonceKey := make([]byte, 16)
-       salsa20.XORKeyStream(
-               nonceKey,
-               make([]byte, 32),
-               make([]byte, xtea.BlockSize),
-               key,
-       )
-       ciph, err := xtea.NewCipher(nonceKey)
-       if err != nil {
-               panic(err)
-       }
-       return ciph
+func newNonces(key *[32]byte, i uint64) chan *[NonceSize]byte {
+       macKey := make([]byte, 32)
+       salsa20.XORKeyStream(macKey, make([]byte, 32), make([]byte, 8), key)
+       mac := blake2b.NewMAC(NonceSize, macKey)
+       nonces := make(chan *[NonceSize]byte, NonceBucketSize*3)
+       go func() {
+               for {
+                       buf := new([NonceSize]byte)
+                       binary.BigEndian.PutUint64(buf[:], i)
+                       mac.Write(buf[:])
+                       mac.Sum(buf[:0])
+                       nonces <- buf
+                       mac.Reset()
+                       i += 2
+               }
+       }()
+       return nonces
 }
 
 type Peer struct {
@@ -88,25 +93,12 @@ type Peer struct {
        Encless     bool
        MTU         int
 
-       // Cryptography related
-       Key          *[SSize]byte `json:"-"`
-       NonceCipher  *xtea.Cipher `json:"-"`
-       nonceRecv    uint64
-       nonceLatest  uint64
-       nonceOur     uint64
-       NonceExpect  uint64 `json:"-"`
-       nonceBucket0 map[uint64]struct{}
-       nonceBucket1 map[uint64]struct{}
-       nonceFound0  bool
-       nonceFound1  bool
-       nonceBucketN int32
+       key *[SSize]byte `json:"-"`
 
        // Timers
-       Timeout       time.Duration `json:"-"`
-       Established   time.Time
-       LastPing      time.Time
-       LastSent      time.Time
-       willSentCycle time.Time
+       Timeout     time.Duration `json:"-"`
+       Established time.Time
+       LastPing    time.Time
 
        // Receiver
        BusyR    sync.Mutex `json:"-"`
@@ -115,13 +107,24 @@ type Peer struct {
        keyAuthR *[SSize]byte
        pktSizeR int
 
+       // UDP-related
+       noncesR      chan *[NonceSize]byte
+       nonceRecv    [NonceSize]byte
+       nonceBucketL map[[NonceSize]byte]struct{}
+       nonceBucketM map[[NonceSize]byte]struct{}
+       nonceBucketH map[[NonceSize]byte]struct{}
+
+       // TCP-related
+       NonceExpect  []byte `json:"-"`
+       noncesExpect chan *[NonceSize]byte
+
        // Transmitter
        BusyT    sync.Mutex `json:"-"`
        bufT     []byte
        tagT     *[TagSize]byte
        keyAuthT *[SSize]byte
        frameT   []byte
-       now      time.Time
+       noncesT  chan *[NonceSize]byte
 }
 
 func (p *Peer) String() string {
@@ -132,7 +135,7 @@ func (p *Peer) String() string {
 func (p *Peer) Zero() {
        p.BusyT.Lock()
        p.BusyR.Lock()
-       SliceZero(p.Key[:])
+       SliceZero(p.key[:])
        SliceZero(p.bufR)
        SliceZero(p.bufT)
        SliceZero(p.keyAuthR[:])
@@ -141,11 +144,6 @@ func (p *Peer) Zero() {
        p.BusyR.Unlock()
 }
 
-func (p *Peer) NonceExpectation(buf []byte) {
-       binary.BigEndian.PutUint64(buf, p.NonceExpect)
-       p.NonceCipher.Encrypt(buf, buf)
-}
-
 func cprCycleCalculate(conf *PeerConf) time.Duration {
        if conf.CPR == 0 {
                return time.Duration(0)
@@ -177,6 +175,7 @@ func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[S
                bufSize += EnclessEnlargeSize
                noiseEnable = true
        }
+
        peer := Peer{
                Addr: addr,
                Id:   conf.Id,
@@ -188,10 +187,7 @@ func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[S
                Encless:     conf.Encless,
                MTU:         conf.MTU,
 
-               Key:          key,
-               NonceCipher:  newNonceCipher(key),
-               nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
-               nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
+               key: key,
 
                Timeout:     timeout,
                Established: now,
@@ -204,15 +200,39 @@ func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[S
                keyAuthR: new([SSize]byte),
                keyAuthT: new([SSize]byte),
        }
+
        if isClient {
-               peer.nonceOur = 1
-               peer.NonceExpect = 0 + 2
+               peer.noncesT = newNonces(peer.key, 1 + 2)
+               peer.noncesR = newNonces(peer.key, 0 + 2)
+               peer.noncesExpect = newNonces(peer.key, 0 + 2)
        } else {
-               peer.nonceOur = 0
-               peer.NonceExpect = 1 + 2
+               peer.noncesT = newNonces(peer.key, 0 + 2)
+               peer.noncesR = newNonces(peer.key, 1 + 2)
+               peer.noncesExpect = newNonces(peer.key, 1 + 2)
        }
-       return &peer
 
+       peer.NonceExpect = make([]byte, NonceSize)
+       nonce := <-peer.noncesExpect
+       copy(peer.NonceExpect, nonce[:])
+
+       var i int
+       peer.nonceBucketL = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
+       for i = 0; i < NonceBucketSize; i++ {
+               nonce = <-peer.noncesR
+               peer.nonceBucketL[*nonce] = struct{}{}
+       }
+       peer.nonceBucketM = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
+       for i = 0; i < NonceBucketSize; i++ {
+               nonce = <-peer.noncesR
+               peer.nonceBucketM[*nonce] = struct{}{}
+       }
+       peer.nonceBucketH = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
+       for i = 0; i < NonceBucketSize; i++ {
+               nonce = <-peer.noncesR
+               peer.nonceBucketH[*nonce] = struct{}{}
+       }
+
+       return &peer
 }
 
 // Process incoming Ethernet packet.
@@ -224,17 +244,11 @@ func (p *Peer) EthProcess(data []byte) {
                log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
                return
        }
-       p.now = time.Now()
        p.BusyT.Lock()
 
        // Zero size is a heartbeat packet
        SliceZero(p.bufT)
        if len(data) == 0 {
-               // If this heartbeat is necessary
-               if !p.LastSent.Add(p.Timeout).Before(p.now) {
-                       p.BusyT.Unlock()
-                       return
-               }
                p.bufT[S20BS+0] = PadByte
                p.HeartbeatSent++
        } else {
@@ -252,17 +266,12 @@ func (p *Peer) EthProcess(data []byte) {
        } else {
                p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
        }
-       p.nonceOur += 2
-       binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
-       p.NonceCipher.Encrypt(
-               p.frameT[len(p.frameT)-NonceSize:],
-               p.frameT[len(p.frameT)-NonceSize:],
-       )
+       copy(p.frameT[len(p.frameT)-NonceSize:], (<-p.noncesT)[:])
        var out []byte
        if p.Encless {
                var err error
                out, err = EnclessEncode(
-                       p.Key,
+                       p.key,
                        p.frameT[len(p.frameT)-NonceSize:],
                        p.frameT[:len(p.frameT)-NonceSize],
                )
@@ -275,7 +284,7 @@ func (p *Peer) EthProcess(data []byte) {
                        p.bufT[:S20BS+len(p.frameT)-NonceSize],
                        p.bufT[:S20BS+len(p.frameT)-NonceSize],
                        p.frameT[len(p.frameT)-NonceSize:],
-                       p.Key,
+                       p.key,
                )
                copy(p.keyAuthT[:], p.bufT[:SSize])
                poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
@@ -283,16 +292,6 @@ func (p *Peer) EthProcess(data []byte) {
                out = append(p.tagT[:], p.frameT...)
        }
        p.FramesOut++
-
-       if p.CPRCycle != time.Duration(0) {
-               p.willSentCycle = p.LastSent.Add(p.CPRCycle)
-               if p.willSentCycle.After(p.now) {
-                       time.Sleep(p.willSentCycle.Sub(p.now))
-                       p.now = p.willSentCycle
-               }
-       }
-
-       p.LastSent = p.now
        p.Conn.Write(out)
        p.BusyT.Unlock()
 }
@@ -309,7 +308,7 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
        if p.Encless {
                var err error
                out, err = EnclessDecode(
-                       p.Key,
+                       p.key,
                        data[len(data)-NonceSize:],
                        data[:len(data)-NonceSize],
                )
@@ -327,7 +326,7 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
                        p.bufR[:S20BS+len(data)-TagSize-NonceSize],
                        p.bufR[:S20BS+len(data)-TagSize-NonceSize],
                        data[len(data)-NonceSize:],
-                       p.Key,
+                       p.key,
                )
                copy(p.keyAuthR[:], p.bufR[:SSize])
                copy(p.tagR[:], data[:TagSize])
@@ -339,41 +338,45 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
                out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
        }
 
-       // Check if received nonce is known to us in either of two buckets.
-       // If yes, then this is ignored duplicate.
-       // Check from the oldest bucket, as in most cases this will result
-       // in constant time check.
-       // If Bucket0 is filled, then it becomes Bucket1.
-       p.NonceCipher.Decrypt(
-               data[len(data)-NonceSize:],
-               data[len(data)-NonceSize:],
-       )
-       p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
        if reorderable {
-               _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
-               _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
-               if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
+               copy(p.nonceRecv[:], data[len(data)-NonceSize:])
+               _, foundL := p.nonceBucketL[p.nonceRecv]
+               _, foundM := p.nonceBucketM[p.nonceRecv]
+               _, foundH := p.nonceBucketH[p.nonceRecv]
+               // If found is none of buckets: either it is too old,
+               // or too new (many packets were lost)
+               if !(foundL || foundM || foundH) {
                        p.FramesDup++
                        p.BusyR.Unlock()
                        return false
                }
-               p.nonceBucket0[p.nonceRecv] = struct{}{}
-               p.nonceBucketN++
-               if p.nonceBucketN == NonceBucketSize {
-                       p.nonceBucket1 = p.nonceBucket0
-                       p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
-                       p.nonceBucketN = 0
+               // Delete seen nonce
+               if foundL {
+                       delete(p.nonceBucketL, p.nonceRecv)
+               }
+               if foundM {
+                       delete(p.nonceBucketM, p.nonceRecv)
+               }
+               if foundH {
+                       delete(p.nonceBucketH, p.nonceRecv)
+               }
+               // If we are dealing with the latest bucket, create the new one
+               if foundH {
+                       p.nonceBucketL, p.nonceBucketM = p.nonceBucketM, p.nonceBucketH
+                       p.nonceBucketH = make(map[[NonceSize]byte]struct{})
+                       var nonce *[NonceSize]byte
+                       for i := 0; i < NonceBucketSize; i++ {
+                               nonce = <-p.noncesR
+                               p.nonceBucketH[*nonce] = struct{}{}
+                       }
                }
        } else {
-               if p.nonceRecv != p.NonceExpect {
+               if subtle.ConstantTimeCompare(data[len(data)-NonceSize:], p.NonceExpect) != 1 {
                        p.FramesDup++
                        p.BusyR.Unlock()
                        return false
                }
-               p.NonceExpect += 2
-       }
-       if p.nonceRecv > p.nonceLatest {
-               p.nonceLatest = p.nonceRecv
+               copy(p.NonceExpect, (<-p.noncesExpect)[:])
        }
 
        p.FramesIn++
@@ -402,3 +405,47 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
        p.BusyR.Unlock()
        return true
 }
+
+func PeerTapProcessor(peer *Peer, tap *TAP, terminator chan struct{}) {
+       var data []byte
+       var now time.Time
+       lastSent := time.Now()
+       heartbeat := time.NewTicker(peer.Timeout)
+       if peer.CPRCycle == time.Duration(0) {
+       RawProcessor:
+               for {
+                       select {
+                       case <-terminator:
+                               break RawProcessor
+                       case <-heartbeat.C:
+                               now = time.Now()
+                               if lastSent.Add(peer.Timeout).Before(now) {
+                                       peer.EthProcess(nil)
+                                       lastSent = now
+                               }
+                       case data = <-tap.Sink:
+                               peer.EthProcess(data)
+                               lastSent = time.Now()
+                       }
+               }
+       } else {
+       CPRProcessor:
+               for {
+                       data = nil
+                       select {
+                       case <-terminator:
+                               break CPRProcessor
+                       case data = <-tap.Sink:
+                               peer.EthProcess(data)
+                       default:
+                       }
+                       if data == nil {
+                               peer.EthProcess(nil)
+                       }
+                       time.Sleep(peer.CPRCycle)
+               }
+       }
+       close(terminator)
+       peer.Zero()
+       heartbeat.Stop()
+}
index 7fa8aab8438a7fd04eaccf53972064edd91b8792..4a0c7523f6a71abc67061c82102116b2fa478a4f 100644 (file)
@@ -51,12 +51,16 @@ func init() {
                MTU:     MTUDefault,
                Timeout: time.Second * time.Duration(TimeoutDefault),
        }
-       testPeer = newPeer(true, "foo", Dummy{&testCt}, testConf, new([SSize]byte))
        testPt = make([]byte, 789)
 }
 
+func testPeerNew() {
+       testPeer = newPeer(true, "foo", Dummy{&testCt}, testConf, new([SSize]byte))
+}
+
 func TestTransportSymmetric(t *testing.T) {
-       peerd := newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
+       testPeerNew()
+       peerd := newPeer(false, "foo", Dummy{nil}, testConf, new([SSize]byte))
        f := func(payload []byte) bool {
                if len(payload) == 0 {
                        return true
@@ -70,7 +74,8 @@ func TestTransportSymmetric(t *testing.T) {
 }
 
 func TestTransportSymmetricNoise(t *testing.T) {
-       peerd := newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
+       testPeerNew()
+       peerd := newPeer(false, "foo", Dummy{nil}, testConf, new([SSize]byte))
        testPeer.NoiseEnable = true
        peerd.NoiseEnable = true
        f := func(payload []byte) bool {
@@ -87,7 +92,8 @@ func TestTransportSymmetricNoise(t *testing.T) {
 }
 
 func TestTransportSymmetricEncless(t *testing.T) {
-       peerd := newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
+       testPeerNew()
+       peerd := newPeer(false, "foo", Dummy{nil}, testConf, new([SSize]byte))
        testPeer.Encless = true
        testPeer.NoiseEnable = true
        peerd.Encless = true
@@ -119,10 +125,14 @@ func BenchmarkDec(b *testing.B) {
        testPeer = newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
        orig := make([]byte, len(testCt))
        copy(orig, testCt)
+       nonce := new([NonceSize]byte)
+       copy(nonce[:], testCt[len(testCt)-NonceSize:])
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
-               testPeer.nonceBucket0 = make(map[uint64]struct{}, 1)
-               testPeer.nonceBucket1 = make(map[uint64]struct{}, 1)
+               testPeer.nonceBucketL = make(map[[NonceSize]byte]struct{}, 1)
+               testPeer.nonceBucketM = make(map[[NonceSize]byte]struct{}, 1)
+               testPeer.nonceBucketH = make(map[[NonceSize]byte]struct{}, 1)
+               testPeer.nonceBucketL[*nonce] = struct{}{}
                copy(testCt, orig)
                if !testPeer.PktProcess(testCt, Dummy{nil}, true) {
                        b.Fail()