forked from pion/webrtc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rtcquictransport.go
156 lines (133 loc) · 3.9 KB
/
rtcquictransport.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
package webrtc
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/pions/dtls"
"github.com/pions/webrtc/internal/mux"
"github.com/pions/webrtc/pkg/quic"
"github.com/pions/webrtc/pkg/rtcerr"
)
// RTCQuicTransport 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 RTCQuicTransport struct {
lock sync.RWMutex
quic.TransportBase
iceTransport *RTCIceTransport
certificates []RTCCertificate
}
// NewRTCQuicTransport creates a new RTCQuicTransport.
// 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) NewRTCQuicTransport(transport *RTCIceTransport, certificates []RTCCertificate) (*RTCQuicTransport, error) {
t := &RTCQuicTransport{iceTransport: transport}
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 = []RTCCertificate{*certificate}
}
return t, nil
}
// GetLocalParameters returns the Quic parameters of the local RTCQuicParameters upon construction.
func (t *RTCQuicTransport) GetLocalParameters() RTCQuicParameters {
fingerprints := []RTCDtlsFingerprint{}
for _, c := range t.certificates {
prints := c.GetFingerprints() // TODO: Should be only one?
fingerprints = append(fingerprints, prints...)
}
return RTCQuicParameters{
Role: RTCQuicRoleAuto, // always returns the default role
Fingerprints: fingerprints,
}
}
// Start Quic transport with the parameters of the remote
func (t *RTCQuicTransport) Start(remoteParameters RTCQuicParameters) 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 RTCQuicRoleClient:
isClient = true
case RTCQuicRoleServer:
isClient = false
default:
if t.iceTransport.Role() == RTCIceRoleControlling {
isClient = false
}
}
cfg := &quic.Config{
Client: isClient,
Certificate: cert.x509Cert,
PrivateKey: cert.privateKey,
}
endpoint := t.iceTransport.mux.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 {
fmt.Println("Warning: Certificate not checked")
}
return nil
}
func (t *RTCQuicTransport) validateFingerPrint(remoteParameters RTCQuicParameters, 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.ToLower(remoteValue) == strings.ToLower(fp.Value) {
return nil
}
}
return errors.New("No matching fingerprint")
}
func (t *RTCQuicTransport) ensureICEConn() error {
if t.iceTransport == nil ||
t.iceTransport.conn == nil ||
t.iceTransport.mux == nil {
return errors.New("ICE connection not started")
}
return nil
}