2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2017 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, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 "github.com/Sirupsen/logrus"
31 "github.com/pkg/errors"
32 "golang.org/x/crypto/blake2b"
35 // IDSize is a size of GoVPN peer's identity
36 const IDSize = 128 / 8
38 // PeerID is identifier of a single GoVPN peer (client)
39 type PeerID [IDSize]byte
41 // String returns peer's ID in stringified form
42 func (id PeerID) String() string {
43 return base64.RawStdEncoding.EncodeToString(id[:])
46 // MarshalJSON returns a JSON serialized peer's ID
47 func (id PeerID) MarshalJSON() ([]byte, error) {
48 return []byte(`"` + id.String() + `"`), nil
51 // MACAndTimeSync is a single peer MAC and timesync
52 type MACAndTimeSync struct {
58 // MACCache caches all MACAndTimeSync for peers allowed to connect
59 type MACCache struct {
60 cache map[PeerID]*MACAndTimeSync
64 // Length returns size of MACCache
65 func (mc *MACCache) Length() int {
69 // NewMACCache returns a new MACCache instance
70 func NewMACCache() *MACCache {
71 return &MACCache{cache: make(map[PeerID]*MACAndTimeSync)}
74 // Update removes disappeared keys, add missing ones with initialized MACs.
75 func (mc *MACCache) Update(peers *map[PeerID]*PeerConf) error {
78 fields := logrus.Fields{
79 "func": logFuncPrefix + "MACCache.Update",
82 logger.WithFields(fields).WithField("size", mc.Length()).Debug("Clean old keys")
83 for pid := range mc.cache {
84 if _, exists := (*peers)[pid]; !exists {
85 logger.WithFields(fields).WithField("pid", pid).Debug("Cleaning key")
89 logger.WithFields(fields).WithField("size", mc.Length()).Debug("Cleaned, add/update new key")
90 for pid, pc := range *peers {
91 if _, exists := mc.cache[pid]; exists {
92 logger.WithFields(fields).WithFields(
95 "old_ts": mc.cache[pid].ts,
96 "new_ts": pc.TimeSync,
97 }).Debug("Rest timesync")
98 mc.cache[pid].ts = pc.TimeSync
101 mac, err := blake2b.New256(pid[:])
103 return errors.Wrap(err, wrapBlake2bNew256)
105 logger.WithFields(fields).WithFields(logrus.Fields{
108 "elapsed": time.Now().Sub(before).String(),
109 }).Debug("Adding key")
110 mc.cache[pid] = &MACAndTimeSync{
116 logger.WithFields(fields).WithField("size", mc.Length()).Debug("Finish")
120 // AddTimeSync XORs timestamp with data if timeSync > 0
121 func AddTimeSync(ts int, data []byte) {
122 fields := logrus.Fields{
123 "func": logFuncPrefix + "AddTimeSync",
125 "data_size": len(data),
126 "data": hex.EncodeToString(data),
131 buf := make([]byte, 8)
132 binary.BigEndian.PutUint64(buf, uint64(time.Now().Unix()/int64(ts)*int64(ts)))
133 for i := 0; i < 8; i++ {
136 logger.WithFields(fields).WithField("after", hex.EncodeToString(data)).Debug("Done")
139 // Find tries to find peer's identity (that equals to MAC)
140 // by taking first blocksize sized bytes from data at the beginning
141 // as plaintext and last bytes as cyphertext.
142 func (mc *MACCache) Find(data []byte) (*PeerID, error) {
143 const minimumSize = 8 * 2
144 fields := logrus.Fields{
145 "func": logFuncPrefix + "MACCache.Find",
149 logger.WithFields(fields).Debug("Starting")
150 if len(data) < minimumSize {
151 return nil, errors.Errorf("MAC is too small %d, minimum %d", len(data), minimumSize)
153 buf := make([]byte, 8)
154 sum := make([]byte, 32)
157 for pid, mt := range mc.cache {
158 loopFields := logrus.Fields{"pid": pid.String()}
159 logger.WithFields(loopFields).Debug("process")
161 AddTimeSync(mt.ts, buf)
164 logger.WithFields(fields).WithField("buf", hex.EncodeToString(buf)).Debug("mt.mac.Write")
165 if _, err := mt.mac.Write(buf); err != nil {
167 return nil, errors.Wrap(err, "mt.mac.Write")
169 logger.WithFields(fields).WithField("buf", hex.EncodeToString(buf[:0])).Debug("mt.mac.Sum")
173 if subtle.ConstantTimeCompare(sum[len(sum)-8:], data[len(data)-8:]) == 1 {
174 logger.WithFields(fields).WithFields(loopFields).Debug("Matching peer")
179 logger.WithFields(fields).WithFields(loopFields).Debug("Not matching peer")
181 logger.WithFields(fields).Debug("Can't find matching peer ID")