diff options
Diffstat (limited to 'vendor/github.com/pion/webrtc/v3/certificate.go')
-rw-r--r-- | vendor/github.com/pion/webrtc/v3/certificate.go | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/vendor/github.com/pion/webrtc/v3/certificate.go b/vendor/github.com/pion/webrtc/v3/certificate.go new file mode 100644 index 0000000..bf106d6 --- /dev/null +++ b/vendor/github.com/pion/webrtc/v3/certificate.go @@ -0,0 +1,185 @@ +// +build !js + +package webrtc + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/hex" + "fmt" + "math/big" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/fingerprint" + "github.com/pion/webrtc/v3/pkg/rtcerr" +) + +// Certificate represents a x509Cert used to authenticate WebRTC communications. +type Certificate struct { + privateKey crypto.PrivateKey + x509Cert *x509.Certificate + statsID string +} + +// NewCertificate generates a new x509 compliant Certificate to be used +// by DTLS for encrypting data sent over the wire. This method differs from +// GenerateCertificate by allowing to specify a template x509.Certificate to +// be used in order to define certificate parameters. +func NewCertificate(key crypto.PrivateKey, tpl x509.Certificate) (*Certificate, error) { + var err error + var certDER []byte + switch sk := key.(type) { + case *rsa.PrivateKey: + pk := sk.Public() + tpl.SignatureAlgorithm = x509.SHA256WithRSA + certDER, err = x509.CreateCertificate(rand.Reader, &tpl, &tpl, pk, sk) + if err != nil { + return nil, &rtcerr.UnknownError{Err: err} + } + case *ecdsa.PrivateKey: + pk := sk.Public() + tpl.SignatureAlgorithm = x509.ECDSAWithSHA256 + certDER, err = x509.CreateCertificate(rand.Reader, &tpl, &tpl, pk, sk) + if err != nil { + return nil, &rtcerr.UnknownError{Err: err} + } + default: + return nil, &rtcerr.NotSupportedError{Err: ErrPrivateKeyType} + } + + cert, err := x509.ParseCertificate(certDER) + if err != nil { + return nil, &rtcerr.UnknownError{Err: err} + } + + return &Certificate{privateKey: key, x509Cert: cert, statsID: fmt.Sprintf("certificate-%d", time.Now().UnixNano())}, nil +} + +// Equals determines if two certificates are identical by comparing both the +// secretKeys and x509Certificates. +func (c Certificate) Equals(o Certificate) bool { + switch cSK := c.privateKey.(type) { + case *rsa.PrivateKey: + if oSK, ok := o.privateKey.(*rsa.PrivateKey); ok { + if cSK.N.Cmp(oSK.N) != 0 { + return false + } + return c.x509Cert.Equal(o.x509Cert) + } + return false + case *ecdsa.PrivateKey: + if oSK, ok := o.privateKey.(*ecdsa.PrivateKey); ok { + if cSK.X.Cmp(oSK.X) != 0 || cSK.Y.Cmp(oSK.Y) != 0 { + return false + } + return c.x509Cert.Equal(o.x509Cert) + } + return false + default: + return false + } +} + +// Expires returns the timestamp after which this certificate is no longer valid. +func (c Certificate) Expires() time.Time { + if c.x509Cert == nil { + return time.Time{} + } + return c.x509Cert.NotAfter +} + +// GetFingerprints returns the list of certificate fingerprints, one of which +// is computed with the digest algorithm used in the certificate signature. +func (c Certificate) GetFingerprints() ([]DTLSFingerprint, error) { + fingerprintAlgorithms := []crypto.Hash{crypto.SHA256} + res := make([]DTLSFingerprint, len(fingerprintAlgorithms)) + + i := 0 + for _, algo := range fingerprintAlgorithms { + name, err := fingerprint.StringFromHash(algo) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err) + } + value, err := fingerprint.Fingerprint(c.x509Cert, algo) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err) + } + res[i] = DTLSFingerprint{ + Algorithm: name, + Value: value, + } + } + + return res[:i+1], nil +} + +// GenerateCertificate causes the creation of an X.509 certificate and +// corresponding private key. +func GenerateCertificate(secretKey crypto.PrivateKey) (*Certificate, error) { + origin := make([]byte, 16) + /* #nosec */ + if _, err := rand.Read(origin); err != nil { + return nil, &rtcerr.UnknownError{Err: err} + } + + // Max random value, a 130-bits integer, i.e 2^130 - 1 + maxBigInt := new(big.Int) + /* #nosec */ + maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1)) + /* #nosec */ + serialNumber, err := rand.Int(rand.Reader, maxBigInt) + if err != nil { + return nil, &rtcerr.UnknownError{Err: err} + } + + return NewCertificate(secretKey, x509.Certificate{ + ExtKeyUsage: []x509.ExtKeyUsage{ + x509.ExtKeyUsageClientAuth, + x509.ExtKeyUsageServerAuth, + }, + BasicConstraintsValid: true, + NotBefore: time.Now(), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + NotAfter: time.Now().AddDate(0, 1, 0), + SerialNumber: serialNumber, + Version: 2, + Subject: pkix.Name{CommonName: hex.EncodeToString(origin)}, + IsCA: true, + }) +} + +// CertificateFromX509 creates a new WebRTC Certificate from a given PrivateKey and Certificate +// +// This can be used if you want to share a certificate across multiple PeerConnections +func CertificateFromX509(privateKey crypto.PrivateKey, certificate *x509.Certificate) Certificate { + return Certificate{privateKey, certificate, fmt.Sprintf("certificate-%d", time.Now().UnixNano())} +} + +func (c Certificate) collectStats(report *statsReportCollector) error { + report.Collecting() + + fingerPrintAlgo, err := c.GetFingerprints() + if err != nil { + return err + } + + base64Certificate := base64.RawURLEncoding.EncodeToString(c.x509Cert.Raw) + + stats := CertificateStats{ + Timestamp: statsTimestampFrom(time.Now()), + Type: StatsTypeCertificate, + ID: c.statsID, + Fingerprint: fingerPrintAlgo[0].Value, + FingerprintAlgorithm: fingerPrintAlgo[0].Algorithm, + Base64Certificate: base64Certificate, + IssuerCertificateID: c.x509Cert.Issuer.String(), + } + + report.Collect(stats.ID, stats) + return nil +} |