In the spirit of #127, there is a TLS-related binary size issue we encounter because we use reqwless on a resource-constrained device with TLS but without certificate verification (for a few setup-internal reasons). Unfortunately, the certificate-verification path is expensive and always compiled into the binary accounting for quite a sizeable chunk of our resulting firmware.
The key point is that reqwless' embedded-tls backend always carries a CertVerifier<'_, Aes128GcmSha256, NoClock, 4096> and a buffer for OwnedCertificate<4096> even if never used. Proposal: gate the certificate-verification path behind an off-by-default feature (e.g. client-cert) so consumers that don't verify certificates don't pay for it.
Measured concretely on an STM32F767 (Cortex-M7, thumbv7em) firmware that uses reqwless + embedded-tls with TlsVerify::None:
- ~+10 KB static RAM (
.bss), entirely attributable to reqwless' request future growing through the certificate branch (it is stored in a statically-allocated task pool)
- ~+30 KB flash (the p256 / certificate-parsing / signature-verification code that is always linked due to certificate handling)
For small MCUs this can be the difference between fitting a RAM budget and not. The baseline against this was measured was by introducing an off-by-default client-cert feature and gate the certificate-verification path with it. Items to gate (line numbers at the time of opening this issue):
| Item |
src/client.rs |
cert-only imports (CertVerifier, SignatureScheme, TlsVerifier; p256::ecdsa::*; rand_core::CryptoRngCore) |
import block (~L12) |
struct Provider<'a> + impl CryptoProvider for Provider<'a> |
L60, L67 |
TlsVerify::Certificate enum variant |
L106 |
the TlsVerify::Certificate => { .. } match arm in open() |
L226 |
Sketch (#[cfg(feature = "embedded-tls")] → #[cfg(all(feature = "embedded-tls", feature = "client-cert"))] on the cert-only items, #[cfg(feature = "client-cert")] on the enum variant):
[features]
default = ["embedded-tls"]
+client-cert = []
-#[cfg(feature = "embedded-tls")]
+#[cfg(all(feature = "embedded-tls", feature = "client-cert"))]
struct Provider<'a> { rng: .., verifier: CertVerifier<'a, Aes128GcmSha256, NoClock, 4096>, .. }
pub enum TlsVerify<'a> {
None,
Psk { .. },
+ #[cfg(feature = "client-cert")]
Certificate { ca: &'a [u8], cert: Option<&'a [u8]>, key: Option<&'a [u8]> },
}
TlsVerify::Psk { .. } => { .. }
+ #[cfg(all(feature = "embedded-tls", feature = "client-cert"))]
TlsVerify::Certificate { ca, cert, key } => { .. }
If you are open to such a change, I'd be happy to open a PR for it.
Disclaimer: snippet suggestion generated with Claude Code
In the spirit of #127, there is a TLS-related binary size issue we encounter because we use reqwless on a resource-constrained device with TLS but without certificate verification (for a few setup-internal reasons). Unfortunately, the certificate-verification path is expensive and always compiled into the binary accounting for quite a sizeable chunk of our resulting firmware.
The key point is that
reqwless'embedded-tlsbackend always carries aCertVerifier<'_, Aes128GcmSha256, NoClock, 4096>and a buffer forOwnedCertificate<4096>even if never used. Proposal: gate the certificate-verification path behind an off-by-default feature (e.g.client-cert) so consumers that don't verify certificates don't pay for it.Measured concretely on an STM32F767 (Cortex-M7,
thumbv7em) firmware that usesreqwless+embedded-tlswithTlsVerify::None:.bss), entirely attributable to reqwless' request future growing through the certificate branch (it is stored in a statically-allocated task pool)For small MCUs this can be the difference between fitting a RAM budget and not. The baseline against this was measured was by introducing an off-by-default
client-certfeature and gate the certificate-verification path with it. Items to gate (line numbers at the time of opening this issue):src/client.rsCertVerifier,SignatureScheme,TlsVerifier;p256::ecdsa::*;rand_core::CryptoRngCore)struct Provider<'a>+impl CryptoProvider for Provider<'a>TlsVerify::Certificateenum variantTlsVerify::Certificate => { .. }match arm inopen()Sketch (
#[cfg(feature = "embedded-tls")]→#[cfg(all(feature = "embedded-tls", feature = "client-cert"))]on the cert-only items,#[cfg(feature = "client-cert")]on the enum variant):[features] default = ["embedded-tls"] +client-cert = []If you are open to such a change, I'd be happy to open a PR for it.
Disclaimer: snippet suggestion generated with Claude Code