From: Sergey Matveev Date: Wed, 4 Nov 2015 10:00:02 +0000 (+0300) Subject: Merge branch 'develop' X-Git-Tag: 4.1^0 X-Git-Url: http://www.git.cypherpunks.ru/?a=commitdiff_plain;h=5ee5b99c6def5751dc4e1e9d36ea5fb48d61793b;hp=4a58ee4c1365408452e03535ca68146aa9cf3540;p=govpn.git Merge branch 'develop' Signed-off-by: Sergey Matveev --- diff --git a/.gitmodules b/.gitmodules index f2d6ae4..ffbe442 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 5186d07..7d5c902 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0 +4.1 diff --git a/doc/about.ru.texi b/doc/about.ru.texi index d70318f..3b2fda6 100644 --- a/doc/about.ru.texi +++ b/doc/about.ru.texi @@ -4,7 +4,7 @@ GoVPN это простой демон виртуальных частных сетей, код которого нацелен на лёгкость чтения и анализа, безопасность, устойчивость к DPI/цензуре. -@itemize @bullet +@itemize @item Свободное программное обеспечение, копилефт: лицензировано под условиями @url{https://www.gnu.org/licenses/gpl-3.0.ru.html, GPLv3+}. @@ -15,7 +15,8 @@ GoVPN это простой демон виртуальных частных с Encrypted Key Exchange)). @item Несбалансированные аутентификационные токены устойчивые к внесетевым -(offline) атакам по словарю. Злоумышленник не может замаскироваться под +(offline) атакам по словарю. Используют усиленный по CPU и памяти +алгоритм хэширования. Злоумышленник не может замаскироваться под клиента даже скомпрометировав базу данных токенов сервера. @item Зашифрованный и аутентифицируемый транспортный протокол передачи данных diff --git a/doc/about.texi b/doc/about.texi index d205a10..6e25072 100644 --- a/doc/about.texi +++ b/doc/about.texi @@ -2,7 +2,7 @@ GoVPN is simple free software virtual private network daemon, aimed to be reviewable, secure and @url{https://en.wikipedia.org/wiki/Deep_packet_inspection, DPI}/censorship-resistant. -@itemize @bullet +@itemize @item Copylefted free software: licenced under @url{https://www.gnu.org/licenses/gpl-3.0.html, GPLv3+}. @@ -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 diff --git a/doc/client.texi b/doc/client.texi index bab6a47..40f310f 100644 --- a/doc/client.texi +++ b/doc/client.texi @@ -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 diff --git a/doc/developer.texi b/doc/developer.texi index 6035225..d9ec24f 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -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 diff --git a/doc/download.texi b/doc/download.texi index 96367a1..41a2c4d 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 4.0 @tab 183 KiB +@tab @url{download/govpn-4.0.tar.xz, link} @url{download/govpn-4.0.tar.xz.sig, sign} +@tab @code{a791c3569c01dea8b18aa2f21d27b797ded76f2c33a8d96c2db864a9abf2615b} + @item 3.5 @tab 179 KiB @tab @url{download/govpn-3.5.tar.xz, link} @url{download/govpn-3.5.tar.xz.sig, sign} @tab @code{6b60c2cd4a8b4b2c893e52d3366510678704fd68a02a0ea24cb112bd753ea54b} diff --git a/doc/example.texi b/doc/example.texi index c262846..6fd62df 100644 --- a/doc/example.texi +++ b/doc/example.texi @@ -4,7 +4,7 @@ Let's assume that there is some insecure link between your computer and WiFi-reachable gateway. -@itemize @bullet +@itemize @item You have got @code{wlan0} NIC with 192.168.0/24 network on it. @item You want to create virtual encrypted and authenticated 172.16.0/24 network and use it as a default transport. @@ -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 diff --git a/doc/handshake.texi b/doc/handshake.texi index c15e456..b445985 100644 --- a/doc/handshake.texi +++ b/doc/handshake.texi @@ -34,7 +34,7 @@ symmetric encryption. @code{El()} is Elligator point encoding algorithm. @verb{|R + enc(H(DSAPub), R, El(CDHPub)) + IDtag -> Server|} [48 bytes] @item -@itemize @bullet +@itemize @item Server remembers client address. @item Decrypts @code{El(CDHPub)}. @item Inverts @code{El()} encoding and gets @code{CDHPub}. @@ -48,7 +48,7 @@ symmetric encryption. @code{El()} is Elligator point encoding algorithm. @verb{|enc(H(DSAPub), R+1, El(SDHPub)) + enc(K, R, RS + SS) + IDtag -> Client|} [80 bytes] @item -@itemize @bullet +@itemize @item Client decrypts @code{El(SDHPub)}. @item Inverts @code{El()} encoding and gets @code{SDHPub}. @item Computes @code{K}. @@ -63,7 +63,7 @@ symmetric encryption. @code{El()} is Elligator point encoding algorithm. @verb{|enc(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag -> Server|} [120 bytes] @item -@itemize @bullet +@itemize @item Server decrypts @code{RS}, @code{RC}, @code{SC}, @code{Sign(DSAPriv, K)}. @@ -81,7 +81,7 @@ symmetric encryption. @code{El()} is Elligator point encoding algorithm. @verb{|ENC(K, R+2, RC) + IDtag -> Client|} [16 bytes] @item -@itemize @bullet +@itemize @item Client decrypts @code{RC} @item Compares with its own one sent before. @item Computes final session encryption key as server did. diff --git a/doc/identity.texi b/doc/identity.texi index 46a6889..7e33b33 100644 --- a/doc/identity.texi +++ b/doc/identity.texi @@ -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. diff --git a/doc/installation.texi b/doc/installation.texi index cc5debb..d8d9587 100644 --- a/doc/installation.texi +++ b/doc/installation.texi @@ -3,7 +3,7 @@ Possibly GoVPN already exists in your distribution: -@itemize @bullet +@itemize @item @url{https://aur.archlinux.org/packages/govpn/, AUR} @item @url{http://www.freshports.org/security/govpn/, FreeBSD ports} @end itemize @@ -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 diff --git a/doc/media.texi b/doc/media.texi index 9423086..b129c0d 100644 --- a/doc/media.texi +++ b/doc/media.texi @@ -1,7 +1,7 @@ @node Media @unnumbered In the media -@itemize @bullet +@itemize @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.texi b/doc/news.texi index bdb178e..c2d06b8 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -3,8 +3,16 @@ @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 @bullet +@itemize @item Handshake messages can be noised: their messages lengths are hidden. Now they are indistinguishable from transport messages. @item Parallelized clients processing on the server side. @@ -13,7 +21,7 @@ hidden. Now they are indistinguishable from transport messages. @end itemize @item Release 3.5 -@itemize @bullet +@itemize @item Ability to use @ref{Network, TCP} network transport. Server can listen on both UDP and TCP sockets. @item Ability to use @ref{Proxy, HTTP proxies} (through CONNECT method) @@ -24,7 +32,7 @@ reasons. @end itemize @item Release 3.4 -@itemize @bullet +@itemize @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. @@ -34,7 +42,7 @@ without performance degradation related to inbound packets reordering. @end itemize @item Release 3.3 -@itemize @bullet +@itemize @item Compatibility with an old GNU Make 3.x. Previously only BSD Make and GNU Make 4.x were supported. @item /dev/urandom is used for correct client identity generation under @@ -44,7 +52,7 @@ GNU/Linux systems. Previously /dev/random can produce less than required @end itemize @item Release 3.2 -@itemize @bullet +@itemize @item Deterministic building: dependent libraries source code commits are fixed in our makefiles. @@ -56,7 +64,7 @@ FreeBSD Make compatibility. GNU Make is not necessary anymore. @end itemize @item Release 3.1 -@itemize @bullet +@itemize @item Diffie-Hellman public keys are encoded with Elligator algorithm when sending over the wire, making them indistinguishable from the random @@ -66,7 +74,7 @@ consume twice entropy for DH key generation in average. @end itemize @item Release 3.0 -@itemize @bullet +@itemize @item EKE protocol is replaced by Augmented-EKE and static symmetric (both sides have it) pre-shared key replaced with server-side verifier. This @@ -98,7 +106,7 @@ Per-peer @code{-timeout}, @code{-noncediff}, @code{-noise} and @end itemize @item Release 2.4 -@itemize @bullet +@itemize @item Added ability to optionally run built-in HTTP-server responding with JSON of all known connected peers information. Real-time client's @@ -109,7 +117,7 @@ Documentation is explicitly licenced under GNU FDL 1.3+. @end itemize @item Release 2.3 -@itemize @bullet +@itemize @item Handshake packets became indistinguishable from the random. Now all GoVPN's traffic is the noise for men in the middle. @@ -124,46 +132,46 @@ consuming and resource heavy computations. @end itemize @item Release 2.2 -@itemize @bullet +@itemize @item Fixed several possible channel deadlocks. @end itemize @item Release 2.1 -@itemize @bullet +@itemize @item Fixed Linux-related building. @end itemize @item Release 2.0 -@itemize @bullet +@itemize @item Added clients identification. @item Simultaneous several clients support by server. @item Per-client up/down scripts. @end itemize @item Release 1.5 -@itemize @bullet +@itemize @item Nonce obfuscation/encryption. @end itemize @item Release 1.4 -@itemize @bullet +@itemize @item Performance optimizations. @end itemize @item Release 1.3 -@itemize @bullet +@itemize @item Heartbeat feature. @item Rehandshake feature. @item up- and down- optinal scripts. @end itemize @item Release 1.1 -@itemize @bullet +@itemize @item FreeBSD support. @end itemize @item Release 1.0 -@itemize @bullet +@itemize @item Initial stable release. @end itemize diff --git a/doc/pake.texi b/doc/pake.texi index d2cb773..fa8a322 100644 --- a/doc/pake.texi +++ b/doc/pake.texi @@ -5,7 +5,7 @@ Previously we used pre-shared high-entropy long-term static key for client-server authentication. Is is secure, but not convenient for some user use-cases: -@itemize @bullet +@itemize @item Compromising of passphrase files on either server or client side allows attacker to masquerade himself a client. @item To prevent compromising of keys on the client side, one needs some @@ -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. diff --git a/doc/precautions.texi b/doc/precautions.texi index fad882d..fbf45d1 100644 --- a/doc/precautions.texi +++ b/doc/precautions.texi @@ -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. diff --git a/doc/server.texi b/doc/server.texi index 49a09fb..a00c452 100644 --- a/doc/server.texi +++ b/doc/server.texi @@ -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 diff --git a/doc/sources.texi b/doc/sources.texi index 40aa499..f2dd06d 100644 --- a/doc/sources.texi +++ b/doc/sources.texi @@ -23,7 +23,9 @@ repositories will be unavailable (they are seldom updated): @multitable @columnfractions .50 .50 @headitem Software/library @tab Mirror @item @code{govpn} @tab @url{https://github.com/stargrave/govpn.git} -@item @code{golang.org/x/crypto} @tab @url{git://git.cypherpunks.ru/crypto.git} @item @code{github.com/agl/ed25519} @tab @url{git://git.cypherpunks.ru/ed25519.git} @item @code{github.com/bigeagle/water} @tab @url{git://git.cypherpunks.ru/water.git} +@item @code{github.com/dchest/blake2b} @tab @url{git://git.cypherpunks.ru/blake2b.git} +@item @code{github.com/magical/argon2} @tab @url{git://git.cypherpunks.ru/argon2.git} +@item @code{golang.org/x/crypto} @tab @url{git://git.cypherpunks.ru/crypto.git} @end multitable diff --git a/doc/thanks.texi b/doc/thanks.texi index b50bc9c..5b9dba3 100644 --- a/doc/thanks.texi +++ b/doc/thanks.texi @@ -3,7 +3,7 @@ Thanks for contributions and suggestions to: -@itemize @bullet +@itemize @item @url{https://www.schneier.com/books/applied_cryptography/, Applied Cryptography} @copyright{} 1996 Bruce Schneier. @@ -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 diff --git a/doc/todo.texi b/doc/todo.texi index f7d00f3..520192c 100644 --- a/doc/todo.texi +++ b/doc/todo.texi @@ -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. diff --git a/doc/verifier.texi b/doc/verifier.texi index 61b1e44..07ba198 100644 --- a/doc/verifier.texi +++ b/doc/verifier.texi @@ -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 diff --git a/doc/verifierstruct.texi b/doc/verifierstruct.texi index 65a4e08..792506d 100644 --- a/doc/verifierstruct.texi +++ b/doc/verifierstruct.texi @@ -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 index 0000000..3c8c640 --- /dev/null +++ b/src/github.com/dchest/blake2b @@ -0,0 +1 @@ +Subproject commit 3c8c640cd7bea3ca78209d812b5854442ab92fed diff --git a/src/github.com/magical/argon2 b/src/github.com/magical/argon2 new file mode 160000 index 0000000..82d59eb --- /dev/null +++ b/src/github.com/magical/argon2 @@ -0,0 +1 @@ +Subproject commit 82d59eb7dab9a6268371a8c6de2100a2c7357bc6 diff --git a/src/govpn/cmd/govpn-client/main.go b/src/govpn/cmd/govpn-client/main.go index 6c611e7..09b06ca 100644 --- a/src/govpn/cmd/govpn-client/main.go +++ b/src/govpn/cmd/govpn-client/main.go @@ -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) @@ -108,6 +107,9 @@ MainCycle: timeouted := make(chan struct{}) rehandshaking := make(chan struct{}) termination := make(chan struct{}) + if *proxyAddr != "" { + *proto = "tcp" + } switch *proto { case "udp": go startUDP(timeouted, rehandshaking, termination) diff --git a/src/govpn/cmd/govpn-server/common.go b/src/govpn/cmd/govpn-server/common.go index 4692e52..f2cc0d3 100644 --- a/src/govpn/cmd/govpn-server/common.go +++ b/src/govpn/cmd/govpn-server/common.go @@ -1,3 +1,21 @@ +/* +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 main import ( diff --git a/src/govpn/cmd/govpn-server/conf.go b/src/govpn/cmd/govpn-server/conf.go index 4136fab..76d56cd 100644 --- a/src/govpn/cmd/govpn-server/conf.go +++ b/src/govpn/cmd/govpn-server/conf.go @@ -19,14 +19,11 @@ along with this program. If not, see . 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 } diff --git a/src/govpn/cmd/govpn-verifier/main.go b/src/govpn/cmd/govpn-verifier/main.go index ede2de7..1757e1e 100644 --- a/src/govpn/cmd/govpn-verifier/main.go +++ b/src/govpn/cmd/govpn-verifier/main.go @@ -20,8 +20,8 @@ along with this program. If not, see . 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) } diff --git a/src/govpn/conf.go b/src/govpn/conf.go index 0eff590..d9e7481 100644 --- a/src/govpn/conf.go +++ b/src/govpn/conf.go @@ -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:"-"` } diff --git a/src/govpn/handshake.go b/src/govpn/handshake.go index 0aa65bd..f0a8e65 100644 --- a/src/govpn/handshake.go +++ b/src/govpn/handshake.go @@ -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 } @@ -191,7 +191,7 @@ func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake { // authenticated Peer is ready, then return nil. func (h *Handshake) Server(data []byte) *Peer { // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag - if h.rNonce == nil { + if h.rNonce == nil && len(data) >= 48 { // Generate DH keypair var dhPubRepr *[32]byte h.dhPriv, dhPubRepr = dhKeypairGen() @@ -237,7 +237,7 @@ func (h *Handshake) Server(data []byte) *Peer { h.LastPing = time.Now() } else // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag - if h.rClient == nil { + if h.rClient == nil && len(data) >= 120 { // Decrypted Rs compare rServer dec := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) salsa20.XORKeyStream( @@ -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 } @@ -290,7 +290,7 @@ func (h *Handshake) Server(data []byte) *Peer { // authenticated Peer is ready, then return nil. func (h *Handshake) Client(data []byte) *Peer { // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag - if h.rServer == nil && h.key == nil { + if h.rServer == nil && h.key == nil && len(data) >= 80 { // Decrypt remote public key and compute shared key sDHRepr := new([32]byte) salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH) @@ -334,7 +334,7 @@ func (h *Handshake) Client(data []byte) *Peer { h.LastPing = time.Now() } else // ENC(K, R+2, RC) + IDtag - if h.key != nil { + if h.key != nil && len(data) >= 16 { // Decrypt rClient dec := make([]byte, RSize) salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key) diff --git a/src/govpn/identify.go b/src/govpn/identify.go index a0b7d22..54c87d3 100644 --- a/src/govpn/identify.go +++ b/src/govpn/identify.go @@ -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 diff --git a/src/govpn/peer.go b/src/govpn/peer.go index 1076e74..45b5dd7 100644 --- a/src/govpn/peer.go +++ b/src/govpn/peer.go @@ -1,3 +1,21 @@ +/* +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 ( @@ -242,6 +260,9 @@ func (p *Peer) EthProcess(data []byte) { } func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool { + if len(data) < MinPktLength { + return false + } p.BusyR.Lock() for i := 0; i < SSize; i++ { p.bufR[i] = byte(0) @@ -309,6 +330,9 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool { p.BusyR.Unlock() return true } + if int(p.pktSizeR) > len(data)-MinPktLength { + return false + } p.BytesPayloadIn += int64(p.pktSizeR) tap.Write(p.bufR[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSizeR]) p.BusyR.Unlock() diff --git a/src/govpn/peer_test.go b/src/govpn/peer_test.go index 9bad8c6..c9c2647 100644 --- a/src/govpn/peer_test.go +++ b/src/govpn/peer_test.go @@ -26,12 +26,13 @@ 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, - Timeout: time.Second * time.Duration(TimeoutDefault), - NoiseEnable: false, - CPR: 0, + Id: &peerId, + Timeout: time.Second * time.Duration(TimeoutDefault), + Noise: false, + CPR: 0, } peer = newPeer(true, "foo", Dummy{&ciphertext}, conf, new([SSize]byte)) plaintext = make([]byte, 789) diff --git a/src/govpn/stats.go b/src/govpn/stats.go index 8d39e7a..7cdb3b7 100644 --- a/src/govpn/stats.go +++ b/src/govpn/stats.go @@ -1,3 +1,21 @@ +/* +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 ( diff --git a/src/govpn/verifier.go b/src/govpn/verifier.go index 8954047..fe09653 100644 --- a/src/govpn/verifier.go +++ b/src/govpn/verifier.go @@ -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. diff --git a/utils/makedist.sh b/utils/makedist.sh index 10f18d9..89bd8c8 100755 --- a/utils/makedist.sh +++ b/utils/makedist.sh @@ -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 @@ -133,6 +133,7 @@ $(git cat-file -p $release | sed -n '6,/^.*BEGIN/p' | sed '$d') ----------------8<-----------------8<-----------------8<---------------- Домашняя страница GoVPN: http://govpn.info -> http://www.cypherpunks.ru/govpn/ +Коротко о демоне: http://www.cypherpunks.ru/govpn/About-RU.html также доступна как скрытый сервис Tor: http://vabu56j2ep2rwv3b.onion/govpn/ Исходный код и его подпись для этой версии находится здесь: diff --git a/utils/newclient.sh b/utils/newclient.sh index 9e1cb35..e0d8c85 100755 --- a/utils/newclient.sh +++ b/utils/newclient.sh @@ -3,8 +3,8 @@ [ -n "$1" ] || { cat < 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 <