2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2015 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/>.
33 "golang.org/x/crypto/xtea"
38 RefreshRate = 60 * time.Second
41 type PeerId [IDSize]byte
43 func (id PeerId) String() string {
44 return hex.EncodeToString(id[:])
47 // Return human readable name of the peer.
48 // It equals either to peers/PEER/name file contents or PEER's hex.
49 func (id PeerId) MarshalJSON() ([]byte, error) {
51 if name, err := ioutil.ReadFile(path.Join(PeersPath, result, "name")); err == nil {
52 result = strings.TrimRight(string(name), "\n")
54 return []byte(`"` + result + `"`), nil
57 type PeerConf struct {
65 type cipherCache map[PeerId]*xtea.Cipher
70 cipherCacheLock sync.RWMutex
74 // Initialize (pre-cache) available peers info.
75 func PeersInit(path string) {
77 IDsCache = make(map[PeerId]*xtea.Cipher)
81 time.Sleep(RefreshRate)
86 // Initialize dummy cache for client-side usage.
87 func PeersInitDummy(id *PeerId, conf PeerConf) {
88 IDsCache = make(map[PeerId]*xtea.Cipher)
89 cipher, err := xtea.NewCipher(id[:])
93 IDsCache[*id] = cipher
97 // Refresh IDsCache: remove disappeared keys, add missing ones with
98 // initialized ciphers.
99 func (cc cipherCache) refresh() {
100 dir, err := os.Open(PeersPath)
104 peerIds, err := dir.Readdirnames(0)
108 available := make(map[PeerId]bool)
109 for _, peerId := range peerIds {
110 id := IDDecode(peerId)
114 available[*id] = true
117 cipherCacheLock.Lock()
118 // Cleanup deleted ones from cache
119 for k, _ := range cc {
120 if _, exists := available[k]; !exists {
122 log.Println("Cleaning key: ", k)
126 for peerId, _ := range available {
127 if _, exists := cc[peerId]; !exists {
128 log.Println("Adding key", peerId)
129 cipher, err := xtea.NewCipher(peerId[:])
136 cipherCacheLock.Unlock()
139 // Try to find peer's identity (that equals to an encryption key)
140 // by taking first blocksize sized bytes from data at the beginning
141 // as plaintext and last bytes as cyphertext.
142 func (cc cipherCache) Find(data []byte) *PeerId {
143 if len(data) < xtea.BlockSize*2 {
146 buf := make([]byte, xtea.BlockSize)
147 cipherCacheLock.RLock()
148 for pid, cipher := range cc {
149 cipher.Decrypt(buf, data[len(data)-xtea.BlockSize:])
150 if subtle.ConstantTimeCompare(buf, data[:xtea.BlockSize]) == 1 {
152 cipherCacheLock.RUnlock()
156 cipherCacheLock.RUnlock()
160 func readIntFromFile(path string) (int, error) {
161 data, err := ioutil.ReadFile(path)
165 val, err := strconv.Atoi(strings.TrimRight(string(data), "\n"))
172 // Get peer related configuration.
173 func (id *PeerId) ConfGet() *PeerConf {
174 if dummyConf != nil {
177 conf := PeerConf{Id: id, Noncediff: 1, NoiseEnable: false, CPR: 0}
178 peerPath := path.Join(PeersPath, id.String())
180 timeout := TimeoutDefault
181 if val, err := readIntFromFile(path.Join(peerPath, "timeout")); err == nil {
184 conf.Timeout = time.Second * time.Duration(timeout)
186 if val, err := readIntFromFile(path.Join(peerPath, "noncediff")); err == nil {
189 if val, err := readIntFromFile(path.Join(peerPath, "noise")); err == nil && val == 1 {
190 conf.NoiseEnable = true
192 if val, err := readIntFromFile(path.Join(peerPath, "cpr")); err == nil {
198 // Decode identification string.
199 // It must be 32 hexadecimal characters long.
200 // If it is not the valid one, then return nil.
201 func IDDecode(raw string) *PeerId {
202 if len(raw) != IDSize*2 {
205 idDecoded, err := hex.DecodeString(raw)
209 idP := new([IDSize]byte)
210 copy(idP[:], idDecoded)