summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/webrtc/v3/certificate.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pion/webrtc/v3/certificate.go')
-rw-r--r--vendor/github.com/pion/webrtc/v3/certificate.go185
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
+}