iris_trust/error.rs
1//! What it means for a decoder to be untrusted.
2
3use iris_format::Digest;
4
5/// Why a decoder was not handed over.
6///
7/// Only one of these is a security event. The other three say the container does not carry a module
8/// this host can run, which is a bad file or an unfinished feature, and a host that wants to tell
9/// those apart in a log can match on the variant rather than read the text.
10#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
11#[non_exhaustive]
12pub enum Untrusted {
13 /// The container does not say which decoder reads it.
14 ///
15 /// A container without a decoder reference is not unreadable, it is just not self decoding, and
16 /// reading it is somebody else's problem rather than this crate's.
17 #[error("the container names no decoder, so nothing here knows how to read it")]
18 Missing,
19
20 /// The decoder lives somewhere else and this host was not told it may go and get it.
21 ///
22 /// This is the default and it fails closed. A decoder named by a URI means a dataset can cause
23 /// a fetch and then have the result executed, which may well be fine and is not something a
24 /// host should end up doing because nobody thought about it.
25 #[error(
26 "the decoder named {name} for this dataset lives outside the container, and this host runs \
27 embedded decoders only. A host that means to run this one calls \
28 Policy::with_external_decoders_resolved_by and supplies the bytes, which are hashed \
29 against the digest the container gives either way."
30 )]
31 External {
32 /// The decoder's name, as the container gives it.
33 name: String,
34 },
35
36 /// The decoder lives somewhere else, this host was told it may go and get it, and it came back
37 /// with nothing.
38 ///
39 /// A resolver that cannot find a decoder is an ordinary outcome rather than an attack: the
40 /// registry is down, or the module was never published, or this host has no copy. The digest
41 /// is here because it is what the next host to try should look for.
42 #[error("nothing resolved the decoder named {name}, whose module should hash to {digest}")]
43 Unresolved {
44 /// The decoder's name, as the container gives it.
45 name: String,
46 /// The digest the module has to hash to, whoever finds it.
47 digest: Digest,
48 },
49
50 /// The container puts the decoder somewhere this build has never heard of.
51 ///
52 /// A newer writer describing a location this build does not know about is a file from the
53 /// future, and the only safe reading of one is that this host cannot read it. Guessing which of
54 /// the locations it does know about was meant is how a host ends up running the wrong bytes.
55 #[error(
56 "the container puts the decoder named {name} somewhere this build has no idea how to \
57 reach, so nothing is run"
58 )]
59 Elsewhere {
60 /// The decoder's name, as the container gives it.
61 name: String,
62 },
63
64 /// The decoder reference names a section the file does not have.
65 ///
66 /// The footer parsed and then disagreed with itself. Nothing was substituted for the missing
67 /// module and nothing ever will be, because a module that is not there has no digest to check.
68 #[error(
69 "the container puts its decoder in section {section}, and there is no section {section} \
70 in the file"
71 )]
72 Lost {
73 /// The section id the decoder reference names.
74 section: u32,
75 },
76
77 /// The module in the container does not hash to what the container says it should.
78 ///
79 /// Both digests are in the message on purpose. The expected one identifies the decoder that was
80 /// meant to be here, which is the thing to go and look for, and the found one identifies what
81 /// actually arrived, which is the thing to keep for whoever asks how it got there.
82 #[error("the decoder module hashes to {found}, and the container says it should be {expected}")]
83 Digest {
84 /// What the container claims the module is.
85 expected: Digest,
86 /// What the bytes in the container actually hash to.
87 found: Digest,
88 },
89}