-
Notifications
You must be signed in to change notification settings - Fork 11
/
user.go
138 lines (124 loc) · 3.03 KB
/
user.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package nymo
import (
"context"
"crypto/ecdsa"
"crypto/tls"
"math/big"
"sync"
"time"
"github.com/nymo-net/nymo/pb"
)
type User struct {
cfg Config
db Database
cohort uint32
key *ecdsa.PrivateKey
cert tls.Certificate
peerLock sync.RWMutex
peers map[[hashTruncate]byte]*peer
total uint
numIn uint
retry peerRetrier
}
// Address returns the address of the user.
func (u *User) Address() *Address {
return &Address{
cohort: u.cohort,
x: u.key.X,
y: u.key.Y,
}
}
// Run runs the main loop for user as a Nymo peer client.
func (u *User) Run(ctx context.Context) {
if u.cfg.LocalPeerAnnounce {
go func() {
if e := u.ipv4PeerAnnounce(ctx); e != nil {
u.cfg.Logger.Print(e)
}
}()
go func() {
if e := u.ipv6PeerAnnounce(ctx); e != nil {
u.cfg.Logger.Print(e)
}
}()
}
if u.cfg.LocalPeerDiscover {
go func() {
if e := u.ipv4PeerDiscover(ctx); e != nil {
u.cfg.Logger.Print(e)
}
}()
go func() {
if e := u.ipv6PeerDiscover(ctx); e != nil {
u.cfg.Logger.Print(e)
}
}()
}
for ctx.Err() == nil {
u.dialNewPeers(ctx)
t := time.NewTimer(u.cfg.ScanPeerTime)
select {
case <-t.C:
case <-ctx.Done():
t.Stop()
return
}
}
}
// AddPeer adds a peer URL, which computes the hash of the URL and synchronously calls Database.AddPeer.
func (u *User) AddPeer(url string) {
hash := hasher([]byte(url))
u.db.AddPeer(url, &pb.Digest{
Hash: hash[:hashTruncate],
Cohort: cohortNumber, // XXX: when unknown, as wildcard
})
}
// OpenSupernode opens a supernode (a relay node). For arguments usage see OpenUser.
func OpenSupernode(db Database, cert tls.Certificate, cfg *Config) *User {
if cfg == nil {
cfg = DefaultConfig()
}
hash := hasher(cert.Certificate[0])
return &User{
cfg: *cfg,
db: db,
cohort: cohortNumber,
cert: cert,
peers: map[[hashTruncate]byte]*peer{truncateHash(hash[:]): nil},
retry: peerRetrier{m: make(map[string]time.Time)},
}
}
// OpenUser opens a user generated by GenerateUser, where the userKey argument should be
// exactly what GenerateUser returned.
//
// cert should contain a valid certificate that is used as the mTLS cert (on both client and server side).
//
// if cfg is nil, DefaultConfig will be used.
func OpenUser(db Database, userKey []byte, cert tls.Certificate, cfg *Config) *User {
if cfg == nil {
cfg = DefaultConfig()
}
key := new(ecdsa.PrivateKey)
key.Curve = curve
key.D = new(big.Int).SetBytes(userKey)
key.X, key.Y = curve.ScalarBaseMult(userKey)
hash := hasher(cert.Certificate[0])
return &User{
cfg: *cfg,
db: db,
cohort: getCohort(key.X, key.Y),
key: key,
cert: cert,
peers: map[[hashTruncate]byte]*peer{truncateHash(hash[:]): nil},
retry: peerRetrier{m: make(map[string]time.Time)},
}
}
// GenerateUser generates a new Nymo user. The returned key is opaque to the frontend
// and should be stored secretly.
func GenerateUser() ([]byte, error) {
key, err := ecdsa.GenerateKey(curve, cReader)
if err != nil {
return nil, err
}
return key.D.Bytes(), nil
}