]> Cypherpunks.ru repositories - govpn.git/blobdiff - src/cypherpunks.ru/govpn/identity.go
Could -> can, for consistency
[govpn.git] / src / cypherpunks.ru / govpn / identity.go
index f85631b01839639b8bf981572f4c8b6db106c984..52d430d6c1807df2ca94025309e76cb46acb93d7 100644 (file)
@@ -1,6 +1,6 @@
 /*
 GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -22,67 +22,109 @@ import (
        "crypto/subtle"
        "encoding/base64"
        "encoding/binary"
-       "log"
+       "encoding/hex"
+       "hash"
        "sync"
        "time"
 
-       "golang.org/x/crypto/xtea"
+       "github.com/Sirupsen/logrus"
+       "github.com/pkg/errors"
+       "golang.org/x/crypto/blake2b"
 )
 
-const (
-       IDSize = 128 / 8
-)
+// IDSize is a size of GoVPN peer's identity
+const IDSize = 128 / 8
 
-type PeerId [IDSize]byte
+// PeerID is identifier of a single GoVPN peer (client)
+type PeerID [IDSize]byte
 
-func (id PeerId) String() string {
+// String returns peer's ID in stringified form
+func (id PeerID) String() string {
        return base64.RawStdEncoding.EncodeToString(id[:])
 }
 
-func (id PeerId) MarshalJSON() ([]byte, error) {
+// MarshalJSON returns a JSON serialized peer's ID
+func (id PeerID) MarshalJSON() ([]byte, error) {
        return []byte(`"` + id.String() + `"`), nil
 }
 
-type CipherAndTimeSync struct {
-       c *xtea.Cipher
-       t int
+// MACAndTimeSync is a single peer MAC and timesync
+type MACAndTimeSync struct {
+       mac hash.Hash
+       ts  int
+       l   sync.Mutex
+}
+
+// MACCache caches all MACAndTimeSync for peers allowed to connect
+type MACCache struct {
+       cache map[PeerID]*MACAndTimeSync
+       l     sync.RWMutex
 }
 
-type CipherCache struct {
-       c map[PeerId]*CipherAndTimeSync
-       l sync.RWMutex
+// Length returns size of MACCache
+func (mc *MACCache) Length() int {
+       return len(mc.cache)
 }
 
-func NewCipherCache() *CipherCache {
-       return &CipherCache{c: make(map[PeerId]*CipherAndTimeSync)}
+// NewMACCache returns a new MACCache instance
+func NewMACCache() *MACCache {
+       return &MACCache{cache: make(map[PeerID]*MACAndTimeSync)}
 }
 
-// Remove disappeared keys, add missing ones with initialized ciphers.
-func (cc *CipherCache) Update(peers *map[PeerId]*PeerConf) {
-       cc.l.Lock()
-       for pid, _ := range cc.c {
+// Update removes disappeared keys, add missing ones with initialized MACs.
+func (mc *MACCache) Update(peers *map[PeerID]*PeerConf) error {
+       mc.l.Lock()
+       defer mc.l.Unlock()
+       fields := logrus.Fields{
+               "func":  logFuncPrefix + "MACCache.Update",
+               "peers": len(*peers),
+       }
+       logger.WithFields(fields).WithField("size", mc.Length()).Debug("Clean old keys")
+       for pid := range mc.cache {
                if _, exists := (*peers)[pid]; !exists {
-                       log.Println("Cleaning key:", pid)
-                       delete(cc.c, pid)
+                       logger.WithFields(fields).WithField("pid", pid).Debug("Cleaning key")
+                       delete(mc.cache, pid)
                }
        }
+       logger.WithFields(fields).WithField("size", mc.Length()).Debug("Cleaned, add/update new key")
        for pid, pc := range *peers {
-               if _, exists := cc.c[pid]; exists {
-                       cc.c[pid].t = pc.TimeSync
+               if _, exists := mc.cache[pid]; exists {
+                       logger.WithFields(fields).WithFields(
+                               logrus.Fields{
+                                       "pid":    pid.String(),
+                                       "old_ts": mc.cache[pid].ts,
+                                       "new_ts": pc.TimeSync,
+                               }).Debug("Rest timesync")
+                       mc.cache[pid].ts = pc.TimeSync
                } else {
-                       log.Println("Adding key", pid)
-                       cipher, err := xtea.NewCipher(pid[:])
+                       before := time.Now()
+                       mac, err := blake2b.New256(pid[:])
                        if err != nil {
-                               panic(err)
+                               return errors.Wrap(err, wrapBlake2bNew256)
+                       }
+                       logger.WithFields(fields).WithFields(logrus.Fields{
+                               "pid":     pid.String(),
+                               "ts":      pc.TimeSync,
+                               "elapsed": time.Now().Sub(before).String(),
+                       }).Debug("Adding key")
+                       mc.cache[pid] = &MACAndTimeSync{
+                               mac: mac,
+                               ts:  pc.TimeSync,
                        }
-                       cc.c[pid] = &CipherAndTimeSync{cipher, pc.TimeSync}
                }
        }
-       cc.l.Unlock()
+       logger.WithFields(fields).WithField("size", mc.Length()).Debug("Finish")
+       return nil
 }
 
-// If timeSync > 0, then XOR timestamp with the data.
+// AddTimeSync XORs timestamp with data if timeSync > 0
 func AddTimeSync(ts int, data []byte) {
+       fields := logrus.Fields{
+               "func":      logFuncPrefix + "AddTimeSync",
+               "ts":        ts,
+               "data_size": len(data),
+               "data":      hex.EncodeToString(data),
+       }
        if ts == 0 {
                return
        }
@@ -91,26 +133,52 @@ func AddTimeSync(ts int, data []byte) {
        for i := 0; i < 8; i++ {
                data[i] ^= buf[i]
        }
+       logger.WithFields(fields).WithField("after", hex.EncodeToString(data)).Debug("Done")
 }
 
-// Try to find peer's identity (that equals to an encryption key)
+// Find tries to find peer's identity (that equals to MAC)
 // by taking first blocksize sized bytes from data at the beginning
 // as plaintext and last bytes as cyphertext.
-func (cc *CipherCache) Find(data []byte) *PeerId {
-       if len(data) < xtea.BlockSize*2 {
-               return nil
+func (mc *MACCache) Find(data []byte) (*PeerID, error) {
+       const minimumSize = 8 * 2
+       lenData := len(data)
+       fields := logrus.Fields{
+               "func": logFuncPrefix + "MACCache.Find",
+               "data": lenData,
+               "size": mc.Length(),
+       }
+       logger.WithFields(fields).Debug("Starting")
+       if lenData < minimumSize {
+               return nil, errors.Errorf("MAC is too small %d, minimum %d", lenData, minimumSize)
        }
-       buf := make([]byte, xtea.BlockSize)
-       cc.l.RLock()
-       for pid, ct := range cc.c {
-               ct.c.Decrypt(buf, data[len(data)-xtea.BlockSize:])
-               AddTimeSync(ct.t, buf)
-               if subtle.ConstantTimeCompare(buf, data[:xtea.BlockSize]) == 1 {
-                       ppid := PeerId(pid)
-                       cc.l.RUnlock()
-                       return &ppid
+       buf := make([]byte, 8)
+       sum := make([]byte, 32)
+       mc.l.RLock()
+       defer mc.l.RUnlock()
+       for pid, mt := range mc.cache {
+               loopFields := logrus.Fields{"pid": pid.String()}
+               logger.WithFields(loopFields).Debug("process")
+               copy(buf, data)
+               AddTimeSync(mt.ts, buf)
+               mt.l.Lock()
+               mt.mac.Reset()
+               logger.WithFields(fields).WithField("buf", hex.EncodeToString(buf)).Debug("mt.mac.Write")
+               if _, err := mt.mac.Write(buf); err != nil {
+                       mt.l.Unlock()
+                       return nil, errors.Wrap(err, "mt.mac.Write")
                }
+               logger.WithFields(fields).WithField("buf", hex.EncodeToString(buf[:0])).Debug("mt.mac.Sum")
+               mt.mac.Sum(sum[:0])
+               mt.l.Unlock()
+
+               if subtle.ConstantTimeCompare(sum[len(sum)-8:], data[lenData-8:]) == 1 {
+                       logger.WithFields(fields).WithFields(loopFields).Debug("Matching peer")
+                       ppid := PeerID(pid)
+                       return &ppid, nil
+               }
+
+               logger.WithFields(fields).WithFields(loopFields).Debug("Not matching peer")
        }
-       cc.l.RUnlock()
-       return nil
+       logger.WithFields(fields).Debug("Can't find matching peer ID")
+       return nil, nil
 }