diff options
Diffstat (limited to 'vendor/github.com/pion/dtls/v2/crypto.go')
-rw-r--r-- | vendor/github.com/pion/dtls/v2/crypto.go | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/vendor/github.com/pion/dtls/v2/crypto.go b/vendor/github.com/pion/dtls/v2/crypto.go new file mode 100644 index 0000000..768ee47 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/crypto.go @@ -0,0 +1,221 @@ +package dtls + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/binary" + "math/big" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/hash" +) + +type ecdsaSignature struct { + R, S *big.Int +} + +func valueKeyMessage(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve) []byte { + serverECDHParams := make([]byte, 4) + serverECDHParams[0] = 3 // named curve + binary.BigEndian.PutUint16(serverECDHParams[1:], uint16(namedCurve)) + serverECDHParams[3] = byte(len(publicKey)) + + plaintext := []byte{} + plaintext = append(plaintext, clientRandom...) + plaintext = append(plaintext, serverRandom...) + plaintext = append(plaintext, serverECDHParams...) + plaintext = append(plaintext, publicKey...) + + return plaintext +} + +// If the client provided a "signature_algorithms" extension, then all +// certificates provided by the server MUST be signed by a +// hash/signature algorithm pair that appears in that extension +// +// https://tools.ietf.org/html/rfc5246#section-7.4.2 +func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) { + msg := valueKeyMessage(clientRandom, serverRandom, publicKey, namedCurve) + switch p := privateKey.(type) { + case ed25519.PrivateKey: + // https://crypto.stackexchange.com/a/55483 + return p.Sign(rand.Reader, msg, crypto.Hash(0)) + case *ecdsa.PrivateKey: + hashed := hashAlgorithm.Digest(msg) + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + case *rsa.PrivateKey: + hashed := hashAlgorithm.Digest(msg) + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + } + + return nil, errKeySignatureGenerateUnimplemented +} + +func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hash.Algorithm, rawCertificates [][]byte) error { //nolint:dupl + if len(rawCertificates) == 0 { + return errLengthMismatch + } + certificate, err := x509.ParseCertificate(rawCertificates[0]) + if err != nil { + return err + } + + switch p := certificate.PublicKey.(type) { + case ed25519.PublicKey: + if ok := ed25519.Verify(p, message, remoteKeySignature); !ok { + return errKeySignatureMismatch + } + return nil + case *ecdsa.PublicKey: + ecdsaSig := &ecdsaSignature{} + if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errInvalidECDSASignature + } + hashed := hashAlgorithm.Digest(message) + if !ecdsa.Verify(p, hashed, ecdsaSig.R, ecdsaSig.S) { + return errKeySignatureMismatch + } + return nil + case *rsa.PublicKey: + switch certificate.SignatureAlgorithm { + case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA: + hashed := hashAlgorithm.Digest(message) + return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature) + default: + return errKeySignatureVerifyUnimplemented + } + } + + return errKeySignatureVerifyUnimplemented +} + +// If the server has sent a CertificateRequest message, the client MUST send the Certificate +// message. The ClientKeyExchange message is now sent, and the content +// of that message will depend on the public key algorithm selected +// between the ClientHello and the ServerHello. If the client has sent +// a certificate with signing ability, a digitally-signed +// CertificateVerify message is sent to explicitly verify possession of +// the private key in the certificate. +// https://tools.ietf.org/html/rfc5246#section-7.3 +func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) { + h := sha256.New() + if _, err := h.Write(handshakeBodies); err != nil { + return nil, err + } + hashed := h.Sum(nil) + + switch p := privateKey.(type) { + case ed25519.PrivateKey: + // https://crypto.stackexchange.com/a/55483 + return p.Sign(rand.Reader, hashed, crypto.Hash(0)) + case *ecdsa.PrivateKey: + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + case *rsa.PrivateKey: + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + } + + return nil, errInvalidSignatureAlgorithm +} + +func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hash.Algorithm, remoteKeySignature []byte, rawCertificates [][]byte) error { //nolint:dupl + if len(rawCertificates) == 0 { + return errLengthMismatch + } + certificate, err := x509.ParseCertificate(rawCertificates[0]) + if err != nil { + return err + } + + switch p := certificate.PublicKey.(type) { + case ed25519.PublicKey: + if ok := ed25519.Verify(p, handshakeBodies, remoteKeySignature); !ok { + return errKeySignatureMismatch + } + return nil + case *ecdsa.PublicKey: + ecdsaSig := &ecdsaSignature{} + if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errInvalidECDSASignature + } + hash := hashAlgorithm.Digest(handshakeBodies) + if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) { + return errKeySignatureMismatch + } + return nil + case *rsa.PublicKey: + switch certificate.SignatureAlgorithm { + case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA: + hash := hashAlgorithm.Digest(handshakeBodies) + return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hash, remoteKeySignature) + default: + return errKeySignatureVerifyUnimplemented + } + } + + return errKeySignatureVerifyUnimplemented +} + +func loadCerts(rawCertificates [][]byte) ([]*x509.Certificate, error) { + if len(rawCertificates) == 0 { + return nil, errLengthMismatch + } + + certs := make([]*x509.Certificate, 0, len(rawCertificates)) + for _, rawCert := range rawCertificates { + cert, err := x509.ParseCertificate(rawCert) + if err != nil { + return nil, err + } + certs = append(certs, cert) + } + return certs, nil +} + +func verifyClientCert(rawCertificates [][]byte, roots *x509.CertPool) (chains [][]*x509.Certificate, err error) { + certificate, err := loadCerts(rawCertificates) + if err != nil { + return nil, err + } + intermediateCAPool := x509.NewCertPool() + for _, cert := range certificate[1:] { + intermediateCAPool.AddCert(cert) + } + opts := x509.VerifyOptions{ + Roots: roots, + CurrentTime: time.Now(), + Intermediates: intermediateCAPool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + return certificate[0].Verify(opts) +} + +func verifyServerCert(rawCertificates [][]byte, roots *x509.CertPool, serverName string) (chains [][]*x509.Certificate, err error) { + certificate, err := loadCerts(rawCertificates) + if err != nil { + return nil, err + } + intermediateCAPool := x509.NewCertPool() + for _, cert := range certificate[1:] { + intermediateCAPool.AddCert(cert) + } + opts := x509.VerifyOptions{ + Roots: roots, + CurrentTime: time.Now(), + DNSName: serverName, + Intermediates: intermediateCAPool, + } + return certificate[0].Verify(opts) +} |