// SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT package dtls import ( "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "crypto/tls" "fmt" "hash" "github.com/pion/dtls/v2/internal/ciphersuite" "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" "github.com/pion/dtls/v2/pkg/protocol/recordlayer" ) // CipherSuiteID is an ID for our supported CipherSuites type CipherSuiteID = ciphersuite.ID // Supported Cipher Suites const ( // AES-128-CCM TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM //nolint:revive,stylecheck TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:revive,stylecheck // AES-128-GCM-SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck // AES-256-CBC-SHA TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:revive,stylecheck TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:revive,stylecheck TLS_PSK_WITH_AES_256_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_256_CCM_8 //nolint:revive,stylecheck TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck ) // CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite type CipherSuiteAuthenticationType = ciphersuite.AuthenticationType // AuthenticationType Enums const ( CipherSuiteAuthenticationTypeCertificate CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeCertificate CipherSuiteAuthenticationTypePreSharedKey CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypePreSharedKey CipherSuiteAuthenticationTypeAnonymous CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeAnonymous ) // CipherSuiteKeyExchangeAlgorithm controls what exchange algorithm is using during the handshake for a CipherSuite type CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithm // CipherSuiteKeyExchangeAlgorithm Bitmask const ( CipherSuiteKeyExchangeAlgorithmNone CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmNone CipherSuiteKeyExchangeAlgorithmPsk CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmPsk CipherSuiteKeyExchangeAlgorithmEcdhe CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmEcdhe ) var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14 // CipherSuite is an interface that all DTLS CipherSuites must satisfy type CipherSuite interface { // String of CipherSuite, only used for logging String() string // ID of CipherSuite. ID() CipherSuiteID // What type of Certificate does this CipherSuite use CertificateType() clientcertificate.Type // What Hash function is used during verification HashFunc() func() hash.Hash // AuthenticationType controls what authentication method is using during the handshake AuthenticationType() CipherSuiteAuthenticationType // KeyExchangeAlgorithm controls what exchange algorithm is using during the handshake KeyExchangeAlgorithm() CipherSuiteKeyExchangeAlgorithm // ECC (Elliptic Curve Cryptography) determines whether ECC extesions will be send during handshake. // https://datatracker.ietf.org/doc/html/rfc4492#page-10 ECC() bool // Called when keying material has been generated, should initialize the internal cipher Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error IsInitialized() bool Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) Decrypt(in []byte) ([]byte, error) } // CipherSuiteName provides the same functionality as tls.CipherSuiteName // that appeared first in Go 1.14. // // Our implementation differs slightly in that it takes in a CiperSuiteID, // like the rest of our library, instead of a uint16 like crypto/tls. func CipherSuiteName(id CipherSuiteID) string { suite := cipherSuiteForID(id, nil) if suite != nil { return suite.String() } return fmt.Sprintf("0x%04X", uint16(id)) } // Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml // A cipherSuite is a specific combination of key agreement, cipher and MAC // function. func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) CipherSuite { switch id { //nolint:exhaustive case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm() case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8() case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: return &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{} case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: return &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{} case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: return &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{} case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: return &ciphersuite.TLSEcdheRsaWithAes256CbcSha{} case TLS_PSK_WITH_AES_128_CCM: return ciphersuite.NewTLSPskWithAes128Ccm() case TLS_PSK_WITH_AES_128_CCM_8: return ciphersuite.NewTLSPskWithAes128Ccm8() case TLS_PSK_WITH_AES_256_CCM_8: return ciphersuite.NewTLSPskWithAes256Ccm8() case TLS_PSK_WITH_AES_128_GCM_SHA256: return &ciphersuite.TLSPskWithAes128GcmSha256{} case TLS_PSK_WITH_AES_128_CBC_SHA256: return &ciphersuite.TLSPskWithAes128CbcSha256{} case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: return &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{} case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: return &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{} case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: return ciphersuite.NewTLSEcdhePskWithAes128CbcSha256() } if customCiphers != nil { for _, c := range customCiphers() { if c.ID() == id { return c } } } return nil } // CipherSuites we support in order of preference func defaultCipherSuites() []CipherSuite { return []CipherSuite{ &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}, &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}, &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}, &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{}, &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{}, } } func allCipherSuites() []CipherSuite { return []CipherSuite{ ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(), ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(), &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}, &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}, &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}, ciphersuite.NewTLSPskWithAes128Ccm(), ciphersuite.NewTLSPskWithAes128Ccm8(), ciphersuite.NewTLSPskWithAes256Ccm8(), &ciphersuite.TLSPskWithAes128GcmSha256{}, &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{}, &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{}, } } func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 { rtrn := []uint16{} for _, c := range cipherSuites { rtrn = append(rtrn, uint16(c.ID())) } return rtrn } func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) { cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) { cipherSuites := []CipherSuite{} for _, id := range ids { c := cipherSuiteForID(id, nil) if c == nil { return nil, &invalidCipherSuiteError{id} } cipherSuites = append(cipherSuites, c) } return cipherSuites, nil } var ( cipherSuites []CipherSuite err error i int ) if userSelectedSuites != nil { cipherSuites, err = cipherSuitesForIDs(userSelectedSuites) if err != nil { return nil, err } } else { cipherSuites = defaultCipherSuites() } // Put CustomCipherSuites before ID selected suites if customCipherSuites != nil { cipherSuites = append(customCipherSuites(), cipherSuites...) } var foundCertificateSuite, foundPSKSuite, foundAnonymousSuite bool for _, c := range cipherSuites { switch { case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate: foundCertificateSuite = true case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey: foundPSKSuite = true case c.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous: foundAnonymousSuite = true default: continue } cipherSuites[i] = c i++ } switch { case includeCertificateSuites && !foundCertificateSuite && !foundAnonymousSuite: return nil, errNoAvailableCertificateCipherSuite case includePSKSuites && !foundPSKSuite: return nil, errNoAvailablePSKCipherSuite case i == 0: return nil, errNoAvailableCipherSuites } return cipherSuites[:i], nil } func filterCipherSuitesForCertificate(cert *tls.Certificate, cipherSuites []CipherSuite) []CipherSuite { if cert == nil || cert.PrivateKey == nil { return cipherSuites } var certType clientcertificate.Type switch cert.PrivateKey.(type) { case ed25519.PrivateKey, *ecdsa.PrivateKey: certType = clientcertificate.ECDSASign case *rsa.PrivateKey: certType = clientcertificate.RSASign } filtered := []CipherSuite{} for _, c := range cipherSuites { if c.AuthenticationType() != CipherSuiteAuthenticationTypeCertificate || certType == c.CertificateType() { filtered = append(filtered, c) } } return filtered }