/
onion.go
259 lines (231 loc) · 6.42 KB
/
onion.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//go:build !gen
// +build !gen
package onramp
import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"path/filepath"
"github.com/cretz/bine/tor"
"github.com/cretz/bine/torutil/ed25519"
)
var torp *tor.Tor
// Onion represents a structure which manages an onion service and
// a Tor client. The onion service will automatically have persistent
// keys.
type Onion struct {
*tor.StartConf
*tor.ListenConf
*tor.DialConf
context.Context
name string
}
func (o *Onion) getStartConf() *tor.StartConf {
if o.StartConf == nil {
o.StartConf = &tor.StartConf{}
}
return o.StartConf
}
func (o *Onion) getContext() context.Context {
if o.Context == nil {
o.Context = context.Background()
}
return o.Context
}
func (o *Onion) getListenConf() *tor.ListenConf {
keys, err := o.Keys()
if err != nil {
log.Fatalf("Unable to get onion service keys, %s", err)
}
if o.ListenConf == nil {
o.ListenConf = &tor.ListenConf{
Key: keys,
}
}
return o.ListenConf
}
func (o *Onion) getDialConf() *tor.DialConf {
if o.DialConf == nil {
o.DialConf = &tor.DialConf{}
}
return o.DialConf
}
func (o *Onion) getTor() *tor.Tor {
if torp == nil {
var err error
torp, err = tor.Start(o.getContext(), o.getStartConf())
if err != nil {
panic(err)
}
}
return torp
}
func (o *Onion) getDialer() *tor.Dialer {
//if o.Dialer == nil {
//var err error
//o.Dialer, err
dialer, err := o.getTor().Dialer(o.getContext(), o.getDialConf())
if err != nil {
panic(err)
}
//}
//return o.Dialer
return dialer
}
func (o *Onion) getName() string {
if o.name == "" {
o.name = "onramp-onion"
}
return o.name
}
// NewListener returns a net.Listener which will listen on an onion
// address, and will automatically generate a keypair and store it.
// the args are always ignored
func (o *Onion) NewListener(n, addr string) (net.Listener, error) {
return o.Listen(n)
}
// Listen returns a net.Listener which will listen on an onion
// address, and will automatically generate a keypair and store it.
// the args are always ignored
func (o *Onion) Listen(args ...string) (net.Listener, error) {
return o.OldListen(args...)
}
// OldListen returns a net.Listener which will listen on an onion
// address, and will automatically generate a keypair and store it.
// the args are always ignored
func (o *Onion) OldListen(args ...string) (net.Listener, error) {
return o.getTor().Listen(o.getContext(), o.getListenConf())
}
// ListenTLS returns a net.Listener which will apply TLS encryption
// to the onion listener, which will not be decrypted until it reaches
// the browser
func (o *Onion) ListenTLS(args ...string) (net.Listener, error) {
cert, err := o.TLSKeys()
if err != nil {
return nil, fmt.Errorf("onramp ListenTLS: %v", err)
}
l, err := o.getTor().Listen(o.getContext(), o.getListenConf())
return tls.NewListener(
l,
&tls.Config{
Certificates: []tls.Certificate{cert},
},
), nil
}
// Dial returns a net.Conn to the given onion address or clearnet address.
func (o *Onion) Dial(net, addr string) (net.Conn, error) {
return o.getDialer().DialContext(o.getContext(), net, addr)
}
// Close closes the Onion Service and all associated resources.
func (o *Onion) Close() error {
return o.getTor().Close()
}
// Keys returns the keys for the Onion
func (o *Onion) Keys() (ed25519.KeyPair, error) {
return TorKeys(o.getName())
}
// DeleteKeys deletes the keys at the given key name in the key store.
// This is permanent and irreversible, and will change the onion service
// address.
func (g *Onion) DeleteKeys() error {
return DeleteOnionKeys(g.getName())
}
// NewOnion returns a new Onion object.
func NewOnion(name string) (*Onion, error) {
return &Onion{
name: name,
}, nil
}
// TorKeys returns a key pair which will be stored at the given key
// name in the key store. If the key already exists, it will be
// returned. If it does not exist, it will be generated.
func TorKeys(keyName string) (ed25519.KeyPair, error) {
keystore, err := TorKeystorePath()
if err != nil {
return nil, fmt.Errorf("onramp OnionKeys: discovery error %v", err)
}
var keys ed25519.KeyPair
keysPath := filepath.Join(keystore, keyName+".tor.private")
if _, err := os.Stat(keysPath); os.IsNotExist(err) {
tkeys, err := ed25519.GenerateKey(nil)
if err != nil {
log.Fatalf("Unable to generate onion service key, %s", err)
}
keys = tkeys
f, err := os.Create(keysPath)
if err != nil {
log.Fatalf("Unable to create Tor keys file for writing, %s", err)
}
defer f.Close()
_, err = f.Write(tkeys.PrivateKey())
if err != nil {
log.Fatalf("Unable to write Tor keys to disk, %s", err)
}
} else if err == nil {
tkeys, err := ioutil.ReadFile(keysPath)
if err != nil {
log.Fatalf("Unable to read Tor keys from disk")
}
k := ed25519.FromCryptoPrivateKey(tkeys)
keys = k
} else {
log.Fatalf("Unable to set up Tor keys, %s", err)
}
return keys, nil
}
var onions map[string]*Onion
// CloseAllOnion closes all onions managed by the onramp package. It does not
// affect objects instantiated by an app.
func CloseAllOnion() {
for i, g := range onions {
log.Println("Closing onion", g.name)
CloseOnion(i)
}
}
// CloseOnion closes the Onion at the given index. It does not affect Onion
// objects instantiated by an app.
func CloseOnion(tunName string) {
g, ok := onions[tunName]
if ok {
g.Close()
}
}
// ListenOnion returns a net.Listener for a onion structure's keys
// corresponding to a structure managed by the onramp library
// and not instantiated by an app.
func ListenOnion(network, keys string) (net.Listener, error) {
g, err := NewOnion(keys)
if err != nil {
return nil, fmt.Errorf("onramp Listen: %v", err)
}
onions[keys] = g
return g.Listen()
}
// DialOnion returns a net.Conn for a onion structure's keys
// corresponding to a structure managed by the onramp library
// and not instantiated by an app.
func DialOnion(network, addr string) (net.Conn, error) {
g, err := NewOnion(addr)
if err != nil {
return nil, fmt.Errorf("onramp Dial: %v", err)
}
onions[addr] = g
return g.Dial(network, addr)
}
// DeleteOnionKeys deletes the key file at the given path as determined by
// keystore + tunName.
func DeleteOnionKeys(tunName string) error {
keystore, err := TorKeystorePath()
if err != nil {
return fmt.Errorf("onramp DeleteOnionKeys: discovery error %v", err)
}
keyspath := filepath.Join(keystore, tunName+".i2p.private")
if err := os.Remove(keyspath); err != nil {
return fmt.Errorf("onramp DeleteOnionKeys: %v", err)
}
return nil
}