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