[submodule "src/github.com/dchest/blake2b"]
path = src/github.com/dchest/blake2b
url = https://github.com/dchest/blake2b.git
+[submodule "src/github.com/go-yaml/yaml"]
+ path = src/github.com/go-yaml/yaml
+ url = https://github.com/go-yaml/yaml.git
+.PHONY: doc
+
LDFLAGS = -X govpn.Version=$(VERSION)
PREFIX ?= /usr/local
BINDIR = $(DESTDIR)$(PREFIX)/bin
govpn.html: *.texi handshake.utxt
rm -f govpn.html/*.html
- $(MAKEINFO) --html -o govpn.html govpn.texi
+ $(MAKEINFO) --html --css-include=style.css -o govpn.html govpn.texi
статистики} о подключённых клиентах в режиме реального времени в
@url{http://json.org/, JSON} формате.
@item
+Сервер конфигурируется используя @url{http://yaml.org/, YAML} файл.
+@item
Написан на языке @url{https://golang.org/, Go} с простым кодом,
ориентированным на лёгкость чтения и анализа.
@item
@ref{Stats, statistics} information about known connected peers in
@url{http://json.org/, JSON} format.
@item
+Server is configured through the @url{http://yaml.org/, YAML} file.
+@item
Written on @url{https://golang.org/, Go} programming language with
simple code that can be read and reviewed.
@item
@multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
@headitem Version @tab Size @tab Tarball @tab SHA256 checksum
+@item 5.0 @tab 237 KiB
+@tab @url{download/govpn-5.0.tar.xz, link} @url{download/govpn-5.0.tar.xz.sig, sign}
+@tab @code{cc186a3b800279b6f5a7c86d61b250c24cf97235f6c3e1bb05a6cb60251085c6}
+
@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}
Enter passphrase:
Your client verifier is: $argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg
-Place the following JSON configuration entry on the server's side:
+Place the following YAML configuration entry on the server's side:
- "Alice": {
- "up": "/path/to/up.sh",
- "iface": "or TAP interface name",
- "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
- }
+ Alice:
+ up: /path/to/up.sh
+ iface: or TAP interface name
+ verifier: $argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10
Verifier was generated with:
govpn-verifier -key /tmp/passphrase
@end verbatim
-@strong{Prepare the server}. Add this entry to @code{peers.json}
+@strong{Prepare the server}. Add this entry to @code{peers.yaml}
configuration file:
@verbatim
-{
- "Alice": {
- "iface": "tap10",
- "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
- }
-}
+Alice:
+ iface: tap10
+ verifier: $argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10
@end verbatim
@strong{Prepare network on GNU/Linux IPv4 server}:
a key. It is used to transmit identity and to mark packet as handshake
message.
-If @ref{Noise} is enabled, then junk data is inserted before
-@code{IDtag} to fill up packet to MTU's size.
+If @ref{Noise, noise} is enabled, then data is padded to fill up packet
+to MTU's size.
@strong{Preparation stage}:
@enumerate
@item
Client knows only his identity and passphrase written somewhere in the
-human. Server knows his identity and
+human readable form. Server knows his identity and
@ref{Verifier structure, verifier}: @code{DSAPub}.
@item
Client computes verifier which produces @code{DSAPriv} and
-@code{DSAPub}. @code{H()} is @emph{HSalsa20} hash function.
+@code{DSAPub}. @code{H()} is @emph{BLAKE2b-256} hash function.
@item
Client generates DH keypair: @code{CDHPub} and @code{CDHPriv}.
Also it generates random 64-bit @code{R} that is used as a nonce for
-symmetric encryption. @code{El()} is Elligator point encoding algorithm.
+symmetric encryption. @code{El()} is Elligator point encoding (and vice
+versa) algorithm.
@end enumerate
@strong{Interaction stage}:
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.
-Possibly you also need to install TUN/TAP interface utilities (depending
-on your operating system): @code{uml-utilities} package in most
-GNU/Linux distributions.
+@emph{Make} (BSD and GNU ones are fine) is recommended for convenient
+building. @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:
@multitable @columnfractions .40 .20 .40
@headitem Library @tab Platform @tab Licence
-@item @code{golang.org/x/crypto} @tab All @tab BSD 3-Clause
@item @code{github.com/agl/ed25519} @tab All @tab BSD 3-Clause
+@item @code{github.com/bigeagle/water} @tab GNU/Linux @tab BSD 3-Clause
@item @code{github.com/dchest/blake2b} @tab All @tab CC0 1.0
+@item @code{github.com/go-yaml/yaml} @tab All @tab LGPLv3 and MIT
@item @code{github.com/magical/argon2} @tab All @tab BSD 2-Clause
-@item @code{github.com/bigeagle/water} @tab GNU/Linux @tab BSD 3-Clause
+@item @code{golang.org/x/crypto} @tab All @tab BSD 3-Clause
@end multitable
Get @ref{Tarballs, the tarball}, check its
their signature to be sure that you have got trusted, untampered
software. For integrity and authentication of downloaded binaries
@url{https://www.gnupg.org/, The GNU Privacy Guard} is used. You must
-download signature (.sig) provided with the tarball.
+download signature (@code{.sig}) provided with the tarball.
For the very first time you need to import signing public keys. They
are provided below, but be sure that you are reading them from the
trusted source. Alternatively check this page from
@ref{Contacts, other sources} and look for the mailing list announcements.
-@include pubkey.texi
+@verbatiminclude pubkey.txt
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.
+@ref{Noise, noise}, @ref{Encless, encryptionless mode} or @ref{CPR} are
+enabled, then all outgoing packets are filled up to that MTU value.
Default MTU equals to 1514 bytes (1500 bytes of Ethernet payload, 14
bytes of Ethernet header).
@table @strong
+@item Release 5.1
+@itemize
+@item Server is configured using @url{http://yaml.org/, YAML} file. It
+is very convenient to have comments and templates, comparing to JSON.
+@item Incompatible with previous versions replacement of @emph{HSalsa20}
+with @emph{BLAKE2b} in handshake code.
+@end itemize
+
@item Release 5.0
@itemize
@item New optional @ref{Encless, encryptionless mode} of operation.
-@verbatim
-pub rsa2048/FFE2F4A1 2015-03-10
-uid [ultimate] Sergey Matveev (GoVPN release signing key) <stargrave@stargrave.org>
-sub rsa2048/8A6C750A 2015-03-10
+pub rsa2048/0xF2F59045FFE2F4A1 2015-03-10
+uid Sergey Matveev (GoVPN release signing key) <stargrave@stargrave.org>
+sub rsa2048/0x3128EE3F8A6C750A 2015-03-10
-----BEGIN PGP PUBLIC KEY BLOCK-----
E0itJPXMaQL+juUfiNM0i2R1O8nJo14=
=LJzj
-----END PGP PUBLIC KEY BLOCK-----
-@end verbatim
Address (@code{host:port} format) we must bind to.
@item -conf
-Path to JSON file with the configuration.
+Path to YAML file with the configuration.
@item -proxy
Start trivial HTTP @ref{Proxy} server on specified @emph{host:port}.
@end table
-Configuration file is JSON file with following example structure:
+Configuration file is YAML file with following example structure:
@verbatim
-{
- "stargrave": { <-- Peer human readable name
- "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
- },
- [...]
-}
+stargrave: { <-- Peer human readable name
+ 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: No <-- OPTIONAL noise enabler
+ cpr: 64 <-- OPTIONAL constant packet rate, KiB/sec
+ encless: No <-- OPTIONAL Encryptionless mode
+ verifier: $argon2d... <-- verifier received from client
+[...]
@end verbatim
At least one of either @code{iface} or @code{up} must be specified. If
[...]
Your client verifier is: $argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg
-Place the following JSON configuration entry on the server's side:
+Place the following YAML configuration entry on the server's side:
- "Alice": {
- "up": "/path/to/up.sh",
- "iface": "or TAP interface name",
- "verifier": "$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10"
- }
-[...]
+ Alice:
+ up: /path/to/up.sh
+ iface: or TAP interface name
+ verifier: $argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg$KCNIqfS4DGsBTtVytamAzcISgrlEWvNxan1UfBrFu10
+@end verbatim
+
+Example configuration file:
+@verbatim
+stargrave:
+ iface: tap0
+ verifier: $argon2d$m=4096,t=128,p=1$VMirzcshcHuG2V4jhUsEjw$X5fC07L8k61h3S1Oro/rC76+m0oGDTA9Bq+aWJ1uOgY
+slow:
+ iface: tap1
+ encless: Yes
+ mtu: 9000
+ cpr: 384
+ verifier: $argon2d$m=4096,t=128,p=1$YbIA5garDqCOhtI/2EZVNg$gOo5vcEGynmpeepNscwclicfZsWxzgYFRLbgG21EZ1U
@end verbatim
@item @code{github.com/agl/ed25519} @tab @url{git://git.cypherpunks.ru/ed25519.git}
@item @code{github.com/bigeagle/water} @tab @url{git://git.cypherpunks.ru/water.git}
@item @code{github.com/dchest/blake2b} @tab @url{git://git.cypherpunks.ru/blake2b.git}
+@item @code{github.com/go-yaml/yaml} @tab @url{git://git.cypherpunks.ru/yaml.git}
@item @code{github.com/magical/argon2} @tab @url{git://git.cypherpunks.ru/argon2.git}
@item @code{golang.org/x/crypto} @tab @url{git://git.cypherpunks.ru/crypto.git}
@end multitable
--- /dev/null
+body {
+ margin: auto;
+ max-width: 800px;
+}
@end verbatim
@code{SERIAL} is message's serial number. Odds are reserved for
-client(->server) messages, evens for server(->client) messages.
+client (to server) messages, evens for server (to client) messages.
@code{PRP} is XTEA block cipher algorithm used here as PRP (pseudo
random permutation function) to obfuscate @code{SERIAL}. Plaintext
@code{SERIAL} state is kept in peers internal state, but encrypted
before transmission.
-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).
+XTEA's encryption key @code{PRP_KEY} is the first 128-bit of Salsa20's
+output with established common key and zero nonce (message nonces start
+from 1).
@verbatim
PRP_KEY = 128bit(ENCRYPT(KEY, 0))
encrypting it.
@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.
+may follow, to fill up packet to conceal payload 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}.
--- /dev/null
+Subproject commit f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
package main
import (
- "encoding/json"
+ "errors"
"io/ioutil"
"log"
"time"
+ "github.com/go-yaml/yaml"
+
"govpn"
)
idsCache *govpn.CipherCache
)
-func confRead() map[govpn.PeerId]*govpn.PeerConf {
+func confRead() (*map[govpn.PeerId]*govpn.PeerConf, error) {
data, err := ioutil.ReadFile(*confPath)
if err != nil {
- log.Fatalln("Unable to read configuration:", err)
+ return nil, err
}
confsRaw := new(map[string]govpn.PeerConf)
- err = json.Unmarshal(data, confsRaw)
+ err = yaml.Unmarshal(data, confsRaw)
if err != nil {
- log.Fatalln("Unable to parse configuration:", err)
+ return nil, err
}
confs := make(map[govpn.PeerId]*govpn.PeerConf, len(*confsRaw))
for name, pc := range *confsRaw {
verifier, err := govpn.VerifierFromString(pc.VerifierRaw)
if err != nil {
- log.Fatalln("Unable to decode the key:", err.Error(), pc.VerifierRaw)
+ return nil, errors.New("Unable to decode verifier: " + err.Error())
}
if pc.Encless {
pc.Noise = true
conf.Timeout = time.Second * time.Duration(pc.TimeoutInt)
confs[*verifier.Id] = &conf
}
- return confs
+ return &confs, nil
}
-func confRefresh() {
- confs = confRead()
+func confRefresh() error {
+ newConfs, err := confRead()
+ if err != nil {
+ log.Println("Unable to parse peers configuration:", err)
+ return err
+ }
+ confs = *newConfs
ids := make([]govpn.PeerId, 0, len(confs))
for peerId, _ := range confs {
ids = append(ids, peerId)
}
idsCache.Update(ids)
+ return nil
}
func confInit() {
idsCache = govpn.NewCipherCache(nil)
- confRefresh()
+ if err := confRefresh(); err != nil {
+ log.Fatalln(err)
+ }
go func() {
for {
time.Sleep(RefreshRate)
var (
bindAddr = flag.String("bind", "[::]:1194", "Bind to address")
proto = flag.String("proto", "udp", "Protocol to use: udp, tcp or all")
- confPath = flag.String("conf", "peers.json", "Path to configuration JSON")
+ confPath = flag.String("conf", "peers.yaml", "Path to configuration YAML")
stats = flag.String("stats", "", "Enable stats retrieving on host:port")
proxy = flag.String("proxy", "", "Enable HTTP proxy on host:port")
egdPath = flag.String("egd", "", "Optional path to EGD socket")
const (
TimeoutDefault = 60
- MTUMax = 9000
- MTUDefault = 1500 + 14
+ EtherSize = 14
+ MTUMax = 9000 + EtherSize
+ MTUDefault = 1500 + EtherSize
)
var (
)
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"`
+ Id *PeerId `yaml:"-"`
+ Name string `yaml:"name"`
+ Iface string `yaml:"iface"`
+ MTU int `yaml:"mtu"`
+ Up string `yaml:"up"`
+ Down string `yaml:"down"`
+ TimeoutInt int `yaml:"timeout"`
+ Timeout time.Duration `yaml:"-"`
+ Noise bool `yaml:"noise"`
+ CPR int `yaml:"cpr"`
+ Encless bool `yaml:"encless"`
+ VerifierRaw string `yaml:"verifier"`
// This is passphrase verifier
- Verifier *Verifier
+ Verifier *Verifier `yaml:"-"`
// This field exists only on client's side
- DSAPriv *[ed25519.PrivateKeySize]byte `json:"-"`
+ DSAPriv *[ed25519.PrivateKeySize]byte `yaml:"-"`
}
"github.com/agl/ed25519"
"github.com/agl/ed25519/extra25519"
+ "github.com/dchest/blake2b"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/salsa20"
- "golang.org/x/crypto/salsa20/salsa"
"golang.org/x/crypto/xtea"
)
return k
}
-// Apply HSalsa20 function for data. Used to hash public keys.
-func HApply(data *[32]byte) {
- salsa.HSalsa20(data, new([16]byte), data, &salsa.Sigma)
-}
-
// Zero handshake's memory state
func (h *Handshake) Zero() {
if h.rNonce != nil {
func dhKeyGen(priv, pub *[32]byte) *[32]byte {
key := new([32]byte)
curve25519.ScalarMult(key, priv, pub)
- HApply(key)
- return key
+ hashed := blake2b.Sum256(key[:])
+ return &hashed
}
// Create new handshake state.
}
state.dsaPubH = new([ed25519.PublicKeySize]byte)
copy(state.dsaPubH[:], state.Conf.Verifier.Pub[:])
- HApply(state.dsaPubH)
+ hashed := blake2b.Sum256(state.dsaPubH[:])
+ state.dsaPubH = &hashed
return &state
}
func TestHandshakeSymmetric(t *testing.T) {
// initial values are taken from peer_test.go's init()
- v := VerifierNew(DefaultM, DefaultT, DefaultP, &testPeerId)
+ v := VerifierNew(1<<10, 1<<4, 1, &testPeerId)
testConf.Verifier = v
testConf.DSAPriv = v.PasswordApply("does not matter")
hsS := NewHandshake("server", Dummy{&testCt}, testConf)
func TestHandshakeNoiseSymmetric(t *testing.T) {
// initial values are taken from peer_test.go's init()
- v := VerifierNew(DefaultM, DefaultT, DefaultP, &testPeerId)
+ v := VerifierNew(1<<10, 1<<4, 1, &testPeerId)
testConf.Verifier = v
testConf.DSAPriv = v.PasswordApply("does not matter")
testConf.Noise = true
}
func TestHandshakeEnclessSymmetric(t *testing.T) {
// initial values are taken from peer_test.go's init()
- v := VerifierNew(DefaultM, DefaultT, DefaultP, &testPeerId)
+ v := VerifierNew(1<<10, 1<<4, 1, &testPeerId)
testConf.Verifier = v
testConf.DSAPriv = v.PasswordApply("does not matter")
testConf.Encless = true
[ -n "$release" ]
git clone . $tmp/govpn-$release
-for repo in src/github.com/bigeagle/water src/github.com/agl/ed25519 src/github.com/magical/argon2 src/github.com/dchest/blake2b src/golang.org/x/crypto; do
+repos="
+ src/github.com/bigeagle/water
+ src/github.com/agl/ed25519
+ src/github.com/magical/argon2
+ src/github.com/dchest/blake2b
+ src/golang.org/x/crypto
+ src/github.com/go-yaml/yaml
+"
+for repo in $repos; do
git clone $repo $tmp/govpn-$release/$repo
done
cd $tmp/govpn-$release
cat <<EOF
Example script for creating new user peer for GoVPN.
It asks for passphrase, generates verifier and shows you example
-JSON entry for server configuration.
+YAML entry for server configuration.
Usage: $0 <username>
EOF
cat <<EOF
Your client verifier is: $verifierC
-Place the following JSON configuration entry on the server's side:
+Place the following YAML configuration entry on the server's side:
- "$username": {
- "up": "/path/to/up.sh",
- "iface": "or TAP interface name",
- "verifier": "$verifierS"
- }
+ $username:
+ up: /path/to/up.sh
+ iface: or TAP interface name
+ verifier: $verifierS
Verifier was generated with:
$(dirname $0)/storekey.sh /tmp/passphrase
govpn-verifier -key /tmp/passphrase
-
-Create up.sh script that will output on the first line TAP interface
-name that must be used for the peer. For example:
-
- % umask 077
- % ed /path/to/up.sh
- a
- #!/bin/sh
- echo tap0
- .
- wq
- 20
- % chmod +x /path/to/up.sh
EOF