opentitanlib/ownership/
signature.rs1use 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}