embedded-tls icon indicating copy to clipboard operation
embedded-tls copied to clipboard

Introduce a way to resolve certificates on the fly

Open MathiasKoch opened this issue 11 months ago • 3 comments

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:

  • https://docs.rs/rustls/latest/rustls/client/trait.ResolvesClientCert.html
  • https://docs.rs/rustls/latest/rustls/server/trait.ResolvesServerCert.html

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?

MathiasKoch avatar Feb 27 '24 14:02 MathiasKoch

Sounds like a great idea to me!

lulf avatar Feb 27 '24 14:02 lulf

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?

MathiasKoch avatar Feb 28 '24 07:02 MathiasKoch

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.

lulf avatar Feb 28 '24 07:02 lulf