Skip to content

Commit

Permalink
[host] Switch SPHINCS+ lib to use our own bindgen library.
Browse files Browse the repository at this point in the history
Instead of using the pqcrypto crate to indirectly access the reference
implementation, we now directly invoke our own bindings for the
reference implementation.

Signed-off-by: Jade Philipoom <[email protected]>
  • Loading branch information
jadephilipoom committed May 30, 2024
1 parent c45ec55 commit ee2a6ef
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 65 deletions.
3 changes: 1 addition & 2 deletions sw/host/opentitanlib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ rust_library(
deps = [
"//hw/top_earlgrey/sw/autogen/chip:top_earlgrey",
"//sw/host/opentitanlib/bindgen",
"//sw/host/sphincsplus",
"@crate_index//:anyhow",
"@crate_index//:arrayvec",
"@crate_index//:bitflags",
Expand Down Expand Up @@ -312,8 +313,6 @@ rust_library(
"@crate_index//:once_cell",
"@crate_index//:p256",
"@crate_index//:pem-rfc7468",
"@crate_index//:pqcrypto-sphincsplus",
"@crate_index//:pqcrypto-traits",
"@crate_index//:rand",
"@crate_index//:regex",
"@crate_index//:rsa",
Expand Down
86 changes: 33 additions & 53 deletions sw/host/opentitanlib/src/crypto/spx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,15 @@ use std::io::{Read, Write};
use std::path::Path;
use std::str::FromStr;

use pqcrypto_sphincsplus::sphincsshake256128ssimple as spx;
use pqcrypto_traits::sign::DetachedSignature;
use pqcrypto_traits::sign::PublicKey;
use pqcrypto_traits::sign::SecretKey;
use sphincsplus::{SPX_PUBLIC_KEY_BYTES, SPX_SECRET_KEY_BYTES, SPX_SIGNATURE_BYTES};

use super::Error;
use crate::util::bigint::fixed_size_bigint;
use crate::util::file::{FromReader, PemSerilizable, ToWriter};

// Signature and key sizes taken from Table 8 on page 57 of the SPHINCS+ Round 3 Specification:
// https://sphincs.org/data/sphincs+-round3-specification.pdf
const PUBLIC_KEY_BYTE_LEN: usize = 32;
const SECRET_KEY_BYTE_LEN: usize = 64;
const SIGNATURE_BIT_LEN: usize = 7856 * 8;
fixed_size_bigint!(Signature, at_most SIGNATURE_BIT_LEN);

/// Trait for implementing public key operations.
pub trait SpxPublicKeyPart {
/// Returns the public key component.
fn pk(&self) -> &spx::PublicKey;
fn pk(&self) -> &sphincsplus::SpxPublicKey;

fn pk_as_bytes(&self) -> &[u8] {
self.pk().as_bytes()
Expand All @@ -40,12 +29,7 @@ pub trait SpxPublicKeyPart {

/// Verify a message signature, returning Ok(()) if the signature matches.
fn verify(&self, message: &[u8], sig: &SpxSignature) -> Result<()> {
spx::verify_detached_signature(
&spx::DetachedSignature::from_bytes(&sig.0.to_le_bytes())?,
message,
self.pk(),
)?;
Ok(())
Ok(sphincsplus::spx_verify(self.pk(), &sig.0, message)?)
}
}

Expand All @@ -56,7 +40,7 @@ pub enum SpxKey {
}

impl SpxPublicKeyPart for SpxKey {
fn pk(&self) -> &spx::PublicKey {
fn pk(&self) -> &sphincsplus::SpxPublicKey {
match self {
SpxKey::Public(k) => k.pk(),
SpxKey::Private(k) => k.pk(),
Expand All @@ -81,21 +65,20 @@ pub fn load_spx_key(key_file: &Path) -> Result<SpxKey> {
/// A SPHINCS+ keypair consisting of the public and secret keys.
#[derive(Clone)]
pub struct SpxKeypair {
pk: spx::PublicKey,
sk: spx::SecretKey,
pk: sphincsplus::SpxPublicKey,
sk: sphincsplus::SpxSecretKey,
}

impl SpxKeypair {
/// Generates a new SPHINCS+ keypair.
pub fn generate() -> Self {
let (pk, sk) = spx::keypair();
let (pk, sk) = sphincsplus::spx_keypair_generate().unwrap();
SpxKeypair { pk, sk }
}

/// Sign `message` using the secret key.
pub fn sign(&self, message: &[u8]) -> SpxSignature {
let sm = spx::detached_sign(message, &self.sk);
SpxSignature(Signature::from_le_bytes(sm.as_bytes()).unwrap())
SpxSignature(sphincsplus::spx_sign(&self.sk, message).unwrap())
}

/// Consumes this keypair and returns the corrisponding public key.
Expand All @@ -105,7 +88,7 @@ impl SpxKeypair {
}

impl SpxPublicKeyPart for SpxKeypair {
fn pk(&self) -> &spx::PublicKey {
fn pk(&self) -> &sphincsplus::SpxPublicKey {
&self.pk
}
}
Expand All @@ -124,12 +107,13 @@ impl FromReader for SpxKeypair {
fn from_reader(mut r: impl Read) -> Result<Self> {
// Read in the buffer as a fixed length byte-string consisting of the public key
// concatenated with the secret key.
let mut buf = [0u8; PUBLIC_KEY_BYTE_LEN + SECRET_KEY_BYTE_LEN];
r.read_exact(&mut buf)?;
Ok(SpxKeypair {
pk: spx::PublicKey::from_bytes(&buf[..PUBLIC_KEY_BYTE_LEN])?,
sk: spx::SecretKey::from_bytes(&buf[PUBLIC_KEY_BYTE_LEN..])?,
})
let mut pk_buf = [0u8; SPX_PUBLIC_KEY_BYTES];
r.read_exact(&mut pk_buf)?;
let pk = sphincsplus::SpxPublicKey::try_from(pk_buf)?;
let mut sk_buf = [0u8; SPX_SECRET_KEY_BYTES];
r.read_exact(&mut sk_buf)?;
let sk = sphincsplus::SpxSecretKey::try_from(sk_buf)?;
Ok(SpxKeypair { pk, sk })
}
}

Expand All @@ -141,16 +125,10 @@ impl PemSerilizable for SpxKeypair {

/// Wrapper for a SPHINCS+ public key.
#[derive(Clone)]
pub struct SpxPublicKey(spx::PublicKey);

impl SpxPublicKey {
pub fn from_bytes(b: &[u8]) -> Result<Self> {
Ok(SpxPublicKey(spx::PublicKey::from_bytes(b)?))
}
}
pub struct SpxPublicKey(sphincsplus::SpxPublicKey);

impl SpxPublicKeyPart for SpxPublicKey {
fn pk(&self) -> &spx::PublicKey {
fn pk(&self) -> &sphincsplus::SpxPublicKey {
&self.0
}
}
Expand All @@ -164,9 +142,10 @@ impl ToWriter for SpxPublicKey {

impl FromReader for SpxPublicKey {
fn from_reader(mut r: impl Read) -> Result<Self> {
let mut buf = [0u8; PUBLIC_KEY_BYTE_LEN];
r.read_exact(&mut buf)?;
Ok(SpxPublicKey(spx::PublicKey::from_bytes(&buf)?))
let mut pk_buf = [0u8; SPX_PUBLIC_KEY_BYTES];
r.read_exact(&mut pk_buf)?;
let pk = sphincsplus::SpxPublicKey::try_from(pk_buf)?;
Ok(SpxPublicKey(pk))
}
}

Expand All @@ -178,26 +157,27 @@ impl PemSerilizable for SpxPublicKey {

/// Wrapper for a SPHINCS+ signature.
#[derive(Clone)]
pub struct SpxSignature(pub Signature);
pub struct SpxSignature(sphincsplus::SpxSignature);

impl ToWriter for SpxSignature {
fn to_writer(&self, w: &mut impl Write) -> Result<()> {
w.write_all(&self.0.to_le_bytes())?;
w.write_all(self.0.as_bytes())?;
Ok(())
}
}

impl FromReader for SpxSignature {
fn from_reader(mut r: impl Read) -> Result<Self> {
let mut buf = [0u8; SIGNATURE_BIT_LEN / 8];
let len = r.read(&mut buf)?;
Ok(SpxSignature(Signature::from_le_bytes(&buf[..len])?))
let mut sig_buf = [0u8; SPX_SIGNATURE_BYTES];
r.read_exact(&mut sig_buf)?;
let sig = sphincsplus::SpxSignature::try_from(sig_buf)?;
Ok(SpxSignature(sig))
}
}

impl ToString for SpxSignature {
fn to_string(&self) -> String {
self.0.to_string()
impl SpxSignature {
pub fn sig_as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
}

Expand All @@ -214,9 +194,9 @@ impl Default for SpxRawPublicKey {
}
}

impl TryFrom<&spx::PublicKey> for SpxRawPublicKey {
impl TryFrom<&sphincsplus::SpxPublicKey> for SpxRawPublicKey {
type Error = Error;
fn try_from(v: &spx::PublicKey) -> Result<Self, Self::Error> {
fn try_from(v: &sphincsplus::SpxPublicKey) -> Result<Self, Self::Error> {
Ok(Self {
key: v.as_bytes().to_vec(),
})
Expand Down
2 changes: 1 addition & 1 deletion sw/host/opentitanlib/src/image/manifest_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl ManifestExtEntry {
name: MANIFEST_EXT_NAME_SPX_SIGNATURE,
},
signature: SigverifySpxSignature {
data: le_bytes_to_word_arr(&signature.0.to_le_bytes())?,
data: le_bytes_to_word_arr(signature.sig_as_bytes())?,
},
},
)))
Expand Down
14 changes: 8 additions & 6 deletions sw/host/opentitantool/src/command/spx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,19 @@ pub enum SpxKeySubcommands {
Generate(SpxKeyGenerateCommand),
}

#[derive(serde::Serialize)]
#[derive(serde::Serialize, Annotate)]
pub struct SpxSignResult {
pub signature: String,
#[serde(with = "serde_bytes")]
#[annotate(format = hexstr)]
pub signature: Vec<u8>,
}

#[derive(Debug, Args)]
pub struct SpxSignCommand {
/// The filename for the message to sign.
message: PathBuf,

/// The file contianing SPHICS+ keypair.
/// The file containing the SPHINCS+ keypair.
#[arg(value_name = "KEY_FILE")]
keypair: PathBuf,
/// The filename to write the signature to.
Expand All @@ -113,9 +115,9 @@ impl CommandDispatch for SpxSignCommand {
signature.write_to_file(output)?;
return Ok(None);
}
Ok(Some(Box::new(SpxSignResult {
signature: signature.to_string(),
})))
let mut sig = Vec::new();
signature.to_writer(&mut sig)?;
Ok(Some(Box::new(SpxSignResult { signature: sig })))
}
}

Expand Down
62 changes: 59 additions & 3 deletions sw/host/sphincsplus/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,62 @@ pub enum SpxError {
BadSignature,
}

impl SpxPublicKey {
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}

impl TryFrom<[u8; SPX_PUBLIC_KEY_BYTES]> for SpxPublicKey {
type Error = SpxError;
#[inline]
fn try_from(buf: [u8; SPX_PUBLIC_KEY_BYTES]) -> Result<Self, SpxError> {
Ok(SpxPublicKey(buf))
}
}

impl SpxSecretKey {
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}

impl TryFrom<[u8; SPX_SECRET_KEY_BYTES]> for SpxSecretKey {
type Error = SpxError;
#[inline]
fn try_from(buf: [u8; SPX_SECRET_KEY_BYTES]) -> Result<Self, SpxError> {
Ok(SpxSecretKey(buf))
}
}

impl SpxSignature {
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}

impl TryFrom<[u8; SPX_SIGNATURE_BYTES]> for SpxSignature {
type Error = SpxError;
#[inline]
fn try_from(buf: [u8; SPX_SIGNATURE_BYTES]) -> Result<Self, SpxError> {
Ok(SpxSignature(buf))
}
}

impl SpxSeed {
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}

impl TryFrom<[u8; SPX_SEED_BYTES]> for SpxSeed {
type Error = SpxError;
#[inline]
fn try_from(buf: [u8; SPX_SEED_BYTES]) -> Result<Self, SpxError> {
Ok(SpxSeed(buf))
}
}

// Generate a new keypair from a seed.
pub fn spx_keypair_from_seed(seed: &SpxSeed) -> Result<(SpxPublicKey, SpxSecretKey), SpxError> {
let mut pk = [0u8; SPX_PUBLIC_KEY_BYTES];
Expand Down Expand Up @@ -68,7 +124,7 @@ pub fn spx_keypair_generate() -> Result<(SpxPublicKey, SpxSecretKey), SpxError>
}

// Generate a detached signature for the message using the secret key.
pub fn spx_sign(sk: &SpxSecretKey, msg: &Vec<u8>) -> Result<SpxSignature, SpxError> {
pub fn spx_sign(sk: &SpxSecretKey, msg: &[u8]) -> Result<SpxSignature, SpxError> {
let mut sig = [0u8; SPX_SIGNATURE_BYTES];
let mut sig_bytes_written = 0;
let err_code =
Expand Down Expand Up @@ -96,7 +152,7 @@ pub fn spx_sign(sk: &SpxSecretKey, msg: &Vec<u8>) -> Result<SpxSignature, SpxErr
}

// Verify a detached signature and return true if the signature is valid.
pub fn spx_verify(pk: &SpxPublicKey, sig: &SpxSignature, msg: &Vec<u8>) -> Result<(), SpxError> {
pub fn spx_verify(pk: &SpxPublicKey, sig: &SpxSignature, msg: &[u8]) -> Result<(), SpxError> {
let err_code =
// SAFETY: the signature and public key buffers here are fixed-length arrays of the size
// expected by the C code, and the message buffer is passed along with its length.
Expand Down Expand Up @@ -153,7 +209,7 @@ mod test {
fn sign_verify_test() {
// Check that a generated signature passes verification.
let (pk, sk) = spx_keypair_generate().unwrap();
let msg: Vec<u8> = vec![255u8; 100];
let msg = [255u8; 100];
let mut sig = spx_sign(&sk, &msg).unwrap();
assert!(spx_verify(&pk, &sig, &msg).is_ok());

Expand Down

0 comments on commit ee2a6ef

Please sign in to comment.