]> Cypherpunks.ru repositories - govpn.git/commitdiff
Merge branch 'develop' 5.0
authorSergey Matveev <stargrave@stargrave.org>
Wed, 6 Jan 2016 16:36:16 +0000 (19:36 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Wed, 6 Jan 2016 16:36:16 +0000 (19:36 +0300)
Signed-off-by: Sergey Matveev <stargrave@stargrave.org>
67 files changed:
README
VERSION
common.mk
doc/Makefile
doc/about.ru.texi
doc/about.texi
doc/client.texi
doc/contacts.texi
doc/cpr.texi
doc/developer.texi
doc/download.texi
doc/egd.texi
doc/encless.texi [new file with mode: 0644]
doc/example.texi
doc/faq.ru.texi
doc/faq.texi
doc/glossary.texi [new file with mode: 0644]
doc/govpn.texi
doc/handshake.texi
doc/handshake.txt
doc/identity.texi
doc/mtu.texi
doc/netproto.texi
doc/news.texi
doc/noise.texi
doc/pake.texi
doc/proxy.texi
doc/server.texi
doc/stats.texi
doc/thanks.texi
doc/timeout.texi
doc/transport.texi
doc/user.texi
doc/verifier.texi
src/govpn/aont/aont_test.go [new file with mode: 0644]
src/govpn/aont/oaep.go [new file with mode: 0644]
src/govpn/cmd/govpn-client/main.go
src/govpn/cmd/govpn-client/proxy.go
src/govpn/cmd/govpn-client/tcp.go
src/govpn/cmd/govpn-client/udp.go
src/govpn/cmd/govpn-server/common.go
src/govpn/cmd/govpn-server/conf.go
src/govpn/cmd/govpn-server/main.go
src/govpn/cmd/govpn-server/proxy.go
src/govpn/cmd/govpn-server/tcp.go
src/govpn/cmd/govpn-server/udp.go
src/govpn/cmd/govpn-verifier/main.go
src/govpn/cnw/cnw.go [new file with mode: 0644]
src/govpn/cnw/cnw_test.go [new file with mode: 0644]
src/govpn/common.go
src/govpn/conf.go
src/govpn/egd.go
src/govpn/encless.go [new file with mode: 0644]
src/govpn/encless_test.go [new file with mode: 0644]
src/govpn/govpn.go
src/govpn/handshake.go
src/govpn/handshake_test.go [new file with mode: 0644]
src/govpn/identify.go
src/govpn/peer.go
src/govpn/peer_test.go
src/govpn/stats.go
src/govpn/tap.go
src/govpn/tap_freebsd.go
src/govpn/tap_linux.go
src/govpn/verifier.go
utils/makedist.sh
utils/newclient.sh

diff --git a/README b/README
index 7cbbb9470f27afad83764ac341c8ad666167ffff..b95b958ca48acb31fefb3d01a6983968dcc3a1a2 100644 (file)
--- a/README
+++ b/README
@@ -4,11 +4,13 @@ be reviewable, secure, DPI/censorship-resistant, written on Go.
 It uses fast strong passphrase authenticated key agreement protocol with
 augmented zero-knowledge mutual peers authentication (PAKE DH A-EKE).
 Encrypted, authenticated data transport that hides message's length and
-timestamps. Perfect forward secrecy property. Resistance to: offline
-dictionary attacks, replay attacks, client's passphrases compromising
-and dictionary attacks on the server side. Built-in heartbeating,
-rehandshaking, real-time statistics. Ability to work through UDP, TCP
-and HTTP proxies. IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support.
+timestamps. Optional encryptionless mode, that still preserves data
+confidentiality. Perfect forward secrecy property. Resistance to:
+offline dictionary attacks, replay attacks, client's passphrases
+compromising and dictionary attacks on the server side. Built-in
+heartbeating, rehandshaking, real-time statistics. Ability to work
+through UDP, TCP and HTTP proxies. IPv4/IPv6-compatibility.
+GNU/Linux and FreeBSD support.
 
 GoVPN is free software: see the file COPYING for copying conditions.
 
@@ -17,7 +19,7 @@ also available as Tor hidden service: http://vabu56j2ep2rwv3b.onion/govpn/
 
 Please send questions regarding the use of GoVPN, bug reports and
 patches to govpn-devel mailing list:
-https://lists.cypherpunks.ru/mailman/listinfo/govpn-devel
+https://lists.cypherpunks.ru/pipermail/govpn-devel/
 
 Development Git source code repository currently is located here:
 http://git.cypherpunks.ru/cgit.cgi/govpn.git/
diff --git a/VERSION b/VERSION
index bf77d549685a9e09678fbbda05a071b312cf2de3..819e07a22435f1e8efcbdd1d1c062deef0e501b1 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.2
+5.0
index 34fd0b56102c6f8ebd12096a0cc9f6310738bef9..a6b9e084b02f45669ee3b17836ea7a20442b0cef 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -17,7 +17,7 @@ govpn-verifier:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" govpn/cmd/govpn-verifier
 
 bench:
-       cd src/govpn ; GOPATH=$(GOPATH) go test -bench .
+       GOPATH=$(GOPATH) go test -bench . govpn/...
 
 clean:
        rm -f govpn-client govpn-server govpn-verifier
index ec5af2ac7212528ccb259acb9f36f228c0727773..b0a891cee96df51bd43ea9e96c725b432e60fdd5 100644 (file)
@@ -2,12 +2,12 @@ all: govpn.info govpn.html
 
 MAKEINFO ?= makeinfo
 
-govpn.info: *.texi handshake.utxt
-       $(MAKEINFO) govpn.texi
-
 handshake.utxt: handshake.txt
        plantuml -tutxt handshake.txt
 
+govpn.info: *.texi handshake.utxt
+       $(MAKEINFO) govpn.texi
+
 govpn.html: *.texi handshake.utxt
        rm -f govpn.html/*.html
        $(MAKEINFO) --html -o govpn.html govpn.texi
index 31386a5f7d3ae84aace61737289fc20f49922200..2797a6317b75e5d7ed9b4ebe97c1d62e1eb25375 100644 (file)
@@ -24,6 +24,12 @@ A-EKE (Diffie-Hellman Augmented Encrypted Key Exchange)).
 передачи данных с 128-бит @ref{Developer, порогом безопасности} и
 современной криптографией.
 @item
+Опциональный @ref{Encless, нешифрованный режим}: функции шифрования не
+применяются для исходящего трафика, вместо них кодирование всё-равно
+обеспечивающее конфиденциальность. Юрисдикции и суды не могут вас
+вынудить выдать ключи шифрования или привлечь за использование
+шифрования.
+@item
 Цензуроустойчивые сообщения транспорта и рукопожатия: неотличимые от
 шума с опциональным скрытием размеров сообщений.
 @item
@@ -60,9 +66,9 @@ A-EKE (Diffie-Hellman Augmented Encrypted Key Exchange)).
 статистики} о подключённых клиентах в режиме реального времени в
 @url{http://json.org/, JSON} формате.
 @item
-Написан на языке @url{http://golang.org/, Go} с простым кодом,
+Написан на языке @url{https://golang.org/, Go} с простым кодом,
 ориентированным на лёгкость чтения и анализа.
 @item
 Поддержка @url{https://www.gnu.org/, GNU}/Linux и
-@url{http://www.freebsd.org/, FreeBSD}.
+@url{https://www.freebsd.org/, FreeBSD}.
 @end itemize
index 6e25072188f06c2bf3ff63f30c5eeeaa500c96ea..03b5c8966a526ed880bd34b780282128e253b874 100644 (file)
@@ -21,8 +21,13 @@ Encrypted and authenticated @ref{Transport, payload transport}
 with 128-bit @ref{Developer, security margin} state-of-the-art
 cryptography.
 @item
+Optional @ref{Encless, encryptionless mode} of operation: no encryption
+functions are applied for outgoing traffic, but still confidentiality
+preserving encoding. Jurisdictions and courts can not either force you
+to reveal encryption keys or sue for encryption usage.
+@item
 Censorship resistant handshake and transport messages: fully
-indistinguishable from the noise with optionally hidden packets lengths.
+indistinguishable from the noise with optionally hidden packets length.
 @item
 @url{https://en.wikipedia.org/wiki/Forward_secrecy, Perfect forward secrecy}
 property.
@@ -55,9 +60,9 @@ Optional built-in HTTP-server for retrieving real-time
 @ref{Stats, statistics} information about known connected peers in
 @url{http://json.org/, JSON} format.
 @item
-Written on @url{http://golang.org/, Go} programming language with
+Written on @url{https://golang.org/, Go} programming language with
 simple code that can be read and reviewed.
 @item
 @url{https://www.gnu.org/, GNU}/Linux and
-@url{http://www.freebsd.org/, FreeBSD} support.
+@url{https://www.freebsd.org/, FreeBSD} support.
 @end itemize
index 40f310f4e145843d6e3b7b8f4f81a0322fb5cc7e..88e338e3090de2c05cf0c89241e4294712101e21 100644 (file)
@@ -1,11 +1,14 @@
 @node Client
 @section Client part
 
-Except for common @code{-mtu}, @code{-stats}, @code{-egd}
-options client has the following ones:
+Except for common @code{-stats}, @code{-egd} options client has the
+following ones:
 
 @table @code
 
+@item -mtu
+Expected TAP interface @ref{MTU}.
+
 @item -proto
 @ref{Network, network protocol} to use. Can be either @emph{udp}
 (default) or @emph{tcp}.
@@ -40,6 +43,9 @@ Enable @ref{Noise}.
 @item -cpr
 Set @ref{CPR} in KiB/sec.
 
+@item -encless
+Enable @ref{Encless, encryptionless mode}.
+
 @item -up
 Optional path to script that will be executed after connection is
 established. Interface name will be given to it as a first argument.
index 262accada3697c33adcacc580f5f5fb487b73382..3f96135deda517e4fc4fd734226e15b455504a1b 100644 (file)
@@ -2,7 +2,7 @@
 @unnumbered Contacts
 
 Please send questions regarding the use of GoVPN, bug reports and patches to
-@url{https://lists.cypherpunks.ru/mailman/listinfo/govpn-devel, govpn-devel}
+@url{https://lists.cypherpunks.ru/pipermail/govpn-devel/, govpn-devel}
 mailing list. Announcements also go to this mailing list.
 
 Official website is @url{http://www.cypherpunks.ru/govpn/}
index 5256a81e7f0c565707ce33478b659ed641f40bbe..5ea57177df8520442865eadd9ce1eab07ac5cc67 100644 (file)
@@ -1,5 +1,5 @@
 @node CPR
-@section Constant Packet Rate
+@subsection Constant Packet Rate
 
 Constant Packet Rate is used to hide fact of underlying payload packets
 appearance. In this mode daemon inserts necessary dummy packets and
@@ -7,4 +7,4 @@ delays other ones.
 
 This mode is turned by @code{-cpr} option, where you specify desired
 outgoing traffic rate in KiB/sec (kibibytes per second). This option also
-@strong{forces} using of the @ref{Noise}! It is turned off by default.
+@strong{forces} using of the @ref{Noise, noise}! It is turned off by default.
index d9ec24fffbcdff245b5983402cfd8e400e2e92a5..4293f800c06d8084790c564e26768c911d4509a4 100644 (file)
@@ -5,26 +5,37 @@ Pay attention how to get @ref{Sources, development source code}.
 
 @table @asis
 @item Nonce and identity encryption
-@url{http://143.53.36.235:8080/tea.htm, XTEA}.
+    @url{http://www.cix.co.uk/~klockstone/xtea.pdf, 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}.
+    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}.
+    @url{http://elligator.cr.yp.to/, Elligator}.
 @item Verifier password hashing algorithm
-@url{https://password-hashing.net/#argon2, Argon2d}.
+    @url{https://password-hashing.net/#argon2, Argon2d}.
+@item Encryptionless confidentiality preserving encoding
+    @url{http://people.csail.mit.edu/rivest/chaffing-980701.txt,
+    Chaffing-and-Winnowing} (two Poly1305 MACs for each bit of message)
+    over 128 bits of
+    @url{http://theory.lcs.mit.edu/~cis/pubs/rivest/fusion.ps,
+    All-Or-Nothing-Transformed} (based on
+    @url{http://cseweb.ucsd.edu/~mihir/papers/oaep.html, OAEP} using
+    Salsa20 with @url{https://blake2.net/, BLAKE2b-256} based
+    @url{http://crypto.stanford.edu/~dabo/abstracts/saep.html, SAEP+}
+    checksums) data with 128-bits of feeded random.
 @item Packet overhead
-26 bytes per packet.
+    25 bytes per packet. Plus 4128 bytes and noise in encryptionless mode.
 @item Handshake overhead
-4 UDP (2 from client, 2 from server) packets (round-trips for TCP),
-264 bytes total payload.
+    4 UDP (2 from client, 2 from server) packets (round-trips for TCP).
+    264 bytes total payload, 20680 in encryptionless mode.
 @item Entropy required
-832 bits in average on client, 832 bits in average on server side per
-handshake.
+    832 bits in average on client, 832 bits in average on server side
+    per handshake. 128 bits for each outgoing packet in encryptionless
+    mode.
 @end table
 
 @menu
index 94c6a222e9e71b72d1c1a75c1cf2283cde250521..029c963a28d620a6b10699e65e368a2391b66412 100644 (file)
@@ -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.2 @tab 233 KiB
+@tab @url{download/govpn-4.2.tar.xz, link} @url{download/govpn-4.2.tar.xz.sig, sign}
+@tab @code{dc2d390b9dcfb30a3612018d410b61ddf8edd82f4d9aa5ed2691b027be10ba0a}
+
 @item 4.1 @tab 227 KiB
 @tab @url{download/govpn-4.1.tar.xz, link} @url{download/govpn-4.1.tar.xz.sig, sign}
 @tab @code{fbc7a730afe96384827dc1e1402c53165710ade5113d90531427c39172e40aca}
index 62d1d87a2e73c60d6e5085844e7156c07d3981b9..c0006db3ac601ae52bb3595339956d8409d75b20 100644 (file)
@@ -1,5 +1,5 @@
 @node EGD
-@section Entropy Gathering Daemon
+@subsection Entropy Gathering Daemon
 
 Overall security mainly depends on client side:
 @ref{PAKE, good passphrase} and cryprographically good pseudo random
diff --git a/doc/encless.texi b/doc/encless.texi
new file mode 100644 (file)
index 0000000..6d44191
--- /dev/null
@@ -0,0 +1,36 @@
+@node Encless
+@subsection Encryptionless mode
+
+Some jurisdictions can force user to reveal his encryption keys. However
+they can not ask for authentication (signing) keys. So you are safe to
+use authentication algorithms, but not the encryption ones. Moreover
+some countries forbids usage of encryption (but again not the
+authentication).
+
+GoVPN provides special encryptionless mode of operation. In this mode it
+replaces Salsa20 function used for confidentiality with rather
+well-known @url{http://people.csail.mit.edu/rivest/chaffing-980701.txt,
+Chaffing-and-Winnowing} (CnW) technology. This is rather traffic and
+resource hungry algorithm, so we use it after
+@url{http://theory.lcs.mit.edu/~cis/pubs/rivest/fusion.ps,
+All-Or-Nothing-Transformation} (based on
+@url{http://cseweb.ucsd.edu/~mihir/papers/oaep.html, Optimal Asymmetric
+Encryption Padding}) on the data. This is confidentiality preserving
+encoding.
+
+AONT is just a keyless encoding of the data. CnW uses only
+authentication function. Handshake additionally uses Diffie-Hellman and
+signature algorithms. No encryption and steganography involved.
+
+In this mode each outgoing packet became larger on 4128 bytes and
+@ref{Noise, noise} is forcefully enabled. So this is resource hungry mode!
+
+@strong{Beware}: by default packet serial numbers are still processed
+through the XTEA encryption. It is not required for confidentiality and
+security, but for randomizing some parts of the traffic to make it
+indistinguishable from the noise, for making it more DPI-proof. It
+safely can be disabled, turned off or maybe its keys even can be
+revealed without security and forward secrecy loss.
+
+See @code{govpn/cnw} and @code{govpn/aont} packages for details of AONT
+and chaffing operations.
index 6fd62dfd12becda2f470b11ec204e98793b4bbde..c2936a61414fb66484e3fd461f5d2c107f293355 100644 (file)
@@ -8,10 +8,9 @@ WiFi-reachable gateway.
 @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.
-@item @code{wlan0} MTU is 1500, 20 bytes overhead per IPv4. So MTU for
-GoVPN is 1500 - 20 - 8 = 1472.
-@item During startup client and server will say that TAP interface MTU
-is 1432.
+@item Assume that outgoing GoVPN packets can be fragmented, so we do not
+bother configuring MTU of TAP interfaces. For better performance just
+lower it and check that no fragmentation of outgoing UDP packets occurs.
 @end itemize
 
 @strong{Install}. At first you must @ref{Installation, install} this
@@ -29,6 +28,7 @@ Place the following JSON configuration entry on the server's side:
 
     "Alice": {
         "up": "/path/to/up.sh",
+        "iface": "or TAP interface name",
         "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
     }
 
@@ -39,17 +39,23 @@ Verifier was generated with:
 @end verbatim
 
 @strong{Prepare the server}. Add this entry to @code{peers.json}
-configuration file.
+configuration file:
+
+@verbatim
+{
+    "Alice": {
+        "iface": "tap10",
+        "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
+    }
+}
+@end verbatim
 
 @strong{Prepare network on GNU/Linux IPv4 server}:
 
 @example
 server% umask 077
-server% echo "#!/bin/sh" > /path/to/up.sh
-server% echo "echo tap10" >> /path/to/up.sh
 server% ip addr add 192.168.0.1/24 dev wlan0
 server% tunctl -t tap10
-server% ip link set mtu 1432 dev tap10
 server% ip addr add 172.16.0.1/24 dev tap10
 server% ip link set up dev tap10
 @end example
@@ -57,7 +63,7 @@ server% ip link set up dev tap10
 @strong{Run server daemon itself}:
 
 @example
-server% govpn-server -bind 192.168.0.1:1194 -mtu 1472
+server% govpn-server -bind 192.168.0.1:1194
 @end example
 
 @strong{Prepare network on GNU/Linux IPv4 client}:
@@ -67,7 +73,6 @@ client% umask 066
 client% utils/storekey.sh key.txt
 client% ip addr add 192.168.0.2/24 dev wlan0
 client% tunctl -t tap10
-client% ip link set mtu 1432 dev tap10
 client% ip addr add 172.16.0.2/24 dev tap10
 client% ip link set up dev tap10
 client% ip route add default via 172.16.0.1
@@ -79,8 +84,7 @@ client% govpn-client \
     -key key.txt \
     -verifier '$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg' \
     -iface tap10 \
-    -remote 192.168.0.1:1194 \
-    -mtu 1472
+    -remote 192.168.0.1:1194
 @end example
 
 @strong{FreeBSD IPv6 similar client-server example}:
@@ -93,7 +97,7 @@ server% govpn-server -bind "fe80::1%em0"
 @example
 client% ifconfig me0 inet6 -ifdisabled auto_linklocal
 client% ifconfig tap10
-client% ifconfig tap10 inet6 fc00::2/96 mtu 1412 up
+client% ifconfig tap10 inet6 fc00::2/96 up
 client% route -6 add default fc00::1
 client% govpn-client \
     -key key.txt \
index 2023b76654c4cc14480f86a575a56bd4563af561..10f7fda6672b2dc0ad4683856d80e4abdb8b556d 100644 (file)
@@ -67,6 +67,17 @@ Go очень легко читается, поддаётся ревью и по
 вид трафика от другого, то при цензуре ваше единственный вариант это
 заблокировать все его виды.
 
+@item Когда я должен использовать @ref{Encless, нешифрованный режим}?
+Если вы работаете под юрисдикциями где суды могут привлечь вас к
+ответственности за использование шифрования или могут вынудить вас
+как-либо выдать ваши ключи шифрования (хотя сессионные ключи шифрования
+генерируются каждую сессию). В большинстве случаев, эти суды не могут
+требовать аутентификационные ключи или ключи для ЭЦП. @strong{Не
+позволяйте} названию режима вас смутить: он всё-равно обеспечивает
+конфиденциальность и аутентичность передаваемых данных! Но имейте в
+виду, что этот режим требователен к ресурсам и трафику и пока работает
+только в TCP режиме.
+
 @item Когда я должен использовать @ref{Noise, noise} опцию?
 В большинстве случаев она вам не нужна без включённого
 @ref{CPR, постоянного по скорости трафика} (CPR). Без CPR и шума, в
index a3269f31a00070bec5b02f2c6c77fd1d9ab65eef..0ac8de80d22a9573668da03bed7b689da19b7579 100644 (file)
@@ -65,6 +65,16 @@ just @code{cat /dev/urandom | nc somehost}. If you can not differentiate
 one kind of traffic from another, then your only option is to forbid all
 kinds of it.
 
+@item When should I use @ref{Encless, encryptionless mode}?
+If you are operating under jurisdiction where courts can either sue you
+for encryption usage or force you to somehow reveal you encryption
+keys (however new session encryption keys are generated each session).
+Those courts can not demand for authentication and signing keys in most
+cases. @strong{Do not} let mode's name to confuse you: it still
+provides confidentiality and authenticity of transmitted data! But pay
+attention that this mode is traffic and resource hungry and currently
+operate only in TCP mode.
+
 @item When should I use @ref{Noise, noise} option?
 In most cases you won't need it without @ref{CPR, constant packer rate}
 turned on. Without CPR and noise options GoVPN traffic (like TLS, IPsec,
diff --git a/doc/glossary.texi b/doc/glossary.texi
new file mode 100644 (file)
index 0000000..46b8e70
--- /dev/null
@@ -0,0 +1,30 @@
+@node Glossary
+@section Glossary
+
+@menu
+* Entropy gathering daemon: EGD.
+* Identity::
+* Password Authenticated Key Agreement: PAKE.
+* Timeout::
+* Network transport: Network.
+* Proxy::
+* Maximum Transmission Unit: MTU.
+* Statistics: Stats.
+* Noise::
+* Constant Packet Rate: CPR.
+* Encryptionless mode: Encless.
+* Verifier::
+@end menu
+
+@include egd.texi
+@include identity.texi
+@include pake.texi
+@include timeout.texi
+@include netproto.texi
+@include proxy.texi
+@include mtu.texi
+@include stats.texi
+@include noise.texi
+@include cpr.texi
+@include encless.texi
+@include verifier.texi
index ca719eaffb4d7679cf451b18c1ea7e0985a50208..7fab9f37734bf745115d85b7c3828e6cd07e10be 100644 (file)
@@ -8,7 +8,7 @@ This manual is for GoVPN -- simple free software virtual private network
 daemon, aimed to be reviewable, secure, DPI/censorship-resistant,
 written on Go.
 
-Copyright @copyright{} 2014-2015 @email{stargrave@@stargrave.org, Sergey Matveev}
+Copyright @copyright{} 2014-2016 @email{stargrave@@stargrave.org, Sergey Matveev}
 
 @quotation
 Permission is granted to copy, distribute and/or modify this document
@@ -24,6 +24,8 @@ A copy of the license is included in the section entitled "Copying conditions".
 
 @include about.texi
 
+@center @strong{@ref{Tarballs, Download it}}.
+
 @menu
 * About (russian): О демоне.
 * Frequently Asked Questions: FAQ.
index b445985240b447553293b1ff08537b790fa679ec..29f59f8b44f989e3c2c23d2bd1a64d3cec32dd58 100644 (file)
@@ -9,7 +9,7 @@ a key. It is used to transmit identity and to mark packet as handshake
 message.
 
 If @ref{Noise} is enabled, then junk data is inserted before
-@code{IDtag} to full up packet to MTU's size.
+@code{IDtag} to fill up packet to MTU's size.
 
 @strong{Preparation stage}:
 
@@ -93,3 +93,6 @@ symmetric encryption. @code{El()} is Elligator point encoding algorithm.
 has 128-bit security margin and that is why are not in use except in
 handshake process. @code{R*} are required for handshake randomization
 and two-way authentication.
+
+In @ref{Encless, encryptionless mode} each @code{enc()} is replaced with
+AONT and chaffing function over the noised data.
index 8804ae1cc684211b3f248d5cbf22824920de57cd..43ed4ff56546cb5195f5daa7f89a5cad8bcbba95 100644 (file)
@@ -1,9 +1,13 @@
 @startuml
+hide footbox
 participant Client
 participant Server
 
+== Preparation ==
 Client -> Client : R=rand(64bit)
 Client -> Client : CDHPriv=rand(256bit)
+
+== Interaction ==
 Client -> Server : R, enc(H(DSAPub), R, El(CDHPub))
 Server -> Server : SDHPriv=rand(256bit)
 Server -> Server : K=H(DH(SDHPriv, CDHPub))
@@ -15,8 +19,10 @@ Client -> Client : RC=rand(64bit); SC=rand(256bit)
 Client -> Server : enc(K, R+1, RS+RC+SC+Sign(DSAPriv, K))
 Server -> Server : compare(RS)
 Server -> Server : Verify(DSAPub, Sign(DSAPriv, K), K)
-Server -> Server : MasterKey=SS XOR SC
 Server -> Client : enc(K, R+2, RC)
+
+== Finalizing ==
 Client -> Client : compare(RC)
 Client -> Client : MasterKey=SS XOR SC
+Server -> Server : MasterKey=SS XOR SC
 @enduml
index 3153abd9489ef509a4b8a63606b8b6b62b92e8d2..3a37790cab5f9b132a69ef345058089d60d322f3 100644 (file)
@@ -1,5 +1,5 @@
 @node Identity
-@section Identity
+@subsection Identity
 
 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
index ef0db05ebc6343fb7ff403eccf09983ad6afd952..6ddb521f4fedc895668269e15514ca80f26898e2 100644 (file)
@@ -1,12 +1,11 @@
 @node MTU
-@section Maximum Transmission Unit
+@subsection Maximum Transmission Unit
 
-MTU command line argument is maximum allowable size of outgoing GoVPN's
-packets. It varies and depends on your environment, so probably has to
-be tuned. By default MTU equals to 1452 bytes: 40 bytes per IPv6 and 8
-bytes per UDP.
+MTU option tells what maximum transmission unit is expected to get from
+TAP interface. It is per-user configuration. Incoming packets of bigger
+sizes (including the padding byte) will be ignored. If either
+@ref{Noise, noise}, or @ref{CPR} are enabled, then all outgoing packets
+are filled up to that MTU value.
 
-Underlying TAP interface has lower MTU value -- 42 bytes smaller: 26
-bytes overhead on transport message and 14 bytes for Ethernet frame.
-Client and server will print what MTU value should be used on TAP
-interface.
+Default MTU equals to 1514 bytes (1500 bytes of Ethernet payload, 14
+bytes of Ethernet header).
index d033656901f090a735b39a268ad49b577969b867..d57edb35ab0dbdf5a95a85dd48ed9b7ce05856bc 100644 (file)
@@ -1,5 +1,5 @@
 @node Network
-@section Network transport
+@subsection Network transport
 
 You can use either UDP or TCP underlying network transport protocols.
 
index e5a334b5f861faa2dfac6e352741495e14dcc3a7..4086a79f4c92271fd9a2a28b0fc2f8362b0ea47e 100644 (file)
@@ -3,6 +3,19 @@
 
 @table @strong
 
+@item Release 5.0
+@itemize
+@item New optional @ref{Encless, encryptionless mode} of operation.
+Technically no encryption functions are applied for outgoing packets, so
+you can not be forced to reveal your encryption keys or sued for
+encryption usage.
+@item @ref{MTU}s are configured on per-user basis.
+@item Simplified payload padding scheme, saving one byte of data.
+@item Ability to specify TAP interface name explicitly without any
+up-scripts for convenience.
+@item @code{govpn-verifier} utility also can use @ref{EGD}.
+@end itemize
+
 @item Release 4.2
 @itemize
 @item Fixed non-critical bug when server may fail if up-script is not
index 06ee1de2051a830a542505f4d8a54fb2c0b417eb..5df68a984a27b2e340b32cfe415811186dd13007 100644 (file)
@@ -1,7 +1,7 @@
 @node Noise
-@section Noise
+@subsection Noise
 
-So-called noise is used to hide underlying payload packets lengths.
+So-called noise is used to hide underlying payload packets length.
 Without it GoVPN provides confidentiality and authenticity of messages,
 but not their timestamps of appearance and sizes.
 
index fa8a322c6666a9f132cfacd4564399dad3a4b2fc..b80f5697f51fc964eb0be98a4d6c173b2b0310fb 100644 (file)
@@ -1,5 +1,5 @@
 @node PAKE
-@section Password Authenticated Key Agreement
+@subsection Password Authenticated Key Agreement
 
 Previously we used pre-shared high-entropy long-term static key for
 client-server authentication. Is is secure, but not convenient for some
index 0e990350df2aa5c45a8ce9d20dba500e72c30369..b0f08fc97644a51300683ec5641fc02e632350ad 100644 (file)
@@ -1,5 +1,5 @@
 @node Proxy
-@section Proxy
+@subsection Proxy
 
 You can proxy your requests through HTTP using CONNECT method. This can
 help if you are only allowed to access outside world through HTTP proxy
index a00c45219329c0bc460deb2f712e92d6656f3776..2668b1da962e543b09dddba3e37a16d0ed233a35 100644 (file)
@@ -1,8 +1,8 @@
 @node Server
 @section Server part
 
-Except for common @code{-mtu}, @code{-stats}, @code{-egd} options server
-has the following ones:
+Except for common @code{-stats}, @code{-egd} options server has the
+following ones:
 
 @table @code
 
@@ -26,23 +26,30 @@ Configuration file is JSON file with following example structure:
 @verbatim
 {
   "stargrave": {                        <-- Peer human readable name
-    "up": "./stargrave-up.sh",          <-- up-script
+    "iface": "tap10",                   <-- OPTIONAL TAP interface name
+    "mtu": 1514,                        <-- OPTIONAL overriden MTU
+    "up": "./stargrave-up.sh",          <-- OPTIONAL up-script
     "down": "./stargrave-down.sh",      <-- OPTIONAL down-script
     "timeout": 60,                      <-- OPTIONAL overriden timeout
     "noise": true,                      <-- OPTIONAL noise enabler
                                             (default: false)
     "cpr": 64,                          <-- OPTIONAL constant packet
                                             rate in KiB/sec
+    "encless": false,                   <-- OPTIONAL Encryptionless mode
     "verifier": "$argon2d..."           <-- verifier received from client
   },
   [...]
 }
 @end verbatim
 
-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
-advanced like this:
+At least one of either @code{iface} or @code{up} must be specified. If
+you specify @code{iface}, then it will be forcefully used to determine
+what TAP interface will be used. If it is not specified, then up-script
+must output interface's name to stdout (first output line).
+
+For example up-script can be just @code{echo tap10}, or more advanced
+like the following one:
+
 @example
 #!/bin/sh
 $tap=$(ifconfig tap create)
@@ -65,6 +72,7 @@ Place the following JSON configuration entry on the server's side:
 
     "Alice": {
         "up": "/path/to/up.sh",
+        "iface": "or TAP interface name",
         "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
     }
 [...]
index f6284e0cad03b52d0ebe1b6eb3ebeedb3a5eff85..c543137a808a08dfe755e3083d755805aab50d23 100644 (file)
@@ -1,5 +1,5 @@
 @node Stats
-@section Stats
+@subsection Statistics
 
 Both client and server has ability to show statistics about known
 connected peers. You retrieve them by downloading JSON from built-in
index 5b9dba36b1380165a4ec53ae090dea1d0fa7a279..d551819bcc3734ff66e4c294145cf27a69d858dc 100644 (file)
@@ -14,11 +14,10 @@ Thanks for contributions and suggestions to:
 @url{https://www.cs.columbia.edu/~smb/papers/aeke.pdf, Augmented Encrypted Key Exchange}:
 a Password-Based Protocol Secure Against Dictionary Attacks and Password
 File Compromise @copyright{} Steven M. Belloving, Michael Merrit.
-@item @url{http://cr.yp.to/ecdh.html, A state-of-the-art Diffie-Hellman function}.
-@item @url{http://cr.yp.to/snuffle.html, Snuffle 2005: the Salsa20 encryption function}.
-@item @url{http://cr.yp.to/mac.html, A state-of-the-art message-authentication code}.
-@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}.
+@item Ronald L. Rivest for its
+@url{http://people.csail.mit.edu/rivest/chaffing-980701.txt, Chaffing
+and Winnowing: Confidentiality without Encryption}.
 @end itemize
index 0f2e2dbf43257272a35fe387068aca35d38e345d..89dd5b011579ae16c80dc05bf8d1f28eea33bcab 100644 (file)
@@ -1,5 +1,5 @@
 @node Timeout
-@section Timeout
+@subsection Timeout
 
 Because of stateless UDP nature there is no way to reliably know if
 remote peer is alive. That is why timeouts are necessary. If no packets
index bfe35013d7de4cbf4676db0e6fa86fcd2778fcd4..f5ca87e1f0576df4af79d0df51595a16caf4c64f 100644 (file)
@@ -19,7 +19,7 @@ TAG || ENCRYPTED || NONCE --> PACKET
 +--< ENCRYPT(KEY, NONCE, PAYLOAD)
                     ^       ^
                     |       |
-                    |       +--< SIZE || DATA [|| NOISE]
+                    |       +--< DATA || PAD [|| ZEROS]
                     |
                     +--< PRP(PRP_KEY, SERIAL)
 @end verbatim
@@ -36,7 +36,7 @@ XTEA's encryption key is the first 128-bit of Salsa20's output with
 established common key and zero nonce (message nonces start from 1).
 
 @verbatim
-PRP_KEY = ENCRYPT(KEY, 0, 128-bit)
+PRP_KEY = 128bit(ENCRYPT(KEY, 0))
 @end verbatim
 
 @code{ENCRYPT} is Salsa20 stream cipher, with established session
@@ -44,18 +44,27 @@ PRP_KEY = ENCRYPT(KEY, 0, 128-bit)
 Salsa20's output is ignored and only remaining is XORed with ther data,
 encrypting it.
 
-@code{SIZE} is big-endian @emph{uint16} storing length of the
-@code{DATA}.
-
-@code{NOISE} is optional. It is just some junk data, intended to fill up
-packet to MTU size. This is useful for concealing payload packets length.
+@code{DATA} is padded with @code{PAD} (0x80 byte). Optional @code{ZEROS}
+may follow, to fillup packet with the junk to conceal pyload packet
+length.
 
 @code{AUTH} is Poly1305 authentication function. First 256 bits of
 Salsa20's output are used as a one-time key for @code{AUTH}.
 
 @verbatim
-AUTH_KEY = ENCRYPT(KEY, NONCE, 256 bit)
+AUTH_KEY = 256bit(ENCRYPT(KEY, NONCE))
 @end verbatim
 
 To prevent replay attacks we must remember received @code{SERIAL}s and
 drop when receiving duplicate ones.
+
+In @ref{Encless, encryptionless mode} this scheme is slightly different:
+
+@verbatim
+ PACKET = ENCODED || NONCE
+ENCODED = ENCLESS(DATA || PAD || ZEROS)
+  NONCE = PRP(PRP_KEY, SERIAL)
+@end verbatim
+
+@code{ENCLESS} is AONT and chaffing function. There is no need in
+explicit separate authentication.
index 73214da36ef357ceae50702ad118233a694749af..d2118b30be55b78f09fb8ce38a3a4fe01723dcdb 100644 (file)
@@ -14,33 +14,13 @@ What network performance can user expect? For example single
 with @emph{Go 1.5.1} gives 786 Mbps (UDP transport) throughput.
 
 @menu
-* EGD:: Entropy gathering daemon
-* Identity::
-* PAKE:: Password Authenticated Key Agreement
-* Timeout::
-* Network transport: Network.
-* Proxy::
-* MTU:: Maximum Transmission Unit
-* Stats::
-* Noise::
-* CPR:: Constant Packet Rate
-* Verifier::
+* Glossary::
 * Client part: Client.
 * Server part: Server.
 * Example usage: Example.
 @end menu
 
-@include egd.texi
-@include identity.texi
-@include pake.texi
-@include timeout.texi
-@include netproto.texi
-@include proxy.texi
-@include mtu.texi
-@include stats.texi
-@include noise.texi
-@include cpr.texi
-@include verifier.texi
+@include glossary.texi
 @include client.texi
 @include server.texi
 @include example.texi
index 74c61496e1d133f6334b02d58152c4a089769d52..bb364d747f6568f5b52e24fe3321571e82c08043 100644 (file)
@@ -1,5 +1,5 @@
 @node Verifier
-@section Verifier
+@subsection Verifier
 
 Verifier is created using @code{govpn-verifier} utility. But currently
 Go does not provide native instruments to read passwords without echoing
diff --git a/src/govpn/aont/aont_test.go b/src/govpn/aont/aont_test.go
new file mode 100644 (file)
index 0000000..93b7db5
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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 aont
+
+import (
+       "bytes"
+       "crypto/rand"
+       "testing"
+       "testing/quick"
+)
+
+var (
+       testKey *[16]byte = new([16]byte)
+)
+
+func init() {
+       rand.Read(testKey[:])
+}
+
+func TestSymmetric(t *testing.T) {
+       f := func(data []byte) bool {
+               encoded, err := Encode(testKey, data)
+               if err != nil {
+                       return false
+               }
+               if len(encoded) != len(data)+16+32 {
+                       return false
+               }
+               decoded, err := Decode(encoded)
+               if err != nil {
+                       return false
+               }
+               return bytes.Compare(decoded, data) == 0
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+}
+
+func TestSmallSize(t *testing.T) {
+       _, err := Decode([]byte("foobar"))
+       if err == nil {
+               t.Fail()
+       }
+}
+
+func TestTampered(t *testing.T) {
+       f := func(data []byte, index int) bool {
+               if len(data) == 0 {
+                       return true
+               }
+               encoded, _ := Encode(testKey, data)
+               encoded[len(data)%index] ^= byte('a')
+               _, err := Decode(encoded)
+               if err == nil {
+                       return false
+               }
+               return true
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+}
+
+func BenchmarkEncode(b *testing.B) {
+       data := make([]byte, 128)
+       rand.Read(data)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               Encode(testKey, data)
+       }
+}
+
+func BenchmarkDecode(b *testing.B) {
+       data := make([]byte, 128)
+       rand.Read(data)
+       encoded, _ := Encode(testKey, data)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               Decode(encoded)
+       }
+}
diff --git a/src/govpn/aont/oaep.go b/src/govpn/aont/oaep.go
new file mode 100644 (file)
index 0000000..35d92fe
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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/>.
+*/
+
+// All-Or-Nothing-Transform, based on OAEP.
+//
+// This package implements OAEP (Optimal Asymmetric Encryption Padding)
+// (http://cseweb.ucsd.edu/~mihir/papers/oaep.html)
+// used there as All-Or-Nothing-Transformation
+// (http://theory.lcs.mit.edu/~cis/pubs/rivest/fusion.ps).
+// We do not fix OAEP parts length, instead we add hash-based
+// checksum like in SAEP+
+// (http://crypto.stanford.edu/~dabo/abstracts/saep.html).
+//
+// AONT takes 128-bit random r, data M to be encoded and produce the
+// package PKG:
+//
+//     PKG = P1 || P2
+//      P1 = Salsa20(key=r, nonce=0x00, 0x00) XOR (M || BLAKE2b(r || M))
+//      P2 = BLAKE2b(P1) XOR r
+package aont
+
+import (
+       "crypto/subtle"
+       "errors"
+
+       "github.com/dchest/blake2b"
+       "golang.org/x/crypto/salsa20"
+)
+
+const (
+       HSize = 32
+       RSize = 16
+)
+
+var (
+       dummyNonce []byte = make([]byte, 8)
+)
+
+// Encode the data, produce AONT package. Data size will be larger than
+// the original one for 48 bytes.
+func Encode(r *[RSize]byte, in []byte) ([]byte, error) {
+       out := make([]byte, len(in)+HSize+RSize)
+       copy(out, in)
+       h := blake2b.New256()
+       h.Write(r[:])
+       h.Write(in)
+       copy(out[len(in):], h.Sum(nil))
+       salsaKey := new([32]byte)
+       copy(salsaKey[:], r[:])
+       salsa20.XORKeyStream(out, out, dummyNonce, salsaKey)
+       h.Reset()
+       h.Write(out[:len(in)+32])
+       for i, b := range h.Sum(nil)[:RSize] {
+               out[len(in)+32+i] = b ^ r[i]
+       }
+       return out, nil
+}
+
+// Decode the data from AONT package. Data size will be smaller than the
+// original one for 48 bytes.
+func Decode(in []byte) ([]byte, error) {
+       if len(in) < HSize+RSize {
+               return nil, errors.New("Too small input buffer")
+       }
+       h := blake2b.New256()
+       h.Write(in[:len(in)-RSize])
+       salsaKey := new([32]byte)
+       for i, b := range h.Sum(nil)[:RSize] {
+               salsaKey[i] = b ^ in[len(in)-RSize+i]
+       }
+       h.Reset()
+       h.Write(salsaKey[:RSize])
+       out := make([]byte, len(in)-RSize)
+       salsa20.XORKeyStream(out, in[:len(in)-RSize], dummyNonce, salsaKey)
+       h.Write(out[:len(out)-HSize])
+       if subtle.ConstantTimeCompare(h.Sum(nil), out[len(out)-HSize:]) != 1 {
+               return nil, errors.New("Invalid checksum")
+       }
+       return out[:len(out)-HSize], nil
+}
index 09b06ca44c753c894357f5b6ebf27fb27e6e5500..c4414050937f20e9740a3233d6e2d0c1fcac193e 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -41,9 +41,10 @@ var (
        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")
+       mtu         = flag.Int("mtu", govpn.MTUDefault, "MTU of TAP interface")
        timeoutP    = flag.Int("timeout", 60, "Timeout seconds")
        noisy       = flag.Bool("noise", false, "Enable noise appending")
+       encless     = flag.Bool("encless", false, "Encryptionless mode")
        cpr         = flag.Int("cpr", 0, "Enable constant KiB/sec out traffic rate")
        egdPath     = flag.String("egd", "", "Optional path to EGD socket")
 
@@ -52,7 +53,7 @@ var (
        timeout     int
        firstUpCall bool = true
        knownPeers  govpn.KnownPeers
-       idsCache    govpn.CipherCache
+       idsCache    *govpn.CipherCache
 )
 
 func main() {
@@ -61,8 +62,9 @@ func main() {
        var err error
        log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
 
-       govpn.MTU = *mtu
-
+       if *mtu > govpn.MTUMax {
+               log.Fatalln("Maximum allowable MTU is", govpn.MTUMax)
+       }
        if *egdPath != "" {
                log.Println("Using", *egdPath, "EGD")
                govpn.EGDInit(*egdPath)
@@ -73,23 +75,31 @@ func main() {
                log.Fatalln(err)
        }
        priv := verifier.PasswordApply(govpn.StringFromFile(*keyPath))
+       if *encless {
+               if *proto != "tcp" {
+                       log.Fatalln("Currently encryptionless mode works only with TCP")
+               }
+               *noisy = true
+       }
        conf = &govpn.PeerConf{
                Id:       verifier.Id,
+               Iface:    *ifaceName,
+               MTU:      *mtu,
                Timeout:  time.Second * time.Duration(timeout),
                Noise:    *noisy,
                CPR:      *cpr,
+               Encless:  *encless,
                Verifier: verifier,
                DSAPriv:  priv,
        }
        idsCache = govpn.NewCipherCache([]govpn.PeerId{*verifier.Id})
        log.Println(govpn.VersionGet())
 
-       tap, err = govpn.TAPListen(*ifaceName)
+       tap, err = govpn.TAPListen(*ifaceName, *mtu)
        if err != nil {
                log.Fatalln("Can not listen on TAP interface:", err)
        }
 
-       log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
        if *stats != "" {
                log.Println("Stats are going to listen on", *stats)
                statsPort, err := net.Listen("tcp", *stats)
index bde828e289f2e77c1b0d5d36ffc85422754bfda4..a4259f39c2840d5f57b61084ca9f1dc8738f6f35 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
index 1dd11ec39eb4c62789062e5cfb39eec8a5ca8653..7f2af6085bebcbef5ed873cd33319fc7dc0c47a7 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -43,7 +43,7 @@ func startTCP(timeouted, rehandshaking, termination chan struct{}) {
 
 func handleTCP(conn *net.TCPConn, timeouted, rehandshaking, termination chan struct{}) {
        hs := govpn.HandshakeStart(*remoteAddr, conn, conf)
-       buf := make([]byte, govpn.MTU)
+       buf := make([]byte, 2*(govpn.EnclessEnlargeSize+*mtu)+*mtu)
        var n int
        var err error
        var prev int
@@ -56,7 +56,7 @@ HandshakeCycle:
                        break HandshakeCycle
                default:
                }
-               if prev == govpn.MTU {
+               if prev == len(buf) {
                        log.Println("Timeouted waiting for the packet")
                        timeouted <- struct{}{}
                        break HandshakeCycle
@@ -125,7 +125,7 @@ TransportCycle:
                        break TransportCycle
                default:
                }
-               if prev == govpn.MTU {
+               if prev == len(buf) {
                        log.Println("Timeouted waiting for the packet")
                        timeouted <- struct{}{}
                        break TransportCycle
index 9f45f8be51b68f8ccac1d2eb8dd41a832da9884b..581e66cf74f122ceee169a53c86f1dab0f6b6dc3 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -39,7 +39,7 @@ func startUDP(timeouted, rehandshaking, termination chan struct{}) {
        log.Println("Connected to UDP:" + *remoteAddr)
 
        hs := govpn.HandshakeStart(*remoteAddr, conn, conf)
-       buf := make([]byte, govpn.MTU)
+       buf := make([]byte, *mtu*2)
        var n int
        var timeouts int
        var peer *govpn.Peer
index 6560cfe1cb8fd01290cc7294febe2546e78326f6..313fc394aed7b70fc21e28d32b7f01bf5236ac9b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -67,15 +67,23 @@ Processor:
 }
 
 func callUp(peerId *govpn.PeerId) (string, error) {
-       result, err := govpn.ScriptCall(confs[*peerId].Up, "")
-       if err != nil {
-               log.Println("Script", confs[*peerId].Up, "call failed", err)
-               return "", err
+       ifaceName := confs[*peerId].Iface
+       if confs[*peerId].Up != "" {
+               result, err := govpn.ScriptCall(confs[*peerId].Up, "")
+               if err != nil {
+                       log.Println("Script", confs[*peerId].Up, "call failed", err)
+                       return "", err
+               }
+               if ifaceName == "" {
+                       sepIndex := bytes.Index(result, []byte{'\n'})
+                       if sepIndex < 0 {
+                               sepIndex = len(result)
+                       }
+                       ifaceName = string(result[:sepIndex])
+               }
        }
-       sepIndex := bytes.Index(result, []byte{'\n'})
-       if sepIndex < 0 {
-               sepIndex = len(result)
+       if ifaceName == "" {
+               log.Println("Can not obtain interface name for", *peerId)
        }
-       ifaceName := string(result[:sepIndex])
        return ifaceName, nil
 }
index 76d56cd8ae9f5185e954ff2dbc729b28a40c15f0..fcee362127f182a528c91796f555917f99a987c4 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -33,7 +33,7 @@ const (
 
 var (
        confs    map[govpn.PeerId]*govpn.PeerConf
-       idsCache govpn.CipherCache
+       idsCache *govpn.CipherCache
 )
 
 func confRead() map[govpn.PeerId]*govpn.PeerConf {
@@ -53,14 +53,27 @@ func confRead() map[govpn.PeerId]*govpn.PeerConf {
                if err != nil {
                        log.Fatalln("Unable to decode the key:", err.Error(), pc.VerifierRaw)
                }
+               if pc.Encless {
+                       pc.Noise = true
+               }
+               if pc.MTU == 0 {
+                       pc.MTU = govpn.MTUDefault
+               }
+               if pc.MTU > govpn.MTUMax {
+                       log.Println("MTU value", pc.MTU, "is too high, overriding to", govpn.MTUMax)
+                       pc.MTU = govpn.MTUMax
+               }
                conf := govpn.PeerConf{
                        Verifier: verifier,
                        Id:       verifier.Id,
                        Name:     name,
+                       Iface:    pc.Iface,
+                       MTU:      pc.MTU,
                        Up:       pc.Up,
                        Down:     pc.Down,
                        Noise:    pc.Noise,
                        CPR:      pc.CPR,
+                       Encless:  pc.Encless,
                }
                if pc.TimeoutInt <= 0 {
                        pc.TimeoutInt = govpn.TimeoutDefault
index be4e7ebc6c83345ead1ed24837ce3aa2ce0e1b46..9a73b25da3acb78a2832182d175750c53c97c340 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -36,7 +36,6 @@ var (
        confPath = flag.String("conf", "peers.json", "Path to configuration JSON")
        stats    = flag.String("stats", "", "Enable stats retrieving on host:port")
        proxy    = flag.String("proxy", "", "Enable HTTP proxy on host:port")
-       mtu      = flag.Int("mtu", 1452, "MTU for outgoing packets")
        egdPath  = flag.String("egd", "", "Optional path to EGD socket")
 )
 
@@ -46,7 +45,6 @@ func main() {
        log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
        log.Println(govpn.VersionGet())
 
-       govpn.MTU = *mtu
        confInit()
        knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer))
 
@@ -73,7 +71,6 @@ func main() {
        hsHeartbeat := time.Tick(timeout)
        go func() { <-hsHeartbeat }()
 
-       log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
        if *stats != "" {
                log.Println("Stats are going to listen on", *stats)
                statsPort, err := net.Listen("tcp", *stats)
index 1a9f81461e66526cfadd5d36c47fa979fe1c3c57..f1f5e09d37aeec0558de8033cd361e2e595e9b62 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
index dcd9606871894b23eb782c2d8c5100277feaf86f..e3458cb54df74bb97091eac06fd8b5363fccdb67 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -51,7 +51,7 @@ func startTCP() {
 
 func handleTCP(conn net.Conn) {
        addr := conn.RemoteAddr().String()
-       buf := make([]byte, govpn.MTU)
+       buf := make([]byte, govpn.EnclessEnlargeSize+2*govpn.MTUMax)
        var n int
        var err error
        var prev int
@@ -61,7 +61,7 @@ func handleTCP(conn net.Conn) {
        var tap *govpn.TAP
        var conf *govpn.PeerConf
        for {
-               if prev == govpn.MTU {
+               if prev == len(buf) {
                        break
                }
                conn.SetReadDeadline(time.Now().Add(time.Duration(govpn.TimeoutDefault) * time.Second))
@@ -120,7 +120,7 @@ func handleTCP(conn net.Conn) {
                                peer = nil
                                break
                        }
-                       tap, err = govpn.TAPListen(ifaceName)
+                       tap, err = govpn.TAPListen(ifaceName, peer.MTU)
                        if err != nil {
                                log.Println("Unable to create TAP:", err)
                                peer = nil
@@ -157,7 +157,7 @@ func handleTCP(conn net.Conn) {
        prev = 0
        var i int
        for {
-               if prev == govpn.MTU {
+               if prev == len(buf) {
                        break
                }
                conn.SetReadDeadline(time.Now().Add(conf.Timeout))
index 0f5e5551d6bc853ffa3dbe29886434b1ed02d1e0..ddbbee81e660251903ec45496f737f02e68a9d80 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -50,7 +50,7 @@ func startUDP() {
        }
        log.Println("Listening on UDP:" + *bindAddr)
 
-       udpBufs <- make([]byte, govpn.MTU)
+       udpBufs <- make([]byte, govpn.MTUMax)
        go func() {
                var buf []byte
                var raddr *net.UDPAddr
@@ -103,8 +103,8 @@ func startUDP() {
                        hsLock.Unlock()
 
                        go func() {
-                               udpBufs <- make([]byte, govpn.MTU)
-                               udpBufs <- make([]byte, govpn.MTU)
+                               udpBufs <- make([]byte, govpn.MTUMax)
+                               udpBufs <- make([]byte, govpn.MTUMax)
                        }()
                        peersByIdLock.RLock()
                        addrPrev, exists = peersById[*peer.Id]
@@ -139,7 +139,7 @@ func startUDP() {
                                        if err != nil {
                                                return
                                        }
-                                       tap, err := govpn.TAPListen(ifaceName)
+                                       tap, err := govpn.TAPListen(ifaceName, peer.MTU)
                                        if err != nil {
                                                log.Println("Unable to create TAP:", err)
                                                return
index 97c43339d2eb24eb0868cadec4f19006131b8119..a7c16f0bbe37971cb1f0e47da8c25ff92a192bc8 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -20,7 +20,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
-       "crypto/rand"
        "crypto/subtle"
        "flag"
        "fmt"
@@ -35,13 +34,17 @@ var (
        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")
+       egdPath  = flag.String("egd", "", "Optional path to EGD socket")
 )
 
 func main() {
        flag.Parse()
+       if *egdPath != "" {
+               govpn.EGDInit(*egdPath)
+       }
        if *verifier == "" {
                id := new([govpn.IDSize]byte)
-               if _, err := rand.Read(id[:]); err != nil {
+               if _, err := govpn.Rand.Read(id[:]); err != nil {
                        log.Fatalln(err)
                }
                pid := govpn.PeerId(*id)
diff --git a/src/govpn/cnw/cnw.go b/src/govpn/cnw/cnw.go
new file mode 100644 (file)
index 0000000..931a0d1
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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/>.
+*/
+
+// Chaffing-and-Winnowing.
+//
+// This package implements Chaffing-and-Winnowing technology
+// (http://people.csail.mit.edu/rivest/chaffing-980701.txt).
+//
+// It outputs two Poly1305 MACs for each bit of input data: one valid,
+// and other is not. MACs sequence is following:
+//
+//     MAC of 1st byte, 1st bit, 0 possible value
+//     MAC of 1st byte, 1st bit, 1 possible value
+//     MAC of 1st byte, 2nd bit, 0 possible value
+//     MAC of 1st byte, 2nd bit, 1 possible value
+//     ...
+//
+// If bit value is 0, then first MAC is taken over "1" and the second
+// one is over "0". If bit value is 1, then first is taken over "0" and
+// second is over "1".
+//
+// Poly1305 uses 256-bit one-time key. We generate it using XSalsa20 for
+// for the whole byte at once (16 MACs).
+//
+//     MACKey1, MACKey2, ... = XSalsa20(authKey, nonce, 0x00...)
+//     nonce = prefix || 0x00... || big endian byte number
+package cnw
+
+import (
+       "crypto/subtle"
+       "encoding/binary"
+       "errors"
+
+       "golang.org/x/crypto/poly1305"
+       "golang.org/x/crypto/salsa20"
+)
+
+const (
+       EnlargeFactor = 16 * poly1305.TagSize
+)
+
+func zero(in []byte) {
+       for i := 0; i < len(in); i++ {
+               in[i] = 0
+       }
+}
+
+// Chaff the data. noncePrfx is 64-bit nonce. Output data will be much
+// larger: 256 bytes for each input byte.
+func Chaff(authKey *[32]byte, noncePrfx, in []byte) []byte {
+       out := make([]byte, len(in)*EnlargeFactor)
+       keys := make([]byte, 8*64)
+       nonce := make([]byte, 24)
+       copy(nonce[:8], noncePrfx)
+       var i int
+       var v byte
+       tag := new([16]byte)
+       macKey := new([32]byte)
+       for n, b := range in {
+               binary.BigEndian.PutUint64(nonce[16:], uint64(n))
+               salsa20.XORKeyStream(keys, keys, nonce, authKey)
+               for i = 0; i < 8; i++ {
+                       v = (b >> uint8(i)) & 1
+                       copy(macKey[:], keys[64*i:64*i+32])
+                       if v == 0 {
+                               poly1305.Sum(tag, []byte("1"), macKey)
+                       } else {
+                               poly1305.Sum(tag, []byte("0"), macKey)
+                       }
+                       copy(out[16*(n*16+i*2):], tag[:])
+                       copy(macKey[:], keys[64*i+32:64*i+64])
+                       if v == 1 {
+                               poly1305.Sum(tag, []byte("1"), macKey)
+                       } else {
+                               poly1305.Sum(tag, []byte("0"), macKey)
+                       }
+                       copy(out[16*(n*16+i*2+1):], tag[:])
+               }
+               zero(keys)
+       }
+       zero(macKey[:])
+       return out
+}
+
+// Winnow the data.
+func Winnow(authKey *[32]byte, noncePrfx, in []byte) ([]byte, error) {
+       if len(in)%EnlargeFactor != 0 {
+               return nil, errors.New("Invalid data size")
+       }
+       out := make([]byte, len(in)/EnlargeFactor)
+       keys := make([]byte, 8*64)
+       nonce := make([]byte, 24)
+       copy(nonce[:8], noncePrfx)
+       var i int
+       var v byte
+       tag := new([16]byte)
+       macKey := new([32]byte)
+       defer zero(macKey[:])
+       var is01 bool
+       var is00 bool
+       var is11 bool
+       var is10 bool
+       for n := 0; n < len(out); n++ {
+               binary.BigEndian.PutUint64(nonce[16:], uint64(n))
+               salsa20.XORKeyStream(keys, keys, nonce, authKey)
+               v = 0
+               for i = 0; i < 8; i++ {
+                       copy(macKey[:], keys[64*i:64*i+32])
+                       poly1305.Sum(tag, []byte("1"), macKey)
+                       is01 = subtle.ConstantTimeCompare(
+                               tag[:],
+                               in[16*(n*16+i*2):16*(n*16+i*2+1)],
+                       ) == 1
+                       poly1305.Sum(tag, []byte("0"), macKey)
+                       is00 = subtle.ConstantTimeCompare(
+                               tag[:],
+                               in[16*(n*16+i*2):16*(n*16+i*2+1)],
+                       ) == 1
+                       copy(macKey[:], keys[64*i+32:64*i+64])
+                       poly1305.Sum(tag, []byte("1"), macKey)
+                       is11 = subtle.ConstantTimeCompare(
+                               tag[:],
+                               in[16*(n*16+i*2+1):16*(n*16+i*2+2)],
+                       ) == 1
+                       poly1305.Sum(tag, []byte("0"), macKey)
+                       is10 = subtle.ConstantTimeCompare(
+                               tag[:],
+                               in[16*(n*16+i*2+1):16*(n*16+i*2+2)],
+                       ) == 1
+                       if !((is01 && is10) || (is00 && is11)) {
+                               zero(keys)
+                               return nil, errors.New("Invalid authenticator received")
+                       }
+                       if is11 {
+                               v = v | 1<<uint8(i)
+                       }
+               }
+               out[n] = v
+               zero(keys)
+       }
+       return out, nil
+}
diff --git a/src/govpn/cnw/cnw_test.go b/src/govpn/cnw/cnw_test.go
new file mode 100644 (file)
index 0000000..24cf452
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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 cnw
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+       "testing/quick"
+)
+
+var (
+       testKey *[32]byte = new([32]byte)
+)
+
+func init() {
+       rand.Read(testKey[:])
+}
+
+func TestSymmetric(t *testing.T) {
+       nonce := make([]byte, 8)
+       f := func(data []byte, pktNum uint64) bool {
+               if len(data) == 0 {
+                       return true
+               }
+               binary.BigEndian.PutUint64(nonce, pktNum)
+               chaffed := Chaff(testKey, nonce, data)
+               if len(chaffed) != len(data)*EnlargeFactor {
+                       return false
+               }
+               decoded, err := Winnow(testKey, nonce, chaffed)
+               if err != nil {
+                       return false
+               }
+               return bytes.Compare(decoded, data) == 0
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+}
+
+func TestSmallSize(t *testing.T) {
+       _, err := Winnow(testKey, []byte("foobar12"), []byte("foobar"))
+       if err == nil {
+               t.Fail()
+       }
+}
+
+func BenchmarkChaff(b *testing.B) {
+       nonce := make([]byte, 8)
+       data := make([]byte, 16)
+       rand.Read(nonce)
+       rand.Read(data)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               Chaff(testKey, nonce, data)
+       }
+}
+
+func BenchmarkWinnow(b *testing.B) {
+       nonce := make([]byte, 8)
+       data := make([]byte, 16)
+       rand.Read(nonce)
+       rand.Read(data)
+       chaffed := Chaff(testKey, nonce, data)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               Winnow(testKey, nonce, chaffed)
+       }
+}
index 898690d67d75d375164e0c658d38ed0f98e9a873..cb2f315b6ee5f89c8691e1873179a397ab1e4838 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -23,15 +23,15 @@ import (
        "os"
        "os/exec"
        "runtime"
-       "time"
 )
 
 const (
        TimeoutDefault = 60
+       MTUMax         = 9000
+       MTUDefault     = 1500 + 14
 )
 
 var (
-       MTU     int
        Version string
 )
 
@@ -54,7 +54,7 @@ func ScriptCall(path, ifaceName string) ([]byte, error) {
 }
 
 // Zero each byte.
-func sliceZero(data []byte) {
+func SliceZero(data []byte) {
        for i := 0; i < len(data); i++ {
                data[i] = 0
        }
@@ -63,10 +63,3 @@ func sliceZero(data []byte) {
 func VersionGet() string {
        return "GoVPN version " + Version + " built with " + runtime.Version()
 }
-
-func cprCycleCalculate(rate int) time.Duration {
-       if rate == 0 {
-               return time.Duration(0)
-       }
-       return time.Second / time.Duration(rate*(1<<10)/MTU)
-}
index d9e74813a058a56dedb05bbd065245aef47ec36c..4cb2f15fd5c7905e7f65dd88732fc25bf7eaf04b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -27,12 +27,15 @@ import (
 type PeerConf struct {
        Id          *PeerId       `json:"-"`
        Name        string        `json:"name"`
+       Iface       string        `json:"iface"`
+       MTU         int           `json:"mtu"`
        Up          string        `json:"up"`
        Down        string        `json:"down"`
        TimeoutInt  int           `json:"timeout"`
        Timeout     time.Duration `json:"-"`
        Noise       bool          `json:"noise"`
        CPR         int           `json:"cpr"`
+       Encless     bool          `json:"encless"`
        VerifierRaw string        `json:"verifier"`
 
        // This is passphrase verifier
index 08dff8318a8b1ec3d06120dee910d672f0328bba..04ec87b9678cf2dfa10c576c84bfc66bbe4ef493 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -19,32 +19,38 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package govpn
 
 import (
+       "crypto/rand"
        "errors"
+       "io"
        "net"
 )
 
 var (
-       egdPath string
+       Rand io.Reader = rand.Reader
 )
 
-func EGDInit(path string) {
-       egdPath = path
-}
+type EGDRand string
 
 // Read n bytes from EGD, blocking mode.
-func EGDRead(b []byte) error {
-       c, err := net.Dial("unix", egdPath)
+func (egdPath EGDRand) Read(b []byte) (int, error) {
+       conn, err := net.Dial("unix", string(egdPath))
        if err != nil {
-               return err
+               return 0, err
        }
-       defer c.Close()
-       c.Write([]byte{0x02, byte(len(b))})
-       r, err := c.Read(b)
+       conn.Write([]byte{0x02, byte(len(b))})
+       read, err := conn.Read(b)
        if err != nil {
-               return err
+               conn.Close()
+               return read, err
        }
-       if r != len(b) {
-               return errors.New("Got less bytes than expected from EGD")
+       if read != len(b) {
+               conn.Close()
+               return read, errors.New("Got less bytes than expected from EGD")
        }
-       return nil
+       conn.Close()
+       return read, nil
+}
+
+func EGDInit(path string) {
+       Rand = EGDRand(path)
 }
diff --git a/src/govpn/encless.go b/src/govpn/encless.go
new file mode 100644 (file)
index 0000000..21162b7
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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 (
+       "govpn/aont"
+       "govpn/cnw"
+)
+
+const (
+       EnclessEnlargeSize = aont.HSize + aont.RSize*cnw.EnlargeFactor
+)
+
+// Confidentiality preserving (but encryptionless) encoding.
+//
+// It uses Chaffing-and-Winnowing technology (it is neither
+// encryption nor steganography) over All-Or-Nothing-Transformed data.
+// nonce is 64-bit nonce. Output data will be EnclessEnlargeSize larger.
+// It also consumes 64-bits of entropy.
+func EnclessEncode(authKey *[32]byte, nonce, in []byte) ([]byte, error) {
+       r := new([aont.RSize]byte)
+       var err error
+       if _, err = Rand.Read(r[:]); err != nil {
+               return nil, err
+       }
+       aonted, err := aont.Encode(r, in)
+       if err != nil {
+               return nil, err
+       }
+       out := append(
+               cnw.Chaff(authKey, nonce, aonted[:aont.RSize]),
+               aonted[aont.RSize:]...,
+       )
+       SliceZero(aonted[:aont.RSize])
+       return out, nil
+}
+
+// Decode EnclessEncode-ed data.
+func EnclessDecode(authKey *[32]byte, nonce, in []byte) ([]byte, error) {
+       var err error
+       winnowed, err := cnw.Winnow(
+               authKey, nonce, in[:aont.RSize*cnw.EnlargeFactor],
+       )
+       if err != nil {
+               return nil, err
+       }
+       out, err := aont.Decode(append(
+               winnowed, in[aont.RSize*cnw.EnlargeFactor:]...,
+       ))
+       SliceZero(winnowed)
+       if err != nil {
+               return nil, err
+       }
+       return out, nil
+}
diff --git a/src/govpn/encless_test.go b/src/govpn/encless_test.go
new file mode 100644 (file)
index 0000000..20b0df6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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 (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+       "testing/quick"
+)
+
+var (
+       testKey *[32]byte = new([32]byte)
+)
+
+func init() {
+       rand.Read(testKey[:])
+}
+
+func TestEnclessSymmetric(t *testing.T) {
+       nonce := make([]byte, 8)
+       f := func(pktNum uint64, in []byte) bool {
+               binary.BigEndian.PutUint64(nonce, pktNum)
+               encoded, err := EnclessEncode(testKey, nonce, in)
+               if err != nil {
+                       return false
+               }
+               decoded, err := EnclessDecode(testKey, nonce, encoded)
+               if err != nil {
+                       return false
+               }
+               return bytes.Compare(decoded, in) == 0
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+}
+
+func BenchmarkEnclessEncode(b *testing.B) {
+       nonce := make([]byte, 8)
+       data := make([]byte, 128)
+       rand.Read(nonce)
+       rand.Read(data)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               EnclessEncode(testKey, nonce, data)
+       }
+}
+
+func BenchmarkEnclessDecode(b *testing.B) {
+       nonce := make([]byte, 8)
+       data := make([]byte, 128)
+       rand.Read(nonce)
+       rand.Read(data)
+       encoded, _ := EnclessEncode(testKey, nonce, data)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               EnclessDecode(testKey, nonce, encoded)
+       }
+}
index 996f31ff903db113da307ccd488ac60461116509..d65e72b84038387a64714ad106b15a922f5202d8 100644 (file)
@@ -1,20 +1,2 @@
-/*
-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/>.
-*/
-
 // Simple secure, DPI/censorship-resistant free software VPN daemon.
 package govpn
index f0a8e65202130cf7302603ea21a08fa9353fa8f3..8c7696728ab6e342a166713c5a7467f876ef445b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -19,7 +19,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package govpn
 
 import (
-       "crypto/rand"
        "crypto/subtle"
        "encoding/binary"
        "io"
@@ -70,28 +69,28 @@ func HApply(data *[32]byte) {
 // Zero handshake's memory state
 func (h *Handshake) Zero() {
        if h.rNonce != nil {
-               sliceZero(h.rNonce[:])
+               SliceZero(h.rNonce[:])
        }
        if h.dhPriv != nil {
-               sliceZero(h.dhPriv[:])
+               SliceZero(h.dhPriv[:])
        }
        if h.key != nil {
-               sliceZero(h.key[:])
+               SliceZero(h.key[:])
        }
        if h.dsaPubH != nil {
-               sliceZero(h.dsaPubH[:])
+               SliceZero(h.dsaPubH[:])
        }
        if h.rServer != nil {
-               sliceZero(h.rServer[:])
+               SliceZero(h.rServer[:])
        }
        if h.rClient != nil {
-               sliceZero(h.rClient[:])
+               SliceZero(h.rClient[:])
        }
        if h.sServer != nil {
-               sliceZero(h.sServer[:])
+               SliceZero(h.sServer[:])
        }
        if h.sClient != nil {
-               sliceZero(h.sClient[:])
+               SliceZero(h.sClient[:])
        }
 }
 
@@ -102,23 +101,13 @@ 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 := randRead(priv[:]); err != nil {
+               if _, err := Rand.Read(priv[:]); err != nil {
                        log.Fatalln("Error reading random for DH private key:", err)
                }
                reprFound = extra25519.ScalarBaseMult(pub, repr, priv)
@@ -167,17 +156,25 @@ func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake {
        state.dhPriv, dhPubRepr = dhKeypairGen()
 
        state.rNonce = new([RSize]byte)
-       if err := randRead(state.rNonce[:]); err != nil {
+       if _, err := Rand.Read(state.rNonce[:]); err != nil {
                log.Fatalln("Error reading random for nonce:", err)
        }
        var enc []byte
        if conf.Noise {
-               enc = make([]byte, MTU-xtea.BlockSize-RSize)
+               enc = make([]byte, conf.MTU-xtea.BlockSize-RSize)
        } else {
                enc = make([]byte, 32)
        }
        copy(enc, dhPubRepr[:])
-       salsa20.XORKeyStream(enc, enc, state.rNonce[:], state.dsaPubH)
+       if conf.Encless {
+               var err error
+               enc, err = EnclessEncode(state.dsaPubH, state.rNonce[:], enc)
+               if err != err {
+                       panic(err)
+               }
+       } else {
+               salsa20.XORKeyStream(enc, enc, state.rNonce[:], state.dsaPubH)
+       }
        data := append(state.rNonce[:], enc...)
        data = append(data, idTag(state.Conf.Id, state.rNonce[:])...)
        state.conn.Write(data)
@@ -191,61 +188,112 @@ 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 && len(data) >= 48 {
-               // Generate DH keypair
-               var dhPubRepr *[32]byte
-               h.dhPriv, dhPubRepr = dhKeypairGen()
-
+       if h.rNonce == nil && ((!h.Conf.Encless && len(data) >= 48) ||
+               (h.Conf.Encless && len(data) == EnclessEnlargeSize+h.Conf.MTU)) {
                h.rNonce = new([RSize]byte)
                copy(h.rNonce[:], data[:RSize])
 
-               // Decrypt remote public key and compute shared key
+               // Decrypt remote public key
                cDHRepr := new([32]byte)
-               salsa20.XORKeyStream(
-                       cDHRepr[:],
-                       data[RSize:RSize+32],
-                       h.rNonce[:],
-                       h.dsaPubH,
-               )
+               if h.Conf.Encless {
+                       out, err := EnclessDecode(
+                               h.dsaPubH,
+                               h.rNonce[:],
+                               data[RSize:len(data)-xtea.BlockSize],
+                       )
+                       if err != nil {
+                               log.Println("Unable to decode packet from", h.addr, err)
+                               return nil
+                       }
+                       copy(cDHRepr[:], out)
+               } else {
+                       salsa20.XORKeyStream(
+                               cDHRepr[:],
+                               data[RSize:RSize+32],
+                               h.rNonce[:],
+                               h.dsaPubH,
+                       )
+               }
+
+               // Generate DH keypair
+               var dhPubRepr *[32]byte
+               h.dhPriv, dhPubRepr = dhKeypairGen()
+
+               // Compute shared key
                cDH := new([32]byte)
                extra25519.RepresentativeToPublicKey(cDH, cDHRepr)
                h.key = dhKeyGen(h.dhPriv, cDH)
 
-               encPub := make([]byte, 32)
-               salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH)
+               var encPub []byte
+               var err error
+               if h.Conf.Encless {
+                       encPub = make([]byte, h.Conf.MTU)
+                       copy(encPub, dhPubRepr[:])
+                       encPub, err = EnclessEncode(h.dsaPubH, h.rNonceNext(1), encPub)
+                       if err != nil {
+                               panic(err)
+                       }
+               } else {
+                       encPub = make([]byte, 32)
+                       salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH)
+               }
 
                // Generate R* and encrypt them
                h.rServer = new([RSize]byte)
-               if err := randRead(h.rServer[:]); err != nil {
+               if _, err = Rand.Read(h.rServer[:]); err != nil {
                        log.Fatalln("Error reading random for R:", err)
                }
                h.sServer = new([SSize]byte)
-               if err := randRead(h.sServer[:]); err != nil {
+               if _, err = Rand.Read(h.sServer[:]); err != nil {
                        log.Fatalln("Error reading random for S:", err)
                }
                var encRs []byte
-               if h.Conf.Noise {
-                       encRs = make([]byte, MTU-len(encPub)-xtea.BlockSize)
+               if h.Conf.Noise && !h.Conf.Encless {
+                       encRs = make([]byte, h.Conf.MTU-len(encPub)-xtea.BlockSize)
+               } else if h.Conf.Encless {
+                       encRs = make([]byte, h.Conf.MTU-xtea.BlockSize)
                } else {
                        encRs = make([]byte, RSize+SSize)
                }
                copy(encRs, append(h.rServer[:], h.sServer[:]...))
-               salsa20.XORKeyStream(encRs, encRs, h.rNonce[:], h.key)
+               if h.Conf.Encless {
+                       encRs, err = EnclessEncode(h.key, h.rNonce[:], encRs)
+                       if err != nil {
+                               panic(err)
+                       }
+               } else {
+                       salsa20.XORKeyStream(encRs, encRs, h.rNonce[:], h.key)
+               }
 
                // Send that to client
                h.conn.Write(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...))
                h.LastPing = time.Now()
        } else
        // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag
-       if h.rClient == nil && len(data) >= 120 {
-               // Decrypted Rs compare rServer
-               dec := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
-               salsa20.XORKeyStream(
-                       dec,
-                       data[:RSize+RSize+SSize+ed25519.SignatureSize],
-                       h.rNonceNext(1),
-                       h.key,
-               )
+       if h.rClient == nil && ((!h.Conf.Encless && len(data) >= 120) ||
+               (h.Conf.Encless && len(data) == EnclessEnlargeSize+h.Conf.MTU)) {
+               var dec []byte
+               var err error
+               if h.Conf.Encless {
+                       dec, err = EnclessDecode(
+                               h.key,
+                               h.rNonceNext(1),
+                               data[:len(data)-xtea.BlockSize],
+                       )
+                       if err != nil {
+                               log.Println("Unable to decode packet from", h.addr, err)
+                               return nil
+                       }
+                       dec = dec[:RSize+RSize+SSize+ed25519.SignatureSize]
+               } else {
+                       dec = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
+                       salsa20.XORKeyStream(
+                               dec,
+                               data[:RSize+RSize+SSize+ed25519.SignatureSize],
+                               h.rNonceNext(1),
+                               h.key,
+                       )
+               }
                if subtle.ConstantTimeCompare(dec[:RSize], h.rServer[:]) != 1 {
                        log.Println("Invalid server's random number with", h.addr)
                        return nil
@@ -260,12 +308,19 @@ func (h *Handshake) Server(data []byte) *Peer {
                // Send final answer to client
                var enc []byte
                if h.Conf.Noise {
-                       enc = make([]byte, MTU-xtea.BlockSize)
+                       enc = make([]byte, h.Conf.MTU-xtea.BlockSize)
                } else {
                        enc = make([]byte, RSize)
                }
                copy(enc, dec[RSize:RSize+RSize])
-               salsa20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key)
+               if h.Conf.Encless {
+                       enc, err = EnclessEncode(h.key, h.rNonceNext(2), enc)
+                       if err != nil {
+                               panic(err)
+                       }
+               } else {
+                       salsa20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key)
+               }
                h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
 
                // Switch peer
@@ -290,54 +345,120 @@ 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 && len(data) >= 80 {
-               // Decrypt remote public key and compute shared key
+       if h.rServer == nil && h.key == nil &&
+               ((!h.Conf.Encless && len(data) >= 80) ||
+                       (h.Conf.Encless && len(data) == 2*(EnclessEnlargeSize+h.Conf.MTU))) {
+               // Decrypt remote public key
                sDHRepr := new([32]byte)
-               salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH)
+               var tmp []byte
+               var err error
+               if h.Conf.Encless {
+                       tmp, err = EnclessDecode(
+                               h.dsaPubH,
+                               h.rNonceNext(1),
+                               data[:len(data)/2],
+                       )
+                       if err != nil {
+                               log.Println("Unable to decode packet from", h.addr, err)
+                               return nil
+                       }
+                       copy(sDHRepr[:], tmp[:32])
+               } else {
+                       salsa20.XORKeyStream(
+                               sDHRepr[:],
+                               data[:32],
+                               h.rNonceNext(1),
+                               h.dsaPubH,
+                       )
+               }
+
+               // Compute shared key
                sDH := new([32]byte)
                extra25519.RepresentativeToPublicKey(sDH, sDHRepr)
                h.key = dhKeyGen(h.dhPriv, sDH)
 
                // Decrypt Rs
-               decRs := make([]byte, RSize+SSize)
-               salsa20.XORKeyStream(decRs, data[SSize:32+RSize+SSize], h.rNonce[:], h.key)
                h.rServer = new([RSize]byte)
-               copy(h.rServer[:], decRs[:RSize])
                h.sServer = new([SSize]byte)
-               copy(h.sServer[:], decRs[RSize:])
+               if h.Conf.Encless {
+                       tmp, err = EnclessDecode(
+                               h.key,
+                               h.rNonce[:],
+                               data[len(data)/2:len(data)-xtea.BlockSize],
+                       )
+                       if err != nil {
+                               log.Println("Unable to decode packet from", h.addr, err)
+                               return nil
+                       }
+                       copy(h.rServer[:], tmp[:RSize])
+                       copy(h.sServer[:], tmp[RSize:RSize+SSize])
+               } else {
+                       decRs := make([]byte, RSize+SSize)
+                       salsa20.XORKeyStream(
+                               decRs,
+                               data[SSize:SSize+RSize+SSize],
+                               h.rNonce[:],
+                               h.key,
+                       )
+                       copy(h.rServer[:], decRs[:RSize])
+                       copy(h.sServer[:], decRs[RSize:])
+               }
 
                // Generate R* and signature and encrypt them
                h.rClient = new([RSize]byte)
-               if err := randRead(h.rClient[:]); err != nil {
+               if _, err = Rand.Read(h.rClient[:]); err != nil {
                        log.Fatalln("Error reading random for R:", err)
                }
                h.sClient = new([SSize]byte)
-               if err := randRead(h.sClient[:]); err != nil {
+               if _, err = Rand.Read(h.sClient[:]); err != nil {
                        log.Fatalln("Error reading random for S:", err)
                }
                sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:])
 
                var enc []byte
                if h.Conf.Noise {
-                       enc = make([]byte, MTU-xtea.BlockSize)
+                       enc = make([]byte, h.Conf.MTU-xtea.BlockSize)
                } else {
                        enc = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
                }
-               copy(enc,
-                       append(h.rServer[:],
-                               append(h.rClient[:],
-                                       append(h.sClient[:], sign[:]...)...)...))
-               salsa20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key)
+               copy(enc, h.rServer[:])
+               copy(enc[RSize:], h.rClient[:])
+               copy(enc[RSize+RSize:], h.sClient[:])
+               copy(enc[RSize+RSize+SSize:], sign[:])
+               if h.Conf.Encless {
+                       enc, err = EnclessEncode(h.key, h.rNonceNext(1), enc)
+                       if err != nil {
+                               panic(err)
+                       }
+               } else {
+                       salsa20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key)
+               }
 
                // Send that to server
                h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
                h.LastPing = time.Now()
        } else
        // ENC(K, R+2, RC) + IDtag
-       if h.key != nil && len(data) >= 16 {
+       if h.key != nil && ((!h.Conf.Encless && len(data) >= 16) ||
+               (h.Conf.Encless && len(data) == EnclessEnlargeSize+h.Conf.MTU)) {
+               var err error
                // Decrypt rClient
-               dec := make([]byte, RSize)
-               salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key)
+               var dec []byte
+               if h.Conf.Encless {
+                       dec, err = EnclessDecode(
+                               h.key,
+                               h.rNonceNext(2),
+                               data[:len(data)-xtea.BlockSize],
+                       )
+                       if err != nil {
+                               log.Println("Unable to decode packet from", h.addr, err)
+                               return nil
+                       }
+                       dec = dec[:RSize]
+               } else {
+                       dec = make([]byte, RSize)
+                       salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key)
+               }
                if subtle.ConstantTimeCompare(dec, h.rClient[:]) != 1 {
                        log.Println("Invalid client's random number with", h.addr)
                        return nil
diff --git a/src/govpn/handshake_test.go b/src/govpn/handshake_test.go
new file mode 100644 (file)
index 0000000..20bafae
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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 (
+       "testing"
+)
+
+func TestHandshakeSymmetric(t *testing.T) {
+       // initial values are taken from peer_test.go's init()
+       v := VerifierNew(DefaultM, DefaultT, DefaultP, &testPeerId)
+       testConf.Verifier = v
+       testConf.DSAPriv = v.PasswordApply("does not matter")
+       hsS := NewHandshake("server", Dummy{&testCt}, testConf)
+       hsC := HandshakeStart("client", Dummy{&testCt}, testConf)
+       hsS.Server(testCt)
+       hsC.Client(testCt)
+       if hsS.Server(testCt) == nil {
+               t.Fail()
+       }
+       if hsC.Client(testCt) == nil {
+               t.Fail()
+       }
+}
+
+func TestHandshakeNoiseSymmetric(t *testing.T) {
+       // initial values are taken from peer_test.go's init()
+       v := VerifierNew(DefaultM, DefaultT, DefaultP, &testPeerId)
+       testConf.Verifier = v
+       testConf.DSAPriv = v.PasswordApply("does not matter")
+       testConf.Noise = true
+       hsS := NewHandshake("server", Dummy{&testCt}, testConf)
+       hsC := HandshakeStart("client", Dummy{&testCt}, testConf)
+       hsS.Server(testCt)
+       hsC.Client(testCt)
+       if hsS.Server(testCt) == nil {
+               t.Fail()
+       }
+       if hsC.Client(testCt) == nil {
+               t.Fail()
+       }
+       testConf.Noise = false
+}
+func TestHandshakeEnclessSymmetric(t *testing.T) {
+       // initial values are taken from peer_test.go's init()
+       v := VerifierNew(DefaultM, DefaultT, DefaultP, &testPeerId)
+       testConf.Verifier = v
+       testConf.DSAPriv = v.PasswordApply("does not matter")
+       testConf.Encless = true
+       testConf.Noise = true
+       hsS := NewHandshake("server", Dummy{&testCt}, testConf)
+       hsC := HandshakeStart("client", Dummy{&testCt}, testConf)
+       hsS.Server(testCt)
+       hsC.Client(testCt)
+       if hsS.Server(testCt) == nil {
+               t.Fail()
+       }
+       if hsC.Client(testCt) == nil {
+               t.Fail()
+       }
+       testConf.Encless = false
+       testConf.Noise = false
+}
index b028b393f0bfce87c2d3fc6ad219378e4c6b7b83..ed55e0dce6148b540878bf502b821b88d848cd9c 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -46,14 +46,14 @@ type CipherCache struct {
        l sync.RWMutex
 }
 
-func NewCipherCache(peerIds []PeerId) CipherCache {
+func NewCipherCache(peerIds []PeerId) *CipherCache {
        cc := CipherCache{c: make(map[PeerId]*xtea.Cipher, len(peerIds))}
        cc.Update(peerIds)
-       return cc
+       return &cc
 }
 
 // Remove disappeared keys, add missing ones with initialized ciphers.
-func (cc CipherCache) Update(peerIds []PeerId) {
+func (cc *CipherCache) Update(peerIds []PeerId) {
        available := make(map[PeerId]struct{})
        for _, peerId := range peerIds {
                available[peerId] = struct{}{}
@@ -81,7 +81,7 @@ func (cc CipherCache) Update(peerIds []PeerId) {
 // Try to find peer's identity (that equals to an encryption key)
 // by taking first blocksize sized bytes from data at the beginning
 // as plaintext and last bytes as cyphertext.
-func (cc CipherCache) Find(data []byte) *PeerId {
+func (cc *CipherCache) Find(data []byte) *PeerId {
        if len(data) < xtea.BlockSize*2 {
                return nil
        }
index 45b5dd7d01ac47022220387e90c3b040215b1e3f..5ea245bbd44a02aa196c66801b3bd22b4c3f19f3 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -19,8 +19,10 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package govpn
 
 import (
+       "bytes"
        "encoding/binary"
        "io"
+       "log"
        "sync"
        "sync/atomic"
        "time"
@@ -38,12 +40,12 @@ const (
        S20BS = 64
        // Maximal amount of bytes transfered with single key (4 GiB)
        MaxBytesPerKey int64 = 1 << 32
-       // Size of packet's size mark in bytes
-       PktSizeSize = 2
        // Heartbeat rate, relative to Timeout
        TimeoutHeartbeat = 4
        // Minimal valid packet length
-       MinPktLength = 2 + 16 + 8
+       MinPktLength = 1 + 16 + 8
+       // Padding byte
+       PadByte = byte(0x80)
 )
 
 func newNonceCipher(key *[32]byte) *xtea.Cipher {
@@ -70,6 +72,8 @@ type Peer struct {
        NoiseEnable bool
        CPR         int
        CPRCycle    time.Duration `json:"-"`
+       Encless     bool
+       MTU         int
 
        // Cryptography related
        Key          *[SSize]byte `json:"-"`
@@ -108,7 +112,7 @@ type Peer struct {
        bufR     []byte
        tagR     *[TagSize]byte
        keyAuthR *[SSize]byte
-       pktSizeR uint16
+       pktSizeR int
 
        // Transmitter
        BusyT    sync.Mutex `json:"-"`
@@ -127,11 +131,11 @@ func (p *Peer) String() string {
 func (p *Peer) Zero() {
        p.BusyT.Lock()
        p.BusyR.Lock()
-       sliceZero(p.Key[:])
-       sliceZero(p.bufR)
-       sliceZero(p.bufT)
-       sliceZero(p.keyAuthR[:])
-       sliceZero(p.keyAuthT[:])
+       SliceZero(p.Key[:])
+       SliceZero(p.bufR)
+       SliceZero(p.bufT)
+       SliceZero(p.keyAuthR[:])
+       SliceZero(p.keyAuthT[:])
        p.BusyT.Unlock()
        p.BusyR.Unlock()
 }
@@ -141,11 +145,24 @@ func (p *Peer) NonceExpectation(buf []byte) {
        p.NonceCipher.Encrypt(buf, buf)
 }
 
+func cprCycleCalculate(conf *PeerConf) time.Duration {
+       if conf.CPR == 0 {
+               return time.Duration(0)
+       }
+       rate := conf.CPR * 1 << 10
+       if conf.Encless {
+               rate /= EnclessEnlargeSize + conf.MTU
+       } else {
+               rate /= conf.MTU
+       }
+       return time.Second / time.Duration(rate)
+}
+
 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
        now := time.Now()
        timeout := conf.Timeout
 
-       cprCycle := cprCycleCalculate(conf.CPR)
+       cprCycle := cprCycleCalculate(conf)
        noiseEnable := conf.Noise
        if conf.CPR > 0 {
                noiseEnable = true
@@ -154,6 +171,11 @@ func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[S
                timeout = timeout / TimeoutHeartbeat
        }
 
+       bufSize := S20BS + 2*conf.MTU
+       if conf.Encless {
+               bufSize += EnclessEnlargeSize
+               noiseEnable = true
+       }
        peer := Peer{
                Addr: addr,
                Id:   conf.Id,
@@ -162,6 +184,8 @@ func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[S
                NoiseEnable: noiseEnable,
                CPR:         conf.CPR,
                CPRCycle:    cprCycle,
+               Encless:     conf.Encless,
+               MTU:         conf.MTU,
 
                Key:          key,
                NonceCipher:  newNonceCipher(key),
@@ -172,8 +196,8 @@ func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[S
                Established: now,
                LastPing:    now,
 
-               bufR:     make([]byte, S20BS+MTU+NonceSize),
-               bufT:     make([]byte, S20BS+MTU+NonceSize),
+               bufR:     make([]byte, bufSize),
+               bufT:     make([]byte, bufSize),
                tagR:     new([TagSize]byte),
                tagT:     new([TagSize]byte),
                keyAuthR: new([SSize]byte),
@@ -195,34 +219,37 @@ func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[S
 // that he is free to receive new packets. Encrypted and authenticated
 // packets will be sent to remote Peer side immediately.
 func (p *Peer) EthProcess(data []byte) {
+       if len(data) > p.MTU-1 { // 1 is for padding byte
+               log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
+               return
+       }
        p.now = time.Now()
        p.BusyT.Lock()
 
        // Zero size is a heartbeat packet
+       SliceZero(p.bufT)
        if len(data) == 0 {
                // If this heartbeat is necessary
                if !p.LastSent.Add(p.Timeout).Before(p.now) {
                        p.BusyT.Unlock()
                        return
                }
-               p.bufT[S20BS+0] = byte(0)
-               p.bufT[S20BS+1] = byte(0)
+               p.bufT[S20BS+0] = PadByte
                p.HeartbeatSent++
        } else {
                // Copy payload to our internal buffer and we are ready to
                // accept the next one
-               binary.BigEndian.PutUint16(
-                       p.bufT[S20BS:S20BS+PktSizeSize],
-                       uint16(len(data)),
-               )
-               copy(p.bufT[S20BS+PktSizeSize:], data)
+               copy(p.bufT[S20BS:], data)
+               p.bufT[S20BS+len(data)] = PadByte
                p.BytesPayloadOut += int64(len(data))
        }
 
-       if p.NoiseEnable {
-               p.frameT = p.bufT[S20BS : S20BS+MTU-TagSize]
+       if p.NoiseEnable && !p.Encless {
+               p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
+       } else if p.Encless {
+               p.frameT = p.bufT[S20BS : S20BS+p.MTU]
        } else {
-               p.frameT = p.bufT[S20BS : S20BS+PktSizeSize+len(data)+NonceSize]
+               p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
        }
        p.nonceOur += 2
        binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
@@ -230,20 +257,30 @@ func (p *Peer) EthProcess(data []byte) {
                p.frameT[len(p.frameT)-NonceSize:],
                p.frameT[len(p.frameT)-NonceSize:],
        )
-       for i := 0; i < SSize; i++ {
-               p.bufT[i] = byte(0)
+       var out []byte
+       if p.Encless {
+               var err error
+               out, err = EnclessEncode(
+                       p.Key,
+                       p.frameT[len(p.frameT)-NonceSize:],
+                       p.frameT[:len(p.frameT)-NonceSize],
+               )
+               if err != nil {
+                       panic(err)
+               }
+               out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
+       } else {
+               salsa20.XORKeyStream(
+                       p.bufT[:S20BS+len(p.frameT)-NonceSize],
+                       p.bufT[:S20BS+len(p.frameT)-NonceSize],
+                       p.frameT[len(p.frameT)-NonceSize:],
+                       p.Key,
+               )
+               copy(p.keyAuthT[:], p.bufT[:SSize])
+               poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
+               atomic.AddInt64(&p.BytesOut, int64(len(p.frameT)+TagSize))
+               out = append(p.tagT[:], p.frameT...)
        }
-       salsa20.XORKeyStream(
-               p.bufT[:S20BS+len(p.frameT)-NonceSize],
-               p.bufT[:S20BS+len(p.frameT)-NonceSize],
-               p.frameT[len(p.frameT)-NonceSize:],
-               p.Key,
-       )
-
-       copy(p.keyAuthT[:], p.bufT[:SSize])
-       poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
-
-       atomic.AddInt64(&p.BytesOut, int64(len(p.frameT)+TagSize))
        p.FramesOut++
 
        if p.CPRCycle != time.Duration(0) {
@@ -255,7 +292,7 @@ func (p *Peer) EthProcess(data []byte) {
        }
 
        p.LastSent = p.now
-       p.Conn.Write(append(p.tagT[:], p.frameT...))
+       p.Conn.Write(out)
        p.BusyT.Unlock()
 }
 
@@ -263,25 +300,43 @@ 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)
-       }
-       copy(p.bufR[S20BS:], data[TagSize:])
-       salsa20.XORKeyStream(
-               p.bufR[:S20BS+len(data)-TagSize-NonceSize],
-               p.bufR[:S20BS+len(data)-TagSize-NonceSize],
-               data[len(data)-NonceSize:],
-               p.Key,
-       )
-
-       copy(p.keyAuthR[:], p.bufR[:SSize])
-       copy(p.tagR[:], data[:TagSize])
-       if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
-               p.FramesUnauth++
-               p.BusyR.Unlock()
+       if !p.Encless && len(data) > len(p.bufR)-S20BS {
                return false
        }
+       var out []byte
+       p.BusyR.Lock()
+       if p.Encless {
+               var err error
+               out, err = EnclessDecode(
+                       p.Key,
+                       data[len(data)-NonceSize:],
+                       data[:len(data)-NonceSize],
+               )
+               if err != nil {
+                       p.FramesUnauth++
+                       p.BusyR.Unlock()
+                       return false
+               }
+       } else {
+               for i := 0; i < SSize; i++ {
+                       p.bufR[i] = 0
+               }
+               copy(p.bufR[S20BS:], data[TagSize:])
+               salsa20.XORKeyStream(
+                       p.bufR[:S20BS+len(data)-TagSize-NonceSize],
+                       p.bufR[:S20BS+len(data)-TagSize-NonceSize],
+                       data[len(data)-NonceSize:],
+                       p.Key,
+               )
+               copy(p.keyAuthR[:], p.bufR[:SSize])
+               copy(p.tagR[:], data[:TagSize])
+               if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
+                       p.FramesUnauth++
+                       p.BusyR.Unlock()
+                       return false
+               }
+               out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
+       }
 
        // Check if received nonce is known to us in either of two buckets.
        // If yes, then this is ignored duplicate.
@@ -323,18 +378,26 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
        p.FramesIn++
        atomic.AddInt64(&p.BytesIn, int64(len(data)))
        p.LastPing = time.Now()
-       p.pktSizeR = binary.BigEndian.Uint16(p.bufR[S20BS : S20BS+PktSizeSize])
+       p.pktSizeR = bytes.LastIndexByte(out, PadByte)
+       if p.pktSizeR == -1 {
+               p.BusyR.Unlock()
+               return false
+       }
+       // Validate the pad
+       for i := p.pktSizeR + 1; i < len(out); i++ {
+               if out[i] != 0 {
+                       p.BusyR.Unlock()
+                       return false
+               }
+       }
 
        if p.pktSizeR == 0 {
                p.HeartbeatRecv++
                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])
+       tap.Write(out[:p.pktSizeR])
        p.BusyR.Unlock()
        return true
 }
index c9c26475e8d7bf58d1ef7468ed1a5b1c8d273f1a..7fa8aab8438a7fd04eaccf53972064edd91b8792 100644 (file)
@@ -1,16 +1,35 @@
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 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 (
        "testing"
+       "testing/quick"
        "time"
 )
 
 var (
-       peer       *Peer
-       plaintext  []byte
-       ciphertext []byte
-       peerId     *PeerId
-       conf       *PeerConf
+       testPeer   *Peer
+       testPt     []byte
+       testCt     []byte
+       testPeerId PeerId
+       testConf   *PeerConf
 )
 
 type Dummy struct {
@@ -25,39 +44,94 @@ func (d Dummy) Write(b []byte) (int, error) {
 }
 
 func init() {
-       MTU = 1500
        id := new([IDSize]byte)
-       peerId := PeerId(*id)
-       conf = &PeerConf{
-               Id:      &peerId,
+       testPeerId = PeerId(*id)
+       testConf = &PeerConf{
+               Id:      &testPeerId,
+               MTU:     MTUDefault,
                Timeout: time.Second * time.Duration(TimeoutDefault),
-               Noise:   false,
-               CPR:     0,
        }
-       peer = newPeer(true, "foo", Dummy{&ciphertext}, conf, new([SSize]byte))
-       plaintext = make([]byte, 789)
+       testPeer = newPeer(true, "foo", Dummy{&testCt}, testConf, new([SSize]byte))
+       testPt = make([]byte, 789)
+}
+
+func TestTransportSymmetric(t *testing.T) {
+       peerd := newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
+       f := func(payload []byte) bool {
+               if len(payload) == 0 {
+                       return true
+               }
+               testPeer.EthProcess(payload)
+               return peerd.PktProcess(testCt, Dummy{nil}, true)
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+}
+
+func TestTransportSymmetricNoise(t *testing.T) {
+       peerd := newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
+       testPeer.NoiseEnable = true
+       peerd.NoiseEnable = true
+       f := func(payload []byte) bool {
+               if len(payload) == 0 {
+                       return true
+               }
+               testPeer.EthProcess(payload)
+               return peerd.PktProcess(testCt, Dummy{nil}, true)
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+       testPeer.NoiseEnable = true
+}
+
+func TestTransportSymmetricEncless(t *testing.T) {
+       peerd := newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
+       testPeer.Encless = true
+       testPeer.NoiseEnable = true
+       peerd.Encless = true
+       peerd.NoiseEnable = true
+       f := func(payload []byte) bool {
+               if len(payload) == 0 {
+                       return true
+               }
+               testPeer.EthProcess(payload)
+               return peerd.PktProcess(testCt, Dummy{nil}, true)
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+       testPeer.NoiseEnable = false
+       testPeer.Encless = false
 }
 
 func BenchmarkEnc(b *testing.B) {
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
-               peer.EthProcess(plaintext)
+               testPeer.EthProcess(testPt)
        }
 }
 
 func BenchmarkDec(b *testing.B) {
-       peer = newPeer(true, "foo", Dummy{&ciphertext}, conf, new([SSize]byte))
-       peer.EthProcess(plaintext)
-       peer = newPeer(true, "foo", Dummy{nil}, conf, new([SSize]byte))
-       orig := make([]byte, len(ciphertext))
-       copy(orig, ciphertext)
+       testPeer = newPeer(true, "foo", Dummy{&testCt}, testConf, new([SSize]byte))
+       testPeer.EthProcess(testPt)
+       testPeer = newPeer(true, "foo", Dummy{nil}, testConf, new([SSize]byte))
+       orig := make([]byte, len(testCt))
+       copy(orig, testCt)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
-               peer.nonceBucket0 = make(map[uint64]struct{}, 1)
-               peer.nonceBucket1 = make(map[uint64]struct{}, 1)
-               copy(ciphertext, orig)
-               if !peer.PktProcess(ciphertext, Dummy{nil}, true) {
+               testPeer.nonceBucket0 = make(map[uint64]struct{}, 1)
+               testPeer.nonceBucket1 = make(map[uint64]struct{}, 1)
+               copy(testCt, orig)
+               if !testPeer.PktProcess(testCt, Dummy{nil}, true) {
                        b.Fail()
                }
        }
 }
+
+func TestTransportBigger(t *testing.T) {
+       tmp := make([]byte, MTUMax*4)
+       Rand.Read(tmp)
+       testPeer.PktProcess(tmp, Dummy{nil}, true)
+}
index 7cdb3b7298d74f3742e587f2560f5db9cebac0f2..1d956742607071447999665b39bd53a4db1dd2e2 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
index 9c999b41374b2750fa05490331219b388fb88107..6e545b4decfeae9262a73f82e00348e60c89c609 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -20,12 +20,6 @@ package govpn
 
 import (
        "io"
-
-       "golang.org/x/crypto/poly1305"
-)
-
-const (
-       EtherSize = 14
 )
 
 type TAP struct {
@@ -41,14 +35,7 @@ var (
        taps = make(map[string]*TAP)
 )
 
-// Return maximal acceptable TAP interface MTU. This is daemon's MTU
-// minus nonce, MAC, packet size mark and Ethernet header sizes.
-func TAPMaxMTU() int {
-       return MTU - poly1305.TagSize - NonceSize - PktSizeSize - EtherSize
-}
-
-func NewTAP(ifaceName string) (*TAP, error) {
-       maxIfacePktSize := TAPMaxMTU() + EtherSize
+func NewTAP(ifaceName string, mtu int) (*TAP, error) {
        tapRaw, err := newTAPer(ifaceName)
        if err != nil {
                return nil, err
@@ -56,8 +43,8 @@ func NewTAP(ifaceName string) (*TAP, error) {
        tap := TAP{
                Name: ifaceName,
                dev:  tapRaw,
-               buf0: make([]byte, maxIfacePktSize),
-               buf1: make([]byte, maxIfacePktSize),
+               buf0: make([]byte, mtu),
+               buf1: make([]byte, mtu),
                Sink: make(chan []byte),
        }
        go func() {
@@ -85,12 +72,12 @@ func (t *TAP) Write(data []byte) (n int, err error) {
        return t.dev.Write(data)
 }
 
-func TAPListen(ifaceName string) (*TAP, error) {
+func TAPListen(ifaceName string, mtu int) (*TAP, error) {
        tap, exists := taps[ifaceName]
        if exists {
                return tap, nil
        }
-       tap, err := NewTAP(ifaceName)
+       tap, err := NewTAP(ifaceName, mtu)
        if err != nil {
                return nil, err
        }
index 676303025e2eeadc7f4eef4056a2cb857adc69a9..157c583c76f1b5ffd28cb0ac1bf8a1c933474465 100644 (file)
@@ -2,7 +2,7 @@
 
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
 */
 
 package govpn
index 71ea90d995c11b65182939acb44da2c96b551f11..fb9df56437b80829340e007b7ec02421dd750148 100644 (file)
@@ -2,7 +2,7 @@
 
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
 */
 
 package govpn
index fe0965348d03f7ad5bc04e11ada533d621c3c1c0..ba51846ce23eb17c42b4a9b9fcc5d0333f848bbc 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 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
@@ -58,7 +58,7 @@ func (v *Verifier) PasswordApply(password string) *[ed25519.PrivateKeySize]byte
        if err != nil {
                log.Fatalln("Unable to apply Argon2d", err)
        }
-       defer sliceZero(r)
+       defer SliceZero(r)
        src := bytes.NewBuffer(r)
        pub, prv, err := ed25519.GenerateKey(src)
        if err != nil {
index 89bd8c8dd9b7b11c276a554b406036bac8ab6093..cf819eceafe3460110d516368b93c90f357a9a5c 100755 (executable)
@@ -22,7 +22,6 @@ golang.org/x/crypto/LICENSE
 golang.org/x/crypto/PATENTS
 golang.org/x/crypto/README
 golang.org/x/crypto/curve25519
-golang.org/x/crypto/pbkdf2
 golang.org/x/crypto/poly1305
 golang.org/x/crypto/salsa20
 golang.org/x/crypto/xtea
@@ -73,11 +72,13 @@ be reviewable, secure, DPI/censorship-resistant, written on Go.
 It uses fast strong passphrase authenticated key agreement protocol with
 augmented zero-knowledge mutual peers authentication (PAKE DH A-EKE).
 Encrypted, authenticated data transport that hides message's length and
-timestamps. Perfect forward secrecy property. Resistance to: offline
-dictionary attacks, replay attacks, client's passphrases compromising
-and dictionary attacks on the server side. Built-in heartbeating,
-rehandshaking, real-time statistics. Ability to work through UDP, TCP
-and HTTP proxies. IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support.
+timestamps. Optional encryptionless mode, that still preserves data
+confidentiality. Perfect forward secrecy property. Resistance to:
+offline dictionary attacks, replay attacks, client's passphrases
+compromising and dictionary attacks on the server side. Built-in
+heartbeating, rehandshaking, real-time statistics. Ability to work
+through UDP, TCP and HTTP proxies. IPv4/IPv6-compatibility.
+GNU/Linux and FreeBSD support.
 
 ----------------8<-----------------8<-----------------8<----------------
 
@@ -87,7 +88,7 @@ $(git cat-file -p $release | sed -n '6,/^.*BEGIN/p' | sed '$d')
 
 ----------------8<-----------------8<-----------------8<----------------
 
-GoVPN's home page is: http://govpn.info -> http://www.cypherpunks.ru/govpn/
+GoVPN's home page is: http://www.cypherpunks.ru/govpn/ (http://govpn.info/)
 also available as Tor hidden service: http://vabu56j2ep2rwv3b.onion/govpn/
 
 Source code and its signature for that version can be found here:
@@ -100,7 +101,7 @@ GPG key ID: 0xF2F59045FFE2F4A1 GoVPN release signing key
 Fingerprint: D269 9B73 3C41 2068 D8DA  656E F2F5 9045 FFE2 F4A1
 
 Please send questions regarding the use of GoVPN, bug reports and patches
-to mailing list: https://lists.cypherpunks.ru/mailman/listinfo/govpn-devel/
+to mailing list: https://lists.cypherpunks.ru/pipermail/govpn-devel/
 EOF
 
 cat <<EOF
@@ -116,13 +117,14 @@ GoVPN это простой демон виртуальных частных с
 несбалансированный протокол согласования ключей с двусторонней
 аутентификацией сторон (PAKE DH A-EKE). Зашифрованный, аутентифицируемый
 транспортный протокол передачи данных, скрывающий длины сообщений и их
-временные характеристики. Свойство совершенной прямой секретности.
-Устойчивость к: внесетевым (offline) атакам по словарю, атакам
-повторного воспроизведения (replay), компрометации клиентских парольных
-фраз на стороне сервера. Встроенные функции сердцебиения (heartbeat),
-пересогласования ключей, статистика реального времени. Возможность
-работы поверх UDP, TCP и HTTP прокси. Совместимость с IPv4 и IPv6.
-Поддержка GNU/Linux и FreeBSD.
+временные характеристики. Опциональный нешифрованный режим, который
+всё-равно обеспечивает конфиденциальность и аутентичность данных.
+Свойство совершенной прямой секретности. Устойчивость к: внесетевым
+(offline) атакам по словарю, атакам повторного воспроизведения (replay),
+компрометации клиентских парольных фраз на стороне сервера. Встроенные
+функции сердцебиения (heartbeat), пересогласования ключей, статистика
+реального времени. Возможность работы поверх UDP, TCP и HTTP прокси.
+Совместимость с IPv4 и IPv6. Поддержка GNU/Linux и FreeBSD.
 
 ----------------8<-----------------8<-----------------8<----------------
 
@@ -132,8 +134,8 @@ $(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
+Домашняя страница GoVPN: http://www.cypherpunks.ru/govpn/ (http://govpn.info/)
+Коротко о демоне: http://www.cypherpunks.ru/govpn/O-demone.html
 также доступна как скрытый сервис Tor: http://vabu56j2ep2rwv3b.onion/govpn/
 
 Исходный код и его подпись для этой версии находится здесь:
@@ -147,5 +149,5 @@ SHA256 хэш: $hash
 
 Пожалуйста все вопросы касающиеся использования GoVPN, отчёты об ошибках
 и патчи отправляйте в govpn-devel почтовую рассылку:
-https://lists.cypherpunks.ru/mailman/listinfo/govpn-devel/
+https://lists.cypherpunks.ru/pipermail/govpn-devel/
 EOF
index e0d8c855b8fad14f1b3c8538242d0eb741c5b581..a57becbd80af8544beea2a7772bfe31f67cd520f 100755 (executable)
@@ -1,5 +1,7 @@
 #!/bin/sh -e
 
+PATH=$PATH:.
+
 [ -n "$1" ] || {
     cat <<EOF
 Example script for creating new user peer for GoVPN.
@@ -28,6 +30,7 @@ Place the following JSON configuration entry on the server's side:
 
     "$username": {
         "up": "/path/to/up.sh",
+        "iface": "or TAP interface name",
         "verifier": "$verifierS"
     }