Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DTLS 1.2 Connection IDs #473

Merged
merged 4 commits into from
Aug 29, 2023

Conversation

hasheddan
Copy link
Contributor

@hasheddan hasheddan commented Aug 24, 2023

Updates pion/dtls dependency to bring in support for DTLS 1.2 Connection IDs.

Fixes #467

Wireshark capture of provided example:
image

I also ran with pion/dtls trace logging enabled, which demonstrates the continuation of the connection.

server

🥽 (go-coap) go run ./examples/dtls/cid/server/main.go 
dtls TRACE: 10:04:23.285196 handshaker.go:177: [handshake:server] Flight 0: Preparing
dtls TRACE: 10:04:23.285239 handshaker.go:177: [handshake:server] Flight 0: Sending
dtls TRACE: 10:04:23.285241 handshaker.go:177: [handshake:server] Flight 0: Waiting
dtls TRACE: 10:04:23.285308 handshaker.go:291: [handshake:server] Flight 0 -> Flight 2
dtls TRACE: 10:04:23.285311 handshaker.go:177: [handshake:server] Flight 2: Preparing
dtls TRACE: 10:04:23.285314 handshaker.go:177: [handshake:server] Flight 2: Sending
dtls TRACE: 10:04:23.285317 conn.go:420: [handshake:server] -> HelloVerifyRequest (epoch: 0, seq: 0)
dtls TRACE: 10:04:23.285342 handshaker.go:177: [handshake:server] Flight 2: Waiting
dtls TRACE: 10:04:23.285458 handshaker.go:291: [handshake:server] Flight 2 -> Flight 4
dtls TRACE: 10:04:23.285466 handshaker.go:177: [handshake:server] Flight 4: Preparing
dtls TRACE: 10:04:23.285472 handshaker.go:177: [handshake:server] Flight 4: Sending
dtls TRACE: 10:04:23.285477 conn.go:420: [handshake:server] -> ServerHello (epoch: 0, seq: 1)
dtls TRACE: 10:04:23.285481 conn.go:420: [handshake:server] -> ServerKeyExchange (epoch: 0, seq: 2)
dtls TRACE: 10:04:23.285484 conn.go:420: [handshake:server] -> ServerHelloDone (epoch: 0, seq: 3)
dtls TRACE: 10:04:23.285505 handshaker.go:177: [handshake:server] Flight 4: Waiting
dtls DEBUG: 10:04:23.285738 conn.go:886: CipherSuite not initialized, queuing packet
dtls DEBUG: 10:04:23.285751 conn.go:764: received packet of next epoch, queuing packet
Client's hint: Pion DTLS Client 
dtls TRACE: 10:04:23.285783 conn.go:892: server: <- ChangeCipherSpec (epoch: 1)
dtls TRACE: 10:04:23.285795 handshaker.go:291: [handshake:server] Flight 4 -> Flight 6
dtls TRACE: 10:04:23.285797 handshaker.go:177: [handshake:server] Flight 6: Preparing
dtls TRACE: 10:04:23.285804 handshaker.go:245: [handshake:server] -> changeCipherSpec (epoch: 1)
dtls TRACE: 10:04:23.285807 handshaker.go:177: [handshake:server] Flight 6: Sending
dtls TRACE: 10:04:23.285809 conn.go:420: [handshake:server] -> Finished (epoch: 1, seq: 4)
dtls TRACE: 10:04:23.285829 handshaker.go:177: [handshake:server] Flight 6: Finished
dtls TRACE: 10:04:23.285833 conn.go:243: Handshake Completed
2023/08/28 10:04:23 got message in handleA:  Code: GET, Token: f272b2d832504b59, Path: /a, Type: Confirmable, MessageID: 49963 from 127.0.0.1:34105
2023/08/28 10:04:23 got message in handleB:  Code: GET, Token: fa7147ece5362a99, Path: /b, Type: Confirmable, MessageID: 49964 from 127.0.0.1:34105
2023/08/28 10:04:23 got message in handleA:  Code: GET, Token: 6954967287626bc8, Path: /a, Type: Confirmable, MessageID: 49967 from 127.0.0.1:43229
2023/08/28 10:04:23 got message in handleB:  Code: GET, Token: e14c0144a32444dc, Path: /b, Type: Confirmable, MessageID: 49968 from 127.0.0.1:43229

client

🥽 (go-coap) go run ./examples/dtls/cid/client/main.go
dtls TRACE: 10:04:23.284858 handshaker.go:177: [handshake:client] Flight 1: Preparing
dtls TRACE: 10:04:23.284887 handshaker.go:177: [handshake:client] Flight 1: Sending
dtls TRACE: 10:04:23.284893 conn.go:420: [handshake:client] -> ClientHello (epoch: 0, seq: 0)
dtls TRACE: 10:04:23.284945 handshaker.go:177: [handshake:client] Flight 1: Waiting
dtls TRACE: 10:04:23.285360 handshaker.go:291: [handshake:client] Flight 1 -> Flight 3
dtls TRACE: 10:04:23.285366 handshaker.go:177: [handshake:client] Flight 3: Preparing
dtls TRACE: 10:04:23.285370 handshaker.go:177: [handshake:client] Flight 3: Sending
dtls TRACE: 10:04:23.285378 conn.go:420: [handshake:client] -> ClientHello (epoch: 0, seq: 1)
dtls TRACE: 10:04:23.285393 handshaker.go:177: [handshake:client] Flight 3: Waiting
dtls TRACE: 10:04:23.285585 flight3handler.go:102: [handshake] use cipher suite: TLS_PSK_WITH_AES_128_CCM_8
Server's hint: Pion DTLS Server 
dtls TRACE: 10:04:23.285595 handshaker.go:291: [handshake:client] Flight 3 -> Flight 5
dtls TRACE: 10:04:23.285598 handshaker.go:177: [handshake:client] Flight 5: Preparing
dtls TRACE: 10:04:23.285625 handshaker.go:245: [handshake:client] -> changeCipherSpec (epoch: 1)
dtls TRACE: 10:04:23.285627 handshaker.go:177: [handshake:client] Flight 5: Sending
dtls TRACE: 10:04:23.285630 conn.go:420: [handshake:client] -> ClientKeyExchange (epoch: 0, seq: 2)
dtls TRACE: 10:04:23.285633 conn.go:420: [handshake:client] -> Finished (epoch: 1, seq: 3)
dtls TRACE: 10:04:23.285654 handshaker.go:177: [handshake:client] Flight 5: Waiting
dtls TRACE: 10:04:23.285903 conn.go:892: client: <- ChangeCipherSpec (epoch: 1)
dtls TRACE: 10:04:23.285937 handshaker.go:291: [handshake:client] Flight 5 -> Flight 5
dtls TRACE: 10:04:23.285940 handshaker.go:177: [handshake:client] Flight 5: Finished
dtls TRACE: 10:04:23.285944 conn.go:243: Handshake Completed
2023/08/28 10:04:23 Response payload: Code: GET, Token: f272b2d832504b59, ContentFormat: text/plain;charset=utf-8, Type: Acknowledgement, MessageID: 49963, PayloadLen: 13
2023/08/28 10:04:23 Response payload: Code: Content, Token: fa7147ece5362a99, ContentFormat: text/plain;charset=utf-8, Type: Confirmable, MessageID: 55126, PayloadLen: 13
dtls TRACE: 10:04:23.286554 handshaker.go:177: [handshake:client] Flight 5: Finished
dtls TRACE: 10:04:23.286563 conn.go:243: Handshake Completed
2023/08/28 10:04:23 Response payload: Code: GET, Token: 6954967287626bc8, ContentFormat: text/plain;charset=utf-8, Type: Acknowledgement, MessageID: 49967, PayloadLen: 13
2023/08/28 10:04:23 Response payload: Code: Content, Token: e14c0144a32444dc, ContentFormat: text/plain;charset=utf-8, Type: Confirmable, MessageID: 55130, PayloadLen: 13

@jkralik
Copy link
Member

jkralik commented Aug 25, 2023

Nice. Is it possible to extend the example by resuming the connection?

@jkralik jkralik self-requested a review August 25, 2023 06:09
Copy link
Contributor Author

@hasheddan hasheddan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkralik absolutely 👍🏻 will get this updated once the referenced PR in the replace statement is merged!

Copy link
Contributor Author

@hasheddan hasheddan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkralik updated with examples, as well as logs and a capture of the behavior 👍🏻

Comment on lines +43 to +50
// wrappedListener wraps a net.Listener and implements a go-coap DTLS
// server.Listener.
// NOTE: this utility is for example purposes only. Context should be handled
// properly in meaningful scenarios.
type wrappedListener struct {
l net.Listener
closed atomic.Bool
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkralik I'm not sure if there is appetite to do so, but I would be interested in go-coap defining a common ContextListener and utility wrappers to allow for the passing of net.Listener. For now, consumers of this functionality will need to use a custom listener as demonstrated in this example.

Copy link
Member

@jkralik jkralik Aug 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I see the point. But one thing - Has handshaking been moved to Conn.Read/Write from listener.Accept? If not, we need to incorporate it with https://github.com/plgd-dev/go-coap/blob/master/net/dtlslistener.go#L37.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But one thing - Has handshaking been moved to Conn.Read/Write from listener.Accept? If not, we need to incorporate it with https://github.com/plgd-dev/go-coap/blob/master/net/dtlslistener.go#L37.

@jkralik I'm not sure I'm following your suggestion here. Handshaking has not moved -- from the perspective of the existing DTLSListener nothing has changed. In fact, setting the connectionIDGenerator on the pion/dtls config passed to NewDTLSListener would enable connection ID support. The issue is that the underlying pion/transport/udp package being used does not support routing based on Connection ID. A replacement has been introduced in https://github.com/pion/dtls/tree/master/internal/net/udp, which is in internal for now so that it can be iterated upon prior to folks taking a direct dependency on its API. It can be consumed by using the pion/dtls.Listen function.

I do think it would be good for the functionality to become the default in go-coap, but right now the DTLSListener is restrictive about the behavior it allows (anecdotally, we use a different implementation of the DTLSListener internally so introduce additional functionality). This PR maintains the existing go-coap functionality, while also demonstrating how a consumer can provide their own listener for custom functionality (including connection IDs).

It seems like previously the pion/dtls.Listener was used, but then was moved away from in order to introduce parallel handshakes. My understanding of this functionality is that the DTLSListener essentially just continuously accepts connections so that the handshake can begin and complete, perhaps before the DTLS Server calls AcceptWithContext(). It seems the major blocker from using the pion/dtls.Listener is the [starting of the pion/dtls.Server in a go pool. My question is whether this could be functionality introduced in pion/dtls instead (i.e. stop blocking Accept() on completion of the handshake), which would allow go-coap to treat the DTLS listener as just a net.Listener (with the generic wrapping to support context handling).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Note: this is what I would like to do long term, but the changes in this PR enable folks to implement custom behavior to enable connection ID support, while keeping the previous behavior of go-coap unchanged when using the provided DTLSListener implementation. This feels like a good intermediate step to me as it does not introduce any breaking changes in go-coap, but I am open to discussion and happy to explore other avenues as well!)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like previously the pion/dtls.Listener was used, but then was moved away from in order to introduce parallel handshakes. My understanding of this functionality is that the DTLSListener essentially just continuously accepts connections so that the handshake can begin and complete, perhaps before the DTLS Server calls AcceptWithContext(). It seems the major blocker from using the pion/dtls.Listener is the [starting of the pion/dtls.Server in a go pool. My question is whether this could be functionality introduced in pion/dtls instead (i.e. stop blocking Accept() on completion of the handshake), which would allow go-coap to treat the DTLS listener as just a net.Listener (with the generic wrapping to support context handling).

There is a discussion (pion/dtls#279) about moving the handshake, as it is in the golang/crypto package, to the Read/Write functions.

I agree with you; we can proceed step by step.

@@ -5,8 +5,8 @@ go 1.18
require (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like linter is stuck on replace statement, but it has been removed and required functionality has been merged upstream 👍🏻

Updates pion/dtls dependency to bring in support for DTLS 1.2 Connection
IDs.

Signed-off-by: Daniel Mangum <georgedanielmangum@gmail.com>
Adds a server example for DTLS Connection IDs. Random 8 byte CIDs are
used. Note that the built-in go-coap DTLS listener does not support this
functionality presently as it uses a UDP listener from pion/transport
that does not support routing datagrams based on attributes other than
remote address.

Signed-off-by: Daniel Mangum <georgedanielmangum@gmail.com>
Adds a DTLS Connection ID client example. Two different "connections"
are used with the second resuming using the state from the first,
allowing it to bypass performing a second DTLS handshake.

Signed-off-by: Daniel Mangum <georgedanielmangum@gmail.com>
pion/dtls uses net.ListenUDP under the hood to allow for changing remote
addresses. Therefore, if there is a mismatch between IPv4 and IPv6
between the client and server then they are unable to communicate.
Additionally, writing to 0.0.0.0 fails on Windows, so 127.0.0.1 is
specified explicitly.

Signed-off-by: Daniel Mangum <georgedanielmangum@gmail.com>
@jkralik jkralik merged commit 2f8ee9c into plgd-dev:master Aug 29, 2023
@jkralik
Copy link
Member

jkralik commented Aug 29, 2023

Thx for contribution :).

@hasheddan
Copy link
Contributor Author

Thank you for all the time and effort you have invested in this library @jkralik! We are always willing to contribute where we can :)

@jkralik
Copy link
Member

jkralik commented Aug 29, 2023

resolves #472

@nicgirault
Copy link

@jkralik your last comment mark #472 as resolved but the issue is still opened. Is serializing and deserializing a UDP DTLS connection functional?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consume pion/dtls connection ID support
3 participants