--- /dev/null
+[submodule "src/github.com/bigeagle/water"]
+ path = src/github.com/bigeagle/water
+ url = https://github.com/bigeagle/water.git
+[submodule "src/github.com/agl/ed25519"]
+ path = src/github.com/agl/ed25519
+ url = https://github.com/agl/ed25519.git
+[submodule "src/golang.org/x/crypto"]
+ path = src/golang.org/x/crypto
+ url = https://go.googlesource.com/crypto
GoVPN is simple secure free software virtual private network daemon,
-aimed to be reviewable, secure, DPI-resistant, written on Go.
+aimed to be reviewable, secure, DPI/censorship-resistant, written on Go.
-It uses fast PAKE DH A-EKE for mutual strong zero-knowledge peers
-authentication. Data transport is encrypted, authenticated, hides
-message's length and timestamp. PFS property, resistance to dictionary
-attacks, replay attacks. Built-in heartbeating, rehandshaking, real-time
-statistics, IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support.
+It uses fast strong password authenticated key agreement protocol with
+augmented 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 passwords 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.
subject "subscribe" to govpn-devel-request@lists.cypherpunks.ru.
Development Git source code repository currently is located here:
-https://github.com/stargrave/govpn.git
+http://git.cypherpunks.ru/cgit.cgi/govpn.git/
For futher information please read either doc/govpn.info or doc/govpn.texi.
-LDFLAGS = -X govpn.Version $(VERSION)
+LDFLAGS = -X govpn.Version=$(VERSION)
PREFIX ?= /usr/local
BINDIR = $(DESTDIR)$(PREFIX)/bin
INFODIR = $(DESTDIR)$(PREFIX)/info
all: govpn-client govpn-server govpn-verifier
-depends:
- $(MAKE) -C src
-
-govpn-client: depends
+govpn-client:
GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" govpn/cmd/govpn-client
-govpn-server: depends
+govpn-server:
GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" govpn/cmd/govpn-server
-govpn-verifier: depends
+govpn-verifier:
GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" govpn/cmd/govpn-verifier
bench:
- cd src/govpn ; GOPATH=$(GOPATH) GOMAXPROC=2 go test -bench .
+ cd src/govpn ; GOPATH=$(GOPATH) go test -bench .
clean:
rm -f govpn-client govpn-server govpn-verifier
@node Client part
@section Client part
-Except for common @code{-mtu}, @code{-stats}, @code{-egd} options client
-has the following ones:
+Except for common @code{-mtu}, @code{-stats}, @code{-egd}
+options client has the following ones:
@table @code
+@item -proto
+@ref{Network transport} to use. Can be either @emph{udp} or @emph{tcp}.
+
+@item -proxy
+Use specified @emph{host:port} @ref{Proxy} server for accessing remote
+server.
+
+@item -proxy-auth
+Optional @emph{user:password} for HTTP Basic authorization on proxy
+server.
+
@item -remote
Address (@code{host:port} format) of remote server we need to connect to.
@node CPR
-@section CPR
+@section 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
@node Developer manual
@unnumbered Developer manual
+Pay attention how to get @ref{Development source code}.
+
@table @asis
@item Nonce and identity encryption
@url{http://143.53.36.235:8080/tea.htm, XTEA}.
@url{https://en.wikipedia.org/wiki/PBKDF2, PBKDF2} based on
@url{https://en.wikipedia.org/wiki/SHA-2, SHA-512}.
@item Packet overhead
-26 bytes per packet.
+26 bytes per packet. Two more bytes in TCP mode.
@item Handshake overhead
-4 UDP (2 from client, 2 from server) packets, 264 bytes total payload.
+4 UDP (2 from client, 2 from server) packets (round-trips for TCP),
+264 bytes total payload (8 bytes more in TCP mode).
@item Entropy required
832 bits in average on client, 832 bits in average on server side per
handshake.
@multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
@headitem Version @tab Size @tab Tarball @tab SHA256 checksum
-@item 1.5 @tab 19 KiB
-@tab @url{download/govpn-1.5.tar.xz, link} @url{download/govpn-1.5.tar.xz.sig, sign}
-@tab @code{715b07d4d1ea4396c3e37014ca65ec3768818423521f3c12e7200b6edca48c31}
+@item 3.4 @tab 175 KiB
+@tab @url{download/govpn-3.4.tar.xz, link} @url{download/govpn-3.4.tar.xz.sig, sign}
+@tab @code{266612a7f8faa6ceb2955ed611c0c21872776306f4eaad5b785145bbb0390c82}
-@item 2.0 @tab 31 KiB
-@tab @url{download/govpn-2.0.tar.xz, link} @url{download/govpn-2.0.tar.xz.sig, sign}
-@tab @code{d43be1248d6a46ba8ca75be2fdab5e3d8b0660fb9df9b6d87cfa3973722b42be}
+@item 3.3 @tab 175 KiB
+@tab @url{download/govpn-3.3.tar.xz, link} @url{download/govpn-3.3.tar.xz.sig, sign}
+@tab @code{1834a057215324f49d6272b2beb89f1532105156f7e853eae855659992ac0c84}
-@item 2.2 @tab 32 KiB
-@tab @url{download/govpn-2.2.tar.xz, link} @url{download/govpn-2.2.tar.xz.sig, sign}
-@tab @code{5745278bce8b9a3bd7ec1636507bbce8c17ba1d79f1568e2f3681b7a90bbe6e1}
+@item 3.2 @tab 174 KiB
+@tab @url{download/govpn-3.2.tar.xz, link} @url{download/govpn-3.2.tar.xz.sig, sign}
+@tab @code{388e98d6adef5ebf3431b0d48419f54d2e2064c657de67e23c669ebcf273126d}
-@item 2.3 @tab 34 KiB
-@tab @url{download/govpn-2.3.tar.xz, link} @url{download/govpn-2.3.tar.xz.sig, sign}
-@tab @code{92986ec6d6da107c6cc1143659e5a154cd19b8f2ede5fa7f5ccc4525ae468e97}
+@item 3.1 @tab 54 KiB
+@tab @url{download/govpn-3.1.tar.xz, link} @url{download/govpn-3.1.tar.xz.sig, sign}
+@tab @code{4034a67eb472e33760ed1783ca871f531c3a6be99b9bd6213f4f83c1147c344b}
+
+@item 3.0 @tab 53 KiB
+@tab @url{download/govpn-3.0.tar.xz, link} @url{download/govpn-3.0.tar.xz.sig, sign}
+@tab @code{12579c5c3cccfe73c66b5893335bc70c42d7b13b8e94c7751ec65d421eaff9a5}
@item 2.4 @tab 42 KiB
@tab @url{download/govpn-2.4.tar.xz, link} @url{download/govpn-2.4.tar.xz.sig, sign}
@tab @code{df45225bac2384c5eed73c5cdb05dc3581495e08d365317beb03a2487d46b98c}
-@item 3.0 @tab 53 KiB
-@tab @url{download/govpn-3.0.tar.xz, link} @url{download/govpn-3.0.tar.xz.sig, sign}
-@tab @code{12579c5c3cccfe73c66b5893335bc70c42d7b13b8e94c7751ec65d421eaff9a5}
+@item 2.3 @tab 34 KiB
+@tab @url{download/govpn-2.3.tar.xz, link} @url{download/govpn-2.3.tar.xz.sig, sign}
+@tab @code{92986ec6d6da107c6cc1143659e5a154cd19b8f2ede5fa7f5ccc4525ae468e97}
-@item 3.1 @tab 54 KiB
-@tab @url{download/govpn-3.1.tar.xz, link} @url{download/govpn-3.1.tar.xz.sig, sign}
-@tab @code{4034a67eb472e33760ed1783ca871f531c3a6be99b9bd6213f4f83c1147c344b}
+@item 2.2 @tab 32 KiB
+@tab @url{download/govpn-2.2.tar.xz, link} @url{download/govpn-2.2.tar.xz.sig, sign}
+@tab @code{5745278bce8b9a3bd7ec1636507bbce8c17ba1d79f1568e2f3681b7a90bbe6e1}
-@item 3.2 @tab 174 KiB
-@tab @url{download/govpn-3.2.tar.xz, link} @url{download/govpn-3.2.tar.xz.sig, sign}
-@tab @code{388e98d6adef5ebf3431b0d48419f54d2e2064c657de67e23c669ebcf273126d}
+@item 2.0 @tab 31 KiB
+@tab @url{download/govpn-2.0.tar.xz, link} @url{download/govpn-2.0.tar.xz.sig, sign}
+@tab @code{d43be1248d6a46ba8ca75be2fdab5e3d8b0660fb9df9b6d87cfa3973722b42be}
-@item 3.3 @tab 175 KiB
-@tab @url{download/govpn-3.3.tar.xz, link} @url{download/govpn-3.3.tar.xz.sig, sign}
-@tab @code{1834a057215324f49d6272b2beb89f1532105156f7e853eae855659992ac0c84}
+@item 1.5 @tab 19 KiB
+@tab @url{download/govpn-1.5.tar.xz, link} @url{download/govpn-1.5.tar.xz.sig, sign}
+@tab @code{715b07d4d1ea4396c3e37014ca65ec3768818423521f3c12e7200b6edca48c31}
@end multitable
Also you can try its @ref{Contacts, .onion} version.
-Sourceforge.net also provides mirror for the files above:
-@url{http://sourceforge.net/projects/govpn/files/}.
@node EGD
-@section EGD
+@section Entropy Gathering Daemon
Overall security mainly depends on client side:
@ref{PAKE, good passphrase} and cryprographically good pseudo random
@strong{Install}. At first you must @ref{Installation, install} this
software: download, check the signature, compile.
-Do not forget about setting @code{GOMAXPROC} environment variable for
-using more than one CPU on both sides:
-
-@example
-% export GOMAXPROC=4
-@end example
-
@strong{Prepare the server}. Create the new client, named (for example)
"Alice":
@example
-% ./utils/newclient.sh Alice
+server% ./utils/newclient.sh Alice
Place verifier to peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier
@end example
identity:
@example
-% ./utils/storekey.sh /tmp/passphrase
+client% ./utils/storekey.sh /tmp/passphrase
Enter passphrase:[my secure passphrase is here]
-% govpn-verifier -id 6d4ac605ce8dc37c2f0bf21cb542a713 -key /tmp/passphrase
+client% govpn-verifier -id 6d4ac605ce8dc37c2f0bf21cb542a713 -key /tmp/passphrase
562556cc9ecf0019b4cf45bcdf42706944ae9b3ac7c73ad299d83f2d5a169c55
-% rm /tmp/passphrase
+client% rm /tmp/passphrase
@end example
"562556cc9ecf0019b4cf45bcdf42706944ae9b3ac7c73ad299d83f2d5a169c55" --
@strong{Save verifier on server}.
@example
-% cat > peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier <<EOF
+server% cat > peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier <<EOF
562556cc9ecf0019b4cf45bcdf42706944ae9b3ac7c73ad299d83f2d5a169c55
EOF
@end example
@copying
This manual is for GoVPN -- simple secure free software virtual private
-network (VPN) daemon, written entirely on Go programming language.
+network daemon, aimed to be reviewable, secure, DPI/censorship-resistant,
+written on Go.
Copyright @copyright{} 2014-2015 @email{stargrave@@stargrave.org, Sergey Matveev}
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
-A copy of the license is included below.
+A copy of the license is included in the section entitled "Copying conditions".
@end quotation
@end copying
@top GoVPN
GoVPN is simple secure free software virtual private network daemon,
-aimed to be reviewable, secure, DPI-resistant, written on Go.
+aimed to be reviewable, secure and
+@url{https://en.wikipedia.org/wiki/Deep_packet_inspection, DPI}/censorship-resistant.
-It uses fast PAKE DH A-EKE for mutual strong zero-knowledge peers
-authentication. Data transport is encrypted, authenticated, hides
-message's length and timestamp. PFS property, resistance to dictionary
-attacks, replay attacks. Built-in heartbeating, rehandshaking, real-time
-statistics, IPv4/IPv6-compatibility. GNU/Linux and FreeBSD support.
+@itemize @bullet
+@item
+Copylefted free software: licensed under
+@url{https://www.gnu.org/licenses/gpl-3.0.html, GPLv3+}.
+@item
+Fast strong @ref{PAKE, password authenticated} augmented key agreement
+(PAKE DH A-EKE) @ref{Handshake protocol, handshake}.
+@item
+Mutual two-side zero-knowledge peers authentication.
+@item
+@ref{Verifier structure, Augmented authentication tokens} resistant to
+offline dictionary attacks. An attacker can not masquerade a client
+even with server password verifiers compromising.
+@item
+Encrypted and authenticated @ref{Transport protocol, payload transport}
+with 128-bit @ref{Developer manual, security margin} state-of-the-art
+cryptography and censorship resistance (indistinguishability from noise).
+@item
+@url{https://en.wikipedia.org/wiki/Forward_secrecy, Perfect forward secrecy}
+property.
+@item
+Replay attack protection (using one-time MACs).
+@item
+Built-in rehandshake (session key rotation) and heartbeat features.
+@item
+Ability to hide payload packets length with the @ref{Noise, noise} data.
+@item
+Ability to hide payload timestamps with @ref{CPR, constant packet rate}
+traffic.
+@item
+Compatible with @url{http://egd.sourceforge.net/, EGD} (entropy
+gathering daemon) PRNGs.
+@item
+Several simultaneous clients support with per-client configuration
+options. Clients have pre-established @ref{Identity, identity} invisible
+for third-parties (they are anonymous).
+@item
+Uses @url{https://en.wikipedia.org/wiki/TAP_(network_driver), TAP}
+underlying network interfaces.
+@item
+Can use @ref{Network transport, UDP and TCP} or HTTP @ref{Proxy, proxies}
+for accessing the server.
+@item
+Fully IPv4 and IPv6 compatible.
+@item
+Optional built-in HTTP-server for retrieving
+@ref{Stats, statistics} information about known connected peers in
+@url{http://json.org/, JSON} format.
+@item
+Written on on @url{http://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.
+@end itemize
-@include keywords.texi
+@include media.texi
@menu
-* Overview::
* News::
* Installation::
* Precautions::
* TODO::
@end menu
-@include overview.texi
@include news.texi
@include installation.texi
@include precautions.texi
Client's identity is 128-bit string. It is not secret, so can be
transmitted and stored in the clear. However handshake applies PRP on it
to make DPI and deanonymization much harder to success.
+
+@example
+% ./utils/newclient.sh doesnotmatter
+Place verifier to peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier
+@end example
+
+"6d4ac605ce8dc37c2f0bf21cb542a713" is client's identity.
@node Installation
@unnumbered Installation
-GoVPN is written on @url{http://golang.org/, Go programming language},
+GoVPN is written on Go programming language and you have to install Go
+compiler (1.5+ version is highly recommended): @code{lang/go} port in
+FreeBSD and @code{golang} package in most GNU/Linux distributions.
@emph{Make} is recommended for convenient building.
-@url{https://www.gnu.org/software/texinfo/, Texinfo}
-is used for building documentation.
+@url{https://www.gnu.org/software/texinfo/, Texinfo} is used for
+building documentation.
+Possibly you also need to install TUN/TAP interface utilities (depending
+on your operating system): @code{uml-utilities} package in most
+GNU/Linux distributions.
Included required libraries:
@item @code{github.com/bigeagle/water} @tab GNU/Linux @tab BSD 3-Clause
@end multitable
-Get the tarball and run @code{make}.
+Get @ref{Prepared tarballs, the tarball}, check its
+@ref{Tarballs integrity check, authenticity} and run @code{make}.
@emph{govpn-client}, @emph{govpn-server}, @emph{govpn-verifier}
-binaries will be build in the current directory.
-
-As a prerequisite you must install Go compiler and possibly TUN/TAP
-interfaces utilities:
-
-@itemize @bullet
-@item @code{lang/go} port in FreeBSD.
-@item @code{golang} and @code{uml-utilities} packages in GNU/Linux
-distributions.
-@end itemize
+binaries will be built in the current directory:
@example
% wget http://www.cypherpunks.ru/govpn/download/govpn-2.3.tar.xz
+++ /dev/null
-Some keywords describing this project: encryption, authentication, key
-exchange, EKE, Diffie-Hellman, DH, DH-EKE, Augmented EKE, A-EKE,
-security, encrypted key exchange, 128-bit margin, DPI, censorship,
-resistance, free software, GPLv3+, reviewability, easy, simple,
-Curve25519, Ed25519, Elligator, SHA-512, Salsa20, Poly1305, AEAD, XTEA,
-PBKDF2, PRP, signatures, asymmetric cryptography, zero-knowledge
-password proof, PAKE, password, passphrase, password authenticated key
-exchange, perfect forward secrecy, PFS, MAC, nonce, verifier,
-rehandshake, heartbeat, replay attack, MiTM, length hiding, timestamps
-hiding, noise, constant traffic, constant packet rate, CPR, EGD,
-entropy, TAP, TAP, VPN, GNU, Linux, FreeBSD, IPv6, dictionary attack,
-mutual authentication, simultaneous clients, JSON, HTTP-server,
-statistics, PRNG, traffic analysis, Go, golang.
--- /dev/null
+In the media:
+
+@itemize @bullet
+@item @url{http://habrahabr.ru/company/ivi/blog/256365/, Реализуем безопасный VPN-протокол} (on russian)
+@item @url{http://habrahabr.ru/company/ivi/blog/257431/, Реализуем ещё более безопасный VPN-протокол} (on russian)
+@item @url{http://www.linuxspace.org/archives/9449, Установка и настройка безопасного VPN-демона GoVPN 3.2} (on russian)
+@end itemize
@node MTU
-@section MTU
+@section 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
--- /dev/null
+@node Network transport
+@section Network transport
+
+You can use either UDP or TCP underlying network transport protocols.
+
+TCP consumes more traffic: two additional bytes per packet. Also it is
+has more complex and slightly slower code. Moreover because of packet
+loss and TCP reliability it can lead to "meltdown" effect: significant
+performance decrease of underlying TCP connections. So generally TCP is
+not advisable for VPNs, but it can help with some nasty firewalls.
@table @strong
+@item Release 3.5
+@itemize @bullet
+@item Ability to use @ref{Network transport, TCP} network transport.
+Server can listen on both UDP and TCP sockets.
+@item Ability to use @ref{Proxy, HTTP proxies} (through CONNECT method)
+for accessing the server. Server can also emulate HTTP proxy behaviour.
+@item Updated Poly1305 library with ARM-related bugfixes.
+@item Go 1.5+ version is highly recommended because of performance
+reasons.
+@end itemize
+
@item Release 3.4
@itemize @bullet
@item Ability to use external @ref{EGD}-compatible PRNGs. Now you are
+++ /dev/null
-@node Overview
-@unnumbered Overview
-
-GoVPN is simple secure virtual private network daemon, written entirely
-on @url{http://golang.org/, Go programming language}.
-
-Reviewability, high 128-bit security margin and
-@url{https://en.wikipedia.org/wiki/Deep_packet_inspection, DPI}
-censorship resistance in mind in free software solution are the main
-goals for that daemon. Most modern widespread protocols and their
-implementations in software are too complex to be reviewed, analyzed and
-modified.
-
-@ref{Developer manual, State off art cryptography technologies}. Strong
-mutual authenticated key exchange is invulnerable to man-in-the middle
-attachs.
-@url{https://en.wikipedia.org/wiki/Forward_secrecy, Perfect forward secrecy}
-property guarantees that compromising of long-term authentication keys
-does not lead to previously captured traffic decrypting.
-Compromising of peers password files on server side won't allow attacker
-to masquerade as the client, because of asymmetric @strong{verifiers}
-usage, resistant to dictionary attacks. Rehandshaking ensures session
-keys rotation. One-time keys MAC authentication protects against
-@url{https://en.wikipedia.org/wiki/Replay_attack, replay attacks}.
-
-Server can work with several clients simultaneously. Each client is
-@strong{identified} by 128-bit key, that does not leak during handshake
-and each client stays @strong{anonymous} for MiTM and DPI. All settings
-are applied per-peer separately.
-
-Optional ability to hide payload packets lengths by appending
-@strong{noise} to them during transmission. Ability to generate constant
-packet rate traffic (@strong{CPR}) that will hide even the fact of
-packets appearance, their timestamps.
-
-The only platform specific requirement is TAP network interface support.
-API to that kind of device is different, OS dependent and non portable.
-So only a few operating systems is officially supported. Author has no
-proprietary software to work with, so currently there is lack of either
-popular Microsoft Windows or Apple OS X support.
-
-@itemize @bullet
-@item
-Copylefted free software: licensed under
-@url{https://www.gnu.org/licenses/gpl-3.0.html, GPLv3+}
-@item
-Works with @url{https://en.wikipedia.org/wiki/TAP_(network_driver), TAP}
-network interfaces on top of UDP entirely
-@item
-@url{https://www.gnu.org/, GNU}/Linux and
-@url{http://www.freebsd.org/, FreeBSD} support.
-@item IPv6 compatible.
-@item Encrypted and authenticated payload transport.
-@item Relatively fast handshake.
-@item Password-authenticated key exchange.
-@item Server-side password verifiers are secure against dictionary
-attacks.
-@item Attacker can not masquerade a client even with password files
-compromising.
-@item Replay attack protection.
-@item Perfect forward secrecy property.
-@item Mutual two-side authentication.
-@item Zero knowledge authentication.
-@item Built-in rehandshake and heartbeat features.
-@item Several simultaneous clients support.
-@item Per-client configuration options.
-@item Hiding of payload packets length with noise.
-@item Hiding of payload packets timestamps with constant packet rate
-traffic.
-@item Optional built-in HTTP-server for retrieving information about
-known connected peers in @url{http://json.org/, JSON} format.
-@item Compatibility with @url{http://egd.sourceforge.net/, EGD} PRNGs.
-@end itemize
@node PAKE
-@section PAKE
+@section 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
it securely as described in @ref{Verifier}.
@item
-You must @strong{never} use one key for multiple clients.
+You must @strong{never} use the same key for multiple clients.
@item
You must use @strong{cryptographically good} pseudo random number
--- /dev/null
+@node Proxy
+@section 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
+server.
+
+Server has @emph{-proxy} option allowing to listen on specified port and
+accept HTTP request. All of them will be treated as a CONNECT method
+switching to raw TCP mode. You can make POST request and server will
+anyway switch to raw TCP mode. You are not forced to use this option:
+any external HTTP proxy server can be used.
+
+Client has @emph{-proxy} option forcing it to connect to proxy and send
+CONNECT method. Optionally it can be authenticated on it using
+@emph{-proxy-auth} HTTP Basic method.
+
+@example
+% govpn-client [...] -proto tcp \
+ -remote "$REMOTE_ADDR":1194 \
+ -proxy 192.168.55.1:8888 \
+ -proxy-auth mylogin:password
+@end example
has the following ones:
@table @code
+
+@item -proto
+@ref{Network transport} to use. Can be @emph{udp}, @emph{tcp} or @emph{all}.
+
@item -bind
Address (@code{host:port} format) we must bind to.
+
@item -peers
Path to the directory containing peers information, database.
+
+@item -proxy
+Start trivial HTTP @ref{Proxy} server on specified @emph{host:port}.
+
@end table
Peers directory must contain subdirectories with the names of client's
@node Development source code
@section Development source code
-Development source contains the latest version of the code. It may be
-buggy. It does not contain compiled documentation and dependent
-libraries source code. Because of that it is not recommended for porters.
+Development source code contains the latest version of the code. It may
+be buggy. It does not contain compiled documentation and dependent
+libraries source code. Because of that, it is not recommended for
+porters: use @ref{Prepared tarballs} instead.
-You can obtain it by cloning Git repository:
-@code{git clone https://github.com/stargrave/govpn.git}.
-@code{src} directory has makefile that will download necessary dependent
-library versions.
+You can obtain it by cloning Git repository and fetching dependent
+libraries source code as git submodules:
+
+@example
+% git clone git://git.cypherpunks.ru/govpn.git govpn
+% cd govpn
+% git checkout develop
+% git submodule update --init
+@end example
Also there is mirror of dependent libraries for safety if their native
repositories will be unavailable (they are seldom updated):
@multitable @columnfractions .50 .50
@headitem Software/library @tab Mirror
-@item @code{govpn} @tab @url{git://git.cypherpunks.ru/govpn.git}
+@item @code{govpn} @tab @url{https://github.com/stargrave/govpn.git}
@item @code{golang.org/x/crypto} @tab @url{git://git.cypherpunks.ru/crypto.git}
@item @code{github.com/agl/ed25519} @tab @url{git://git.cypherpunks.ru/ed25519.git}
+@item @code{github.com/bigeagle/water} @tab @url{git://git.cypherpunks.ru/water.git}
@end multitable
@node Thanks
@unnumbered Thanks
+Thanks for contributions and suggestions to:
+
@itemize @bullet
@item
@url{https://www.schneier.com/books/applied_cryptography/, Applied Cryptography}
@strong{Beware}: this consumes traffic.
Stale peers and handshake states are cleaned up every timeout period.
+
+This applies to TCP connections too: relatively much time can pass until
+we understand that remote TCP peer is suddenly died and did not
+normally terminate connection.
@itemize
@item Ability to tunnel only specified TCP connections, without full
featured VPN solution. Similar to ssh -R.
-@item Ability to work over HTTP, WebSockets and something similar.
@item Ability to noise handshake packets sizes.
@item Increase performance.
@item Randomize ports usage.
@section Transport protocol
@verbatim
-ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA+NOISE) +
+[PktLen] + ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA+NOISE) +
AUTH(ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA+NOISE))
@end verbatim
All transport and handshake messages are indistinguishable from
-pseudo random noise.
+pseudo random noise, except when using TCP connections.
+
+@code{PktLen} is used only with TCP connections. It is big-endian
+@emph{uint16} length of the whole packet (except @code{PktLen} itself).
@code{SERIAL} is message's serial number. Odds are reserved for
-client(→server) messages, evens for server(→client) messages.
+client(->server) messages, evens for server(->client) messages.
@code{ENCn} is XTEA block cipher algorithm used here as PRP (pseudo
-random permutation) to randomize, obfuscate @code{SERIAL}. Plaintext
+random permutation function) to obfuscate @code{SERIAL}. Plaintext
@code{SERIAL} state is kept in peers internal state, but encrypted
before transmission. XTEA is compact and fast enough. Salsa20 is PRF
-function and requires much more code to create PRP from it. 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).
+function and requires much more code to create PRP from it.
+
+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).
+
+@code{ENC} is Salsa20 stream cipher, with established session @code{KEY}
+and obfuscated @code{SERIAL} used as a nonce. First 256 bits of
+Salsa20's output is used as Poly1305 authentication key, next 256 bits
+are ignored. All remaining output is XORed with the data, encrypting it.
-Encrypted @code{SERIAL} is used as a nonce for @code{DATA} encryption:
-encryption key is different during each handshake, so (key, nonce) pair
-is always used only once. @code{ENC} is Salsa20 cipher, with established
-session @code{KEY} and encrypted @code{SERIAL} used as a nonce.
-@code{DATA_SIZE} is @emph{uint16} storing length of the @code{DATA}.
+@code{DATA_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{AUTH} is Poly1305 authentication function. First 256 bits of
-Salsa20 output are used as a one-time key for @code{AUTH}. Next 256 bits
-of Salsa20 are ignored. All remaining output is XORed with the data,
-encrypting it.
+Salsa20 output are used as a one-time key for @code{AUTH}.
To prevent replay attacks we must remember received @code{SERIAL}s and
if meet one, then drop it. Basically we could just store latest number
Announcements about updates and new releases can be found in @ref{Contacts}.
GoVPN is split into two pieces: client and server. Each of them work on
-top of UDP and TAP virtual network interfaces. GoVPN is just a
+top of UDP/TCP and TAP virtual network interfaces. GoVPN is just a
tunnelling of Ethernet frames, nothing less, nothing more. All you
IP-related network management is not touched by VPN at all. You can
automate it using up and down shell scripts.
+What network performance can user expect? For example single
+@emph{Intel i5-2450M 2.5 GHz} core on @emph{FreeBSD 10.2 amd64}
+with @emph{Go 1.5} gives 435 Mbps TCP (over UDP) throughput.
+
@menu
* EGD:: Entropy gathering daemon
* Identity::
* PAKE:: Password Authenticated Key Agreement
* Timeout::
+* Network transport::
+* Proxy::
* MTU:: Maximum Transmission Unit
* Stats::
* Noise::
@include identity.texi
@include pake.texi
@include timeout.texi
+@include netproto.texi
+@include proxy.texi
@include mtu.texi
@include stats.texi
@include noise.texi
+++ /dev/null
-github.com
-golang.org
+++ /dev/null
-GIT ?= git
-
-all: golang.org/x/crypto github.com/agl/ed25519 github.com/bigeagle/water
-
-github.com/bigeagle/water:
- mkdir -p github.com/bigeagle
- $(GIT) clone https://github.com/bigeagle/water.git github.com/bigeagle/water
- cd github.com/bigeagle/water && $(GIT) checkout --force 36aebfeb35da4f1f6a975726716c6fc563c5c495
-
-github.com/agl/ed25519:
- mkdir -p github.com/agl
- $(GIT) clone https://github.com/agl/ed25519.git github.com/agl/ed25519
- cd github.com/agl/ed25519 && $(GIT) checkout --force d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
-
-golang.org/x/crypto:
- mkdir -p golang.org/x
- $(GIT) clone https://go.googlesource.com/crypto golang.org/x/crypto
- cd golang.org/x/crypto && $(GIT) checkout --force 24ffb5feb3312a39054178a4b0a4554fc2201248
--- /dev/null
+Subproject commit d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
--- /dev/null
+Subproject commit 36aebfeb35da4f1f6a975726716c6fc563c5c495
--- /dev/null
+Subproject commit 81bf7719a6b7ce9b665598222362b50122dfc13b
import (
"flag"
+ "io"
"log"
"net"
"os"
var (
remoteAddr = flag.String("remote", "", "Remote server address")
+ proto = flag.String("proto", "udp", "Protocol to use: udp or tcp")
ifaceName = flag.String("iface", "tap0", "TAP network interface")
IDRaw = flag.String("id", "", "Client identification")
keyPath = flag.String("key", "", "Path to passphrase file")
upPath = flag.String("up", "", "Path to up-script")
downPath = flag.String("down", "", "Path to down-script")
stats = flag.String("stats", "", "Enable stats retrieving on host:port")
+ proxyAddr = flag.String("proxy", "", "Use HTTP proxy on host:port")
+ proxyAuth = flag.String("proxy-auth", "", "user:password Basic proxy auth")
mtu = flag.Int("mtu", 1452, "MTU for outgoing packets")
timeoutP = flag.Int("timeout", 60, "Timeout seconds")
noisy = flag.Bool("noise", false, "Enable noise appending")
}
govpn.PeersInitDummy(id, conf)
- bind, err := net.ResolveUDPAddr("udp", "0.0.0.0:0")
- if err != nil {
- log.Fatalln("Can not resolve address:", err)
- }
- conn, err := net.ListenUDP("udp", bind)
- if err != nil {
- log.Fatalln("Can not listen on UDP:", err)
- }
- remote, err := net.ResolveUDPAddr("udp", *remoteAddr)
- if err != nil {
- log.Fatalln("Can not resolve remote address:", err)
+ var conn io.Writer
+ var sink chan []byte
+ var ready chan struct{}
+ switch *proto {
+ case "udp":
+ conn, sink, ready = startUDP()
+ case "tcp":
+ if *proxyAddr != "" {
+ conn, sink, ready = proxyTCP()
+ } else {
+ conn, sink, ready = startTCP()
+ }
+ default:
+ log.Fatalln("Unknown protocol specified")
}
tap, ethSink, ethReady, _, err := govpn.TAPListen(
if err != nil {
log.Fatalln("Can not listen on TAP interface:", err)
}
- udpSink, udpBuf, udpReady := govpn.ConnListen(conn)
timeouts := 0
firstUpCall := true
var peer *govpn.Peer
var ethPkt []byte
- var udpPkt govpn.UDPPkt
- var udpPktData []byte
- knownPeers := govpn.KnownPeers(map[string]**govpn.Peer{remote.String(): &peer})
+ var pkt []byte
+ knownPeers := govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer})
log.Println(govpn.VersionGet())
+ log.Println("Connected to", *proto, *remoteAddr)
log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
if *stats != "" {
log.Println("Stats are going to listen on", *stats)
signal.Notify(termSignal, os.Interrupt, os.Kill)
log.Println("Starting handshake")
- handshake := govpn.HandshakeStart(conf, conn, remote)
+ handshake := govpn.HandshakeStart(*remoteAddr, conn, conf)
MainCycle:
for {
if peer != nil && (peer.BytesIn+peer.BytesOut) > govpn.MaxBytesPerKey {
peer.Zero()
peer = nil
- handshake = govpn.HandshakeStart(conf, conn, remote)
+ handshake = govpn.HandshakeStart(*remoteAddr, conn, conf)
log.Println("Rehandshaking")
}
select {
}
continue
}
- peer.EthProcess(ethPkt, conn, ethReady)
- case udpPkt = <-udpSink:
+ peer.EthProcess(ethPkt, ethReady)
+ case pkt = <-sink:
timeouts++
if timeouts >= timeout {
break MainCycle
}
- if udpPkt.Addr == nil {
- udpReady <- struct{}{}
+ if pkt == nil {
+ ready <- struct{}{}
continue
}
- udpPktData = udpBuf[:udpPkt.Size]
if peer == nil {
- if udpPkt.Addr.String() != remote.String() {
- udpReady <- struct{}{}
- log.Println("Unknown handshake message")
- continue
- }
- if govpn.IDsCache.Find(udpPktData) == nil {
+ if govpn.IDsCache.Find(pkt) == nil {
log.Println("Invalid identity in handshake packet")
- udpReady <- struct{}{}
+ ready <- struct{}{}
continue
}
- if p := handshake.Client(conn, udpPktData); p != nil {
+ if p := handshake.Client(pkt); p != nil {
log.Println("Handshake completed")
if firstUpCall {
go govpn.ScriptCall(*upPath, *ifaceName)
handshake.Zero()
handshake = nil
}
- udpReady <- struct{}{}
+ ready <- struct{}{}
continue
}
if peer == nil {
- udpReady <- struct{}{}
+ ready <- struct{}{}
continue
}
- if peer.UDPProcess(udpPktData, tap, udpReady) {
+ if peer.PktProcess(pkt, tap, ready) {
timeouts = 0
}
}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "bufio"
+ "encoding/base64"
+ "io"
+ "log"
+ "net"
+ "net/http"
+)
+
+func proxyTCP() (io.Writer, chan []byte, chan struct{}) {
+ proxyAddr, err := net.ResolveTCPAddr("tcp", *proxyAddr)
+ if err != nil {
+ log.Fatalln("Can not resolve proxy address:", err)
+ }
+ conn, err := net.DialTCP("tcp", nil, proxyAddr)
+ if err != nil {
+ log.Fatalln("Can not connect to proxy:", err)
+ }
+ req := "CONNECT " + *remoteAddr + " HTTP/1.1\n"
+ req += "Host: " + *remoteAddr + "\n"
+ if *proxyAuth != "" {
+ req += "Proxy-Authorization: Basic "
+ req += base64.StdEncoding.EncodeToString([]byte(*proxyAuth)) + "\n"
+ }
+ req += "\n"
+ conn.Write([]byte(req))
+ resp, err := http.ReadResponse(
+ bufio.NewReader(conn),
+ &http.Request{Method: "CONNECT"},
+ )
+ if err != nil || resp.StatusCode != http.StatusOK {
+ log.Fatalln("Unexpected response from proxy")
+ }
+ sink := make(chan []byte)
+ ready := make(chan struct{})
+ go handleTCP(conn, sink, ready)
+ go func() { ready <- struct{}{} }()
+ return TCPSender{conn}, sink, ready
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "encoding/binary"
+ "io"
+ "log"
+ "net"
+
+ "govpn"
+)
+
+// TCPSender prepends size prefix to each outgoing packet.
+type TCPSender struct {
+ conn *net.TCPConn
+}
+
+func (c TCPSender) Write(data []byte) (int, error) {
+ size := make([]byte, 2)
+ binary.BigEndian.PutUint16(size, uint16(len(data)))
+ return c.conn.Write(append(size, data...))
+}
+
+func startTCP() (io.Writer, chan []byte, chan struct{}) {
+ remote, err := net.ResolveTCPAddr("tcp", *remoteAddr)
+ if err != nil {
+ log.Fatalln("Can not resolve remote address:", err)
+ }
+ c, err := net.DialTCP("tcp", nil, remote)
+ conn := TCPSender{c}
+ if err != nil {
+ log.Fatalln("Can not connect TCP:", err)
+ }
+ sink := make(chan []byte)
+ ready := make(chan struct{})
+ go handleTCP(c, sink, ready)
+ go func() { ready <- struct{}{} }()
+ return conn, sink, ready
+}
+
+func handleTCP(conn *net.TCPConn, sink chan []byte, ready chan struct{}) {
+ var err error
+ var n int
+ var sizeNbuf int
+ sizeBuf := make([]byte, 2)
+ var sizeNeed uint16
+ var bufN uint16
+ buf := make([]byte, govpn.MTU)
+ for {
+ <-ready
+ if sizeNbuf != 2 {
+ n, err = conn.Read(sizeBuf[sizeNbuf:2])
+ if err != nil {
+ break
+ }
+ sizeNbuf += n
+ if sizeNbuf != 2 {
+ sink <- nil
+ continue
+ }
+ sizeNeed = binary.BigEndian.Uint16(sizeBuf)
+ if int(sizeNeed) > govpn.MTU-2 {
+ log.Println("Invalid TCP size, skipping")
+ sizeNbuf = 0
+ sink <- nil
+ continue
+ }
+ bufN = 0
+ }
+ ReadMore:
+ if sizeNeed != bufN {
+ n, err = conn.Read(buf[bufN:sizeNeed])
+ if err != nil {
+ break
+ }
+ bufN += uint16(n)
+ goto ReadMore
+ }
+ sizeNbuf = 0
+ sink <- buf[:sizeNeed]
+ }
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "io"
+ "log"
+ "net"
+ "time"
+
+ "govpn"
+)
+
+func startUDP() (io.Writer, chan []byte, chan struct{}) {
+ remote, err := net.ResolveUDPAddr("udp", *remoteAddr)
+ if err != nil {
+ log.Fatalln("Can not resolve remote address:", err)
+ }
+ c, err := net.DialUDP("udp", nil, remote)
+ conn := io.Writer(c)
+ if err != nil {
+ log.Fatalln("Can not listen on UDP:", err)
+ }
+ sink := make(chan []byte)
+ ready := make(chan struct{})
+ go func() {
+ buf := make([]byte, govpn.MTU)
+ var n int
+ var err error
+ for {
+ <-ready
+ c.SetReadDeadline(time.Now().Add(time.Second))
+ n, err = c.Read(buf)
+ if err != nil {
+ sink <- nil
+ continue
+ }
+ sink <- buf[:n]
+ }
+ }()
+ ready <- struct{}{}
+ return conn, sink, ready
+}
import (
"bytes"
"flag"
+ "io"
"log"
"net"
"os"
var (
bindAddr = flag.String("bind", "[::]:1194", "Bind to address")
+ proto = flag.String("proto", "udp", "Protocol to use: udp, tcp or all")
peersPath = flag.String("peers", "peers", "Path to peers keys directory")
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")
)
+type Pkt struct {
+ addr string
+ conn io.Writer
+ data []byte
+ ready chan struct{}
+}
+
type PeerReadyEvent struct {
peer *govpn.Peer
iface string
func main() {
flag.Parse()
timeout := time.Second * time.Duration(govpn.TimeoutDefault)
- var err error
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
+ log.Println(govpn.VersionGet())
govpn.MTU = *mtu
govpn.PeersInit(*peersPath)
govpn.EGDInit(*egdPath)
}
- bind, err := net.ResolveUDPAddr("udp", *bindAddr)
- if err != nil {
- log.Fatalln("Can not resolve bind address:", err)
+ sink := make(chan Pkt)
+ switch *proto {
+ case "udp":
+ startUDP(sink)
+ case "tcp":
+ startTCP(sink)
+ case "all":
+ startUDP(sink)
+ startTCP(sink)
+ default:
+ log.Fatalln("Unknown protocol specified")
}
- conn, err := net.ListenUDP("udp", bind)
- if err != nil {
- log.Fatalln("Can listen on UDP:", err)
- }
- udpSink, udpBuf, udpReady := govpn.ConnListen(conn)
termSignal := make(chan os.Signal, 1)
signal.Notify(termSignal, os.Interrupt, os.Kill)
hsHeartbeat := time.Tick(timeout)
go func() { <-hsHeartbeat }()
- var addr string
var state *govpn.Handshake
var peerState *PeerState
var peer *govpn.Peer
peerReadySink := make(chan PeerReadyEvent)
knownPeers := govpn.KnownPeers(make(map[string]**govpn.Peer))
var peerReady PeerReadyEvent
- var udpPkt govpn.UDPPkt
- var udpPktData []byte
+ var pkt Pkt
var ethEvent EthEvent
var peerId *govpn.PeerId
var peerConf *govpn.PeerConf
var handshakeProcessForce bool
ethSink := make(chan EthEvent)
- log.Println(govpn.VersionGet())
log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
if *stats != "" {
log.Println("Stats are going to listen on", *stats)
}
go govpn.StatsProcessor(statsPort, &knownPeers)
}
+ if *proxy != "" {
+ go proxyStart(sink)
+ }
log.Println("Server started")
MainCycle:
state.peer.Zero()
break
}
- addr = peerReady.peer.Addr.String()
state := NewPeerState(peerReady.peer, peerReady.iface)
if state == nil {
continue
}
- peers[addr] = state
- knownPeers[addr] = &peerReady.peer
- states[addr].Zero()
- delete(states, addr)
+ peers[peerReady.peer.Addr] = state
+ knownPeers[peerReady.peer.Addr] = &peerReady.peer
+ states[peerReady.peer.Addr].Zero()
+ delete(states, peerReady.peer.Addr)
log.Println("Registered interface", peerReady.iface, "with peer", peer)
go func(state *PeerState) {
for data := range state.sink {
}
}(state)
case ethEvent = <-ethSink:
- if s, exists := peers[ethEvent.peer.Addr.String()]; !exists || s.peer != ethEvent.peer {
+ if s, exists := peers[ethEvent.peer.Addr]; !exists || s.peer != ethEvent.peer {
continue
}
- ethEvent.peer.EthProcess(ethEvent.data, conn, ethEvent.ready)
- case udpPkt = <-udpSink:
- if udpPkt.Addr == nil {
- udpReady <- struct{}{}
+ ethEvent.peer.EthProcess(ethEvent.data, ethEvent.ready)
+ case pkt = <-sink:
+ if pkt.data == nil {
+ pkt.ready <- struct{}{}
continue
}
- udpPktData = udpBuf[:udpPkt.Size]
- addr = udpPkt.Addr.String()
handshakeProcessForce = false
HandshakeProcess:
- if _, exists = peers[addr]; handshakeProcessForce || !exists {
- peerId = govpn.IDsCache.Find(udpPktData)
+ if _, exists = peers[pkt.addr]; handshakeProcessForce || !exists {
+ peerId = govpn.IDsCache.Find(pkt.data)
if peerId == nil {
- log.Println("Unknown identity from", addr)
- udpReady <- struct{}{}
+ log.Println("Unknown identity from", pkt.addr)
+ pkt.ready <- struct{}{}
continue
}
peerConf = peerId.Conf()
if peerConf == nil {
log.Println("Can not get peer configuration", peerId.String())
- udpReady <- struct{}{}
+ pkt.ready <- struct{}{}
continue
}
- state, exists = states[addr]
+ state, exists = states[pkt.addr]
if !exists {
- state = govpn.HandshakeNew(udpPkt.Addr, peerConf)
- states[addr] = state
+ state = govpn.HandshakeNew(pkt.addr, pkt.conn, peerConf)
+ states[pkt.addr] = state
}
- peer = state.Server(conn, udpPktData)
+ peer = state.Server(pkt.data)
if peer != nil {
log.Println("Peer handshake finished", peer)
- if _, exists = peers[addr]; exists {
+ if _, exists = peers[pkt.addr]; exists {
go func() {
- peerReadySink <- PeerReadyEvent{peer, peers[addr].tap.Name}
+ peerReadySink <- PeerReadyEvent{
+ peer, peers[pkt.addr].tap.Name,
+ }
}()
} else {
go func() {
}
}
if !handshakeProcessForce {
- udpReady <- struct{}{}
+ pkt.ready <- struct{}{}
}
continue
}
- peerState, exists = peers[addr]
+ peerState, exists = peers[pkt.addr]
if !exists {
- udpReady <- struct{}{}
+ pkt.ready <- struct{}{}
continue
}
// If it fails during processing, then try to work with it
// as with handshake packet
- if !peerState.peer.UDPProcess(udpPktData, peerState.tap, udpReady) {
+ if !peerState.peer.PktProcess(pkt.data, peerState.tap, pkt.ready) {
handshakeProcessForce = true
goto HandshakeProcess
}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "log"
+ "net/http"
+)
+
+type proxyHandler struct {
+ sink chan Pkt
+}
+
+func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ conn, _, err := w.(http.Hijacker).Hijack()
+ if err != nil {
+ log.Println("Hijacking failed:", err.Error())
+ return
+ }
+ conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
+ ready := make(chan struct{}, 1)
+ go handleTCP(conn, p.sink, ready)
+ ready <- struct{}{}
+
+}
+
+func proxyStart(sink chan Pkt) {
+ log.Println("HTTP proxy listening on:", *proxy)
+ s := &http.Server{
+ Addr: *proxy,
+ Handler: proxyHandler{sink},
+ }
+ log.Println("HTTP proxy result:", s.ListenAndServe())
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "encoding/binary"
+ "log"
+ "net"
+
+ "govpn"
+)
+
+type TCPSender struct {
+ conn net.Conn
+}
+
+func (c TCPSender) Write(data []byte) (int, error) {
+ size := make([]byte, 2)
+ binary.BigEndian.PutUint16(size, uint16(len(data)))
+ return c.conn.Write(append(size, data...))
+}
+
+func startTCP(sink chan Pkt) {
+ bind, err := net.ResolveTCPAddr("tcp", *bindAddr)
+ if err != nil {
+ log.Fatalln("Can not resolve bind address:", err)
+ }
+ listener, err := net.ListenTCP("tcp", bind)
+ if err != nil {
+ log.Fatalln("Can not listen on TCP:", err)
+ }
+ log.Println("Listening on TCP", *bindAddr)
+ go func() {
+ for {
+ conn, _ := listener.AcceptTCP()
+ ready := make(chan struct{}, 1)
+ go handleTCP(conn, sink, ready)
+ ready <- struct{}{}
+ }
+ }()
+}
+
+func handleTCP(conn net.Conn, sink chan Pkt, ready chan struct{}) {
+ addr := conn.RemoteAddr().String()
+ var err error
+ var n int
+ var sizeNbuf int
+ sizeBuf := make([]byte, 2)
+ var sizeNeed uint16
+ var bufN uint16
+ buf := make([]byte, govpn.MTU)
+ for {
+ <-ready
+ if sizeNbuf != 2 {
+ n, err = conn.Read(sizeBuf[sizeNbuf:2])
+ if err != nil {
+ break
+ }
+ sizeNbuf += n
+ if sizeNbuf != 2 {
+ sink <- Pkt{ready: ready}
+ continue
+ }
+ sizeNeed = binary.BigEndian.Uint16(sizeBuf)
+ if int(sizeNeed) > govpn.MTU-2 {
+ log.Println("Invalid TCP size, skipping")
+ sizeNbuf = 0
+ sink <- Pkt{ready: ready}
+ continue
+ }
+ bufN = 0
+ }
+ ReadMore:
+ if sizeNeed != bufN {
+ n, err = conn.Read(buf[bufN:sizeNeed])
+ if err != nil {
+ break
+ }
+ bufN += uint16(n)
+ goto ReadMore
+ }
+ sizeNbuf = 0
+ sink <- Pkt{
+ addr,
+ TCPSender{conn},
+ buf[:sizeNeed],
+ ready,
+ }
+ }
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "log"
+ "net"
+ "time"
+
+ "govpn"
+)
+
+type UDPSender struct {
+ conn *net.UDPConn
+ addr *net.UDPAddr
+}
+
+func (c UDPSender) Write(data []byte) (int, error) {
+ return c.conn.WriteToUDP(data, c.addr)
+}
+
+func startUDP(sink chan Pkt) {
+ bind, err := net.ResolveUDPAddr("udp", *bindAddr)
+ ready := make(chan struct{})
+ if err != nil {
+ log.Fatalln("Can not resolve bind address:", err)
+ }
+ lconn, err := net.ListenUDP("udp", bind)
+ if err != nil {
+ log.Fatalln("Can not listen on UDP:", err)
+ }
+ log.Println("Listening on UDP", *bindAddr)
+ go func() {
+ buf := make([]byte, govpn.MTU)
+ var n int
+ var raddr *net.UDPAddr
+ var err error
+ for {
+ <-ready
+ lconn.SetReadDeadline(time.Now().Add(time.Second))
+ n, raddr, err = lconn.ReadFromUDP(buf)
+ if err != nil {
+ sink <- Pkt{ready: ready}
+ continue
+ }
+ sink <- Pkt{
+ raddr.String(),
+ UDPSender{lconn, raddr},
+ buf[:n],
+ ready,
+ }
+ }
+ }()
+ ready <- struct{}{}
+}
"crypto/rand"
"crypto/subtle"
"encoding/binary"
+ "io"
"log"
- "net"
"time"
"github.com/agl/ed25519"
)
type Handshake struct {
- addr *net.UDPAddr
+ addr string
+ conn io.Writer
LastPing time.Time
Conf *PeerConf
dsaPubH *[ed25519.PublicKeySize]byte
}
// Create new handshake state.
-func HandshakeNew(addr *net.UDPAddr, conf *PeerConf) *Handshake {
+func HandshakeNew(addr string, conn io.Writer, conf *PeerConf) *Handshake {
state := Handshake{
addr: addr,
+ conn: conn,
LastPing: time.Now(),
Conf: conf,
}
}
// Start handshake's procedure from the client. It is the entry point
-// for starting the handshake procedure. You have to specify outgoing
-// conn address, remote's addr address, our own peer configuration.
-// First handshake packet will be sent immediately.
-func HandshakeStart(conf *PeerConf, conn *net.UDPConn, addr *net.UDPAddr) *Handshake {
- state := HandshakeNew(addr, conf)
+// for starting the handshake procedure. // First handshake packet
+// will be sent immediately.
+func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake {
+ state := HandshakeNew(addr, conn, conf)
var dhPubRepr *[32]byte
state.dhPriv, dhPubRepr = dhKeypairGen()
salsa20.XORKeyStream(enc, dhPubRepr[:], state.rNonce[:], state.dsaPubH)
data := append(state.rNonce[:], enc...)
data = append(data, idTag(state.Conf.Id, state.rNonce[:])...)
- conn.WriteToUDP(data, addr)
+ state.conn.Write(data)
return state
}
// Process handshake message on the server side.
// This function is intended to be called on server's side.
-// Our outgoing conn connection and received data are required.
// If this is the final handshake message, then new Peer object
// will be created and used as a transport. If no mutually
// authenticated Peer is ready, then return nil.
-func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer {
+func (h *Handshake) Server(data []byte) *Peer {
// R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag
if len(data) == 48 && h.rNonce == nil {
// Generate DH keypair
salsa20.XORKeyStream(encRs, append(h.rServer[:], h.sServer[:]...), h.rNonce[:], h.key)
// Send that to client
- conn.WriteToUDP(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...), h.addr)
+ 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
// Send final answer to client
enc := make([]byte, RSize)
salsa20.XORKeyStream(enc, dec[RSize:RSize+RSize], h.rNonceNext(2), h.key)
- conn.WriteToUDP(append(enc, idTag(h.Conf.Id, enc)...), h.addr)
+ h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
// Switch peer
peer := newPeer(
h.addr,
+ h.conn,
h.Conf,
0,
keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize]))
// Process handshake message on the client side.
// This function is intended to be called on client's side.
-// Our outgoing conn connection, authentication
-// key and received data are required.
// If this is the final handshake message, then new Peer object
// will be created and used as a transport. If no mutually
// authenticated Peer is ready, then return nil.
-func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer {
+func (h *Handshake) Client(data []byte) *Peer {
switch len(data) {
case 80: // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag
if h.key != nil {
append(h.sClient[:], sign[:]...)...)...), h.rNonceNext(1), h.key)
// Send that to server
- conn.WriteToUDP(append(enc, idTag(h.Conf.Id, enc)...), h.addr)
+ h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
h.LastPing = time.Now()
case 16: // ENC(K, R+2, RC) + IDtag
if h.key == nil {
}
// Switch peer
- peer := newPeer(h.addr, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:]))
+ peer := newPeer(h.addr, h.conn, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:]))
h.LastPing = time.Now()
return peer
default:
import (
"encoding/binary"
"io"
- "net"
"time"
"golang.org/x/crypto/poly1305"
TimeoutHeartbeat = 4
)
-type UDPPkt struct {
- Addr *net.UDPAddr
- Size int
-}
-
type Peer struct {
- Addr *net.UDPAddr
+ Addr string
Id *PeerId
+ Conn io.Writer
// Traffic behaviour
NoiseEnable bool
}
func (p *Peer) String() string {
- return p.Id.String() + ":" + p.Addr.String()
+ return p.Id.String() + ":" + p.Addr
}
// Zero peer's memory state.
return tap, sink, sinkReady, sinkTerminate, nil
}
-// Create UDP listening goroutine.
-// This function takes already listening UDP socket and a buffer where
-// all UDP packet data will be saved, channel where information about
-// remote address and number of written bytes are stored, and a channel
-// used to tell that buffer is ready to be overwritten.
-func ConnListen(conn *net.UDPConn) (chan UDPPkt, []byte, chan struct{}) {
- buf := make([]byte, MTU)
- sink := make(chan UDPPkt)
- sinkReady := make(chan struct{})
- go func(conn *net.UDPConn) {
- var n int
- var addr *net.UDPAddr
- var err error
- for {
- <-sinkReady
- conn.SetReadDeadline(time.Now().Add(time.Second))
- n, addr, err = conn.ReadFromUDP(buf)
- if err != nil {
- // This is needed for ticking the timeouts counter outside
- sink <- UDPPkt{nil, 0}
- continue
- }
- sink <- UDPPkt{addr, n}
- }
- }(conn)
- sinkReady <- struct{}{}
- return sink, buf, sinkReady
-}
-
func newNonceCipher(key *[32]byte) *xtea.Cipher {
nonceKey := make([]byte, 16)
salsa20.XORKeyStream(
return time.Second / time.Duration(rate*(1<<10)/MTU)
}
-func newPeer(addr *net.UDPAddr, conf *PeerConf, nonce int, key *[SSize]byte) *Peer {
+func newPeer(addr string, conn io.Writer, conf *PeerConf, nonce int, key *[SSize]byte) *Peer {
now := time.Now()
timeout := conf.Timeout
cprCycle := cprCycleCalculate(conf.CPR)
}
peer := Peer{
Addr: addr,
+ Conn: conn,
Timeout: timeout,
Established: now,
LastPing: now,
}
// Process incoming UDP packet.
-// udpPkt is received data, related to the peer tap interface and
// ConnListen'es synchronization channel used to tell him that he is
// free to receive new packets. Authenticated and decrypted packets
// will be written to the interface immediately (except heartbeat ones).
-func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) bool {
- p.size = len(udpPkt)
+func (p *Peer) PktProcess(data []byte, tap io.Writer, ready chan struct{}) bool {
+ p.size = len(data)
copy(p.buf, Emptiness)
- copy(p.tag[:], udpPkt[p.size-poly1305.TagSize:])
- copy(p.buf[S20BS:], udpPkt[NonceSize:p.size-poly1305.TagSize])
+ copy(p.tag[:], data[p.size-poly1305.TagSize:])
+ copy(p.buf[S20BS:], data[NonceSize:p.size-poly1305.TagSize])
salsa20.XORKeyStream(
p.buf[:S20BS+p.size-poly1305.TagSize],
p.buf[:S20BS+p.size-poly1305.TagSize],
- udpPkt[:NonceSize],
+ data[:NonceSize],
p.Key,
)
copy(p.keyAuth[:], p.buf[:SSize])
- if !poly1305.Verify(p.tag, udpPkt[:p.size-poly1305.TagSize], p.keyAuth) {
+ if !poly1305.Verify(p.tag, data[:p.size-poly1305.TagSize], p.keyAuth) {
ready <- struct{}{}
p.FramesUnauth++
return false
// Check from the oldest bucket, as in most cases this will result
// in constant time check.
// If Bucket0 is filled, then it becomes Bucket1.
- p.NonceCipher.Decrypt(p.buf, udpPkt[:NonceSize])
+ p.NonceCipher.Decrypt(p.buf, data[:NonceSize])
ready <- struct{}{}
p.nonceRecv, _ = binary.Uvarint(p.buf[:NonceSize])
if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound {
return true
}
-type WriteToUDPer interface {
- WriteToUDP([]byte, *net.UDPAddr) (int, error)
-}
-
// Process incoming Ethernet packet.
-// ethPkt is received data, conn is our outgoing connection.
// ready channel is TAPListen's synchronization channel used to tell him
// that he is free to receive new packets. Encrypted and authenticated
// packets will be sent to remote Peer side immediately.
-func (p *Peer) EthProcess(ethPkt []byte, conn WriteToUDPer, ready chan struct{}) {
+func (p *Peer) EthProcess(data []byte, ready chan struct{}) {
p.now = time.Now()
- p.size = len(ethPkt)
+ p.size = len(data)
// If this heartbeat is necessary
if p.size == 0 && !p.LastSent.Add(p.Timeout).Before(p.now) {
return
}
copy(p.buf, Emptiness)
if p.size > 0 {
- copy(p.buf[S20BS+PktSizeSize:], ethPkt)
+ copy(p.buf[S20BS+PktSizeSize:], data)
ready <- struct{}{}
binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(p.size))
p.BytesPayloadOut += int64(p.size)
}
}
p.LastSent = p.now
- conn.WriteToUDP(append(p.frame, p.tag[:]...), p.Addr)
+ p.Conn.Write(append(p.frame, p.tag[:]...))
}
package govpn
import (
- "net"
"testing"
"time"
)
peer *Peer
plaintext []byte
ready chan struct{}
- dummy = &Dummy{}
ciphertext []byte
- addr *net.UDPAddr
peerId *PeerId
conf *PeerConf
)
+type Dummy struct{
+ dst *[]byte
+}
+
+func (d Dummy) Write(b []byte) (int, error) {
+ if d.dst != nil {
+ *d.dst = b
+ }
+ return len(b), nil
+}
+
func init() {
MTU = 1500
- addr, _ = net.ResolveUDPAddr("udp", "[::1]:1")
peerId, _ = IDDecode("ffffffffffffffffffffffffffffffff")
conf = &PeerConf{
Id: peerId,
NoiseEnable: false,
CPR: 0,
}
- peer = newPeer(addr, conf, 128, new([SSize]byte))
+ peer = newPeer("foo", Dummy{&ciphertext}, conf, 128, new([SSize]byte))
plaintext = make([]byte, 789)
ready = make(chan struct{})
go func() {
}()
}
-type Dummy struct{}
-
-func (d *Dummy) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
- ciphertext = b
- return len(b), nil
-}
-
-func (d *Dummy) Write(p []byte) (n int, err error) {
- return len(p), nil
-}
-
func BenchmarkEnc(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
peer.NonceOur = 128
- peer.EthProcess(plaintext, dummy, ready)
+ peer.EthProcess(plaintext, ready)
}
}
func BenchmarkDec(b *testing.B) {
- peer.EthProcess(plaintext, dummy, ready)
- peer = newPeer(addr, conf, 128, new([SSize]byte))
+ peer.EthProcess(plaintext, ready)
+ peer = newPeer("foo", Dummy{nil}, conf, 128, new([SSize]byte))
b.ResetTimer()
for i := 0; i < b.N; i++ {
peer.nonceBucket0 = make(map[uint64]struct{}, 1)
peer.nonceBucket1 = make(map[uint64]struct{}, 1)
- if !peer.UDPProcess(ciphertext, dummy, ready) {
+ if !peer.PktProcess(ciphertext, Dummy{nil}, ready) {
b.Fail()
}
}
[ -n "$release" ]
git clone . $tmp/govpn-$release
+for repo in src/github.com/bigeagle/water src/github.com/agl/ed25519 src/golang.org/x/crypto; do
+ git clone $repo $tmp/govpn-$release/$repo
+done
+cd $tmp/govpn-$release
+git checkout $release
+git submodule update --init
+
cat > $tmp/includes <<EOF
-github.com
golang.org/x/crypto/AUTHORS
golang.org/x/crypto/CONTRIBUTORS
golang.org/x/crypto/LICENSE
golang.org/x/crypto/salsa20
golang.org/x/crypto/xtea
EOF
-tar cfCI - src $tmp/includes | tar xfC - $tmp/govpn-$release/src
-rm $tmp/includes
-
-cd $tmp/govpn-$release
-git checkout $release
+tar cfCI - src $tmp/includes | tar xfC - $tmp
+rm -fr src/golang.org
+mv $tmp/golang.org src/
+rm -fr $tmp/golang.org $tmp/includes
cat > doc/download.texi <<EOF
@node Prepared tarballs
cd ..
tar cvf govpn-"$release".tar govpn-"$release"
xz -9 govpn-"$release".tar
-gpg --detach-sign --sign --local-user FFE2F4A1 govpn-"$release".tar.xz
+gpg --detach-sign --sign --local-user F2F59045FFE2F4A1 govpn-"$release".tar.xz
mv $tmp/govpn-"$release".tar.xz $tmp/govpn-"$release".tar.xz.sig $cur/doc/govpn.html/download