mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 23:26:58 +08:00
174 lines
4.2 KiB
Go
174 lines
4.2 KiB
Go
// +build !js
|
|
// +build quic
|
|
|
|
package webrtc
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"errors"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/pion/dtls/v2/pkg/crypto/fingerprint"
|
|
"github.com/pion/logging"
|
|
"github.com/pion/quic"
|
|
"github.com/pion/webrtc/v3/internal/mux"
|
|
"github.com/pion/webrtc/v3/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
|
|
|
|
loggingFactory logging.LoggerFactory
|
|
}
|
|
|
|
var (
|
|
errQuicTransportFingerprintNoMatch = errors.New("no matching fingerprint")
|
|
errQuicTransportICEConnectionNotStarted = errors.New("ICE connection not started")
|
|
)
|
|
|
|
// 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,
|
|
loggingFactory: api.settingEngine.LoggerFactory,
|
|
}
|
|
|
|
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()
|
|
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
|
|
}
|
|
|
|
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,
|
|
LoggerFactory: t.loggingFactory,
|
|
}
|
|
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
|
|
remoteCerts := t.TransportBase.GetRemoteCertificates()
|
|
if len(remoteCerts) > 0 {
|
|
err := t.validateFingerPrint(remoteParameters, remoteCerts[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
log := t.loggingFactory.NewLogger("quic-go")
|
|
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 := fingerprint.HashFromString(fp.Algorithm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
remoteValue, err := fingerprint.Fingerprint(remoteCert, hashAlgo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if strings.EqualFold(remoteValue, fp.Value) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return errQuicTransportFingerprintNoMatch
|
|
}
|
|
|
|
func (t *QUICTransport) ensureICEConn() error {
|
|
if t.iceTransport == nil ||
|
|
t.iceTransport.State() == ICETransportStateNew {
|
|
return errQuicTransportICEConnectionNotStarted
|
|
}
|
|
|
|
return nil
|
|
}
|