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};
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}