Repository: pion/dtls
Pull request #339 (2021-01-25 05:10:39)

Expose CipherSuite
Also change `IsPSK` to `AuthenticationType` to allow users to choose
between PSK, Certificate and Anonymous authentication. In a follow up
commit users will be able to pass their own ciphers.

Relates to #337

----

Expose CustomCipherSuites

Allow a user to implement their own CipherSuite if they implement the
interface.

Relates to #337


codecov[bot]: # [Codecov](https://codecov.io/gh/pion/dtls/pull/339?src=pr&el=h1) Report
> Merging [#339](https://codecov.io/gh/pion/dtls/pull/339?src=pr&el=desc) (d2222bd) into [master](https://codecov.io/gh/pion/dtls/commit/3b40dd9e436961d2830695354a9f946858d79ac4?el=desc) (3b40dd9) will **decrease** coverage by `0.04%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/pion/dtls/pull/339/graphs/tree.svg?width=650&height=150&src=pr&token=bcCB2jB88I)](https://codecov.io/gh/pion/dtls/pull/339?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #339      +/-   ##
==========================================
- Coverage   66.83%   66.79%   -0.05%     
==========================================
  Files          64       63       -1     
  Lines        3148     3147       -1     
==========================================
- Hits         2104     2102       -2     
- Misses        822      823       +1     
  Partials      222      222              
```

| Flag | Coverage Δ | |
|---|---|---|
| go | `66.79% <100.00%> (-0.07%)` | :arrow_down: |
| wasm | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/pion/dtls/pull/339?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [cipher\_suite\_go114.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-Y2lwaGVyX3N1aXRlX2dvMTE0Lmdv) | `100.00% <ø> (ø)` | |
| [handshaker.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-aGFuZHNoYWtlci5nbw==) | `69.34% <ø> (ø)` | |
| [state.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-c3RhdGUuZ28=) | `89.47% <ø> (ø)` | |
| [cipher\_suite.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-Y2lwaGVyX3N1aXRlLmdv) | `94.87% <100.00%> (ø)` | |
| [flight0handler.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-ZmxpZ2h0MGhhbmRsZXIuZ28=) | `76.00% <100.00%> (ø)` | |
| [flight3handler.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-ZmxpZ2h0M2hhbmRsZXIuZ28=) | `88.23% <100.00%> (ø)` | |
| [flight4handler.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-ZmxpZ2h0NGhhbmRsZXIuZ28=) | `80.46% <100.00%> (ø)` | |
| [util.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-dXRpbC5nbw==) | `100.00% <100.00%> (ø)` | |
| [internal/net/dpipe/dpipe.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbmV0L2RwaXBlL2RwaXBlLmdv) | `91.89% <0.00%> (-2.71%)` | :arrow_down: |
| [errors\_noerrno.go](https://codecov.io/gh/pion/dtls/pull/339/diff?src=pr&el=tree#diff-ZXJyb3JzX25vZXJybm8uZ28=) | | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/dtls/pull/339?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pion/dtls/pull/339?src=pr&el=footer). Last update [3b40dd9...d2222bd](https://codecov.io/gh/pion/dtls/pull/339?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Sean-Der: Ok this is the last one before anonymous!

@jkralik what command do you run to test with openssl? I can get it working with that, and will open a subsequent PR and we can resolve :) 
jkralik: > 
> 
> Ok this is the last one before anonymous!
> 
> @jkralik what command do you run to test with openssl? I can get it working with that, and will open a subsequent PR and we can resolve :)

@Sean-Der For server:
-  For server `openssl s_server -dtls -cipher "ADH-AES128-SHA:ADH-AES128-SHA256:ADH-AES256-SHA256:@SECLEVEL=0" -state -accept localhost:14000 -nocert -msg`
- For client `openssl s_client -dtls -cipher "ADH-AES128-SHA:ADH-AES128-SHA256:ADH-AES256-SHA256:@SECLEVEL=0" -state -connect localhost:14000 -msg`

Sean-Der: @jkralik @daenney Anything in this PR I can/should change?

----

@jkralik Would you mind helping me with the final PR? I can't get your existing branch to work, and am a bit stuck :( 

If I do `openssl s_client -dtls -cipher "AECDH-AES128-SHA:@SECLEVEL=0" -state -connect 127.0.0.1:4444 -msg` I end up with 

```
goroutine 18 [running]:
github.com/pion/dtls/v2.(*CryptoCBC).Decrypt(0xc000058a80, 0xc0000b2048, 0x4d, 0x1fb8, 0x40ea58, 0x20, 0x5f6300, 0x1, 0xc00000eda0)
        /home/sean/go/src/github.com/pion/dtls/crypto_cbc.go:123 +0x672
github.com/pion/dtls/v2.(*CipherSuiteTLSEcdhAnonWithAes128CbcSha256).Decrypt(0xc00000eb20, 0xc0000b2048, 0x4d, 0x1fb8, 0x2, 0xc00000ed80, 0x1, 0x2, 0xe1060)
        /home/sean/go/src/github.com/pion/dtls/cipher_suite_tls_ecdh_anon_with_aes_128_cbc_sha256.go:95 +0xa9
github.com/pion/dtls/v2.(*Conn).handleIncomingPacket(0xc0000aa000, 0xc0000b2048, 0x4d, 0x1fb8, 0xc000058a00, 0x0, 0x0, 0x0, 0x0)
        /home/sean/go/src/github.com/pion/dtls/conn.go:726 +0x557
github.com/pion/dtls/v2.(*Conn).handleQueuedPackets(0xc0000aa000, 0x65a060, 0xc00009e140, 0x40, 0xc00001a520)
        /home/sean/go/src/github.com/pion/dtls/conn.go:655 +0xbb
github.com/pion/dtls/v2.flight4Parse(0x65a060, 0xc00009e140, 0x65a720, 0xc0000aa000, 0xc0000aa040, 0xc0000940a0, 0xc0000ac000, 0x94, 0x765520, 0x611100, ...)
        /home/sean/go/src/github.com/pion/dtls/flight4handler.go:121 +0x4ef
github.com/pion/dtls/v2.(*handshakeFSM).wait(0xc00009a320, 0x65a060, 0xc00009e140, 0x65a720, 0xc0000aa000, 0x3, 0x0, 0x0)
        /home/sean/go/src/github.com/pion/dtls/handshaker.go:249 +0x249
github.com/pion/dtls/v2.(*handshakeFSM).Run(0xc00009a320, 0x65a060, 0xc00009e140, 0x65a720, 0xc0000aa000, 0x2001, 0x0, 0x0)
        /home/sean/go/src/github.com/pion/dtls/handshaker.go:159 +0x417
github.com/pion/dtls/v2.(*Conn).handshake.func2(0xc0000aa000, 0x65a060, 0xc00009e140, 0x203001, 0xc00009c120)
        /home/sean/go/src/github.com/pion/dtls/conn.go:858 +0x9c
created by github.com/pion/dtls/v2.(*Conn).handshake
        /home/sean/go/src/github.com/pion/dtls/conn.go:856 +0x2ab
```
Sean-Der: I got it 100% working! Here is an example client and server using my branch. https://gist.github.com/Sean-Der/1e4192b37c8a9ef5cf18d6272d22e503

Would you mind approving this PR? I am gonna open up the other PR that contains the changes needed for anonymous ciphers. The other PR is much more sensitive so will need a lot more scrutiny. 

Patch:
diff --git a/cipher_suite.go b/cipher_suite.go
index b682a9c..550fb2e 100644
--- a/cipher_suite.go
+++ b/cipher_suite.go
@@ -32,18 +32,38 @@ const (
 	TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:golint,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
+)
+
 var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14
 
-type cipherSuite interface {
+// 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
-	IsPSK() bool
-	IsInitialized() bool
 
-	// Generate the internal encryption state
+	// AuthenticationType controls what authentication method is using during the handshake
+	AuthenticationType() CipherSuiteAuthenticationType
+
+	// 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)
@@ -55,7 +75,7 @@ type cipherSuite interface {
 // 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)
+	suite := cipherSuiteForID(id, nil)
 	if suite != nil {
 		return suite.String()
 	}
@@ -65,7 +85,7 @@ func CipherSuiteName(id CipherSuiteID) string {
 // 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) cipherSuite {
+func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) CipherSuite {
 	switch id { //nolint:exhaustive
 	case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
 		return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm()
@@ -88,12 +108,21 @@ func cipherSuiteForID(id CipherSuiteID) cipherSuite {
 	case TLS_PSK_WITH_AES_128_CBC_SHA256:
 		return &ciphersuite.TLSPskWithAes128CbcSha256{}
 	}
+
+	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{
+func defaultCipherSuites() []CipherSuite {
+	return []CipherSuite{
 		&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
 		&ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
 		&ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
@@ -101,8 +130,8 @@ func defaultCipherSuites() []cipherSuite {
 	}
 }
 
-func allCipherSuites() []cipherSuite {
-	return []cipherSuite{
+func allCipherSuites() []CipherSuite {
+	return []CipherSuite{
 		ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(),
 		ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(),
 		&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
@@ -115,7 +144,7 @@ func allCipherSuites() []cipherSuite {
 	}
 }
 
-func cipherSuiteIDs(cipherSuites []cipherSuite) []uint16 {
+func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 {
 	rtrn := []uint16{}
 	for _, c := range cipherSuites {
 		rtrn = append(rtrn, uint16(c.ID()))
@@ -123,15 +152,15 @@ func cipherSuiteIDs(cipherSuites []cipherSuite) []uint16 {
 	return rtrn
 }
 
-func parseCipherSuites(userSelectedSuites []CipherSuiteID, includeCertificateSuites, includePSKSuites bool) ([]cipherSuite, error) {
+func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) {
 	if !includeCertificateSuites && !includePSKSuites {
 		return nil, errNoAvailableCipherSuites
 	}
 
-	cipherSuitesForIDs := func(ids []CipherSuiteID) ([]cipherSuite, error) {
-		cipherSuites := []cipherSuite{}
+	cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) {
+		cipherSuites := []CipherSuite{}
 		for _, id := range ids {
-			c := cipherSuiteForID(id)
+			c := cipherSuiteForID(id, nil)
 			if c == nil {
 				return nil, &invalidCipherSuite{id}
 			}
@@ -141,11 +170,11 @@ func parseCipherSuites(userSelectedSuites []CipherSuiteID, includeCertificateSui
 	}
 
 	var (
-		cipherSuites []cipherSuite
+		cipherSuites []CipherSuite
 		err          error
 		i            int
 	)
-	if len(userSelectedSuites) != 0 {
+	if userSelectedSuites != nil {
 		cipherSuites, err = cipherSuitesForIDs(userSelectedSuites)
 		if err != nil {
 			return nil, err
@@ -154,12 +183,17 @@ func parseCipherSuites(userSelectedSuites []CipherSuiteID, includeCertificateSui
 		cipherSuites = defaultCipherSuites()
 	}
 
+	// Put CustomCipherSuites before ID selected suites
+	if customCipherSuites != nil {
+		cipherSuites = append(customCipherSuites(), cipherSuites...)
+	}
+
 	var foundCertificateSuite, foundPSKSuite bool
 	for _, c := range cipherSuites {
 		switch {
-		case includeCertificateSuites && !c.IsPSK():
+		case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
 			foundCertificateSuite = true
-		case includePSKSuites && c.IsPSK():
+		case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey:
 			foundPSKSuite = true
 		default:
 			continue
diff --git a/cipher_suite_go114.go b/cipher_suite_go114.go
index ceb5f41..7bba16e 100644
--- a/cipher_suite_go114.go
+++ b/cipher_suite_go114.go
@@ -11,7 +11,7 @@ import (
 const VersionDTLS12 = 0xfefd
 
 // Convert from our cipherSuite interface to a tls.CipherSuite struct
-func toTLSCipherSuite(c cipherSuite) *tls.CipherSuite {
+func toTLSCipherSuite(c CipherSuite) *tls.CipherSuite {
 	return &tls.CipherSuite{
 		ID:                uint16(c.ID()),
 		Name:              c.String(),
diff --git a/config.go b/config.go
index 0807ed6..f0160d6 100644
--- a/config.go
+++ b/config.go
@@ -23,6 +23,11 @@ type Config struct {
 	// If CipherSuites is nil, a default list is used
 	CipherSuites []CipherSuiteID
 
+	// CustomCipherSuites is a list of CipherSuites that can be
+	// provided by the user. This allow users to user Ciphers that are reserved
+	// for private usage.
+	CustomCipherSuites func() []CipherSuite
+
 	// SignatureSchemes contains the signature and hash schemes that the peer requests to verify.
 	SignatureSchemes []tls.SignatureScheme
 
@@ -172,6 +177,6 @@ func validateConfig(config *Config) error {
 		}
 	}
 
-	_, err := parseCipherSuites(config.CipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil)
+	_, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil)
 	return err
 }
diff --git a/conn.go b/conn.go
index f0aea27..76f0809 100644
--- a/conn.go
+++ b/conn.go
@@ -87,7 +87,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient
 		return nil, errNilNextConn
 	}
 
-	cipherSuites, err := parseCipherSuites(config.CipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil)
+	cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil)
 	if err != nil {
 		return nil, err
 	}
@@ -173,6 +173,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient
 		verifyPeerCertificate:       config.VerifyPeerCertificate,
 		rootCAs:                     config.RootCAs,
 		clientCAs:                   config.ClientCAs,
+		customCipherSuites:          config.CustomCipherSuites,
 		retransmitInterval:          workerInterval,
 		log:                         logger,
 		initialEpoch:                0,
diff --git a/flight0handler.go b/flight0handler.go
index 6e45221..949d7c0 100644
--- a/flight0handler.go
+++ b/flight0handler.go
@@ -34,9 +34,9 @@ func flight0Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 
 	state.remoteRandom = clientHello.Random
 
-	cipherSuites := []cipherSuite{}
+	cipherSuites := []CipherSuite{}
 	for _, id := range clientHello.CipherSuiteIDs {
-		if c := cipherSuiteForID(CipherSuiteID(id)); c != nil {
+		if c := cipherSuiteForID(CipherSuiteID(id), cfg.customCipherSuites); c != nil {
 			cipherSuites = append(cipherSuites, c)
 		}
 	}
diff --git a/flight3handler.go b/flight3handler.go
index 492c9c6..8a1aff4 100644
--- a/flight3handler.go
+++ b/flight3handler.go
@@ -78,12 +78,12 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension
 		}
 
-		remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*h.CipherSuiteID))
+		remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*h.CipherSuiteID), cfg.customCipherSuites)
 		if remoteCipherSuite == nil {
 			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
 		}
 
-		selectedCipherSuite, ok := findMatchingCipherSuite([]cipherSuite{remoteCipherSuite}, cfg.localCipherSuites)
+		selectedCipherSuite, ok := findMatchingCipherSuite([]CipherSuite{remoteCipherSuite}, cfg.localCipherSuites)
 		if !ok {
 			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
 		}
diff --git a/flight4handler.go b/flight4handler.go
index 144d425..78b6143 100644
--- a/flight4handler.go
+++ b/flight4handler.go
@@ -90,7 +90,7 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 
 		var err error
 		var preMasterSecret []byte
-		if state.cipherSuite.IsPSK() {
+		if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey {
 			var psk []byte
 			if psk, err = cfg.localPSKCallback(clientKeyExchange.IdentityHint); err != nil {
 				return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
@@ -181,7 +181,7 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 			ProtectionProfiles: []SRTPProtectionProfile{state.srtpProtectionProfile},
 		})
 	}
-	if !state.cipherSuite.IsPSK() {
+	if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
 		extensions = append(extensions, []extension.Extension{
 			&extension.SupportedEllipticCurves{
 				EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384},
@@ -212,7 +212,7 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 		},
 	})
 
-	if !state.cipherSuite.IsPSK() {
+	if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
 		certificate, err := cfg.getCertificate(cfg.serverName)
 		if err != nil {
 			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
diff --git a/handshaker.go b/handshaker.go
index d8e9e5c..4a93db8 100644
--- a/handshaker.go
+++ b/handshaker.go
@@ -88,7 +88,7 @@ type handshakeFSM struct {
 type handshakeConfig struct {
 	localPSKCallback            PSKCallback
 	localPSKIdentityHint        []byte
-	localCipherSuites           []cipherSuite             // Available CipherSuites
+	localCipherSuites           []CipherSuite             // Available CipherSuites
 	localSignatureSchemes       []signaturehash.Algorithm // Available signature schemes
 	extendedMasterSecret        ExtendedMasterSecretType  // Policy for the Extended Master Support extension
 	localSRTPProtectionProfiles []SRTPProtectionProfile   // Available SRTPProtectionProfiles, if empty no SRTP support
@@ -101,6 +101,7 @@ type handshakeConfig struct {
 	rootCAs                     *x509.CertPool
 	clientCAs                   *x509.CertPool
 	retransmitInterval          time.Duration
+	customCipherSuites          func() []CipherSuite
 
 	onFlightState func(flightVal, handshakeState)
 	log           logging.LeveledLogger
diff --git a/internal/ciphersuite/aes_128_ccm.go b/internal/ciphersuite/aes_128_ccm.go
index 6e0c3b4..dcc5379 100644
--- a/internal/ciphersuite/aes_128_ccm.go
+++ b/internal/ciphersuite/aes_128_ccm.go
@@ -49,9 +49,12 @@ func (c *Aes128Ccm) HashFunc() func() hash.Hash {
 	return sha256.New
 }
 
-// IsPSK returns if the CipherSuite requires a pre-shared key
-func (c *Aes128Ccm) IsPSK() bool {
-	return c.psk
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *Aes128Ccm) AuthenticationType() AuthenticationType {
+	if c.psk {
+		return AuthenticationTypePreSharedKey
+	}
+	return AuthenticationTypeCertificate
 }
 
 // IsInitialized returns if the CipherSuite has keying material and can
diff --git a/internal/ciphersuite/ciphersuite.go b/internal/ciphersuite/ciphersuite.go
index c28c62b..3a4fb43 100644
--- a/internal/ciphersuite/ciphersuite.go
+++ b/internal/ciphersuite/ciphersuite.go
@@ -59,3 +59,13 @@ const (
 	TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:golint,stylecheck
 	TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:golint,stylecheck
 )
+
+// AuthenticationType controls what authentication method is using during the handshake
+type AuthenticationType int
+
+// AuthenticationType Enums
+const (
+	AuthenticationTypeCertificate AuthenticationType = iota + 1
+	AuthenticationTypePreSharedKey
+	AuthenticationTypeAnonymous
+)
diff --git a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
index 60f1570..b491320 100644
--- a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
+++ b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
@@ -36,9 +36,9 @@ func (c *TLSEcdheEcdsaWithAes128GcmSha256) HashFunc() func() hash.Hash {
 	return sha256.New
 }
 
-// IsPSK returns if the CipherSuite requires a pre-shared key
-func (c *TLSEcdheEcdsaWithAes128GcmSha256) IsPSK() bool {
-	return false
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) AuthenticationType() AuthenticationType {
+	return AuthenticationTypeCertificate
 }
 
 // IsInitialized returns if the CipherSuite has keying material and can
diff --git a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
index 53c22e9..f7a33ad 100644
--- a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
+++ b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
@@ -37,9 +37,9 @@ func (c *TLSEcdheEcdsaWithAes256CbcSha) HashFunc() func() hash.Hash {
 	return sha256.New
 }
 
-// IsPSK returns if the CipherSuite requires a pre-shared key
-func (c *TLSEcdheEcdsaWithAes256CbcSha) IsPSK() bool {
-	return false
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdheEcdsaWithAes256CbcSha) AuthenticationType() AuthenticationType {
+	return AuthenticationTypeCertificate
 }
 
 // IsInitialized returns if the CipherSuite has keying material and can
diff --git a/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go b/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
index c1a4e38..43e5e38 100644
--- a/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
+++ b/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
@@ -36,9 +36,9 @@ func (c *TLSPskWithAes128CbcSha256) HashFunc() func() hash.Hash {
 	return sha256.New
 }
 
-// IsPSK returns if the CipherSuite requires a pre-shared key
-func (c *TLSPskWithAes128CbcSha256) IsPSK() bool {
-	return true
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSPskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
+	return AuthenticationTypePreSharedKey
 }
 
 // IsInitialized returns if the CipherSuite has keying material and can
diff --git a/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go b/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
index 44ac6c3..5f10335 100644
--- a/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
+++ b/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
@@ -21,7 +21,7 @@ func (c *TLSPskWithAes128GcmSha256) String() string {
 	return "TLS_PSK_WITH_AES_128_GCM_SHA256"
 }
 
-// IsPSK returns if the CipherSuite requires a pre-shared key
-func (c *TLSPskWithAes128GcmSha256) IsPSK() bool {
-	return true
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSPskWithAes128GcmSha256) AuthenticationType() AuthenticationType {
+	return AuthenticationTypePreSharedKey
 }
diff --git a/state.go b/state.go
index 8ac4d22..53fca6a 100644
--- a/state.go
+++ b/state.go
@@ -17,7 +17,7 @@ type State struct {
 	localSequenceNumber       []uint64 // uint48
 	localRandom, remoteRandom handshake.Random
 	masterSecret              []byte
-	cipherSuite               cipherSuite // nil if a cipherSuite hasn't been chosen
+	cipherSuite               CipherSuite // nil if a cipherSuite hasn't been chosen
 
 	srtpProtectionProfile SRTPProtectionProfile // Negotiated SRTPProtectionProfile
 	PeerCertificates      [][]byte
@@ -111,7 +111,7 @@ func (s *State) deserialize(serialized serializedState) {
 	s.masterSecret = serialized.MasterSecret
 
 	// Set cipher suite
-	s.cipherSuite = cipherSuiteForID(CipherSuiteID(serialized.CipherSuiteID))
+	s.cipherSuite = cipherSuiteForID(CipherSuiteID(serialized.CipherSuiteID), nil)
 
 	atomic.StoreUint64(&s.localSequenceNumber[epoch], serialized.SequenceNumber)
 	s.srtpProtectionProfile = SRTPProtectionProfile(serialized.SRTPProtectionProfile)
diff --git a/util.go b/util.go
index cdc0676..745182d 100644
--- a/util.go
+++ b/util.go
@@ -11,7 +11,7 @@ func findMatchingSRTPProfile(a, b []SRTPProtectionProfile) (SRTPProtectionProfil
 	return 0, false
 }
 
-func findMatchingCipherSuite(a, b []cipherSuite) (cipherSuite, bool) { //nolint
+func findMatchingCipherSuite(a, b []CipherSuite) (CipherSuite, bool) { //nolint
 	for _, aSuite := range a {
 		for _, bSuite := range b {
 			if aSuite.ID() == bSuite.ID() {


Test patch:
diff --git a/cipher_suite_test.go b/cipher_suite_test.go
index 825d4b9..a256d6f 100644
--- a/cipher_suite_test.go
+++ b/cipher_suite_test.go
@@ -1,7 +1,13 @@
 package dtls
 
 import (
+	"context"
 	"testing"
+	"time"
+
+	"github.com/pion/dtls/v2/internal/ciphersuite"
+	"github.com/pion/dtls/v2/internal/net/dpipe"
+	"github.com/pion/transport/test"
 )
 
 func TestCipherSuiteName(t *testing.T) {
@@ -27,3 +33,57 @@ func TestAllCipherSuites(t *testing.T) {
 		t.Fatal()
 	}
 }
+
+// CustomCipher that is just used to test CustomerCiphers and Anonymous Authentication
+type testCustomCipherSuite struct {
+	ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256
+}
+
+func (t *testCustomCipherSuite) ID() CipherSuiteID {
+	return 0xFFFF
+}
+
+// Assert that two connections that pass in a CipherSuite with a CustomID works
+func TestCustomCipherSuite(t *testing.T) {
+	type result struct {
+		c   *Conn
+		err error
+	}
+
+	// Check for leaking routines
+	report := test.CheckRoutines(t)
+	defer report()
+
+	t.Run("Custom ID", func(t *testing.T) {
+		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+		defer cancel()
+
+		ca, cb := dpipe.Pipe()
+		c := make(chan result)
+
+		go func() {
+			client, err := testClient(ctx, ca, &Config{
+				CipherSuites:       []CipherSuiteID{},
+				CustomCipherSuites: func() []CipherSuite { return []CipherSuite{&testCustomCipherSuite{}} },
+			}, true)
+			c <- result{client, err}
+		}()
+
+		server, err := testServer(ctx, cb, &Config{
+			CipherSuites:       []CipherSuiteID{},
+			CustomCipherSuites: func() []CipherSuite { return []CipherSuite{&testCustomCipherSuite{}} },
+		}, true)
+
+		if err != nil {
+			t.Error(err)
+		} else {
+			_ = server.Close()
+		}
+
+		if res := <-c; res.err != nil {
+			t.Error(res.err)
+		} else {
+			_ = res.c.Close()
+		}
+	})
+}
diff --git a/conn_test.go b/conn_test.go
index d1512ba..3532f95 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -1428,7 +1428,7 @@ func TestServerTimeout(t *testing.T) {
 	var rand [28]byte
 	random := handshake.Random{GMTUnixTime: time.Unix(500, 0), RandomBytes: rand}
 
-	cipherSuites := []cipherSuite{
+	cipherSuites := []CipherSuite{
 		&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
 		&ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
 	}
diff --git a/handshaker_test.go b/handshaker_test.go
index ebb2714..c9c4c79 100644
--- a/handshaker_test.go
+++ b/handshaker_test.go
@@ -26,7 +26,7 @@ func TestHandshaker(t *testing.T) {
 	loggerFactory := logging.NewDefaultLoggerFactory()
 	logger := loggerFactory.NewLogger("dtls")
 
-	cipherSuites, err := parseCipherSuites(nil, true, false)
+	cipherSuites, err := parseCipherSuites(nil, nil, true, false)
 	if err != nil {
 		t.Fatal(err)
 	}