forked from libp2p/go-libp2p-tls
-
Notifications
You must be signed in to change notification settings - Fork 0
/
transport.go
147 lines (129 loc) · 3.48 KB
/
transport.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
package libp2ptls
import (
"context"
"crypto/tls"
"errors"
"net"
"sync"
ci "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/sec"
)
// ID is the protocol ID (used when negotiating with multistream)
const ID = "/tls/1.0.0"
// Transport constructs secure communication sessions for a peer.
type Transport struct {
identity *Identity
localPeer peer.ID
privKey ci.PrivKey
}
// New creates a TLS encrypted transport
func New(key ci.PrivKey) (*Transport, error) {
id, err := peer.IDFromPrivateKey(key)
if err != nil {
return nil, err
}
t := &Transport{
localPeer: id,
privKey: key,
}
identity, err := NewIdentity(key)
if err != nil {
return nil, err
}
t.identity = identity
return t, nil
}
var _ sec.SecureTransport = &Transport{}
// SecureInbound runs the TLS handshake as a server.
func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) {
config, keyCh := t.identity.ConfigForAny()
cs, err := t.handshake(ctx, tls.Server(insecure, config), keyCh)
if err != nil {
insecure.Close()
}
return cs, err
}
// SecureOutbound runs the TLS handshake as a client.
// Note that SecureOutbound will not return an error if the server doesn't
// accept the certificate. This is due to the fact that in TLS 1.3, the client
// sends its certificate and the ClientFinished in the same flight, and can send
// application data immediately afterwards.
// If the handshake fails, the server will close the connection. The client will
// notice this after 1 RTT when calling Read.
func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
config, keyCh := t.identity.ConfigForPeer(p)
cs, err := t.handshake(ctx, tls.Client(insecure, config), keyCh)
if err != nil {
insecure.Close()
}
return cs, err
}
func (t *Transport) handshake(
ctx context.Context,
tlsConn *tls.Conn,
keyCh <-chan ci.PubKey,
) (sec.SecureConn, error) {
// There's no way to pass a context to tls.Conn.Handshake().
// See https://github.com/golang/go/issues/18482.
// Close the connection instead.
select {
case <-ctx.Done():
tlsConn.Close()
default:
}
done := make(chan struct{})
var wg sync.WaitGroup
// Ensure that we do not return before
// either being done or having a context
// cancellation.
defer wg.Wait()
defer close(done)
wg.Add(1)
go func() {
defer wg.Done()
select {
case <-done:
case <-ctx.Done():
tlsConn.Close()
}
}()
if err := tlsConn.Handshake(); err != nil {
// if the context was canceled, return the context error
if ctxErr := ctx.Err(); ctxErr != nil {
return nil, ctxErr
}
return nil, err
}
// Should be ready by this point, don't block.
var remotePubKey ci.PubKey
select {
case remotePubKey = <-keyCh:
default:
}
if remotePubKey == nil {
return nil, errors.New("go-libp2p-tls BUG: expected remote pub key to be set")
}
conn, err := t.setupConn(tlsConn, remotePubKey)
if err != nil {
// if the context was canceled, return the context error
if ctxErr := ctx.Err(); ctxErr != nil {
return nil, ctxErr
}
return nil, err
}
return conn, nil
}
func (t *Transport) setupConn(tlsConn *tls.Conn, remotePubKey ci.PubKey) (sec.SecureConn, error) {
remotePeerID, err := peer.IDFromPublicKey(remotePubKey)
if err != nil {
return nil, err
}
return &conn{
Conn: tlsConn,
localPeer: t.localPeer,
privKey: t.privKey,
remotePeer: remotePeerID,
remotePubKey: remotePubKey,
}, nil
}