Skip to content

Commit

Permalink
[hsmtool] Support sphincs+ signing domains
Browse files Browse the repository at this point in the history
Prepare data according to the input format (plain-text, sha256-hash,
etc) and the sphincs+ signing domain (none, pure, prehashedsha256).

Signed-off-by: Chris Frantz <[email protected]>
(cherry picked from commit fbd2358)
  • Loading branch information
cfrantz committed Dec 5, 2024
1 parent c8de296 commit 1b868fe
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 1 deletion.
13 changes: 13 additions & 0 deletions sw/host/hsmtool/src/commands/spx/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,30 @@ 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;

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 {
#[arg(long)]
id: Option<String>,
#[arg(short, long)]
label: Option<String>,
#[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,
Expand All @@ -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::<BasicResult>::default())
Expand Down
13 changes: 13 additions & 0 deletions sw/host/hsmtool/src/commands/spx/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,30 @@ 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;

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 {
#[arg(long)]
id: Option<String>,
#[arg(short, long)]
label: Option<String>,
#[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,
}
Expand All @@ -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 {
Expand Down
46 changes: 46 additions & 0 deletions sw/host/hsmtool/src/util/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -94,6 +95,51 @@ impl SignData {
}
}

pub fn spx_prepare(
&self,
domain: SpxDomain,
input: &[u8],
little_endian: bool,
) -> Result<Vec<u8>> {
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<Mechanism> {
match keytype {
Expand Down
3 changes: 2 additions & 1 deletion sw/host/sphincsplus/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 1b868fe

Please sign in to comment.