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        NotPresent = u32::from_le_bytes(*b"ZZZZ"),
28    }
29
30    pub enum OwnershipKeyAlg: u32 [default = Self::Unknown] {
31        Unknown = 0,
32        Rsa = u32::from_le_bytes(*b"RSA3"),
33        EcdsaP256 = u32::from_le_bytes(*b"P256"),
34        SpxPure = u32::from_le_bytes(*b"S+Pu"),
35        SpxPrehash = u32::from_le_bytes(*b"S+S2"),
36        HybridSpxPure = u32::from_le_bytes(*b"H+Pu"),
37        HybridSpxPrehash = u32::from_le_bytes(*b"H+S2"),
38    }
39}
40
41#[derive(Clone, Debug, Default, Serialize, Deserialize, Annotate)]
42#[serde(try_from = "String", into = "String")]
43pub struct StructVersion {
44    pub major: u8,
45    pub minor: u8,
46}
47
48impl TryFrom<&str> for StructVersion {
49    type Error = anyhow::Error;
50
51    fn try_from(s: &str) -> Result<Self, Self::Error> {
52        if let Some((major, minor)) = s.split_once('.') {
53            Ok(StructVersion {
54                major: major.parse()?,
55                minor: minor.parse()?,
56            })
57        } else {
58            Ok(StructVersion {
59                major: s.parse()?,
60                minor: 0,
61            })
62        }
63    }
64}
65
66impl TryFrom<String> for StructVersion {
67    type Error = anyhow::Error;
68    fn try_from(s: String) -> Result<Self, Self::Error> {
69        StructVersion::try_from(s.as_str())
70    }
71}
72
73impl From<StructVersion> for String {
74    fn from(sv: StructVersion) -> String {
75        format!("{}.{}", sv.major, sv.minor)
76    }
77}
78
79impl StructVersion {
80    pub fn read(src: &mut impl Read) -> Result<Self> {
81        Ok(Self {
82            major: src.read_u8()?,
83            minor: src.read_u8()?,
84        })
85    }
86    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
87        dest.write_u8(self.major)?;
88        dest.write_u8(self.minor)?;
89        Ok(())
90    }
91}
92
93#[derive(Debug, Default, Serialize, Deserialize, Annotate)]
94pub struct TlvHeader {
95    #[serde(default)]
96    pub identifier: TlvTag,
97    #[serde(default)]
98    pub length: usize,
99    #[serde(default)]
100    pub version: StructVersion,
101}
102
103impl TlvHeader {
104    pub const SIZE: usize = 8;
105    pub fn new(id: TlvTag, len: usize, version: &str) -> Self {
106        Self {
107            identifier: id,
108            length: len,
109            version: version.try_into().expect("major.minor version string"),
110        }
111    }
112
113    pub fn read(src: &mut impl Read) -> Result<Self> {
114        Ok(Self {
115            identifier: TlvTag(src.read_u32::<LittleEndian>()?),
116            length: src.read_u16::<LittleEndian>()? as usize,
117            version: StructVersion::read(src)?,
118        })
119    }
120
121    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
122        dest.write_u32::<LittleEndian>(u32::from(self.identifier))?;
123        dest.write_u16::<LittleEndian>(self.length as u16)?;
124        self.version.write(dest)?;
125        Ok(())
126    }
127    pub fn write_len(&self, dest: &mut impl Write, length: usize) -> Result<()> {
128        dest.write_u32::<LittleEndian>(u32::from(self.identifier))?;
129        dest.write_u16::<LittleEndian>(length as u16)?;
130        self.version.write(dest)?;
131        Ok(())
132    }
133}
134
135#[derive(Debug, Serialize, Deserialize)]
136pub struct HybridRawPublicKey {
137    #[serde(deserialize_with = "string_or_struct")]
138    pub ecdsa: EcdsaRawPublicKey,
139    #[serde(deserialize_with = "string_or_struct")]
140    pub spx: SpxRawPublicKey,
141}
142
143impl HybridRawPublicKey {
144    const SIZE: usize = EcdsaRawPublicKey::SIZE + SpxRawPublicKey::SIZE;
145
146    pub fn read(src: &mut impl Read) -> Result<Self> {
147        Ok(Self {
148            ecdsa: EcdsaRawPublicKey::read(src)?,
149            spx: SpxRawPublicKey::read(src)?,
150        })
151    }
152
153    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
154        self.ecdsa.write(dest)?;
155        self.spx.write(dest)?;
156        Ok(())
157    }
158}
159
160/// Low-level key material (ie: bit representation).
161#[derive(Debug, Serialize, Deserialize)]
162#[allow(clippy::len_without_is_empty)]
163pub enum KeyMaterial {
164    #[serde(alias = "unknown")]
165    Unknown(Vec<u8>),
166    #[serde(alias = "ecdsa")]
167    Ecdsa(#[serde(deserialize_with = "string_or_struct")] EcdsaRawPublicKey),
168    #[serde(alias = "rsa")]
169    Rsa(#[serde(deserialize_with = "string_or_struct")] RsaRawPublicKey),
170    #[serde(alias = "spx")]
171    Spx(#[serde(deserialize_with = "string_or_struct")] SpxRawPublicKey),
172    #[serde(alias = "hybrid")]
173    Hybrid(HybridRawPublicKey),
174}
175
176impl Default for KeyMaterial {
177    fn default() -> Self {
178        Self::Unknown(Vec::default())
179    }
180}
181
182impl KeyMaterial {
183    pub fn len(&self) -> usize {
184        match self {
185            KeyMaterial::Ecdsa(_) => EcdsaRawPublicKey::SIZE,
186            KeyMaterial::Rsa(_) => RsaRawPublicKey::SIZE,
187            KeyMaterial::Spx(_) => SpxRawPublicKey::SIZE,
188            KeyMaterial::Hybrid(_) => HybridRawPublicKey::SIZE,
189            KeyMaterial::Unknown(u) => u.len(),
190        }
191    }
192
193    pub fn kind(&self) -> OwnershipKeyAlg {
194        match self {
195            KeyMaterial::Ecdsa(_) => OwnershipKeyAlg::EcdsaP256,
196            KeyMaterial::Rsa(_) => OwnershipKeyAlg::Rsa,
197            KeyMaterial::Spx(_) => OwnershipKeyAlg::SpxPure,
198            KeyMaterial::Hybrid(_) => OwnershipKeyAlg::HybridSpxPure,
199            KeyMaterial::Unknown(_) => OwnershipKeyAlg::Unknown,
200        }
201    }
202
203    pub fn read_length(src: &mut impl Read, kind: OwnershipKeyAlg, buflen: usize) -> Result<Self> {
204        let result = match kind {
205            OwnershipKeyAlg::Rsa => KeyMaterial::Rsa(RsaRawPublicKey::read(src)?),
206            OwnershipKeyAlg::EcdsaP256 => KeyMaterial::Ecdsa(EcdsaRawPublicKey::read(src)?),
207            OwnershipKeyAlg::SpxPure | OwnershipKeyAlg::SpxPrehash => {
208                KeyMaterial::Spx(SpxRawPublicKey::read(src)?)
209            }
210            OwnershipKeyAlg::HybridSpxPure | OwnershipKeyAlg::HybridSpxPrehash => {
211                KeyMaterial::Hybrid(HybridRawPublicKey::read(src)?)
212            }
213            _ => {
214                return Err(
215                    Error::InvalidPublicKey(anyhow!("Unknown key algorithm {}", kind)).into(),
216                );
217            }
218        };
219        let len = result.len();
220        if buflen != 0 {
221            match len.cmp(&buflen) {
222                Ordering::Less => {
223                    let mut discard = vec![0; buflen - len];
224                    src.read_exact(&mut discard)?;
225                }
226                Ordering::Greater => {
227                    return Err(Error::InvalidPublicKey(anyhow!(
228                        "Key type {} does not fit in {} bytes",
229                        kind,
230                        buflen
231                    ))
232                    .into());
233                }
234                Ordering::Equal => {}
235            };
236        }
237        Ok(result)
238    }
239
240    pub fn write_length(&self, dest: &mut impl Write, buflen: usize) -> Result<()> {
241        match self {
242            KeyMaterial::Ecdsa(k) => k.write(dest)?,
243            KeyMaterial::Rsa(k) => k.write(dest)?,
244            KeyMaterial::Spx(k) => k.write(dest)?,
245            KeyMaterial::Hybrid(k) => k.write(dest)?,
246            _ => {
247                return Err(Error::InvalidPublicKey(anyhow!("Unknown key type")).into());
248            }
249        };
250
251        if buflen != 0 {
252            let len = self.len();
253            match len.cmp(&buflen) {
254                Ordering::Less => {
255                    let zero = vec![0; buflen - len];
256                    dest.write_all(&zero)?;
257                }
258                Ordering::Greater => {
259                    return Err(Error::InvalidPublicKey(anyhow!(
260                        "Key type {} does not fit in {} bytes",
261                        self.kind(),
262                        buflen
263                    ))
264                    .into());
265                }
266                Ordering::Equal => {}
267            };
268        }
269        Ok(())
270    }
271}