@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}
+@item DH elliptic-curve point encoding for public keys
+@url{http://elligator.cr.yp.to/, Elligator}
@item Key derivation function for verifier generation
@url{https://en.wikipedia.org/wiki/PBKDF2, PBKDF2} based on
@url{https://en.wikipedia.org/wiki/SHA-2, SHA-512}
+@node Prepared tarballs
+@section Prepared tarballs
+
You can obtain releases source code prepared tarballs from the links below:
@multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
@tab @url{download/govpn-2.4.tar.xz, link} @url{download/govpn-2.4.tar.xz.sig, sign}
@tab @code{df45225bac2384c5eed73c5cdb05dc3581495e08d365317beb03a2487d46b98c}
+@item 3.0 @tab 53 KiB
+@tab @url{download/govpn-3.0.tar.xz, link} @url{download/govpn-3.0.tar.xz.sig, sign}
+@tab @code{12579c5c3cccfe73c66b5893335bc70c42d7b13b8e94c7751ec65d421eaff9a5}
+
@end multitable
-Also you can try it's @ref{Contacts, .onion} version.
+Also you can try its @ref{Contacts, .onion} version.
Sourceforge.net also provides mirror for the files above:
@url{http://sourceforge.net/projects/govpn/files/}.
-
-You can obtain it's development source code by cloning
-Git repository: @code{git clone https://github.com/stargrave/govpn.git}.
-Pay attention that it does not contain compiled documentation and is not
-recommended for porters because of that.
This manual is for GoVPN -- simple secure free software virtual private
network (VPN) daemon, written entirely on Go programming language.
-Copyright @copyright{} 2014-2015 Sergey Matveev @email{stargrave@@stargrave.org}
+Copyright @copyright{} 2014-2015 @email{stargrave@@stargrave.org, Sergey Matveev}
@quotation
Permission is granted to copy, distribute and/or modify this document
@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.
+symmetric encryption. @code{El()} is Elligator point encoding algorithm.
@end enumerate
@strong{Interaction stage}:
@enumerate
@item
-@verb{|R + enc(H(DSAPub), R, CDHPub) + IDtag -> Server|} [48 bytes]
+@verb{|R + enc(H(DSAPub), R, El(CDHPub)) + IDtag -> Server|} [48 bytes]
@item
@itemize @bullet
@item Server remembers client address.
-@item Decrypts @code{CDHPub}.
+@item Decrypts @code{El(CDHPub)}.
+@item Inverts @code{El()} encoding and gets @code{CDHPub}.
@item Generates DH keypair: @code{SDHPriv}/@code{SDHPub}.
@item Computes common shared key @code{K = H(DH(SDHPriv, CDHPub))}.
@item Generates 64-bit random number @code{RS}.
@end itemize
@item
-@verb{|enc(H(DSAPub), R+1, SDHPub) + enc(K, R, RS + SS) + IDtag -> Client|} [80 bytes]
+@verb{|enc(H(DSAPub), R+1, El(SDHPub)) + enc(K, R, RS + SS) + IDtag -> Client|} [80 bytes]
@item
@itemize @bullet
-@item Client decrypts @code{SDHPub}.
+@item Client decrypts @code{El(SDHPub)}.
+@item Inverts @code{El()} encoding and gets @code{SDHPub}.
@item Computes @code{K}.
@item Decrypts @code{RS} and @code{SS}.
@item Remembers @code{SS}.
@item Server decrypts @code{RS}, @code{RC}, @code{SC},
@code{Sign(DSAPriv, K)}.
- @item Compares @code{RS} with it's own one sent before. Server
+ @item Compares @code{RS} with its own one sent before. Server
decrypts @code{RS}, @code{RC}, @code{SC} with key @code{K}, compares
- @code{RS} with it's own one sent before.
+ @code{RS} with its own one sent before.
@item Verifies @code{K} signature with verifier @code{DSAPub}.
@item
@itemize @bullet
@item Client decrypts @code{RC}
-@item Compares with it's own one sent before.
+@item Compares with its own one sent before.
@item Computes final session encryption key as server did.
@end itemize
Client -> Client : R=rand(64bit)
Client -> Client : CDHPriv=rand(256bit)
-Client -> Server : R, enc(H(DSAPub), R, CDHPub)
+Client -> Server : R, enc(H(DSAPub), R, El(CDHPub))
Server -> Server : SDHPriv=rand(256bit)
Server -> Server : K=H(DH(SDHPriv, CDHPub))
Server -> Server : RS=rand(64bit)
Server -> Server : SS=rand(256bit)
-Server -> Client : enc(H(DSAPub), R+1, SDHPub); enc(K, R, RS+SS)
+Server -> Client : enc(H(DSAPub), R+1, El(SDHPub)); enc(K, R, RS+SS)
Client -> Client : K=H(DH(CDHPriv, SDHPub))
Client -> Client : RC=rand(64bit); SC=rand(256bit)
Client -> Server : enc(K, R+1, RS+RC+SC+Sign(DSAPriv, K))
@item @code{github.com/bigeagle/water} @tab GNU/Linux @tab BSD 3-Clause
@end multitable
-@include download.texi
-
-You @strong{have to} verify downloaded archives integrity and check
-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 provided with the tarball.
-
-For the very first time you have 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.
-
-For example you can get tarball, set proper @code{$GOPATH} and run
+You can get tarball, set proper @code{$GOPATH} and run
@code{make} (that will install all necessary libraries and build
@emph{govpn-client}, @emph{govpn-server}, @emph{govpn-verifier} binaries:
% gmake -C govpn/src/govpn all
@end example
-@include pubkey.texi
+@menu
+* Prepared tarballs::
+* Development source code::
+* Tarballs integrity check::
+@end menu
+
+@include download.texi
+@include sources.texi
+@include integrity.texi
--- /dev/null
+@node Tarballs integrity check
+@section Tarballs integrity check
+
+You @strong{have to} verify downloaded archives integrity and check
+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 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
exchange, EKE, Diffie-Hellman, DH, DH-EKE, Augmented EKE, A-EKE,
security, encrypted key exchange, 128-bit margin, DPI, censorship,
resistance, free software, GPLv3+, reviewability, easy, simple,
-Curve25519, Ed25519, SHA-512, Salsa20, Poly1305, AEAD, XTEA, PBKDF2,
+Curve25519, Ed25519, Elligator, SHA-512, Salsa20, Poly1305, AEAD, XTEA, PBKDF2,
PRP, signatures, asymmetric cryptography, zero-knowledge password proof,
PAKE, password, passphrase, password authenticated key exchange, perfect
forward secrecy, PFS, MAC, nonce, verifier, rehandshake, heartbeat,
@unnumbered News
@table @strong
+@item Release 3.1
+@itemize @bullet
+@item
+Diffie-Hellman public keys are encoded with Elligator algorithm when
+sending over the wire, making them indistinguishable from the random
+strings, preventing detection of successful decryption try when guessing
+passwords (that are used to create DSA public keys). But this will
+consume twice entropy for DH key generation in average.
+@end itemize
+
@item Release 3.0
@itemize @bullet
@item
@item
Ability to hide underlying packets lengths by appending noise, junk
-data during transmission. Each packet can be fill up-ed to it's
+data during transmission. Each packet can be fill up-ed to its
maximal MTU size.
@item
hash function,
@url{https://en.wikipedia.org/wiki/Encrypted_key_exchange,
Diffie-Hellman Augmented Encrypted Key Exchange}
-(DH-A-EKE) powered by @url{http://cr.yp.to/ecdh.html, Curve25519} and
-@url{http://ed25519.cr.yp.to/, Ed25519} signatures.
+(DH-A-EKE) powered by @url{http://cr.yp.to/ecdh.html, Curve25519},
+@url{http://ed25519.cr.yp.to/, Ed25519} signatures and
+@url{http://elligator.cr.yp.to/, Elligator} curve-point encoding.
Strong
@url{https://en.wikipedia.org/wiki/Zero-knowledge_password_proof, zero-knowledge}
mutual authentication with key exchange stage is invulnerable
The very important precaution is the @strong{cryptographically good}
pseudo random number generator. GoVPN uses native operating system PRNG
-as entropy source. You have no way to check it's quality in closed
+as entropy source. You have no way to check its quality in closed
source code operating systems, so it is recommended not to use them if
you really needs security. Moreover it is possible that those OS leaks
information about possible PRNG states. And at least Apple OS X and
Microsoft Windows are already known to have weak CSPRNGs.
-GoVPN could use it's own PRNG implementation like
+GoVPN could use its own PRNG implementation like
@url{https://www.schneier.com/fortuna.html, Fortuna}, but it is
much easier to use the right OS, to use free software.
related to that key could be decrypted.
We use password (passphrase) authentication, so overall security fully
-depends on it's strength. So you should use long, high-entropy
+depends on its strength. So you should use long, high-entropy
passphrases. Also remember to keep passphrase on temporary file as
described in @ref{Verifier}.
--- /dev/null
+@node Development source code
+@section Development source code
+
+Development source contains the latest version of the code. It may be
+buggy. Also it does not contain compiled documentation and is not
+recommended for porters because of that.
+
+You can obtain it by cloning Git repository:
+@code{git clone https://github.com/stargrave/govpn.git}.
+
+Also there is mirror of dependent libraries for safety if their native
+repositories will be unavailable (they are seldom updated):
+
+@multitable @columnfractions .50 .50
+@headitem Software/library @tab Mirror
+@item @code{govpn} @tab @url{git://git.cypherpunks.ru/govpn.git}
+@item @code{golang.org/x/crypto/poly1305} @tab @url{git://git.cypherpunks.ru/crypto.git}
+@item @code{golang.org/x/crypto/salsa20} @tab @url{git://git.cypherpunks.ru/crypto.git}
+@item @code{golang.org/x/crypto/xtea} @tab @url{git://git.cypherpunks.ru/crypto.git}
+@item @code{golang.org/x/crypto/pbkdf2} @tab @url{git://git.cypherpunks.ru/crypto.git}
+@item @code{github.com/agl/ed25519} @tab @url{git://git.cypherpunks.ru/ed25519.git}
+@end multitable
@itemize @bullet
@item
@url{https://www.schneier.com/books/applied_cryptography/, Applied Cryptography}
-@copyright{} 1996 Bruce Schneier
+@copyright{} 1996 Bruce Schneier.
@item
@url{http://tnlandforms.us/cns05/speke.pdf, Strong Password-Only Authenticated Key Exchange}
-@copyright{} 1996 David P. Jablon
+@copyright{} 1996 David P. Jablon.
@item
@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}
+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.
@end itemize
Verifier is a derivative of the password. It is resistant to
dictionary attacks and can not be used for authentication (only
-it's verifying).
+its verifying).
@verbatim
SOURCE = PBKDF2(SALT=PeerId, PASSWORD, 1<<16, SHA512)
"time"
"github.com/agl/ed25519"
+ "github.com/agl/ed25519/extra25519"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/salsa20"
"golang.org/x/crypto/salsa20/salsa"
return nonce
}
-func dhPrivGen() *[32]byte {
- dh := new([32]byte)
- if _, err := rand.Read(dh[:]); err != nil {
- panic("Can not read random for DH private key")
+func dhKeypairGen() (*[32]byte, *[32]byte) {
+ priv := new([32]byte)
+ pub := new([32]byte)
+ repr := new([32]byte)
+ reprFound := false
+ for !reprFound {
+ if _, err := rand.Read(priv[:]); err != nil {
+ panic("Can not read random for DH private key")
+ }
+ reprFound = extra25519.ScalarBaseMult(pub, repr, priv)
}
- return dh
+ return priv, repr
}
func dhKeyGen(priv, pub *[32]byte) *[32]byte {
func HandshakeStart(conf *PeerConf, conn *net.UDPConn, addr *net.UDPAddr) *Handshake {
state := HandshakeNew(addr, conf)
- state.dhPriv = dhPrivGen()
- dhPub := new([32]byte)
- curve25519.ScalarBaseMult(dhPub, state.dhPriv)
+ var dhPubRepr *[32]byte
+ state.dhPriv, dhPubRepr = dhKeypairGen()
state.rNonce = new([RSize]byte)
if _, err := rand.Read(state.rNonce[:]); err != nil {
panic("Can not read random for handshake nonce")
}
enc := make([]byte, 32)
- salsa20.XORKeyStream(enc, dhPub[:], state.rNonce[:], state.dsaPubH)
+ salsa20.XORKeyStream(enc, dhPubRepr[:], state.rNonce[:], state.dsaPubH)
data := append(state.rNonce[:], enc...)
data = append(data, idTag(state.Conf.Id, state.rNonce[:])...)
if _, err := conn.WriteTo(data, addr); err != nil {
// will be created and used as a transport. If no mutually
// authenticated Peer is ready, then return nil.
func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer {
- // R + ENC(H(DSAPub), R, CDHPub) + IDtag
+ // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag
if len(data) == 48 && h.rNonce == nil {
- // Generate private DH key
- h.dhPriv = dhPrivGen()
- dhPub := new([32]byte)
- curve25519.ScalarBaseMult(dhPub, h.dhPriv)
+ // Generate DH keypair
+ var dhPubRepr *[32]byte
+ h.dhPriv, dhPubRepr = dhKeypairGen()
h.rNonce = new([RSize]byte)
copy(h.rNonce[:], data[:RSize])
// Decrypt remote public key and compute shared key
- dec := new([32]byte)
- salsa20.XORKeyStream(dec[:], data[RSize:RSize+32], h.rNonce[:], h.dsaPubH)
- h.key = dhKeyGen(h.dhPriv, dec)
+ cDHRepr := new([32]byte)
+ salsa20.XORKeyStream(
+ cDHRepr[:],
+ data[RSize:RSize+32],
+ h.rNonce[:],
+ h.dsaPubH,
+ )
+ cDH := new([32]byte)
+ extra25519.RepresentativeToPublicKey(cDH, cDHRepr)
+ h.key = dhKeyGen(h.dhPriv, cDH)
encPub := make([]byte, 32)
- salsa20.XORKeyStream(encPub, dhPub[:], h.rNonceNext(1), h.dsaPubH)
+ salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH)
// Generate R* and encrypt them
h.rServer = new([RSize]byte)
// authenticated Peer is ready, then return nil.
func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer {
switch len(data) {
- case 80: // ENC(H(DSAPub), R+1, SDHPub) + ENC(K, R, RS + SS) + IDtag
+ case 80: // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag
if h.key != nil {
log.Println("Invalid handshake stage from", h.addr)
return nil
}
// Decrypt remote public key and compute shared key
- dec := new([32]byte)
- salsa20.XORKeyStream(dec[:], data[:32], h.rNonceNext(1), h.dsaPubH)
- h.key = dhKeyGen(h.dhPriv, dec)
+ sDHRepr := new([32]byte)
+ salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH)
+ sDH := new([32]byte)
+ extra25519.RepresentativeToPublicKey(sDH, sDHRepr)
+ h.key = dhKeyGen(h.dhPriv, sDH)
// Decrypt Rs
decRs := make([]byte, RSize+SSize)
import (
"encoding/binary"
"io"
- "log"
"net"
"time"
frame []byte
nonce []byte
pktSize uint64
+ size int
+ now time.Time
// Statistics
BytesIn int64
// free to receive new packets. Authenticated and decrypted packets
// will be written to the interface immediately (except heartbeat ones).
func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) bool {
- size := len(udpPkt)
+ p.size = len(udpPkt)
copy(p.buf, Emptiness)
- copy(p.tag[:], udpPkt[size-poly1305.TagSize:])
- copy(p.buf[S20BS:], udpPkt[NonceSize:size-poly1305.TagSize])
+ copy(p.tag[:], udpPkt[p.size-poly1305.TagSize:])
+ copy(p.buf[S20BS:], udpPkt[NonceSize:p.size-poly1305.TagSize])
salsa20.XORKeyStream(
- p.buf[:S20BS+size-poly1305.TagSize],
- p.buf[:S20BS+size-poly1305.TagSize],
+ p.buf[:S20BS+p.size-poly1305.TagSize],
+ p.buf[:S20BS+p.size-poly1305.TagSize],
udpPkt[:NonceSize],
p.Key,
)
copy(p.keyAuth[:], p.buf[:SSize])
- if !poly1305.Verify(p.tag, udpPkt[:size-poly1305.TagSize], p.keyAuth) {
+ if !poly1305.Verify(p.tag, udpPkt[:p.size-poly1305.TagSize], p.keyAuth) {
ready <- struct{}{}
p.FramesUnauth++
return false
}
ready <- struct{}{}
p.FramesIn++
- p.BytesIn += int64(size)
+ p.BytesIn += int64(p.size)
p.LastPing = time.Now()
p.NonceRecv = p.nonceRecv
p.pktSize, _ = binary.Uvarint(p.buf[S20BS : S20BS+PktSizeSize])
return true
}
-type WriteToer interface {
- WriteTo([]byte, net.Addr) (int, error)
+type WriteToUDPer interface {
+ WriteToUDP([]byte, *net.UDPAddr) (int, error)
}
// Process incoming Ethernet packet.
// ready channel is TAPListen's synchronization channel used to tell him
// that he is free to receive new packets. Encrypted and authenticated
// packets will be sent to remote Peer side immediately.
-func (p *Peer) EthProcess(ethPkt []byte, conn WriteToer, ready chan struct{}) {
- now := time.Now()
- size := len(ethPkt)
+func (p *Peer) EthProcess(ethPkt []byte, conn WriteToUDPer, ready chan struct{}) {
+ p.now = time.Now()
+ p.size = len(ethPkt)
// If this heartbeat is necessary
- if size == 0 && !p.LastSent.Add(p.Timeout).Before(now) {
+ if p.size == 0 && !p.LastSent.Add(p.Timeout).Before(p.now) {
return
}
copy(p.buf, Emptiness)
- if size > 0 {
+ if p.size > 0 {
copy(p.buf[S20BS+PktSizeSize:], ethPkt)
ready <- struct{}{}
- binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(size))
- p.BytesPayloadOut += int64(size)
+ binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(p.size))
+ p.BytesPayloadOut += int64(p.size)
} else {
p.HeartbeatSent++
}
if p.NoiseEnable {
p.frame = p.buf[S20BS-NonceSize : S20BS+MTU-NonceSize-poly1305.TagSize]
} else {
- p.frame = p.buf[S20BS-NonceSize : S20BS+PktSizeSize+size]
+ p.frame = p.buf[S20BS-NonceSize : S20BS+PktSizeSize+p.size]
}
poly1305.Sum(p.tag, p.frame, p.keyAuth)
if p.CPRCycle != time.Duration(0) {
p.willSentCycle = p.LastSent.Add(p.CPRCycle)
- if p.willSentCycle.After(now) {
- time.Sleep(p.willSentCycle.Sub(now))
- now = p.willSentCycle
+ if p.willSentCycle.After(p.now) {
+ time.Sleep(p.willSentCycle.Sub(p.now))
+ p.now = p.willSentCycle
}
}
- p.LastSent = now
- if _, err := conn.WriteTo(append(p.frame, p.tag[:]...), p.Addr); err != nil {
- log.Println("Error sending UDP", err)
- }
+ p.LastSent = p.now
+ conn.WriteToUDP(append(p.frame, p.tag[:]...), p.Addr)
}
type Dummy struct{}
-func (d *Dummy) WriteTo(b []byte, addr net.Addr) (int, error) {
+func (d *Dummy) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
ciphertext = b
return len(b), nil
}
git checkout $release
rm -fr .git
find . -name .gitignore -delete
-echo > doc/download.texi
+cat > doc/download.texi <<EOF
+@node Prepared tarballs
+@section Prepared tarballs
+You can obtain releases source code prepared tarballs on
+@url{http://www.cypherpunks.ru/govpn/}.
+EOF
rm utils/makedist.sh
make -C doc
cd $tmp