Skip to main content

iris_trust/
policy.rs

1//! Where a host is willing to get a decoder from.
2
3use std::fmt;
4use std::sync::Arc;
5
6use iris_format::DecoderRef;
7
8/// Something that can produce the module for a decoder that is not in the container.
9///
10/// A resolver is whatever the host has: a directory of modules it ships, a cache, a registry
11/// client, an operator who copied a file into place. This crate does not care which, and it does
12/// not care whether the resolver checked anything, because whatever comes back is hashed against
13/// the digest in the container before it goes anywhere.
14///
15/// Returning `None` is the ordinary answer for a decoder this host has no copy of. It is not an
16/// error and it is not a refusal, it is a resolver saying it does not have that one.
17pub trait Resolve: fmt::Debug + Send + Sync {
18    /// Finds the module for a decoder, by whatever means this host has.
19    ///
20    /// The whole reference is passed rather than just the digest, because a resolver that goes and
21    /// fetches something needs the name to fetch it by. The digest is what the answer is checked
22    /// against, so a resolver that ignores the name and returns the wrong module is caught rather
23    /// than trusted.
24    fn resolve(&self, decoder: &DecoderRef<'_>) -> Option<Vec<u8>>;
25}
26
27/// What a host will run.
28///
29/// The default runs decoders embedded in the container and nothing else. That is the case the
30/// format is designed around: the dataset carries the code that reads it, so there is nothing to
31/// fetch and nothing to decide. A decoder named by a URI is a different proposition, because a
32/// dataset that names one can cause a host to go and get something and then execute it, and this
33/// crate will not do that unless a host has said so with a resolver of its own.
34///
35/// There is no boolean here on purpose. Turning external decoders on means writing the thing that
36/// goes and finds them, which is not something anybody does by accident.
37#[derive(Clone, Default)]
38pub struct Policy {
39    external: Option<Arc<dyn Resolve>>,
40}
41
42impl Policy {
43    /// Embedded decoders and nothing else, which is the default.
44    #[must_use]
45    pub const fn embedded_only() -> Self {
46        Self { external: None }
47    }
48
49    /// Also runs decoders that live outside the container, using this resolver to find them.
50    ///
51    /// The bytes the resolver returns are hashed and compared to the digest in the container in
52    /// exactly the same way an embedded module is. A resolver that returns the wrong module, or a
53    /// registry that has been tampered with, fails here rather than at the compiler.
54    #[must_use]
55    pub fn with_external_decoders_resolved_by(resolver: impl Resolve + 'static) -> Self {
56        Self {
57            external: Some(Arc::new(resolver)),
58        }
59    }
60
61    /// The resolver this policy will use for a decoder that is not in the container, if any.
62    #[must_use]
63    pub fn resolver(&self) -> Option<&dyn Resolve> {
64        self.external.as_deref()
65    }
66}
67
68// Written out rather than derived because `Arc<dyn Resolve>` cannot be derived through, and because
69// what a reader wants from a policy in a log line is whether external decoders are on, not the
70// address of a trait object.
71impl fmt::Debug for Policy {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        match &self.external {
74            None => f.write_str("Policy { embedded decoders only }"),
75            Some(resolver) => write!(f, "Policy {{ external decoders resolved by {resolver:?} }}"),
76        }
77    }
78}