import (
"crypto/subtle"
"encoding/hex"
+ "io/ioutil"
"log"
"os"
+ "path"
+ "strings"
+ "sync"
+ "time"
"golang.org/x/crypto/xtea"
)
const (
- IDSize = 128 / 8
+ IDSize = 128 / 8
+ RefreshRate = 60 * time.Second
)
type PeerId [IDSize]byte
return hex.EncodeToString(id[:])
}
+// Return human readable name of the peer.
+// It equals either to peers/PEER/name file contents or PEER's hex.
+func (id PeerId) MarshalJSON() ([]byte, error) {
+ result := id.String()
+ if name, err := ioutil.ReadFile(path.Join(PeersPath, result, "name")); err == nil {
+ result = strings.TrimRight(string(name), "\n")
+ }
+ return []byte(`"` + result + `"`), nil
+}
+
type cipherCache map[PeerId]*xtea.Cipher
var (
- PeersPath string
- IDsCache cipherCache
+ PeersPath string
+ IDsCache cipherCache
+ cipherCacheLock sync.RWMutex
)
// Initialize (pre-cache) available peers info.
func PeersInit(path string) {
PeersPath = path
IDsCache = make(map[PeerId]*xtea.Cipher)
- IDsCache.refresh()
+ go func() {
+ for {
+ IDsCache.refresh()
+ time.Sleep(RefreshRate)
+ }
+ }()
+}
+
+// Initialize dummy cache for client-side usage. It will consist only
+// of single key.
+func PeersInitDummy(id *PeerId) {
+ IDsCache = make(map[PeerId]*xtea.Cipher)
+ cipher, err := xtea.NewCipher(id[:])
+ if err != nil {
+ panic(err)
+ }
+ IDsCache[*id] = cipher
}
// Refresh IDsCache: remove disappeared keys, add missing ones with
}
available[*id] = true
}
+
+ cipherCacheLock.Lock()
// Cleanup deleted ones from cache
for k, _ := range cc {
if _, exists := available[k]; !exists {
cc[peerId] = cipher
}
}
+ cipherCacheLock.Unlock()
}
// Try to find peer's identity (that equals to an encryption key)
-// by providing cipher and plain texts.
-func (cc cipherCache) Find(plaintext, ciphertext []byte) *PeerId {
- cc.refresh()
+// 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
+ }
buf := make([]byte, xtea.BlockSize)
+ cipherCacheLock.RLock()
for pid, cipher := range cc {
- cipher.Decrypt(buf, ciphertext)
- if subtle.ConstantTimeCompare(buf, plaintext) == 1 {
+ cipher.Decrypt(buf, data[len(data)-xtea.BlockSize:])
+ if subtle.ConstantTimeCompare(buf, data[:xtea.BlockSize]) == 1 {
ppid := PeerId(pid)
+ cipherCacheLock.RUnlock()
return &ppid
}
}
+ cipherCacheLock.RUnlock()
return nil
}