]> Cypherpunks.ru repositories - govpn.git/commitdiff
Merge branch 'develop' 3.4
authorSergey Matveev <stargrave@stargrave.org>
Sun, 24 May 2015 08:39:17 +0000 (11:39 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 24 May 2015 08:39:17 +0000 (11:39 +0300)
Signed-off-by: Sergey Matveev <stargrave@stargrave.org>
24 files changed:
TODO [changed from file to symlink]
VERSION
doc/client.texi
doc/developer.texi
doc/download.texi
doc/egd.texi [new file with mode: 0644]
doc/govpn.texi
doc/keywords.texi
doc/news.texi
doc/noncediff.texi [deleted file]
doc/overview.texi
doc/precautions.texi
doc/server.texi
doc/todo.texi [new file with mode: 0644]
doc/transport.texi
doc/user.texi
doc/verifierstruct.texi
src/govpn/cmd/govpn-client/main.go
src/govpn/cmd/govpn-server/main.go
src/govpn/egd.go [new file with mode: 0644]
src/govpn/handshake.go
src/govpn/identify.go
src/govpn/transport.go
src/govpn/transport_test.go

diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index a2043caacde4e0a934049ddc1a15beca7921bbca..0000000000000000000000000000000000000000
--- a/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-* Increase performance
-* Randomize ports usage
diff --git a/TODO b/TODO
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..e8b79ff7bcccc2d0ab0409382a279d9b0ddf963e
--- /dev/null
+++ b/TODO
@@ -0,0 +1 @@
+doc/todo.texi
\ No newline at end of file
diff --git a/VERSION b/VERSION
index eb39e5382f4f035e4d71c7f67712cdbfa6c0c335..2f4b60750dc3500b0e4cf08f316a960a7ca42b40 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.3
+3.4
index dfee4f19b68f9dc3c4c8f56490b0e1ca278c17a6..e2c26fd727b83089a079e0d1afb14b899ce2d18f 100644 (file)
@@ -1,8 +1,8 @@
 @node Client part
 @section Client part
 
-Except for common @code{-mtu}, @code{-stats} options client has the
-following ones:
+Except for common @code{-mtu}, @code{-stats}, @code{-egd} options client
+has the following ones:
 
 @table @code
 
@@ -22,9 +22,6 @@ how to enter passphrase from stdin silently and store it in the file.
 @item -timeout
 @ref{Timeout} setting in seconds.
 
-@item -noncediff
-Allowable @ref{Nonce difference}.
-
 @item -noise
 Enable @ref{Noise}.
 
index be57f2c8a3f117d87b6988c8154b2a1324281804..5f85e00c4dad1cb6a088238549dc70bb56d531f5 100644 (file)
@@ -3,23 +3,26 @@
 
 @table @asis
 @item Nonce and identity encryption
-@url{http://143.53.36.235:8080/tea.htm, XTEA}
+@url{http://143.53.36.235:8080/tea.htm, XTEA}.
 @item Data encryption
-@url{http://cr.yp.to/snuffle.html, Salsa20}
+@url{http://cr.yp.to/snuffle.html, Salsa20}.
 @item Message authentication
-@url{http://cr.yp.to/mac.html, Poly1305}
+@url{http://cr.yp.to/mac.html, Poly1305}.
 @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}
+and @url{http://ed25519.cr.yp.to/, Ed25519}.
 @item DH elliptic-curve point encoding for public keys
-@url{http://elligator.cr.yp.to/, Elligator}
+@url{http://elligator.cr.yp.to/, Elligator}.
 @item Key derivation function for verifier generation
 @url{https://en.wikipedia.org/wiki/PBKDF2, PBKDF2} based on
-@url{https://en.wikipedia.org/wiki/SHA-2, SHA-512}
+@url{https://en.wikipedia.org/wiki/SHA-2, SHA-512}.
 @item Packet overhead
-26 bytes per packet
+26 bytes per packet.
 @item Handshake overhead
-4 UDP (2 from client, 2 from server) packets, 264 bytes total payload
+4 UDP (2 from client, 2 from server) packets, 264 bytes total payload.
+@item Entropy required
+832 bits in average on client, 832 bits in average on server side per
+handshake.
 @end table
 
 @menu
index 4ce61a53934e3bdfbe620c603e6a80b7d4ccc440..83abb667b808e364500024d78d0e7f24ebb8463b 100644 (file)
@@ -38,6 +38,10 @@ You can obtain releases source code prepared tarballs from the links below:
 @tab @url{download/govpn-3.2.tar.xz, link} @url{download/govpn-3.2.tar.xz.sig, sign}
 @tab @code{388e98d6adef5ebf3431b0d48419f54d2e2064c657de67e23c669ebcf273126d}
 
+@item 3.3 @tab 175 KiB
+@tab @url{download/govpn-3.3.tar.xz, link} @url{download/govpn-3.3.tar.xz.sig, sign}
+@tab @code{1834a057215324f49d6272b2beb89f1532105156f7e853eae855659992ac0c84}
+
 @end multitable
 
 Also you can try its @ref{Contacts, .onion} version.
diff --git a/doc/egd.texi b/doc/egd.texi
new file mode 100644 (file)
index 0000000..77d34bf
--- /dev/null
@@ -0,0 +1,19 @@
+@node EGD
+@section EGD
+
+Overall security mainly depends on client side:
+@ref{PAKE, good passphrase} and cryprographically good pseudo random
+number generator.
+
+Some operating systems do not have good enough quality PRNG, bad
+@code{/dev/urandom}. You should use separate PRNG with them. GoVPN
+communicates with them using
+@url{http://egd.sourceforge.net/, Entropy Gathering Daemon} protocol.
+
+To switch using EGD-compatible daemons instead of @code{crypto/rand}
+library you provide @code{-egd PATH} command line option, where
+@code{PATH} is the domain socket.
+
+@example
+% ./govpn-server [...] -egd /var/run/egd.sock
+@end example
index a22e85f98efe9e633c825c5a31f348f22e8b33b7..c8fa79c4fa7a05010c2fc1d0502c7112b300fd3d 100644 (file)
@@ -42,6 +42,7 @@ statistics, IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support.
 * Contacts::
 * Copying conditions::
 * Thanks::
+* TODO::
 @end menu
 
 @include overview.texi
@@ -59,4 +60,5 @@ statistics, IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support.
 @verbatiminclude fdl.txt
 
 @include thanks.texi
+@include todo.texi
 @bye
index a513724a640648574277389211b6c56f4627447c..a365ce5fef119d61fdea85e55ca0f3929156f00c 100644 (file)
@@ -2,11 +2,12 @@ Some keywords describing this project: encryption, authentication, key
 exchange, EKE, Diffie-Hellman, DH, DH-EKE, Augmented EKE, A-EKE,
 security, encrypted key exchange, 128-bit margin, DPI, censorship,
 resistance, free software, GPLv3+, reviewability, easy, simple,
-Curve25519, Ed25519, Elligator, SHA-512, Salsa20, Poly1305, AEAD, XTEA, PBKDF2,
-PRP, signatures, asymmetric cryptography, zero-knowledge password proof,
-PAKE, password, passphrase, password authenticated key exchange, perfect
-forward secrecy, PFS, MAC, nonce, verifier, rehandshake, heartbeat,
-replay attack, MiTM, length hiding, timestamps hiding, noise, constant
-traffic, constant packet rate, CPR, TAP, TAP, VPN, GNU, Linux, FreeBSD,
-IPv6, dictionary attack, mutual authentication, simultaneous clients,
-JSON, HTTP-server, statistics, PRNG, traffic analysis, Go, golang.
+Curve25519, Ed25519, Elligator, SHA-512, Salsa20, Poly1305, AEAD, XTEA,
+PBKDF2, PRP, signatures, asymmetric cryptography, zero-knowledge
+password proof, PAKE, password, passphrase, password authenticated key
+exchange, perfect forward secrecy, PFS, MAC, nonce, verifier,
+rehandshake, heartbeat, replay attack, MiTM, length hiding, timestamps
+hiding, noise, constant traffic, constant packet rate, CPR, EGD,
+entropy, TAP, TAP, VPN, GNU, Linux, FreeBSD, IPv6, dictionary attack,
+mutual authentication, simultaneous clients, JSON, HTTP-server,
+statistics, PRNG, traffic analysis, Go, golang.
index 0ef3f82365c16d0475ad20abdc497f3ac134f64f..d3a9ab13a56cb511b7afdea17373fd83efa65282 100644 (file)
@@ -3,6 +3,16 @@
 
 @table @strong
 
+@item Release 3.4
+@itemize @bullet
+@item Ability to use external @ref{EGD}-compatible PRNGs. Now you are
+able to use GoVPN even on systems with the bad @code{/dev/random},
+providing higher quality entropy from external sources.
+@item Removed @code{-noncediff} option. It is replaced with in-memory
+storage of seen nonces, thus eliminating possible replay attacks at all
+without performance degradation related to inbound packets reordering.
+@end itemize
+
 @item Release 3.3
 @itemize @bullet
 @item Compatibility with an old GNU Make 3.x. Previously only BSD Make
diff --git a/doc/noncediff.texi b/doc/noncediff.texi
deleted file mode 100644 (file)
index 7ce3aa2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-@node Nonce difference
-@section Nonce difference
-
-GoVPN prevents replay attacks by remembering the last used nonce in
-messages from the remote peer. All incoming messages must have higher
-nonce number (technically it is counter), otherwise they are dropped.
-
-Because of UDP nature that does not guarantee packet ordering during
-transmission, GoVPN will drop valid non-replayed UDP packets. That leads
-to performance decrease.
-
-In most cases there is no need in so strict nonce boundaries and
-@code{-noncediff} command line option allows to create the window of
-allowable nonce differences. This is trade-off between highest security
-and possible performance degradation. For example @code{-noncediff 128}
-works rather well (no packet drops) with 1 Gbps link with two switches.
-By default no nonce differences are allowed (highest security).
index dc68994470bfa4776cb60685c1fd9545b5e4d260..23ba75d24332396b43a37418312e8b832dce69a1 100644 (file)
@@ -11,29 +11,16 @@ goals for that daemon. Most modern widespread protocols and their
 implementations in software are too complex to be reviewed, analyzed and
 modified.
 
-State off art cryptography technologies includes:
-@url{http://cr.yp.to/snuffle.html, Salsa20} stream encryption,
-@url{http://143.53.36.235:8080/tea.htm, XTEA} PRP,
-@url{http://cr.yp.to/mac.html, Poly1305} message authentication,
-@url{https://en.wikipedia.org/wiki/PBKDF2} password-based key derivation
-function based on @url{https://en.wikipedia.org/wiki/SHA-2, SHA-512}
-hash function,
-@url{https://en.wikipedia.org/wiki/Encrypted_key_exchange,
-Diffie-Hellman Augmented Encrypted Key Exchange}
-(DH-A-EKE) powered by @url{http://cr.yp.to/ecdh.html, Curve25519},
-@url{http://ed25519.cr.yp.to/, Ed25519} signatures and
-@url{http://elligator.cr.yp.to/, Elligator} curve-point encoding.
-Strong
-@url{https://en.wikipedia.org/wiki/Zero-knowledge_password_proof, zero-knowledge}
-mutual authentication with key exchange stage is invulnerable
-to man-in-the-middle attacks.
+@ref{Developer manual, State off art cryptography technologies}. Strong
+mutual authenticated key exchange is invulnerable to man-in-the middle
+attachs.
 @url{https://en.wikipedia.org/wiki/Forward_secrecy, Perfect forward secrecy}
-property guarantee that compromising of long-term authentication
-pre-shared key can not lead to previously captured traffic decrypting.
-Compromising of peers password file on server side won't allow attacker
+property guarantees that compromising of long-term authentication keys
+does not lead to previously captured traffic decrypting.
+Compromising of peers password files on server side won't allow attacker
 to masquerade as the client, because of asymmetric @strong{verifiers}
 usage, resistant to dictionary attacks. Rehandshaking ensures session
-keys rotation. MAC authentication with one-time keys protects against
+keys rotation. One-time keys MAC authentication protects against
 @url{https://en.wikipedia.org/wiki/Replay_attack, replay attacks}.
 
 Server can work with several clients simultaneously. Each client is
@@ -61,22 +48,26 @@ Works with @url{https://en.wikipedia.org/wiki/TAP_(network_driver), TAP}
 network interfaces on top of UDP entirely
 @item
 @url{https://www.gnu.org/, GNU}/Linux and
-@url{http://www.freebsd.org/, FreeBSD} support
-@item IPv6 compatible
-@item Encrypted and authenticated payload transport
-@item Relatively fast handshake
-@item Password-authenticated key exchange
-@item Server-side password verifiers are secure against dictionary attacks
-@item Attacker can not masquerade a client even with password files compromising
-@item Replay attack protection
-@item Perfect forward secrecy property
-@item Mutual two-side authentication
-@item Zero knowledge authentication
-@item Built-in rehandshake and heartbeat features
-@item Several simultaneous clients support
-@item Per-client configuration options
-@item Hiding of payload packets length with noise
-@item Hiding of payload packets timestamps with constant packet rate traffic
+@url{http://www.freebsd.org/, FreeBSD} support.
+@item IPv6 compatible.
+@item Encrypted and authenticated payload transport.
+@item Relatively fast handshake.
+@item Password-authenticated key exchange.
+@item Server-side password verifiers are secure against dictionary
+attacks.
+@item Attacker can not masquerade a client even with password files
+compromising.
+@item Replay attack protection.
+@item Perfect forward secrecy property.
+@item Mutual two-side authentication.
+@item Zero knowledge authentication.
+@item Built-in rehandshake and heartbeat features.
+@item Several simultaneous clients support.
+@item Per-client configuration options.
+@item Hiding of payload packets length with noise.
+@item Hiding of payload packets timestamps with constant packet rate
+traffic.
 @item Optional built-in HTTP-server for retrieving information about
-known connected peers in @url{http://json.org/, JSON} format
+known connected peers in @url{http://json.org/, JSON} format.
+@item Compatibility with @url{http://egd.sourceforge.net/, EGD} PRNGs.
 @end itemize
index cd759ea72708dd0503cb77fd01d3f226c0b6f3f5..0539822893cd83d64bec34431b6c73c588c69438 100644 (file)
@@ -1,24 +1,20 @@
 @node Precautions
 @unnumbered Precautions
 
-The very important precaution is the @strong{cryptographically good}
-pseudo random number generator. GoVPN uses native operating system PRNG
-as entropy source. You have no way to check its quality in closed
-source code operating systems, so it is recommended not to use them if
-you really needs security. Moreover it is possible that those OS leaks
-information about possible PRNG states. And at least Apple OS X and
-Microsoft Windows are already known to have weak CSPRNGs.
-
-GoVPN could use its own PRNG implementation like
-@url{https://www.schneier.com/fortuna.html, Fortuna}, but it is
-much easier to use the right OS, to use free software.
+@enumerate
+@item
+We use password (passphrase) authentication, so overall security fully
+depends on its strength. You @strong{should} use long, high-entropy
+passphrases. Also remember to keep passphrase in temporary file and read
+it securely as described in @ref{Verifier}.
 
-Also you should @strong{never} use one key for multiple clients. Salsa20
-encryption is randomized in each session, but it depends again on PRNG.
-If it fails, produces equal values at least once, then all you traffic
-related to that key could be decrypted.
+@item
+You must @strong{never} use one key for multiple clients.
 
-We use password (passphrase) authentication, so overall security fully
-depends on its strength. So you should use long, high-entropy
-passphrases. Also remember to keep passphrase on temporary file as
-described in @ref{Verifier}.
+@item
+You must use @strong{cryptographically good} pseudo random number
+generator. By default we use default @code{crypto/rand} library that
+reads @code{/dev/urandom} source. Some GNU/Linux and FreeBSD systems
+are rather good with this entropy source. Closed proprietary ones are
+always not and you must use optional @ref{EGD} feature with them.
+@end enumerate
index c5f2eb29e8213190bbae74e3574d53b16e2acc10..989a8a8a8f8e17f0964a7e15a36d82aed7d3fa4f 100644 (file)
@@ -1,8 +1,8 @@
 @node Server part
 @section Server part
 
-Except for common @code{-mtu}, @code{-stats} options server has the
-following ones:
+Except for common @code{-mtu}, @code{-stats}, @code{-egd} options server
+has the following ones:
 
 @table @code
 @item -bind
@@ -46,10 +46,6 @@ Optional. Contains human readable username. Used to beauty output of
 Optional. Contains @ref{Timeout} setting (decimal notation) in seconds.
 Otherwise default minute timeout will be used.
 
-@item noncediff
-Optional. Contains allowable @ref{Nonce difference} setting (decimal
-notation).
-
 @item noise
 Optional. Contains either "1" (enable @ref{Noise} adding), or "0".
 
diff --git a/doc/todo.texi b/doc/todo.texi
new file mode 100644 (file)
index 0000000..93b95f1
--- /dev/null
@@ -0,0 +1,11 @@
+@node TODO
+@unnumbered TODO
+
+@itemize
+@item Ability to tunnel only specified TCP connections, without full
+  featured VPN solution. Similar to ssh -R.
+@item Ability to work over HTTP, WebSockets and something similar.
+@item Ability to noise handshake packets sizes.
+@item Increase performance.
+@item Randomize ports usage.
+@end itemize
index b76286138f63af78bb9f43bc02ec5d774d1e5716..cdbe7aecfa47365502a5ffdd0dc811d1c262d90b 100644 (file)
@@ -34,12 +34,9 @@ Salsa20 output are used as a one-time key for @code{AUTH}. Next 256 bits
 of Salsa20 are ignored. All remaining output is XORed with the data,
 encrypting it.
 
-To prevent replay attacks we remember latest @code{SERIAL} from the
-remote peer. If received message's @code{SERIAL} is not greater that the
-saved one, then drop it. Optionally, because some UDP packets can be
-reordered during transmission, we can allow some window for valid
-serials with the @code{-noncediff} option. @code{-noncediff 10} with
-current saved serial state equals to 78 allows messages with 68…78
-serials. That time window can be used by attacker to replay packets, so
-by default it equals to 1. However it can improve performance because of
-rearranged UDP packets.
+To prevent replay attacks we must remember received @code{SERIAL}s and
+if meet one, then drop it. Basically we could just store latest number
+and check if received one is greater, but because of UDP packets
+reordering this can lead to valid packets dropping and overall
+performance degradation. We store up to 256 seen nonces in hash
+structure, in two swapping buckets.
index 3080ce1f1ff3974939c64a568b061802936337cc..3702f8488b7c5a005e7b003cbcb9b466e7b62b93 100644 (file)
@@ -10,10 +10,10 @@ IP-related network management is not touched by VPN at all. You can
 automate it using up and down shell scripts.
 
 @menu
+* EGD:: Entropy gathering daemon
 * Identity::
 * PAKE:: Password Authenticated Key Agreement
 * Timeout::
-* Nonce difference::
 * MTU:: Maximum Transmission Unit
 * Stats::
 * Noise::
@@ -24,10 +24,10 @@ automate it using up and down shell scripts.
 * Example usage::
 @end menu
 
+@include egd.texi
 @include identity.texi
 @include pake.texi
 @include timeout.texi
-@include noncediff.texi
 @include mtu.texi
 @include stats.texi
 @include noise.texi
index b508b483536d4b9e4aebef987362d126b42abe5d..65a4e086c14d7fbf8e5fd896fc4145c05845e077 100644 (file)
@@ -10,7 +10,7 @@ SOURCE = PBKDF2(SALT=PeerId, PASSWORD, 1<<16, SHA512)
 PUB, PRIV = Ed25519.Generate(SOURCE)
 @end verbatim
 
-Verifier is public key of Ed25519 generated from the PBKDF2 of the
-passphrase in hexadecimal encoding. @code{PeerId} is used as a 128-bit
-salt. Server stores and knows only verifier. Client can compute the
-whole keypair every time he makes handshake.
+Verifier is @code{PUB} public key of Ed25519 generated from the PBKDF2
+of the passphrase in hexadecimal encoding. @code{PeerId} is used as a
+128-bit salt. Server stores and knows only verifier. Client can compute
+the whole keypair every time he makes handshake.
index a951160a21d7ce737e01d53188b3fa4b70a272df..9b6567763ecea2e8f1787c9230f7f712aea0a78a 100644 (file)
@@ -39,10 +39,10 @@ var (
        downPath   = flag.String("down", "", "Path to down-script")
        stats      = flag.String("stats", "", "Enable stats retrieving on host:port")
        mtu        = flag.Int("mtu", 1452, "MTU for outgoing packets")
-       nonceDiff  = flag.Int("noncediff", 1, "Allow nonce difference")
        timeoutP   = flag.Int("timeout", 60, "Timeout seconds")
        noisy      = flag.Bool("noise", false, "Enable noise appending")
        cpr        = flag.Int("cpr", 0, "Enable constant KiB/sec out traffic rate")
+       egdPath    = flag.String("egd", "", "Optional path to EGD socket")
 )
 
 func main() {
@@ -58,11 +58,15 @@ func main() {
                log.Fatalln(err)
        }
 
+       if *egdPath != "" {
+               log.Println("Using", *egdPath, "EGD")
+               govpn.EGDInit(*egdPath)
+       }
+
        pub, priv := govpn.NewVerifier(id, govpn.StringFromFile(*keyPath))
        conf := &govpn.PeerConf{
                Id:          id,
                Timeout:     time.Second * time.Duration(timeout),
-               Noncediff:   *nonceDiff,
                NoiseEnable: *noisy,
                CPR:         *cpr,
                DSAPub:      pub,
index 0a127efa78104147f93b0a48e72853ba910ed21b..9fb7ae1edf7d9225c6504ee1d89da65515857f18 100644 (file)
@@ -37,6 +37,7 @@ var (
        peersPath = flag.String("peers", "peers", "Path to peers keys directory")
        stats     = flag.String("stats", "", "Enable stats retrieving on host:port")
        mtu       = flag.Int("mtu", 1452, "MTU for outgoing packets")
+       egdPath   = flag.String("egd", "", "Optional path to EGD socket")
 )
 
 type PeerReadyEvent struct {
@@ -83,6 +84,11 @@ func main() {
        govpn.MTU = *mtu
        govpn.PeersInit(*peersPath)
 
+       if *egdPath != "" {
+               log.Println("Using", *egdPath, "EGD")
+               govpn.EGDInit(*egdPath)
+       }
+
        bind, err := net.ResolveUDPAddr("udp", *bindAddr)
        if err != nil {
                log.Fatalln("Can not resolve bind address:", err)
diff --git a/src/govpn/egd.go b/src/govpn/egd.go
new file mode 100644 (file)
index 0000000..08dff83
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+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 (
+       "errors"
+       "net"
+)
+
+var (
+       egdPath string
+)
+
+func EGDInit(path string) {
+       egdPath = path
+}
+
+// Read n bytes from EGD, blocking mode.
+func EGDRead(b []byte) error {
+       c, err := net.Dial("unix", egdPath)
+       if err != nil {
+               return err
+       }
+       defer c.Close()
+       c.Write([]byte{0x02, byte(len(b))})
+       r, err := c.Read(b)
+       if err != nil {
+               return err
+       }
+       if r != len(b) {
+               return errors.New("Got less bytes than expected from EGD")
+       }
+       return nil
+}
index 7019148e899949f754435bdbad61c2042963350c..61b5b4f67dfe1f50fdb37d3c12ca032c62f8ce16 100644 (file)
@@ -101,13 +101,23 @@ func (h *Handshake) rNonceNext(count uint64) []byte {
        return nonce
 }
 
+func randRead(b []byte) error {
+       var err error
+       if egdPath == "" {
+               _, err = rand.Read(b)
+       } else {
+               err = EGDRead(b)
+       }
+       return err
+}
+
 func dhKeypairGen() (*[32]byte, *[32]byte) {
        priv := new([32]byte)
        pub := new([32]byte)
        repr := new([32]byte)
        reprFound := false
        for !reprFound {
-               if _, err := rand.Read(priv[:]); err != nil {
+               if err := randRead(priv[:]); err != nil {
                        log.Fatalln("Error reading random for DH private key:", err)
                }
                reprFound = extra25519.ScalarBaseMult(pub, repr, priv)
@@ -157,7 +167,7 @@ func HandshakeStart(conf *PeerConf, conn *net.UDPConn, addr *net.UDPAddr) *Hands
        state.dhPriv, dhPubRepr = dhKeypairGen()
 
        state.rNonce = new([RSize]byte)
-       if _, err := rand.Read(state.rNonce[:]); err != nil {
+       if err := randRead(state.rNonce[:]); err != nil {
                log.Fatalln("Error reading random for nonce:", err)
        }
        enc := make([]byte, 32)
@@ -201,11 +211,11 @@ func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer {
 
                // Generate R* and encrypt them
                h.rServer = new([RSize]byte)
-               if _, err := rand.Read(h.rServer[:]); err != nil {
+               if err := randRead(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 := randRead(h.sServer[:]); err != nil {
                        log.Fatalln("Error reading random for S:", err)
                }
                encRs := make([]byte, RSize+SSize)
@@ -287,11 +297,11 @@ func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer {
 
                // Generate R* and signature and encrypt them
                h.rClient = new([RSize]byte)
-               if _, err := rand.Read(h.rClient[:]); err != nil {
+               if err := randRead(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 := randRead(h.sClient[:]); err != nil {
                        log.Fatalln("Error reading random for S:", err)
                }
                sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:])
index 663afe73599bf14b8fc40acf7e2b738c9ae6d7e6..c7675b6368b48cc62e7eef3180e8515ac1421e1b 100644 (file)
@@ -59,7 +59,6 @@ func (id PeerId) MarshalJSON() ([]byte, error) {
 type PeerConf struct {
        Id          *PeerId
        Timeout     time.Duration
-       Noncediff   int
        NoiseEnable bool
        CPR         int
        // This is passphrase verifier
@@ -180,7 +179,7 @@ func (id *PeerId) Conf() *PeerConf {
        if dummyConf != nil {
                return dummyConf
        }
-       conf := PeerConf{Id: id, Noncediff: 1, NoiseEnable: false, CPR: 0}
+       conf := PeerConf{Id: id, NoiseEnable: false, CPR: 0}
        peerPath := path.Join(PeersPath, id.String())
 
        verPath := path.Join(peerPath, "verifier")
@@ -207,9 +206,6 @@ func (id *PeerId) Conf() *PeerConf {
        }
        conf.Timeout = time.Second * time.Duration(timeout)
 
-       if val, err := readIntFromFile(path.Join(peerPath, "noncediff")); err == nil {
-               conf.Noncediff = val
-       }
        if val, err := readIntFromFile(path.Join(peerPath, "noise")); err == nil && val == 1 {
                conf.NoiseEnable = true
        }
index f46eeefb62b3aae5c9af6fbebe46705d9688e1b7..2f5a345e57a6f5c1dd9034f7885408ff886db64b 100644 (file)
@@ -30,7 +30,8 @@ import (
 )
 
 const (
-       NonceSize = 8
+       NonceSize       = 8
+       NonceBucketSize = 128
        // S20BS is Salsa20's internal blocksize in bytes
        S20BS = 64
        // Maximal amount of bytes transfered with single key (4 GiB)
@@ -56,11 +57,14 @@ type Peer struct {
        CPRCycle    time.Duration `json:"-"`
 
        // Cryptography related
-       Key         *[SSize]byte `json:"-"`
-       Noncediff   int
-       NonceOur    uint64       `json:"-"`
-       NonceRecv   uint64       `json:"-"`
-       NonceCipher *xtea.Cipher `json:"-"`
+       Key          *[SSize]byte `json:"-"`
+       NonceOur     uint64       `json:"-"`
+       NonceRecv    uint64       `json:"-"`
+       NonceCipher  *xtea.Cipher `json:"-"`
+       nonceBucket0 map[uint64]struct{}
+       nonceBucket1 map[uint64]struct{}
+       nonceFound   bool
+       nonceBucketN int32
 
        // Timers
        Timeout       time.Duration `json:"-"`
@@ -243,23 +247,24 @@ func newPeer(addr *net.UDPAddr, conf *PeerConf, nonce int, key *[SSize]byte) *Pe
                timeout = timeout / TimeoutHeartbeat
        }
        peer := Peer{
-               Addr:        addr,
-               Timeout:     timeout,
-               Established: now,
-               LastPing:    now,
-               Id:          conf.Id,
-               NoiseEnable: noiseEnable,
-               CPR:         conf.CPR,
-               CPRCycle:    cprCycle,
-               Noncediff:   conf.Noncediff,
-               NonceOur:    uint64(conf.Noncediff + nonce),
-               NonceRecv:   uint64(conf.Noncediff + 0),
-               Key:         key,
-               NonceCipher: newNonceCipher(key),
-               buf:         make([]byte, MTU+S20BS),
-               tag:         new([poly1305.TagSize]byte),
-               keyAuth:     new([SSize]byte),
-               nonce:       make([]byte, NonceSize),
+               Addr:         addr,
+               Timeout:      timeout,
+               Established:  now,
+               LastPing:     now,
+               Id:           conf.Id,
+               NoiseEnable:  noiseEnable,
+               CPR:          conf.CPR,
+               CPRCycle:     cprCycle,
+               NonceOur:     uint64(nonce),
+               NonceRecv:    uint64(0),
+               nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
+               nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
+               Key:          key,
+               NonceCipher:  newNonceCipher(key),
+               buf:          make([]byte, MTU+S20BS),
+               tag:          new([poly1305.TagSize]byte),
+               keyAuth:      new([SSize]byte),
+               nonce:        make([]byte, NonceSize),
        }
        return &peer
 }
@@ -286,14 +291,31 @@ func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) boo
                p.FramesUnauth++
                return false
        }
+
+       // 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(p.buf, udpPkt[:NonceSize])
+       ready <- struct{}{}
        p.nonceRecv, _ = binary.Uvarint(p.buf[:NonceSize])
-       if int(p.NonceRecv)-p.Noncediff >= 0 && int(p.nonceRecv) < int(p.NonceRecv)-p.Noncediff {
-               ready <- struct{}{}
+       if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound {
                p.FramesDup++
                return false
        }
-       ready <- struct{}{}
+       if _, p.nonceFound = p.nonceBucket0[p.NonceRecv]; p.nonceFound {
+               p.FramesDup++
+               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
+       }
+
        p.FramesIn++
        p.BytesIn += int64(p.size)
        p.LastPing = time.Now()
index d12c26dacb68210e1f54ebf8d93ced4580d9eb3f..45f4d01cb6b6997bd871b7d9b6a5df29c87894d0 100644 (file)
@@ -24,7 +24,6 @@ func init() {
        conf = &PeerConf{
                Id:          peerId,
                Timeout:     time.Second * time.Duration(TimeoutDefault),
-               Noncediff:   1,
                NoiseEnable: false,
                CPR:         0,
        }
@@ -62,6 +61,8 @@ func BenchmarkDec(b *testing.B) {
        peer = newPeer(addr, conf, 128, new([SSize]byte))
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
+               peer.nonceBucket0 = make(map[uint64]struct{}, 1)
+               peer.nonceBucket1 = make(map[uint64]struct{}, 1)
                if !peer.UDPProcess(ciphertext, dummy, ready) {
                        b.Fail()
                }