]> Cypherpunks.ru repositories - govpn.git/blob - identify.go
Replace handshake NULLs with an IDtag
[govpn.git] / identify.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2015 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/hex"
24         "log"
25         "os"
26         "sync"
27         "time"
28
29         "golang.org/x/crypto/xtea"
30 )
31
32 const (
33         IDSize      = 128 / 8
34         RefreshRate = 60 * time.Second
35 )
36
37 type PeerId [IDSize]byte
38
39 func (id PeerId) String() string {
40         return hex.EncodeToString(id[:])
41 }
42
43 type cipherCache map[PeerId]*xtea.Cipher
44
45 var (
46         PeersPath       string
47         IDsCache        cipherCache
48         cipherCacheLock sync.RWMutex
49 )
50
51 // Initialize (pre-cache) available peers info.
52 func PeersInit(path string) {
53         PeersPath = path
54         IDsCache = make(map[PeerId]*xtea.Cipher)
55         go func() {
56                 for {
57                         IDsCache.refresh()
58                         time.Sleep(RefreshRate)
59                 }
60         }()
61 }
62
63 // Initialize dummy cache for client-side usage. It will consist only
64 // of single key.
65 func PeersInitDummy(id *PeerId) {
66         IDsCache = make(map[PeerId]*xtea.Cipher)
67         cipher, err := xtea.NewCipher(id[:])
68         if err != nil {
69                 panic(err)
70         }
71         IDsCache[*id] = cipher
72 }
73
74 // Refresh IDsCache: remove disappeared keys, add missing ones with
75 // initialized ciphers.
76 func (cc cipherCache) refresh() {
77         dir, err := os.Open(PeersPath)
78         if err != nil {
79                 panic(err)
80         }
81         peerIds, err := dir.Readdirnames(0)
82         if err != nil {
83                 panic(err)
84         }
85         available := make(map[PeerId]bool)
86         for _, peerId := range peerIds {
87                 id := IDDecode(peerId)
88                 if id == nil {
89                         continue
90                 }
91                 available[*id] = true
92         }
93
94         cipherCacheLock.Lock()
95         // Cleanup deleted ones from cache
96         for k, _ := range cc {
97                 if _, exists := available[k]; !exists {
98                         delete(cc, k)
99                         log.Println("Cleaning key: ", k)
100                 }
101         }
102         // Add missing ones
103         for peerId, _ := range available {
104                 if _, exists := cc[peerId]; !exists {
105                         log.Println("Adding key", peerId)
106                         cipher, err := xtea.NewCipher(peerId[:])
107                         if err != nil {
108                                 panic(err)
109                         }
110                         cc[peerId] = cipher
111                 }
112         }
113         cipherCacheLock.Unlock()
114 }
115
116 // Try to find peer's identity (that equals to an encryption key)
117 // by taking first blocksize sized bytes from data at the beginning
118 // as plaintext and last bytes as cyphertext.
119 func (cc cipherCache) Find(data []byte) *PeerId {
120         if len(data) < xtea.BlockSize*2 {
121                 return nil
122         }
123         buf := make([]byte, xtea.BlockSize)
124         cipherCacheLock.RLock()
125         for pid, cipher := range cc {
126                 cipher.Decrypt(buf, data[len(data)-xtea.BlockSize:])
127                 if subtle.ConstantTimeCompare(buf, data[:xtea.BlockSize]) == 1 {
128                         ppid := PeerId(pid)
129                         cipherCacheLock.RUnlock()
130                         return &ppid
131                 }
132         }
133         cipherCacheLock.RUnlock()
134         return nil
135 }
136
137 // Decode identification string.
138 // It must be 32 hexadecimal characters long.
139 // If it is not the valid one, then return nil.
140 func IDDecode(raw string) *PeerId {
141         if len(raw) != IDSize*2 {
142                 return nil
143         }
144         idDecoded, err := hex.DecodeString(raw)
145         if err != nil {
146                 return nil
147         }
148         idP := new([IDSize]byte)
149         copy(idP[:], idDecoded)
150         id := PeerId(*idP)
151         return &id
152 }