diff --git a/sw/host/hsmtool/src/commands/spx/sign.rs b/sw/host/hsmtool/src/commands/spx/sign.rs index 7a1dc0b024f7c..7fb85ead29e63 100644 --- a/sw/host/hsmtool/src/commands/spx/sign.rs +++ b/sw/host/hsmtool/src/commands/spx/sign.rs @@ -6,6 +6,7 @@ use anyhow::Result; use cryptoki::session::Session; use serde::{Deserialize, Serialize}; use serde_annotate::Annotate; +use sphincsplus::SpxDomain; use std::any::Any; use std::path::PathBuf; @@ -13,6 +14,7 @@ use crate::commands::{BasicResult, Dispatch}; use crate::error::HsmError; use crate::module::Module; use crate::util::helper; +use crate::util::signing::SignData; #[derive(clap::Args, Debug, Serialize, Deserialize)] pub struct Sign { @@ -20,6 +22,14 @@ pub struct Sign { id: Option, #[arg(short, long)] label: Option, + #[arg(short, long, default_value = "plain-text", help=SignData::HELP)] + format: SignData, + /// Reverse the input data (for little-endian targets). + #[arg(short = 'r', long)] + little_endian: bool, + /// The SPHINCS+ signing domain. + #[arg(short = 'd', long, default_value = "pure")] + domain: SpxDomain, #[arg(short, long)] output: PathBuf, input: PathBuf, @@ -37,6 +47,9 @@ impl Dispatch for Sign { let _token = hsm.token.as_deref().ok_or(HsmError::SessionRequired)?; let data = helper::read_file(&self.input)?; + let data = self + .format + .spx_prepare(self.domain, &data, self.little_endian)?; let result = acorn.sign(self.label.as_deref(), self.id.as_deref(), &data)?; helper::write_file(&self.output, &result)?; Ok(Box::::default()) diff --git a/sw/host/hsmtool/src/commands/spx/verify.rs b/sw/host/hsmtool/src/commands/spx/verify.rs index e7d9b60f12677..54de012125486 100644 --- a/sw/host/hsmtool/src/commands/spx/verify.rs +++ b/sw/host/hsmtool/src/commands/spx/verify.rs @@ -6,6 +6,7 @@ use anyhow::Result; use cryptoki::session::Session; use serde::{Deserialize, Serialize}; use serde_annotate::Annotate; +use sphincsplus::SpxDomain; use std::any::Any; use std::path::PathBuf; @@ -13,6 +14,7 @@ use crate::commands::{BasicResult, Dispatch}; use crate::error::HsmError; use crate::module::Module; use crate::util::helper; +use crate::util::signing::SignData; #[derive(clap::Args, Debug, Serialize, Deserialize)] pub struct Verify { @@ -20,6 +22,14 @@ pub struct Verify { id: Option, #[arg(short, long)] label: Option, + #[arg(short, long, default_value = "plain-text", help=SignData::HELP)] + format: SignData, + /// Reverse the input data (for little-endian targets). + #[arg(short = 'r', long)] + little_endian: bool, + /// The SPHINCS+ signing domain. + #[arg(short = 'd', long, default_value = "pure")] + domain: SpxDomain, input: PathBuf, signature: PathBuf, } @@ -36,6 +46,9 @@ impl Dispatch for Verify { let _token = hsm.token.as_deref().ok_or(HsmError::SessionRequired)?; let data = helper::read_file(&self.input)?; + let data = self + .format + .spx_prepare(self.domain, &data, self.little_endian)?; let signature = helper::read_file(&self.signature)?; let result = acorn.verify(self.label.as_deref(), self.id.as_deref(), &data, &signature)?; Ok(Box::new(BasicResult { diff --git a/sw/host/hsmtool/src/util/signing.rs b/sw/host/hsmtool/src/util/signing.rs index da11890ca2e01..02837a13800b1 100644 --- a/sw/host/hsmtool/src/util/signing.rs +++ b/sw/host/hsmtool/src/util/signing.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use sha2::digest::const_oid::AssociatedOid; use sha2::digest::Digest; use sha2::Sha256; +use sphincsplus::SpxDomain; use std::str::FromStr; use crate::error::HsmError; @@ -94,6 +95,51 @@ impl SignData { } } + pub fn spx_prepare( + &self, + domain: SpxDomain, + input: &[u8], + little_endian: bool, + ) -> Result> { + match self { + SignData::PlainText => { + match domain { + // Plaintext in domain None or Pure: prepare. + SpxDomain::None | SpxDomain::Pure => Ok(domain.prepare(input).into()), + // Plaintext in domain PreHashed: hash first, then prepare. + SpxDomain::PreHashedSha256 => { + let digest = Sha256::digest(input).as_slice().to_vec(); + Ok(domain.prepare(&digest).into()) + } + } + } + SignData::Sha256Hash => { + // Sha256 input in any domain: perform the domain preparation. + // If the `little_endian` flag is true, we assume the pre-hashed input came + // from opentitantool, which writes out the hash in little endian order, + // and therefore, needs to be reversed before the signing operation. + let digest = Self::data_raw(input, little_endian)?; + Ok(domain.prepare(&digest).into()) + } + SignData::Raw => { + // Raw data in any domain: perform the domain preparation. + Ok(domain.prepare(input).into()) + } + SignData::Slice(a, b) => { + let input = &input[*a..*b]; + match domain { + // A slice of the input in domain None or Pure: prepare. + SpxDomain::None | SpxDomain::Pure => Ok(domain.prepare(input).into()), + // A slice of the input in domain PreHashed: hash first, then prepare. + SpxDomain::PreHashedSha256 => { + let digest = Sha256::digest(input).as_slice().to_vec(); + Ok(domain.prepare(&digest).into()) + } + } + } + } + } + /// Return the `Mechanism` needed during signing or verification. pub fn mechanism(&self, keytype: KeyType) -> Result { match keytype { diff --git a/sw/host/sphincsplus/key.rs b/sw/host/sphincsplus/key.rs index 8a8463ea191e8..9f0b90399d3f8 100644 --- a/sw/host/sphincsplus/key.rs +++ b/sw/host/sphincsplus/key.rs @@ -4,6 +4,7 @@ use crate::{DecodeKey, EncodeKey, SphincsPlus, SpxError}; use pem_rfc7468::LineEnding; +use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::fmt; use std::str::FromStr; @@ -31,7 +32,7 @@ impl fmt::Debug for SpxSecretKey { } } -#[derive(Default, Debug, Clone, Copy, EnumString, Display)] +#[derive(Default, Debug, Clone, Copy, EnumString, Display, Serialize, Deserialize)] #[strum(ascii_case_insensitive)] pub enum SpxDomain { None,