pamoja_update/trust.rs
1//! Who a device will take an update from, and how that can change.
2//!
3//! Trusting one key forever is a trap. The key has to be reachable to sign each
4//! release, which is exactly what makes it likely to leak eventually, and a device
5//! that trusts only that key is then permanently takeable. Lose it instead of
6//! leaking it and the fleet becomes permanently unreachable, which is no better.
7//!
8//! So a device anchors its trust in a key that is used almost never and can live
9//! in a safe, and that anchor signs a [`Delegation`] naming the key that actually
10//! signs releases. Rotating the release key means issuing a new delegation, not
11//! visiting the devices. This is the arrangement RFC 9124 calls a delegation
12//! chain, at the depth that covers rotation: anchor, then release key.
13
14use pamoja_security::{DeviceIdentity, PublicIdentity};
15
16use crate::cbor::{Reader, Writer};
17use crate::error::{Refusal, Result};
18use crate::manifest::{expect_key, read_key, seal, Envelope};
19
20/// A buffer of this size always holds an encoded delegation envelope.
21pub const DELEGATION_MAX: usize = 192;
22
23/// The length of a public key, in bytes.
24const KEY_LEN: usize = 32;
25
26// Map keys, ascending, as the deterministic encoding requires.
27const KEY_EPOCH: u64 = 1;
28const KEY_RELEASE: u64 = 2;
29const KEY_EXPIRES: u64 = 3;
30
31/// A statement, signed by a device's trust anchor, naming the key that may sign
32/// its updates.
33///
34/// # Examples
35///
36/// ```
37/// use pamoja_security::DeviceIdentity;
38/// use pamoja_update::{Delegation, DELEGATION_MAX};
39///
40/// let anchor = DeviceIdentity::from_seed(&[1u8; 32]);
41/// let release = DeviceIdentity::from_seed(&[2u8; 32]);
42///
43/// let delegation = Delegation {
44/// epoch: 1,
45/// release_key: release.public().to_bytes(),
46/// expires: 0,
47/// };
48///
49/// let mut buf = [0u8; DELEGATION_MAX];
50/// let written = delegation.sign(&anchor, &mut buf).unwrap();
51///
52/// let adopted = Delegation::open(&buf[..written], &anchor.public()).unwrap();
53/// assert_eq!(adopted, delegation);
54/// ```
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub struct Delegation {
57 /// Rises with every rotation. A device refuses a delegation not above the one
58 /// it holds, so a retired key cannot be reinstated by replaying the statement
59 /// that once authorised it.
60 pub epoch: u64,
61 /// The public key that may sign manifests while this delegation stands.
62 pub release_key: [u8; KEY_LEN],
63 /// When this delegation stops being honoured, in seconds since the Unix epoch,
64 /// or `0` to never expire. Setting one requires the device to have a clock.
65 pub expires: u64,
66}
67
68impl Delegation {
69 /// Encodes the delegation body, which is the part a signature covers.
70 ///
71 /// # Arguments
72 ///
73 /// * `buf` - the destination.
74 ///
75 /// # Returns
76 ///
77 /// How many bytes were written.
78 ///
79 /// # Errors
80 ///
81 /// Returns [`Refusal::Malformed`] if `buf` is too small.
82 pub fn encode(&self, buf: &mut [u8]) -> Result<usize> {
83 let mut writer = Writer::new(buf);
84 writer.map(3)?;
85 writer.uint(KEY_EPOCH)?;
86 writer.uint(self.epoch)?;
87 writer.uint(KEY_RELEASE)?;
88 writer.bytes(&self.release_key)?;
89 writer.uint(KEY_EXPIRES)?;
90 writer.uint(self.expires)?;
91 Ok(writer.finish())
92 }
93
94 /// Decodes a delegation body.
95 ///
96 /// # Arguments
97 ///
98 /// * `bytes` - an encoded delegation body.
99 ///
100 /// # Returns
101 ///
102 /// The delegation.
103 ///
104 /// # Errors
105 ///
106 /// Returns [`Refusal::Malformed`] if the encoding is not a well-formed
107 /// delegation with its keys in order.
108 pub fn decode(bytes: &[u8]) -> Result<Self> {
109 let mut reader = Reader::new(bytes);
110 if reader.map()? != 3 {
111 return Err(Refusal::Malformed);
112 }
113
114 let epoch = read_key(&mut reader, KEY_EPOCH)?;
115
116 expect_key(&mut reader, KEY_RELEASE)?;
117 let release_key =
118 <[u8; KEY_LEN]>::try_from(reader.bytes()?).map_err(|_| Refusal::Malformed)?;
119
120 let expires = read_key(&mut reader, KEY_EXPIRES)?;
121
122 if reader.position() != bytes.len() {
123 return Err(Refusal::Malformed);
124 }
125
126 Ok(Self {
127 epoch,
128 release_key,
129 expires,
130 })
131 }
132
133 /// Encodes the delegation and signs it with the trust anchor.
134 ///
135 /// # Arguments
136 ///
137 /// * `anchor` - the trust anchor's identity, which alone may delegate.
138 /// * `buf` - the destination, at least [`DELEGATION_MAX`] bytes.
139 ///
140 /// # Returns
141 ///
142 /// How many bytes of `buf` the envelope occupies.
143 ///
144 /// # Errors
145 ///
146 /// Returns [`Refusal::Malformed`] if `buf` is too small.
147 pub fn sign(&self, anchor: &DeviceIdentity, buf: &mut [u8]) -> Result<usize> {
148 let mut body = [0u8; DELEGATION_MAX];
149 let body_len = self.encode(&mut body)?;
150 seal(&body[..body_len], anchor, buf)
151 }
152
153 /// Checks a delegation envelope against a trust anchor and reads it.
154 ///
155 /// # Arguments
156 ///
157 /// * `envelope` - the signed delegation.
158 /// * `anchor` - the key the device anchors its trust in.
159 ///
160 /// # Returns
161 ///
162 /// The delegation, now known to be from the anchor and unaltered.
163 ///
164 /// # Errors
165 ///
166 /// Returns [`Refusal::Signature`] if it is not the anchor's, or a decoding
167 /// refusal if the body is not a valid delegation.
168 pub fn open(envelope: &[u8], anchor: &PublicIdentity) -> Result<Self> {
169 let body = Envelope::decode(envelope)?.verified_body(anchor)?;
170 Self::decode(body)
171 }
172
173 /// Returns the release key as an identity that can check a manifest.
174 ///
175 /// # Returns
176 ///
177 /// The delegated public key.
178 ///
179 /// # Errors
180 ///
181 /// Returns [`Refusal::Signature`] if the delegated bytes are not a usable
182 /// public key, so a malformed delegation cannot leave a device trusting
183 /// nothing while believing it trusts something.
184 pub fn signer(&self) -> Result<PublicIdentity> {
185 PublicIdentity::from_bytes(&self.release_key).map_err(|_| Refusal::Signature)
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 /// The anchor the tests delegate from.
194 fn anchor() -> DeviceIdentity {
195 DeviceIdentity::from_seed(&[1u8; 32])
196 }
197
198 /// A delegation naming a release key derived from `seed`.
199 fn delegation(epoch: u64, seed: u8) -> Delegation {
200 Delegation {
201 epoch,
202 release_key: DeviceIdentity::from_seed(&[seed; 32]).public().to_bytes(),
203 expires: 0,
204 }
205 }
206
207 #[test]
208 fn a_delegation_round_trips() {
209 let original = delegation(3, 2);
210 let mut buf = [0u8; DELEGATION_MAX];
211 let written = original.encode(&mut buf).expect("encode");
212 assert_eq!(
213 Delegation::decode(&buf[..written]).expect("decode"),
214 original
215 );
216 }
217
218 #[test]
219 fn the_encoding_fits_the_documented_buffer_size() {
220 let mut buf = [0u8; DELEGATION_MAX];
221 assert!(delegation(u64::MAX, 2).sign(&anchor(), &mut buf).is_ok());
222 }
223
224 #[test]
225 fn a_delegation_opens_against_its_anchor() {
226 let original = delegation(1, 2);
227 let mut buf = [0u8; DELEGATION_MAX];
228 let written = original.sign(&anchor(), &mut buf).expect("sign");
229 assert_eq!(
230 Delegation::open(&buf[..written], &anchor().public()).expect("open"),
231 original
232 );
233 }
234
235 #[test]
236 fn a_delegation_from_anyone_else_is_refused() {
237 let impostor = DeviceIdentity::from_seed(&[9u8; 32]);
238 let mut buf = [0u8; DELEGATION_MAX];
239 let written = delegation(1, 2).sign(&impostor, &mut buf).expect("sign");
240 assert_eq!(
241 Delegation::open(&buf[..written], &anchor().public()),
242 Err(Refusal::Signature),
243 "only the trust anchor may hand on the right to sign updates"
244 );
245 }
246
247 #[test]
248 fn altering_a_delegation_breaks_its_signature() {
249 let mut buf = [0u8; DELEGATION_MAX];
250 let written = delegation(1, 2).sign(&anchor(), &mut buf).expect("sign");
251 // Swap in a different release key inside the signed body.
252 buf[10] ^= 0x01;
253 assert_eq!(
254 Delegation::open(&buf[..written], &anchor().public()),
255 Err(Refusal::Signature)
256 );
257 }
258
259 #[test]
260 fn the_delegated_key_is_usable() {
261 let release = DeviceIdentity::from_seed(&[2u8; 32]);
262 let signer = delegation(1, 2).signer().expect("signer");
263 assert_eq!(signer.to_bytes(), release.public().to_bytes());
264 }
265}