Skip to main content

embedded_tls/
config.rs

1use core::marker::PhantomData;
2
3use crate::TlsError;
4use crate::cipher_suites::CipherSuite;
5use crate::extensions::extension_data::signature_algorithms::SignatureScheme;
6use crate::extensions::extension_data::supported_groups::NamedGroup;
7pub use crate::handshake::certificate::{CertificateEntryRef, CertificateRef};
8pub use crate::handshake::certificate_verify::CertificateVerifyRef;
9use aes_gcm::{AeadInPlace, Aes128Gcm, Aes256Gcm, KeyInit};
10use digest::core_api::BlockSizeUser;
11use digest::{Digest, FixedOutput, OutputSizeUser, Reset};
12use ecdsa::elliptic_curve::SecretKey;
13use generic_array::ArrayLength;
14use heapless::Vec;
15use p256::ecdsa::SigningKey;
16use rand_core::CryptoRngCore;
17pub use sha2::{Sha256, Sha384};
18use typenum::{Sum, U10, U12, U16, U32};
19
20pub use crate::extensions::extension_data::max_fragment_length::MaxFragmentLength;
21
22pub const TLS_RECORD_OVERHEAD: usize = 128;
23
24// longest label is 12b -> buf <= 2 + 1 + 6 + longest + 1 + hash_out = hash_out + 22
25type LongestLabel = U12;
26type LabelOverhead = U10;
27type LabelBuffer<CipherSuite> = Sum<
28    <<CipherSuite as TlsCipherSuite>::Hash as OutputSizeUser>::OutputSize,
29    Sum<LongestLabel, LabelOverhead>,
30>;
31
32/// Represents a TLS 1.3 cipher suite
33pub trait TlsCipherSuite {
34    const CODE_POINT: u16;
35    type Cipher: KeyInit<KeySize = Self::KeyLen> + AeadInPlace<NonceSize = Self::IvLen>;
36    type KeyLen: ArrayLength<u8>;
37    type IvLen: ArrayLength<u8>;
38
39    type Hash: Digest + Reset + Clone + OutputSizeUser + BlockSizeUser + FixedOutput;
40    type LabelBufferSize: ArrayLength<u8>;
41}
42
43pub struct Aes128GcmSha256;
44impl TlsCipherSuite for Aes128GcmSha256 {
45    const CODE_POINT: u16 = CipherSuite::TlsAes128GcmSha256 as u16;
46    type Cipher = Aes128Gcm;
47    type KeyLen = U16;
48    type IvLen = U12;
49
50    type Hash = Sha256;
51    type LabelBufferSize = LabelBuffer<Self>;
52}
53
54pub struct Aes256GcmSha384;
55impl TlsCipherSuite for Aes256GcmSha384 {
56    const CODE_POINT: u16 = CipherSuite::TlsAes256GcmSha384 as u16;
57    type Cipher = Aes256Gcm;
58    type KeyLen = U32;
59    type IvLen = U12;
60
61    type Hash = Sha384;
62    type LabelBufferSize = LabelBuffer<Self>;
63}
64
65/// A TLS 1.3 verifier.
66///
67/// The verifier is responsible for verifying certificates and signatures. Since certificate verification is
68/// an expensive process, this trait allows clients to choose how much verification should take place,
69/// and also to skip the verification if the server is verified through other means (I.e. a pre-shared key).
70pub trait TlsVerifier<CipherSuite>
71where
72    CipherSuite: TlsCipherSuite,
73{
74    /// Host verification is enabled by passing a server hostname.
75    fn set_hostname_verification(&mut self, hostname: &str) -> Result<(), crate::TlsError>;
76
77    /// Verify a certificate.
78    ///
79    /// The handshake transcript up to this point and the server certificate is provided
80    /// for the implementation to use. The verifier is responsible for resolving the CA
81    /// certificate internally.
82    fn verify_certificate(
83        &mut self,
84        transcript: &CipherSuite::Hash,
85        cert: CertificateRef,
86    ) -> Result<(), TlsError>;
87
88    /// Verify the certificate signature.
89    ///
90    /// The signature verification uses the transcript and certificate provided earlier to decode the provided signature.
91    fn verify_signature(&mut self, verify: CertificateVerifyRef) -> Result<(), crate::TlsError>;
92}
93
94pub struct NoVerify;
95
96impl<CipherSuite> TlsVerifier<CipherSuite> for NoVerify
97where
98    CipherSuite: TlsCipherSuite,
99{
100    fn set_hostname_verification(&mut self, _hostname: &str) -> Result<(), crate::TlsError> {
101        Ok(())
102    }
103
104    fn verify_certificate(
105        &mut self,
106        _transcript: &CipherSuite::Hash,
107        _cert: CertificateRef,
108    ) -> Result<(), TlsError> {
109        Ok(())
110    }
111
112    fn verify_signature(&mut self, _verify: CertificateVerifyRef) -> Result<(), crate::TlsError> {
113        Ok(())
114    }
115}
116
117#[derive(Debug, Clone)]
118#[cfg_attr(feature = "defmt", derive(defmt::Format))]
119#[must_use = "TlsConfig does nothing unless consumed"]
120pub struct TlsConfig<'a> {
121    pub(crate) server_name: Option<&'a str>,
122    pub(crate) alpn_protocols: Option<&'a [&'a [u8]]>,
123    pub(crate) psk: Option<(&'a [u8], Vec<&'a [u8], 4>)>,
124    pub(crate) signature_schemes: Vec<SignatureScheme, 25>,
125    pub(crate) named_groups: Vec<NamedGroup, 13>,
126    pub(crate) max_fragment_length: Option<MaxFragmentLength>,
127}
128
129pub trait TlsClock {
130    fn now() -> Option<u64>;
131}
132
133pub struct NoClock;
134
135impl TlsClock for NoClock {
136    fn now() -> Option<u64> {
137        None
138    }
139}
140
141pub trait CryptoProvider {
142    type CipherSuite: TlsCipherSuite;
143    type Signature: AsRef<[u8]>;
144
145    fn rng(&mut self) -> impl CryptoRngCore;
146
147    fn verifier(&mut self) -> Result<&mut impl TlsVerifier<Self::CipherSuite>, crate::TlsError> {
148        Err::<&mut NoVerify, _>(crate::TlsError::Unimplemented)
149    }
150
151    /// Provide a signing key for client certificate authentication.
152    ///
153    /// The provider resolves the private key internally (e.g. from memory, flash, or a hardware
154    /// crypto module such as an HSM/TPM/secure element).
155    fn signer(
156        &mut self,
157    ) -> Result<(impl signature::SignerMut<Self::Signature>, SignatureScheme), crate::TlsError>
158    {
159        Err::<(NoSign, _), crate::TlsError>(crate::TlsError::Unimplemented)
160    }
161
162    /// Resolve the client certificate for mutual TLS authentication.
163    ///
164    /// Return `None` if no client certificate is available (an empty certificate message will
165    /// be sent to the server). The data type `D` can be borrowed (`&[u8]`) or owned
166    /// (e.g. `heapless::Vec<u8, N>`) — the certificate is only needed long enough to encode
167    /// into the TLS message.
168    fn client_cert(&mut self) -> Option<Certificate<impl AsRef<[u8]>>> {
169        None::<Certificate<&[u8]>>
170    }
171}
172
173impl<T: CryptoProvider> CryptoProvider for &mut T {
174    type CipherSuite = T::CipherSuite;
175
176    type Signature = T::Signature;
177
178    fn rng(&mut self) -> impl CryptoRngCore {
179        T::rng(self)
180    }
181
182    fn verifier(&mut self) -> Result<&mut impl TlsVerifier<Self::CipherSuite>, crate::TlsError> {
183        T::verifier(self)
184    }
185
186    fn signer(
187        &mut self,
188    ) -> Result<(impl signature::SignerMut<Self::Signature>, SignatureScheme), crate::TlsError>
189    {
190        T::signer(self)
191    }
192
193    fn client_cert(&mut self) -> Option<Certificate<impl AsRef<[u8]>>> {
194        T::client_cert(self)
195    }
196}
197
198pub struct NoSign;
199
200impl<S> signature::Signer<S> for NoSign {
201    fn try_sign(&self, _msg: &[u8]) -> Result<S, signature::Error> {
202        unimplemented!()
203    }
204}
205
206pub struct UnsecureProvider<'a, CipherSuite, RNG> {
207    rng: RNG,
208    priv_key: Option<&'a [u8]>,
209    client_cert: Option<Certificate<&'a [u8]>>,
210    _marker: PhantomData<CipherSuite>,
211}
212
213impl<RNG: CryptoRngCore> UnsecureProvider<'_, (), RNG> {
214    pub fn new<CipherSuite: TlsCipherSuite>(
215        rng: RNG,
216    ) -> UnsecureProvider<'static, CipherSuite, RNG> {
217        UnsecureProvider {
218            rng,
219            priv_key: None,
220            client_cert: None,
221            _marker: PhantomData,
222        }
223    }
224}
225
226impl<'a, CipherSuite: TlsCipherSuite, RNG: CryptoRngCore> UnsecureProvider<'a, CipherSuite, RNG> {
227    pub fn with_priv_key(mut self, priv_key: &'a [u8]) -> Self {
228        self.priv_key = Some(priv_key);
229        self
230    }
231
232    pub fn with_cert(mut self, cert: Certificate<&'a [u8]>) -> Self {
233        self.client_cert = Some(cert);
234        self
235    }
236}
237
238impl<CipherSuite: TlsCipherSuite, RNG: CryptoRngCore> CryptoProvider
239    for UnsecureProvider<'_, CipherSuite, RNG>
240{
241    type CipherSuite = CipherSuite;
242    type Signature = p256::ecdsa::DerSignature;
243
244    fn rng(&mut self) -> impl CryptoRngCore {
245        &mut self.rng
246    }
247
248    fn signer(
249        &mut self,
250    ) -> Result<(impl signature::SignerMut<Self::Signature>, SignatureScheme), crate::TlsError>
251    {
252        let key_der = self.priv_key.ok_or(TlsError::InvalidPrivateKey)?;
253        let secret_key =
254            SecretKey::from_sec1_der(key_der).map_err(|_| TlsError::InvalidPrivateKey)?;
255
256        Ok((
257            SigningKey::from(&secret_key),
258            SignatureScheme::EcdsaSecp256r1Sha256,
259        ))
260    }
261
262    fn client_cert(&mut self) -> Option<Certificate<impl AsRef<[u8]>>> {
263        self.client_cert.clone()
264    }
265}
266
267#[derive(Debug)]
268#[cfg_attr(feature = "defmt", derive(defmt::Format))]
269pub struct TlsContext<'a, Provider>
270where
271    Provider: CryptoProvider,
272{
273    pub(crate) config: &'a TlsConfig<'a>,
274    pub(crate) crypto_provider: Provider,
275}
276
277impl<'a, Provider> TlsContext<'a, Provider>
278where
279    Provider: CryptoProvider,
280{
281    /// Create a new context with a given config and a crypto provider.
282    pub fn new(config: &'a TlsConfig<'a>, crypto_provider: Provider) -> Self {
283        Self {
284            config,
285            crypto_provider,
286        }
287    }
288}
289
290impl<'a> TlsConfig<'a> {
291    pub fn new() -> Self {
292        let mut config = Self {
293            signature_schemes: Vec::new(),
294            named_groups: Vec::new(),
295            max_fragment_length: None,
296            psk: None,
297            server_name: None,
298            alpn_protocols: None,
299        };
300
301        if cfg!(feature = "alloc") {
302            config = config.enable_rsa_signatures();
303        }
304
305        unwrap!(
306            config
307                .signature_schemes
308                .push(SignatureScheme::EcdsaSecp256r1Sha256)
309                .ok()
310        );
311        unwrap!(
312            config
313                .signature_schemes
314                .push(SignatureScheme::EcdsaSecp384r1Sha384)
315                .ok()
316        );
317        unwrap!(config.signature_schemes.push(SignatureScheme::Ed25519).ok());
318
319        unwrap!(config.named_groups.push(NamedGroup::Secp256r1));
320
321        config
322    }
323
324    /// Enable RSA ciphers even if they might not be supported.
325    pub fn enable_rsa_signatures(mut self) -> Self {
326        unwrap!(
327            self.signature_schemes
328                .push(SignatureScheme::RsaPkcs1Sha256)
329                .ok()
330        );
331        unwrap!(
332            self.signature_schemes
333                .push(SignatureScheme::RsaPkcs1Sha384)
334                .ok()
335        );
336        unwrap!(
337            self.signature_schemes
338                .push(SignatureScheme::RsaPkcs1Sha512)
339                .ok()
340        );
341        unwrap!(
342            self.signature_schemes
343                .push(SignatureScheme::RsaPssRsaeSha256)
344                .ok()
345        );
346        unwrap!(
347            self.signature_schemes
348                .push(SignatureScheme::RsaPssRsaeSha384)
349                .ok()
350        );
351        unwrap!(
352            self.signature_schemes
353                .push(SignatureScheme::RsaPssRsaeSha512)
354                .ok()
355        );
356        self
357    }
358
359    pub fn with_server_name(mut self, server_name: &'a str) -> Self {
360        self.server_name = Some(server_name);
361        self
362    }
363
364    /// Configure ALPN protocol names to send in the ClientHello.
365    ///
366    /// The server will select one of the offered protocols and echo it back
367    /// in EncryptedExtensions. This is required for endpoints that multiplex
368    /// protocols on a single port (e.g. AWS IoT Core MQTT over port 443).
369    pub fn with_alpn(mut self, protocols: &'a [&'a [u8]]) -> Self {
370        self.alpn_protocols = Some(protocols);
371        self
372    }
373
374    /// Configures the maximum plaintext fragment size.
375    ///
376    /// This option may help reduce memory size, as smaller fragment lengths require smaller
377    /// read/write buffers. Note that embedded-tls does not currently use this option to fragment
378    /// writes. Note that the buffers need to include some overhead over the configured fragment
379    /// length.
380    ///
381    /// From [RFC 6066, Section 4.  Maximum Fragment Length Negotiation](https://www.rfc-editor.org/rfc/rfc6066#page-8):
382    ///
383    /// > Without this extension, TLS specifies a fixed maximum plaintext
384    /// > fragment length of 2^14 bytes.  It may be desirable for constrained
385    /// > clients to negotiate a smaller maximum fragment length due to memory
386    /// > limitations or bandwidth limitations.
387    ///
388    /// > For example, if the negotiated length is 2^9=512, then, when using currently defined
389    /// > cipher suites ([...]) and null compression, the record-layer output can be at most
390    /// > 805 bytes: 5 bytes of headers, 512 bytes of application data, 256 bytes of padding,
391    /// > and 32 bytes of MAC.
392    pub fn with_max_fragment_length(mut self, max_fragment_length: MaxFragmentLength) -> Self {
393        self.max_fragment_length = Some(max_fragment_length);
394        self
395    }
396
397    /// Resets the max fragment length to 14 bits (16384).
398    pub fn reset_max_fragment_length(mut self) -> Self {
399        self.max_fragment_length = None;
400        self
401    }
402
403    pub fn with_psk(mut self, psk: &'a [u8], identities: &[&'a [u8]]) -> Self {
404        // TODO: Remove potential panic
405        self.psk = Some((psk, unwrap!(Vec::from_slice(identities).ok())));
406        self
407    }
408}
409
410impl Default for TlsConfig<'_> {
411    fn default() -> Self {
412        TlsConfig::new()
413    }
414}
415
416#[derive(Debug, Copy, Clone, PartialEq)]
417#[cfg_attr(feature = "defmt", derive(defmt::Format))]
418pub enum Certificate<D> {
419    X509(D),
420    RawPublicKey(D),
421}