Theoreticall it is more faster and secure.
@table @asis
@item Data encryption
- @url{http://cr.yp.to/snuffle.html, Salsa20}.
+ @url{https://cr.yp.to/chacha.html, ChaCha20}.
@item Message authentication
@url{https://cr.yp.to/mac.html, Poly1305}.
@item Nonce and identity obfuscation
@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 BLAKE2b-256 based
+ ChaCha20 with BLAKE2b-256 based
@url{http://crypto.stanford.edu/~dabo/abstracts/saep.html, SAEP+}
checksums) data with 128-bits of feeded random.
@item Packet overhead
authentication).
GoVPN provides special encryptionless mode of operation. In this mode it
-replaces Salsa20 function used for confidentiality with rather
+replaces ChaCha20 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
@node Новости
@section Новости
+@node Релиз 7.0
+@subsection Релиз 7.0
+@itemize
+@item (X)Salsa20 заменён на ChaCha20. Теоретически он должен быть
+быстрее и более безопасным.
+@end itemize
+
@node Релиз 6.0
@subsection Релиз 6.0
@itemize
See also this page @ref{Новости, on russian}.
+@node Release 7.0
+@section Release 7.0
+@itemize
+@item (X)Salsa20 is replaced with ChaCha20. Theoretically it should be
+faster and more secure.
+@end itemize
+
@node Release 6.0
@section Release 6.0
@itemize
@section Transport protocol
@verbatim
- NONCE = 64bit(MAC(MAC_KEY, SERIAL))
+ NONCE = 64bit(ZEROS) || 64bit(MAC(MAC_KEY, SERIAL))
PAYLOAD = DATA || PAD [|| ZEROS]
CIPHERTEXT = ENCRYPT(KEY, NONCE, PAYLOAD)
TAG = AUTH(AUTH_KEY, CIPHERTEXT || NONCE)
client (to server) messages, evens for server (to client) messages.
@code{MAC} is BLAKE2b-MAC used to obfuscate @code{SERIAL}. MAC's key
-@code{MAC_KEY} is the first 256-bit of Salsa20's output with established
+@code{MAC_KEY} is the first 256-bit of ChaCha20's output with established
common key and zero nonce (message nonces start from 1).
@verbatim
MAC_KEY = 256bit(ENCRYPT(KEY, 0))
@end verbatim
-@code{ENCRYPT} is Salsa20 stream cipher, with established session
+@code{ENCRYPT} is ChaCha20 stream cipher, with established session
@code{KEY} and obfuscated @code{SERIAL} used as a nonce. 512 bit of
-Salsa20's output is ignored and only remaining is XORed with ther data,
+ChaCha20's output is ignored and only remaining is XORed with ther data,
encrypting it.
@code{DATA} is padded using ISO/IEC 7816-4 format (@code{PAD} (0x80
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}.
+ChaCha20's output are used as a one-time key for @code{AUTH}.
@verbatim
AUTH_KEY = 256bit(ENCRYPT(KEY, NONCE))
--- /dev/null
+golang.org/x/crypto/chacha20poly1305/internal/chacha20
\ No newline at end of file
// package PKG:
//
// PKG = P1 || P2
-// P1 = Salsa20(key=r, nonce=0x00, 0x00) XOR (M || BLAKE2b(r || M))
+// P1 = ChaCha20(key=r, nonce=0x00, 0x00) XOR (M || BLAKE2b(r || M))
// P2 = BLAKE2b(P1) XOR r
package aont
"crypto/subtle"
"errors"
+ "chacha20"
"golang.org/x/crypto/blake2b"
- "golang.org/x/crypto/salsa20"
)
const (
)
var (
- dummyNonce []byte = make([]byte, 8)
+ dummyNonce *[16]byte = new([16]byte)
)
// Encode the data, produce AONT package. Data size will be larger than
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)
+ chachaKey := new([32]byte)
+ copy(chachaKey[:], r[:])
+ chacha20.XORKeyStream(out, out, dummyNonce, chachaKey)
h.Reset()
h.Write(out[:len(in)+32])
for i, b := range h.Sum(nil)[:RSize] {
return nil, err
}
h.Write(in[:len(in)-RSize])
- salsaKey := new([32]byte)
+ chachaKey := new([32]byte)
for i, b := range h.Sum(nil)[:RSize] {
- salsaKey[i] = b ^ in[len(in)-RSize+i]
+ chachaKey[i] = b ^ in[len(in)-RSize+i]
}
h.Reset()
- h.Write(salsaKey[:RSize])
+ h.Write(chachaKey[:RSize])
out := make([]byte, len(in)-RSize)
- salsa20.XORKeyStream(out, in[:len(in)-RSize], dummyNonce, salsaKey)
+ chacha20.XORKeyStream(out, in[:len(in)-RSize], dummyNonce, chachaKey)
h.Write(out[:len(out)-HSize])
if subtle.ConstantTimeCompare(h.Sum(nil), out[len(out)-HSize:]) != 1 {
return nil, errors.New("Invalid checksum")
// 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
+// Poly1305 uses 256-bit one-time key. We generate it using ChaCha20 for
// for the whole byte at once (16 MACs).
//
-// MACKey1, MACKey2, ... = XSalsa20(authKey, nonce, 0x00...)
-// nonce = prefix || 0x00... || big endian byte number
+// MACKey1, MACKey2, ... = ChaCha20(authKey, nonce, 0x00...)
+// nonce = prefix || big endian byte number
package cnw
import (
"encoding/binary"
"errors"
+ "chacha20"
"golang.org/x/crypto/poly1305"
- "golang.org/x/crypto/salsa20"
)
const (
func Chaff(authKey *[32]byte, noncePrfx, in []byte) []byte {
out := make([]byte, len(in)*EnlargeFactor)
keys := make([]byte, 8*64)
- nonce := make([]byte, 24)
+ nonce := new([16]byte)
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)
+ binary.BigEndian.PutUint64(nonce[8:], uint64(n))
+ chacha20.XORKeyStream(keys, keys, nonce, authKey)
for i = 0; i < 8; i++ {
v = (b >> uint8(i)) & 1
copy(macKey[:], keys[64*i:64*i+32])
}
out := make([]byte, len(in)/EnlargeFactor)
keys := make([]byte, 8*64)
- nonce := make([]byte, 24)
+ nonce := new([16]byte)
copy(nonce[:8], noncePrfx)
var i int
var v byte
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)
+ binary.BigEndian.PutUint64(nonce[8:], uint64(n))
+ chacha20.XORKeyStream(keys, keys, nonce, authKey)
v = 0
for i = 0; i < 8; i++ {
copy(macKey[:], keys[64*i:64*i+32])
// 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) {
+func EnclessEncode(authKey *[32]byte, nonce *[16]byte, in []byte) ([]byte, error) {
r := new([aont.RSize]byte)
var err error
if _, err = io.ReadFull(Rand, r[:]); err != nil {
return nil, err
}
out := append(
- cnw.Chaff(authKey, nonce, aonted[:aont.RSize]),
+ cnw.Chaff(authKey, nonce[8:], aonted[:aont.RSize]),
aonted[aont.RSize:]...,
)
SliceZero(aonted[:aont.RSize])
}
// Decode EnclessEncode-ed data.
-func EnclessDecode(authKey *[32]byte, nonce, in []byte) ([]byte, error) {
+func EnclessDecode(authKey *[32]byte, nonce *[16]byte, in []byte) ([]byte, error) {
var err error
winnowed, err := cnw.Winnow(
- authKey, nonce, in[:aont.RSize*cnw.EnlargeFactor],
+ authKey, nonce[8:], in[:aont.RSize*cnw.EnlargeFactor],
)
if err != nil {
return nil, err
}
func TestEnclessSymmetric(t *testing.T) {
- nonce := make([]byte, 8)
+ nonce := new([16]byte)
f := func(pktNum uint64, in []byte) bool {
- binary.BigEndian.PutUint64(nonce, pktNum)
+ binary.BigEndian.PutUint64(nonce[8:], pktNum)
encoded, err := EnclessEncode(testKey, nonce, in)
if err != nil {
return false
}
func BenchmarkEnclessEncode(b *testing.B) {
- nonce := make([]byte, 8)
+ nonce := new([16]byte)
data := make([]byte, 128)
- io.ReadFull(Rand, nonce)
+ io.ReadFull(Rand, nonce[8:])
io.ReadFull(Rand, data)
b.ResetTimer()
for i := 0; i < b.N; i++ {
}
func BenchmarkEnclessDecode(b *testing.B) {
- nonce := make([]byte, 8)
+ nonce := new([16]byte)
data := make([]byte, 128)
- io.ReadFull(Rand, nonce)
+ io.ReadFull(Rand, nonce[8:])
io.ReadFull(Rand, data)
encoded, _ := EnclessEncode(testKey, nonce, data)
b.ResetTimer()
"log"
"time"
+ "chacha20"
"github.com/agl/ed25519"
"github.com/agl/ed25519/extra25519"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/curve25519"
- "golang.org/x/crypto/salsa20"
)
const (
Conf *PeerConf
dsaPubH *[ed25519.PublicKeySize]byte
key *[32]byte
- rNonce *[RSize]byte
+ rNonce *[16]byte
dhPriv *[32]byte // own private DH key
rServer *[RSize]byte // random string for authentication
rClient *[RSize]byte
}
}
-func (h *Handshake) rNonceNext(count uint64) []byte {
- nonce := make([]byte, RSize)
- nonceCurrent, _ := binary.Uvarint(h.rNonce[:])
- binary.PutUvarint(nonce, nonceCurrent+count)
+func (h *Handshake) rNonceNext(count uint64) *[16]byte {
+ nonce := new([16]byte)
+ nonceCurrent, _ := binary.Uvarint(h.rNonce[8:])
+ binary.PutUvarint(nonce[8:], nonceCurrent+count)
return nonce
}
var dhPubRepr *[32]byte
state.dhPriv, dhPubRepr = dhKeypairGen()
- state.rNonce = new([RSize]byte)
- if _, err := io.ReadFull(Rand, state.rNonce[:]); err != nil {
+ state.rNonce = new([16]byte)
+ if _, err := io.ReadFull(Rand, state.rNonce[8:]); err != nil {
log.Fatalln("Error reading random for nonce:", err)
}
var enc []byte
copy(enc, dhPubRepr[:])
if conf.Encless {
var err error
- enc, err = EnclessEncode(state.dsaPubH, state.rNonce[:], enc)
+ enc, err = EnclessEncode(state.dsaPubH, state.rNonce, enc)
if err != err {
panic(err)
}
} else {
- salsa20.XORKeyStream(enc, enc, state.rNonce[:], state.dsaPubH)
+ chacha20.XORKeyStream(enc, enc, state.rNonce, state.dsaPubH)
}
- data := append(state.rNonce[:], enc...)
- data = append(data, idTag(state.Conf.Id, state.Conf.TimeSync, state.rNonce[:])...)
+ data := append(state.rNonce[8:], enc...)
+ data = append(data, idTag(state.Conf.Id, state.Conf.TimeSync, state.rNonce[8:])...)
state.conn.Write(data)
return state
}
// R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag
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])
+ h.rNonce = new([16]byte)
+ copy(h.rNonce[8:], data[:RSize])
// Decrypt remote public key
cDHRepr := new([32]byte)
if h.Conf.Encless {
out, err := EnclessDecode(
h.dsaPubH,
- h.rNonce[:],
+ h.rNonce,
data[RSize:len(data)-8],
)
if err != nil {
}
copy(cDHRepr[:], out)
} else {
- salsa20.XORKeyStream(cDHRepr[:], data[RSize:RSize+32], h.rNonce[:], h.dsaPubH)
+ chacha20.XORKeyStream(cDHRepr[:], data[RSize:RSize+32], h.rNonce, h.dsaPubH)
}
// Generate DH keypair
}
} else {
encPub = make([]byte, 32)
- salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH)
+ chacha20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH)
}
// Generate R* and encrypt them
}
copy(encRs, append(h.rServer[:], h.sServer[:]...))
if h.Conf.Encless {
- encRs, err = EnclessEncode(h.key, h.rNonce[:], encRs)
+ encRs, err = EnclessEncode(h.key, h.rNonce, encRs)
if err != nil {
panic(err)
}
} else {
- salsa20.XORKeyStream(encRs, encRs, h.rNonce[:], h.key)
+ chacha20.XORKeyStream(encRs, encRs, h.rNonce, h.key)
}
// Send that to client
dec = dec[:RSize+RSize+SSize+ed25519.SignatureSize]
} else {
dec = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
- salsa20.XORKeyStream(
+ chacha20.XORKeyStream(
dec,
data[:RSize+RSize+SSize+ed25519.SignatureSize],
h.rNonceNext(1),
panic(err)
}
} else {
- salsa20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key)
+ chacha20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key)
}
h.conn.Write(append(enc, idTag(h.Conf.Id, h.Conf.TimeSync, enc)...))
}
copy(sDHRepr[:], tmp[:32])
} else {
- salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH)
+ chacha20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH)
}
// Compute shared key
h.rServer = new([RSize]byte)
h.sServer = new([SSize]byte)
if h.Conf.Encless {
- tmp, err = EnclessDecode(
- h.key,
- h.rNonce[:],
- data[len(data)/2:len(data)-8],
- )
+ tmp, err = EnclessDecode(h.key, h.rNonce, data[len(data)/2:len(data)-8])
if err != nil {
log.Println("Unable to decode packet from", h.addr, err)
return nil
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)
+ chacha20.XORKeyStream(decRs, data[SSize:SSize+RSize+SSize], h.rNonce, h.key)
copy(h.rServer[:], decRs[:RSize])
copy(h.sServer[:], decRs[RSize:])
}
panic(err)
}
} else {
- salsa20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key)
+ chacha20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key)
}
// Send that to server
dec = dec[:RSize]
} else {
dec = make([]byte, RSize)
- salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key)
+ chacha20.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)
"sync/atomic"
"time"
+ "chacha20"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/poly1305"
- "golang.org/x/crypto/salsa20"
)
const (
NonceSize = 8
NonceBucketSize = 256
TagSize = poly1305.TagSize
- // S20BS is Salsa20's internal blocksize in bytes
+ // S20BS is ChaCha20's internal blocksize in bytes
S20BS = 64
// Maximal amount of bytes transfered with single key (4 GiB)
MaxBytesPerKey uint64 = 1 << 32
func newNonces(key *[32]byte, i uint64) chan *[NonceSize]byte {
macKey := make([]byte, 32)
- salsa20.XORKeyStream(macKey, make([]byte, 32), make([]byte, 8), key)
+ chacha20.XORKeyStream(macKey, make([]byte, 32), new([16]byte), key)
mac, err := blake2b.New256(macKey)
if err != nil {
panic(err)
bufR []byte
tagR *[TagSize]byte
keyAuthR *[SSize]byte
+ nonceR *[16]byte
pktSizeR int
// UDP-related
bufT []byte
tagT *[TagSize]byte
keyAuthT *[SSize]byte
+ nonceT *[16]byte
frameT []byte
noncesT chan *[NonceSize]byte
}
tagR: new([TagSize]byte),
tagT: new([TagSize]byte),
keyAuthR: new([SSize]byte),
+ nonceR: new([16]byte),
keyAuthT: new([SSize]byte),
+ nonceT: new([16]byte),
}
if isClient {
}
copy(p.frameT[len(p.frameT)-NonceSize:], (<-p.noncesT)[:])
var out []byte
+ copy(p.nonceT[8:], p.frameT[len(p.frameT)-NonceSize:])
if p.Encless {
var err error
- out, err = EnclessEncode(
- p.key,
- p.frameT[len(p.frameT)-NonceSize:],
- p.frameT[:len(p.frameT)-NonceSize],
- )
+ out, err = EnclessEncode(p.key, p.nonceT, p.frameT[:len(p.frameT)-NonceSize])
if err != nil {
panic(err)
}
out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
} else {
- salsa20.XORKeyStream(
+ chacha20.XORKeyStream(
p.bufT[:S20BS+len(p.frameT)-NonceSize],
p.bufT[:S20BS+len(p.frameT)-NonceSize],
- p.frameT[len(p.frameT)-NonceSize:],
+ p.nonceT,
p.key,
)
copy(p.keyAuthT[:], p.bufT[:SSize])
}
var out []byte
p.BusyR.Lock()
+ copy(p.nonceR[8:], data[len(data)-NonceSize:])
if p.Encless {
var err error
- out, err = EnclessDecode(
- p.key,
- data[len(data)-NonceSize:],
- data[:len(data)-NonceSize],
- )
+ out, err = EnclessDecode(p.key, p.nonceR, data[:len(data)-NonceSize])
if err != nil {
p.FramesUnauth++
p.BusyR.Unlock()
p.bufR[i] = 0
}
copy(p.bufR[S20BS:], data[TagSize:])
- salsa20.XORKeyStream(
+ chacha20.XORKeyStream(
p.bufR[:S20BS+len(data)-TagSize-NonceSize],
p.bufR[:S20BS+len(data)-TagSize-NonceSize],
- data[len(data)-NonceSize:],
+ p.nonceR,
p.key,
)
copy(p.keyAuthR[:], p.bufR[:SSize])
golang.org/x/crypto/PATENTS
golang.org/x/crypto/README
golang.org/x/crypto/blake2b
+golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/curve25519
golang.org/x/crypto/poly1305
-golang.org/x/crypto/salsa20
golang.org/x/crypto/ssh/terminal
EOF
tar cfCI - src $tmp/includes | tar xfC - $tmp