2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2019 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 // Chaffing-and-Winnowing.
20 // This package implements Chaffing-and-Winnowing technology
21 // (http://people.csail.mit.edu/rivest/chaffing-980701.txt).
23 // It outputs two Poly1305 MACs for each bit of input data: one valid,
24 // and other is not. MACs sequence is following:
26 // MAC of 1st byte, 1st bit, 0 possible value
27 // MAC of 1st byte, 1st bit, 1 possible value
28 // MAC of 1st byte, 2nd bit, 0 possible value
29 // MAC of 1st byte, 2nd bit, 1 possible value
32 // If bit value is 0, then first MAC is taken over "1" and the second
33 // one is over "0". If bit value is 1, then first is taken over "0" and
34 // second is over "1".
36 // Poly1305 uses 256-bit one-time key. We generate it using ChaCha20 for
37 // for the whole byte at once (16 MACs).
39 // MACKey1, MACKey2, ... = ChaCha20(authKey, nonce, 0x00...)
40 // nonce = prefix || big endian byte number
49 "golang.org/x/crypto/poly1305"
53 EnlargeFactor = 16 * poly1305.TagSize
56 func zero(in []byte) {
57 for i := 0; i < len(in); i++ {
62 // Chaff the data. noncePrfx is 64-bit nonce. Output data will be much
63 // larger: 256 bytes for each input byte.
64 func Chaff(authKey *[32]byte, noncePrfx, in []byte) []byte {
65 out := make([]byte, len(in)*EnlargeFactor)
66 keys := make([]byte, 8*64)
67 nonce := new([16]byte)
68 copy(nonce[:8], noncePrfx)
72 macKey := new([32]byte)
73 for n, b := range in {
74 binary.BigEndian.PutUint64(nonce[8:], uint64(n))
75 chacha20.XORKeyStream(keys, keys, nonce, authKey)
76 for i = 0; i < 8; i++ {
77 v = (b >> uint8(i)) & 1
78 copy(macKey[:], keys[64*i:64*i+32])
80 poly1305.Sum(tag, []byte("1"), macKey)
82 poly1305.Sum(tag, []byte("0"), macKey)
84 copy(out[16*(n*16+i*2):], tag[:])
85 copy(macKey[:], keys[64*i+32:64*i+64])
87 poly1305.Sum(tag, []byte("1"), macKey)
89 poly1305.Sum(tag, []byte("0"), macKey)
91 copy(out[16*(n*16+i*2+1):], tag[:])
100 func Winnow(authKey *[32]byte, noncePrfx, in []byte) ([]byte, error) {
101 if len(in)%EnlargeFactor != 0 {
102 return nil, errors.New("Invalid data size")
104 out := make([]byte, len(in)/EnlargeFactor)
105 keys := make([]byte, 8*64)
106 nonce := new([16]byte)
107 copy(nonce[:8], noncePrfx)
111 macKey := new([32]byte)
112 defer zero(macKey[:])
117 for n := 0; n < len(out); n++ {
118 binary.BigEndian.PutUint64(nonce[8:], uint64(n))
119 chacha20.XORKeyStream(keys, keys, nonce, authKey)
121 for i = 0; i < 8; i++ {
122 copy(macKey[:], keys[64*i:64*i+32])
123 poly1305.Sum(tag, []byte("1"), macKey)
124 is01 = subtle.ConstantTimeCompare(
126 in[16*(n*16+i*2):16*(n*16+i*2+1)],
128 poly1305.Sum(tag, []byte("0"), macKey)
129 is00 = subtle.ConstantTimeCompare(
131 in[16*(n*16+i*2):16*(n*16+i*2+1)],
133 copy(macKey[:], keys[64*i+32:64*i+64])
134 poly1305.Sum(tag, []byte("1"), macKey)
135 is11 = subtle.ConstantTimeCompare(
137 in[16*(n*16+i*2+1):16*(n*16+i*2+2)],
139 poly1305.Sum(tag, []byte("0"), macKey)
140 is10 = subtle.ConstantTimeCompare(
142 in[16*(n*16+i*2+1):16*(n*16+i*2+2)],
144 if !((is01 && is10) || (is00 && is11)) {
146 return nil, errors.New("Invalid authenticator received")