opentitanlib/ownership/
signature.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;
8use serde_annotate::Annotate;
9use sphincsplus::{SpxDomain, SpxSecretKey};
10use std::io::{Read, Write};
11use std::path::Path;
12
13use super::GlobalFlags;
14use super::misc::{OwnershipKeyAlg, TlvHeader, TlvTag};
15use crate::crypto::Error;
16use crate::crypto::ecdsa::{EcdsaPrivateKey, EcdsaRawSignature};
17use crate::crypto::sha256::Sha256Digest;
18
19#[derive(Debug, Deserialize, Annotate)]
20pub struct DetachedSignature {
21    #[serde(
22        skip_serializing_if = "GlobalFlags::not_debug",
23        default = "DetachedSignature::default_header"
24    )]
25    pub header: TlvHeader,
26    #[serde(default, skip_serializing_if = "GlobalFlags::not_debug")]
27    #[annotate(format=hex)]
28    pub reserved: [u32; 2],
29    #[serde(default)]
30    pub command: u32,
31    #[serde(default)]
32    pub algorithm: OwnershipKeyAlg,
33    #[serde(default)]
34    pub nonce: u64,
35    #[serde(default)]
36    pub ecdsa: Option<EcdsaRawSignature>,
37    #[serde(default)]
38    pub spx: Option<Vec<u8>>,
39}
40
41impl Default for DetachedSignature {
42    fn default() -> Self {
43        Self {
44            header: Self::default_header(),
45            reserved: [0, 0],
46            command: 0,
47            algorithm: OwnershipKeyAlg::Unknown,
48            nonce: 0,
49            ecdsa: None,
50            spx: None,
51        }
52    }
53}
54
55impl DetachedSignature {
56    const SIZE: usize = 8192;
57    const SPX_SIZE: usize = 7856;
58
59    fn default_header() -> TlvHeader {
60        TlvHeader::new(TlvTag::DetachedSignature, 0, "0.0")
61    }
62
63    pub fn read(src: &mut impl Read, header: TlvHeader) -> Result<Self> {
64        let mut reserved = [0u32; 2];
65        src.read_u32_into::<LittleEndian>(&mut reserved)?;
66        let command = src.read_u32::<LittleEndian>()?;
67        let algorithm = OwnershipKeyAlg(src.read_u32::<LittleEndian>()?);
68        let nonce = src.read_u64::<LittleEndian>()?;
69        let ecdsa = if algorithm.is_ecdsa() {
70            Some(EcdsaRawSignature::read(src)?)
71        } else {
72            None
73        };
74        let spx = if algorithm.is_spx() {
75            let mut spx = vec![0u8; Self::SPX_SIZE];
76            src.read_exact(&mut spx)?;
77            Some(spx)
78        } else {
79            None
80        };
81        Ok(Self {
82            header,
83            reserved,
84            command,
85            algorithm,
86            nonce,
87            ecdsa,
88            spx,
89        })
90    }
91
92    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
93        let header = TlvHeader::new(TlvTag::DetachedSignature, Self::SIZE, "0.0");
94        header.write(dest)?;
95        for x in &self.reserved {
96            dest.write_u32::<LittleEndian>(*x)?;
97        }
98        dest.write_u32::<LittleEndian>(self.command)?;
99        dest.write_u32::<LittleEndian>(u32::from(self.algorithm))?;
100        dest.write_u64::<LittleEndian>(self.nonce)?;
101        let mut len = 32;
102        if self.algorithm.is_ecdsa() {
103            let ecdsa = self.ecdsa.as_ref().ok_or_else(|| {
104                Error::InvalidSignature(anyhow!(
105                    "Algorithm {} requires an ECDSA signature",
106                    self.algorithm
107                ))
108            })?;
109            len += ecdsa.write(dest)?;
110        }
111        if self.algorithm.is_spx() {
112            let spx = self.spx.as_ref().ok_or_else(|| {
113                Error::InvalidSignature(anyhow!(
114                    "Algorithm {} requires an SPX signature",
115                    self.algorithm
116                ))
117            })?;
118            dest.write_all(spx.as_slice())?;
119            len += spx.len();
120        }
121
122        let pad = vec![0xffu8; Self::SIZE - len];
123        dest.write_all(&pad)?;
124        Ok(())
125    }
126
127    pub fn to_vec(&self) -> Result<Vec<u8>> {
128        let mut result = Vec::new();
129        self.write(&mut result)?;
130        Ok(result)
131    }
132
133    pub fn new(
134        data: &[u8],
135        command: u32,
136        algorithm: OwnershipKeyAlg,
137        nonce: u64,
138        ecdsa_key: Option<&EcdsaPrivateKey>,
139        spx_key: Option<&SpxSecretKey>,
140    ) -> Result<Self> {
141        let digest = Sha256Digest::hash(data);
142        let ecdsa = if algorithm.is_ecdsa() {
143            let key = ecdsa_key.ok_or_else(|| {
144                Error::SignFailed(anyhow!("Algorithm {algorithm} requires an ECDSA key"))
145            })?;
146            Some(key.sign(&digest)?)
147        } else {
148            None
149        };
150        let spx = if algorithm.is_spx() {
151            let key = spx_key.ok_or_else(|| {
152                Error::SignFailed(anyhow!("Algorithm {algorithm} requires an SPX key"))
153            })?;
154            let (domain, msg) = if algorithm.is_prehashed() {
155                let domain = SpxDomain::PreHashedSha256;
156                (domain, digest.as_ref())
157            } else {
158                let domain = SpxDomain::Pure;
159                (domain, data)
160            };
161            Some(key.sign(domain, msg)?)
162        } else {
163            None
164        };
165
166        Ok(Self {
167            header: Self::default_header(),
168            command,
169            algorithm,
170            nonce,
171            ecdsa,
172            spx,
173            ..Default::default()
174        })
175    }
176
177    pub fn from_raw_signatures(
178        command: u32,
179        algorithm: OwnershipKeyAlg,
180        nonce: u64,
181        ecdsa_signature_path: Option<&Path>,
182        spx_signature_path: Option<&Path>,
183    ) -> Result<Self> {
184        let mut ecdsa_sig: Option<EcdsaRawSignature> = None;
185        let mut spx_sig: Option<Vec<u8>> = None;
186
187        if algorithm.is_ecdsa() {
188            let path =
189                ecdsa_signature_path.ok_or_else(|| anyhow!("No ecdsa signature provided"))?;
190            let mut file = std::fs::File::open(path)?;
191            ecdsa_sig = Some(EcdsaRawSignature::read(&mut file)?);
192        }
193
194        if algorithm.is_spx() {
195            let path = spx_signature_path.ok_or_else(|| anyhow!("No spx signature provided"))?;
196            let spx_bytes = std::fs::read(path)?;
197            spx_sig = Some(spx_bytes);
198        }
199
200        Ok(Self {
201            header: Self::default_header(),
202            command,
203            algorithm,
204            nonce,
205            ecdsa: ecdsa_sig,
206            spx: spx_sig,
207            ..Default::default()
208        })
209    }
210}