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.
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/
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
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
передачи данных с 128-бит @ref{Developer, порогом безопасности} и
современной криптографией.
@item
+Опциональный @ref{Encless, нешифрованный режим}: функции шифрования не
+применяются для исходящего трафика, вместо них кодирование всё-равно
+обеспечивающее конфиденциальность. Юрисдикции и суды не могут вас
+вынудить выдать ключи шифрования или привлечь за использование
+шифрования.
+@item
Цензуроустойчивые сообщения транспорта и рукопожатия: неотличимые от
шума с опциональным скрытием размеров сообщений.
@item
статистики} о подключённых клиентах в режиме реального времени в
@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
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.
@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
@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}.
@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.
@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/}
@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
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.
@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
@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}
@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
--- /dev/null
+@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.
@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
"Alice": {
"up": "/path/to/up.sh",
+ "iface": "or TAP interface name",
"verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
}
@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
@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}:
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
-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}:
@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 \
вид трафика от другого, то при цензуре ваше единственный вариант это
заблокировать все его виды.
+@item Когда я должен использовать @ref{Encless, нешифрованный режим}?
+Если вы работаете под юрисдикциями где суды могут привлечь вас к
+ответственности за использование шифрования или могут вынудить вас
+как-либо выдать ваши ключи шифрования (хотя сессионные ключи шифрования
+генерируются каждую сессию). В большинстве случаев, эти суды не могут
+требовать аутентификационные ключи или ключи для ЭЦП. @strong{Не
+позволяйте} названию режима вас смутить: он всё-равно обеспечивает
+конфиденциальность и аутентичность передаваемых данных! Но имейте в
+виду, что этот режим требователен к ресурсам и трафику и пока работает
+только в TCP режиме.
+
@item Когда я должен использовать @ref{Noise, noise} опцию?
В большинстве случаев она вам не нужна без включённого
@ref{CPR, постоянного по скорости трафика} (CPR). Без CPR и шума, в
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,
--- /dev/null
+@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
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
@include about.texi
+@center @strong{@ref{Tarballs, Download it}}.
+
@menu
* About (russian): О демоне.
* Frequently Asked Questions: FAQ.
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}:
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.
@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))
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
@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
@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).
@node Network
-@section Network transport
+@subsection Network transport
You can use either UDP or TCP underlying network transport protocols.
@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
@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.
@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
@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
@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
@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)
"Alice": {
"up": "/path/to/up.sh",
+ "iface": "or TAP interface name",
"verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
}
[...]
@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
@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
@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
+--< ENCRYPT(KEY, NONCE, PAYLOAD)
^ ^
| |
- | +--< SIZE || DATA [|| NOISE]
+ | +--< DATA || PAD [|| ZEROS]
|
+--< PRP(PRP_KEY, SERIAL)
@end verbatim
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
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.
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
@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
--- /dev/null
+/*
+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)
+ }
+}
--- /dev/null
+/*
+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
+}
/*
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
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")
timeout int
firstUpCall bool = true
knownPeers govpn.KnownPeers
- idsCache govpn.CipherCache
+ idsCache *govpn.CipherCache
)
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)
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)
/*
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
/*
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
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
break HandshakeCycle
default:
}
- if prev == govpn.MTU {
+ if prev == len(buf) {
log.Println("Timeouted waiting for the packet")
timeouted <- struct{}{}
break HandshakeCycle
break TransportCycle
default:
}
- if prev == govpn.MTU {
+ if prev == len(buf) {
log.Println("Timeouted waiting for the packet")
timeouted <- struct{}{}
break TransportCycle
/*
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
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
/*
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
}
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
}
/*
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
var (
confs map[govpn.PeerId]*govpn.PeerConf
- idsCache govpn.CipherCache
+ idsCache *govpn.CipherCache
)
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
/*
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
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")
)
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
log.Println(govpn.VersionGet())
- govpn.MTU = *mtu
confInit()
knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer))
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)
/*
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
/*
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
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
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))
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
prev = 0
var i int
for {
- if prev == govpn.MTU {
+ if prev == len(buf) {
break
}
conn.SetReadDeadline(time.Now().Add(conf.Timeout))
/*
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
}
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
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]
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
/*
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
package main
import (
- "crypto/rand"
"crypto/subtle"
"flag"
"fmt"
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)
--- /dev/null
+/*
+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
+}
--- /dev/null
+/*
+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)
+ }
+}
/*
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
"os"
"os/exec"
"runtime"
- "time"
)
const (
TimeoutDefault = 60
+ MTUMax = 9000
+ MTUDefault = 1500 + 14
)
var (
- MTU int
Version string
)
}
// Zero each byte.
-func sliceZero(data []byte) {
+func SliceZero(data []byte) {
for i := 0; i < len(data); i++ {
data[i] = 0
}
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)
-}
/*
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
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
/*
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
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)
}
--- /dev/null
+/*
+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
+}
--- /dev/null
+/*
+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)
+ }
+}
-/*
-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
/*
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
package govpn
import (
- "crypto/rand"
"crypto/subtle"
"encoding/binary"
"io"
// 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[:])
}
}
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)
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)
// 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
// 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
// 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
--- /dev/null
+/*
+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
+}
/*
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
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{}{}
// 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
}
/*
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
package govpn
import (
+ "bytes"
"encoding/binary"
"io"
+ "log"
"sync"
"sync/atomic"
"time"
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 {
NoiseEnable bool
CPR int
CPRCycle time.Duration `json:"-"`
+ Encless bool
+ MTU int
// Cryptography related
Key *[SSize]byte `json:"-"`
bufR []byte
tagR *[TagSize]byte
keyAuthR *[SSize]byte
- pktSizeR uint16
+ pktSizeR int
// Transmitter
BusyT sync.Mutex `json:"-"`
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()
}
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
timeout = timeout / TimeoutHeartbeat
}
+ bufSize := S20BS + 2*conf.MTU
+ if conf.Encless {
+ bufSize += EnclessEnlargeSize
+ noiseEnable = true
+ }
peer := Peer{
Addr: addr,
Id: conf.Id,
NoiseEnable: noiseEnable,
CPR: conf.CPR,
CPRCycle: cprCycle,
+ Encless: conf.Encless,
+ MTU: conf.MTU,
Key: key,
NonceCipher: newNonceCipher(key),
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),
// 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)
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) {
}
p.LastSent = p.now
- p.Conn.Write(append(p.tagT[:], p.frameT...))
+ p.Conn.Write(out)
p.BusyT.Unlock()
}
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.
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
}
+/*
+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 {
}
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)
+}
/*
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
/*
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
import (
"io"
-
- "golang.org/x/crypto/poly1305"
-)
-
-const (
- EtherSize = 14
)
type TAP struct {
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
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() {
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
}
/*
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
/*
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
/*
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
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 {
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
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<----------------
----------------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:
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
несбалансированный протокол согласования ключей с двусторонней
аутентификацией сторон (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<----------------
----------------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/
Исходный код и его подпись для этой версии находится здесь:
Пожалуйста все вопросы касающиеся использования GoVPN, отчёты об ошибках
и патчи отправляйте в govpn-devel почтовую рассылку:
-https://lists.cypherpunks.ru/mailman/listinfo/govpn-devel/
+https://lists.cypherpunks.ru/pipermail/govpn-devel/
EOF
#!/bin/sh -e
+PATH=$PATH:.
+
[ -n "$1" ] || {
cat <<EOF
Example script for creating new user peer for GoVPN.
"$username": {
"up": "/path/to/up.sh",
+ "iface": "or TAP interface name",
"verifier": "$verifierS"
}