Repository: pion/dtls
Pull request #214 (2020-03-08 03:37:03)

Close on outgoing sequence number overflow
Implementation must either abandon an association or rehandshake. RFC 6347 Section 4.1.
Close connection for now.


codecov[bot]: # [Codecov](https://codecov.io/gh/pion/dtls/pull/214?src=pr&el=h1) Report
> Merging [#214](https://codecov.io/gh/pion/dtls/pull/214?src=pr&el=desc) into [master](https://codecov.io/gh/pion/dtls/commit/1a6006035b6484338e08224ebfdc5ed1cad1b5e3?src=pr&el=desc) will **decrease** coverage by `0.07%`.
> The diff coverage is `50%`.

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

```diff
@@            Coverage Diff             @@
##           master     #214      +/-   ##
==========================================
- Coverage   77.09%   77.01%   -0.08%     
==========================================
  Files          75       75              
  Lines        4055     4059       +4     
==========================================
  Hits         3126     3126              
- Misses        627      631       +4     
  Partials      302      302
```

| Flag | Coverage Δ | |
|---|---|---|
| #go | `76.98% <50%> (-0.08%)` | :arrow_down: |
| #wasm | `71.81% <50%> (+0.02%)` | :arrow_up: |

| [Impacted Files](https://codecov.io/gh/pion/dtls/pull/214?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [conn.go](https://codecov.io/gh/pion/dtls/pull/214/diff?src=pr&el=tree#diff-Y29ubi5nbw==) | `83.74% <50%> (+0.11%)` | :arrow_up: |
| [internal/net/udp/conn.go](https://codecov.io/gh/pion/dtls/pull/214/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbmV0L3VkcC9jb25uLmdv) | `82.03% <0%> (-3.13%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/dtls/pull/214?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/214?src=pr&el=footer). Last update [1a60060...1d798cf](https://codecov.io/gh/pion/dtls/pull/214?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

at-wat: rebased

Patch:
diff --git a/README.md b/README.md
index 4086447..885d0f8 100644
--- a/README.md
+++ b/README.md
@@ -134,6 +134,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
 * [Robert Eperjesi](https://github.com/epes)
 * [Atsushi Watanabe](https://github.com/at-wat)
 * [Julien Salleyron](https://github.com/juliens) - *Server Name Indication*
+* [Jeroen de Bruijn](https://github.com/vidavidorra)
 
 ### License
 MIT License - see [LICENSE](LICENSE) for full text
diff --git a/config.go b/config.go
index 059865f..ad082f6 100644
--- a/config.go
+++ b/config.go
@@ -24,6 +24,9 @@ type Config struct {
 	// If CipherSuites is nil, a default list is used
 	CipherSuites []CipherSuiteID
 
+	// SignatureSchemes contains the signature and hash schemes that the peer requests to verify.
+	SignatureSchemes []tls.SignatureScheme
+
 	// SRTPProtectionProfiles are the supported protection profiles
 	// Clients will send this via use_srtp and assert that the server properly responds
 	// Servers will assert that clients send one of these profiles and will respond as needed
diff --git a/conn.go b/conn.go
index f0a20ef..ed59bd1 100644
--- a/conn.go
+++ b/conn.go
@@ -48,7 +48,6 @@ type Conn struct {
 
 	encryptedPackets [][]byte
 
-	connectionClosed       bool
 	connectionClosedByUser bool
 	closeLock              sync.Mutex
 	closed                 *closer.Closer
@@ -84,6 +83,11 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient
 		return nil, err
 	}
 
+	signatureSchemes, err := parseSignatureSchemes(config.SignatureSchemes)
+	if err != nil {
+		return nil, err
+	}
+
 	workerInterval := initialTickerInterval
 	if config.FlightInterval != 0 {
 		workerInterval = config.FlightInterval
@@ -150,6 +154,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient
 		localPSKCallback:            config.PSK,
 		localPSKIdentityHint:        config.PSKIdentityHint,
 		localCipherSuites:           cipherSuites,
+		localSignatureSchemes:       signatureSchemes,
 		extendedMasterSecret:        config.ExtendedMasterSecret,
 		localSRTPProtectionProfiles: config.SRTPProtectionProfiles,
 		serverName:                  serverName,
@@ -273,8 +278,6 @@ func (c *Conn) Read(p []byte) (n int, err error) {
 		select {
 		case <-c.readDeadline.Done():
 			return 0, errDeadlineExceeded
-		case <-c.closed.Done():
-			return 0, io.EOF
 		case out, ok := <-c.decrypted:
 			if !ok {
 				return 0, io.EOF
@@ -452,6 +455,12 @@ func (c *Conn) processPacket(p *packet) ([]byte, error) {
 		c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0))
 	}
 	seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1
+	if seq > maxSequenceNumber {
+		// RFC 6347 Section 4.1.0
+		// The implementation must either abandon an association or rehandshake
+		// prior to allowing the sequence number to wrap.
+		return nil, errSequenceNumberOverflow
+	}
 	p.record.recordLayerHeader.sequenceNumber = seq
 
 	rawPacket, err := p.record.Marshal()
@@ -484,6 +493,9 @@ func (c *Conn) processHandshakePacket(p *packet, h *handshake) ([][]byte, error)
 
 	for _, handshakeFragment := range handshakeFragments {
 		seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1
+		if seq > maxSequenceNumber {
+			return nil, errSequenceNumberOverflow
+		}
 
 		recordLayerHeader := &recordLayerHeader{
 			protocolVersion: p.record.recordLayerHeader.protocolVersion,
@@ -755,10 +767,6 @@ func (c *Conn) handleIncomingPacket(buf []byte, enqueue bool) (bool, *alert, err
 			return false, &alert{alertLevelFatal, alertUnexpectedMessage}, fmt.Errorf("ApplicationData with epoch of 0")
 		}
 
-		if c.isConnectionClosed() {
-			return false, nil, io.EOF
-		}
-
 		markPacketAsValid()
 
 		select {
@@ -836,6 +844,11 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
 		}
 	}()
 	go func() {
+		defer func() {
+			// Escaping read loop.
+			// It's safe to close decrypted channnel now.
+			close(c.decrypted)
+		}()
 		defer c.handshakeLoopsFinished.Done()
 		for {
 			if err := c.readAndBuffer(ctxRead); err != nil {
@@ -910,7 +923,6 @@ func (c *Conn) translateHandshakeCtxError(err error) error {
 func (c *Conn) close(byUser bool) error {
 	c.cancelHandshaker()
 	c.cancelHandshakeReader()
-	c.closed.Close()
 
 	if c.isHandshakeCompletedSuccessfully() && byUser {
 		// Discard error from notify() to return non-error on the first user call of Close()
@@ -924,9 +936,7 @@ func (c *Conn) close(byUser bool) error {
 	if byUser {
 		c.connectionClosedByUser = true
 	}
-	if !c.connectionClosed {
-		c.connectionClosed = true
-	}
+	c.closed.Close()
 	c.closeLock.Unlock()
 
 	if closedByUser {
@@ -937,9 +947,12 @@ func (c *Conn) close(byUser bool) error {
 }
 
 func (c *Conn) isConnectionClosed() bool {
-	c.closeLock.Lock()
-	defer c.closeLock.Unlock()
-	return c.connectionClosed
+	select {
+	case <-c.closed.Done():
+		return true
+	default:
+		return false
+	}
 }
 
 func (c *Conn) setLocalEpoch(epoch uint16) {
diff --git a/crypto.go b/crypto.go
index 3634a4f..4eb2a99 100644
--- a/crypto.go
+++ b/crypto.go
@@ -19,7 +19,7 @@ type ecdsaSignature struct {
 	R, S *big.Int
 }
 
-func valueKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve namedCurve, hashAlgorithm hashAlgorithm) []byte {
+func valueKeyMessage(clientRandom, serverRandom, publicKey []byte, namedCurve namedCurve) []byte {
 	serverECDHParams := make([]byte, 4)
 	serverECDHParams[0] = 3 // named curve
 	binary.BigEndian.PutUint16(serverECDHParams[1:], uint16(namedCurve))
@@ -30,7 +30,8 @@ func valueKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve
 	plaintext = append(plaintext, serverRandom...)
 	plaintext = append(plaintext, serverECDHParams...)
 	plaintext = append(plaintext, publicKey...)
-	return hashAlgorithm.digest(plaintext)
+
+	return plaintext
 }
 
 // If the client provided a "signature_algorithms" extension, then all
@@ -39,21 +40,23 @@ func valueKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve
 //
 // https://tools.ietf.org/html/rfc5246#section-7.4.2
 func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve namedCurve, privateKey crypto.PrivateKey, hashAlgorithm hashAlgorithm) ([]byte, error) {
-	hashed := valueKeySignature(clientRandom, serverRandom, publicKey, namedCurve, hashAlgorithm)
+	msg := valueKeyMessage(clientRandom, serverRandom, publicKey, namedCurve)
 	switch p := privateKey.(type) {
 	case ed25519.PrivateKey:
 		// https://crypto.stackexchange.com/a/55483
-		return p.Sign(rand.Reader, hashed, crypto.Hash(0))
+		return p.Sign(rand.Reader, msg, crypto.Hash(0))
 	case *ecdsa.PrivateKey:
-		return p.Sign(rand.Reader, hashed, crypto.SHA256)
+		hashed := hashAlgorithm.digest(msg)
+		return p.Sign(rand.Reader, hashed, hashAlgorithm.cryptoHash())
 	case *rsa.PrivateKey:
-		return p.Sign(rand.Reader, hashed, crypto.SHA256)
+		hashed := hashAlgorithm.digest(msg)
+		return p.Sign(rand.Reader, hashed, hashAlgorithm.cryptoHash())
 	}
 
 	return nil, errKeySignatureGenerateUnimplemented
 }
 
-func verifyKeySignature(hash, remoteKeySignature []byte, hashAlgorithm hashAlgorithm, rawCertificates [][]byte) error {
+func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hashAlgorithm, rawCertificates [][]byte) error {
 	if len(rawCertificates) == 0 {
 		return errLengthMismatch
 	}
@@ -64,7 +67,7 @@ func verifyKeySignature(hash, remoteKeySignature []byte, hashAlgorithm hashAlgor
 
 	switch p := certificate.PublicKey.(type) {
 	case ed25519.PublicKey:
-		if ok := ed25519.Verify(p, hash, remoteKeySignature); !ok {
+		if ok := ed25519.Verify(p, message, remoteKeySignature); !ok {
 			return errKeySignatureMismatch
 		}
 		return nil
@@ -76,14 +79,16 @@ func verifyKeySignature(hash, remoteKeySignature []byte, hashAlgorithm hashAlgor
 		if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
 			return errInvalidECDSASignature
 		}
-		if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) {
+		hashed := hashAlgorithm.digest(message)
+		if !ecdsa.Verify(p, hashed, ecdsaSig.R, ecdsaSig.S) {
 			return errKeySignatureMismatch
 		}
 		return nil
 	case *rsa.PublicKey:
 		switch certificate.SignatureAlgorithm {
 		case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
-			return rsa.VerifyPKCS1v15(p, hashAlgorithm.cryptoHash(), hash, remoteKeySignature)
+			hashed := hashAlgorithm.digest(message)
+			return rsa.VerifyPKCS1v15(p, hashAlgorithm.cryptoHash(), hashed, remoteKeySignature)
 		}
 	}
 
@@ -98,7 +103,7 @@ func verifyKeySignature(hash, remoteKeySignature []byte, hashAlgorithm hashAlgor
 // CertificateVerify message is sent to explicitly verify possession of
 // the private key in the certificate.
 // https://tools.ietf.org/html/rfc5246#section-7.3
-func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey) ([]byte, error) {
+func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey, hashAlgorithm hashAlgorithm) ([]byte, error) {
 	h := sha256.New()
 	if _, err := h.Write(handshakeBodies); err != nil {
 		return nil, err
@@ -110,9 +115,9 @@ func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.Private
 		// https://crypto.stackexchange.com/a/55483
 		return p.Sign(rand.Reader, hashed, crypto.Hash(0))
 	case *ecdsa.PrivateKey:
-		return p.Sign(rand.Reader, hashed, crypto.SHA256)
+		return p.Sign(rand.Reader, hashed, hashAlgorithm.cryptoHash())
 	case *rsa.PrivateKey:
-		return p.Sign(rand.Reader, hashed, crypto.SHA256)
+		return p.Sign(rand.Reader, hashed, hashAlgorithm.cryptoHash())
 	}
 
 	return nil, errInvalidSignatureAlgorithm
@@ -127,10 +132,9 @@ func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hashAlgorithm
 		return err
 	}
 
-	hash := hashAlgorithm.digest(handshakeBodies)
 	switch p := certificate.PublicKey.(type) {
 	case ed25519.PublicKey:
-		if ok := ed25519.Verify(p, hash, remoteKeySignature); !ok {
+		if ok := ed25519.Verify(p, handshakeBodies, remoteKeySignature); !ok {
 			return errKeySignatureMismatch
 		}
 		return nil
@@ -142,6 +146,7 @@ func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hashAlgorithm
 		if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
 			return errInvalidECDSASignature
 		}
+		hash := hashAlgorithm.digest(handshakeBodies)
 		if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) {
 			return errKeySignatureMismatch
 		}
@@ -149,6 +154,7 @@ func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hashAlgorithm
 	case *rsa.PublicKey:
 		switch certificate.SignatureAlgorithm {
 		case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
+			hash := hashAlgorithm.digest(handshakeBodies)
 			return rsa.VerifyPKCS1v15(p, hashAlgorithm.cryptoHash(), hash, remoteKeySignature)
 		}
 	}
diff --git a/errors.go b/errors.go
index 43821b3..4546411 100644
--- a/errors.go
+++ b/errors.go
@@ -54,6 +54,7 @@ var (
 	errKeySignatureMismatch             = &ErrFatal{errors.New("expected and actual key signature do not match")}
 	errNilNextConn                      = &ErrFatal{errors.New("Conn can not be created with a nil nextConn")}
 	errNoAvailableCipherSuites          = &ErrFatal{errors.New("connection can not be created, no CipherSuites satisfy this Config")}
+	errNoAvailableSignatureSchemes      = &ErrFatal{errors.New("connection can not be created, no SignatureScheme satisfy this Config")}
 	errNoCertificates                   = &ErrFatal{errors.New("no certificates configured")}
 	errNoConfigProvided                 = &ErrFatal{errors.New("no config provided")}
 	errNoSupportedEllipticCurves        = &ErrFatal{errors.New("client requested zero or more elliptic curves that are not supported by the server")}
diff --git a/examples/listen-psk/main.go b/examples/listen-psk/main.go
index 72eb3c3..41d64a5 100644
--- a/examples/listen-psk/main.go
+++ b/examples/listen-psk/main.go
@@ -56,6 +56,10 @@ func main() {
 			util.Check(err)
 			// defer conn.Close() // TODO: graceful shutdown
 
+			// `conn` is of type `net.Conn` but may be casted to `dtls.Conn`
+			// using `dtlsConn := conn.(*dtls.Conn)` in order to to expose
+			// functions like `RemoteCertificate` etc.
+
 			// Register the connection with the chat hub
 			hub.Register(conn)
 		}
diff --git a/examples/listen/main.go b/examples/listen/main.go
index 6243252..d3ad255 100644
--- a/examples/listen/main.go
+++ b/examples/listen/main.go
@@ -57,6 +57,10 @@ func main() {
 			util.Check(err)
 			// defer conn.Close() // TODO: graceful shutdown
 
+			// `conn` is of type `net.Conn` but may be casted to `dtls.Conn`
+			// using `dtlsConn := conn.(*dtls.Conn)` in order to to expose
+			// functions like `RemoteCertificate` etc.
+
 			// Register the connection with the chat hub
 			hub.Register(conn)
 		}
diff --git a/flight1handler.go b/flight1handler.go
index f1bed7b..88e9607 100644
--- a/flight1handler.go
+++ b/flight1handler.go
@@ -49,14 +49,7 @@ func flight1Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 
 	extensions := []extension{
 		&extensionSupportedSignatureAlgorithms{
-			signatureHashAlgorithms: []signatureHashAlgorithm{
-				{hashAlgorithmSHA256, signatureAlgorithmECDSA},
-				{hashAlgorithmSHA384, signatureAlgorithmECDSA},
-				{hashAlgorithmSHA512, signatureAlgorithmECDSA},
-				{hashAlgorithmSHA256, signatureAlgorithmRSA},
-				{hashAlgorithmSHA384, signatureAlgorithmRSA},
-				{hashAlgorithmSHA512, signatureAlgorithmRSA},
-			},
+			signatureHashAlgorithms: cfg.localSignatureSchemes,
 		},
 	}
 	if cfg.localPSKCallback == nil {
diff --git a/flight3handler.go b/flight3handler.go
index 8854727..8949dd9 100644
--- a/flight3handler.go
+++ b/flight3handler.go
@@ -106,14 +106,7 @@ func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h
 func flight3Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert, error) {
 	extensions := []extension{
 		&extensionSupportedSignatureAlgorithms{
-			signatureHashAlgorithms: []signatureHashAlgorithm{
-				{hashAlgorithmSHA256, signatureAlgorithmECDSA},
-				{hashAlgorithmSHA384, signatureAlgorithmECDSA},
-				{hashAlgorithmSHA512, signatureAlgorithmECDSA},
-				{hashAlgorithmSHA256, signatureAlgorithmRSA},
-				{hashAlgorithmSHA384, signatureAlgorithmRSA},
-				{hashAlgorithmSHA512, signatureAlgorithmRSA},
-			},
+			signatureHashAlgorithms: cfg.localSignatureSchemes,
 		},
 	}
 	if cfg.localPSKCallback == nil {
diff --git a/flight4handler.go b/flight4handler.go
index 40ba91d..19aa562 100644
--- a/flight4handler.go
+++ b/flight4handler.go
@@ -42,6 +42,18 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh
 			handshakeCachePullRule{handshakeTypeClientKeyExchange, cfg.initialEpoch, true, false},
 		)
 
+		// Verify that the pair of hash algorithm and signiture is listed.
+		var validSignatureScheme bool
+		for _, ss := range cfg.localSignatureSchemes {
+			if ss.hash == h.hashAlgorithm && ss.signature == h.signatureAlgorithm {
+				validSignatureScheme = true
+				break
+			}
+		}
+		if !validSignatureScheme {
+			return 0, &alert{alertLevelFatal, alertInsufficientSecurity}, errNoAvailableSignatureSchemes
+		}
+
 		if err := verifyCertificateVerify(plainText, h.hashAlgorithm, h.signature, state.remoteCertificate); err != nil {
 			return 0, &alert{alertLevelFatal, alertBadCertificate}, err
 		}
@@ -218,7 +230,13 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 			return nil, &alert{alertLevelFatal, alertInternalError}, err
 		}
 
-		signature, err := generateKeySignature(clientRandom, serverRandom, state.localKeypair.publicKey, state.namedCurve, certificate.PrivateKey, hashAlgorithmSHA256)
+		// Find compatible signature scheme
+		signatureHashAlgo, err := selectSignatureScheme(cfg.localSignatureSchemes, certificate.PrivateKey)
+		if err != nil {
+			return nil, &alert{alertLevelFatal, alertInsufficientSecurity}, err
+		}
+
+		signature, err := generateKeySignature(clientRandom, serverRandom, state.localKeypair.publicKey, state.namedCurve, certificate.PrivateKey, signatureHashAlgo.hash)
 		if err != nil {
 			return nil, &alert{alertLevelFatal, alertInternalError}, err
 		}
@@ -234,8 +252,8 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 						ellipticCurveType:  ellipticCurveTypeNamedCurve,
 						namedCurve:         state.namedCurve,
 						publicKey:          state.localKeypair.publicKey,
-						hashAlgorithm:      hashAlgorithmSHA256,
-						signatureAlgorithm: signatureAlgorithmECDSA,
+						hashAlgorithm:      signatureHashAlgo.hash,
+						signatureAlgorithm: signatureHashAlgo.signature,
 						signature:          state.localKeySignature,
 					}},
 			},
@@ -249,15 +267,8 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 					},
 					content: &handshake{
 						handshakeMessage: &handshakeMessageCertificateRequest{
-							certificateTypes: []clientCertificateType{clientCertificateTypeRSASign, clientCertificateTypeECDSASign},
-							signatureHashAlgorithms: []signatureHashAlgorithm{
-								{hashAlgorithmSHA256, signatureAlgorithmRSA},
-								{hashAlgorithmSHA384, signatureAlgorithmRSA},
-								{hashAlgorithmSHA512, signatureAlgorithmRSA},
-								{hashAlgorithmSHA256, signatureAlgorithmECDSA},
-								{hashAlgorithmSHA384, signatureAlgorithmECDSA},
-								{hashAlgorithmSHA512, signatureAlgorithmECDSA},
-							},
+							certificateTypes:        []clientCertificateType{clientCertificateTypeRSASign, clientCertificateTypeECDSASign},
+							signatureHashAlgorithms: cfg.localSignatureSchemes,
 						},
 					},
 				},
diff --git a/flight5handler.go b/flight5handler.go
index 6d1004b..cf286fa 100644
--- a/flight5handler.go
+++ b/flight5handler.go
@@ -155,7 +155,13 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 			handshakeCachePullRule{handshakeTypeClientKeyExchange, cfg.initialEpoch, true, false},
 		), merged...)
 
-		certVerify, err := generateCertificateVerify(plainText, privateKey)
+		// Find compatible signature scheme
+		signatureHashAlgo, err := selectSignatureScheme(cfg.localSignatureSchemes, privateKey)
+		if err != nil {
+			return nil, &alert{alertLevelFatal, alertInsufficientSecurity}, err
+		}
+
+		certVerify, err := generateCertificateVerify(plainText, privateKey, signatureHashAlgo.hash)
 		if err != nil {
 			return nil, &alert{alertLevelFatal, alertInternalError}, err
 		}
@@ -168,8 +174,8 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 				},
 				content: &handshake{
 					handshakeMessage: &handshakeMessageCertificateVerify{
-						hashAlgorithm:      hashAlgorithmSHA256,
-						signatureAlgorithm: signatureAlgorithmECDSA,
+						hashAlgorithm:      signatureHashAlgo.hash,
+						signatureAlgorithm: signatureHashAlgo.signature,
 						signature:          state.localCertificatesVerify,
 					}},
 			},
@@ -272,8 +278,20 @@ func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeCon
 	}
 
 	if cfg.localPSKCallback == nil {
-		expectedHash := valueKeySignature(clientRandom, serverRandom, h.publicKey, h.namedCurve, h.hashAlgorithm)
-		if err = verifyKeySignature(expectedHash, h.signature, h.hashAlgorithm, state.remoteCertificate); err != nil {
+		// Verify that the pair of hash algorithm and signiture is listed.
+		var validSignatureScheme bool
+		for _, ss := range cfg.localSignatureSchemes {
+			if ss.hash == h.hashAlgorithm && ss.signature == h.signatureAlgorithm {
+				validSignatureScheme = true
+				break
+			}
+		}
+		if !validSignatureScheme {
+			return &alert{alertLevelFatal, alertInsufficientSecurity}, errNoAvailableSignatureSchemes
+		}
+
+		expectedMsg := valueKeyMessage(clientRandom, serverRandom, h.publicKey, h.namedCurve)
+		if err = verifyKeySignature(expectedMsg, h.signature, h.hashAlgorithm, state.remoteCertificate); err != nil {
 			return &alert{alertLevelFatal, alertBadCertificate}, err
 		}
 		var chains [][]*x509.Certificate
diff --git a/go.mod b/go.mod
index 1a01226..56a604d 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,7 @@ module github.com/pion/dtls/v2
 
 require (
 	github.com/pion/logging v0.2.2
-	github.com/pion/transport v0.9.1
+	github.com/pion/transport v0.9.2
 	golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
 	golang.org/x/net v0.0.0-20200301022130-244492dfa37a
 	golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
diff --git a/go.sum b/go.sum
index 303589c..4bc9177 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,8 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
 github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
-github.com/pion/transport v0.9.1 h1:ubUOWYKJOEjfw8atslUz3Ay8BaybomFGbtX5LsxB2Ko=
-github.com/pion/transport v0.9.1/go.mod h1:O5wqQ7gDvcsMmTG+aIZR0ckHIRM4CmNTOzbLT1ZFA8g=
+github.com/pion/transport v0.9.2 h1:B4k7nmf/gL+M9506Zgm6ymRtQuYYZHSlaGv25Hwrs9M=
+github.com/pion/transport v0.9.2/go.mod h1:O5wqQ7gDvcsMmTG+aIZR0ckHIRM4CmNTOzbLT1ZFA8g=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
diff --git a/handshaker.go b/handshaker.go
index 72e4b3d..a479377 100644
--- a/handshaker.go
+++ b/handshaker.go
@@ -87,7 +87,8 @@ type handshakeFSM struct {
 type handshakeConfig struct {
 	localPSKCallback            PSKCallback
 	localPSKIdentityHint        []byte
-	localCipherSuites           []cipherSuite            // Available CipherSuites, if empty use default list
+	localCipherSuites           []cipherSuite            // Available CipherSuites
+	localSignatureSchemes       []signatureHashAlgorithm // Available signature schemes
 	extendedMasterSecret        ExtendedMasterSecretType // Policy for the Extended Master Support extension
 	localSRTPProtectionProfiles []SRTPProtectionProfile  // Available SRTPProtectionProfiles, if empty no SRTP support
 	serverName                  string
diff --git a/hash_algorithm.go b/hash_algorithm.go
index ef18343..d2f3434 100644
--- a/hash_algorithm.go
+++ b/hash_algorithm.go
@@ -15,12 +15,13 @@ type hashAlgorithm uint16
 // Supported hash hash algorithms
 const (
 	// hashAlgorithmMD2    hashAlgorithm = 0 // Blacklisted
-	hashAlgorithmMD5    hashAlgorithm = 1 // Blacklisted
-	hashAlgorithmSHA1   hashAlgorithm = 2 // Blacklisted
-	hashAlgorithmSHA224 hashAlgorithm = 3
-	hashAlgorithmSHA256 hashAlgorithm = 4
-	hashAlgorithmSHA384 hashAlgorithm = 5
-	hashAlgorithmSHA512 hashAlgorithm = 6
+	hashAlgorithmMD5     hashAlgorithm = 1 // Blacklisted
+	hashAlgorithmSHA1    hashAlgorithm = 2 // Blacklisted
+	hashAlgorithmSHA224  hashAlgorithm = 3
+	hashAlgorithmSHA256  hashAlgorithm = 4
+	hashAlgorithmSHA384  hashAlgorithm = 5
+	hashAlgorithmSHA512  hashAlgorithm = 6
+	hashAlgorithmEd25519 hashAlgorithm = 8
 )
 
 // String makes hashAlgorithm printable
@@ -38,6 +39,8 @@ func (h hashAlgorithm) String() string {
 		return "sha-384" // [RFC4055]
 	case hashAlgorithmSHA512:
 		return "sha-512" // [RFC4055]
+	case hashAlgorithmEd25519:
+		return "null"
 	default:
 		return "unknown hash algorithm"
 	}
@@ -82,16 +85,19 @@ func (h hashAlgorithm) cryptoHash() crypto.Hash {
 		return crypto.SHA384
 	case hashAlgorithmSHA512:
 		return crypto.SHA512
+	case hashAlgorithmEd25519:
+		return crypto.Hash(0)
 	default:
 		return 0
 	}
 }
 
 var hashAlgorithms = map[hashAlgorithm]struct{}{
-	hashAlgorithmMD5:    {},
-	hashAlgorithmSHA1:   {},
-	hashAlgorithmSHA224: {},
-	hashAlgorithmSHA256: {},
-	hashAlgorithmSHA384: {},
-	hashAlgorithmSHA512: {},
+	hashAlgorithmMD5:     {},
+	hashAlgorithmSHA1:    {},
+	hashAlgorithmSHA224:  {},
+	hashAlgorithmSHA256:  {},
+	hashAlgorithmSHA384:  {},
+	hashAlgorithmSHA512:  {},
+	hashAlgorithmEd25519: {},
 }
diff --git a/signature_algorithm.go b/signature_algorithm.go
index c1a8508..f215cfc 100644
--- a/signature_algorithm.go
+++ b/signature_algorithm.go
@@ -4,11 +4,13 @@ package dtls
 type signatureAlgorithm uint16
 
 const (
-	signatureAlgorithmRSA   signatureAlgorithm = 1
-	signatureAlgorithmECDSA signatureAlgorithm = 3
+	signatureAlgorithmRSA     signatureAlgorithm = 1
+	signatureAlgorithmECDSA   signatureAlgorithm = 3
+	signatureAlgorithmEd25519 signatureAlgorithm = 7
 )
 
 var signatureAlgorithms = map[signatureAlgorithm]bool{
-	signatureAlgorithmRSA:   true,
-	signatureAlgorithmECDSA: true,
+	signatureAlgorithmRSA:     true,
+	signatureAlgorithmECDSA:   true,
+	signatureAlgorithmEd25519: true,
 }
diff --git a/signature_hash_algorithm.go b/signature_hash_algorithm.go
index 3f0f9e7..58ac63b 100644
--- a/signature_hash_algorithm.go
+++ b/signature_hash_algorithm.go
@@ -1,6 +1,80 @@
 package dtls
 
+import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"crypto/tls"
+
+	"golang.org/x/crypto/ed25519"
+	"golang.org/x/xerrors"
+)
+
 type signatureHashAlgorithm struct {
 	hash      hashAlgorithm
 	signature signatureAlgorithm
 }
+
+func defaultSignatureSchemes() []signatureHashAlgorithm {
+	return []signatureHashAlgorithm{
+		{hashAlgorithmSHA256, signatureAlgorithmECDSA},
+		{hashAlgorithmSHA384, signatureAlgorithmECDSA},
+		{hashAlgorithmSHA512, signatureAlgorithmECDSA},
+		{hashAlgorithmSHA256, signatureAlgorithmRSA},
+		{hashAlgorithmSHA384, signatureAlgorithmRSA},
+		{hashAlgorithmSHA512, signatureAlgorithmRSA},
+		{hashAlgorithmEd25519, signatureAlgorithmEd25519},
+	}
+}
+
+// select Signature Scheme returns most preferred and compatible scheme.
+func selectSignatureScheme(sigs []signatureHashAlgorithm, privateKey crypto.PrivateKey) (signatureHashAlgorithm, error) {
+	for _, ss := range sigs {
+		if ss.isCompatible(privateKey) {
+			return ss, nil
+		}
+	}
+	return signatureHashAlgorithm{}, errNoAvailableSignatureSchemes
+}
+
+// isCompatible checks that given private key is compatible with the signature scheme.
+func (s *signatureHashAlgorithm) isCompatible(privateKey crypto.PrivateKey) bool {
+	switch privateKey.(type) {
+	case ed25519.PrivateKey:
+		return s.signature == signatureAlgorithmEd25519
+	case *ecdsa.PrivateKey:
+		return s.signature == signatureAlgorithmECDSA
+	case *rsa.PrivateKey:
+		return s.signature == signatureAlgorithmRSA
+	default:
+		return false
+	}
+}
+
+// parseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
+// It returns default signature scheme list if no SignatureScheme is passed.
+func parseSignatureSchemes(sigs []tls.SignatureScheme) ([]signatureHashAlgorithm, error) {
+	if len(sigs) == 0 {
+		return defaultSignatureSchemes(), nil
+	}
+	out := []signatureHashAlgorithm{}
+	for _, ss := range sigs {
+		sig := signatureAlgorithm(ss & 0xFF)
+		if _, ok := signatureAlgorithms[sig]; !ok {
+			return nil, &ErrFatal{
+				xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm),
+			}
+		}
+		h := hashAlgorithm(ss >> 8)
+		if _, ok := hashAlgorithms[h]; !ok {
+			return nil, &ErrFatal{
+				xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm),
+			}
+		}
+		out = append(out, signatureHashAlgorithm{
+			hash:      h,
+			signature: sig,
+		})
+	}
+	return out, nil
+}


Test patch:
diff --git a/conn_test.go b/conn_test.go
index 4f99b20..82916e2 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -11,6 +11,7 @@ import (
 	"io"
 	"net"
 	"sync"
+	"sync/atomic"
 	"testing"
 	"time"
 
@@ -145,6 +146,76 @@ func TestReadWriteDeadline(t *testing.T) {
 	}
 }
 
+func TestSequenceNumberOverflow(t *testing.T) {
+	// Limit runtime in case of deadlocks
+	lim := test.TimeOut(5 * time.Second)
+	defer lim.Stop()
+
+	// Check for leaking routines
+	report := test.CheckRoutines(t)
+	defer report()
+
+	t.Run("ApplicationData", func(t *testing.T) {
+		ca, cb, err := pipeMemory()
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		atomic.StoreUint64(&ca.state.localSequenceNumber[1], maxSequenceNumber)
+		if _, werr := ca.Write(make([]byte, 100)); werr != nil {
+			t.Errorf("Write must send message with maximum sequence number, but errord: %v", werr)
+		}
+		if _, werr := ca.Write(make([]byte, 100)); werr != errSequenceNumberOverflow {
+			t.Errorf("Write must abandonsend message with maximum sequence number, but errord: %v", werr)
+		}
+
+		if err := ca.Close(); err != nil {
+			t.Error(err)
+		}
+		if err := cb.Close(); err != nil {
+			t.Error(err)
+		}
+	})
+	t.Run("Handshake", func(t *testing.T) {
+		ca, cb, err := pipeMemory()
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+		defer cancel()
+
+		atomic.StoreUint64(&ca.state.localSequenceNumber[0], maxSequenceNumber+1)
+
+		// Try to send handshake packet.
+		if werr := ca.writePackets(ctx, []*packet{
+			{
+				record: &recordLayer{
+					recordLayerHeader: recordLayerHeader{
+						protocolVersion: protocolVersion1_2,
+					},
+					content: &handshake{
+						handshakeMessage: &handshakeMessageClientHello{
+							version:            protocolVersion1_2,
+							cookie:             make([]byte, 64),
+							cipherSuites:       defaultCipherSuites(),
+							compressionMethods: defaultCompressionMethods,
+						}},
+				},
+			},
+		}); werr != errSequenceNumberOverflow {
+			t.Errorf("Connection must fail on handshake packet reaches maximum sequence number")
+		}
+
+		if err := ca.Close(); err != nil {
+			t.Error(err)
+		}
+		if err := cb.Close(); err != nil {
+			t.Error(err)
+		}
+	})
+}
+
 func pipeMemory() (*Conn, *Conn, error) {
 	// In memory pipe
 	ca, cb := dpipe.Pipe()
@@ -206,8 +277,6 @@ func testServer(ctx context.Context, c net.Conn, cfg *Config, generateCertificat
 }
 
 func TestHandshakeWithAlert(t *testing.T) {
-	alertErr := errors.New("alert: Alert LevelFatal: InsufficientSecurity")
-
 	// Limit runtime in case of deadlocks
 	lim := test.TimeOut(time.Second * 20)
 	defer lim.Stop()
@@ -219,28 +288,59 @@ func TestHandshakeWithAlert(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 
-	clientErr := make(chan error, 1)
-
-	ca, cb := dpipe.Pipe()
-	go func() {
-		conf := &Config{
-			CipherSuites: []CipherSuiteID{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-		}
+	cases := map[string]struct {
+		configServer, configClient *Config
+		errServer, errClient       interface{}
+	}{
+		"CipherSuiteNoIntersection": {
+			configServer: &Config{
+				CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+			},
+			configClient: &Config{
+				CipherSuites: []CipherSuiteID{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			},
+			errServer: errCipherSuiteNoIntersection,
+			errClient: "alert: Alert LevelFatal: InsufficientSecurity",
+		},
+		"SignatureSchemesNoIntersection": {
+			configServer: &Config{
+				CipherSuites:     []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+				SignatureSchemes: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256},
+			},
+			configClient: &Config{
+				CipherSuites:     []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+				SignatureSchemes: []tls.SignatureScheme{tls.ECDSAWithP521AndSHA512},
+			},
+			errServer: "alert: Alert LevelFatal: InsufficientSecurity",
+			errClient: errNoAvailableSignatureSchemes,
+		},
+	}
 
-		_, err := testClient(ctx, ca, conf, true)
-		clientErr <- err
-	}()
+	for name, testCase := range cases {
+		testCase := testCase
+		t.Run(name, func(t *testing.T) {
+			clientErr := make(chan error, 1)
 
-	config := &Config{
-		CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-	}
+			ca, cb := dpipe.Pipe()
+			go func() {
+				_, err := testClient(ctx, ca, testCase.configClient, true)
+				clientErr <- err
+			}()
 
-	if _, err := testServer(ctx, cb, config, true); err != errCipherSuiteNoIntersection {
-		t.Fatalf("TestHandshakeWithAlert: Client error exp(%v) failed(%v)", errCipherSuiteNoIntersection, err)
-	}
+			_, errServer := testServer(ctx, cb, testCase.configServer, true)
+			if errExp, ok := testCase.errServer.(error); ok && errServer != errExp {
+				t.Fatalf("Server error exp(%v) failed(%v)", errExp, errServer)
+			} else if strExp, ok := testCase.errServer.(string); ok && errServer.Error() != strExp {
+				t.Fatalf("Server error exp(%s) failed(%v)", strExp, errServer)
+			}
 
-	if err := <-clientErr; err.Error() != alertErr.Error() {
-		t.Fatalf("TestHandshakeWithAlert: Client error exp(%v) failed(%v)", alertErr, err)
+			errClient := <-clientErr
+			if errExp, ok := testCase.errClient.(error); ok && errClient != errExp {
+				t.Fatalf("Client error exp(%v) failed(%v)", errExp, errClient)
+			} else if strExp, ok := testCase.errClient.(string); ok && errClient.Error() != strExp {
+				t.Fatalf("Client error exp(%s) failed(%v)", strExp, errClient)
+			}
+		})
 	}
 }
 
diff --git a/e2e/e2e_openssl_v113_test.go b/e2e/e2e_openssl_v113_test.go
index 979b9e3..1d947b6 100644
--- a/e2e/e2e_openssl_v113_test.go
+++ b/e2e/e2e_openssl_v113_test.go
@@ -7,7 +7,7 @@ import (
 )
 
 func TestPionOpenSSLE2ESimpleED25519(t *testing.T) {
-	t.Skip("TODO: make ED25519 test work with openssl")
+	t.Skip("TODO: waiting OpenSSL's DTLS Ed25519 support")
 	t.Run("OpenSSLServer", func(t *testing.T) {
 		testPionE2ESimpleED25519(t, serverOpenSSL, clientPion)
 	})
diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go
index 531402b..68a537e 100644
--- a/e2e/e2e_test.go
+++ b/e2e/e2e_test.go
@@ -112,19 +112,16 @@ func (c *comm) assert(t *testing.T) {
 	go c.server(c)
 
 	defer func() {
-		c.clientMutex.Lock()
-		c.serverMutex.Lock()
-		defer c.clientMutex.Unlock()
-		defer c.serverMutex.Unlock()
-
-		if err := c.clientConn.Close(); err != nil {
-			t.Fatal(err)
+		if c.clientConn != nil {
+			if err := c.clientConn.Close(); err != nil {
+				t.Fatal(err)
+			}
 		}
-
-		if err := c.serverConn.Close(); err != nil {
-			t.Fatal(err)
+		if c.serverConn != nil {
+			if err := c.serverConn.Close(); err != nil {
+				t.Fatal(err)
+			}
 		}
-
 		if c.serverListener != nil {
 			if err := c.serverListener.Close(); err != nil {
 				t.Fatal(err)
@@ -223,8 +220,6 @@ func testPionE2ESimple(t *testing.T, server, client func(*comm)) {
 	report := test.CheckRoutines(t)
 	defer report()
 
-	serverPort := randomPort(t)
-
 	for _, cipherSuite := range []dtls.CipherSuiteID{
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
@@ -244,6 +239,7 @@ func testPionE2ESimple(t *testing.T, server, client func(*comm)) {
 				CipherSuites:       []dtls.CipherSuiteID{cipherSuite},
 				InsecureSkipVerify: true,
 			}
+			serverPort := randomPort(t)
 			comm := newComm(ctx, cfg, cfg, serverPort, server, client)
 			comm.assert(t)
 		})
@@ -257,8 +253,6 @@ func testPionE2ESimplePSK(t *testing.T, server, client func(*comm)) {
 	report := test.CheckRoutines(t)
 	defer report()
 
-	serverPort := randomPort(t)
-
 	for _, cipherSuite := range []dtls.CipherSuiteID{
 		dtls.TLS_PSK_WITH_AES_128_CCM,
 		dtls.TLS_PSK_WITH_AES_128_CCM_8,
@@ -276,6 +270,7 @@ func testPionE2ESimplePSK(t *testing.T, server, client func(*comm)) {
 				PSKIdentityHint: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
 				CipherSuites:    []dtls.CipherSuiteID{cipherSuite},
 			}
+			serverPort := randomPort(t)
 			comm := newComm(ctx, cfg, cfg, serverPort, server, client)
 			comm.assert(t)
 		})
@@ -289,8 +284,6 @@ func testPionE2EMTUs(t *testing.T, server, client func(*comm)) {
 	report := test.CheckRoutines(t)
 	defer report()
 
-	serverPort := randomPort(t)
-
 	for _, mtu := range []int{
 		10000,
 		1000,
@@ -312,6 +305,7 @@ func testPionE2EMTUs(t *testing.T, server, client func(*comm)) {
 				InsecureSkipVerify: true,
 				MTU:                mtu,
 			}
+			serverPort := randomPort(t)
 			comm := newComm(ctx, cfg, cfg, serverPort, server, client)
 			comm.assert(t)
 		})
diff --git a/e2e/e2e_v113_test.go b/e2e/e2e_v113_test.go
index 7b96e56..657e6db 100644
--- a/e2e/e2e_v113_test.go
+++ b/e2e/e2e_v113_test.go
@@ -26,8 +26,6 @@ func testPionE2ESimpleED25519(t *testing.T, server, client func(*comm)) {
 	report := test.CheckRoutines(t)
 	defer report()
 
-	serverPort := randomPort(t)
-
 	for _, cipherSuite := range []dtls.CipherSuiteID{
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
@@ -53,6 +51,7 @@ func testPionE2ESimpleED25519(t *testing.T, server, client func(*comm)) {
 				CipherSuites:       []dtls.CipherSuiteID{cipherSuite},
 				InsecureSkipVerify: true,
 			}
+			serverPort := randomPort(t)
 			comm := newComm(ctx, cfg, cfg, serverPort, server, client)
 			comm.assert(t)
 		})
diff --git a/handshaker_test.go b/handshaker_test.go
index 138b9fc..45eb9ae 100644
--- a/handshaker_test.go
+++ b/handshaker_test.go
@@ -45,10 +45,11 @@ func TestHandshaker(t *testing.T) {
 	go func() {
 		defer wg.Done()
 		cfg := &handshakeConfig{
-			localCipherSuites:  cipherSuites,
-			localCertificates:  []tls.Certificate{clientCert},
-			insecureSkipVerify: true,
-			log:                logger,
+			localCipherSuites:     cipherSuites,
+			localCertificates:     []tls.Certificate{clientCert},
+			localSignatureSchemes: defaultSignatureSchemes(),
+			insecureSkipVerify:    true,
+			log:                   logger,
 			onFlightState: func(f flightVal, s handshakeState) {
 				if s == handshakeFinished {
 					cancelCli()
@@ -70,10 +71,11 @@ func TestHandshaker(t *testing.T) {
 	go func() {
 		defer wg.Done()
 		cfg := &handshakeConfig{
-			localCipherSuites:  cipherSuites,
-			localCertificates:  []tls.Certificate{clientCert},
-			insecureSkipVerify: true,
-			log:                logger,
+			localCipherSuites:     cipherSuites,
+			localCertificates:     []tls.Certificate{clientCert},
+			localSignatureSchemes: defaultSignatureSchemes(),
+			insecureSkipVerify:    true,
+			log:                   logger,
 			onFlightState: func(f flightVal, s handshakeState) {
 				if s == handshakeFinished {
 					cancelSrv()
diff --git a/hash_algorithm_test.go b/hash_algorithm_test.go
index d6c3603..bf01bd2 100644
--- a/hash_algorithm_test.go
+++ b/hash_algorithm_test.go
@@ -8,6 +8,10 @@ import (
 
 func TestHashAlgorithm_StringRoundtrip(t *testing.T) {
 	for algo := range hashAlgorithms {
+		if algo == hashAlgorithmEd25519 {
+			// Ed25519 does hashing and signing in one time.
+			continue
+		}
 		str := algo.String()
 		hash1 := algo.cryptoHash()
 		hash2, err := fingerprint.HashFromString(str)
diff --git a/signature_hash_algorithm_go113_test.go b/signature_hash_algorithm_go113_test.go
new file mode 100644
index 0000000..2071500
--- /dev/null
+++ b/signature_hash_algorithm_go113_test.go
@@ -0,0 +1,42 @@
+// +build go1.13
+
+package dtls
+
+import (
+	"crypto/tls"
+	"reflect"
+	"testing"
+
+	"golang.org/x/xerrors"
+)
+
+func TestParseSignatureSchemes_Ed25519(t *testing.T) {
+	cases := map[string]struct {
+		input    []tls.SignatureScheme
+		expected []signatureHashAlgorithm
+		err      error
+	}{
+		"Translate": {
+			input: []tls.SignatureScheme{
+				tls.Ed25519,
+			},
+			expected: []signatureHashAlgorithm{
+				{hashAlgorithmEd25519, signatureAlgorithmEd25519},
+			},
+			err: nil,
+		},
+	}
+
+	for name, testCase := range cases {
+		testCase := testCase
+		t.Run(name, func(t *testing.T) {
+			output, err := parseSignatureSchemes(testCase.input)
+			if testCase.err != nil && !xerrors.Is(err, testCase.err) {
+				t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
+			}
+			if !reflect.DeepEqual(testCase.expected, output) {
+				t.Errorf("Expected signatureHashAlgorithm:\n%+v\ngot:\n%+v", testCase.expected, output)
+			}
+		})
+	}
+}
diff --git a/signature_hash_algorithm_test.go b/signature_hash_algorithm_test.go
new file mode 100644
index 0000000..429363d
--- /dev/null
+++ b/signature_hash_algorithm_test.go
@@ -0,0 +1,66 @@
+package dtls
+
+import (
+	"crypto/tls"
+	"reflect"
+	"testing"
+
+	"golang.org/x/xerrors"
+)
+
+func TestParseSignatureSchemes(t *testing.T) {
+	cases := map[string]struct {
+		input    []tls.SignatureScheme
+		expected []signatureHashAlgorithm
+		err      error
+	}{
+		"Translate": {
+			input: []tls.SignatureScheme{
+				tls.ECDSAWithP256AndSHA256,
+				tls.ECDSAWithP384AndSHA384,
+				tls.ECDSAWithP521AndSHA512,
+				tls.PKCS1WithSHA256,
+				tls.PKCS1WithSHA384,
+				tls.PKCS1WithSHA512,
+			},
+			expected: []signatureHashAlgorithm{
+				{hashAlgorithmSHA256, signatureAlgorithmECDSA},
+				{hashAlgorithmSHA384, signatureAlgorithmECDSA},
+				{hashAlgorithmSHA512, signatureAlgorithmECDSA},
+				{hashAlgorithmSHA256, signatureAlgorithmRSA},
+				{hashAlgorithmSHA384, signatureAlgorithmRSA},
+				{hashAlgorithmSHA512, signatureAlgorithmRSA},
+			},
+			err: nil,
+		},
+		"InvalidSignatureAlgorithm": {
+			input: []tls.SignatureScheme{
+				tls.ECDSAWithP256AndSHA256, // Valid
+				0x04FF,                     // Invalid: unknown signature with SHA-256
+			},
+			expected: nil,
+			err:      errInvalidSignatureAlgorithm,
+		},
+		"InvalidHashAlgorithm": {
+			input: []tls.SignatureScheme{
+				tls.ECDSAWithP256AndSHA256, // Valid
+				0x0003,                     // Invalid: ECDSA with MD2
+			},
+			expected: nil,
+			err:      errInvalidHashAlgorithm,
+		},
+	}
+
+	for name, testCase := range cases {
+		testCase := testCase
+		t.Run(name, func(t *testing.T) {
+			output, err := parseSignatureSchemes(testCase.input)
+			if testCase.err != nil && !xerrors.Is(err, testCase.err) {
+				t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
+			}
+			if !reflect.DeepEqual(testCase.expected, output) {
+				t.Errorf("Expected signatureHashAlgorithm:\n%+v\ngot:\n%+v", testCase.expected, output)
+			}
+		})
+	}
+}