drogue-iot / embedded-tls

An Rust TLS 1.3 implementation for embedded devices.
Apache License 2.0
168 stars 21 forks source link

Introduce a way to resolve certificates on the fly #137

Open MathiasKoch opened 4 months ago

MathiasKoch commented 4 months ago

This is a follow-up to https://github.com/drogue-iot/embedded-tls/pull/135

Currently, the TlsConfig holds CA, Device Certificate and Private Key in memory, basically for the full lifetime of the TLS connection, even though they are only used during the handshake. This can be very costly on the RAM usage for no reason at all.

I would like to introduce some sort of resolver, that can load the certificates on the fly in the handshake, and then drop them again when no longer needed.

It could be something along the lines of rustls's resolves traits:

I am not quite sure if these should be part of the TlsConfig or if it would make more sense to have them as part of the provider implementation somehow:

pub trait CryptoProvider {
    type CipherSuite: TlsCipherSuite;
    type Signature: AsRef<[u8]>;

    fn rng(&mut self) -> impl CryptoRngCore;

    fn verifier(
        &mut self,
    ) -> Result<&mut impl TlsVerifier<'_, Self::CipherSuite>, crate::TlsError> {
        Err::<&mut NoVerify, _>(crate::TlsError::Unimplemented)
    }

    /// Decode and validate a private signing key from `key_der`.
    fn signer(
        &mut self,
        _key_der: &[u8], <-- REMOVE THIS
    ) -> Result<(impl signature::SignerMut<Self::Signature>, SignatureScheme), crate::TlsError>
    {
        // Resolve private key here. This could be as simple as self.priv_key, which would be the same as today.
        // It could also be I/O or flash read using self.

        // Return a signer impl based on above private key
    }
}

Similarly, the Verifier could resolve the CA, left is only something to resolve the ClientCertificate?

Not sure if i am missing something obvious here though?

lulf commented 4 months ago

Sounds like a great idea to me!

MathiasKoch commented 4 months ago

Sounds like a great idea to me!

Do you have any preferences whether it ends up being rustls'like with a new trait in the TlsConfig, or an implied part of CryptoProvider?

Another option is to add additional functions to the provider, along the lines of resolve_ca(), etc.? That would make it less implied, than requiring the signer() fn to also resolve a private key as part of the impl, on the other hand it might make lifetimes etc. much more tricky, at no other win than what could just as well be solved by proper docs?

I think I am leaning towards the implied resolution in verifier, signer and then a new fn client_cert() or similar? Solely because I would like to avoid introducing the additional generic in the config, as i this a config should be as lean as possible?

lulf commented 4 months ago

Sounds like a great idea to me!

Do you have any preferences whether it ends up being rustls'like with a new trait in the TlsConfig, or an implied part of CryptoProvider?

I don't, I think you're in a better position to make that judgement really.

Another option is to add additional functions to the provider, along the lines of resolve_ca(), etc.? That would make it less implied, than requiring the signer() fn to also resolve a private key as part of the impl, on the other hand it might make lifetimes etc. much more tricky, at no other win than what could just as well be solved by proper docs?

I think I am leaning towards the implied resolution in verifier, signer and then a new fn client_cert() or similar? Solely because I would like to avoid introducing the additional generic in the config, as i this a config should be as lean as possible?

Yeah, I think that approach is the best path forward. If it turns out not to be, we can always change it.