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("Cleaning old keys")
83 for pid := range mc.cache {
84 if _, exists := (*peers)[pid]; !exists {
85 logger.WithFields(fields).WithField("pid", pid).Debug("Cleaning key")
94 ).Debug("Cleaned, adding/updating new keys")
95 for pid, pc := range *peers {
96 if _, exists := mc.cache[pid]; exists {
97 logger.WithFields(fields).WithFields(
100 "old_ts": mc.cache[pid].ts,
101 "new_ts": pc.TimeSync,
102 }).Debug("Rest timesync")
103 mc.cache[pid].ts = pc.TimeSync
106 mac, err := blake2b.New256(pid[:])
108 return errors.Wrap(err, wrapBlake2bNew256)
110 logger.WithFields(fields).WithFields(logrus.Fields{
113 "elapsed": time.Now().Sub(before).String(),
114 }).Debug("Adding key")
115 mc.cache[pid] = &MACAndTimeSync{
121 logger.WithFields(fields).WithField("size", mc.Length()).Debug("Finished")
125 // AddTimeSync XORs timestamp with data if timeSync > 0
126 func AddTimeSync(ts int, data []byte) {
127 fields := logrus.Fields{
128 "func": logFuncPrefix + "AddTimeSync",
130 "data_size": len(data),
131 "data": hex.EncodeToString(data),
136 buf := make([]byte, 8)
137 binary.BigEndian.PutUint64(buf, uint64(time.Now().Unix()/int64(ts)*int64(ts)))
138 for i := 0; i < 8; i++ {
145 hex.EncodeToString(data),
149 // Find tries to find peer's identity (that equals to MAC)
150 // by taking first blocksize sized bytes from data at the beginning
151 // as plaintext and last bytes as cyphertext.
152 func (mc *MACCache) Find(data []byte) (*PeerID, error) {
153 const minimumSize = 8 * 2
154 fields := logrus.Fields{
155 "func": logFuncPrefix + "MACCache.Find",
159 logger.WithFields(fields).Debug("Starting")
160 if len(data) < minimumSize {
161 return nil, errors.Errorf("MAC is too short %d, minimum %d", len(data), minimumSize)
163 buf := make([]byte, 8)
164 sum := make([]byte, 32)
167 for pid, mt := range mc.cache {
168 loopFields := logrus.Fields{"pid": pid.String()}
169 logger.WithFields(loopFields).Debug("Processing")
171 AddTimeSync(mt.ts, buf)
177 "buf", hex.EncodeToString(buf),
178 ).Debug("mt.mac.Write")
179 if _, err := mt.mac.Write(buf); err != nil {
181 return nil, errors.Wrap(err, "mt.mac.Write")
187 hex.EncodeToString(buf[:0]),
188 ).Debug("mt.mac.Sum")
192 if subtle.ConstantTimeCompare(sum[len(sum)-8:], data[len(data)-8:]) == 1 {
193 logger.WithFields(fields).WithFields(loopFields).Debug("Matching peer")
198 logger.WithFields(fields).WithFields(loopFields).Debug("Peer is not matched")
200 logger.WithFields(fields).Debug("Can not find matching peer ID")