Repository: pion/dtls
Pull request #231 (2020-03-22 15:12:34)

Disable insecure hash algorithms by default
We add a new option on the configuratio which toggles the use of hash
algorithms that are deemed insecure. We default to disallowing insecure
hash algorithms, MD2 (which we don't support at all), MD5 and SHA-1.

When parsing signature algorithms we check whether it's being passed an
insecure hash algorithm, and whether we are configured to allow it or
not. If we don't allow it, we omit adding it, to ensure we never
advertise this capability.

daenney: Not sure if this is the best approach to solving this, but it's one and it seems to have the intended effect. 
codecov[bot]: # [Codecov](https://codecov.io/gh/pion/dtls/pull/231?src=pr&el=h1) Report
> Merging [#231](https://codecov.io/gh/pion/dtls/pull/231?src=pr&el=desc) into [master](https://codecov.io/gh/pion/dtls/commit/70aa5250c991871e0529d776818c0a6e998cb210&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `71.42%`.

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

```diff
@@            Coverage Diff             @@
##           master     #231      +/-   ##
==========================================
- Coverage   76.93%   76.92%   -0.02%     
==========================================
  Files          76       76              
  Lines        4098     4108      +10     
==========================================
+ Hits         3153     3160       +7     
- Misses        645      648       +3     
  Partials      300      300              
```

| Flag | Coverage Δ | |
|---|---|---|
| #go | `76.93% <71.42%> (-0.05%)` | :arrow_down: |
| #wasm | `71.78% <71.42%> (+0.04%)` | :arrow_up: |

| [Impacted Files](https://codecov.io/gh/pion/dtls/pull/231?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [config.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-Y29uZmlnLmdv) | `100.00% <ø> (ø)` | |
| [hash\_algorithm.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-aGFzaF9hbGdvcml0aG0uZ28=) | `59.09% <60.00%> (+2.19%)` | :arrow_up: |
| [conn.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-Y29ubi5nbw==) | `84.02% <100.00%> (-0.53%)` | :arrow_down: |
| [signature\_hash\_algorithm.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-c2lnbmF0dXJlX2hhc2hfYWxnb3JpdGhtLmdv) | `88.63% <100.00%> (+0.54%)` | :arrow_up: |
| [crypto\_gcm.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-Y3J5cHRvX2djbS5nbw==) | `65.38% <0.00%> (-3.85%)` | :arrow_down: |
| [internal/net/deadline/deadline.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbmV0L2RlYWRsaW5lL2RlYWRsaW5lLmdv) | `82.45% <0.00%> (-3.51%)` | :arrow_down: |
| [internal/net/udp/conn.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbmV0L3VkcC9jb25uLmdv) | `86.71% <0.00%> (+1.56%)` | :arrow_up: |
| [internal/net/connctx/connctx.go](https://codecov.io/gh/pion/dtls/pull/231/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbmV0L2Nvbm5jdHgvY29ubmN0eC5nbw==) | `91.35% <0.00%> (+4.93%)` | :arrow_up: |

------

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

daenney: @at-wat Addressed your comments, added an extra test too.

Patch:
diff --git a/config.go b/config.go
index ad082f6..d366274 100644
--- a/config.go
+++ b/config.go
@@ -57,6 +57,10 @@ type Config struct {
 	// This should be used only for testing.
 	InsecureSkipVerify bool
 
+	// InsecureHashes allows the use of hashing algorithms that are known
+	// to be vulnerable.
+	InsecureHashes bool
+
 	// VerifyPeerCertificate, if not nil, is called after normal
 	// certificate verification by either a client or server. It
 	// receives the certificate provided by the peer and also a flag
diff --git a/conn.go b/conn.go
index ed59bd1..b326913 100644
--- a/conn.go
+++ b/conn.go
@@ -11,8 +11,8 @@ import (
 
 	"github.com/pion/dtls/v2/internal/closer"
 	"github.com/pion/dtls/v2/internal/net/connctx"
-	"github.com/pion/dtls/v2/internal/net/deadline"
 	"github.com/pion/logging"
+	"github.com/pion/transport/deadline"
 	"github.com/pion/transport/replaydetector"
 )
 
@@ -83,7 +83,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient
 		return nil, err
 	}
 
-	signatureSchemes, err := parseSignatureSchemes(config.SignatureSchemes)
+	signatureSchemes, err := parseSignatureSchemes(config.SignatureSchemes, config.InsecureHashes)
 	if err != nil {
 		return nil, err
 	}
diff --git a/go.mod b/go.mod
index 56a604d..57f4c08 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.2
+	github.com/pion/transport v0.10.0
 	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 4bc9177..b062bab 100644
--- a/go.sum
+++ b/go.sum
@@ -1,16 +1,20 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 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.2 h1:B4k7nmf/gL+M9506Zgm6ymRtQuYYZHSlaGv25Hwrs9M=
-github.com/pion/transport v0.9.2/go.mod h1:O5wqQ7gDvcsMmTG+aIZR0ckHIRM4CmNTOzbLT1ZFA8g=
+github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80=
+github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 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 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
 golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -19,5 +23,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/hash_algorithm.go b/hash_algorithm.go
index d2f3434..29ac2da 100644
--- a/hash_algorithm.go
+++ b/hash_algorithm.go
@@ -14,7 +14,7 @@ type hashAlgorithm uint16
 
 // Supported hash hash algorithms
 const (
-	// hashAlgorithmMD2    hashAlgorithm = 0 // Blacklisted
+	hashAlgorithmMD2     hashAlgorithm = 0 // Blacklisted
 	hashAlgorithmMD5     hashAlgorithm = 1 // Blacklisted
 	hashAlgorithmSHA1    hashAlgorithm = 2 // Blacklisted
 	hashAlgorithmSHA224  hashAlgorithm = 3
@@ -27,6 +27,8 @@ const (
 // String makes hashAlgorithm printable
 func (h hashAlgorithm) String() string {
 	switch h {
+	case hashAlgorithmMD2:
+		return "md2"
 	case hashAlgorithmMD5:
 		return "md5" // [RFC3279]
 	case hashAlgorithmSHA1:
@@ -42,7 +44,7 @@ func (h hashAlgorithm) String() string {
 	case hashAlgorithmEd25519:
 		return "null"
 	default:
-		return "unknown hash algorithm"
+		return "unknown or unsupported hash algorithm"
 	}
 }
 
@@ -71,6 +73,15 @@ func (h hashAlgorithm) digest(b []byte) []byte {
 	}
 }
 
+func (h hashAlgorithm) insecure() bool {
+	switch h {
+	case hashAlgorithmMD2, hashAlgorithmMD5, hashAlgorithmSHA1:
+		return true
+	default:
+		return false
+	}
+}
+
 func (h hashAlgorithm) cryptoHash() crypto.Hash {
 	switch h {
 	case hashAlgorithmMD5:
@@ -88,7 +99,7 @@ func (h hashAlgorithm) cryptoHash() crypto.Hash {
 	case hashAlgorithmEd25519:
 		return crypto.Hash(0)
 	default:
-		return 0
+		return crypto.Hash(0)
 	}
 }
 
diff --git a/internal/net/deadline/deadline.go b/internal/net/deadline/deadline.go
deleted file mode 100644
index 6f97386..0000000
--- a/internal/net/deadline/deadline.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// Package deadline provides deadline timer used to implement
-// net.Conn compatible connection
-package deadline
-
-import (
-	"context"
-	"sync"
-	"time"
-)
-
-// Deadline signals updatable deadline timer.
-// Also, it implements context.Context.
-type Deadline struct {
-	exceeded chan struct{}
-	stop     chan struct{}
-	stopped  chan bool
-	deadline time.Time
-	mu       sync.RWMutex
-}
-
-// New creates new deadline timer.
-func New() *Deadline {
-	d := &Deadline{
-		exceeded: make(chan struct{}),
-		stop:     make(chan struct{}),
-		stopped:  make(chan bool, 1),
-	}
-	d.stopped <- true
-	return d
-}
-
-// Set new deadline. Zero value means no deadline.
-func (d *Deadline) Set(t time.Time) {
-	d.mu.Lock()
-	defer d.mu.Unlock()
-
-	d.deadline = t
-
-	close(d.stop)
-
-	select {
-	case <-d.exceeded:
-		d.exceeded = make(chan struct{})
-	default:
-		stopped := <-d.stopped
-		if !stopped {
-			d.exceeded = make(chan struct{})
-		}
-	}
-	d.stop = make(chan struct{})
-	d.stopped = make(chan bool, 1)
-
-	if t.IsZero() {
-		d.stopped <- true
-		return
-	}
-
-	if dur := time.Until(t); dur > 0 {
-		exceeded := d.exceeded
-		stopped := d.stopped
-		go func() {
-			select {
-			case <-time.After(dur):
-				close(exceeded)
-				stopped <- false
-			case <-d.stop:
-				stopped <- true
-			}
-		}()
-		return
-	}
-
-	close(d.exceeded)
-	d.stopped <- false
-}
-
-// Done receives deadline signal.
-func (d *Deadline) Done() <-chan struct{} {
-	d.mu.RLock()
-	defer d.mu.RUnlock()
-	return d.exceeded
-}
-
-// Err returns context.DeadlineExceeded if the deadline is exceeded.
-// Otherwise, it returns nil.
-func (d *Deadline) Err() error {
-	d.mu.RLock()
-	defer d.mu.RUnlock()
-	select {
-	case <-d.exceeded:
-		return context.DeadlineExceeded
-	default:
-		return nil
-	}
-}
-
-// Deadline returns current deadline.
-func (d *Deadline) Deadline() (time.Time, bool) {
-	d.mu.RLock()
-	defer d.mu.RUnlock()
-	if d.deadline.IsZero() {
-		return d.deadline, false
-	}
-	return d.deadline, true
-}
-
-// Value returns nil.
-func (d *Deadline) Value(interface{}) interface{} {
-	return nil
-}
diff --git a/internal/net/dpipe/dpipe.go b/internal/net/dpipe/dpipe.go
index 3d569e9..9afd512 100644
--- a/internal/net/dpipe/dpipe.go
+++ b/internal/net/dpipe/dpipe.go
@@ -8,7 +8,7 @@ import (
 	"sync"
 	"time"
 
-	"github.com/pion/dtls/v2/internal/net/deadline"
+	"github.com/pion/transport/deadline"
 )
 
 // Pipe creates pair of non-stream conn on memory.
diff --git a/internal/net/udp/conn.go b/internal/net/udp/conn.go
index a030641..7e3ab40 100644
--- a/internal/net/udp/conn.go
+++ b/internal/net/udp/conn.go
@@ -4,21 +4,23 @@ package udp
 import (
 	"context"
 	"errors"
-	"io"
 	"net"
 	"sync"
 	"sync/atomic"
 	"time"
 
-	"github.com/pion/dtls/v2/internal/net/deadline"
+	"github.com/pion/transport/deadline"
+	"github.com/pion/transport/packetio"
 )
 
 const receiveMTU = 8192
+const defaultListenBacklog = 128 // same as Linux default
 
 var errClosedListener = errors.New("udp: listener closed")
+var errListenQueueExceeded = errors.New("udp: listen queue exceeded")
 
-// Listener augments a connection-oriented Listener over a UDP PacketConn
-type Listener struct {
+// listener augments a connection-oriented Listener over a UDP PacketConn
+type listener struct {
 	pConn *net.UDPConn
 
 	accepting atomic.Value // bool
@@ -35,7 +37,7 @@ type Listener struct {
 }
 
 // Accept waits for and returns the next connection to the listener.
-func (l *Listener) Accept() (*Conn, error) {
+func (l *listener) Accept() (net.Conn, error) {
 	select {
 	case c := <-l.acceptCh:
 		l.connWG.Add(1)
@@ -48,17 +50,30 @@ func (l *Listener) Accept() (*Conn, error) {
 
 // Close closes the listener.
 // Any blocked Accept operations will be unblocked and return errors.
-func (l *Listener) Close() error {
+func (l *listener) Close() error {
 	var err error
 	l.doneOnce.Do(func() {
-		l.connWG.Done()
 		l.accepting.Store(false)
 		close(l.doneCh)
 
 		l.connLock.Lock()
+		// Close unaccepted connections
+	L_CLOSE:
+		for {
+			select {
+			case c := <-l.acceptCh:
+				close(c.doneCh)
+				delete(l.conns, c.rAddr.String())
+
+			default:
+				break L_CLOSE
+			}
+		}
 		nConns := len(l.conns)
 		l.connLock.Unlock()
 
+		l.connWG.Done()
+
 		if nConns == 0 {
 			// Wait if this is the final connection
 			l.readWG.Wait()
@@ -74,20 +89,35 @@ func (l *Listener) Close() error {
 }
 
 // Addr returns the listener's network address.
-func (l *Listener) Addr() net.Addr {
+func (l *listener) Addr() net.Addr {
 	return l.pConn.LocalAddr()
 }
 
-// Listen creates a new listener
-func Listen(network string, laddr *net.UDPAddr) (*Listener, error) {
+// ListenConfig stores options for listening to an address.
+type ListenConfig struct {
+	// Backlog defines the maximum length of the queue of pending
+	// connections. It is equivalent of the backlog argument of
+	// POSIX listen function.
+	// If a connection request arrives when the queue is full,
+	// the request will be silently discarded, unlike TCP.
+	// Set zero to use default value 128 which is same as Linux default.
+	Backlog int
+}
+
+// Listen creates a new listener based on the ListenConfig.
+func (lc *ListenConfig) Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
+	if lc.Backlog == 0 {
+		lc.Backlog = defaultListenBacklog
+	}
+
 	conn, err := net.ListenUDP(network, laddr)
 	if err != nil {
 		return nil, err
 	}
 
-	l := &Listener{
+	l := &listener{
 		pConn:    conn,
-		acceptCh: make(chan *Conn),
+		acceptCh: make(chan *Conn, lc.Backlog),
 		conns:    make(map[string]*Conn),
 		doneCh:   make(chan struct{}),
 	}
@@ -107,15 +137,27 @@ func Listen(network string, laddr *net.UDPAddr) (*Listener, error) {
 	return l, nil
 }
 
+// Listen creates a new listener using default ListenConfig.
+func Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
+	return (&ListenConfig{}).Listen(network, laddr)
+}
+
+var readBufferPool = &sync.Pool{
+	New: func() interface{} {
+		buf := make([]byte, receiveMTU)
+		return &buf
+	},
+}
+
 // readLoop has to tasks:
 // 1. Dispatching incoming packets to the correct Conn.
 //    It can therefore not be ended until all Conns are closed.
 // 2. Creating a new Conn when receiving from a new remote.
-func (l *Listener) readLoop() {
+func (l *listener) readLoop() {
 	defer l.readWG.Done()
-	buf := make([]byte, receiveMTU)
 
 	for {
+		buf := *(readBufferPool.Get().(*[]byte))
 		n, raddr, err := l.pConn.ReadFrom(buf)
 		if err != nil {
 			return
@@ -124,17 +166,11 @@ func (l *Listener) readLoop() {
 		if err != nil {
 			continue
 		}
-		select {
-		case <-l.doneCh:
-			return
-		case cBuf := <-conn.readCh:
-			n = copy(cBuf, buf[:n])
-			conn.sizeCh <- n
-		}
+		_, _ = conn.buffer.Write(buf[:n])
 	}
 }
 
-func (l *Listener) getConn(raddr net.Addr) (*Conn, error) {
+func (l *listener) getConn(raddr net.Addr) (*Conn, error) {
 	l.connLock.Lock()
 	defer l.connLock.Unlock()
 	conn, ok := l.conns[raddr.String()]
@@ -143,11 +179,11 @@ func (l *Listener) getConn(raddr net.Addr) (*Conn, error) {
 			return nil, errClosedListener
 		}
 		conn = l.newConn(raddr)
-		l.conns[raddr.String()] = conn
 		select {
-		case <-l.doneCh:
-			return nil, errClosedListener
 		case l.acceptCh <- conn:
+			l.conns[raddr.String()] = conn
+		default:
+			return nil, errListenQueueExceeded
 		}
 	}
 	return conn, nil
@@ -155,43 +191,31 @@ func (l *Listener) getConn(raddr net.Addr) (*Conn, error) {
 
 // Conn augments a connection-oriented connection over a UDP PacketConn
 type Conn struct {
-	listener *Listener
+	listener *listener
 
 	rAddr net.Addr
 
-	readCh chan []byte
-	sizeCh chan int
+	buffer *packetio.Buffer
 
 	doneCh   chan struct{}
 	doneOnce sync.Once
 
-	readDeadline  *deadline.Deadline
 	writeDeadline *deadline.Deadline
 }
 
-func (l *Listener) newConn(rAddr net.Addr) *Conn {
+func (l *listener) newConn(rAddr net.Addr) *Conn {
 	return &Conn{
 		listener:      l,
 		rAddr:         rAddr,
-		readCh:        make(chan []byte),
-		sizeCh:        make(chan int),
+		buffer:        packetio.NewBuffer(),
 		doneCh:        make(chan struct{}),
-		readDeadline:  deadline.New(),
 		writeDeadline: deadline.New(),
 	}
 }
 
 // Read
 func (c *Conn) Read(p []byte) (int, error) {
-	select {
-	case c.readCh <- p:
-		n := <-c.sizeCh
-		return n, nil
-	case <-c.doneCh:
-		return 0, io.EOF
-	case <-c.readDeadline.Done():
-		return 0, context.DeadlineExceeded
-	}
+	return c.buffer.Read(p)
 }
 
 // Write writes len(p) bytes from p to the DTLS connection
@@ -241,21 +265,19 @@ func (c *Conn) RemoteAddr() net.Addr {
 
 // SetDeadline implements net.Conn.SetDeadline
 func (c *Conn) SetDeadline(t time.Time) error {
-	c.readDeadline.Set(t)
 	c.writeDeadline.Set(t)
-	// Deadline of underlying connection should not be changed
-	// since the connection can be shared.
-	return nil
+	return c.SetReadDeadline(t)
 }
 
 // SetReadDeadline implements net.Conn.SetDeadline
 func (c *Conn) SetReadDeadline(t time.Time) error {
-	c.readDeadline.Set(t)
-	return nil
+	return c.buffer.SetReadDeadline(t)
 }
 
 // SetWriteDeadline implements net.Conn.SetDeadline
 func (c *Conn) SetWriteDeadline(t time.Time) error {
 	c.writeDeadline.Set(t)
+	// Write deadline of underlying connection should not be changed
+	// since the connection can be shared.
 	return nil
 }
diff --git a/listener.go b/listener.go
index 622637b..1fe3632 100644
--- a/listener.go
+++ b/listener.go
@@ -22,10 +22,22 @@ func Listen(network string, laddr *net.UDPAddr, config *Config) (net.Listener, e
 	}, nil
 }
 
+// NewListener creates a DTLS listener which accepts connections from an inner Listener.
+func NewListener(inner net.Listener, config *Config) (net.Listener, error) {
+	if err := validateConfig(config); err != nil {
+		return nil, err
+	}
+
+	return &listener{
+		config: config,
+		parent: inner,
+	}, nil
+}
+
 // listener represents a DTLS listener
 type listener struct {
 	config *Config
-	parent *udp.Listener
+	parent net.Listener
 }
 
 // Accept waits for and returns the next connection to the listener.
diff --git a/signature_hash_algorithm.go b/signature_hash_algorithm.go
index c8a15ad..dd2dff4 100644
--- a/signature_hash_algorithm.go
+++ b/signature_hash_algorithm.go
@@ -53,7 +53,7 @@ func (s *signatureHashAlgorithm) isCompatible(privateKey crypto.PrivateKey) bool
 
 // parseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
 // It returns default signature scheme list if no SignatureScheme is passed.
-func parseSignatureSchemes(sigs []tls.SignatureScheme) ([]signatureHashAlgorithm, error) {
+func parseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]signatureHashAlgorithm, error) {
 	if len(sigs) == 0 {
 		return defaultSignatureSchemes(), nil
 	}
@@ -71,10 +71,18 @@ func parseSignatureSchemes(sigs []tls.SignatureScheme) ([]signatureHashAlgorithm
 				xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm),
 			}
 		}
+		if h.insecure() && !insecureHashes {
+			continue
+		}
 		out = append(out, signatureHashAlgorithm{
 			hash:      h,
 			signature: sig,
 		})
 	}
+
+	if len(out) == 0 {
+		return nil, errNoAvailableSignatureSchemes
+	}
+
 	return out, nil
 }


Test patch:
diff --git a/internal/net/deadline/deadline_test.go b/internal/net/deadline/deadline_test.go
deleted file mode 100644
index 526a029..0000000
--- a/internal/net/deadline/deadline_test.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package deadline
-
-import (
-	"bytes"
-	"context"
-	"testing"
-	"time"
-)
-
-func TestDeadline(t *testing.T) {
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	t.Run("Deadline", func(t *testing.T) {
-		now := time.Now()
-
-		ctx0, cancel0 := context.WithDeadline(ctx, now.Add(40*time.Millisecond))
-		defer cancel0()
-		ctx1, cancel1 := context.WithDeadline(ctx, now.Add(60*time.Millisecond))
-		defer cancel1()
-		d := New()
-		d.Set(now.Add(50 * time.Millisecond))
-
-		ch := make(chan byte)
-		go sendOnDone(ctx, ctx0.Done(), ch, 0)
-		go sendOnDone(ctx, ctx1.Done(), ch, 1)
-		go sendOnDone(ctx, d.Done(), ch, 2)
-
-		calls := collectCh(ch, 3, 100*time.Millisecond)
-		expectedCalls := []byte{0, 2, 1}
-		if !bytes.Equal(calls, expectedCalls) {
-			t.Errorf("Wrong order of deadline signal, expected: %v, got: %v", expectedCalls, calls)
-		}
-	})
-
-	t.Run("DeadlineExtend", func(t *testing.T) {
-		now := time.Now()
-
-		ctx0, cancel0 := context.WithDeadline(ctx, now.Add(40*time.Millisecond))
-		defer cancel0()
-		ctx1, cancel1 := context.WithDeadline(ctx, now.Add(60*time.Millisecond))
-		defer cancel1()
-		d := New()
-		d.Set(now.Add(50 * time.Millisecond))
-		d.Set(now.Add(70 * time.Millisecond))
-
-		ch := make(chan byte)
-		go sendOnDone(ctx, ctx0.Done(), ch, 0)
-		go sendOnDone(ctx, ctx1.Done(), ch, 1)
-		go sendOnDone(ctx, d.Done(), ch, 2)
-
-		calls := collectCh(ch, 3, 100*time.Millisecond)
-		expectedCalls := []byte{0, 1, 2}
-		if !bytes.Equal(calls, expectedCalls) {
-			t.Errorf("Wrong order of deadline signal, expected: %v, got: %v", expectedCalls, calls)
-		}
-	})
-
-	t.Run("DeadlinePretend", func(t *testing.T) {
-		now := time.Now()
-
-		ctx0, cancel0 := context.WithDeadline(ctx, now.Add(40*time.Millisecond))
-		defer cancel0()
-		ctx1, cancel1 := context.WithDeadline(ctx, now.Add(60*time.Millisecond))
-		defer cancel1()
-		d := New()
-		d.Set(now.Add(50 * time.Millisecond))
-		d.Set(now.Add(30 * time.Millisecond))
-
-		ch := make(chan byte)
-		go sendOnDone(ctx, ctx0.Done(), ch, 0)
-		go sendOnDone(ctx, ctx1.Done(), ch, 1)
-		go sendOnDone(ctx, d.Done(), ch, 2)
-
-		calls := collectCh(ch, 3, 100*time.Millisecond)
-		expectedCalls := []byte{2, 0, 1}
-		if !bytes.Equal(calls, expectedCalls) {
-			t.Errorf("Wrong order of deadline signal, expected: %v, got: %v", expectedCalls, calls)
-		}
-	})
-
-	t.Run("DeadlineCancel", func(t *testing.T) {
-		now := time.Now()
-
-		ctx0, cancel0 := context.WithDeadline(ctx, now.Add(40*time.Millisecond))
-		defer cancel0()
-		d := New()
-		d.Set(now.Add(50 * time.Millisecond))
-		d.Set(time.Time{})
-
-		ch := make(chan byte)
-		go sendOnDone(ctx, ctx0.Done(), ch, 0)
-		go sendOnDone(ctx, d.Done(), ch, 1)
-
-		calls := collectCh(ch, 2, 60*time.Millisecond)
-		expectedCalls := []byte{0}
-		if !bytes.Equal(calls, expectedCalls) {
-			t.Errorf("Wrong order of deadline signal, expected: %v, got: %v", expectedCalls, calls)
-		}
-	})
-}
-
-func sendOnDone(ctx context.Context, done <-chan struct{}, dest chan byte, val byte) {
-	select {
-	case <-done:
-	case <-ctx.Done():
-		return
-	}
-	dest <- val
-}
-func collectCh(ch <-chan byte, n int, timeout time.Duration) []byte {
-	a := time.After(timeout)
-	var calls []byte
-	for len(calls) < n {
-		select {
-		case call := <-ch:
-			calls = append(calls, call)
-		case <-a:
-			return calls
-		}
-	}
-	return calls
-}
-
-func TestContext(t *testing.T) {
-	t.Run("Cancel", func(t *testing.T) {
-		d := New()
-		select {
-		case <-d.Done():
-			t.Fatal("Deadline unexpectedly done")
-		case <-time.After(50 * time.Millisecond):
-		}
-		if err := d.Err(); err != nil {
-			t.Errorf("Wrong Err(), expected: nil, got: %v", err)
-		}
-		d.Set(time.Unix(0, 1)) // exceeded
-		select {
-		case <-d.Done():
-		case <-time.After(50 * time.Millisecond):
-			t.Fatal("Timeout")
-		}
-		if err := d.Err(); err != context.DeadlineExceeded {
-			t.Errorf("Wrong Err(), expected: %v, got: %v", context.DeadlineExceeded, err)
-		}
-	})
-}
diff --git a/internal/net/udp/conn_test.go b/internal/net/udp/conn_test.go
index 615de27..5f9c8c8 100644
--- a/internal/net/udp/conn_test.go
+++ b/internal/net/udp/conn_test.go
@@ -3,8 +3,10 @@
 package udp
 
 import (
+	"bytes"
 	"fmt"
 	"net"
+	"sync"
 	"testing"
 	"time"
 
@@ -101,7 +103,123 @@ func TestListenerCloseTimeout(t *testing.T) {
 	}
 }
 
-func pipe() (*Listener, net.Conn, *net.UDPConn, error) {
+func TestListenerCloseUnaccepted(t *testing.T) {
+	// Limit runtime in case of deadlocks
+	lim := test.TimeOut(time.Second * 20)
+	defer lim.Stop()
+
+	// Check for leaking routines
+	report := test.CheckRoutines(t)
+	defer report()
+
+	const backlog = 2
+
+	network, addr := getConfig()
+	listener, err := (&ListenConfig{
+		Backlog: backlog,
+	}).Listen(network, addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for i := 0; i < backlog; i++ {
+		conn, derr := net.DialUDP(network, nil, listener.Addr().(*net.UDPAddr))
+		if derr != nil {
+			t.Error(derr)
+			continue
+		}
+		if _, werr := conn.Write([]byte{byte(i)}); werr != nil {
+			t.Error(werr)
+		}
+		if cerr := conn.Close(); cerr != nil {
+			t.Error(cerr)
+		}
+	}
+
+	time.Sleep(100 * time.Millisecond) // Wait all packets being processed by readLoop
+
+	// Unaccepted connections must be closed by listener.Close()
+	err = listener.Close()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestListenerConcurrent(t *testing.T) {
+	// Limit runtime in case of deadlocks
+	lim := test.TimeOut(time.Second * 20)
+	defer lim.Stop()
+
+	// Check for leaking routines
+	report := test.CheckRoutines(t)
+	defer report()
+
+	const backlog = 2
+
+	network, addr := getConfig()
+	listener, err := (&ListenConfig{
+		Backlog: backlog,
+	}).Listen(network, addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for i := 0; i < backlog+1; i++ {
+		conn, derr := net.DialUDP(network, nil, listener.Addr().(*net.UDPAddr))
+		if derr != nil {
+			t.Error(derr)
+			continue
+		}
+		if _, werr := conn.Write([]byte{byte(i)}); werr != nil {
+			t.Error(werr)
+		}
+		if cerr := conn.Close(); cerr != nil {
+			t.Error(cerr)
+		}
+	}
+
+	time.Sleep(100 * time.Millisecond) // Wait all packets being processed by readLoop
+
+	for i := 0; i < backlog; i++ {
+		conn, aerr := listener.Accept()
+		if aerr != nil {
+			t.Error(aerr)
+			continue
+		}
+		b := make([]byte, 1)
+		n, rerr := conn.Read(b)
+		if rerr != nil {
+			t.Error(rerr)
+		} else if !bytes.Equal([]byte{byte(i)}, b[:n]) {
+			t.Errorf("Packet from connection %d is wrong, expected: [%d], got: %v", i, i, b[:n])
+		}
+		if err = conn.Close(); err != nil {
+			t.Error(err)
+		}
+	}
+
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+		if conn, aerr := listener.Accept(); aerr != errClosedListener {
+			t.Errorf("Connection exceeding backlog limit must be discarded: %v", aerr)
+			if aerr == nil {
+				_ = conn.Close()
+			}
+		}
+	}()
+
+	time.Sleep(100 * time.Millisecond) // Last Accept should be discarded
+	err = listener.Close()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	wg.Wait()
+}
+
+func pipe() (net.Listener, net.Conn, *net.UDPConn, error) {
 	// Start listening
 	network, addr := getConfig()
 	listener, err := Listen(network, addr)
@@ -158,7 +276,7 @@ func TestConnClose(t *testing.T) {
 		report := test.CheckRoutines(t)
 		defer report()
 
-		listener, ca, cb, errPipe := pipe()
+		l, ca, cb, errPipe := pipe()
 		if errPipe != nil {
 			t.Fatal(errPipe)
 		}
@@ -168,7 +286,7 @@ func TestConnClose(t *testing.T) {
 		if err := cb.Close(); err != nil {
 			t.Errorf("Failed to close B side: %v", err)
 		}
-		if err := listener.Close(); err != nil {
+		if err := l.Close(); err != nil {
 			t.Errorf("Failed to close listener: %v", err)
 		}
 	})
@@ -177,12 +295,12 @@ func TestConnClose(t *testing.T) {
 		report := test.CheckRoutines(t)
 		defer report()
 
-		listener, ca, cb, errPipe := pipe()
+		l, ca, cb, errPipe := pipe()
 		if errPipe != nil {
 			t.Fatal(errPipe)
 		}
-		// Close listener.pConn to inject error.
-		if err := listener.pConn.Close(); err != nil {
+		// Close l.pConn to inject error.
+		if err := l.(*listener).pConn.Close(); err != nil {
 			t.Error(err)
 		}
 
@@ -192,7 +310,7 @@ func TestConnClose(t *testing.T) {
 		if err := ca.Close(); err != nil {
 			t.Errorf("Failed to close B side: %v", err)
 		}
-		if err := listener.Close(); err == nil {
+		if err := l.Close(); err == nil {
 			t.Errorf("Error is not propagated to Listener.Close")
 		}
 	})
@@ -201,19 +319,19 @@ func TestConnClose(t *testing.T) {
 		report := test.CheckRoutines(t)
 		defer report()
 
-		listener, ca, cb, errPipe := pipe()
+		l, ca, cb, errPipe := pipe()
 		if errPipe != nil {
 			t.Fatal(errPipe)
 		}
-		// Close listener.pConn to inject error.
-		if err := listener.pConn.Close(); err != nil {
+		// Close l.pConn to inject error.
+		if err := l.(*listener).pConn.Close(); err != nil {
 			t.Error(err)
 		}
 
 		if err := cb.Close(); err != nil {
 			t.Errorf("Failed to close A side: %v", err)
 		}
-		if err := listener.Close(); err != nil {
+		if err := l.Close(); err != nil {
 			t.Errorf("Failed to close listener: %v", err)
 		}
 		if err := ca.Close(); err == nil {
diff --git a/signature_hash_algorithm_go113_test.go b/signature_hash_algorithm_go113_test.go
index 2071500..fb51a26 100644
--- a/signature_hash_algorithm_go113_test.go
+++ b/signature_hash_algorithm_go113_test.go
@@ -12,9 +12,10 @@ import (
 
 func TestParseSignatureSchemes_Ed25519(t *testing.T) {
 	cases := map[string]struct {
-		input    []tls.SignatureScheme
-		expected []signatureHashAlgorithm
-		err      error
+		input          []tls.SignatureScheme
+		expected       []signatureHashAlgorithm
+		err            error
+		insecureHashes bool
 	}{
 		"Translate": {
 			input: []tls.SignatureScheme{
@@ -23,14 +24,15 @@ func TestParseSignatureSchemes_Ed25519(t *testing.T) {
 			expected: []signatureHashAlgorithm{
 				{hashAlgorithmEd25519, signatureAlgorithmEd25519},
 			},
-			err: nil,
+			err:            nil,
+			insecureHashes: false,
 		},
 	}
 
 	for name, testCase := range cases {
 		testCase := testCase
 		t.Run(name, func(t *testing.T) {
-			output, err := parseSignatureSchemes(testCase.input)
+			output, err := parseSignatureSchemes(testCase.input, testCase.insecureHashes)
 			if testCase.err != nil && !xerrors.Is(err, testCase.err) {
 				t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
 			}
diff --git a/signature_hash_algorithm_test.go b/signature_hash_algorithm_test.go
index 429363d..7b9bd71 100644
--- a/signature_hash_algorithm_test.go
+++ b/signature_hash_algorithm_test.go
@@ -10,9 +10,10 @@ import (
 
 func TestParseSignatureSchemes(t *testing.T) {
 	cases := map[string]struct {
-		input    []tls.SignatureScheme
-		expected []signatureHashAlgorithm
-		err      error
+		input          []tls.SignatureScheme
+		expected       []signatureHashAlgorithm
+		err            error
+		insecureHashes bool
 	}{
 		"Translate": {
 			input: []tls.SignatureScheme{
@@ -31,30 +32,63 @@ func TestParseSignatureSchemes(t *testing.T) {
 				{hashAlgorithmSHA384, signatureAlgorithmRSA},
 				{hashAlgorithmSHA512, signatureAlgorithmRSA},
 			},
-			err: nil,
+			insecureHashes: false,
+			err:            nil,
 		},
 		"InvalidSignatureAlgorithm": {
 			input: []tls.SignatureScheme{
 				tls.ECDSAWithP256AndSHA256, // Valid
 				0x04FF,                     // Invalid: unknown signature with SHA-256
 			},
-			expected: nil,
-			err:      errInvalidSignatureAlgorithm,
+			expected:       nil,
+			insecureHashes: false,
+			err:            errInvalidSignatureAlgorithm,
 		},
 		"InvalidHashAlgorithm": {
 			input: []tls.SignatureScheme{
 				tls.ECDSAWithP256AndSHA256, // Valid
 				0x0003,                     // Invalid: ECDSA with MD2
 			},
-			expected: nil,
-			err:      errInvalidHashAlgorithm,
+			expected:       nil,
+			insecureHashes: false,
+			err:            errInvalidHashAlgorithm,
+		},
+		"InsecureHashAlgorithmDenied": {
+			input: []tls.SignatureScheme{
+				tls.ECDSAWithP256AndSHA256, // Valid
+				tls.ECDSAWithSHA1,          // Insecure
+			},
+			expected: []signatureHashAlgorithm{
+				{hashAlgorithmSHA256, signatureAlgorithmECDSA},
+			},
+			insecureHashes: false,
+			err:            nil,
+		},
+		"InsecureHashAlgorithmAllowed": {
+			input: []tls.SignatureScheme{
+				tls.ECDSAWithP256AndSHA256, // Valid
+				tls.ECDSAWithSHA1,          // Insecure
+			},
+			expected: []signatureHashAlgorithm{
+				{hashAlgorithmSHA256, signatureAlgorithmECDSA},
+				{hashAlgorithmSHA1, signatureAlgorithmECDSA},
+			},
+			insecureHashes: true,
+			err:            nil,
+		},
+		"OnlyInsecureHashAlgorithm": {
+			input: []tls.SignatureScheme{
+				tls.ECDSAWithSHA1, // Insecure
+			},
+			insecureHashes: false,
+			err:            errNoAvailableSignatureSchemes,
 		},
 	}
 
 	for name, testCase := range cases {
 		testCase := testCase
 		t.Run(name, func(t *testing.T) {
-			output, err := parseSignatureSchemes(testCase.input)
+			output, err := parseSignatureSchemes(testCase.input, testCase.insecureHashes)
 			if testCase.err != nil && !xerrors.Is(err, testCase.err) {
 				t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
 			}