1use crate::manifest::{OtaManifest, OtaManifestError, parse_ota_manifest};
8use ota_manifest_proto::fuchsia::update::manifest as proto;
9use prost::Message as _;
10use ring::signature::UnparsedPublicKey;
11use zerocopy::byteorder::little_endian::U32;
12use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
13
14#[derive(Debug, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
16#[repr(C)]
17struct Header {
18 magic: [u8; 4],
19 version: U32,
20 manifest_size: U32,
21}
22
23pub const MAGIC: [u8; 4] = [0xfc, 0x1a, 0x07, 0xaf];
25
26pub const VERSION: u32 = 1;
28
29pub const MAX_MANIFEST_SIZE: usize = 10 * 1024 * 1024;
31
32pub const MAX_SIGNATURE_SIZE: usize = 1024 * 1024;
34
35trait U32Ext {
36 fn get_usize(&self) -> usize;
37}
38
39impl U32Ext for U32 {
40 fn get_usize(&self) -> usize {
41 const { assert!(usize::BITS >= u32::BITS) }
42 self.get() as usize
43 }
44}
45
46#[derive(Debug, thiserror::Error)]
48#[allow(missing_docs)]
49pub enum SignedManifestError {
50 #[error("file truncated: file size {file_size} is less than required size {expected_size}")]
51 Truncated { file_size: usize, expected_size: usize },
52
53 #[error("invalid magic: {0:?}")]
54 InvalidMagic([u8; 4]),
55
56 #[error("unknown version: {0}")]
57 UnknownVersion(u32),
58
59 #[error("manifest size too large: {0} > {MAX_MANIFEST_SIZE}")]
60 ManifestSizeTooLarge(usize),
61
62 #[error("signature size too large: {0} > {MAX_SIGNATURE_SIZE}")]
63 SignatureSizeTooLarge(usize),
64
65 #[error("failed to deserialize signatures")]
66 InvalidSignatures(#[source] prost::DecodeError),
67
68 #[error("no valid signature found")]
69 SignatureVerificationFailed,
70
71 #[error("invalid manifest")]
72 InvalidManifest(#[from] OtaManifestError),
73}
74
75pub struct RawManifest<'a> {
77 pub version: u32,
79 pub manifest_payload: &'a [u8],
81 pub signatures: Vec<Vec<u8>>,
83 pub signed_bytes: &'a [u8],
85}
86
87impl<'a> RawManifest<'a> {
88 pub fn verify(
91 &self,
92 public_keys: &[UnparsedPublicKey<Vec<u8>>],
93 ) -> Result<(), SignedManifestError> {
94 if !self.signatures.iter().any(|signature| {
95 public_keys.iter().any(|key| key.verify(self.signed_bytes, signature).is_ok())
96 }) {
97 return Err(SignedManifestError::SignatureVerificationFailed);
98 }
99 Ok(())
100 }
101}
102
103pub fn parse_raw(bytes: &[u8]) -> Result<RawManifest<'_>, SignedManifestError> {
107 let (header, rest) =
108 Header::read_from_prefix(bytes).map_err(|_| SignedManifestError::Truncated {
109 file_size: bytes.len(),
110 expected_size: std::mem::size_of::<Header>(),
111 })?;
112
113 if header.magic != MAGIC {
114 return Err(SignedManifestError::InvalidMagic(header.magic));
115 }
116
117 let version = header.version.get();
118 if version != VERSION {
119 return Err(SignedManifestError::UnknownVersion(version));
120 }
121
122 let manifest_size = header.manifest_size.get_usize();
123 if manifest_size > MAX_MANIFEST_SIZE {
124 return Err(SignedManifestError::ManifestSizeTooLarge(manifest_size));
125 }
126
127 let (manifest_payload, after_manifest) =
128 rest.split_at_checked(manifest_size).ok_or_else(|| SignedManifestError::Truncated {
129 file_size: bytes.len(),
130 expected_size: std::mem::size_of::<Header>() + manifest_size,
131 })?;
132
133 let (signature_size_val, signature_bytes) =
134 U32::read_from_prefix(after_manifest).map_err(|_| SignedManifestError::Truncated {
135 file_size: bytes.len(),
136 expected_size: std::mem::size_of::<Header>()
137 + manifest_size
138 + std::mem::size_of::<U32>(),
139 })?;
140
141 let signature_size = signature_size_val.get_usize();
142 if signature_size > MAX_SIGNATURE_SIZE {
143 return Err(SignedManifestError::SignatureSizeTooLarge(signature_size));
144 }
145
146 let (signature_payload, _) =
147 signature_bytes.split_at_checked(signature_size).ok_or_else(|| {
148 SignedManifestError::Truncated {
149 file_size: bytes.len(),
150 expected_size: std::mem::size_of::<Header>()
151 + manifest_size
152 + std::mem::size_of::<U32>()
153 + signature_size,
154 }
155 })?;
156
157 let signatures_msg = proto::Signatures::decode(signature_payload)
158 .map_err(SignedManifestError::InvalidSignatures)?;
159
160 let signed_bytes = &bytes[..std::mem::size_of::<Header>() + manifest_size];
162
163 Ok(RawManifest {
164 version,
165 manifest_payload,
166 signatures: signatures_msg.signatures,
167 signed_bytes,
168 })
169}
170
171pub fn parse_and_verify(
175 bytes: &[u8],
176 public_keys: &[UnparsedPublicKey<Vec<u8>>],
177) -> Result<OtaManifest, SignedManifestError> {
178 let raw = parse_raw(bytes)?;
179 let () = raw.verify(public_keys)?;
180 Ok(parse_ota_manifest(raw.manifest_payload)?)
181}
182
183pub fn generate(
185 manifest: OtaManifest,
186 key: &ring::signature::Ed25519KeyPair,
187) -> Result<Vec<u8>, SignedManifestError> {
188 let manifest_bytes = manifest.serialize();
189 if manifest_bytes.len() > MAX_MANIFEST_SIZE {
190 return Err(SignedManifestError::ManifestSizeTooLarge(manifest_bytes.len()));
191 }
192 let manifest_size = manifest_bytes.len() as u32;
193
194 let header =
195 Header { magic: MAGIC, version: U32::new(VERSION), manifest_size: U32::new(manifest_size) };
196
197 let mut signed_bytes = Vec::with_capacity(std::mem::size_of::<Header>() + manifest_bytes.len());
198 signed_bytes.extend_from_slice(header.as_bytes());
199 signed_bytes.extend_from_slice(&manifest_bytes);
200
201 let signature = key.sign(&signed_bytes);
202
203 let signatures_msg = proto::Signatures { signatures: vec![signature.as_ref().to_vec()] };
204 let signatures_bytes = signatures_msg.encode_to_vec();
205 if signatures_bytes.len() > MAX_SIGNATURE_SIZE {
206 return Err(SignedManifestError::SignatureSizeTooLarge(signatures_bytes.len()));
207 }
208 let signatures_size = signatures_bytes.len() as u32;
209
210 let mut out = signed_bytes;
211 out.extend_from_slice(U32::new(signatures_size).as_bytes());
212 out.extend_from_slice(&signatures_bytes);
213 Ok(out)
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use assert_matches::assert_matches;
220 use ring::signature::KeyPair as _;
221
222 fn make_ota_manifest() -> OtaManifest {
223 OtaManifest {
224 build_info_version: "1.2.3.4".parse().unwrap(),
225 board: "test-board".to_string(),
226 epoch: 1,
227 mode: crate::update_mode::UpdateMode::Normal,
228 blob_base_url: "http://example.com".to_string(),
229 images: vec![],
230 blobs: vec![],
231 }
232 }
233
234 fn make_keypair() -> ring::signature::Ed25519KeyPair {
235 let rng = ring::rand::SystemRandom::new();
236 let pkcs8 = ring::signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
237 ring::signature::Ed25519KeyPair::from_pkcs8(pkcs8.as_ref()).unwrap()
238 }
239
240 fn make_public_key(keypair: &ring::signature::Ed25519KeyPair) -> UnparsedPublicKey<Vec<u8>> {
241 UnparsedPublicKey::new(&ring::signature::ED25519, keypair.public_key().as_ref().to_vec())
242 }
243
244 #[test]
245 fn test_parse_and_verify_success() {
246 let keypair = make_keypair();
247 let manifest = make_ota_manifest();
248 let bytes = generate(manifest.clone(), &keypair).unwrap();
249
250 let trusted_keys = vec![make_public_key(&keypair)];
251 let parsed = parse_and_verify(&bytes, &trusted_keys).unwrap();
252 assert_eq!(parsed, manifest);
253 }
254
255 #[test]
256 fn test_parse_and_verify_wrong_magic() {
257 let keypair = make_keypair();
258 let manifest = make_ota_manifest();
259 let mut bytes = generate(manifest.clone(), &keypair).unwrap();
260
261 bytes[0] ^= 0xff;
262
263 let trusted_keys = vec![make_public_key(&keypair)];
264 let err = parse_and_verify(&bytes, &trusted_keys).unwrap_err();
265 assert_matches!(err, SignedManifestError::InvalidMagic(_));
266 }
267
268 #[test]
269 fn test_parse_and_verify_wrong_version() {
270 let keypair = make_keypair();
271 let manifest = make_ota_manifest();
272 let mut bytes = generate(manifest.clone(), &keypair).unwrap();
273
274 bytes[4] ^= 0x01;
276
277 let trusted_keys = vec![make_public_key(&keypair)];
278 let err = parse_and_verify(&bytes, &trusted_keys).unwrap_err();
279 assert_matches!(err, SignedManifestError::UnknownVersion(_));
280 }
281
282 #[test]
283 fn test_parse_and_verify_truncated_header() {
284 let trusted_keys = vec![];
285 let bytes = vec![0; std::mem::size_of::<Header>() - 1];
286 let err = parse_and_verify(&bytes, &trusted_keys).unwrap_err();
287 assert_matches!(err, SignedManifestError::Truncated { .. });
288 }
289
290 #[test]
291 fn test_parse_and_verify_truncated_signature() {
292 let keypair = make_keypair();
293 let manifest = make_ota_manifest();
294 let mut bytes = generate(manifest.clone(), &keypair).unwrap();
295
296 bytes.truncate(bytes.len() - 1);
298
299 let trusted_keys = vec![make_public_key(&keypair)];
300 let err = parse_and_verify(&bytes, &trusted_keys).unwrap_err();
301 assert_matches!(err, SignedManifestError::Truncated { .. });
302 }
303
304 #[test]
305 fn test_parse_and_verify_bad_signature() {
306 let keypair = make_keypair();
307 let manifest = make_ota_manifest();
308 let bytes = generate(manifest.clone(), &keypair).unwrap();
309
310 let wrong_keypair = make_keypair();
311 let trusted_keys = vec![make_public_key(&wrong_keypair)];
312
313 let err = parse_and_verify(&bytes, &trusted_keys).unwrap_err();
314 assert_matches!(err, SignedManifestError::SignatureVerificationFailed);
315 }
316}