From 23594738bb8908bc6b487c6a831509206ba18a91 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Fri, 22 May 2015 23:43:03 +0300 Subject: [PATCH] Ability to use EGD-compatible PRNGs Signed-off-by: Sergey Matveev --- doc/client.texi | 4 +-- doc/egd.texi | 18 +++++++++++ doc/keywords.texi | 17 +++++----- doc/news.texi | 7 +++++ doc/precautions.texi | 36 ++++++++++----------- doc/server.texi | 4 +-- doc/todo.texi | 1 - doc/user.texi | 2 ++ src/govpn/cmd/govpn-client/main.go | 6 ++++ src/govpn/cmd/govpn-server/main.go | 6 ++++ src/govpn/egd.go | 50 ++++++++++++++++++++++++++++++ src/govpn/handshake.go | 22 +++++++++---- 12 files changed, 135 insertions(+), 38 deletions(-) create mode 100644 doc/egd.texi create mode 100644 src/govpn/egd.go diff --git a/doc/client.texi b/doc/client.texi index dfee4f1..a6fc6b0 100644 --- a/doc/client.texi +++ b/doc/client.texi @@ -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 diff --git a/doc/egd.texi b/doc/egd.texi new file mode 100644 index 0000000..6b6780e --- /dev/null +++ b/doc/egd.texi @@ -0,0 +1,18 @@ +@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 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 either host:port or path to the domain socket. + +@example +% ./govpn-server [...] -egd /var/run/egd.sock +@end example diff --git a/doc/keywords.texi b/doc/keywords.texi index a513724..a365ce5 100644 --- a/doc/keywords.texi +++ b/doc/keywords.texi @@ -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. diff --git a/doc/news.texi b/doc/news.texi index 0ef3f82..b0b6437 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -3,6 +3,13 @@ @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. +@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/precautions.texi b/doc/precautions.texi index cd759ea..f69d534 100644 --- a/doc/precautions.texi +++ b/doc/precautions.texi @@ -1,24 +1,22 @@ @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. - -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. - +@enumerate +@item 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}. +passphrases. Also remember to keep passphrase on temporary file and read +it securely as described in @ref{Verifier}. + +@item +You must @strong{never} use one key for multiple clients. +If so, then all security is ruined and transmitted data can +be decrypted. + +@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 diff --git a/doc/server.texi b/doc/server.texi index c5f2eb2..810461e 100644 --- a/doc/server.texi +++ b/doc/server.texi @@ -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 diff --git a/doc/todo.texi b/doc/todo.texi index f480f7e..8e912e3 100644 --- a/doc/todo.texi +++ b/doc/todo.texi @@ -2,7 +2,6 @@ @unnumbered TODO @itemize -@item Ability to use EGD. @item Replace -noncediff with in-memory storage of previously seen nonces. @item Ability to tunnel only specified TCP connections, without full featured VPN solution. Similar to ssh -R. diff --git a/doc/user.texi b/doc/user.texi index 3080ce1..19e24e3 100644 --- a/doc/user.texi +++ b/doc/user.texi @@ -10,6 +10,7 @@ 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:: @@ -24,6 +25,7 @@ automate it using up and down shell scripts. * Example usage:: @end menu +@include egd.texi @include identity.texi @include pake.texi @include timeout.texi diff --git a/src/govpn/cmd/govpn-client/main.go b/src/govpn/cmd/govpn-client/main.go index a951160..f212490 100644 --- a/src/govpn/cmd/govpn-client/main.go +++ b/src/govpn/cmd/govpn-client/main.go @@ -43,6 +43,7 @@ var ( 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,6 +59,11 @@ 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, diff --git a/src/govpn/cmd/govpn-server/main.go b/src/govpn/cmd/govpn-server/main.go index 0a127ef..9fb7ae1 100644 --- a/src/govpn/cmd/govpn-server/main.go +++ b/src/govpn/cmd/govpn-server/main.go @@ -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 index 0000000..08dff83 --- /dev/null +++ b/src/govpn/egd.go @@ -0,0 +1,50 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 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 ( + "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 +} diff --git a/src/govpn/handshake.go b/src/govpn/handshake.go index 7019148..61b5b4f 100644 --- a/src/govpn/handshake.go +++ b/src/govpn/handshake.go @@ -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[:]) -- 2.44.0