222 lines
6.8 KiB
Go
222 lines
6.8 KiB
Go
|
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)
|
||
|
}
|