github.com/pion/dtls/v2@v2.2.12/cipher_suite.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package dtls
     5  
     6  import (
     7  	"crypto/ecdsa"
     8  	"crypto/ed25519"
     9  	"crypto/rsa"
    10  	"crypto/tls"
    11  	"fmt"
    12  	"hash"
    13  
    14  	"github.com/pion/dtls/v2/internal/ciphersuite"
    15  	"github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
    16  	"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
    17  )
    18  
    19  // CipherSuiteID is an ID for our supported CipherSuites
    20  type CipherSuiteID = ciphersuite.ID
    21  
    22  // Supported Cipher Suites
    23  const (
    24  	// AES-128-CCM
    25  	TLS_ECDHE_ECDSA_WITH_AES_128_CCM   CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM   //nolint:revive,stylecheck
    26  	TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:revive,stylecheck
    27  
    28  	// AES-128-GCM-SHA256
    29  	TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
    30  	TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   //nolint:revive,stylecheck
    31  
    32  	TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck
    33  	TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384   CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384   //nolint:revive,stylecheck
    34  
    35  	// AES-256-CBC-SHA
    36  	TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck
    37  	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA   CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA   //nolint:revive,stylecheck
    38  
    39  	TLS_PSK_WITH_AES_128_CCM        CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM        //nolint:revive,stylecheck
    40  	TLS_PSK_WITH_AES_128_CCM_8      CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8      //nolint:revive,stylecheck
    41  	TLS_PSK_WITH_AES_256_CCM_8      CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_256_CCM_8      //nolint:revive,stylecheck
    42  	TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
    43  	TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck
    44  
    45  	TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck
    46  )
    47  
    48  // CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite
    49  type CipherSuiteAuthenticationType = ciphersuite.AuthenticationType
    50  
    51  // AuthenticationType Enums
    52  const (
    53  	CipherSuiteAuthenticationTypeCertificate  CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeCertificate
    54  	CipherSuiteAuthenticationTypePreSharedKey CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypePreSharedKey
    55  	CipherSuiteAuthenticationTypeAnonymous    CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeAnonymous
    56  )
    57  
    58  // CipherSuiteKeyExchangeAlgorithm controls what exchange algorithm is using during the handshake for a CipherSuite
    59  type CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithm
    60  
    61  // CipherSuiteKeyExchangeAlgorithm Bitmask
    62  const (
    63  	CipherSuiteKeyExchangeAlgorithmNone  CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmNone
    64  	CipherSuiteKeyExchangeAlgorithmPsk   CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmPsk
    65  	CipherSuiteKeyExchangeAlgorithmEcdhe CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmEcdhe
    66  )
    67  
    68  var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14
    69  
    70  // CipherSuite is an interface that all DTLS CipherSuites must satisfy
    71  type CipherSuite interface {
    72  	// String of CipherSuite, only used for logging
    73  	String() string
    74  
    75  	// ID of CipherSuite.
    76  	ID() CipherSuiteID
    77  
    78  	// What type of Certificate does this CipherSuite use
    79  	CertificateType() clientcertificate.Type
    80  
    81  	// What Hash function is used during verification
    82  	HashFunc() func() hash.Hash
    83  
    84  	// AuthenticationType controls what authentication method is using during the handshake
    85  	AuthenticationType() CipherSuiteAuthenticationType
    86  
    87  	// KeyExchangeAlgorithm controls what exchange algorithm is using during the handshake
    88  	KeyExchangeAlgorithm() CipherSuiteKeyExchangeAlgorithm
    89  
    90  	// ECC (Elliptic Curve Cryptography) determines whether ECC extesions will be send during handshake.
    91  	// https://datatracker.ietf.org/doc/html/rfc4492#page-10
    92  	ECC() bool
    93  
    94  	// Called when keying material has been generated, should initialize the internal cipher
    95  	Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error
    96  	IsInitialized() bool
    97  	Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
    98  	Decrypt(in []byte) ([]byte, error)
    99  }
   100  
   101  // CipherSuiteName provides the same functionality as tls.CipherSuiteName
   102  // that appeared first in Go 1.14.
   103  //
   104  // Our implementation differs slightly in that it takes in a CiperSuiteID,
   105  // like the rest of our library, instead of a uint16 like crypto/tls.
   106  func CipherSuiteName(id CipherSuiteID) string {
   107  	suite := cipherSuiteForID(id, nil)
   108  	if suite != nil {
   109  		return suite.String()
   110  	}
   111  	return fmt.Sprintf("0x%04X", uint16(id))
   112  }
   113  
   114  // Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
   115  // A cipherSuite is a specific combination of key agreement, cipher and MAC
   116  // function.
   117  func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) CipherSuite {
   118  	switch id { //nolint:exhaustive
   119  	case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
   120  		return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm()
   121  	case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
   122  		return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8()
   123  	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
   124  		return &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}
   125  	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
   126  		return &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}
   127  	case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
   128  		return &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}
   129  	case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
   130  		return &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}
   131  	case TLS_PSK_WITH_AES_128_CCM:
   132  		return ciphersuite.NewTLSPskWithAes128Ccm()
   133  	case TLS_PSK_WITH_AES_128_CCM_8:
   134  		return ciphersuite.NewTLSPskWithAes128Ccm8()
   135  	case TLS_PSK_WITH_AES_256_CCM_8:
   136  		return ciphersuite.NewTLSPskWithAes256Ccm8()
   137  	case TLS_PSK_WITH_AES_128_GCM_SHA256:
   138  		return &ciphersuite.TLSPskWithAes128GcmSha256{}
   139  	case TLS_PSK_WITH_AES_128_CBC_SHA256:
   140  		return &ciphersuite.TLSPskWithAes128CbcSha256{}
   141  	case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
   142  		return &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{}
   143  	case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
   144  		return &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{}
   145  	case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
   146  		return ciphersuite.NewTLSEcdhePskWithAes128CbcSha256()
   147  	}
   148  
   149  	if customCiphers != nil {
   150  		for _, c := range customCiphers() {
   151  			if c.ID() == id {
   152  				return c
   153  			}
   154  		}
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  // CipherSuites we support in order of preference
   161  func defaultCipherSuites() []CipherSuite {
   162  	return []CipherSuite{
   163  		&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
   164  		&ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
   165  		&ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
   166  		&ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
   167  		&ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
   168  		&ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
   169  	}
   170  }
   171  
   172  func allCipherSuites() []CipherSuite {
   173  	return []CipherSuite{
   174  		ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(),
   175  		ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(),
   176  		&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
   177  		&ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
   178  		&ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
   179  		&ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
   180  		ciphersuite.NewTLSPskWithAes128Ccm(),
   181  		ciphersuite.NewTLSPskWithAes128Ccm8(),
   182  		ciphersuite.NewTLSPskWithAes256Ccm8(),
   183  		&ciphersuite.TLSPskWithAes128GcmSha256{},
   184  		&ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
   185  		&ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
   186  	}
   187  }
   188  
   189  func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 {
   190  	rtrn := []uint16{}
   191  	for _, c := range cipherSuites {
   192  		rtrn = append(rtrn, uint16(c.ID()))
   193  	}
   194  	return rtrn
   195  }
   196  
   197  func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) {
   198  	cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) {
   199  		cipherSuites := []CipherSuite{}
   200  		for _, id := range ids {
   201  			c := cipherSuiteForID(id, nil)
   202  			if c == nil {
   203  				return nil, &invalidCipherSuiteError{id}
   204  			}
   205  			cipherSuites = append(cipherSuites, c)
   206  		}
   207  		return cipherSuites, nil
   208  	}
   209  
   210  	var (
   211  		cipherSuites []CipherSuite
   212  		err          error
   213  		i            int
   214  	)
   215  	if userSelectedSuites != nil {
   216  		cipherSuites, err = cipherSuitesForIDs(userSelectedSuites)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  	} else {
   221  		cipherSuites = defaultCipherSuites()
   222  	}
   223  
   224  	// Put CustomCipherSuites before ID selected suites
   225  	if customCipherSuites != nil {
   226  		cipherSuites = append(customCipherSuites(), cipherSuites...)
   227  	}
   228  
   229  	var foundCertificateSuite, foundPSKSuite, foundAnonymousSuite bool
   230  	for _, c := range cipherSuites {
   231  		switch {
   232  		case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
   233  			foundCertificateSuite = true
   234  		case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey:
   235  			foundPSKSuite = true
   236  		case c.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous:
   237  			foundAnonymousSuite = true
   238  		default:
   239  			continue
   240  		}
   241  		cipherSuites[i] = c
   242  		i++
   243  	}
   244  
   245  	switch {
   246  	case includeCertificateSuites && !foundCertificateSuite && !foundAnonymousSuite:
   247  		return nil, errNoAvailableCertificateCipherSuite
   248  	case includePSKSuites && !foundPSKSuite:
   249  		return nil, errNoAvailablePSKCipherSuite
   250  	case i == 0:
   251  		return nil, errNoAvailableCipherSuites
   252  	}
   253  
   254  	return cipherSuites[:i], nil
   255  }
   256  
   257  func filterCipherSuitesForCertificate(cert *tls.Certificate, cipherSuites []CipherSuite) []CipherSuite {
   258  	if cert == nil || cert.PrivateKey == nil {
   259  		return cipherSuites
   260  	}
   261  	var certType clientcertificate.Type
   262  	switch cert.PrivateKey.(type) {
   263  	case ed25519.PrivateKey, *ecdsa.PrivateKey:
   264  		certType = clientcertificate.ECDSASign
   265  	case *rsa.PrivateKey:
   266  		certType = clientcertificate.RSASign
   267  	}
   268  
   269  	filtered := []CipherSuite{}
   270  	for _, c := range cipherSuites {
   271  		if c.AuthenticationType() != CipherSuiteAuthenticationTypeCertificate || certType == c.CertificateType() {
   272  			filtered = append(filtered, c)
   273  		}
   274  	}
   275  	return filtered
   276  }