]> Cypherpunks.ru repositories - govpn.git/commitdiff
Use Argon2d PHC winner instead of PBKDF2
authorSergey Matveev <stargrave@stargrave.org>
Tue, 3 Nov 2015 21:46:47 +0000 (00:46 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 3 Nov 2015 22:46:17 +0000 (01:46 +0300)
Signed-off-by: Sergey Matveev <stargrave@stargrave.org>
30 files changed:
.gitmodules
VERSION
doc/about.ru.texi
doc/about.texi
doc/client.texi
doc/developer.texi
doc/example.texi
doc/identity.texi
doc/installation.texi
doc/news.texi
doc/pake.texi
doc/precautions.texi
doc/server.texi
doc/thanks.texi
doc/todo.texi
doc/verifier.texi
doc/verifierstruct.texi
src/github.com/dchest/blake2b [new submodule]
src/github.com/magical/argon2 [new submodule]
src/govpn/cmd/govpn-client/main.go
src/govpn/cmd/govpn-server/conf.go
src/govpn/cmd/govpn-verifier/main.go
src/govpn/conf.go
src/govpn/handshake.go
src/govpn/identify.go
src/govpn/peer.go
src/govpn/peer_test.go
src/govpn/verifier.go
utils/makedist.sh
utils/newclient.sh

index f2d6ae4dfa28c8ac448fae2d6a660339e74c044a..ffbe4428c4b4ad35b59057f4f46ed0a0ed81ca90 100644 (file)
@@ -7,3 +7,9 @@
 [submodule "src/golang.org/x/crypto"]
        path = src/golang.org/x/crypto
        url = https://go.googlesource.com/crypto
+[submodule "src/github.com/magical/argon2"]
+       path = src/github.com/magical/argon2
+       url = https://github.com/magical/argon2.git
+[submodule "src/github.com/dchest/blake2b"]
+       path = src/github.com/dchest/blake2b
+       url = https://github.com/dchest/blake2b.git
diff --git a/VERSION b/VERSION
index 5186d07068cfed4c3f4705df79c73e115dd35c43..7d5c902e777905446c67de7e52a945b88fd2038c 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.0
+4.1
index 2054c432d1cb276cc29c0ab4e37cd22f1b76c944..3b2fda693f9235cecc521199c6e24de0c39fd430 100644 (file)
@@ -15,7 +15,8 @@ GoVPN это простой демон виртуальных частных с
 Encrypted Key Exchange)).
 @item
 Несбалансированные аутентификационные токены устойчивые к внесетевым
-(offline) атакам по словарю. Злоумышленник не может замаскироваться под
+(offline) атакам по словарю. Используют усиленный по CPU и памяти
+алгоритм хэширования. Злоумышленник не может замаскироваться под
 клиента даже скомпрометировав базу данных токенов сервера.
 @item
 Зашифрованный и аутентифицируемый транспортный протокол передачи данных
index 7d956ae1c9ddaa267ba82d4a2005ac44ac64e6dc..6e25072188f06c2bf3ff63f30c5eeeaa500c96ea 100644 (file)
@@ -13,8 +13,9 @@ authentication (PAKE DH A-EKE (Diffie-Hellman Augmented Encrypted Key
 Exchange)).
 @item
 @ref{Verifier structure, Augmented authentication tokens} resistant to
-offline dictionary attacks. An attacker can not masquerade a client
-even with server passphrase verifiers compromising.
+offline dictionary attacks. They use CPU and memory hardened hashing
+algorithm. An attacker can not masquerade a client even with server
+passphrase verifiers compromising.
 @item
 Encrypted and authenticated @ref{Transport, payload transport}
 with 128-bit @ref{Developer, security margin} state-of-the-art
index bab6a47c911b947592fb105ddd35b51b89b7dfd3..40f310f4e145843d6e3b7b8f4f81a0322fb5cc7e 100644 (file)
@@ -24,8 +24,8 @@ Address (@code{host:port} format) of remote server we need to connect to.
 @item -iface
 TAP interface name.
 
-@item -id
-Our client's @ref{Identity} (hexadecimal string).
+@item -verifier
+Our client's @ref{Verifier}.
 
 @item -key
 Path to the file with the passphrase. See @ref{Verifier} for
index 60352254bbbdb391b2f08e17b97e38539e9ac605..d9ec24fffbcdff245b5983402cfd8e400e2e92a5 100644 (file)
@@ -15,9 +15,8 @@ DH-A-EKE powered by @url{http://cr.yp.to/ecdh.html, Curve25519}
 and @url{http://ed25519.cr.yp.to/, Ed25519}.
 @item DH elliptic-curve point encoding for public keys
 @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}.
+@item Verifier password hashing algorithm
+@url{https://password-hashing.net/#argon2, Argon2d}.
 @item Packet overhead
 26 bytes per packet.
 @item Handshake overhead
index b6daf92325ce4af0c3b953372e59ce56c29b95d9..6fd62dfd12becda2f470b11ec204e98793b4bbde 100644 (file)
@@ -17,25 +17,25 @@ is 1432.
 @strong{Install}. At first you must @ref{Installation, install} this
 software: download, @ref{Integrity, check the signature}, compile.
 
-@strong{Prepare the client}. Generate client's identity and verifier for
-Alice as an example:
+@strong{Prepare the client}. Generate client's verifier for Alice as an
+example:
+
 @verbatim
 client% ./utils/newclient.sh Alice
 Enter passphrase:
-Your id is: 7012df29deee2170594119df5091d4a2
+Your client verifier is: $argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg
 
 Place the following JSON configuration entry on the server's side:
 
-    "7012df29deee2170594119df5091d4a2": {
-        "name": "Alice",
+    "Alice": {
         "up": "/path/to/up.sh",
-        "verifier": "fb43255ca3fe5bd884e364e5eae0cd37ad14774930a027fd38d8938fd0b57425"
+        "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
     }
 
 Verifier was generated with:
 
     ./utils/storekey.sh /tmp/passphrase
-    govpn-verifier -id 7012df29deee2170594119df5091d4a2 -key /tmp/passphrase
+    govpn-verifier -key /tmp/passphrase
 @end verbatim
 
 @strong{Prepare the server}. Add this entry to @code{peers.json}
@@ -77,7 +77,7 @@ client% ip route add default via 172.16.0.1
 @example
 client% govpn-client \
     -key key.txt \
-    -id 906e34b98750c4f686d6c5489508763c \
+    -verifier '$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg' \
     -iface tap10 \
     -remote 192.168.0.1:1194 \
     -mtu 1472
@@ -97,7 +97,7 @@ client% ifconfig tap10 inet6 fc00::2/96 mtu 1412 up
 client% route -6 add default fc00::1
 client% govpn-client \
     -key key.txt \
-    -id 906e34b98750c4f686d6c5489508763c \
+    -verifier '$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg' \
     -iface tap10 \
     -remote "[fe80::1%me0]":1194
 @end example
index 46a6889d2c1fda4041f4c89a1d5c30ffb408ef90..7e33b330254a60894680793506661008453154ed 100644 (file)
@@ -3,11 +3,5 @@
 
 Client's identity is 128-bit string. It is not secret, so can be
 transmitted and stored in the clear. However handshake applies PRP on it
-to make DPI and deanonymization much harder to success.
-
-@example
-% ./utils/newclient.sh doesnotmatter
-Your id is: 7012df29deee2170594119df5091d4a2
-@end example
-
-@code{7012df29deee2170594119df5091d4a2} is client's identity.
+to make DPI and deanonymization much harder to success. It is used as a
+salt in verifier.
index 8b4197783bd058c08c642a770494cd72d61640d7..d8d958767c619ac06f1c608ee156bcdb6c9ad4bb 100644 (file)
@@ -24,6 +24,8 @@ Included required libraries:
 @headitem Library @tab Platform @tab Licence
 @item @code{golang.org/x/crypto} @tab All @tab BSD 3-Clause
 @item @code{github.com/agl/ed25519} @tab All @tab BSD 3-Clause
+@item @code{github.com/dchest/blake2b} @tab All @tab CC0 1.0
+@item @code{github.com/magical/argon2} @tab All @tab BSD 2-Clause
 @item @code{github.com/bigeagle/water} @tab GNU/Linux @tab BSD 3-Clause
 @end multitable
 
index 35ee26d013d6a692da4c7f642842d5f3ff09a3d6..c2d06b89b094afa5294fd166583a6d38ef4a72b2 100644 (file)
@@ -3,6 +3,14 @@
 
 @table @strong
 
+@item Release 4.1
+@itemize
+@item @url{https://password-hashing.net/#argon2, Argon2d} is used instead
+of PBKDF2 for password verifier hashing.
+@item Client's identity is stored inside the verifier, so it simplifies
+server-side configuration and the code.
+@end itemize
+
 @item Release 4.0
 @itemize
 @item Handshake messages can be noised: their messages lengths are
index 0b52770f516c09c22776efb3496afaeca3d27a48..fa8a322c6666a9f132cfacd4564399dad3a4b2fc 100644 (file)
@@ -25,6 +25,6 @@ they are memorable. Because of their quantity, they acts as a high
 entropy source.
 
 Passphrases are entered directly by the human on the client side. Server
-side stores previously shared so-called @ref{Verifier}. Verifier contains
-dictionary attack resistant password derivative. Attacker can not use it
-to act as a client.
+side stores previously shared so-called @ref{Verifier, verifier}. Verifier
+contains dictionary attack resistant password derivative. Attacker can not
+use it to act as a client.
index fad882d6b411e0f2f74d1473888a897ba4b94e69..fbf45d13a4636b2315c7ec52f504362b53858e5c 100644 (file)
@@ -6,7 +6,7 @@
 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}.
+it securely as described in @ref{Verifier, verifier}.
 
 @item
 You must @strong{never} use the same key for multiple clients.
index 49a09fb990438998a7a939d354e650c800761120..a00c45219329c0bc460deb2f712e92d6656f3776 100644 (file)
@@ -25,8 +25,7 @@ Configuration file is JSON file with following example structure:
 
 @verbatim
 {
-  "9b40701bdaf522f2b291cb039490312": {  <-- Peer identifier
-    "name": "stargrave",                <-- OPTIONAL human readable name
+  "stargrave": {                        <-- Peer human readable name
     "up": "./stargrave-up.sh",          <-- up-script
     "down": "./stargrave-down.sh",      <-- OPTIONAL down-script
     "timeout": 60,                      <-- OPTIONAL overriden timeout
@@ -34,14 +33,12 @@ Configuration file is JSON file with following example structure:
                                             (default: false)
     "cpr": 64,                          <-- OPTIONAL constant packet
                                             rate in KiB/sec
-    "verifier": "2c15bbdffc73193bea56db412bce1143c68ccbdaa9e2eade53a684497646a685"
+    "verifier": "$argon2d..."           <-- verifier received from client
   },
   [...]
 }
 @end verbatim
 
-See @ref{Verifier} for its description.
-
 up-script executes each time connection with the client is established.
 Its @emph{stdout} output must contain TAP interface name as the first
 line. This script can be simple @code{echo tap10}, or maybe more
@@ -62,14 +59,13 @@ creation:
 @verbatim
 % ./utils/newclient.sh Alice
 [...]
-Your id is: 7012df29deee2170594119df5091d4a2
+Your client verifier is: $argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg
 
 Place the following JSON configuration entry on the server's side:
 
-    "906e34b98750c4f686d6c5489508763c": {
-        "name": "Alice",
+    "Alice": {
         "up": "/path/to/up.sh",
-        "verifier": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+        "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
     }
 [...]
 @end verbatim
index db82ca11c5e0fc1a8709f26904389949012ec041..5b9dba36b1380165a4ec53ae090dea1d0fa7a279 100644 (file)
@@ -20,4 +20,5 @@ File Compromise @copyright{} Steven M. Belloving, Michael Merrit.
 @item @url{http://ed25519.cr.yp.to/, Ed25519: high-speed high-security signatures}.
 @item @email{watsonbladd@@gmail.com, Watson Ladd} for suggestion of
     @url{http://elligator.cr.yp.to/, Elligator} encoding.
+@item @url{https://password-hashing.net/#argon2, PHC for Argon2}.
 @end itemize
index f7d00f3102f60cedcf7719c63ca03ccbb9761b94..520192c6ac01901d009024313d60f99ff7df4330 100644 (file)
@@ -2,9 +2,6 @@
 @unnumbered TODO
 
 @itemize
-@item Use Argon2:
-@url{https://password-hashing.net/, Password Hashing Competition}
-winner, instead of PBKDF2.
 @item Ability to tunnel only specified TCP connections, without full
   featured VPN solution. Similar to ssh -R.
 @item Randomize ports usage.
index 61b1e4479f954082327f98bb77f76accee825385..07ba19826e5ccf2fb40f89ff819d472f6817a3b1 100644 (file)
@@ -9,18 +9,23 @@ silently.
 @example
 % utils/storekey.sh mypass.txt
 Enter passphrase:[hello world]
-% govpn-verifier -id 9da9bf91112d0e4483c135b12d5b48de -key mypass.txt
-210e3878542828901a3af9b4aa00b004de530410eef5c1ba2ffb6d04504371b2
+% govpn-verifier -key mypass.txt
+$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10
+$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg
 @end example
 
-Store @code{210...1b2} string on the server's side in corresponding
+First line is the verifier for the server side. Second line is for the
+client -- it lacks generated public key. However you can server's one
+on the client side too.
+
+Store @code{$argon2d...u10} string on the server's side in corresponding
 @code{verifier} configuration file's field.
 
 You can check passphrase against verifier by specifying @code{-verifier}
 option with the path to verifier file:
 
 @example
-% govpn-verifier -id 9da9bf91112d0e4483c135b12d5b48de -key mypass.txt -verifier verifier
+% govpn-verifier -key mypass.txt -verifier '$argon2d...'
 true
 @end example
 
index 65a4e086c14d7fbf8e5fd896fc4145c05845e077..792506dd9f93ac95f4509998c07b8ce2db8cbf14 100644 (file)
@@ -6,11 +6,17 @@ dictionary attacks and can not be used for authentication (only
 its verifying).
 
 @verbatim
-SOURCE = PBKDF2(SALT=PeerId, PASSWORD, 1<<16, SHA512)
+SOURCE = Argon2d(m, t, p, SALT=PeerId, PASSWORD)
 PUB, PRIV = Ed25519.Generate(SOURCE)
 @end verbatim
 
-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.
+Verifier is serialized representation of public data above:
+@verbatim
+$argon2d$m=m,t=t,p=p$Base64(SALT)$Base64(PUB)
+@end verbatim
+
+m, t and p parameters are Argon2d-specific: memory, iterations and
+parallelizm parameters.
+
+Server stores and knows only verifier. Client can compute the whole
+keypair every time he makes handshake.
diff --git a/src/github.com/dchest/blake2b b/src/github.com/dchest/blake2b
new file mode 160000 (submodule)
index 0000000..3c8c640
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 3c8c640cd7bea3ca78209d812b5854442ab92fed
diff --git a/src/github.com/magical/argon2 b/src/github.com/magical/argon2
new file mode 160000 (submodule)
index 0000000..82d59eb
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 82d59eb7dab9a6268371a8c6de2100a2c7357bc6
index 8ebb78dcc95da7c9276e387df69b3be6f5b6c544..09b06ca44c753c894357f5b6ebf27fb27e6e5500 100644 (file)
@@ -31,21 +31,21 @@ import (
 )
 
 var (
-       remoteAddr = flag.String("remote", "", "Remote server address")
-       proto      = flag.String("proto", "udp", "Protocol to use: udp or tcp")
-       ifaceName  = flag.String("iface", "tap0", "TAP network interface")
-       IDRaw      = flag.String("id", "", "Client identification")
-       keyPath    = flag.String("key", "", "Path to passphrase file")
-       upPath     = flag.String("up", "", "Path to up-script")
-       downPath   = flag.String("down", "", "Path to down-script")
-       stats      = flag.String("stats", "", "Enable stats retrieving on host:port")
-       proxyAddr  = flag.String("proxy", "", "Use HTTP proxy on host:port")
-       proxyAuth  = flag.String("proxy-auth", "", "user:password Basic proxy auth")
-       mtu        = flag.Int("mtu", 1452, "MTU for outgoing packets")
-       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")
+       remoteAddr  = flag.String("remote", "", "Remote server address")
+       proto       = flag.String("proto", "udp", "Protocol to use: udp or tcp")
+       ifaceName   = flag.String("iface", "tap0", "TAP network interface")
+       verifierRaw = flag.String("verifier", "", "Verifier")
+       keyPath     = flag.String("key", "", "Path to passphrase file")
+       upPath      = flag.String("up", "", "Path to up-script")
+       downPath    = flag.String("down", "", "Path to down-script")
+       stats       = flag.String("stats", "", "Enable stats retrieving on host:port")
+       proxyAddr   = flag.String("proxy", "", "Use HTTP proxy on host:port")
+       proxyAuth   = flag.String("proxy-auth", "", "user:password Basic proxy auth")
+       mtu         = flag.Int("mtu", 1452, "MTU for outgoing packets")
+       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")
 
        conf        *govpn.PeerConf
        tap         *govpn.TAP
@@ -63,26 +63,25 @@ func main() {
 
        govpn.MTU = *mtu
 
-       id, err := govpn.IDDecode(*IDRaw)
-       if err != nil {
-               log.Fatalln(err)
-       }
-
        if *egdPath != "" {
                log.Println("Using", *egdPath, "EGD")
                govpn.EGDInit(*egdPath)
        }
 
-       pub, priv := govpn.NewVerifier(id, govpn.StringFromFile(*keyPath))
+       verifier, err := govpn.VerifierFromString(*verifierRaw)
+       if err != nil {
+               log.Fatalln(err)
+       }
+       priv := verifier.PasswordApply(govpn.StringFromFile(*keyPath))
        conf = &govpn.PeerConf{
-               Id:      id,
-               Timeout: time.Second * time.Duration(timeout),
-               Noise:   *noisy,
-               CPR:     *cpr,
-               DSAPub:  pub,
-               DSAPriv: priv,
+               Id:       verifier.Id,
+               Timeout:  time.Second * time.Duration(timeout),
+               Noise:    *noisy,
+               CPR:      *cpr,
+               Verifier: verifier,
+               DSAPriv:  priv,
        }
-       idsCache = govpn.NewCipherCache([]govpn.PeerId{*id})
+       idsCache = govpn.NewCipherCache([]govpn.PeerId{*verifier.Id})
        log.Println(govpn.VersionGet())
 
        tap, err = govpn.TAPListen(*ifaceName)
index 4136fabbdec4a2472af4ee80405d625344750f49..76d56cd8ae9f5185e954ff2dbc729b28a40c15f0 100644 (file)
@@ -19,14 +19,11 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
-       "encoding/hex"
        "encoding/json"
        "io/ioutil"
        "log"
        "time"
 
-       "github.com/agl/ed25519"
-
        "govpn"
 )
 
@@ -51,35 +48,25 @@ func confRead() map[govpn.PeerId]*govpn.PeerConf {
        }
 
        confs := make(map[govpn.PeerId]*govpn.PeerConf, len(*confsRaw))
-       for peerIdRaw, pc := range *confsRaw {
-               peerId, err := govpn.IDDecode(peerIdRaw)
+       for name, pc := range *confsRaw {
+               verifier, err := govpn.VerifierFromString(pc.VerifierRaw)
                if err != nil {
-                       log.Fatalln("Invalid peer ID:", peerIdRaw, err)
+                       log.Fatalln("Unable to decode the key:", err.Error(), pc.VerifierRaw)
                }
                conf := govpn.PeerConf{
-                       Id:    peerId,
-                       Name:  pc.Name,
-                       Up:    pc.Up,
-                       Down:  pc.Down,
-                       Noise: pc.Noise,
-                       CPR:   pc.CPR,
+                       Verifier: verifier,
+                       Id:       verifier.Id,
+                       Name:     name,
+                       Up:       pc.Up,
+                       Down:     pc.Down,
+                       Noise:    pc.Noise,
+                       CPR:      pc.CPR,
                }
                if pc.TimeoutInt <= 0 {
                        pc.TimeoutInt = govpn.TimeoutDefault
                }
                conf.Timeout = time.Second * time.Duration(pc.TimeoutInt)
-
-               if len(pc.Verifier) != ed25519.PublicKeySize*2 {
-                       log.Fatalln("Verifier must be 64 hex characters long")
-               }
-               keyDecoded, err := hex.DecodeString(string(pc.Verifier))
-               if err != nil {
-                       log.Fatalln("Unable to decode the key:", err.Error(), pc.Verifier)
-               }
-               conf.DSAPub = new([ed25519.PublicKeySize]byte)
-               copy(conf.DSAPub[:], keyDecoded)
-
-               confs[*peerId] = &conf
+               confs[*verifier.Id] = &conf
        }
        return confs
 }
index ede2de7438a370c43f770120a818ed454a453b71..1757e1e183976a6e16b36dbb3c6cf21373689b0e 100644 (file)
@@ -20,8 +20,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
+       "crypto/rand"
        "crypto/subtle"
-       "encoding/hex"
        "flag"
        "fmt"
        "log"
@@ -30,25 +30,32 @@ import (
 )
 
 var (
-       IDRaw        = flag.String("id", "", "Client identification")
-       keyPath      = flag.String("key", "", "Path to passphrase file")
-       verifierPath = flag.String("verifier", "", "Optional path to verifier")
+       keyPath  = flag.String("key", "", "Path to passphrase file")
+       verifier = flag.String("verifier", "", "Optional verifier")
+       mOpt     = flag.Int("m", govpn.DefaultM, "Argon2d memory parameter (KiBs)")
+       tOpt     = flag.Int("t", govpn.DefaultT, "Argon2d iteration parameter")
+       pOpt     = flag.Int("p", govpn.DefaultP, "Argon2d parallelizm parameter")
 )
 
 func main() {
        flag.Parse()
-       id, err := govpn.IDDecode(*IDRaw)
-       if err != nil {
-               log.Fatalln(err)
-       }
-       pub, _ := govpn.NewVerifier(id, govpn.StringFromFile(*keyPath))
-       if *verifierPath == "" {
-               fmt.Println(hex.EncodeToString(pub[:]))
-       } else {
-               verifier, err := hex.DecodeString(govpn.StringFromFile(*verifierPath))
-               if err != nil {
-                       log.Fatalln("Can not decode verifier:", err)
+       if *verifier == "" {
+               id := new([govpn.IDSize]byte)
+               if _, err := rand.Read(id[:]); err != nil {
+                       log.Fatalln(err)
                }
-               fmt.Println(subtle.ConstantTimeCompare(verifier[:], pub[:]) == 1)
+               pid := govpn.PeerId(*id)
+               v := govpn.VerifierNew(*mOpt, *tOpt, *pOpt, &pid)
+               v.PasswordApply(govpn.StringFromFile(*keyPath))
+               fmt.Println(v.LongForm())
+               fmt.Println(v.ShortForm())
+               return
+       }
+       v, err := govpn.VerifierFromString(*verifier)
+       if err != nil {
+               log.Fatalln("Can not decode verifier", err)
        }
+       pub := *v.Pub
+       v.PasswordApply(govpn.StringFromFile(*keyPath))
+       fmt.Println(subtle.ConstantTimeCompare(v.Pub[:], pub[:]) == 1)
 }
index 0eff5909fa75c7cc6f243acde2e42a0523e3fd01..d9e74813a058a56dedb05bbd065245aef47ec36c 100644 (file)
@@ -25,18 +25,18 @@ import (
 )
 
 type PeerConf struct {
-       Id         *PeerId       `json:"-"`
-       Name       string        `json:"name"`
-       Up         string        `json:"up"`
-       Down       string        `json:"down"`
-       TimeoutInt int           `json:"timeout"`
-       Timeout    time.Duration `json:"-"`
-       Noise      bool          `json:"noise"`
-       CPR        int           `json:"cpr"`
-       Verifier   string        `json:"verifier"`
+       Id          *PeerId       `json:"-"`
+       Name        string        `json:"name"`
+       Up          string        `json:"up"`
+       Down        string        `json:"down"`
+       TimeoutInt  int           `json:"timeout"`
+       Timeout     time.Duration `json:"-"`
+       Noise       bool          `json:"noise"`
+       CPR         int           `json:"cpr"`
+       VerifierRaw string        `json:"verifier"`
 
        // This is passphrase verifier
-       DSAPub *[ed25519.PublicKeySize]byte `json:"-"`
+       Verifier *Verifier
        // This field exists only on client's side
        DSAPriv *[ed25519.PrivateKeySize]byte `json:"-"`
 }
index 24a0f1f5a7dbb44ef486c3f5d1b5151418615f83..f0a8e65202130cf7302603ea21a08fa9353fa8f3 100644 (file)
@@ -142,7 +142,7 @@ func NewHandshake(addr string, conn io.Writer, conf *PeerConf) *Handshake {
                Conf:     conf,
        }
        state.dsaPubH = new([ed25519.PublicKeySize]byte)
-       copy(state.dsaPubH[:], state.Conf.DSAPub[:])
+       copy(state.dsaPubH[:], state.Conf.Verifier.Pub[:])
        HApply(state.dsaPubH)
        return &state
 }
@@ -252,7 +252,7 @@ func (h *Handshake) Server(data []byte) *Peer {
                }
                sign := new([ed25519.SignatureSize]byte)
                copy(sign[:], dec[RSize+RSize+SSize:])
-               if !ed25519.Verify(h.Conf.DSAPub, h.key[:], sign) {
+               if !ed25519.Verify(h.Conf.Verifier.Pub, h.key[:], sign) {
                        log.Println("Invalid signature from", h.addr)
                        return nil
                }
index a0b7d22f65de59d0840de73f8b4efbb8be6626ca..54c87d31e0117a300bab5a6b21fc4edd5aec1f01 100644 (file)
@@ -21,7 +21,6 @@ package govpn
 import (
        "crypto/subtle"
        "encoding/hex"
-       "errors"
        "log"
        "sync"
 
@@ -38,22 +37,6 @@ func (id PeerId) String() string {
        return hex.EncodeToString(id[:])
 }
 
-// Decode identification string.
-// It must be 32 hexadecimal characters long.
-func IDDecode(raw string) (*PeerId, error) {
-       if len(raw) != IDSize*2 {
-               return nil, errors.New("ID must be 32 characters long")
-       }
-       idDecoded, err := hex.DecodeString(raw)
-       if err != nil {
-               return nil, errors.New("ID must contain hexadecimal characters only")
-       }
-       idP := new([IDSize]byte)
-       copy(idP[:], idDecoded)
-       id := PeerId(*idP)
-       return &id, nil
-}
-
 type CipherCache struct {
        c map[PeerId]*xtea.Cipher
        l sync.RWMutex
index 1097eacf030975378344b30557bdc7d4ae4915c8..45b5dd7d01ac47022220387e90c3b040215b1e3f 100644 (file)
@@ -330,7 +330,7 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
                p.BusyR.Unlock()
                return true
        }
-       if int(p.pktSizeR) > len(data) - MinPktLength {
+       if int(p.pktSizeR) > len(data)-MinPktLength {
                return false
        }
        p.BytesPayloadIn += int64(p.pktSizeR)
index 0b8b917c5d44fb3bf3e95b3c398bdd10f4cd4f58..c9c26475e8d7bf58d1ef7468ed1a5b1c8d273f1a 100644 (file)
@@ -26,9 +26,10 @@ func (d Dummy) Write(b []byte) (int, error) {
 
 func init() {
        MTU = 1500
-       peerId, _ = IDDecode("ffffffffffffffffffffffffffffffff")
+       id := new([IDSize]byte)
+       peerId := PeerId(*id)
        conf = &PeerConf{
-               Id:      peerId,
+               Id:      &peerId,
                Timeout: time.Second * time.Duration(TimeoutDefault),
                Noise:   false,
                CPR:     0,
index 895404792cfd9edcdff67e28f43528d724adb026..fe0965348d03f7ad5bc04e11ada533d621c3c1c0 100644 (file)
@@ -20,35 +20,101 @@ package govpn
 
 import (
        "bytes"
-       "crypto/sha512"
+       "encoding/base64"
+       "errors"
+       "fmt"
        "io/ioutil"
        "log"
        "strings"
 
        "github.com/agl/ed25519"
-       "golang.org/x/crypto/pbkdf2"
+       "github.com/magical/argon2"
 )
 
 const (
-       PBKDF2Iters = 1 << 16
+       DefaultM = 1 << 12
+       DefaultT = 1 << 7
+       DefaultP = 1
 )
 
-// Create verifier from supplied password for given PeerId.
-func NewVerifier(id *PeerId, password string) (*[ed25519.PublicKeySize]byte, *[ed25519.PrivateKeySize]byte) {
-       r := pbkdf2.Key(
-               []byte(password),
-               id[:],
-               PBKDF2Iters,
-               ed25519.PrivateKeySize,
-               sha512.New,
-       )
+type Verifier struct {
+       M   int
+       T   int
+       P   int
+       Id  *PeerId
+       Pub *[ed25519.PublicKeySize]byte
+}
+
+// Generate new verifier for given peer, with specified password and
+// hashing parameters.
+func VerifierNew(m, t, p int, id *PeerId) *Verifier {
+       return &Verifier{M: m, T: t, P: p, Id: id}
+}
+
+// Apply the password: create Ed25519 keypair based on it, save public
+// key in verifier.
+func (v *Verifier) PasswordApply(password string) *[ed25519.PrivateKeySize]byte {
+       r, err := argon2.Key([]byte(password), v.Id[:], v.T, v.P, int64(v.M), 32)
+       if err != nil {
+               log.Fatalln("Unable to apply Argon2d", err)
+       }
        defer sliceZero(r)
        src := bytes.NewBuffer(r)
-       pub, priv, err := ed25519.GenerateKey(src)
+       pub, prv, err := ed25519.GenerateKey(src)
        if err != nil {
                log.Fatalln("Unable to generate Ed25519 keypair", err)
        }
-       return pub, priv
+       v.Pub = pub
+       return prv
+}
+
+// Parse either short or long verifier form.
+func VerifierFromString(input string) (*Verifier, error) {
+       s := strings.Split(input, "$")
+       if !(len(s) != 4 || len(s) != 5) || s[1] != "argon2d" {
+               return nil, errors.New("Invalid verifier structure")
+       }
+       var m, t, p int
+       n, err := fmt.Sscanf(s[2], "m=%d,t=%d,p=%d", &m, &t, &p)
+       if n != 3 || err != nil {
+               return nil, errors.New("Invalid verifier parameters")
+       }
+       salt, err := base64.RawStdEncoding.DecodeString(s[3])
+       if err != nil {
+               return nil, err
+       }
+       v := Verifier{M: m, T: t, P: p}
+       id := new([IDSize]byte)
+       copy(id[:], salt)
+       pid := PeerId(*id)
+       v.Id = &pid
+       if len(s) == 5 {
+               pub, err := base64.RawStdEncoding.DecodeString(s[4])
+               if err != nil {
+                       return nil, err
+               }
+               v.Pub = new([ed25519.PublicKeySize]byte)
+               copy(v.Pub[:], pub)
+       }
+       return &v, nil
+}
+
+// Short verifier string form -- it is useful for the client.
+// Does not include public key.
+func (v *Verifier) ShortForm() string {
+       return fmt.Sprintf(
+               "$argon2d$m=%d,t=%d,p=%d$%s",
+               v.M, v.T, v.P, base64.RawStdEncoding.EncodeToString(v.Id[:]),
+       )
+}
+
+// Long verifier string form -- it is useful for the server.
+// Includes public key.
+func (v *Verifier) LongForm() string {
+       return fmt.Sprintf(
+               "%s$%s", v.ShortForm(),
+               base64.RawStdEncoding.EncodeToString(v.Pub[:]),
+       )
 }
 
 // Read string from the file, trimming newline.
index 5c8b5db81a5525de4314951e07d8485ea2ca8d5d..89bd8c8dd9b7b11c276a554b406036bac8ab6093 100755 (executable)
@@ -8,7 +8,7 @@ release=$1
 [ -n "$release" ]
 
 git clone . $tmp/govpn-$release
-for repo in src/github.com/bigeagle/water src/github.com/agl/ed25519 src/golang.org/x/crypto; do
+for repo in src/github.com/bigeagle/water src/github.com/agl/ed25519 src/github.com/magical/argon2 src/github.com/dchest/blake2b src/golang.org/x/crypto; do
     git clone $repo $tmp/govpn-$release/$repo
 done
 cd $tmp/govpn-$release
index 9e1cb3578faaf71f131245cf2c128b2df3d07c06..e0d8c855b8fad14f1b3c8538242d0eb741c5b581 100755 (executable)
@@ -3,8 +3,8 @@
 [ -n "$1" ] || {
     cat <<EOF
 Example script for creating new user peer for GoVPN.
-It generates random client's identity, ask for passphrase, generates
-verifier and shows you example JSON entry for server configuration.
+It asks for passphrase, generates verifier and shows you example
+JSON entry for server configuration.
 
 Usage: $0 <username>
 EOF
@@ -12,30 +12,29 @@ EOF
 }
 
 username=$1
-peerid=$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | hexdump -ve '"%02x"')
-[ $(echo -n $peerid | wc -c) = 32 ] || peerid=0"$peerid"
 umask 077
 passphrase=$(mktemp)
 $(dirname $0)/storekey.sh $passphrase
-verifier=$(govpn-verifier -id $peerid -key $passphrase)
+verifier=$(govpn-verifier -key $passphrase)
 rm -f $passphrase
+verifierS=$(echo $verifier | sed 's/^\(.*\) .*$/\1/')
+verifierC=$(echo $verifier | sed 's/^.* \(.*\)$/\1/')
 echo
 
 cat <<EOF
-Your id is: $peerid
+Your client verifier is: $verifierC
 
 Place the following JSON configuration entry on the server's side:
 
-    "$peerid": {
-        "name": "$username",
+    "$username": {
         "up": "/path/to/up.sh",
-        "verifier": "$verifier"
+        "verifier": "$verifierS"
     }
 
 Verifier was generated with:
 
     $(dirname $0)/storekey.sh /tmp/passphrase
-    govpn-verifier -id $peerid -key /tmp/passphrase
+    govpn-verifier -key /tmp/passphrase
 
 Create up.sh script that will output on the first line TAP interface
 name that must be used for the peer. For example: