1*d289c2baSAndroid Build Coastguard Worker // Copyright 2023, The Android Open Source Project
2*d289c2baSAndroid Build Coastguard Worker //
3*d289c2baSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*d289c2baSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*d289c2baSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*d289c2baSAndroid Build Coastguard Worker //
7*d289c2baSAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*d289c2baSAndroid Build Coastguard Worker //
9*d289c2baSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*d289c2baSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*d289c2baSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*d289c2baSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*d289c2baSAndroid Build Coastguard Worker // limitations under the License.
14*d289c2baSAndroid Build Coastguard Worker
15*d289c2baSAndroid Build Coastguard Worker //! Descriptor utilities.
16*d289c2baSAndroid Build Coastguard Worker
17*d289c2baSAndroid Build Coastguard Worker use super::{DescriptorError, DescriptorResult};
18*d289c2baSAndroid Build Coastguard Worker use zerocopy::{FromBytes, FromZeroes, Ref};
19*d289c2baSAndroid Build Coastguard Worker
20*d289c2baSAndroid Build Coastguard Worker /// Splits `size` bytes off the front of `data`.
21*d289c2baSAndroid Build Coastguard Worker ///
22*d289c2baSAndroid Build Coastguard Worker /// This is a thin wrapper around `slice::split_at()` but it:
23*d289c2baSAndroid Build Coastguard Worker /// 1. Returns a `DescriptorError` rather than panicking if `data` is too small.
24*d289c2baSAndroid Build Coastguard Worker /// 2. Accepts a variety of `size` types since descriptors commonly use `u32` or `u64`.
25*d289c2baSAndroid Build Coastguard Worker ///
26*d289c2baSAndroid Build Coastguard Worker /// # Arguments
27*d289c2baSAndroid Build Coastguard Worker /// * `data`: descriptor data.
28*d289c2baSAndroid Build Coastguard Worker /// * `size`: the number of bytes to pull off the front.
29*d289c2baSAndroid Build Coastguard Worker ///
30*d289c2baSAndroid Build Coastguard Worker /// # Returns
31*d289c2baSAndroid Build Coastguard Worker /// A tuple containing (extracted_bytes, data_remainder) on success, or
32*d289c2baSAndroid Build Coastguard Worker /// `DescriptorError` if we couldn't get `size` bytes out of `data`.
split_slice<T>(data: &[u8], size: T) -> DescriptorResult<(&[u8], &[u8])> where T: TryInto<usize>,33*d289c2baSAndroid Build Coastguard Worker pub(super) fn split_slice<T>(data: &[u8], size: T) -> DescriptorResult<(&[u8], &[u8])>
34*d289c2baSAndroid Build Coastguard Worker where
35*d289c2baSAndroid Build Coastguard Worker T: TryInto<usize>,
36*d289c2baSAndroid Build Coastguard Worker {
37*d289c2baSAndroid Build Coastguard Worker let size = size.try_into().map_err(|_| DescriptorError::InvalidValue)?;
38*d289c2baSAndroid Build Coastguard Worker if size > data.len() {
39*d289c2baSAndroid Build Coastguard Worker Err(DescriptorError::InvalidSize)
40*d289c2baSAndroid Build Coastguard Worker } else {
41*d289c2baSAndroid Build Coastguard Worker Ok(data.split_at(size))
42*d289c2baSAndroid Build Coastguard Worker }
43*d289c2baSAndroid Build Coastguard Worker }
44*d289c2baSAndroid Build Coastguard Worker
45*d289c2baSAndroid Build Coastguard Worker /// Function type for the `avb_*descriptor_validate_and_byteswap()` C functions.
46*d289c2baSAndroid Build Coastguard Worker pub(super) type ValidationFunc<T> = unsafe extern "C" fn(*const T, *mut T) -> bool;
47*d289c2baSAndroid Build Coastguard Worker
48*d289c2baSAndroid Build Coastguard Worker /// Trait to represent an `Avb*Descriptor` type which has an associated `ValidationFunc`.
49*d289c2baSAndroid Build Coastguard Worker ///
50*d289c2baSAndroid Build Coastguard Worker /// This allows the generic `parse_descriptor()` function to extract a descriptor header for any
51*d289c2baSAndroid Build Coastguard Worker /// descriptor type.
52*d289c2baSAndroid Build Coastguard Worker ///
53*d289c2baSAndroid Build Coastguard Worker /// To use, implement `ValidateAndByteSwap` on the `Avb*Descriptor` struct.
54*d289c2baSAndroid Build Coastguard Worker ///
55*d289c2baSAndroid Build Coastguard Worker /// # Safety
56*d289c2baSAndroid Build Coastguard Worker /// The function assigned to `VALIDATE_AND_BYTESWAP_FUNC` must be the libavb
57*d289c2baSAndroid Build Coastguard Worker /// `avb_*descriptor_validate_and_byteswap()` function corresponding to the descriptor implementing
58*d289c2baSAndroid Build Coastguard Worker /// this trait (e.g. `AvbHashDescriptor` -> `avb_hash_descriptor_validate_and_byteswap`).
59*d289c2baSAndroid Build Coastguard Worker pub(super) unsafe trait ValidateAndByteswap {
60*d289c2baSAndroid Build Coastguard Worker /// The specific libavb validation function for this descriptor type.
61*d289c2baSAndroid Build Coastguard Worker const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self>;
62*d289c2baSAndroid Build Coastguard Worker }
63*d289c2baSAndroid Build Coastguard Worker
64*d289c2baSAndroid Build Coastguard Worker /// A descriptor that has been extracted, validated, and byteswapped.
65*d289c2baSAndroid Build Coastguard Worker #[derive(Debug)]
66*d289c2baSAndroid Build Coastguard Worker pub(super) struct ParsedDescriptor<'a, T> {
67*d289c2baSAndroid Build Coastguard Worker /// The original raw (big-endian) header.
68*d289c2baSAndroid Build Coastguard Worker pub raw_header: &'a T,
69*d289c2baSAndroid Build Coastguard Worker /// A copy of the header in host-endian format.
70*d289c2baSAndroid Build Coastguard Worker pub header: T,
71*d289c2baSAndroid Build Coastguard Worker /// The descriptor body contents.
72*d289c2baSAndroid Build Coastguard Worker pub body: &'a [u8],
73*d289c2baSAndroid Build Coastguard Worker }
74*d289c2baSAndroid Build Coastguard Worker
75*d289c2baSAndroid Build Coastguard Worker /// Extracts the descriptor header from the given buffer.
76*d289c2baSAndroid Build Coastguard Worker ///
77*d289c2baSAndroid Build Coastguard Worker /// # Arguments
78*d289c2baSAndroid Build Coastguard Worker /// `data`: the descriptor contents in raw (big-endian) format.
79*d289c2baSAndroid Build Coastguard Worker ///
80*d289c2baSAndroid Build Coastguard Worker /// # Returns
81*d289c2baSAndroid Build Coastguard Worker /// A `ParsedDescriptor` on success, `DescriptorError` if `data` was too small or the header looks
82*d289c2baSAndroid Build Coastguard Worker /// invalid.
parse_descriptor<T>(data: &[u8]) -> DescriptorResult<ParsedDescriptor<T>> where T: Default + FromZeroes + FromBytes + ValidateAndByteswap,83*d289c2baSAndroid Build Coastguard Worker pub(super) fn parse_descriptor<T>(data: &[u8]) -> DescriptorResult<ParsedDescriptor<T>>
84*d289c2baSAndroid Build Coastguard Worker where
85*d289c2baSAndroid Build Coastguard Worker T: Default + FromZeroes + FromBytes + ValidateAndByteswap,
86*d289c2baSAndroid Build Coastguard Worker {
87*d289c2baSAndroid Build Coastguard Worker let (raw_header, body) =
88*d289c2baSAndroid Build Coastguard Worker Ref::<_, T>::new_from_prefix(data).ok_or(DescriptorError::InvalidHeader)?;
89*d289c2baSAndroid Build Coastguard Worker let raw_header = raw_header.into_ref();
90*d289c2baSAndroid Build Coastguard Worker
91*d289c2baSAndroid Build Coastguard Worker let mut header = T::default();
92*d289c2baSAndroid Build Coastguard Worker // SAFETY:
93*d289c2baSAndroid Build Coastguard Worker // * all `VALIDATE_AND_BYTESWAP_FUNC` functions check the validity of the fields.
94*d289c2baSAndroid Build Coastguard Worker // * even if the data is corrupted somehow and is not detected by the validation logic, these
95*d289c2baSAndroid Build Coastguard Worker // functions never try to access memory outside of the header.
96*d289c2baSAndroid Build Coastguard Worker if !unsafe { T::VALIDATE_AND_BYTESWAP_FUNC(raw_header, &mut header) } {
97*d289c2baSAndroid Build Coastguard Worker return Err(DescriptorError::InvalidHeader);
98*d289c2baSAndroid Build Coastguard Worker }
99*d289c2baSAndroid Build Coastguard Worker
100*d289c2baSAndroid Build Coastguard Worker Ok(ParsedDescriptor {
101*d289c2baSAndroid Build Coastguard Worker raw_header,
102*d289c2baSAndroid Build Coastguard Worker header,
103*d289c2baSAndroid Build Coastguard Worker body,
104*d289c2baSAndroid Build Coastguard Worker })
105*d289c2baSAndroid Build Coastguard Worker }
106*d289c2baSAndroid Build Coastguard Worker
107*d289c2baSAndroid Build Coastguard Worker #[cfg(test)]
108*d289c2baSAndroid Build Coastguard Worker mod tests {
109*d289c2baSAndroid Build Coastguard Worker use super::*;
110*d289c2baSAndroid Build Coastguard Worker use avb_bindgen::{avb_descriptor_validate_and_byteswap, AvbDescriptor, AvbHashDescriptor};
111*d289c2baSAndroid Build Coastguard Worker use std::mem::size_of;
112*d289c2baSAndroid Build Coastguard Worker
113*d289c2baSAndroid Build Coastguard Worker #[test]
split_slice_with_various_size_types_succeeds()114*d289c2baSAndroid Build Coastguard Worker fn split_slice_with_various_size_types_succeeds() {
115*d289c2baSAndroid Build Coastguard Worker let data = &[1, 2, 3, 4];
116*d289c2baSAndroid Build Coastguard Worker let expected = Ok((&data[..2], &data[2..]));
117*d289c2baSAndroid Build Coastguard Worker assert_eq!(split_slice(data, 2u32), expected);
118*d289c2baSAndroid Build Coastguard Worker assert_eq!(split_slice(data, 2u64), expected);
119*d289c2baSAndroid Build Coastguard Worker assert_eq!(split_slice(data, 2usize), expected);
120*d289c2baSAndroid Build Coastguard Worker }
121*d289c2baSAndroid Build Coastguard Worker
122*d289c2baSAndroid Build Coastguard Worker #[test]
split_slice_with_negative_size_fails()123*d289c2baSAndroid Build Coastguard Worker fn split_slice_with_negative_size_fails() {
124*d289c2baSAndroid Build Coastguard Worker let data = &[1, 2, 3, 4];
125*d289c2baSAndroid Build Coastguard Worker assert_eq!(split_slice(data, -1i32), Err(DescriptorError::InvalidValue));
126*d289c2baSAndroid Build Coastguard Worker }
127*d289c2baSAndroid Build Coastguard Worker
128*d289c2baSAndroid Build Coastguard Worker #[test]
split_slice_with_size_overflow_fails()129*d289c2baSAndroid Build Coastguard Worker fn split_slice_with_size_overflow_fails() {
130*d289c2baSAndroid Build Coastguard Worker let data = &[1, 2, 3, 4];
131*d289c2baSAndroid Build Coastguard Worker assert_eq!(split_slice(data, 5u32), Err(DescriptorError::InvalidSize));
132*d289c2baSAndroid Build Coastguard Worker }
133*d289c2baSAndroid Build Coastguard Worker
134*d289c2baSAndroid Build Coastguard Worker // Enable `parse_descriptor()` on a generic `AvbDescriptor` of any sub-type.
135*d289c2baSAndroid Build Coastguard Worker // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor.
136*d289c2baSAndroid Build Coastguard Worker unsafe impl ValidateAndByteswap for AvbDescriptor {
137*d289c2baSAndroid Build Coastguard Worker const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> =
138*d289c2baSAndroid Build Coastguard Worker avb_descriptor_validate_and_byteswap;
139*d289c2baSAndroid Build Coastguard Worker }
140*d289c2baSAndroid Build Coastguard Worker
141*d289c2baSAndroid Build Coastguard Worker // Hardcoded test descriptor of custom sub-type (tag = 42).
142*d289c2baSAndroid Build Coastguard Worker const TEST_DESCRIPTOR: &[u8] = &[
143*d289c2baSAndroid Build Coastguard Worker 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // tag = 0x42u64 (BE)
144*d289c2baSAndroid Build Coastguard Worker 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // num_bytes_following = 8u64 (BE)
145*d289c2baSAndroid Build Coastguard Worker 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // fake contents
146*d289c2baSAndroid Build Coastguard Worker ];
147*d289c2baSAndroid Build Coastguard Worker
148*d289c2baSAndroid Build Coastguard Worker #[test]
parse_descriptor_success()149*d289c2baSAndroid Build Coastguard Worker fn parse_descriptor_success() {
150*d289c2baSAndroid Build Coastguard Worker let descriptor = parse_descriptor::<AvbDescriptor>(TEST_DESCRIPTOR).unwrap();
151*d289c2baSAndroid Build Coastguard Worker
152*d289c2baSAndroid Build Coastguard Worker // `assert_eq!()` cannot be used on `repr(packed)` struct fields.
153*d289c2baSAndroid Build Coastguard Worker assert!(descriptor.raw_header.tag == 0x42u64.to_be());
154*d289c2baSAndroid Build Coastguard Worker assert!(descriptor.raw_header.num_bytes_following == 8u64.to_be());
155*d289c2baSAndroid Build Coastguard Worker assert!(descriptor.header.tag == 0x42);
156*d289c2baSAndroid Build Coastguard Worker assert!(descriptor.header.num_bytes_following == 8);
157*d289c2baSAndroid Build Coastguard Worker assert_eq!(descriptor.body, &[1, 2, 3, 4, 5, 6, 7, 8]);
158*d289c2baSAndroid Build Coastguard Worker }
159*d289c2baSAndroid Build Coastguard Worker
160*d289c2baSAndroid Build Coastguard Worker #[test]
parse_descriptor_buffer_too_short_failure()161*d289c2baSAndroid Build Coastguard Worker fn parse_descriptor_buffer_too_short_failure() {
162*d289c2baSAndroid Build Coastguard Worker let bad_length = size_of::<AvbDescriptor>() - 1;
163*d289c2baSAndroid Build Coastguard Worker assert_eq!(
164*d289c2baSAndroid Build Coastguard Worker parse_descriptor::<AvbDescriptor>(&TEST_DESCRIPTOR[..bad_length]).unwrap_err(),
165*d289c2baSAndroid Build Coastguard Worker DescriptorError::InvalidHeader
166*d289c2baSAndroid Build Coastguard Worker );
167*d289c2baSAndroid Build Coastguard Worker }
168*d289c2baSAndroid Build Coastguard Worker
169*d289c2baSAndroid Build Coastguard Worker #[test]
parse_descriptor_wrong_type_failure()170*d289c2baSAndroid Build Coastguard Worker fn parse_descriptor_wrong_type_failure() {
171*d289c2baSAndroid Build Coastguard Worker assert_eq!(
172*d289c2baSAndroid Build Coastguard Worker // `TEST_DESCRIPTOR` is not a valid `AvbHashDescriptor`, this should fail without
173*d289c2baSAndroid Build Coastguard Worker // triggering any undefined behavior.
174*d289c2baSAndroid Build Coastguard Worker parse_descriptor::<AvbHashDescriptor>(TEST_DESCRIPTOR).unwrap_err(),
175*d289c2baSAndroid Build Coastguard Worker DescriptorError::InvalidHeader
176*d289c2baSAndroid Build Coastguard Worker );
177*d289c2baSAndroid Build Coastguard Worker }
178*d289c2baSAndroid Build Coastguard Worker }
179