opentitanlib/ownership/
misc.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{Result, anyhow};
6use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
7use serde::{Deserialize, Serialize};
8use serde_annotate::Annotate;
9use std::cmp::Ordering;
10use std::io::{Read, Write};
11
12use crate::crypto::Error;
13use crate::crypto::ecdsa::EcdsaRawPublicKey;
14use crate::crypto::rsa::RsaRawPublicKey;
15use crate::crypto::spx::SpxRawPublicKey;
16use crate::util::serde::string_or_struct;
17use crate::with_unknown;
18
19with_unknown! {
20    pub enum TlvTag: u32 [default = Self::Unknown] {
21        Unknown = 0,
22        Owner = u32::from_le_bytes(*b"OWNR"),
23        ApplicationKey = u32::from_le_bytes(*b"APPK"),
24        FlashConfig = u32::from_le_bytes(*b"FLSH"),
25        FlashInfoConfig = u32::from_le_bytes(*b"INFO"),
26        Rescue = u32::from_le_bytes(*b"RESQ"),
27        DetachedSignature = u32::from_le_bytes(*b"SIGN"),
28        IntegratorSpecificFirmwareBinding = u32::from_le_bytes(*b"ISFB"),
29        NotPresent = u32::from_le_bytes(*b"ZZZZ"),
30    }
31
32    pub enum OwnershipKeyAlg: u32 [default = Self::Unknown] {
33        Unknown = 0,
34        Rsa = u32::from_le_bytes(*b"RSA3"),
35        EcdsaP256 = u32::from_le_bytes(*b"P256"),
36        SpxPure = u32::from_le_bytes(*b"S+Pu"),
37        SpxPrehash = u32::from_le_bytes(*b"S+S2"),
38        HybridSpxPure = u32::from_le_bytes(*b"H+Pu"),
39        HybridSpxPrehash = u32::from_le_bytes(*b"H+S2"),
40    }
41    pub enum DetachedSignatureCommand: u32 [default = Self::Unknown] {
42        Unknown = 0,
43        Owner = u32::from_le_bytes(*b"OWNR"),
44        Unlock = u32::from_le_bytes(*b"UNLK"),
45        Activate = u32::from_le_bytes(*b"ACTV"),
46    }
47}
48
49impl OwnershipKeyAlg {
50    pub fn is_detached(self) -> bool {
51        !matches!(self, Self::EcdsaP256)
52    }
53
54    pub fn is_ecdsa(self) -> bool {
55        matches!(
56            self,
57            Self::EcdsaP256 | Self::HybridSpxPure | Self::HybridSpxPrehash
58        )
59    }
60
61    pub fn is_spx(self) -> bool {
62        matches!(
63            self,
64            Self::SpxPure | Self::SpxPrehash | Self::HybridSpxPure | Self::HybridSpxPrehash
65        )
66    }
67
68    pub fn is_hybrid(self) -> bool {
69        matches!(self, Self::HybridSpxPure | Self::HybridSpxPrehash)
70    }
71
72    pub fn is_prehashed(self) -> bool {
73        matches!(self, Self::SpxPrehash | Self::HybridSpxPrehash)
74    }
75}
76
77#[derive(Clone, Debug, Default, Deserialize, Serialize)]
78#[serde(try_from = "String", into = "String")]
79pub struct StructVersion {
80    pub major: u8,
81    pub minor: u8,
82}
83
84impl TryFrom<&str> for StructVersion {
85    type Error = anyhow::Error;
86
87    fn try_from(s: &str) -> Result<Self, Self::Error> {
88        if let Some((major, minor)) = s.split_once('.') {
89            Ok(StructVersion {
90                major: major.parse()?,
91                minor: minor.parse()?,
92            })
93        } else {
94            Ok(StructVersion {
95                major: s.parse()?,
96                minor: 0,
97            })
98        }
99    }
100}
101
102impl TryFrom<String> for StructVersion {
103    type Error = anyhow::Error;
104    fn try_from(s: String) -> Result<Self, Self::Error> {
105        StructVersion::try_from(s.as_str())
106    }
107}
108
109impl From<StructVersion> for String {
110    fn from(sv: StructVersion) -> String {
111        format!("{}.{}", sv.major, sv.minor)
112    }
113}
114
115impl StructVersion {
116    pub fn read(src: &mut impl Read) -> Result<Self> {
117        Ok(Self {
118            major: src.read_u8()?,
119            minor: src.read_u8()?,
120        })
121    }
122    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
123        dest.write_u8(self.major)?;
124        dest.write_u8(self.minor)?;
125        Ok(())
126    }
127}
128
129#[derive(Debug, Default, Deserialize, Annotate)]
130pub struct TlvHeader {
131    #[serde(default)]
132    pub identifier: TlvTag,
133    #[serde(default)]
134    pub length: usize,
135    #[serde(default)]
136    pub version: StructVersion,
137}
138
139impl TlvHeader {
140    pub const SIZE: usize = 8;
141    pub fn new(id: TlvTag, len: usize, version: &str) -> Self {
142        Self {
143            identifier: id,
144            length: len,
145            version: version.try_into().expect("major.minor version string"),
146        }
147    }
148
149    pub fn read(src: &mut impl Read) -> Result<Self> {
150        Ok(Self {
151            identifier: TlvTag(src.read_u32::<LittleEndian>()?),
152            length: src.read_u16::<LittleEndian>()? as usize,
153            version: StructVersion::read(src)?,
154        })
155    }
156
157    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
158        dest.write_u32::<LittleEndian>(u32::from(self.identifier))?;
159        dest.write_u16::<LittleEndian>(self.length as u16)?;
160        self.version.write(dest)?;
161        Ok(())
162    }
163    pub fn write_len(&self, dest: &mut impl Write, length: usize) -> Result<()> {
164        dest.write_u32::<LittleEndian>(u32::from(self.identifier))?;
165        dest.write_u16::<LittleEndian>(length as u16)?;
166        self.version.write(dest)?;
167        Ok(())
168    }
169}
170
171#[derive(Debug, Serialize, Deserialize)]
172pub struct HybridRawPublicKey {
173    #[serde(deserialize_with = "string_or_struct")]
174    pub ecdsa: EcdsaRawPublicKey,
175    #[serde(deserialize_with = "string_or_struct")]
176    pub spx: SpxRawPublicKey,
177}
178
179impl HybridRawPublicKey {
180    const SIZE: usize = EcdsaRawPublicKey::SIZE + SpxRawPublicKey::SIZE;
181
182    pub fn read(src: &mut impl Read) -> Result<Self> {
183        Ok(Self {
184            ecdsa: EcdsaRawPublicKey::read(src)?,
185            spx: SpxRawPublicKey::read(src)?,
186        })
187    }
188
189    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
190        self.ecdsa.write(dest)?;
191        self.spx.write(dest)?;
192        Ok(())
193    }
194}
195
196/// Low-level key material (ie: bit representation).
197#[derive(Debug, Serialize, Deserialize)]
198#[allow(clippy::len_without_is_empty)]
199pub enum KeyMaterial {
200    #[serde(alias = "unknown")]
201    Unknown(Vec<u8>),
202    #[serde(alias = "ecdsa")]
203    Ecdsa(#[serde(deserialize_with = "string_or_struct")] EcdsaRawPublicKey),
204    #[serde(alias = "rsa")]
205    Rsa(#[serde(deserialize_with = "string_or_struct")] RsaRawPublicKey),
206    #[serde(alias = "spx")]
207    Spx(#[serde(deserialize_with = "string_or_struct")] SpxRawPublicKey),
208    #[serde(alias = "hybrid")]
209    Hybrid(HybridRawPublicKey),
210}
211
212impl Default for KeyMaterial {
213    fn default() -> Self {
214        Self::Unknown(Vec::default())
215    }
216}
217
218impl KeyMaterial {
219    pub fn len(&self) -> usize {
220        match self {
221            KeyMaterial::Ecdsa(_) => EcdsaRawPublicKey::SIZE,
222            KeyMaterial::Rsa(_) => RsaRawPublicKey::SIZE,
223            KeyMaterial::Spx(_) => SpxRawPublicKey::SIZE,
224            KeyMaterial::Hybrid(_) => HybridRawPublicKey::SIZE,
225            KeyMaterial::Unknown(u) => u.len(),
226        }
227    }
228
229    pub fn kind(&self) -> OwnershipKeyAlg {
230        match self {
231            KeyMaterial::Ecdsa(_) => OwnershipKeyAlg::EcdsaP256,
232            KeyMaterial::Rsa(_) => OwnershipKeyAlg::Rsa,
233            KeyMaterial::Spx(_) => OwnershipKeyAlg::SpxPure,
234            KeyMaterial::Hybrid(_) => OwnershipKeyAlg::HybridSpxPure,
235            KeyMaterial::Unknown(_) => OwnershipKeyAlg::Unknown,
236        }
237    }
238
239    pub fn read_length(src: &mut impl Read, kind: OwnershipKeyAlg, buflen: usize) -> Result<Self> {
240        let result = match kind {
241            OwnershipKeyAlg::Rsa => KeyMaterial::Rsa(RsaRawPublicKey::read(src)?),
242            OwnershipKeyAlg::EcdsaP256 => KeyMaterial::Ecdsa(EcdsaRawPublicKey::read(src)?),
243            OwnershipKeyAlg::SpxPure | OwnershipKeyAlg::SpxPrehash => {
244                KeyMaterial::Spx(SpxRawPublicKey::read(src)?)
245            }
246            OwnershipKeyAlg::HybridSpxPure | OwnershipKeyAlg::HybridSpxPrehash => {
247                KeyMaterial::Hybrid(HybridRawPublicKey::read(src)?)
248            }
249            _ => {
250                return Err(
251                    Error::InvalidPublicKey(anyhow!("Unknown key algorithm {}", kind)).into(),
252                );
253            }
254        };
255        let len = result.len();
256        if buflen != 0 {
257            match len.cmp(&buflen) {
258                Ordering::Less => {
259                    let mut discard = vec![0; buflen - len];
260                    src.read_exact(&mut discard)?;
261                }
262                Ordering::Greater => {
263                    return Err(Error::InvalidPublicKey(anyhow!(
264                        "Key type {} does not fit in {} bytes",
265                        kind,
266                        buflen
267                    ))
268                    .into());
269                }
270                Ordering::Equal => {}
271            };
272        }
273        Ok(result)
274    }
275
276    pub fn write_length(&self, dest: &mut impl Write, buflen: usize) -> Result<()> {
277        match self {
278            KeyMaterial::Ecdsa(k) => k.write(dest)?,
279            KeyMaterial::Rsa(k) => k.write(dest)?,
280            KeyMaterial::Spx(k) => k.write(dest)?,
281            KeyMaterial::Hybrid(k) => k.write(dest)?,
282            _ => {
283                return Err(Error::InvalidPublicKey(anyhow!("Unknown key type")).into());
284            }
285        };
286
287        if buflen != 0 {
288            let len = self.len();
289            match len.cmp(&buflen) {
290                Ordering::Less => {
291                    let zero = vec![0; buflen - len];
292                    dest.write_all(&zero)?;
293                }
294                Ordering::Greater => {
295                    return Err(Error::InvalidPublicKey(anyhow!(
296                        "Key type {} does not fit in {} bytes",
297                        self.kind(),
298                        buflen
299                    ))
300                    .into());
301                }
302                Ordering::Equal => {}
303            };
304        }
305        Ok(())
306    }
307}