Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ type Config struct { //nolint:dupl

// ListenConfig used to create the underlying listener socket.
listenConfig net.ListenConfig

// version13
// WIP experimental feature, see https://github.com/pion/dtls/issues/188
version13 bool
}

func (c *Config) includeCertificateSuites() bool {
Expand Down
114 changes: 95 additions & 19 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,12 @@
cancelHandshaker func()
cancelHandshakeReader func()

fsm *handshakeFSM
fsm handshakeFSM

replayProtectionWindow uint

handshakeConfig *handshakeConfig
handshakeConfig *handshakeConfig
handshakeConfig13 *handshakeConfig13
}

// createConn creates a new DTLS connection.
Expand Down Expand Up @@ -245,6 +246,14 @@
},
}

if config.version13 {
handshakeConfig13 := &handshakeConfig13{
handshakeConfig: handshakeConfig,
}
conn.handshakeConfig13 = handshakeConfig13
conn.handshakeConfig = nil
}

conn.setRemoteEpoch(0)
conn.setLocalEpoch(0)

Expand Down Expand Up @@ -273,7 +282,7 @@
//
// Most uses of this package need not call HandshakeContext explicitly: the
// first [Conn.Read] or [Conn.Write] will call it automatically.
func (c *Conn) HandshakeContext(ctx context.Context) error {
func (c *Conn) HandshakeContext(ctx context.Context) error { //nolint:cyclop
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()

Expand All @@ -287,6 +296,18 @@
c.handshakeDone = handshakeDone
c.closeLock.Unlock()

if c.isVersion13Enabled() {
initialFlight := flight13_0
initialFSMState := handshakePreparing

if err := c.handshake(ctx, flightVal(initialFlight), initialFSMState); err != nil {
return err
}
c.log.Trace("Handshake DTLS 1.3 Completed")

return nil
}

// rfc5246#section-7.4.3
// In addition, the hash and signature algorithms MUST be compatible
// with the key in the server's end-entity certificate.
Expand Down Expand Up @@ -319,7 +340,7 @@
initialFSMState = handshakePreparing
}
// Do handshake
if err := c.handshake(ctx, c.handshakeConfig, initialFlight, initialFSMState); err != nil {
if err := c.handshake(ctx, initialFlight, initialFSMState); err != nil {
return err
}

Expand Down Expand Up @@ -471,6 +492,7 @@
return 0, err
}

// todo: check for version

Check failure on line 495 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

Line contains TODO/BUG/FIXME: "todo: check for version" (godox)
return len(payload), c.writePackets(c.writeDeadline, []*packet{
{
record: &recordlayer.RecordLayer{
Expand Down Expand Up @@ -797,7 +819,7 @@
},
}

func (c *Conn) readAndBuffer(ctx context.Context) error { //nolint:cyclop
func (c *Conn) readAndBuffer(ctx context.Context) error { //nolint:cyclop,gocognit
bufptr, ok := poolReadBuffer.Get().(*[]byte)
if !ok {
return errFailedToAccessPoolReadBuffer
Expand All @@ -817,6 +839,7 @@

var hasHandshake, isRetransmit bool
for _, p := range pkts {
// todo: check version

Check failure on line 842 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

Line contains TODO/BUG/FIXME: "todo: check version" (godox)
hs, rtx, alert, err := c.handleIncomingPacket(ctx, p, rAddr, true)
if alert != nil {
if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
Expand Down Expand Up @@ -864,6 +887,7 @@
c.lock.Unlock()

for _, p := range pkts {
// todo: check version

Check failure on line 890 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

Line contains TODO/BUG/FIXME: "todo: check version" (godox)
_, _, alert, err := c.handleIncomingPacket(ctx, p.data, p.rAddr, false) // don't re-enqueue
if alert != nil {
if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
Expand Down Expand Up @@ -897,6 +921,17 @@
return false
}

// nolint:unusedparams
func (c *Conn) handleIncomingPacket13(

Check failure on line 925 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

func (*Conn).handleIncomingPacket13 is unused (unused)
ctx context.Context,
buf []byte,
rAddr net.Addr,
enqueue bool,
) (bool, bool, *alert.Alert, error) {
// Placeholder function
return false, false, nil, nil
}

//nolint:gocognit,gocyclo,cyclop,maintidx
func (c *Conn) handleIncomingPacket(
ctx context.Context,
Expand Down Expand Up @@ -1118,17 +1153,29 @@
}

func (c *Conn) notify(ctx context.Context, level alert.Level, desc alert.Description) error {
if level == alert.Fatal && len(c.state.SessionID) > 0 {

Check failure on line 1156 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

`if level == alert.Fatal && len(c.state.SessionID) > 0` has complex nested blocks (complexity: 12) (nestif)
// According to the RFC, we need to delete the stored session.
// https://datatracker.ietf.org/doc/html/rfc5246#section-7.2
if ss := c.fsm.cfg.sessionStore; ss != nil {
c.log.Tracef("clean invalid session: %s", c.state.SessionID)
if err := ss.Del(c.sessionKey()); err != nil {
return err
if c.isVersion13Enabled() {
// With compability mode for 1.3, CH uses a non-empty session_id

Check failure on line 1158 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

`compability` is a misspelling of `compatibility` (misspell)
// https://datatracker.ietf.org/doc/html/rfc8446#appendix-D.4
if ss := c.fsm.(*handshakeFSM13).cfg.sessionStore; ss != nil {

Check failure on line 1160 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

type assertion must be checked (forcetypeassert)
c.log.Tracef("clean invalid session: %s", c.state.SessionID)
if err := ss.Del(c.sessionKey()); err != nil {
return err
}
}
} else {
// According to the RFC, we need to delete the stored session.
// https://datatracker.ietf.org/doc/html/rfc5246#section-7.2
if ss := c.fsm.(*handshakeFSM12).cfg.sessionStore; ss != nil {

Check failure on line 1169 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

type assertion must be checked (forcetypeassert)
c.log.Tracef("clean invalid session: %s", c.state.SessionID)
if err := ss.Del(c.sessionKey()); err != nil {
return err
}
}
}
}

// This should be updated with DTLS 1.3 record encoding.
return c.writePackets(ctx, []*packet{
{
record: &recordlayer.RecordLayer{
Expand Down Expand Up @@ -1158,20 +1205,41 @@
//nolint:cyclop,gocognit,contextcheck
func (c *Conn) handshake(
ctx context.Context,
cfg *handshakeConfig,
initialFlight flightVal,
initialState handshakeState,
) error {
c.fsm = newHandshakeFSM(&c.state, c.handshakeCache, cfg, initialFlight)

done := make(chan struct{})
ctxRead, cancelRead := context.WithCancel(context.Background())
cfg.onFlightState = func(_ flightVal, s handshakeState) {
if s == handshakeFinished && c.setHandshakeCompletedSuccessfully() {
close(done)
if c.isVersion13Enabled() {
c.fsm = &handshakeFSM13{
currentFlight: flightVal13(initialFlight),
state: &c.state,
cache: c.handshakeCache,
cfg: c.handshakeConfig13,
retransmitInterval: c.handshakeConfig13.initialRetransmitInterval,
closed: make(chan struct{}),
}
c.handshakeConfig13.onFlightState13 = func(_ flightVal13, s handshakeState) {
if c.fsm.(*handshakeFSM13).currentFlight.isLastSendFlight() {

Check failure on line 1222 in conn.go

View workflow job for this annotation

GitHub Actions / lint / Go

type assertion must be checked (forcetypeassert)
close(done)
}
}
} else {
c.fsm = &handshakeFSM12{
currentFlight: initialFlight,
state: &c.state,
cache: c.handshakeCache,
cfg: c.handshakeConfig,
retransmitInterval: c.handshakeConfig.initialRetransmitInterval,
closed: make(chan struct{}),
}
c.handshakeConfig.onFlightState = func(_ flightVal, s handshakeState) {
if s == handshakeFinished && c.setHandshakeCompletedSuccessfully() {
close(done)
}
}
}

ctxRead, cancelRead := context.WithCancel(context.Background())
ctxHs, cancel := context.WithCancel(context.Background())

c.closeLock.Lock()
Expand Down Expand Up @@ -1368,7 +1436,11 @@
// As ServerName can be like 0.example.com, it's better to add
// delimiter character which is not allowed to be in
// neither address or domain name.
return []byte(c.rAddr.String() + "_" + c.fsm.cfg.serverName)
if c.isVersion13Enabled() {
return []byte(c.rAddr.String() + "_" + c.fsm.(*handshakeFSM13).cfg.serverName)
}

return []byte(c.rAddr.String() + "_" + c.fsm.(*handshakeFSM12).cfg.serverName)
}

return c.state.SessionID
Expand All @@ -1395,3 +1467,7 @@
// Write deadline is also fully managed by this layer.
return nil
}

func (c *Conn) isVersion13Enabled() bool {
return c.handshakeConfig13 != nil
}
51 changes: 49 additions & 2 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3127,10 +3127,10 @@ func TestEllipticCurveConfiguration(t *testing.T) {
assert.True(t, ok, "Failed to default Elliptic curves")

if len(test.ConfigCurves) != 0 {
assert.Equal(t, len(test.HandshakeCurves), len(server.fsm.cfg.ellipticCurves), "Failed to configure Elliptic curves")
assert.Equal(t, len(test.HandshakeCurves), len(server.fsm.(*handshakeFSM12).cfg.ellipticCurves), "Failed to configure Elliptic curves")

for i, c := range test.ConfigCurves {
assert.Equal(t, c, server.fsm.cfg.ellipticCurves[i], "Failed to maintain Elliptic curve order")
assert.Equal(t, c, server.fsm.(*handshakeFSM12).cfg.ellipticCurves[i], "Failed to maintain Elliptic curve order")
}
}

Expand Down Expand Up @@ -3500,3 +3500,50 @@ func TestCloseWithoutHandshake(t *testing.T) {
assert.NoError(t, err)
assert.NoError(t, server.Close())
}

// WIP! Tests if DTLS 1.3 handshake flow is enabled and the correct error is returned.
func TestDTLS13Config(t *testing.T) {
ca, cb := dpipe.Pipe()

// Setup client
clientCert, err := selfsign.GenerateSelfSigned()
assert.NoError(t, err)

clientcfg, err := buildClientConfig(
WithCertificates(clientCert),
WithInsecureSkipVerify(true),
withVersion13(true),
)

assert.NoError(t, err)

client, err := Client(dtlsnet.PacketConnFromConn(ca), ca.RemoteAddr(), clientcfg)
assert.NoError(t, err)
defer func() {
_ = client.Close()
}()

_, ok := client.ConnectionState()
assert.False(t, ok)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
errorChannel := make(chan error)
go func() {
errC := client.HandshakeContext(ctx)
errorChannel <- errC
}()

// Setup server, ignore error
server, _ := testServer(ctx, dtlsnet.PacketConnFromConn(cb), cb.RemoteAddr(), &Config{}, true)
assert.NoError(t, err)

defer func() {
_ = server.Close()
}()

err = <-errorChannel
if err.Error() == errFlightUnimplemented13.Error() {
return
}
}
4 changes: 4 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ var (
//nolint:err113
errInvalidFlight = &InternalError{Err: errors.New("invalid flight number")}
//nolint:err113
errFlightUnimplemented13 = &InternalError{Err: errors.New("unimplemeted DTLS 1.3 flight")}
//nolint:err113
errStateUnimplemented13 = &InternalError{Err: errors.New("unimplemeted DTLS 1.3 handshake state")}
//nolint:err113
errKeySignatureGenerateUnimplemented = &InternalError{
Err: errors.New("unable to generate key signature, unimplemented"),
}
Expand Down
Loading
Loading