]> Cypherpunks.ru repositories - govpn.git/blob - src/cypherpunks.ru/govpn/identity.go
Merge branch 'develop'
[govpn.git] / src / cypherpunks.ru / govpn / identity.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
4
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.
9
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.
14
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/>.
17 */
18
19 package govpn
20
21 import (
22         "crypto/subtle"
23         "encoding/base64"
24         "encoding/binary"
25         "log"
26         "sync"
27         "time"
28
29         "golang.org/x/crypto/xtea"
30 )
31
32 const (
33         IDSize = 128 / 8
34 )
35
36 type PeerId [IDSize]byte
37
38 func (id PeerId) String() string {
39         return base64.RawStdEncoding.EncodeToString(id[:])
40 }
41
42 func (id PeerId) MarshalJSON() ([]byte, error) {
43         return []byte(`"` + id.String() + `"`), nil
44 }
45
46 type CipherAndTimeSync struct {
47         c *xtea.Cipher
48         t int
49 }
50
51 type CipherCache struct {
52         c map[PeerId]*CipherAndTimeSync
53         l sync.RWMutex
54 }
55
56 func NewCipherCache() *CipherCache {
57         return &CipherCache{c: make(map[PeerId]*CipherAndTimeSync)}
58 }
59
60 // Remove disappeared keys, add missing ones with initialized ciphers.
61 func (cc *CipherCache) Update(peers *map[PeerId]*PeerConf) {
62         cc.l.Lock()
63         for pid, _ := range cc.c {
64                 if _, exists := (*peers)[pid]; !exists {
65                         log.Println("Cleaning key:", pid)
66                         delete(cc.c, pid)
67                 }
68         }
69         for pid, pc := range *peers {
70                 if _, exists := cc.c[pid]; exists {
71                         cc.c[pid].t = pc.TimeSync
72                 } else {
73                         log.Println("Adding key", pid)
74                         cipher, err := xtea.NewCipher(pid[:])
75                         if err != nil {
76                                 panic(err)
77                         }
78                         cc.c[pid] = &CipherAndTimeSync{cipher, pc.TimeSync}
79                 }
80         }
81         cc.l.Unlock()
82 }
83
84 // If timeSync > 0, then XOR timestamp with the data.
85 func AddTimeSync(ts int, data []byte) {
86         if ts == 0 {
87                 return
88         }
89         buf := make([]byte, 8)
90         binary.BigEndian.PutUint64(buf, uint64(time.Now().Unix()/int64(ts)*int64(ts)))
91         for i := 0; i < 8; i++ {
92                 data[i] ^= buf[i]
93         }
94 }
95
96 // Try to find peer's identity (that equals to an encryption key)
97 // by taking first blocksize sized bytes from data at the beginning
98 // as plaintext and last bytes as cyphertext.
99 func (cc *CipherCache) Find(data []byte) *PeerId {
100         if len(data) < xtea.BlockSize*2 {
101                 return nil
102         }
103         buf := make([]byte, xtea.BlockSize)
104         cc.l.RLock()
105         for pid, ct := range cc.c {
106                 ct.c.Decrypt(buf, data[len(data)-xtea.BlockSize:])
107                 AddTimeSync(ct.t, buf)
108                 if subtle.ConstantTimeCompare(buf, data[:xtea.BlockSize]) == 1 {
109                         ppid := PeerId(pid)
110                         cc.l.RUnlock()
111                         return &ppid
112                 }
113         }
114         cc.l.RUnlock()
115         return nil
116 }