Repository: pion/dtls
Pull request #436 (2022-03-04 07:24:43)

Add cipher suite TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
#### Description

Extends for `TLS_ECDHE_PSK_*` ciphers + implementation TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256


codecov[bot]: # [Codecov](https://codecov.io/gh/pion/dtls/pull/436?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) Report
> Merging [#436](https://codecov.io/gh/pion/dtls/pull/436?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) (85c338e) into [master](https://codecov.io/gh/pion/dtls/commit/bfbe15ef34f393f864a8caacc26f5199eef8b236?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) (bfbe15e) will **decrease** coverage by `0.42%`.
> The diff coverage is `59.39%`.

[![Impacted file tree graph](https://codecov.io/gh/pion/dtls/pull/436/graphs/tree.svg?width=650&height=150&src=pr&token=bcCB2jB88I&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion)](https://codecov.io/gh/pion/dtls/pull/436?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion)

```diff
@@            Coverage Diff             @@
##           master     #436      +/-   ##
==========================================
- Coverage   76.03%   75.61%   -0.43%     
==========================================
  Files          94       95       +1     
  Lines        5400     5520     +120     
==========================================
+ Hits         4106     4174      +68     
- Misses        978     1026      +48     
- Partials      316      320       +4     
```

| Flag | Coverage Δ | |
|---|---|---|
| go | `75.64% <59.39%> (-0.43%)` | :arrow_down: |
| wasm | `66.45% <26.78%> (-0.47%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/pion/dtls/pull/436?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) | Coverage Δ | |
|---|---|---|
| [internal/ciphersuite/ciphersuite.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-aW50ZXJuYWwvY2lwaGVyc3VpdGUvY2lwaGVyc3VpdGUuZ28=) | `62.50% <0.00%> (-4.17%)` | :arrow_down: |
| [.../protocol/handshake/message\_client\_key\_exchange.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-cGtnL3Byb3RvY29sL2hhbmRzaGFrZS9tZXNzYWdlX2NsaWVudF9rZXlfZXhjaGFuZ2UuZ28=) | `44.73% <0.00%> (-23.27%)` | :arrow_down: |
| [.../protocol/handshake/message\_server\_key\_exchange.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-cGtnL3Byb3RvY29sL2hhbmRzaGFrZS9tZXNzYWdlX3NlcnZlcl9rZXlfZXhjaGFuZ2UuZ28=) | `51.13% <0.00%> (-4.42%)` | :arrow_down: |
| [flight3handler.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-ZmxpZ2h0M2hhbmRsZXIuZ28=) | `73.72% <14.28%> (-6.00%)` | :arrow_down: |
| [...phersuite/tls\_ecdhe\_psk\_with\_aes\_128\_cbc\_sha256.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-aW50ZXJuYWwvY2lwaGVyc3VpdGUvdGxzX2VjZGhlX3Bza193aXRoX2Flc18xMjhfY2JjX3NoYTI1Ni5nbw==) | `80.35% <80.35%> (ø)` | |
| [cipher\_suite.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-Y2lwaGVyX3N1aXRlLmdv) | `98.51% <100.00%> (+0.02%)` | :arrow_up: |
| [flight1handler.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-ZmxpZ2h0MWhhbmRsZXIuZ28=) | `87.85% <100.00%> (+0.72%)` | :arrow_up: |
| [flight5handler.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-ZmxpZ2h0NWhhbmRsZXIuZ28=) | `77.85% <100.00%> (+0.23%)` | :arrow_up: |
| [internal/ciphersuite/aes\_128\_ccm.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-aW50ZXJuYWwvY2lwaGVyc3VpdGUvYWVzXzEyOF9jY20uZ28=) | `100.00% <100.00%> (ø)` | |
| [internal/ciphersuite/aes\_256\_ccm.go](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-aW50ZXJuYWwvY2lwaGVyc3VpdGUvYWVzXzI1Nl9jY20uZ28=) | `100.00% <100.00%> (ø)` | |
| ... and [12 more](https://codecov.io/gh/pion/dtls/pull/436/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/dtls/pull/436?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pion/dtls/pull/436?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion). Last update [bfbe15e...85c338e](https://codecov.io/gh/pion/dtls/pull/436?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion).

jkralik: @Sean-Der Is there anything else that needs to be done for this PR? Thx
Sean-Der: No! Let me review now and get it merged, sorry about that @jkralik 
Sean-Der: @jkralik The PR looks fantastic to me! Having the constant for KeyExchange is amazing, makes all the existing code better as well. I love checking the algo and not just if PSK callback is nil.


Sean-Der: This an approve from me. Really great work :)

@at-wat @daenney if you have some bandwidth mind looking as well? If you don't have time happy to merge this in a day or two.
Sean-Der: @jkralik Mind looking at the changes I made and ok'ing?

* I removed the heuristics from `MessageClientKeyExchange` and it matches `ServerKeyExchange` now.
* MessageClientKeyExchange during the Marshal only has one branch. We don't use separate code IdentityHint, PublicKey and IdentityHint+PublicKey
* In Conn we just unmarshal the header. That way we don't need to know the `keyExchangeAlgorithm`
at-wat: https://github.com/pion/dtls/pull/436#issuecomment-1071204324
I'll take a look today
Sean-Der: Feel free to merge w/e you want @jkralik!

Really appreciate your patience with this PR. This was a tricky one, but super happy with how it ended up.

Patch:
diff --git a/README.md b/README.md
index fd35882..02fc7d7 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,7 @@ We would love contributions that fall under the 'Planned Features' and any bug f
 #### Supported ciphers
 
 ##### ECDHE
+
 * TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655])
 * TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
 * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
@@ -54,16 +55,22 @@ We would love contributions that fall under the 'Planned Features' and any bug f
 * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
 
 ##### PSK
+
 * TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655])
 * TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
 * TLS_PSK_WITH_AES_256_CCM_8 ([RFC 6655][rfc6655])
 * TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487])
 * TLS_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5487][rfc5487])
 
+##### ECDHE & PSK
+
+* TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5489][rfc5489])
+
 [rfc5289]: https://tools.ietf.org/html/rfc5289
 [rfc8422]: https://tools.ietf.org/html/rfc8422
 [rfc6655]: https://tools.ietf.org/html/rfc6655
 [rfc5487]: https://tools.ietf.org/html/rfc5487
+[rfc5489]: https://tools.ietf.org/html/rfc5489
 
 #### Planned Features
 * Chacha20Poly1305
diff --git a/cipher_suite.go b/cipher_suite.go
index 49ebf48..39d2b32 100644
--- a/cipher_suite.go
+++ b/cipher_suite.go
@@ -38,6 +38,8 @@ const (
 	TLS_PSK_WITH_AES_256_CCM_8      CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_256_CCM_8      //nolint:golint,stylecheck
 	TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck
 	TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:golint,stylecheck
+
+	TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 //nolint:golint,stylecheck
 )
 
 // CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite
@@ -50,6 +52,16 @@ const (
 	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
@@ -69,6 +81,13 @@ type CipherSuite interface {
 	// 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
@@ -120,6 +139,8 @@ func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) Ciph
 		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 {
diff --git a/conn.go b/conn.go
index 59834a3..7c586e7 100644
--- a/conn.go
+++ b/conn.go
@@ -702,13 +702,12 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo
 	} else if isHandshake {
 		markPacketAsValid()
 		for out, epoch := c.fragmentBuffer.pop(); out != nil; out, epoch = c.fragmentBuffer.pop() {
-			rawHandshake := &handshake.Handshake{}
-			if err := rawHandshake.Unmarshal(out); err != nil {
+			header := &handshake.Header{}
+			if err := header.Unmarshal(out); err != nil {
 				c.log.Debugf("%s: handshake parse failed: %s", srvCliStr(c.state.isClient), err)
 				continue
 			}
-
-			_ = c.handshakeCache.push(out, epoch, rawHandshake.Header.MessageSequence, rawHandshake.Header.Type, !c.state.isClient)
+			_ = c.handshakeCache.push(out, epoch, header.MessageSequence, header.Type, !c.state.isClient)
 		}
 
 		return true, nil, nil
diff --git a/flight0handler.go b/flight0handler.go
index 23ddded..0b16eb6 100644
--- a/flight0handler.go
+++ b/flight0handler.go
@@ -12,7 +12,7 @@ import (
 )
 
 func flight0Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
-	seq, msgs, ok := cache.fullPullMap(0,
+	seq, msgs, ok := cache.fullPullMap(0, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
 	)
 	if !ok {
diff --git a/flight1handler.go b/flight1handler.go
index 48c8201..3e14edd 100644
--- a/flight1handler.go
+++ b/flight1handler.go
@@ -14,7 +14,7 @@ import (
 func flight1Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
 	// HelloVerifyRequest can be skipped by the server,
 	// so allow ServerHello during flight1 also
-	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
 		handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, true},
 	)
@@ -62,7 +62,16 @@ func flight1Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 			RenegotiatedConnection: 0,
 		},
 	}
-	if cfg.localPSKCallback == nil {
+
+	var setEllipticCurveCryptographyClientHelloExtensions bool
+	for _, c := range cfg.localCipherSuites {
+		if c.ECC() {
+			setEllipticCurveCryptographyClientHelloExtensions = true
+			break
+		}
+	}
+
+	if setEllipticCurveCryptographyClientHelloExtensions {
 		extensions = append(extensions, []extension.Extension{
 			&extension.SupportedEllipticCurves{
 				EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384},
diff --git a/flight2handler.go b/flight2handler.go
index bb8e91d..9a1bf34 100644
--- a/flight2handler.go
+++ b/flight2handler.go
@@ -11,7 +11,7 @@ import (
 )
 
 func flight2Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
-	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
 	)
 	if !ok {
diff --git a/flight3handler.go b/flight3handler.go
index 697b304..49da5a9 100644
--- a/flight3handler.go
+++ b/flight3handler.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"context"
 
+	"github.com/pion/dtls/v2/internal/ciphersuite/types"
 	"github.com/pion/dtls/v2/pkg/crypto/elliptic"
 	"github.com/pion/dtls/v2/pkg/crypto/prf"
 	"github.com/pion/dtls/v2/pkg/protocol"
@@ -17,7 +18,7 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 	// Clients may receive multiple HelloVerifyRequest messages with different cookies.
 	// Clients SHOULD handle this by sending a new ClientHello with a cookie in response
 	// to the new HelloVerifyRequest. RFC 6347 Section 4.2.1
-	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
 	)
 	if ok {
@@ -33,7 +34,7 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 		}
 	}
 
-	_, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence,
+	_, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
 	)
 	if !ok {
@@ -106,12 +107,12 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 	}
 
 	if cfg.localPSKCallback != nil {
-		seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1,
+		seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
 			handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true},
 			handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
 		)
 	} else {
-		seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1,
+		seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
 			handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, true},
 			handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
 			handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, true},
@@ -154,7 +155,7 @@ func handleResumption(ctx context.Context, c flightConn, state *State, cache *ha
 		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
 	}
 
-	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence+1,
+	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
 	)
 	if !ok {
@@ -187,13 +188,29 @@ func handleResumption(ctx context.Context, c flightConn, state *State, cache *ha
 
 func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange) (*alert.Alert, error) {
 	var err error
+	if state.cipherSuite == nil {
+		return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
+	}
 	if cfg.localPSKCallback != nil {
 		var psk []byte
 		if psk, err = cfg.localPSKCallback(h.IdentityHint); err != nil {
 			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
 		}
 		state.IdentityHint = h.IdentityHint
-		state.preMasterSecret = prf.PSKPreMasterSecret(psk)
+		switch state.cipherSuite.KeyExchangeAlgorithm() {
+		case types.KeyExchangeAlgorithmPsk:
+			state.preMasterSecret = prf.PSKPreMasterSecret(psk)
+		case (types.KeyExchangeAlgorithmEcdhe | types.KeyExchangeAlgorithmPsk):
+			if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil {
+				return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+			}
+			state.preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
+			if err != nil {
+				return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+			}
+		default:
+			return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
+		}
 	} else {
 		if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil {
 			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
@@ -216,7 +233,7 @@ func flight3Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 			RenegotiatedConnection: 0,
 		},
 	}
-	if cfg.localPSKCallback == nil {
+	if state.namedCurve != 0 {
 		extensions = append(extensions, []extension.Extension{
 			&extension.SupportedEllipticCurves{
 				EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384},
diff --git a/flight4bhandler.go b/flight4bhandler.go
index 36cd0b4..46515c5 100644
--- a/flight4bhandler.go
+++ b/flight4bhandler.go
@@ -13,7 +13,7 @@ import (
 )
 
 func flight4bParse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
-	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
 	)
 	if !ok {
diff --git a/flight4handler.go b/flight4handler.go
index a6ba36c..9e90f19 100644
--- a/flight4handler.go
+++ b/flight4handler.go
@@ -17,7 +17,7 @@ import (
 )
 
 func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit
-	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+	seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true},
 		handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
 		handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true},
@@ -103,7 +103,16 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 				return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
 			}
 			state.IdentityHint = clientKeyExchange.IdentityHint
-			preMasterSecret = prf.PSKPreMasterSecret(psk)
+			switch state.cipherSuite.KeyExchangeAlgorithm() {
+			case CipherSuiteKeyExchangeAlgorithmPsk:
+				preMasterSecret = prf.PSKPreMasterSecret(psk)
+			case (CipherSuiteKeyExchangeAlgorithmPsk | CipherSuiteKeyExchangeAlgorithmEcdhe):
+				if preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil {
+					return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+				}
+			default:
+				return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidCipherSuite
+			}
 		} else {
 			preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
 			if err != nil {
@@ -151,7 +160,7 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
 	}
 
-	seq, msgs, ok = cache.fullPullMap(seq,
+	seq, msgs, ok = cache.fullPullMap(seq, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
 	)
 	if !ok {
@@ -319,36 +328,28 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 				},
 			})
 		}
-	case cfg.localPSKIdentityHint != nil:
+	case cfg.localPSKIdentityHint != nil || state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe):
 		// To help the client in selecting which identity to use, the server
 		// can provide a "PSK identity hint" in the ServerKeyExchange message.
-		// If no hint is provided, the ServerKeyExchange message is omitted.
+		// If no hint is provided and cipher suite doesn't use elliptic curve,
+		// the ServerKeyExchange message is omitted.
 		//
 		// https://tools.ietf.org/html/rfc4279#section-2
+		srvExchange := &handshake.MessageServerKeyExchange{
+			IdentityHint: cfg.localPSKIdentityHint,
+		}
+		if state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe) {
+			srvExchange.EllipticCurveType = elliptic.CurveTypeNamedCurve
+			srvExchange.NamedCurve = state.namedCurve
+			srvExchange.PublicKey = state.localKeypair.PublicKey
+		}
 		pkts = append(pkts, &packet{
 			record: &recordlayer.RecordLayer{
 				Header: recordlayer.Header{
 					Version: protocol.Version1_2,
 				},
 				Content: &handshake.Handshake{
-					Message: &handshake.MessageServerKeyExchange{
-						IdentityHint: cfg.localPSKIdentityHint,
-					},
-				},
-			},
-		})
-	case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous:
-		pkts = append(pkts, &packet{
-			record: &recordlayer.RecordLayer{
-				Header: recordlayer.Header{
-					Version: protocol.Version1_2,
-				},
-				Content: &handshake.Handshake{
-					Message: &handshake.MessageServerKeyExchange{
-						EllipticCurveType: elliptic.CurveTypeNamedCurve,
-						NamedCurve:        state.namedCurve,
-						PublicKey:         state.localKeypair.PublicKey,
-					},
+					Message: srvExchange,
 				},
 			},
 		})
diff --git a/flight5bhandler.go b/flight5bhandler.go
index 577342e..bd330d5 100644
--- a/flight5bhandler.go
+++ b/flight5bhandler.go
@@ -11,7 +11,7 @@ import (
 )
 
 func flight5bParse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
-	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1,
+	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
 	)
 	if !ok {
diff --git a/flight5handler.go b/flight5handler.go
index 510fa45..f409e56 100644
--- a/flight5handler.go
+++ b/flight5handler.go
@@ -15,7 +15,7 @@ import (
 )
 
 func flight5Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
-	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
 	)
 	if !ok {
@@ -98,6 +98,9 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 	} else {
 		clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint
 	}
+	if state != nil && state.localKeypair != nil && len(state.localKeypair.PublicKey) > 0 {
+		clientKeyExchange.PublicKey = state.localKeypair.PublicKey
+	}
 
 	pkts = append(pkts,
 		&packet{
@@ -124,7 +127,9 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 			return nil, alertPtr, err
 		}
 	} else {
-		rawHandshake := &handshake.Handshake{}
+		rawHandshake := &handshake.Handshake{
+			KeyExchangeAlgorithm: state.cipherSuite.KeyExchangeAlgorithm(),
+		}
 		err := rawHandshake.Unmarshal(serverKeyExchangeData)
 		if err != nil {
 			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, err
diff --git a/flight6handler.go b/flight6handler.go
index fddaa0e..d3d62f0 100644
--- a/flight6handler.go
+++ b/flight6handler.go
@@ -11,7 +11,7 @@ import (
 )
 
 func flight6Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
-	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1,
+	_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
 		handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
 	)
 	if !ok {
diff --git a/handshake_cache.go b/handshake_cache.go
index 063a858..2cecfea 100644
--- a/handshake_cache.go
+++ b/handshake_cache.go
@@ -77,7 +77,7 @@ func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCache
 }
 
 // fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map.
-func (h *handshakeCache) fullPullMap(startSeq int, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) {
+func (h *handshakeCache) fullPullMap(startSeq int, cipherSuite CipherSuite, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) {
 	h.mu.Lock()
 	defer h.mu.Unlock()
 
@@ -108,7 +108,13 @@ func (h *handshakeCache) fullPullMap(startSeq int, rules ...handshakeCachePullRu
 		if i == nil {
 			continue
 		}
-		rawHandshake := &handshake.Handshake{}
+		var keyExchangeAlgorithm CipherSuiteKeyExchangeAlgorithm
+		if cipherSuite != nil {
+			keyExchangeAlgorithm = cipherSuite.KeyExchangeAlgorithm()
+		}
+		rawHandshake := &handshake.Handshake{
+			KeyExchangeAlgorithm: keyExchangeAlgorithm,
+		}
 		if err := rawHandshake.Unmarshal(i.data); err != nil {
 			return startSeq, nil, false
 		}
diff --git a/internal/ciphersuite/aes_128_ccm.go b/internal/ciphersuite/aes_128_ccm.go
index 47f2f3f..afff736 100644
--- a/internal/ciphersuite/aes_128_ccm.go
+++ b/internal/ciphersuite/aes_128_ccm.go
@@ -10,13 +10,15 @@ type Aes128Ccm struct {
 	AesCcm
 }
 
-func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen) *Aes128Ccm {
+func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes128Ccm {
 	return &Aes128Ccm{
 		AesCcm: AesCcm{
 			clientCertificateType: clientCertificateType,
 			id:                    id,
 			psk:                   psk,
 			cryptoCCMTagLen:       cryptoCCMTagLen,
+			keyExchangeAlgorithm:  keyExchangeAlgorithm,
+			ecc:                   ecc,
 		},
 	}
 }
diff --git a/internal/ciphersuite/aes_256_ccm.go b/internal/ciphersuite/aes_256_ccm.go
index b43bf2e..d56ffc5 100644
--- a/internal/ciphersuite/aes_256_ccm.go
+++ b/internal/ciphersuite/aes_256_ccm.go
@@ -10,13 +10,15 @@ type Aes256Ccm struct {
 	AesCcm
 }
 
-func newAes256Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen) *Aes256Ccm {
+func newAes256Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes256Ccm {
 	return &Aes256Ccm{
 		AesCcm: AesCcm{
 			clientCertificateType: clientCertificateType,
 			id:                    id,
 			psk:                   psk,
 			cryptoCCMTagLen:       cryptoCCMTagLen,
+			keyExchangeAlgorithm:  keyExchangeAlgorithm,
+			ecc:                   ecc,
 		},
 	}
 }
diff --git a/internal/ciphersuite/aes_ccm.go b/internal/ciphersuite/aes_ccm.go
index dcb24bc..e3e3f59 100644
--- a/internal/ciphersuite/aes_ccm.go
+++ b/internal/ciphersuite/aes_ccm.go
@@ -18,7 +18,9 @@ type AesCcm struct {
 	clientCertificateType clientcertificate.Type
 	id                    ID
 	psk                   bool
+	keyExchangeAlgorithm  KeyExchangeAlgorithm
 	cryptoCCMTagLen       ciphersuite.CCMTagLen
+	ecc                   bool
 }
 
 // CertificateType returns what type of certificate this CipherSuite exchanges
@@ -35,6 +37,16 @@ func (c *AesCcm) String() string {
 	return c.id.String()
 }
 
+// ECC uses Elliptic Curve Cryptography
+func (c *AesCcm) ECC() bool {
+	return c.ecc
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *AesCcm) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+	return c.keyExchangeAlgorithm
+}
+
 // HashFunc returns the hashing func for this CipherSuite
 func (c *AesCcm) HashFunc() func() hash.Hash {
 	return sha256.New
diff --git a/internal/ciphersuite/ciphersuite.go b/internal/ciphersuite/ciphersuite.go
index c56283e..2cd6cd7 100644
--- a/internal/ciphersuite/ciphersuite.go
+++ b/internal/ciphersuite/ciphersuite.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 
+	"github.com/pion/dtls/v2/internal/ciphersuite/types"
 	"github.com/pion/dtls/v2/pkg/protocol"
 )
 
@@ -41,6 +42,8 @@ func (i ID) String() string {
 		return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
 	case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
 		return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
+	case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+		return "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"
 	default:
 		return fmt.Sprintf("unknown(%v)", uint16(i))
 	}
@@ -67,14 +70,26 @@ const (
 	TLS_PSK_WITH_AES_256_CCM_8      ID = 0xc0a9 //nolint:golint,stylecheck
 	TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:golint,stylecheck
 	TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:golint,stylecheck
+
+	TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ID = 0xC037 //nolint:golint,stylecheck
 )
 
 // AuthenticationType controls what authentication method is using during the handshake
-type AuthenticationType int
+type AuthenticationType = types.AuthenticationType
 
 // AuthenticationType Enums
 const (
-	AuthenticationTypeCertificate AuthenticationType = iota + 1
-	AuthenticationTypePreSharedKey
-	AuthenticationTypeAnonymous
+	AuthenticationTypeCertificate  AuthenticationType = types.AuthenticationTypeCertificate
+	AuthenticationTypePreSharedKey AuthenticationType = types.AuthenticationTypePreSharedKey
+	AuthenticationTypeAnonymous    AuthenticationType = types.AuthenticationTypeAnonymous
+)
+
+// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
+type KeyExchangeAlgorithm = types.KeyExchangeAlgorithm
+
+// KeyExchangeAlgorithm Bitmask
+const (
+	KeyExchangeAlgorithmNone  KeyExchangeAlgorithm = types.KeyExchangeAlgorithmNone
+	KeyExchangeAlgorithmPsk   KeyExchangeAlgorithm = types.KeyExchangeAlgorithmPsk
+	KeyExchangeAlgorithmEcdhe KeyExchangeAlgorithm = types.KeyExchangeAlgorithmEcdhe
 )
diff --git a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
index ac73556..91189e1 100644
--- a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
+++ b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
@@ -7,5 +7,5 @@ import (
 
 // NewTLSEcdheEcdsaWithAes128Ccm constructs a TLS_ECDHE_ECDSA_WITH_AES_128_CCM Cipher
 func NewTLSEcdheEcdsaWithAes128Ccm() *Aes128Ccm {
-	return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM, false, ciphersuite.CCMTagLength)
+	return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM, false, ciphersuite.CCMTagLength, KeyExchangeAlgorithmEcdhe, true)
 }
diff --git a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
index 49b1a83..81368f4 100644
--- a/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
+++ b/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
@@ -7,5 +7,5 @@ import (
 
 // NewTLSEcdheEcdsaWithAes128Ccm8 creates a new TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuite
 func NewTLSEcdheEcdsaWithAes128Ccm8() *Aes128Ccm {
-	return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, false, ciphersuite.CCMTagLength8)
+	return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, false, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmEcdhe, true)
 }
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 ede12bb..d3501bd 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
@@ -22,6 +22,16 @@ func (c *TLSEcdheEcdsaWithAes128GcmSha256) CertificateType() clientcertificate.T
 	return clientcertificate.ECDSASign
 }
 
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+	return KeyExchangeAlgorithmEcdhe
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) ECC() bool {
+	return true
+}
+
 // ID returns the ID of the CipherSuite
 func (c *TLSEcdheEcdsaWithAes128GcmSha256) ID() ID {
 	return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
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 f7a33ad..251d77b 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
@@ -23,6 +23,16 @@ func (c *TLSEcdheEcdsaWithAes256CbcSha) CertificateType() clientcertificate.Type
 	return clientcertificate.ECDSASign
 }
 
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSEcdheEcdsaWithAes256CbcSha) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+	return KeyExchangeAlgorithmEcdhe
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSEcdheEcdsaWithAes256CbcSha) ECC() bool {
+	return true
+}
+
 // ID returns the ID of the CipherSuite
 func (c *TLSEcdheEcdsaWithAes256CbcSha) ID() ID {
 	return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
diff --git a/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go b/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go
new file mode 100644
index 0000000..4ce104a
--- /dev/null
+++ b/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go
@@ -0,0 +1,115 @@
+package ciphersuite
+
+import (
+	"crypto/sha256"
+	"fmt"
+	"hash"
+	"sync/atomic"
+
+	"github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+	"github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+	"github.com/pion/dtls/v2/pkg/crypto/prf"
+	"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSEcdhePskWithAes128CbcSha256 implements the TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuite
+type TLSEcdhePskWithAes128CbcSha256 struct {
+	cbc atomic.Value // *cryptoCBC
+}
+
+// NewTLSEcdhePskWithAes128CbcSha256 creates TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 cipher.
+func NewTLSEcdhePskWithAes128CbcSha256() *TLSEcdhePskWithAes128CbcSha256 {
+	return &TLSEcdhePskWithAes128CbcSha256{}
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSEcdhePskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
+	return clientcertificate.Type(0)
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSEcdhePskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+	return (KeyExchangeAlgorithmPsk | KeyExchangeAlgorithmEcdhe)
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSEcdhePskWithAes128CbcSha256) ECC() bool {
+	return true
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdhePskWithAes128CbcSha256) ID() ID {
+	return TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
+}
+
+func (c *TLSEcdhePskWithAes128CbcSha256) String() string {
+	return "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSEcdhePskWithAes128CbcSha256) HashFunc() func() hash.Hash {
+	return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdhePskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
+	return AuthenticationTypePreSharedKey
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSEcdhePskWithAes128CbcSha256) IsInitialized() bool {
+	return c.cbc.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSEcdhePskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+	const (
+		prfMacLen = 32
+		prfKeyLen = 16
+		prfIvLen  = 16
+	)
+
+	keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+	if err != nil {
+		return err
+	}
+
+	var cbc *ciphersuite.CBC
+	if isClient {
+		cbc, err = ciphersuite.NewCBC(
+			keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+			keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+			c.HashFunc(),
+		)
+	} else {
+		cbc, err = ciphersuite.NewCBC(
+			keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+			keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+			c.HashFunc(),
+		)
+	}
+	c.cbc.Store(cbc)
+
+	return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSEcdhePskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+	cbc := c.cbc.Load()
+	if cbc == nil { // !c.isInitialized()
+		return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+	}
+
+	return cbc.(*ciphersuite.CBC).Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSEcdhePskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) {
+	cbc := c.cbc.Load()
+	if cbc == nil { // !c.isInitialized()
+		return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+	}
+
+	return cbc.(*ciphersuite.CBC).Decrypt(raw)
+}
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 43e5e38..7c68666 100644
--- a/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
+++ b/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
@@ -22,6 +22,16 @@ func (c *TLSPskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
 	return clientcertificate.Type(0)
 }
 
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSPskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+	return KeyExchangeAlgorithmPsk
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSPskWithAes128CbcSha256) ECC() bool {
+	return false
+}
+
 // ID returns the ID of the CipherSuite
 func (c *TLSPskWithAes128CbcSha256) ID() ID {
 	return TLS_PSK_WITH_AES_128_CBC_SHA256
diff --git a/internal/ciphersuite/tls_psk_with_aes_128_ccm.go b/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
index 8c13bb1..02b9640 100644
--- a/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
+++ b/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
@@ -7,5 +7,5 @@ import (
 
 // NewTLSPskWithAes128Ccm returns the TLS_PSK_WITH_AES_128_CCM CipherSuite
 func NewTLSPskWithAes128Ccm() *Aes128Ccm {
-	return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM, true, ciphersuite.CCMTagLength)
+	return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM, true, ciphersuite.CCMTagLength, KeyExchangeAlgorithmPsk, false)
 }
diff --git a/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go b/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
index d04abb4..faf9cb4 100644
--- a/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
+++ b/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
@@ -7,5 +7,5 @@ import (
 
 // NewTLSPskWithAes128Ccm8 returns the TLS_PSK_WITH_AES_128_CCM_8 CipherSuite
 func NewTLSPskWithAes128Ccm8() *Aes128Ccm {
-	return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM_8, true, ciphersuite.CCMTagLength8)
+	return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM_8, true, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmPsk, false)
 }
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 5f10335..98f7a4a 100644
--- a/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
+++ b/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
@@ -12,6 +12,11 @@ func (c *TLSPskWithAes128GcmSha256) CertificateType() clientcertificate.Type {
 	return clientcertificate.Type(0)
 }
 
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSPskWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+	return KeyExchangeAlgorithmPsk
+}
+
 // ID returns the ID of the CipherSuite
 func (c *TLSPskWithAes128GcmSha256) ID() ID {
 	return TLS_PSK_WITH_AES_128_GCM_SHA256
diff --git a/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go b/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
index edf66cc..9058ff2 100644
--- a/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
+++ b/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
@@ -7,5 +7,5 @@ import (
 
 // NewTLSPskWithAes256Ccm8 returns the TLS_PSK_WITH_AES_256_CCM_8 CipherSuite
 func NewTLSPskWithAes256Ccm8() *Aes256Ccm {
-	return newAes256Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_256_CCM_8, true, ciphersuite.CCMTagLength8)
+	return newAes256Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_256_CCM_8, true, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmPsk, false)
 }
diff --git a/internal/ciphersuite/types/authentication_type.go b/internal/ciphersuite/types/authentication_type.go
new file mode 100644
index 0000000..75d599f
--- /dev/null
+++ b/internal/ciphersuite/types/authentication_type.go
@@ -0,0 +1,11 @@
+package types
+
+// 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/types/key_exchange_algorithm.go b/internal/ciphersuite/types/key_exchange_algorithm.go
new file mode 100644
index 0000000..fbf8347
--- /dev/null
+++ b/internal/ciphersuite/types/key_exchange_algorithm.go
@@ -0,0 +1,17 @@
+// Package types provides types for TLS Ciphers
+package types
+
+// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
+type KeyExchangeAlgorithm int
+
+// KeyExchangeAlgorithm Bitmask
+const (
+	KeyExchangeAlgorithmNone KeyExchangeAlgorithm = 0
+	KeyExchangeAlgorithmPsk  KeyExchangeAlgorithm = iota << 1
+	KeyExchangeAlgorithmEcdhe
+)
+
+// Has check if keyExchangeAlgorithm is supported.
+func (a KeyExchangeAlgorithm) Has(v KeyExchangeAlgorithm) bool {
+	return (a & v) == v
+}
diff --git a/pkg/crypto/prf/prf.go b/pkg/crypto/prf/prf.go
index d33df19..11f53a1 100644
--- a/pkg/crypto/prf/prf.go
+++ b/pkg/crypto/prf/prf.go
@@ -74,6 +74,34 @@ func PSKPreMasterSecret(psk []byte) []byte {
 	return out
 }
 
+// EcdhePSKPreMasterSecret implements TLS 1.2 Premaster Secret generation given a psk, a keypair and a curve
+//
+// https://datatracker.ietf.org/doc/html/rfc5489#section-2
+func EcdhePSKPreMasterSecret(psk, publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
+	preMasterSecret, err := PreMasterSecret(publicKey, privateKey, curve)
+	if err != nil {
+		return nil, err
+	}
+	out := make([]byte, 2+len(preMasterSecret)+2+len(psk))
+
+	// write preMasterSecret length
+	offset := 0
+	binary.BigEndian.PutUint16(out[offset:], uint16(len(preMasterSecret)))
+	offset += 2
+
+	// write preMasterSecret
+	copy(out[offset:], preMasterSecret)
+	offset += len(preMasterSecret)
+
+	// write psk length
+	binary.BigEndian.PutUint16(out[offset:], uint16(len(psk)))
+	offset += 2
+
+	// write psk
+	copy(out[offset:], psk)
+	return out, nil
+}
+
 // PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve
 func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
 	switch curve {
diff --git a/pkg/protocol/handshake/handshake.go b/pkg/protocol/handshake/handshake.go
index f254581..55f1a7c 100644
--- a/pkg/protocol/handshake/handshake.go
+++ b/pkg/protocol/handshake/handshake.go
@@ -2,6 +2,7 @@
 package handshake
 
 import (
+	"github.com/pion/dtls/v2/internal/ciphersuite/types"
 	"github.com/pion/dtls/v2/internal/util"
 	"github.com/pion/dtls/v2/pkg/protocol"
 )
@@ -70,6 +71,8 @@ type Message interface {
 type Handshake struct {
 	Header  Header
 	Message Message
+
+	KeyExchangeAlgorithm types.KeyExchangeAlgorithm
 }
 
 // ContentType returns what kind of content this message is carying
@@ -126,13 +129,13 @@ func (h *Handshake) Unmarshal(data []byte) error {
 	case TypeCertificate:
 		h.Message = &MessageCertificate{}
 	case TypeServerKeyExchange:
-		h.Message = &MessageServerKeyExchange{}
+		h.Message = &MessageServerKeyExchange{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm}
 	case TypeCertificateRequest:
 		h.Message = &MessageCertificateRequest{}
 	case TypeServerHelloDone:
 		h.Message = &MessageServerHelloDone{}
 	case TypeClientKeyExchange:
-		h.Message = &MessageClientKeyExchange{}
+		h.Message = &MessageClientKeyExchange{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm}
 	case TypeFinished:
 		h.Message = &MessageFinished{}
 	case TypeCertificateVerify:
diff --git a/pkg/protocol/handshake/message_client_key_exchange.go b/pkg/protocol/handshake/message_client_key_exchange.go
index f8fc369..34c5c48 100644
--- a/pkg/protocol/handshake/message_client_key_exchange.go
+++ b/pkg/protocol/handshake/message_client_key_exchange.go
@@ -2,6 +2,8 @@ package handshake
 
 import (
 	"encoding/binary"
+
+	"github.com/pion/dtls/v2/internal/ciphersuite/types"
 )
 
 // MessageClientKeyExchange is a DTLS Handshake Message
@@ -14,6 +16,9 @@ import (
 type MessageClientKeyExchange struct {
 	IdentityHint []byte
 	PublicKey    []byte
+
+	// for unmarshaling
+	KeyExchangeAlgorithm types.KeyExchangeAlgorithm
 }
 
 // Type returns the Handshake Type
@@ -22,35 +27,52 @@ func (m MessageClientKeyExchange) Type() Type {
 }
 
 // Marshal encodes the Handshake
-func (m *MessageClientKeyExchange) Marshal() ([]byte, error) {
-	switch {
-	case (m.IdentityHint != nil && m.PublicKey != nil) || (m.IdentityHint == nil && m.PublicKey == nil):
+func (m *MessageClientKeyExchange) Marshal() (out []byte, err error) {
+	if m.IdentityHint == nil && m.PublicKey == nil {
 		return nil, errInvalidClientKeyExchange
-	case m.PublicKey != nil:
-		return append([]byte{byte(len(m.PublicKey))}, m.PublicKey...), nil
-	default:
-		out := append([]byte{0x00, 0x00}, m.IdentityHint...)
+	}
+
+	if m.IdentityHint != nil {
+		out = append([]byte{0x00, 0x00}, m.IdentityHint...)
 		binary.BigEndian.PutUint16(out, uint16(len(out)-2))
-		return out, nil
 	}
+
+	if m.PublicKey != nil {
+		out = append(out, byte(len(m.PublicKey)))
+		out = append(out, m.PublicKey...)
+	}
+
+	return out, nil
 }
 
 // Unmarshal populates the message from encoded data
 func (m *MessageClientKeyExchange) Unmarshal(data []byte) error {
-	if len(data) < 2 {
+	switch {
+	case len(data) < 2:
 		return errBufferTooSmall
+	case m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmNone:
+		return errCipherSuiteUnset
 	}
 
-	// If parsed as PSK return early and only populate PSK Identity Hint
-	if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) {
-		m.IdentityHint = append([]byte{}, data[2:]...)
-		return nil
+	offset := 0
+	if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmPsk) {
+		pskLength := int(binary.BigEndian.Uint16(data))
+		if pskLength > len(data)-2 {
+			return errBufferTooSmall
+		}
+
+		m.IdentityHint = append([]byte{}, data[2:pskLength+2]...)
+		offset += pskLength + 2
 	}
 
-	if publicKeyLength := int(data[0]); len(data) != publicKeyLength+1 {
-		return errBufferTooSmall
+	if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmEcdhe) {
+		publicKeyLength := int(data[offset])
+		if publicKeyLength > len(data)-1-offset {
+			return errBufferTooSmall
+		}
+
+		m.PublicKey = append([]byte{}, data[offset+1:]...)
 	}
 
-	m.PublicKey = append([]byte{}, data[1:]...)
 	return nil
 }
diff --git a/pkg/protocol/handshake/message_server_key_exchange.go b/pkg/protocol/handshake/message_server_key_exchange.go
index 4148fe0..e84734a 100644
--- a/pkg/protocol/handshake/message_server_key_exchange.go
+++ b/pkg/protocol/handshake/message_server_key_exchange.go
@@ -3,6 +3,7 @@ package handshake
 import (
 	"encoding/binary"
 
+	"github.com/pion/dtls/v2/internal/ciphersuite/types"
 	"github.com/pion/dtls/v2/pkg/crypto/elliptic"
 	"github.com/pion/dtls/v2/pkg/crypto/hash"
 	"github.com/pion/dtls/v2/pkg/crypto/signature"
@@ -18,6 +19,9 @@ type MessageServerKeyExchange struct {
 	HashAlgorithm      hash.Algorithm
 	SignatureAlgorithm signature.Algorithm
 	Signature          []byte
+
+	// for unmarshaling
+	KeyExchangeAlgorithm types.KeyExchangeAlgorithm
 }
 
 // Type returns the Handshake Type
@@ -27,19 +31,28 @@ func (m MessageServerKeyExchange) Type() Type {
 
 // Marshal encodes the Handshake
 func (m *MessageServerKeyExchange) Marshal() ([]byte, error) {
+	var out []byte
 	if m.IdentityHint != nil {
-		out := append([]byte{0x00, 0x00}, m.IdentityHint...)
+		out = append([]byte{0x00, 0x00}, m.IdentityHint...)
 		binary.BigEndian.PutUint16(out, uint16(len(out)-2))
-		return out, nil
 	}
 
-	out := []byte{byte(m.EllipticCurveType), 0x00, 0x00}
-	binary.BigEndian.PutUint16(out[1:], uint16(m.NamedCurve))
+	if m.EllipticCurveType == 0 || len(m.PublicKey) == 0 {
+		return out, nil
+	}
+	out = append(out, byte(m.EllipticCurveType), 0x00, 0x00)
+	binary.BigEndian.PutUint16(out[len(out)-2:], uint16(m.NamedCurve))
 
 	out = append(out, byte(len(m.PublicKey)))
 	out = append(out, m.PublicKey...)
-
-	if m.HashAlgorithm == hash.None && m.SignatureAlgorithm == signature.Anonymous && len(m.Signature) == 0 {
+	switch {
+	case m.HashAlgorithm != hash.None && len(m.Signature) == 0:
+		return nil, errInvalidHashAlgorithm
+	case m.HashAlgorithm == hash.None && len(m.Signature) > 0:
+		return nil, errInvalidHashAlgorithm
+	case m.SignatureAlgorithm == signature.Anonymous && (m.HashAlgorithm != hash.None || len(m.Signature) > 0):
+		return nil, errInvalidSignatureAlgorithm
+	case m.SignatureAlgorithm == signature.Anonymous:
 		return out, nil
 	}
 
@@ -52,14 +65,27 @@ func (m *MessageServerKeyExchange) Marshal() ([]byte, error) {
 
 // Unmarshal populates the message from encoded data
 func (m *MessageServerKeyExchange) Unmarshal(data []byte) error {
-	if len(data) < 2 {
+	switch {
+	case len(data) < 2:
 		return errBufferTooSmall
+	case m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmNone:
+		return errCipherSuiteUnset
 	}
 
-	// If parsed as PSK return early and only populate PSK Identity Hint
-	if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) {
-		m.IdentityHint = append([]byte{}, data[2:]...)
-		return nil
+	hintLength := binary.BigEndian.Uint16(data)
+	if int(hintLength) <= len(data)-2 && m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmPsk) {
+		m.IdentityHint = append([]byte{}, data[2:2+hintLength]...)
+		data = data[2+hintLength:]
+	}
+	if m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmPsk {
+		if len(data) == 0 {
+			return nil
+		}
+		return errLengthMismatch
+	}
+
+	if !m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmEcdhe) {
+		return errLengthMismatch
 	}
 
 	if _, ok := elliptic.CurveTypes()[elliptic.CurveType(data[0])]; ok {


Test patch:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index cd788c9..0d01db5 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -28,7 +28,7 @@ jobs:
     steps:
       - uses: actions/checkout@v3
 
-      - uses: actions/cache@v2
+      - uses: actions/cache@v3
         with:
           path: |
             ~/go/pkg/mod
@@ -79,7 +79,7 @@ jobs:
     steps:
       - uses: actions/checkout@v3
 
-      - uses: actions/cache@v2
+      - uses: actions/cache@v3
         with:
           path: |
             ~/go/pkg/mod
@@ -117,7 +117,7 @@ jobs:
         with:
           node-version: '16.x'
 
-      - uses: actions/cache@v2
+      - uses: actions/cache@v3
         with:
           path: |
             ~/go/pkg/mod
diff --git a/conn_test.go b/conn_test.go
index 9cb260d..57fd5cb 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -483,6 +483,11 @@ func TestPSK(t *testing.T) {
 			ServerIdentity: nil,
 			CipherSuites:   []CipherSuiteID{TLS_PSK_WITH_AES_128_CBC_SHA256},
 		},
+		{
+			Name:           "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
+			ServerIdentity: nil,
+			CipherSuites:   []CipherSuiteID{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256},
+		},
 	} {
 		test := test
 		t.Run(test.Name, func(t *testing.T) {
diff --git a/e2e/e2e_openssl_test.go b/e2e/e2e_openssl_test.go
index 2bb113f..b7634be 100644
--- a/e2e/e2e_openssl_test.go
+++ b/e2e/e2e_openssl_test.go
@@ -181,6 +181,8 @@ func ciphersOpenSSL(cfg *dtls.Config) string {
 		dtls.TLS_PSK_WITH_AES_128_CCM_8:      "PSK-AES128-CCM8",
 		dtls.TLS_PSK_WITH_AES_256_CCM_8:      "PSK-AES256-CCM8",
 		dtls.TLS_PSK_WITH_AES_128_GCM_SHA256: "PSK-AES128-GCM-SHA256",
+
+		dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: "ECDHE-PSK-AES128-CBC-SHA256",
 	}
 
 	var ciphers []string
diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go
index 57b5ac2..e04d827 100644
--- a/e2e/e2e_test.go
+++ b/e2e/e2e_test.go
@@ -264,6 +264,7 @@ func testPionE2ESimplePSK(t *testing.T, server, client func(*comm)) {
 		dtls.TLS_PSK_WITH_AES_128_CCM_8,
 		dtls.TLS_PSK_WITH_AES_256_CCM_8,
 		dtls.TLS_PSK_WITH_AES_128_GCM_SHA256,
+		dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
 	} {
 		cipherSuite := cipherSuite
 		t.Run(cipherSuite.String(), func(t *testing.T) {
diff --git a/pkg/protocol/handshake/message_client_key_exchange_test.go b/pkg/protocol/handshake/message_client_key_exchange_test.go
index d1b938a..ab6cbd1 100644
--- a/pkg/protocol/handshake/message_client_key_exchange_test.go
+++ b/pkg/protocol/handshake/message_client_key_exchange_test.go
@@ -3,6 +3,8 @@ package handshake
 import (
 	"reflect"
 	"testing"
+
+	"github.com/pion/dtls/v2/internal/ciphersuite/types"
 )
 
 func TestHandshakeMessageClientKeyExchange(t *testing.T) {
@@ -12,10 +14,13 @@ func TestHandshakeMessageClientKeyExchange(t *testing.T) {
 		0x54,
 	}
 	parsedClientKeyExchange := &MessageClientKeyExchange{
-		PublicKey: rawClientKeyExchange[1:],
+		PublicKey:            rawClientKeyExchange[1:],
+		KeyExchangeAlgorithm: types.KeyExchangeAlgorithmEcdhe,
 	}
 
-	c := &MessageClientKeyExchange{}
+	c := &MessageClientKeyExchange{
+		KeyExchangeAlgorithm: types.KeyExchangeAlgorithmEcdhe,
+	}
 	if err := c.Unmarshal(rawClientKeyExchange); err != nil {
 		t.Error(err)
 	} else if !reflect.DeepEqual(c, parsedClientKeyExchange) {
diff --git a/pkg/protocol/handshake/message_server_key_exchange_test.go b/pkg/protocol/handshake/message_server_key_exchange_test.go
index dfe7d1f..2d749b9 100644
--- a/pkg/protocol/handshake/message_server_key_exchange_test.go
+++ b/pkg/protocol/handshake/message_server_key_exchange_test.go
@@ -4,6 +4,7 @@ import (
 	"reflect"
 	"testing"
 
+	"github.com/pion/dtls/v2/internal/ciphersuite/types"
 	"github.com/pion/dtls/v2/pkg/crypto/elliptic"
 	"github.com/pion/dtls/v2/pkg/crypto/hash"
 	"github.com/pion/dtls/v2/pkg/crypto/signature"
@@ -11,7 +12,9 @@ import (
 
 func TestHandshakeMessageServerKeyExchange(t *testing.T) {
 	test := func(rawServerKeyExchange []byte, parsedServerKeyExchange *MessageServerKeyExchange) {
-		c := &MessageServerKeyExchange{}
+		c := &MessageServerKeyExchange{
+			KeyExchangeAlgorithm: types.KeyExchangeAlgorithmEcdhe,
+		}
 		if err := c.Unmarshal(rawServerKeyExchange); err != nil {
 			t.Error(err)
 		} else if !reflect.DeepEqual(c, parsedServerKeyExchange) {
@@ -39,12 +42,13 @@ func TestHandshakeMessageServerKeyExchange(t *testing.T) {
 			0xe8, 0xdf, 0x43, 0x75, 0xc7, 0xb9, 0x37, 0x6e, 0x90, 0xe5, 0x3b, 0x81, 0xd4, 0xda, 0x68, 0xcd,
 		}
 		parsedServerKeyExchange := &MessageServerKeyExchange{
-			EllipticCurveType:  elliptic.CurveTypeNamedCurve,
-			NamedCurve:         elliptic.X25519,
-			PublicKey:          rawServerKeyExchange[4:69],
-			HashAlgorithm:      hash.SHA1,
-			SignatureAlgorithm: signature.ECDSA,
-			Signature:          rawServerKeyExchange[73:144],
+			EllipticCurveType:    elliptic.CurveTypeNamedCurve,
+			NamedCurve:           elliptic.X25519,
+			PublicKey:            rawServerKeyExchange[4:69],
+			HashAlgorithm:        hash.SHA1,
+			SignatureAlgorithm:   signature.ECDSA,
+			Signature:            rawServerKeyExchange[73:144],
+			KeyExchangeAlgorithm: types.KeyExchangeAlgorithmEcdhe,
 		}
 
 		test(rawServerKeyExchange, parsedServerKeyExchange)
@@ -59,11 +63,12 @@ func TestHandshakeMessageServerKeyExchange(t *testing.T) {
 			0x45, 0x28, 0xac, 0x3f, 0x35,
 		}
 		parsedServerKeyExchange := &MessageServerKeyExchange{
-			EllipticCurveType:  elliptic.CurveTypeNamedCurve,
-			NamedCurve:         elliptic.X25519,
-			PublicKey:          rawServerKeyExchange[4:69],
-			HashAlgorithm:      hash.None,
-			SignatureAlgorithm: signature.Anonymous,
+			EllipticCurveType:    elliptic.CurveTypeNamedCurve,
+			NamedCurve:           elliptic.X25519,
+			PublicKey:            rawServerKeyExchange[4:69],
+			HashAlgorithm:        hash.None,
+			SignatureAlgorithm:   signature.Anonymous,
+			KeyExchangeAlgorithm: types.KeyExchangeAlgorithmEcdhe,
 		}
 
 		test(rawServerKeyExchange, parsedServerKeyExchange)