/
quictransport.go
169 lines (143 loc) · 4.04 KB
/
quictransport.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
// +build !js
// +build quic
package webrtc
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"errors"
"strings"
"sync"
"time"
"github.com/pion/dtls"
"github.com/pion/logging"
"github.com/pion/quic"
"github.com/pion/webrtc/v2/internal/mux"
"github.com/pion/webrtc/v2/pkg/rtcerr"
)
// QUICTransport is a specialization of QuicTransportBase focused on
// peer-to-peer use cases and includes information relating to use of a
// QUIC transport with an ICE transport.
type QUICTransport struct {
lock sync.RWMutex
quic.TransportBase
iceTransport *ICETransport
certificates []Certificate
api *API
log logging.LeveledLogger
}
// NewQUICTransport creates a new QUICTransport.
// This constructor is part of the ORTC API. It is not
// meant to be used together with the basic WebRTC API.
// Note that the Quic transport is a draft and therefore
// highly experimental. It is currently not supported by
// any browsers yet.
func (api *API) NewQUICTransport(transport *ICETransport, certificates []Certificate) (*QUICTransport, error) {
t := &QUICTransport{
iceTransport: transport,
api: api,
log: api.settingEngine.LoggerFactory.NewLogger("quic"),
}
if len(certificates) > 0 {
now := time.Now()
for _, x509Cert := range certificates {
if !x509Cert.Expires().IsZero() && now.After(x509Cert.Expires()) {
return nil, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}
}
t.certificates = append(t.certificates, x509Cert)
}
} else {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, &rtcerr.UnknownError{Err: err}
}
certificate, err := GenerateCertificate(sk)
if err != nil {
return nil, err
}
t.certificates = []Certificate{*certificate}
}
return t, nil
}
// GetLocalParameters returns the Quic parameters of the local QUICParameters upon construction.
func (t *QUICTransport) GetLocalParameters() (QUICParameters, error) {
fingerprints := []DTLSFingerprint{}
for _, c := range t.certificates {
prints, err := c.GetFingerprints() // TODO: Should be only one?
if err != nil {
return QUICParameters{}, err
}
fingerprints = append(fingerprints, prints...)
}
return QUICParameters{
Role: QUICRoleAuto, // always returns the default role
Fingerprints: fingerprints,
}, nil
}
// Start Quic transport with the parameters of the remote
func (t *QUICTransport) Start(remoteParameters QUICParameters) error {
t.lock.Lock()
defer t.lock.Unlock()
if err := t.ensureICEConn(); err != nil {
return err
}
// TODO: handle multiple certs
cert := t.certificates[0]
isClient := true
switch remoteParameters.Role {
case QUICRoleClient:
isClient = true
case QUICRoleServer:
isClient = false
default:
if t.iceTransport.Role() == ICERoleControlling {
isClient = false
}
}
cfg := &quic.Config{
Client: isClient,
Certificate: cert.x509Cert,
PrivateKey: cert.privateKey,
}
endpoint := t.iceTransport.NewEndpoint(mux.MatchAll)
err := t.TransportBase.StartBase(endpoint, cfg)
if err != nil {
return err
}
// Check the fingerprint if a certificate was exchanged
// TODO: Check why never received.
remoteCerts := t.TransportBase.GetRemoteCertificates()
if len(remoteCerts) > 0 {
err := t.validateFingerPrint(remoteParameters, remoteCerts[0])
if err != nil {
return err
}
} else {
t.log.Errorf("Warning: Certificate not checked")
}
return nil
}
func (t *QUICTransport) validateFingerPrint(remoteParameters QUICParameters, remoteCert *x509.Certificate) error {
for _, fp := range remoteParameters.Fingerprints {
hashAlgo, err := dtls.HashAlgorithmString(fp.Algorithm)
if err != nil {
return err
}
remoteValue, err := dtls.Fingerprint(remoteCert, hashAlgo)
if err != nil {
return err
}
if strings.EqualFold(remoteValue, fp.Value) {
return nil
}
}
return errors.New("no matching fingerprint")
}
func (t *QUICTransport) ensureICEConn() error {
if t.iceTransport == nil ||
t.iceTransport.State() == ICETransportStateNew {
return errors.New("ICE connection not started")
}
return nil
}