Skip to main content

rustls/crypto/
mod.rs

1use alloc::borrow::Cow;
2use alloc::boxed::Box;
3use alloc::vec::Vec;
4use core::borrow::Borrow;
5use core::fmt::{self, Debug};
6use core::hash::{Hash, Hasher};
7use core::time::Duration;
8
9use pki_types::{FipsStatus, PrivateKeyDer, SignatureVerificationAlgorithm};
10
11use crate::crypto::kx::KeyExchangeAlgorithm;
12use crate::enums::ProtocolVersion;
13#[cfg(feature = "webpki")]
14use crate::error::PeerMisbehaved;
15use crate::error::{ApiMisuse, Error};
16use crate::msgs::ALL_KEY_EXCHANGE_ALGORITHMS;
17use crate::sync::Arc;
18#[cfg(feature = "webpki")]
19pub use crate::webpki::{verify_tls12_signature, verify_tls13_signature};
20#[cfg(doc)]
21use crate::{ClientConfig, ConfigBuilder, ServerConfig, client, crypto, server};
22use crate::{SupportedCipherSuite, Tls12CipherSuite, Tls13CipherSuite};
23
24/// TLS message encryption/decryption interfaces.
25pub mod cipher;
26
27mod enums;
28pub use enums::{CipherSuite, HashAlgorithm, SignatureAlgorithm, SignatureScheme};
29
30/// Hashing interfaces.
31pub mod hash;
32
33/// HMAC interfaces.
34pub mod hmac;
35
36/// Key exchange interfaces.
37pub mod kx;
38use kx::{NamedGroup, SupportedKxGroup};
39
40/// Cryptography specific to TLS1.2.
41pub mod tls12;
42
43/// Cryptography specific to TLS1.3.
44pub mod tls13;
45
46/// Hybrid public key encryption (RFC 9180).
47pub mod hpke;
48
49#[cfg(any(doc, test))]
50pub(crate) mod test_provider;
51#[cfg(doc)]
52#[doc(hidden)]
53pub use test_provider::TEST_PROVIDER;
54#[cfg(test)]
55pub(crate) use test_provider::{TEST_PROVIDER, TLS13_TEST_SUITE};
56
57// Message signing interfaces.
58mod signer;
59pub use signer::{
60    CertificateIdentity, Credentials, Identity, InconsistentKeys, SelectedCredential, Signer,
61    SigningKey, SingleCredential, VerifiedIdentity, public_key_to_spki,
62};
63
64pub use crate::suites::CipherSuiteCommon;
65
66/// Controls core cryptography used by rustls.
67///
68/// This structure provides defaults. Everything in it can be overridden at
69/// runtime by replacing field values as needed.
70///
71/// # Using a specific `CryptoProvider`
72///
73/// Supply the provider when constructing your [`ClientConfig`] or [`ServerConfig`]:
74///
75/// - [`ClientConfig::builder()`][crate::ClientConfig::builder()]
76/// - [`ServerConfig::builder()`][crate::ServerConfig::builder()]
77///
78/// When creating and configuring a webpki-backed client or server certificate verifier, a choice of
79/// provider is also needed to start the configuration process:
80///
81/// - [`WebPkiServerVerifier::builder()`][crate::client::WebPkiServerVerifier::builder()]
82/// - [`WebPkiClientVerifier::builder()`][crate::server::WebPkiClientVerifier::builder()]
83///
84/// # Making a custom `CryptoProvider`
85///
86/// Your goal will be to populate an instance of this `CryptoProvider` struct.
87///
88/// ## Which elements are required?
89///
90/// There is no requirement that the individual elements ([`SupportedCipherSuite`], [`SupportedKxGroup`],
91/// [`SigningKey`], etc.) come from the same crate.  It is allowed and expected that uninteresting
92/// elements would be delegated back to one of the default providers (statically) or a parent
93/// provider (dynamically).
94///
95/// For example, if we want to make a provider that just overrides key loading in the config builder
96/// API (with [`ConfigBuilder::with_single_cert`], etc.), it might look like this:
97///
98/// ```
99/// # use std::sync::Arc;
100/// # mod fictitious_hsm_api { pub fn load_private_key(key_der: pki_types::PrivateKeyDer<'static>) -> ! { unreachable!(); } }
101///
102/// pub fn provider() -> rustls::crypto::CryptoProvider {
103/// # let DEFAULT_PROVIDER = panic!();
104///   rustls::crypto::CryptoProvider {
105///     key_provider: &HsmKeyLoader,
106///     ..DEFAULT_PROVIDER
107///   }
108/// }
109///
110/// #[derive(Debug)]
111/// struct HsmKeyLoader;
112///
113/// impl rustls::crypto::KeyProvider for HsmKeyLoader {
114///     fn load_private_key(&self, key_der: pki_types::PrivateKeyDer<'static>) -> Result<Box<dyn rustls::crypto::SigningKey>, rustls::Error> {
115///          fictitious_hsm_api::load_private_key(key_der)
116///     }
117/// }
118/// ```
119///
120/// ## References to the individual elements
121///
122/// The elements are documented separately:
123///
124/// - **Random** - see [`SecureRandom::fill()`].
125/// - **Cipher suites** - see [`SupportedCipherSuite`], [`Tls12CipherSuite`], and
126///   [`Tls13CipherSuite`].
127/// - **Key exchange groups** - see [`SupportedKxGroup`].
128/// - **Signature verification algorithms** - see [`WebPkiSupportedAlgorithms`].
129/// - **Authentication key loading** - see [`KeyProvider::load_private_key()`] and
130///   [`SigningKey`].
131///
132/// # FIPS-approved cryptography
133///
134/// Each element of a `CryptoProvider` may be implemented using FIPS-approved cryptography,
135/// and the FIPS status of the overall provider is derived from the status of its elements.
136/// Call [`CryptoProvider::fips()`] to determine the FIPS status of a given provider.
137///
138/// You can verify the configuration at runtime by checking
139/// [`ServerConfig::fips()`]/[`ClientConfig::fips()`].
140#[expect(clippy::exhaustive_structs)]
141#[derive(Debug, Clone)]
142pub struct CryptoProvider {
143    /// List of supported TLS1.2 cipher suites, in preference order -- the first element
144    /// is the highest priority.
145    ///
146    /// Note that the protocol version is negotiated before the cipher suite.
147    ///
148    /// The `Tls12CipherSuite` type carries both configuration and implementation.
149    ///
150    /// A valid `CryptoProvider` must ensure that all cipher suites are accompanied by at least
151    /// one matching key exchange group in [`CryptoProvider::kx_groups`].
152    pub tls12_cipher_suites: Cow<'static, [&'static Tls12CipherSuite]>,
153
154    /// List of supported TLS1.3 cipher suites, in preference order -- the first element
155    /// is the highest priority.
156    ///
157    /// Note that the protocol version is negotiated before the cipher suite.
158    ///
159    /// The `Tls13CipherSuite` type carries both configuration and implementation.
160    pub tls13_cipher_suites: Cow<'static, [&'static Tls13CipherSuite]>,
161
162    /// List of supported key exchange groups, in preference order -- the
163    /// first element is the highest priority.
164    ///
165    /// The first element in this list is the _default key share algorithm_,
166    /// and in TLS1.3 a key share for it is sent in the client hello.
167    ///
168    /// The `SupportedKxGroup` type carries both configuration and implementation.
169    pub kx_groups: Cow<'static, [&'static dyn SupportedKxGroup]>,
170
171    /// List of signature verification algorithms for use with webpki.
172    ///
173    /// These are used for both certificate chain verification and handshake signature verification.
174    ///
175    /// This is called by [`ConfigBuilder::with_root_certificates()`],
176    /// [`server::WebPkiClientVerifier::builder()`] and
177    /// [`client::WebPkiServerVerifier::builder()`].
178    pub signature_verification_algorithms: WebPkiSupportedAlgorithms,
179
180    /// Source of cryptographically secure random numbers.
181    pub secure_random: &'static dyn SecureRandom,
182
183    /// Provider for loading private [`SigningKey`]s from [`PrivateKeyDer`].
184    pub key_provider: &'static dyn KeyProvider,
185
186    /// Provider for creating [`TicketProducer`]s for stateless session resumption.
187    pub ticketer_factory: &'static dyn TicketerFactory,
188}
189
190impl CryptoProvider {
191    /// Return the FIPS validation status for this `CryptoProvider`.
192    ///
193    /// This covers only the cryptographic parts of FIPS approval.  There are
194    /// also TLS protocol-level recommendations made by NIST.  You should
195    /// prefer to call [`ClientConfig::fips()`] or [`ServerConfig::fips()`]
196    /// which take these into account.
197    pub fn fips(&self) -> FipsStatus {
198        let Self {
199            tls12_cipher_suites,
200            tls13_cipher_suites,
201            kx_groups,
202            signature_verification_algorithms,
203            secure_random,
204            key_provider,
205            ticketer_factory,
206        } = self;
207
208        let mut status = Ord::min(
209            signature_verification_algorithms.fips(),
210            secure_random.fips(),
211        );
212        status = Ord::min(status, key_provider.fips());
213        status = Ord::min(status, ticketer_factory.fips());
214        for cs in tls12_cipher_suites.iter() {
215            status = Ord::min(status, cs.fips());
216        }
217        for cs in tls13_cipher_suites.iter() {
218            status = Ord::min(status, cs.fips());
219        }
220        for kx in kx_groups.iter() {
221            status = Ord::min(status, kx.fips());
222        }
223
224        status
225    }
226
227    pub(crate) fn consistency_check(&self) -> Result<(), Error> {
228        if self.tls12_cipher_suites.is_empty() && self.tls13_cipher_suites.is_empty() {
229            return Err(ApiMisuse::NoCipherSuitesConfigured.into());
230        }
231
232        if self.kx_groups.is_empty() {
233            return Err(ApiMisuse::NoKeyExchangeGroupsConfigured.into());
234        }
235
236        // verifying DHE kx groups return their actual group
237        for group in self.kx_groups.iter() {
238            if group.name().key_exchange_algorithm() == KeyExchangeAlgorithm::DHE
239                && group.ffdhe_group().is_none()
240            {
241                return Err(Error::General(alloc::format!(
242                    "SupportedKxGroup {group:?} must return Some() from `ffdhe_group()`"
243                )));
244            }
245        }
246
247        // verifying cipher suites have matching kx groups
248        let mut supported_kx_algos = Vec::with_capacity(ALL_KEY_EXCHANGE_ALGORITHMS.len());
249        for group in self.kx_groups.iter() {
250            let kx = group.name().key_exchange_algorithm();
251            if !supported_kx_algos.contains(&kx) {
252                supported_kx_algos.push(kx);
253            }
254            // Small optimization. We don't need to go over other key exchange groups
255            // if we already cover all supported key exchange algorithms
256            if supported_kx_algos.len() == ALL_KEY_EXCHANGE_ALGORITHMS.len() {
257                break;
258            }
259        }
260
261        for cs in self.tls12_cipher_suites.iter() {
262            if supported_kx_algos.contains(&cs.kx) {
263                continue;
264            }
265            let suite_name = cs.common.suite;
266            return Err(Error::General(alloc::format!(
267                "TLS1.2 cipher suite {suite_name:?} requires {0:?} key exchange, but no {0:?}-compatible \
268                key exchange groups were present in `CryptoProvider`'s `kx_groups` field",
269                cs.kx,
270            )));
271        }
272
273        Ok(())
274    }
275
276    pub(crate) fn iter_cipher_suites(&self) -> impl Iterator<Item = SupportedCipherSuite> + '_ {
277        self.tls13_cipher_suites
278            .iter()
279            .copied()
280            .map(SupportedCipherSuite::Tls13)
281            .chain(
282                self.tls12_cipher_suites
283                    .iter()
284                    .copied()
285                    .map(SupportedCipherSuite::Tls12),
286            )
287    }
288
289    /// We support a given TLS version if at least one ciphersuite for the version
290    /// is available.
291    pub(crate) fn supports_version(&self, v: ProtocolVersion) -> bool {
292        match v {
293            ProtocolVersion::TLSv1_2 => !self.tls12_cipher_suites.is_empty(),
294            ProtocolVersion::TLSv1_3 => !self.tls13_cipher_suites.is_empty(),
295            _ => false,
296        }
297    }
298
299    pub(crate) fn find_kx_group(
300        &self,
301        name: NamedGroup,
302        version: ProtocolVersion,
303    ) -> Option<&'static dyn SupportedKxGroup> {
304        if !name.usable_for_version(version) {
305            return None;
306        }
307        self.kx_groups
308            .iter()
309            .find(|skxg| skxg.name() == name)
310            .copied()
311    }
312}
313
314impl Borrow<[&'static Tls12CipherSuite]> for CryptoProvider {
315    fn borrow(&self) -> &[&'static Tls12CipherSuite] {
316        &self.tls12_cipher_suites
317    }
318}
319
320impl Borrow<[&'static Tls13CipherSuite]> for CryptoProvider {
321    fn borrow(&self) -> &[&'static Tls13CipherSuite] {
322        &self.tls13_cipher_suites
323    }
324}
325
326/// Describes which `webpki` signature verification algorithms are supported and
327/// how they map to TLS [`SignatureScheme`]s.
328///
329/// Create one with [`WebPkiSupportedAlgorithms::new`], which can be done in const-context.
330#[derive(Clone, Copy)]
331pub struct WebPkiSupportedAlgorithms {
332    /// A list of all supported signature verification algorithms.
333    ///
334    /// Used for verifying certificate chains.
335    ///
336    /// The order of this list is not significant.  It may be empty, but the default
337    /// certificate verifier will reject all certificates so a custom verifier will be required.
338    pub(crate) all: &'static [&'static dyn SignatureVerificationAlgorithm],
339
340    /// A mapping from TLS `SignatureScheme`s to matching webpki signature verification algorithms.
341    ///
342    /// This field has invariants enforced by [`Self::new()`]:
343    ///
344    /// - The mappings must be non-empty.
345    /// - The list of verification algorithms for each mapping must be non-empty.
346    ///
347    /// This is one (`SignatureScheme`) to many ([`SignatureVerificationAlgorithm`]) because
348    /// (depending on the protocol version) there is not necessary a 1-to-1 mapping.
349    ///
350    /// For TLS1.2, all `SignatureVerificationAlgorithm`s are tried in sequence.
351    ///
352    /// For TLS1.3, only the first is tried.
353    ///
354    /// The supported schemes in this mapping is communicated to the peer and the order is significant.
355    /// The first mapping is our highest preference.
356    pub(crate) mapping: &'static [(
357        SignatureScheme,
358        &'static [&'static dyn SignatureVerificationAlgorithm],
359    )],
360}
361
362impl WebPkiSupportedAlgorithms {
363    /// Creating a `WebPkiSupportedAlgorithms` and checking its consistency.
364    ///
365    /// This is intended to only be called in const context, so the panics are
366    /// compile-time.
367    pub const fn new(
368        all: &'static [&'static dyn SignatureVerificationAlgorithm],
369        mapping: &'static [(
370            SignatureScheme,
371            &'static [&'static dyn SignatureVerificationAlgorithm],
372        )],
373    ) -> Result<Self, ApiMisuse> {
374        let s = Self { all, mapping };
375        if mapping.is_empty() {
376            return Err(ApiMisuse::NoSignatureVerificationAlgorithms);
377        }
378
379        // TODO: rewrite when feature(const_iter) and feature(const_for) are available
380        let mut i = 0;
381        while i < s.mapping.len() {
382            if s.mapping[i].1.is_empty() {
383                return Err(ApiMisuse::NoSignatureVerificationAlgorithms);
384            }
385            assert!(!s.mapping[i].1.is_empty());
386            i += 1;
387        }
388
389        Ok(s)
390    }
391
392    /// Return all the `scheme` items in `mapping`, maintaining order.
393    pub fn supported_schemes(&self) -> Vec<SignatureScheme> {
394        self.mapping
395            .iter()
396            .map(|item| item.0)
397            .collect()
398    }
399
400    /// Return the FIPS validation status of this implementation.
401    pub fn fips(&self) -> FipsStatus {
402        let algs = self
403            .all
404            .iter()
405            .map(|alg| alg.fips_status())
406            .min();
407        let mapped = self
408            .mapping
409            .iter()
410            .flat_map(|(_, algs)| algs.iter().map(|alg| alg.fips_status()))
411            .min();
412
413        match (algs, mapped) {
414            (Some(algs), Some(mapped)) => Ord::min(algs, mapped),
415            (Some(status), None) | (None, Some(status)) => status,
416            (None, None) => FipsStatus::Unvalidated,
417        }
418    }
419
420    /// Accessor for the `mapping` field.
421    pub fn mapping(
422        &self,
423    ) -> &'static [(
424        SignatureScheme,
425        &'static [&'static dyn SignatureVerificationAlgorithm],
426    )] {
427        self.mapping
428    }
429
430    /// Return the first item in `mapping` that matches `scheme`.
431    #[cfg(feature = "webpki")]
432    pub(crate) fn convert_scheme(
433        &self,
434        scheme: SignatureScheme,
435    ) -> Result<&[&'static dyn SignatureVerificationAlgorithm], Error> {
436        self.mapping
437            .iter()
438            .filter_map(|item| if item.0 == scheme { Some(item.1) } else { None })
439            .next()
440            .ok_or_else(|| PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into())
441    }
442}
443
444impl Debug for WebPkiSupportedAlgorithms {
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446        write!(f, "WebPkiSupportedAlgorithms {{ all: [ .. ], mapping: ")?;
447        f.debug_list()
448            .entries(self.mapping.iter().map(|item| item.0))
449            .finish()?;
450        write!(f, " }}")
451    }
452}
453
454impl Hash for WebPkiSupportedAlgorithms {
455    fn hash<H: Hasher>(&self, state: &mut H) {
456        let Self { all, mapping } = self;
457
458        write_algs(state, all);
459        state.write_usize(mapping.len());
460        for (scheme, algs) in *mapping {
461            state.write_u16(u16::from(*scheme));
462            write_algs(state, algs);
463        }
464
465        fn write_algs<H: Hasher>(
466            state: &mut H,
467            algs: &[&'static dyn SignatureVerificationAlgorithm],
468        ) {
469            state.write_usize(algs.len());
470            for alg in algs {
471                state.write(alg.public_key_alg_id().as_ref());
472                state.write(alg.signature_alg_id().as_ref());
473            }
474        }
475    }
476}
477
478pub(crate) mod rand {
479    use super::{GetRandomFailed, SecureRandom};
480
481    /// Make an array of size `N` containing random material.
482    pub(crate) fn random_array<const N: usize>(
483        secure_random: &dyn SecureRandom,
484    ) -> Result<[u8; N], GetRandomFailed> {
485        let mut v = [0; N];
486        secure_random.fill(&mut v)?;
487        Ok(v)
488    }
489
490    /// Return a uniformly random [`u32`].
491    pub(crate) fn random_u32(secure_random: &dyn SecureRandom) -> Result<u32, GetRandomFailed> {
492        Ok(u32::from_be_bytes(random_array(secure_random)?))
493    }
494
495    /// Return a uniformly random [`u16`].
496    pub(crate) fn random_u16(secure_random: &dyn SecureRandom) -> Result<u16, GetRandomFailed> {
497        Ok(u16::from_be_bytes(random_array(secure_random)?))
498    }
499}
500
501/// Random material generation failed.
502#[expect(clippy::exhaustive_structs)]
503#[derive(Debug)]
504pub struct GetRandomFailed;
505
506/// A source of cryptographically secure randomness.
507pub trait SecureRandom: Send + Sync + Debug {
508    /// Fill the given buffer with random bytes.
509    ///
510    /// The bytes must be sourced from a cryptographically secure random number
511    /// generator seeded with good quality, secret entropy.
512    ///
513    /// This is used for all randomness required by rustls, but not necessarily
514    /// randomness required by the underlying cryptography library.  For example:
515    /// [`SupportedKxGroup::start()`] requires random material to generate
516    /// an ephemeral key exchange key, but this is not included in the interface with
517    /// rustls: it is assumed that the cryptography library provides for this itself.
518    fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed>;
519
520    /// Return the FIPS validation status of this implementation.
521    fn fips(&self) -> FipsStatus {
522        FipsStatus::Unvalidated
523    }
524}
525
526/// A mechanism for loading private [`SigningKey`]s from [`PrivateKeyDer`].
527///
528/// This trait is intended to be used with private key material that is sourced from DER,
529/// such as a private-key that may be present on-disk. It is not intended to be used with
530/// keys held in hardware security modules (HSMs) or physical tokens. For these use-cases
531/// see the Rustls manual section on [customizing private key usage].
532///
533/// [customizing private key usage]: <https://docs.rs/rustls/latest/rustls/manual/_03_howto/index.html#customising-private-key-usage>
534pub trait KeyProvider: Send + Sync + Debug {
535    /// Decode and validate a private signing key from `key_der`.
536    ///
537    /// This is used by [`ConfigBuilder::with_client_auth_cert()`], [`ConfigBuilder::with_single_cert()`],
538    /// and [`ConfigBuilder::with_single_cert_with_ocsp()`].  The key types and formats supported by this
539    /// function directly defines the key types and formats supported in those APIs.
540    ///
541    /// Return an error if the key type encoding is not supported, or if the key fails validation.
542    fn load_private_key(
543        &self,
544        key_der: PrivateKeyDer<'static>,
545    ) -> Result<Box<dyn SigningKey>, Error>;
546
547    /// Return the FIPS validation status for this key provider.
548    ///
549    /// The returned status must cover all possible key types supported by
550    /// [`KeyProvider::load_private_key()`].
551    fn fips(&self) -> FipsStatus {
552        FipsStatus::Unvalidated
553    }
554}
555
556/// A factory that builds [`TicketProducer`]s.
557///
558/// These can be used in [`ServerConfig::ticketer`] to enable stateless resumption.
559///
560/// [`ServerConfig::ticketer`]: crate::server::ServerConfig::ticketer
561pub trait TicketerFactory: Debug + Send + Sync {
562    /// Build a new `TicketProducer`.
563    fn ticketer(&self) -> Result<Arc<dyn TicketProducer>, Error>;
564
565    /// Return the FIPS validation status of ticketers produced from here.
566    fn fips(&self) -> FipsStatus {
567        FipsStatus::Unvalidated
568    }
569}
570
571/// A trait for the ability to encrypt and decrypt tickets.
572pub trait TicketProducer: Debug + Send + Sync {
573    /// Encrypt and authenticate `plain`, returning the resulting
574    /// ticket.  Return None if `plain` cannot be encrypted for
575    /// some reason: an empty ticket will be sent and the connection
576    /// will continue.
577    fn encrypt(&self, plain: &[u8]) -> Option<Vec<u8>>;
578
579    /// Decrypt `cipher`, validating its authenticity protection
580    /// and recovering the plaintext.  `cipher` is fully attacker
581    /// controlled, so this decryption must be side-channel free,
582    /// panic-proof, and otherwise bullet-proof.  If the decryption
583    /// fails, return None.
584    fn decrypt(&self, cipher: &[u8]) -> Option<Vec<u8>>;
585
586    /// Returns the lifetime of tickets produced now.
587    /// The lifetime is provided as a hint to clients that the
588    /// ticket will not be useful after the given time.
589    ///
590    /// This lifetime must be implemented by key rolling and
591    /// erasure, *not* by storing a lifetime in the ticket.
592    ///
593    /// The objective is to limit damage to forward secrecy caused
594    /// by tickets, not just limiting their lifetime.
595    fn lifetime(&self) -> Duration;
596}
597
598#[cfg(test)]
599#[track_caller]
600pub(crate) fn tls13_suite(
601    suite: CipherSuite,
602    provider: &CryptoProvider,
603) -> &'static Tls13CipherSuite {
604    provider
605        .tls13_cipher_suites
606        .iter()
607        .find(|cs| cs.common.suite == suite)
608        .unwrap()
609}
610
611#[cfg(test)]
612#[track_caller]
613pub(crate) fn tls12_suite(
614    suite: CipherSuite,
615    provider: &CryptoProvider,
616) -> &'static Tls12CipherSuite {
617    provider
618        .tls12_cipher_suites
619        .iter()
620        .find(|cs| cs.common.suite == suite)
621        .unwrap()
622}
623
624#[cfg(test)]
625#[track_caller]
626pub(crate) fn tls13_only(provider: CryptoProvider) -> CryptoProvider {
627    CryptoProvider {
628        tls12_cipher_suites: Cow::default(),
629        ..provider
630    }
631}
632
633#[cfg(test)]
634#[track_caller]
635pub(crate) fn tls12_only(provider: CryptoProvider) -> CryptoProvider {
636    CryptoProvider {
637        tls13_cipher_suites: Cow::default(),
638        ..provider
639    }
640}