pion / dtls

DTLS 1.2 Server/Client implementation for Go
https://pion.ly/
MIT License
584 stars 154 forks source link

How to config a server with the most simple handshake? #389

Closed swechencheng closed 3 months ago

swechencheng commented 3 years ago

Summary

Is it possible to config a server using PSK with the most simple flow?

Motivation

I'm trying to follow what is described here: https://docs.oracle.com/en/java/javase/16/security/transport-layer-security-tls-protocol-overview.html#GUID-F1BFB231-BE35-4B14-BB8D-7F33D31A117D

dtls-handshake

I just want to config the server to exclude all the optional part, so no certificate, no key exchange, no signature. Is it possible to do so? If yes, how?

Thanks!

daenney commented 3 years ago

Take a look at https://github.com/pion/dtls/blob/master/examples/listen/psk/main.go. I think that's what you want? Configurability wise, whatever nobs exist on dtls.Config you can use.

swechencheng commented 3 years ago

Yeah, I am trying with that. But how to get rid of ServerKeyExchange or simply get rid of Certificate when using PSK? As Certificate is indicated as optional in the graph.

daenney commented 3 years ago

Certificates aren't used if you configure PSK.

swechencheng commented 3 years ago

But the documentation says:

    // Certificates contains certificate chain to present to the other side of the connection.
    // Server MUST set this if PSK is non-nil
    // client SHOULD sets this so CertificateRequests can be handled if PSK is non-nil
    Certificates []tls.Certificate

which makes me confused...

daenney commented 3 years ago

I think that's a typo in the docs. I believe it should be "Servers MUST set this if PSK is nil". Just don't set it in your dtls.Config.

Also, if you want to verify what happens, wireshark is your friend.

swechencheng commented 3 years ago

Well, actually I am running a coap server. But my client which uses tinydtls still complains that it receive a received handshake packet of type: server_key_exchange (12) but it is not designed to handle that. So the coap server is not giving any Certificates but it still seems to require that: https://github.com/plgd-dev/go-coap/blob/9b9018ae0577d6c64733622554a1fd870f0e6777/examples/dtls/psk/server/main.go#L56

daenney commented 3 years ago

Interesting. So that seems to happen due to https://github.com/pion/dtls/blob/2e643d98a435e17c0cd7b87bcd421fe11a0a0632/flight3handler.go#L38 most likely. I believe that should be conditional on a PSK client identity hint being set, and if it's not we should be skipping the server key exchange.

I'm not 100% sure on that, so finding some actual RFCs that state what's supposed to happen here would be good.

swechencheng commented 3 years ago

BTW, when I am looking some other implementation like Californium: https://github.com/GoogleCloudPlatform/community/blob/70507b2ac25d214c91d62b14102e9047b184225f/tutorials/cloud-iot-coap-proxy/proxy/src/main/java/com/agosto/iot/CredentialsUtil.java#L151 Their server is using something called pskIdentity, is that the same as PSKIdentityHint that your dtls lib uses?

daenney commented 3 years ago

Yeah, it's a bit confusingly named. But basically the "hint" is provided by the server, to help the client pick an identity. The client can set a specific identity. We're just reusing the same field, which we might be better off not doing to make this a bit clearer.

I don't think our implementation will actually work if you set a PSK but don't set an identity, based on https://github.com/pion/dtls/blob/2e643d98a435e17c0cd7b87bcd421fe11a0a0632/conn.go#L257

That feels wrong. Should be fine for the client to never pick an identity, even if the server gave it a hint. Though you can always set something, it's harmless if the server doesn't care about it I think :confused:.

daenney commented 3 years ago

If you can modify the code locally, I'd say try and see what happens if you make a change like this in flight3handler.go:

if cfg.localPSKCallback != nil {
  flights := []handshakeCachePullRule{
    handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
  }

  if len(cfg.localPSKIdentityHint) > 0 {
        flights = append(flights, handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true})
  }

  flights = append(flights, handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false})
  seq, msgs, ok = cache.fullPullMap(flights...)
}

That should fix it to work as expected as a server. Just make sure to not set dtls.Config.PSKIdentityHint.

swechencheng commented 3 years ago

Tested and it works! Just a syntax fix needed:

seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, flights...)

@daenney Tack så mycket! Would you make a PR for this or?

daenney commented 3 years ago

I'll take a look at that. I need to make sure we can always elide the ServerKeyExchange in the PSK scenario this way. I suspect there might be other properties that if set even with PSK we should still send ServerKeyExchange even if the identity is null.

I'll need to dig into the RFCs and hope something spells this out, b/c right now I cribbed this trick based on a paragraph in OpenSSL docs.

@sean-der You seem to have a knack for knowing these things. Any chance you can chime in?

swechencheng commented 3 years ago

Hi again, One thing I have observed from our old java implementation based on Eclipse Californium is that the server use "flight 4" to handle the same DTLS client handshake hello I used above:

Aug 20 13:31:51 Parsing HANDSHAKE message without a session
Aug 20 13:31:52 Parsing HANDSHAKE message without a session
Aug 20 13:31:52 connection: add CID=88E7A0A6C864 (size 3)
Aug 20 13:31:52 connection: CID=88E7A0A6C864 - /fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616 added! CID=0F2F63B09269 removed from address.
Aug 20 13:31:52 Setting MTU for peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616] to 1500 bytes
Aug 20 13:31:52 Setting maximum fragment length for peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616] to 1411 bytes
Aug 20 13:31:52 Processing CLIENT_HELLO (1) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616], seqn: [1]
Aug 20 13:31:52 handshake started dtls-con: CID=88E7A0A6C864, /fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616, is alive
Aug 20 13:31:52 Handshake with [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616] has been started
Aug 20 13:31:52 Negotiated cipher suite [TLS_PSK_WITH_AES_128_CCM_8] with peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616]
Aug 20 13:31:52 Processed CLIENT_HELLO (1) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616]
Aug 20 13:31:52 Updated receive window with sequence number [1]: new upper boundary [1], new bit vector [10]
Aug 20 13:31:52 Parsing HANDSHAKE message plaintext with parameter [KeyExgAl=PSK, cert.type=X_509]
Aug 20 13:31:52 response for flight 4 started
Aug 20 13:31:52 Processing CLIENT_KEY_EXCHANGE (16) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616], seqn: [2]
Aug 20 13:31:52 client [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616] uses PSK identity [XgJQ]
Aug 20 13:31:52 Processed CLIENT_KEY_EXCHANGE (16) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616]
Aug 20 13:31:52 Updated receive window with sequence number [2]: new upper boundary [2], new bit vector [110]
Aug 20 13:31:52 Processing Change Cipher Spec (20) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616]
Aug 20 13:31:52 Processed Change Cipher Spec (20) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616]
Aug 20 13:31:53 Parsing HANDSHAKE message plaintext with parameter [KeyExgAl=PSK, cert.type=X_509]
Aug 20 13:31:53 response for flight 4 started
Aug 20 13:31:53 Processing FINISHED (20) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616], seqn: [3]
Aug 20 13:31:53 Setting maximum fragment length for peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616] to 1395 bytes
Aug 20 13:31:53 session established dtls-con: CID=88E7A0A6C864, /fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616, ongoing handshake 581C5FD16E69, is alive
Aug 20 13:31:53 Session with [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616] has been established
Aug 20 13:31:53 Processed FINISHED (20) message from peer [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616]
Aug 20 13:31:53 Updated receive window with sequence number [0]: new upper boundary [0], new bit vector [1]
Aug 20 13:31:55 Updated receive window with sequence number [1]: new upper boundary [1], new bit vector [11]
Aug 20 13:31:55 Handshake with [/fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616] has been completed
Aug 20 13:31:55 handshake completed dtls-con: CID=88E7A0A6C864, /fd00:e859:ff00:0:212:4b00:1e1b:3e62:61616, session established 581C5FD16E69, is alive

Why it is flight 4 in Californium? And why we are using flight 3 in your library? What's the difference? From the RFC document I realize that flight 3 is on the client side and flight 4 is on the server side. So is your flight3handler actually handles flight 3 and do flight 4? Thanks!

Sean-Der commented 3 months ago

Two things that need to be fixed here