1use anyhow::Result;
6use cryptoki::mechanism::Mechanism;
7use rsa::pkcs1v15::Pkcs1v15Sign;
8use serde::{Deserialize, Serialize};
9use sha2::Sha256;
10use sha2::digest::Digest;
11use sha2::digest::const_oid::AssociatedOid;
12use sphincsplus::SpxDomain;
13use std::str::FromStr;
14
15use crate::error::HsmError;
16use crate::util::attribute::KeyType;
17use crate::util::helper::parse_range;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21pub enum SignData {
22 #[serde(alias = "plain-text")]
25 PlainText,
26 #[serde(alias = "sha256-hash")]
29 Sha256Hash,
30 #[serde(alias = "raw")]
32 Raw,
33 #[serde(alias = "slice")]
36 Slice(usize, usize),
37}
38
39impl FromStr for SignData {
40 type Err = anyhow::Error;
41 fn from_str(input: &str) -> Result<Self> {
42 if input.eq_ignore_ascii_case("plain-text") {
43 Ok(SignData::PlainText)
44 } else if input.eq_ignore_ascii_case("sha256-hash") {
45 Ok(SignData::Sha256Hash)
46 } else if input.eq_ignore_ascii_case("raw") {
47 Ok(SignData::Raw)
48 } else if input[..6].eq_ignore_ascii_case("slice:") {
49 let r = parse_range(&input[6..])?;
50 Ok(SignData::Slice(r.start, r.end))
51 } else {
52 Err(HsmError::Unsupported(format!("invalid variant: {input}")).into())
53 }
54 }
55}
56
57impl SignData {
58 pub const HELP: &'static str = "[allowed values: plain-text, sha256-hash, raw, slice:m..n]";
59 pub fn prepare(&self, keytype: KeyType, input: &[u8], little_endian: bool) -> Result<Vec<u8>> {
61 match keytype {
62 KeyType::Rsa => match self {
63 SignData::PlainText => {
65 Self::pkcs15sign::<Sha256>(&Self::data_plain_text(input, false)?)
66 }
67 SignData::Sha256Hash => {
72 Self::pkcs15sign::<Sha256>(&Self::data_raw(input, little_endian)?)
73 }
74 SignData::Raw => Self::data_raw(input, false),
76 SignData::Slice(a, b) => {
78 Self::pkcs15sign::<Sha256>(&Self::data_plain_text(&input[*a..*b], false)?)
79 }
80 },
81 KeyType::Ec => match self {
82 SignData::PlainText => Self::data_plain_text(input, false),
84 SignData::Sha256Hash => Self::data_raw(input, little_endian),
89 SignData::Raw => Self::data_raw(input, false),
91 SignData::Slice(a, b) => Self::data_plain_text(&input[*a..*b], false),
93 },
94 _ => Err(HsmError::Unsupported(format!("SignData prepare for {keytype:?}")).into()),
95 }
96 }
97
98 pub fn spx_prepare(
99 &self,
100 domain: SpxDomain,
101 input: &[u8],
102 little_endian: bool,
103 ) -> Result<Vec<u8>> {
104 match self {
105 SignData::PlainText => {
106 match domain {
107 SpxDomain::None | SpxDomain::Pure => Ok(domain.prepare(input).into()),
109 SpxDomain::PreHashedSha256 => {
111 let digest = Sha256::digest(input).as_slice().to_vec();
112 Ok(domain.prepare(&digest).into())
113 }
114 }
115 }
116 SignData::Sha256Hash => {
117 let digest = Self::data_raw(input, little_endian)?;
122 Ok(domain.prepare(&digest).into())
123 }
124 SignData::Raw => {
125 Ok(domain.prepare(input).into())
127 }
128 SignData::Slice(a, b) => {
129 let input = &input[*a..*b];
130 match domain {
131 SpxDomain::None | SpxDomain::Pure => Ok(domain.prepare(input).into()),
133 SpxDomain::PreHashedSha256 => {
135 let digest = Sha256::digest(input).as_slice().to_vec();
136 Ok(domain.prepare(&digest).into())
137 }
138 }
139 }
140 }
141 }
142
143 pub fn mechanism(&self, keytype: KeyType) -> Result<Mechanism<'_>> {
145 match keytype {
146 KeyType::Rsa => match self {
147 SignData::PlainText => Ok(Mechanism::RsaPkcs),
148 SignData::Sha256Hash => Ok(Mechanism::RsaPkcs),
149 SignData::Raw => Err(HsmError::Unsupported(
150 "rust-cryptoki Mechanism doesn't include RSA_X_509".into(),
151 )
152 .into()),
153 SignData::Slice(_, _) => Ok(Mechanism::RsaPkcs),
154 },
155 KeyType::Ec => match self {
156 SignData::PlainText => Ok(Mechanism::Ecdsa),
157 SignData::Sha256Hash => Ok(Mechanism::Ecdsa),
158 SignData::Raw => Ok(Mechanism::Ecdsa),
159 SignData::Slice(_, _) => Ok(Mechanism::Ecdsa),
160 },
161 _ => Err(HsmError::Unsupported(format!("No mechanism for {keytype:?}")).into()),
162 }
163 }
164
165 fn data_raw(input: &[u8], little_endian: bool) -> Result<Vec<u8>> {
166 let mut result = Vec::new();
167 result.extend_from_slice(input);
168 if little_endian {
169 result.reverse();
170 }
171 Ok(result)
172 }
173
174 fn pkcs15sign<D>(input: &[u8]) -> Result<Vec<u8>>
175 where
176 D: Digest + AssociatedOid,
177 {
178 let s = Pkcs1v15Sign::new::<D>();
179 let hash_len = s.hash_len.unwrap();
180 if hash_len != input.len() {
181 return Err(HsmError::HashSizeError(hash_len, input.len()).into());
182 }
183 let mut result = Vec::new();
184 result.extend_from_slice(&s.prefix);
185 result.extend_from_slice(input);
186 Ok(result)
187 }
188
189 fn data_plain_text(input: &[u8], little_endian: bool) -> Result<Vec<u8>> {
190 let mut result = Sha256::digest(input).as_slice().to_vec();
191 if little_endian {
192 result.reverse();
193 }
194 Ok(result)
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_raw() -> Result<()> {
204 let result = SignData::Raw.prepare(KeyType::Rsa, b"abc123", false)?;
205 assert_eq!(result, b"abc123");
206 Ok(())
207 }
208
209 #[test]
210 fn test_plain_text() -> Result<()> {
211 let result = SignData::PlainText.prepare(
212 KeyType::Rsa,
213 b"The quick brown fox jumped over the lazy dog",
214 false,
215 )?;
216 assert_eq!(
217 hex::encode(result),
218 "3031300d0609608648016503040201050004207d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69"
219 );
220
221 let result = SignData::PlainText.prepare(
222 KeyType::Ec,
223 b"The quick brown fox jumped over the lazy dog",
224 false,
225 )?;
226 assert_eq!(
227 hex::encode(result),
228 "7d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69",
229 );
230 Ok(())
231 }
232
233 #[test]
234 fn test_hashed() -> Result<()> {
235 let input =
236 hex::decode("7d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69")?;
237 let result = SignData::Sha256Hash.prepare(KeyType::Rsa, &input, false)?;
238 assert_eq!(
239 hex::encode(result),
240 "3031300d0609608648016503040201050004207d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69"
241 );
242
243 assert!(
244 SignData::Sha256Hash
245 .prepare(KeyType::Rsa, b"", false)
246 .is_err()
247 );
248 Ok(())
249 }
250
251 #[test]
252 fn test_slice() -> Result<()> {
253 let result = SignData::Slice(0, 3).prepare(
254 KeyType::Ec,
255 b"The quick brown fox jumped over the lazy dog",
256 false,
257 )?;
258 assert_eq!(
259 hex::encode(result),
260 "b344d80e24a3679999fa964450b34bc24d1578a35509f934c1418b0a20d21a67",
262 );
263 Ok(())
264 }
265}