image4/
restore_info.rs

1//! Contains the [`RestoreInfo`] type that represents Image4 restore info (an IM4R file).
2
3use crate::{
4    property::{DictRef, ValueRef},
5    util, Tag,
6};
7use core::ops::Add;
8use der::{
9    asn1::Ia5StringRef, Decode, Encode, EncodeValue, FixedTag, Length, Reader, Tagged, Writer,
10};
11#[cfg(any(feature = "alloc", test))]
12use {
13    crate::property::{Dict, Value},
14    alloc::collections::BTreeMap,
15    der::referenced::{OwnedToRef, RefToOwned},
16};
17
18/// Restore info that might be present in an Image4 file.
19#[derive(Clone, Debug)]
20#[cfg_attr(test, derive(Eq, PartialEq))]
21pub struct RestoreInfoRef<'a>(DictRef<'a>);
22
23impl<'a> RestoreInfoRef<'a> {
24    /// Decodes the part of encoded restore info after the magic string.
25    ///
26    /// May be used when it is required to first identify what kind of image you're looking at by
27    /// checking the magic string.
28    pub fn decode_after_magic<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
29        Ok(Self(DictRef::decode(decoder)?))
30    }
31
32    /// Decodes body until `"BNCN"` tag is found, then returns the integer value.
33    ///
34    /// # Errors
35    ///
36    /// An error may be returned if a decoding error occurs while searching for the tag or if the tag value is
37    /// not an integer.
38    ///
39    /// # Security
40    ///
41    /// This function may not be used to validate that Image4 encoding inside restore info is valid. As soon
42    /// as the boot nonce tag is found decoding stops and whatever follows the tag may be invalid. In case it
43    /// is required to validate the encoding, [`RestoreInfoRef::decode_owned`] method should be used.
44    pub fn decode_boot_nonce(&self) -> der::Result<Option<u64>> {
45        for result in self.0.iter()? {
46            let (tag, value) = result?;
47
48            if tag != Tag::from_bytes(*b"BNCN") {
49                continue;
50            }
51
52            let ValueRef::Integer(nonce) = value else {
53                return Err(der::Error::new(
54                    der::ErrorKind::TagUnexpected {
55                        expected: Some(der::Tag::Integer),
56                        actual: value.tag(),
57                    },
58                    Length::ZERO,
59                ));
60            };
61
62            return Ok(Some(nonce));
63        }
64
65        Ok(None)
66    }
67
68    /// Decodes the contents of the body into an owned type.
69    ///
70    /// This function completely validates the contents and currently is the only way to do
71    /// this.
72    #[cfg(any(feature = "alloc", test))]
73    pub fn decode_owned(&self) -> der::Result<BTreeMap<Tag, Value>> {
74        self.0.decode_owned()
75    }
76}
77
78impl<'a> Decode<'a> for RestoreInfoRef<'a> {
79    fn decode<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
80        decoder.sequence(|nested_decoder| {
81            util::decode_and_check_magic(nested_decoder, b"IM4R")?;
82            Self::decode_after_magic(nested_decoder)
83        })
84    }
85}
86
87impl EncodeValue for RestoreInfoRef<'_> {
88    fn value_len(&self) -> der::Result<Length> {
89        Ia5StringRef::new(&b"IM4R")?
90            .encoded_len()?
91            .add(self.0.encoded_len()?)
92    }
93
94    fn encode_value(&self, encoder: &mut impl Writer) -> der::Result<()> {
95        Ia5StringRef::new(&b"IM4R")?.encode(encoder)?;
96        self.0.encode(encoder)
97    }
98}
99
100impl FixedTag for RestoreInfoRef<'_> {
101    const TAG: der::Tag = der::Tag::Sequence;
102}
103
104#[cfg(any(feature = "alloc", test))]
105impl<'a> RefToOwned<'a> for RestoreInfoRef<'a> {
106    type Owned = RestoreInfo;
107
108    fn ref_to_owned(&self) -> Self::Owned {
109        RestoreInfo(self.0.ref_to_owned())
110    }
111}
112
113/// Restore info that might be present in an Image4 file.
114#[derive(Clone, Debug)]
115#[cfg_attr(test, derive(Eq, PartialEq))]
116#[cfg(any(feature = "alloc", test))]
117pub struct RestoreInfo(Dict);
118
119#[cfg(any(feature = "alloc", test))]
120impl RestoreInfo {
121    /// Encodes a [`RestoreInfo`] containing only a boot nonce.
122    pub fn with_boot_nonce(nonce: u64) -> der::Result<Self> {
123        let mut map = BTreeMap::new();
124        map.insert(Tag::from_bytes(*b"BNCN"), Value::Integer(nonce));
125
126        Self::encode_from(&map)
127    }
128
129    /// Encodes a [`BTreeMap`] describing a dictionary of Image4 properties into a [`Dict`] and wraps
130    /// it into a [`RestoreInfo`].
131    pub fn encode_from(value: &BTreeMap<Tag, Value>) -> der::Result<Self> {
132        Dict::encode_from(value).map(Self)
133    }
134
135    /// Returns a byte slice for the encoded wrapped dictionary.
136    pub fn as_bytes(&self) -> &[u8] {
137        self.0.as_bytes()
138    }
139
140    /// Converts restore info into the dictionary it wraps.
141    pub fn into_dict(self) -> Dict {
142        self.0
143    }
144
145    /// Returns a borrowed version of the underlying dictionary.
146    pub fn as_dict_ref(&self) -> DictRef<'_> {
147        self.0.owned_to_ref()
148    }
149}
150
151#[cfg(any(feature = "alloc", test))]
152impl From<Dict> for RestoreInfo {
153    fn from(value: Dict) -> Self {
154        Self(value)
155    }
156}
157
158#[cfg(any(feature = "alloc", test))]
159impl From<&'_ DictRef<'_>> for RestoreInfo {
160    fn from(value: &'_ DictRef<'_>) -> Self {
161        Self(value.into())
162    }
163}
164
165#[cfg(any(feature = "alloc", test))]
166impl From<DictRef<'_>> for RestoreInfo {
167    fn from(value: DictRef<'_>) -> Self {
168        Self(value.into())
169    }
170}
171
172#[cfg(any(feature = "alloc", test))]
173impl From<RestoreInfo> for Dict {
174    fn from(value: RestoreInfo) -> Self {
175        value.0
176    }
177}
178
179#[cfg(any(feature = "alloc", test))]
180impl<'a> Decode<'a> for RestoreInfo {
181    fn decode<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
182        Dict::decode(decoder).map(Self)
183    }
184}
185
186#[cfg(any(feature = "alloc", test))]
187impl Encode for RestoreInfo {
188    fn encoded_len(&self) -> der::Result<Length> {
189        self.owned_to_ref().encoded_len()
190    }
191
192    fn encode(&self, encoder: &mut impl Writer) -> der::Result<()> {
193        self.owned_to_ref().encode(encoder)
194    }
195}
196
197#[cfg(any(feature = "alloc", test))]
198impl OwnedToRef for RestoreInfo {
199    type Borrowed<'a> = RestoreInfoRef<'a>;
200
201    fn owned_to_ref(&self) -> Self::Borrowed<'_> {
202        RestoreInfoRef(self.0.owned_to_ref())
203    }
204}