From: Sergey Matveev Date: Tue, 10 May 2016 08:21:22 +0000 (+0300) Subject: Merge branch 'develop' X-Git-Tag: 5.8^0 X-Git-Url: http://www.git.cypherpunks.ru/?p=govpn.git;a=commitdiff_plain;h=4cc7cf27a64355bbe1f64418a55e860baeb63ac0;hp=cee89cfdfbc6b2b429d56f0b420bc5b981e475b8 Merge branch 'develop' --- diff --git a/TODO b/TODO index 2ecafb0..3c1c91b 100644 --- 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 760606e..3659ea2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.7 +5.8 diff --git a/common.mk b/common.mk index bd2f3a4..99e1c0d 100644 --- 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) diff --git a/doc/about.ru.texi b/doc/about.ru.texi index 5bb7995..aa89817 100644 --- a/doc/about.ru.texi +++ b/doc/about.ru.texi @@ -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} с простым кодом, ориентированным на лёгкость чтения и анализа. diff --git a/doc/about.texi b/doc/about.texi index a3ed945..833c516 100644 --- a/doc/about.texi +++ b/doc/about.texi @@ -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. diff --git a/doc/client.texi b/doc/client.texi index b6d8aab..d3a7f41 100644 --- a/doc/client.texi +++ b/doc/client.texi @@ -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 diff --git a/doc/developer.texi b/doc/developer.texi index 4293f80..f2fe00e 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -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 diff --git a/doc/download.texi b/doc/download.texi index aeab481..721e4f9 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -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} diff --git a/doc/encless.texi b/doc/encless.texi index 6d44191..6d694b2 100644 --- a/doc/encless.texi +++ b/doc/encless.texi @@ -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. diff --git a/doc/glossary.texi b/doc/glossary.texi index 8fed167..3cf8d90 100644 --- a/doc/glossary.texi +++ b/doc/glossary.texi @@ -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 diff --git a/doc/handshake.texi b/doc/handshake.texi index f19fde0..75a6508 100644 --- a/doc/handshake.texi +++ b/doc/handshake.texi @@ -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. diff --git a/doc/handshake.txt b/doc/handshake.txt index 43ed4ff..50b479a 100644 --- a/doc/handshake.txt +++ b/doc/handshake.txt @@ -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 diff --git a/doc/media.texi b/doc/media.texi index b129c0d..5c30def 100644 --- a/doc/media.texi +++ b/doc/media.texi @@ -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) diff --git a/doc/news.ru.texi b/doc/news.ru.texi index 362ada0..6ea4181 100644 --- a/doc/news.ru.texi +++ b/doc/news.ru.texi @@ -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 diff --git a/doc/news.texi b/doc/news.texi index 1956730..f363b84 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -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 diff --git a/doc/server.texi b/doc/server.texi index b3c641e..63f3239 100644 --- a/doc/server.texi +++ b/doc/server.texi @@ -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 index 0000000..82ba7b3 --- /dev/null +++ b/doc/syslog.texi @@ -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. diff --git a/doc/transport.texi b/doc/transport.texi index 4b8413b..1518d6f 100644 --- a/doc/transport.texi +++ b/doc/transport.texi @@ -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 diff --git a/src/cypherpunks.ru/govpn/aont/aont_test.go b/src/cypherpunks.ru/govpn/aont/aont_test.go index 93b7db5..0ef140c 100644 --- a/src/cypherpunks.ru/govpn/aont/aont_test.go +++ b/src/cypherpunks.ru/govpn/aont/aont_test.go @@ -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++ { diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go b/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go index 58d5ce7..3d8cbe6 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-client/main.go @@ -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: diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go b/src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go index a4259f3..0a6729c 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-client/proxy.go @@ -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) } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go b/src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go index 4b11eac..57a412d 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-client/tcp.go @@ -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 diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go b/src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go index acacbd1..96dbfba 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-client/udp.go @@ -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{}{} diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go index f7435dd..7c2dd41 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/common.go @@ -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 } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go index e486ec9..ea61c81 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/conf.go @@ -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) } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go index 606caf0..c70e82a 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/main.go @@ -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) diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go index f1f5e09..02c1f0f 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/proxy.go @@ -19,8 +19,9 @@ along with this program. If not, see . 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()) } diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go index 6ca6967..bfb4709 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/tcp.go @@ -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 diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go b/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go index d9197cf..98993d3 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-server/udp.go @@ -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( diff --git a/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go b/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go index bdea079..d5dcfa5 100644 --- a/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go +++ b/src/cypherpunks.ru/govpn/cmd/govpn-verifier/main.go @@ -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) diff --git a/src/cypherpunks.ru/govpn/cnw/cnw_test.go b/src/cypherpunks.ru/govpn/cnw/cnw_test.go index 24cf452..cbb28e9 100644 --- a/src/cypherpunks.ru/govpn/cnw/cnw_test.go +++ b/src/cypherpunks.ru/govpn/cnw/cnw_test.go @@ -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++ { diff --git a/src/cypherpunks.ru/govpn/egd.go b/src/cypherpunks.ru/govpn/egd.go index 04ec87b..3d0b791 100644 --- a/src/cypherpunks.ru/govpn/egd.go +++ b/src/cypherpunks.ru/govpn/egd.go @@ -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) { diff --git a/src/cypherpunks.ru/govpn/encless.go b/src/cypherpunks.ru/govpn/encless.go index 30eb4a5..f9d9bbf 100644 --- a/src/cypherpunks.ru/govpn/encless.go +++ b/src/cypherpunks.ru/govpn/encless.go @@ -19,6 +19,8 @@ along with this program. If not, see . 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) diff --git a/src/cypherpunks.ru/govpn/encless_test.go b/src/cypherpunks.ru/govpn/encless_test.go index 20b0df6..1f9fece 100644 --- a/src/cypherpunks.ru/govpn/encless_test.go +++ b/src/cypherpunks.ru/govpn/encless_test.go @@ -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++ { diff --git a/src/cypherpunks.ru/govpn/handshake.go b/src/cypherpunks.ru/govpn/handshake.go index 4f080eb..9c536b3 100644 --- a/src/cypherpunks.ru/govpn/handshake.go +++ b/src/cypherpunks.ru/govpn/handshake.go @@ -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 diff --git a/src/cypherpunks.ru/govpn/identity.go b/src/cypherpunks.ru/govpn/identity.go index f85631b..4dc74ee 100644 --- a/src/cypherpunks.ru/govpn/identity.go +++ b/src/cypherpunks.ru/govpn/identity.go @@ -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 index 0000000..b110ce3 --- /dev/null +++ b/src/cypherpunks.ru/govpn/logger.go @@ -0,0 +1,55 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2016 Sergey Matveev + +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 . +*/ + +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...) + } +} diff --git a/src/cypherpunks.ru/govpn/peer.go b/src/cypherpunks.ru/govpn/peer.go index 5160bd4..fc36179 100644 --- a/src/cypherpunks.ru/govpn/peer.go +++ b/src/cypherpunks.ru/govpn/peer.go @@ -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() +} diff --git a/src/cypherpunks.ru/govpn/peer_test.go b/src/cypherpunks.ru/govpn/peer_test.go index 7fa8aab..4a0c752 100644 --- a/src/cypherpunks.ru/govpn/peer_test.go +++ b/src/cypherpunks.ru/govpn/peer_test.go @@ -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()