Synss / python-mbedtls

Cryptographic library with an mbed TLS back end
MIT License
79 stars 28 forks source link

Use of PSK for DTLS #29

Closed Krolken closed 4 years ago

Krolken commented 4 years ago

NOTE: Please use stackoverflow for support questions. This repository's issues are reserved for feature requests and bug reports.

I am submitting a …

Description

I read the source code and I can't see a way to use PSK for DTLS. It seems only to handle certificates. Is there a way to specify the use of PSK with the current implementation? Otherwise what would be needed to implement PSK? Is it possible to have a PSK store? My use case involves many devices with a different PSK. I haven't found a good way of managing individual device PSK in other open source projects so thought it might be best to look at a lower level implementation as mbedTLS.

Synss commented 4 years ago

Hi @Krolken,

Thank you for your interest.

libmbedtls has some support for this, see mbedtls_ssl_conf_psk and mbedtls_ssl_conf_psk_cb.

Could you check with the documentation linked above whether this is what you are looking for?

Krolken commented 4 years ago

mbedtls_ssl_conf_psk_cb seems to be what I need since I am interested in making a DTLS server. But maybe it is also good to implement the client side with mbedtls_ss_conf_psk to be able to test the server implementation.

I guess _BaseConfiguration is where it could be added. What is you opinion on API? Just add it as another kwarg and always prefer PSK over cert if specified? Since the configuration is done on the context I guess not much more needs to be changed in the handshake procedure.

It would be nice to keep it callback based so that it is possible to implement an external PSK store. For example it can be OK to store in memory from config for a few fixed PSK but when you have thousands of PSKs that are changed over time it would get annoying to stop the server and add the new PSKs. On the top of my head I think I would implement a store with a simple redis backend which would fit in nicely with device management.

Example: callback

# psk_identity in ClientKeyExange seems to be an UTF8 encoded string"
def my_psk_callback(psk_identity: str)-> Union[byte, None]:
    my_psks = {'client1': b'verysecretkey', 'client2': b'alsoverysecret'}
    return my_psks.get(psk_identity, None)

dtls_srv_ctx = tls.ServerContext(tls.DTLSConfiguration(get_psk_callback=my_psk_callback))

It might also be easy for people of there where a simple way to quickly add some psks so it could also be a solution to just be able to add a dict as keystore.

dtls_srv_ctx = tls.ServerContext(tls.DTLSConfiguration(psk_store={'client1': b'verysecretkey', 'client2': b'alsoverysecret'}))

Alt always provide a PskStore object and then override get_psk to add external store


class PskStore:
   # inherit from DIct instead?
    def __init__(self, psk_map)
        self.psk_map = psk_map

   def get_psk(psk_identity):
       return self.psk_map.get(psk_identity, None)

dtls_srv_ctx = tls.ServerContext(tls.DTLSConfiguration(psk_store=PskStore({'client1': b'verysecretkey', 'client2': b'alsoverysecret'}))
Synss commented 4 years ago

@Krolken : I agree with pretty much everything you write above.

Would you prepare a pull request? If so:

Otherwise, I would happily support this feature but I cannot promise doing it in a timely fashion.

Synss commented 4 years ago

Hi @Krolken,

I have drafted something now and I would rather expose two configuration options only and keep the callback an implementation detail on the C side.

The two options are the key for the client and a store for the server. The key must be a two-tuple with an identifier and the PSK. The store must have the Sequence protocol, that is __len__() and __getitem__() and map identifiers to PSKs.

This way, the store may be a dict as you suggested for the simplest case or a database as well. Users may need to write some glue but implementing two methods is not much.

In the end, it is like your class PskStore.

Now I have to make sure it actually works :wink:

Synss commented 4 years ago

Mapping type makes more sense than Sequence. I have pushed my draft to https://github.com/Synss/python-mbedtls/tree/issue_29 now. Maybe it works already, maybe not. I need more tests.

Also note that this is a temporary branch and I allow myself to change the history in that case. You may want to be careful if you fetch the branch (or just let me know and I will try to be more careful).

You can have a look anyway.

Synss commented 4 years ago

I have added tests and fixed a bug in the callback. The latest version is once more in the issue_29 branch. I still have to document the feature before releasing.

Anyway, now it should work. Feedback is welcome.

Krolken commented 4 years ago

Hi. Thanks for taking the time. And sorry for my late reponse. I have been bogged down in some other issues.

I agree that Mapping seems more appropriate than Sequence. I am looking into using DLTS for IoT use cases and it can become a lot of devices and keys to keep track of.

I will receive some test devices hopefully next week and can see if the DTLS works with them. I'll try and make a simple socketserver using the psk implementation and see what happens.

My end goal is to have a proper CoAP server that is implemented similar to HTTP servers so I can build out LWM2M functionality on top of CoAP. I had to use the Java implementation Leshan to keep the project going further and wrap it an a RPC-interface so I can build my device management in Python. But I would rather use python all the way. And if this works I guess the next step is to get it to work with asyncio. Just trying to get my head around asyncio ssl_proto gave me a headache...

Synss commented 4 years ago

Hi,

Your project looks interesting as well! If you go that far, you can most likely use python-mbedtls in asyncio.

Anyway, PSK is in master now. So this issue is closed.

Feel free to report bugs, your success, or submit patches here!