mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-06 13:00:50 +02:00
Merge pull request #9910 from holtrop-wolfssl/rust-lms
Rust wrapper: add lms module
This commit is contained in:
@@ -331,6 +331,12 @@ fn scan_cfg() -> Result<()> {
|
||||
/* mlkem / ML-KEM */
|
||||
check_cfg(&binding, "wc_MlKemKey_Init", "mlkem");
|
||||
|
||||
/* lms / HSS */
|
||||
check_cfg(&binding, "wc_LmsKey_Init", "lms");
|
||||
check_cfg(&binding, "wc_LmsKey_MakeKey", "lms_make_key");
|
||||
check_cfg(&binding, "wc_LmsParm_WC_LMS_PARM_L1_H5_W1", "lms_sha256_256");
|
||||
check_cfg(&binding, "wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H5_W1", "lms_sha256_192");
|
||||
|
||||
/* sha */
|
||||
check_cfg(&binding, "wc_InitSha", "sha");
|
||||
check_cfg(&binding, "wc_InitSha224", "sha224");
|
||||
|
||||
@@ -21,3 +21,4 @@
|
||||
#include "wolfssl/wolfcrypt/pwdbased.h"
|
||||
#include "wolfssl/wolfcrypt/dilithium.h"
|
||||
#include "wolfssl/wolfcrypt/mlkem.h"
|
||||
#include "wolfssl/wolfcrypt/wc_lms.h"
|
||||
|
||||
@@ -37,6 +37,7 @@ pub mod fips;
|
||||
pub mod hkdf;
|
||||
pub mod hmac;
|
||||
pub mod kdf;
|
||||
pub mod lms;
|
||||
pub mod mlkem;
|
||||
pub mod prf;
|
||||
pub mod random;
|
||||
|
||||
@@ -0,0 +1,785 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
/*!
|
||||
This module provides a Rust wrapper for the wolfCrypt library's LMS/HSS
|
||||
(Leighton-Micali Signature / Hierarchical Signature System) post-quantum
|
||||
hash-based signature functionality (RFC 8554).
|
||||
|
||||
The primary component is the [`Lms`] struct, which manages the lifecycle of
|
||||
a wolfSSL `LmsKey` object. It ensures proper initialization and deallocation.
|
||||
|
||||
LMS/HSS signatures are controlled by three parameters:
|
||||
|
||||
| Parameter | Valid Values | Effect |
|
||||
|--------------|-----------------------|---------------------------------|
|
||||
| `levels` | 1..8 | Number of Merkle tree levels |
|
||||
| `height` | 5, 10, 15, 20, 25 | Height of each Merkle tree |
|
||||
| `winternitz` | 1, 2, 4, 8 | Bits per Winternitz chain step |
|
||||
|
||||
The total number of available signatures is `2^(levels * height)`.
|
||||
Larger `winternitz` values reduce signature size at the cost of slower
|
||||
key generation and signing.
|
||||
|
||||
Predefined parameter sets are available as `Lms::PARM_*` constants
|
||||
(e.g., `Lms::PARM_L1_H5_W4`), or parameters can be set directly via
|
||||
[`Lms::set_parameters()`].
|
||||
|
||||
Signing requires private key I/O callbacks to persist the evolving private
|
||||
key state. Register callbacks with [`Lms::set_write_cb()`] and
|
||||
[`Lms::set_read_cb()`] before calling [`Lms::make_key()`] or
|
||||
[`Lms::reload()`]. These methods are absent when wolfCrypt is built with
|
||||
`WOLFSSL_LMS_VERIFY_ONLY`.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust,no_run
|
||||
#[cfg(all(lms, lms_make_key, random))]
|
||||
{
|
||||
use wolfssl_wolfcrypt::random::RNG;
|
||||
use wolfssl_wolfcrypt::lms::Lms;
|
||||
use wolfssl_wolfcrypt::sys;
|
||||
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
let mut key = Lms::new().expect("Lms::new() failed");
|
||||
|
||||
// Use a small, fast parameter set for demonstration.
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("set_parm failed");
|
||||
|
||||
// The private key I/O callbacks must be registered before making a key.
|
||||
// (Omitted here for brevity; see set_write_cb / set_read_cb.)
|
||||
|
||||
let sig_len = key.get_sig_len().expect("get_sig_len failed");
|
||||
let pub_len = key.get_pub_len().expect("get_pub_len failed");
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
let mut pub_buf = vec![0u8; pub_len];
|
||||
|
||||
key.make_key(&mut rng).expect("make_key failed");
|
||||
key.sign(b"hello", &mut sig).expect("sign failed");
|
||||
key.export_pub_raw(&mut pub_buf).expect("export_pub_raw failed");
|
||||
|
||||
let mut vkey = Lms::new().expect("Lms::new() for verify failed");
|
||||
vkey.set_parm(Lms::PARM_L1_H5_W8).expect("set_parm failed");
|
||||
vkey.import_pub_raw(&pub_buf).expect("import_pub_raw failed");
|
||||
vkey.verify(&sig, b"hello").expect("verify failed");
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
#![cfg(lms)]
|
||||
|
||||
use crate::sys;
|
||||
use core::mem::MaybeUninit;
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
use crate::random::RNG;
|
||||
|
||||
/// Rust wrapper for a wolfSSL `LmsKey` object (LMS/HSS, RFC 8554).
|
||||
///
|
||||
/// Manages the lifecycle of the underlying key, including initialization and
|
||||
/// deallocation via the [`Drop`] trait.
|
||||
///
|
||||
/// An instance is created with [`Lms::new()`] or [`Lms::new_ex()`].
|
||||
/// Parameters must be set with [`Lms::set_parm()`] or
|
||||
/// [`Lms::set_parameters()`] before generating or importing a key.
|
||||
///
|
||||
/// When `WOLFSSL_LMS_VERIFY_ONLY` is **not** set, private-key callbacks must
|
||||
/// be registered (see [`Lms::set_write_cb()`] and [`Lms::set_read_cb()`])
|
||||
/// before calling [`Lms::make_key()`] or [`Lms::reload()`].
|
||||
pub struct Lms {
|
||||
ws_key: sys::LmsKey,
|
||||
}
|
||||
|
||||
#[cfg(lms_sha256_256)]
|
||||
impl Lms {
|
||||
pub const PARM_NONE: u32 = sys::wc_LmsParm_WC_LMS_PARM_NONE;
|
||||
pub const PARM_L1_H5_W1: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H5_W1;
|
||||
pub const PARM_L1_H5_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H5_W2;
|
||||
pub const PARM_L1_H5_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H5_W4;
|
||||
pub const PARM_L1_H5_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H5_W8;
|
||||
pub const PARM_L1_H10_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H10_W2;
|
||||
pub const PARM_L1_H10_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H10_W4;
|
||||
pub const PARM_L1_H10_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H10_W8;
|
||||
pub const PARM_L1_H15_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H15_W2;
|
||||
pub const PARM_L1_H15_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H15_W4;
|
||||
pub const PARM_L1_H15_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H15_W8;
|
||||
pub const PARM_L1_H20_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H20_W2;
|
||||
pub const PARM_L1_H20_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H20_W4;
|
||||
pub const PARM_L1_H20_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L1_H20_W8;
|
||||
pub const PARM_L2_H5_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H5_W2;
|
||||
pub const PARM_L2_H5_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H5_W4;
|
||||
pub const PARM_L2_H5_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H5_W8;
|
||||
pub const PARM_L2_H10_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H10_W2;
|
||||
pub const PARM_L2_H10_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H10_W4;
|
||||
pub const PARM_L2_H10_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H10_W8;
|
||||
pub const PARM_L2_H15_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H15_W2;
|
||||
pub const PARM_L2_H15_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H15_W4;
|
||||
pub const PARM_L2_H15_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H15_W8;
|
||||
pub const PARM_L2_H20_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H20_W2;
|
||||
pub const PARM_L2_H20_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H20_W4;
|
||||
pub const PARM_L2_H20_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L2_H20_W8;
|
||||
pub const PARM_L3_H5_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L3_H5_W2;
|
||||
pub const PARM_L3_H5_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L3_H5_W4;
|
||||
pub const PARM_L3_H5_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L3_H5_W8;
|
||||
pub const PARM_L3_H10_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L3_H10_W4;
|
||||
pub const PARM_L3_H10_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L3_H10_W8;
|
||||
pub const PARM_L4_H5_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_L4_H5_W2;
|
||||
pub const PARM_L4_H5_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L4_H5_W4;
|
||||
pub const PARM_L4_H5_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L4_H5_W8;
|
||||
pub const PARM_L4_H10_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_L4_H10_W4;
|
||||
pub const PARM_L4_H10_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_L4_H10_W8;
|
||||
}
|
||||
|
||||
#[cfg(lms_sha256_192)]
|
||||
impl Lms {
|
||||
pub const PARM_SHA256_192_L1_H5_W1 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H5_W1;
|
||||
pub const PARM_SHA256_192_L1_H5_W2 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H5_W2;
|
||||
pub const PARM_SHA256_192_L1_H5_W4 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H5_W4;
|
||||
pub const PARM_SHA256_192_L1_H5_W8 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H5_W8;
|
||||
pub const PARM_SHA256_192_L1_H10_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H10_W2;
|
||||
pub const PARM_SHA256_192_L1_H10_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H10_W4;
|
||||
pub const PARM_SHA256_192_L1_H10_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H10_W8;
|
||||
pub const PARM_SHA256_192_L1_H15_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H15_W2;
|
||||
pub const PARM_SHA256_192_L1_H15_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H15_W4;
|
||||
pub const PARM_SHA256_192_L1_H20_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H20_W2;
|
||||
pub const PARM_SHA256_192_L1_H20_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H20_W4;
|
||||
pub const PARM_SHA256_192_L1_H20_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L1_H20_W8;
|
||||
pub const PARM_SHA256_192_L2_H10_W2: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L2_H10_W2;
|
||||
pub const PARM_SHA256_192_L2_H10_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L2_H10_W4;
|
||||
pub const PARM_SHA256_192_L2_H10_W8: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L2_H10_W8;
|
||||
pub const PARM_SHA256_192_L3_H5_W2 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L3_H5_W2;
|
||||
pub const PARM_SHA256_192_L3_H5_W4 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L3_H5_W4;
|
||||
pub const PARM_SHA256_192_L3_H5_W8 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L3_H5_W8;
|
||||
pub const PARM_SHA256_192_L3_H10_W4: u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L3_H10_W4;
|
||||
pub const PARM_SHA256_192_L4_H5_W8 : u32 = sys::wc_LmsParm_WC_LMS_PARM_SHA256_192_L4_H5_W8;
|
||||
}
|
||||
|
||||
impl Lms {
|
||||
/// Length of the LMS Key ID (`WC_LMS_I_LEN` = 16 bytes).
|
||||
pub const KEY_ID_LEN: usize = sys::WC_LMS_I_LEN as usize;
|
||||
|
||||
/// Create and initialize a new `Lms` key instance.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(Lms) containing the key instance or Err(e)
|
||||
/// containing the wolfSSL library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(lms)]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let key = Lms::new().expect("Error with Lms::new()");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new() -> Result<Self, i32> {
|
||||
Self::new_ex(None, None)
|
||||
}
|
||||
|
||||
/// Create and initialize a new `Lms` key instance with optional heap hint
|
||||
/// and device ID.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `heap`: Optional heap hint.
|
||||
/// * `dev_id`: Optional device ID for crypto callbacks or async hardware.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(Lms) containing the key instance or Err(e)
|
||||
/// containing the wolfSSL library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(lms)]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let key = Lms::new_ex(None, None).expect("Error with Lms::new_ex()");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new_ex(
|
||||
heap: Option<*mut core::ffi::c_void>,
|
||||
dev_id: Option<i32>,
|
||||
) -> Result<Self, i32> {
|
||||
let heap = match heap {
|
||||
Some(h) => h,
|
||||
None => core::ptr::null_mut(),
|
||||
};
|
||||
let dev_id = match dev_id {
|
||||
Some(id) => id,
|
||||
None => sys::INVALID_DEVID,
|
||||
};
|
||||
let mut ws_key: MaybeUninit<sys::LmsKey> = MaybeUninit::uninit();
|
||||
let rc = unsafe { sys::wc_LmsKey_Init(ws_key.as_mut_ptr(), heap, dev_id) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
let ws_key = unsafe { ws_key.assume_init() };
|
||||
let lms = Lms { ws_key };
|
||||
Ok(lms)
|
||||
}
|
||||
|
||||
/// Set parameters using a predefined `wc_LmsParm` parameter set.
|
||||
///
|
||||
/// Use `Lms::PARM_*` constants for the `parm` value (e.g.,
|
||||
/// `Lms::PARM_L1_H5_W8`).
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `parm`: A `Lms::PARM_*` value identifying the parameter set.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(lms)]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn set_parm(&mut self, parm: u32) -> Result<(), i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_SetLmsParm(&mut self.ws_key, parm) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set LMS/HSS parameters directly by levels, height, and Winternitz factor.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `levels`: Number of Merkle tree levels (1..8).
|
||||
/// * `height`: Height of each Merkle tree (5, 10, 15, 20, or 25).
|
||||
/// * `winternitz`: Winternitz factor (1, 2, 4, or 8).
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(lms)]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parameters(1, 5, 8).expect("Error with set_parameters()");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn set_parameters(&mut self, levels: i32, height: i32, winternitz: i32) -> Result<(), i32> {
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_SetParameters(&mut self.ws_key, levels, height, winternitz)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the current LMS/HSS parameter values.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok((levels, height, winternitz)) on success or Err(e)
|
||||
/// containing the wolfSSL library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(lms)]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parameters(1, 5, 8).expect("Error with set_parameters()");
|
||||
/// let (levels, height, winternitz) = key.get_parameters()
|
||||
/// .expect("Error with get_parameters()");
|
||||
/// assert_eq!((levels, height, winternitz), (1, 5, 8));
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_parameters(&self) -> Result<(i32, i32, i32), i32> {
|
||||
let mut levels = 0i32;
|
||||
let mut height = 0i32;
|
||||
let mut winternitz = 0i32;
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_GetParameters(
|
||||
&self.ws_key,
|
||||
&mut levels,
|
||||
&mut height,
|
||||
&mut winternitz,
|
||||
)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok((levels, height, winternitz))
|
||||
}
|
||||
|
||||
/// Register a callback to write (persist) the private key.
|
||||
///
|
||||
/// The callback is called by [`Lms::make_key()`] and [`Lms::sign()`]
|
||||
/// whenever the private key state must be saved to non-volatile storage.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `write_cb`: Callback function of type
|
||||
/// `wc_lms_write_private_key_cb`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
#[cfg(lms_make_key)]
|
||||
pub fn set_write_cb(&mut self, write_cb: sys::wc_lms_write_private_key_cb) -> Result<(), i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_SetWriteCb(&mut self.ws_key, write_cb) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register a callback to read (restore) the private key.
|
||||
///
|
||||
/// The callback is called by [`Lms::reload()`] and [`Lms::sign()`]
|
||||
/// whenever the private key must be read from non-volatile storage.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `read_cb`: Callback function of type `wc_lms_read_private_key_cb`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
#[cfg(lms_make_key)]
|
||||
pub fn set_read_cb(&mut self, read_cb: sys::wc_lms_read_private_key_cb) -> Result<(), i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_SetReadCb(&mut self.ws_key, read_cb) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the context pointer passed to the private key I/O callbacks.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `context`: An arbitrary pointer passed unchanged to the write and
|
||||
/// read callbacks.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure `context` remains valid for as long as the key
|
||||
/// may invoke the registered write or read callbacks (i.e., across calls
|
||||
/// to [`Lms::make_key()`], [`Lms::reload()`], and [`Lms::sign()`]).
|
||||
#[cfg(lms_make_key)]
|
||||
pub unsafe fn set_context(&mut self, context: *mut core::ffi::c_void) -> Result<(), i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_SetContext(&mut self.ws_key, context) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate a new LMS/HSS key pair using an RNG.
|
||||
///
|
||||
/// Parameters must be set with [`Lms::set_parm()`] or
|
||||
/// [`Lms::set_parameters()`] and private key callbacks must be
|
||||
/// registered with [`Lms::set_write_cb()`] and [`Lms::set_read_cb()`]
|
||||
/// before calling this function.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `rng`: `RNG` instance to use for random number generation.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(all(lms, lms_make_key, random))]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::random::RNG;
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut rng = RNG::new().expect("Error creating RNG");
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
/// // Register write/read callbacks before calling make_key().
|
||||
/// // key.make_key(&mut rng).expect("Error with make_key()");
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
pub fn make_key(&mut self, rng: &mut RNG) -> Result<(), i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_MakeKey(&mut self.ws_key, &mut rng.wc_rng) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reload a previously generated LMS/HSS key from non-volatile storage.
|
||||
///
|
||||
/// The read callback registered with [`Lms::set_read_cb()`] is called
|
||||
/// to restore the private key state. Parameters must have been set and
|
||||
/// the read callback registered before calling this function.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
#[cfg(lms_make_key)]
|
||||
pub fn reload(&mut self) -> Result<(), i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_Reload(&mut self.ws_key) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the encoded private key length in bytes.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(length) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
#[cfg(lms_make_key)]
|
||||
pub fn get_priv_len(&self) -> Result<usize, i32> {
|
||||
let mut len = 0u32;
|
||||
let rc = unsafe { sys::wc_LmsKey_GetPrivLen(&self.ws_key, &mut len) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(len as usize)
|
||||
}
|
||||
|
||||
/// Sign a message with this LMS/HSS private key.
|
||||
///
|
||||
/// The `sig` buffer must be at least `get_sig_len()` bytes. The write
|
||||
/// callback is invoked to persist the updated private key state after
|
||||
/// signing.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `msg`: Message bytes to sign.
|
||||
/// * `sig`: Output buffer for the signature. Must be at least
|
||||
/// `get_sig_len()` bytes.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(sig_len) containing the number of bytes written to
|
||||
/// `sig` on success, or Err(e) containing the wolfSSL library error code.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(all(lms, lms_make_key, random))]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::random::RNG;
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut rng = RNG::new().expect("Error creating RNG");
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
/// // Register callbacks and make key first (omitted for brevity).
|
||||
/// // let sig_len = key.get_sig_len().unwrap();
|
||||
/// // let mut sig = vec![0u8; sig_len];
|
||||
/// // let written = key.sign(b"hello", &mut sig).expect("Error with sign()");
|
||||
/// // assert_eq!(written, sig_len);
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(lms_make_key)]
|
||||
pub fn sign(&mut self, msg: &[u8], sig: &mut [u8]) -> Result<usize, i32> {
|
||||
// wc_LmsKey_Sign treats sigSz as write-only: it writes sig_len bytes
|
||||
// to sig without reading or checking *sigSz beforehand. Validate here.
|
||||
let expected_sig_len = self.get_sig_len()?;
|
||||
if sig.len() < expected_sig_len {
|
||||
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
|
||||
}
|
||||
let mut sig_sz = sig.len() as u32;
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_Sign(
|
||||
&mut self.ws_key,
|
||||
sig.as_mut_ptr(),
|
||||
&mut sig_sz,
|
||||
msg.as_ptr(),
|
||||
msg.len() as core::ffi::c_int,
|
||||
)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(sig_sz as usize)
|
||||
}
|
||||
|
||||
/// Return the number of signatures remaining for this key.
|
||||
///
|
||||
/// Returns `Ok(true)` if at least one signature remains, `Ok(false)` if
|
||||
/// exhausted, or `Err(e)` on error. This is a conservative check only.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(count) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
#[cfg(lms_make_key)]
|
||||
pub fn sigs_left(&mut self) -> Result<bool, i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_SigsLeft(&mut self.ws_key) };
|
||||
if rc < 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(rc != 0)
|
||||
}
|
||||
|
||||
/// Get the signature length in bytes for this key's parameters.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(length) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(lms)]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
/// let sig_len = key.get_sig_len().expect("Error with get_sig_len()");
|
||||
/// assert!(sig_len > 0);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_sig_len(&self) -> Result<usize, i32> {
|
||||
let mut len = 0u32;
|
||||
let rc = unsafe { sys::wc_LmsKey_GetSigLen(&self.ws_key, &mut len) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(len as usize)
|
||||
}
|
||||
|
||||
/// Get the public key length in bytes for this key's parameters.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(length) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(lms)]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
/// let pub_len = key.get_pub_len().expect("Error with get_pub_len()");
|
||||
/// assert!(pub_len > 0);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_pub_len(&self) -> Result<usize, i32> {
|
||||
let mut len = 0u32;
|
||||
let rc = unsafe { sys::wc_LmsKey_GetPubLen(&self.ws_key, &mut len) };
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(len as usize)
|
||||
}
|
||||
|
||||
/// Copy the public key from `src` into this key instance.
|
||||
///
|
||||
/// Both keys must have matching parameters. After a successful call,
|
||||
/// this key can be used for verification.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `src`: Source key to copy the public portion from.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
pub fn export_pub_from(&mut self, src: &Lms) -> Result<(), i32> {
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_ExportPub(&mut self.ws_key, &src.ws_key)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Export the raw public key bytes into `out`.
|
||||
///
|
||||
/// The `out` buffer must be at least `get_pub_len()` bytes.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `out`: Output buffer for the raw public key. Must be at least
|
||||
/// `get_pub_len()` bytes.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(length) containing the number of bytes written on
|
||||
/// success, or Err(e) containing the wolfSSL library error code.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(all(lms, lms_make_key, random))]
|
||||
/// {
|
||||
/// use wolfssl_wolfcrypt::lms::Lms;
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
/// // After make_key():
|
||||
/// // let pub_len = key.get_pub_len().unwrap();
|
||||
/// // let mut pub_buf = vec![0u8; pub_len];
|
||||
/// // let written = key.export_pub_raw(&mut pub_buf).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn export_pub_raw(&self, out: &mut [u8]) -> Result<usize, i32> {
|
||||
let mut out_len = out.len() as u32;
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_ExportPubRaw(&self.ws_key, out.as_mut_ptr(), &mut out_len)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(out_len as usize)
|
||||
}
|
||||
|
||||
/// Import a raw public key from `data` into this key instance.
|
||||
///
|
||||
/// Parameters **must** be set with [`Lms::set_parm()`] or
|
||||
/// [`Lms::set_parameters()`] before calling this function, and the
|
||||
/// length of `data` must match `get_pub_len()`. After a successful
|
||||
/// import the key can be used for verification.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `data`: Buffer containing the raw public key bytes.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
|
||||
/// library error code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
/// key.set_parm(Lms::PARM_L1_H5_W8).expect("set_parm failed");
|
||||
/// key.import_pub_raw(&pub_buf).expect("Error with import_pub_raw()");
|
||||
/// ```
|
||||
pub fn import_pub_raw(&mut self, data: &[u8]) -> Result<(), i32> {
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_ImportPubRaw(&mut self.ws_key, data.as_ptr(), data.len() as u32)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify an LMS/HSS signature over a message.
|
||||
///
|
||||
/// The key must have a public key loaded (via [`Lms::make_key()`],
|
||||
/// [`Lms::reload()`], or [`Lms::import_pub_raw()`]).
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `sig`: Signature bytes to verify.
|
||||
/// * `msg`: Message bytes that were signed.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(()) if the signature is valid, or Err(e) containing
|
||||
/// the wolfSSL library error code value (including a verification failure
|
||||
/// code).
|
||||
pub fn verify(&mut self, sig: &[u8], msg: &[u8]) -> Result<(), i32> {
|
||||
// wc_lms.c validates sigSz, but ext_lms.c passes sigSz through to
|
||||
// hss_validate_signature without checking it. Validate here for both.
|
||||
let expected_sig_len = self.get_sig_len()?;
|
||||
if sig.len() != expected_sig_len {
|
||||
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
|
||||
}
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_Verify(
|
||||
&mut self.ws_key,
|
||||
sig.as_ptr(),
|
||||
sig.len() as u32,
|
||||
msg.as_ptr(),
|
||||
msg.len() as core::ffi::c_int,
|
||||
)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the Key ID (I value) for this LMS/HSS key.
|
||||
///
|
||||
/// Returns a slice pointing into the key's internal storage.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(&[u8]) containing the key ID on success, or Err(e)
|
||||
/// containing the wolfSSL library error code value.
|
||||
pub fn get_kid(&mut self) -> Result<&[u8], i32> {
|
||||
let mut kid_ptr: *const u8 = core::ptr::null();
|
||||
let mut kid_sz: u32 = 0;
|
||||
let rc = unsafe {
|
||||
sys::wc_LmsKey_GetKid(&mut self.ws_key, &mut kid_ptr, &mut kid_sz)
|
||||
};
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
let slice = unsafe { core::slice::from_raw_parts(kid_ptr, kid_sz as usize) };
|
||||
Ok(slice)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Lms {
|
||||
/// Safely free the underlying wolfSSL LMS/HSS key context.
|
||||
///
|
||||
/// This calls `wc_LmsKey_Free()`. The Rust Drop trait guarantees this
|
||||
/// is called when the `Lms` struct goes out of scope.
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
sys::wc_LmsKey_Free(&mut self.ws_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
#![cfg(lms)]
|
||||
|
||||
mod common;
|
||||
|
||||
use wolfssl_wolfcrypt::lms::Lms;
|
||||
use wolfssl_wolfcrypt::sys;
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
use wolfssl_wolfcrypt::random::RNG;
|
||||
|
||||
/// Private key NV storage for tests that require make_key / sign / reload.
|
||||
///
|
||||
/// The write and read callbacks use a raw pointer to this struct as the
|
||||
/// context argument. Each test that needs persistent NV storage creates its
|
||||
/// own instance and boxes it to keep it alive for the duration of the test.
|
||||
#[cfg(lms_make_key)]
|
||||
struct KeyStore {
|
||||
buf: [u8; 16384],
|
||||
}
|
||||
|
||||
#[cfg(lms_make_key)]
|
||||
unsafe extern "C" fn write_key_cb(
|
||||
priv_data: *const u8,
|
||||
priv_sz: u32,
|
||||
ctx: *mut core::ffi::c_void,
|
||||
) -> i32 {
|
||||
let store = unsafe { &mut *(ctx as *mut KeyStore) };
|
||||
let priv_sz = priv_sz as usize;
|
||||
store.buf[..priv_sz]
|
||||
.copy_from_slice(unsafe { core::slice::from_raw_parts(priv_data, priv_sz) });
|
||||
sys::wc_LmsRc_WC_LMS_RC_SAVED_TO_NV_MEMORY as i32
|
||||
}
|
||||
|
||||
#[cfg(lms_make_key)]
|
||||
unsafe extern "C" fn read_key_cb(
|
||||
priv_data: *mut u8,
|
||||
priv_sz: u32,
|
||||
ctx: *mut core::ffi::c_void,
|
||||
) -> i32 {
|
||||
let store = unsafe { &*(ctx as *mut KeyStore) };
|
||||
let priv_sz = priv_sz as usize;
|
||||
unsafe { core::slice::from_raw_parts_mut(priv_data, priv_sz) }
|
||||
.copy_from_slice(&store.buf[..priv_sz]);
|
||||
sys::wc_LmsRc_WC_LMS_RC_READ_TO_MEMORY as i32
|
||||
}
|
||||
|
||||
/// Register the write and read callbacks and a context pointer on `key`.
|
||||
#[cfg(lms_make_key)]
|
||||
fn setup_callbacks(key: &mut Lms, ctx: *mut core::ffi::c_void) {
|
||||
key.set_write_cb(Some(write_key_cb)).expect("Error with set_write_cb()");
|
||||
key.set_read_cb(Some(read_key_cb)).expect("Error with set_read_cb()");
|
||||
// Safety: ctx points to a BoxedKeyStore that outlives all callback invocations.
|
||||
unsafe { key.set_context(ctx).expect("Error with set_context()") };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constant and construction tests (no keygen required)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Verify the KEY_ID_LEN constant matches the C `WC_LMS_I_LEN` value.
|
||||
#[test]
|
||||
fn test_key_id_len_constant() {
|
||||
assert_eq!(Lms::KEY_ID_LEN, 16);
|
||||
}
|
||||
|
||||
/// Verify that `new()` succeeds.
|
||||
#[test]
|
||||
fn test_new() {
|
||||
common::setup();
|
||||
Lms::new().expect("Error with Lms::new()");
|
||||
}
|
||||
|
||||
/// Verify that `new_ex()` accepts the optional heap and device ID parameters.
|
||||
#[test]
|
||||
fn test_new_ex() {
|
||||
common::setup();
|
||||
Lms::new_ex(None, None).expect("Error with Lms::new_ex()");
|
||||
}
|
||||
|
||||
/// Verify that `set_parm()` accepts a predefined wc_LmsParm value.
|
||||
#[test]
|
||||
fn test_set_parm() {
|
||||
common::setup();
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
}
|
||||
|
||||
/// Verify that `set_parameters()` accepts explicit L/H/W values and that
|
||||
/// `get_parameters()` returns them unchanged.
|
||||
#[test]
|
||||
fn test_set_get_parameters() {
|
||||
common::setup();
|
||||
|
||||
for &(levels, height, winternitz) in &[(1, 5, 8), (1, 10, 4), (2, 5, 8)] {
|
||||
// Use a fresh key for each parameter set; wc_LmsKey_SetParameters does
|
||||
// not allow re-setting parameters on an already-configured key.
|
||||
let mut k = Lms::new().expect("Error with Lms::new()");
|
||||
k.set_parameters(levels, height, winternitz)
|
||||
.expect("Error with set_parameters()");
|
||||
let (l, h, w) = k.get_parameters().expect("Error with get_parameters()");
|
||||
assert_eq!(l, levels, "levels mismatch for ({},{},{})", levels, height, winternitz);
|
||||
assert_eq!(h, height, "height mismatch for ({},{},{})", levels, height, winternitz);
|
||||
assert_eq!(w, winternitz, "winternitz mismatch for ({},{},{})", levels, height, winternitz);
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify that `get_sig_len()` and `get_pub_len()` return positive values
|
||||
/// after setting a predefined parameter set.
|
||||
#[test]
|
||||
fn test_size_queries_after_set_parm() {
|
||||
common::setup();
|
||||
for &parm in &[
|
||||
Lms::PARM_L1_H5_W8,
|
||||
Lms::PARM_L1_H5_W4,
|
||||
Lms::PARM_L1_H10_W8,
|
||||
] {
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(parm).expect("Error with set_parm()");
|
||||
let sig_len = key.get_sig_len().expect("Error with get_sig_len()");
|
||||
let pub_len = key.get_pub_len().expect("Error with get_pub_len()");
|
||||
assert!(sig_len > 0, "sig_len must be positive for parm {}", parm);
|
||||
assert!(pub_len > 0, "pub_len must be positive for parm {}", parm);
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify that `get_sig_len()` grows with the number of levels (higher
|
||||
/// level count increases signature size significantly).
|
||||
#[test]
|
||||
fn test_sig_len_increases_with_levels() {
|
||||
common::setup();
|
||||
let mut key1 = Lms::new().expect("Error with Lms::new()");
|
||||
key1.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
let sig_len_l1 = key1.get_sig_len().expect("Error with get_sig_len() L1");
|
||||
|
||||
let mut key2 = Lms::new().expect("Error with Lms::new()");
|
||||
key2.set_parm(Lms::PARM_L2_H5_W8).expect("Error with set_parm()");
|
||||
let sig_len_l2 = key2.get_sig_len().expect("Error with get_sig_len() L2");
|
||||
|
||||
assert!(
|
||||
sig_len_l2 > sig_len_l1,
|
||||
"L2 sig_len ({}) must exceed L1 sig_len ({})",
|
||||
sig_len_l2,
|
||||
sig_len_l1
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private-key-length query (needs lms_make_key for the API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Verify that `get_priv_len()` returns a positive value after parameters
|
||||
/// are set. No key generation is required for this query.
|
||||
#[test]
|
||||
#[cfg(lms_make_key)]
|
||||
fn test_get_priv_len() {
|
||||
common::setup();
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
let priv_len = key.get_priv_len().expect("Error with get_priv_len()");
|
||||
assert!(priv_len > 0, "priv_len must be positive");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Key generation and signing tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Verify that `make_key()` completes without error for the test parameter set.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_make_key() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let _ = store; // keep alive
|
||||
}
|
||||
|
||||
/// Sign a message and verify it with the same key.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_sign_verify() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let message = b"Hello, LMS/HSS!";
|
||||
let sig_len = key.get_sig_len().expect("Error with get_sig_len()");
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
|
||||
let written = key.sign(message, &mut sig).expect("Error with sign()");
|
||||
assert_eq!(written, sig_len, "sign() must fill the entire signature buffer");
|
||||
|
||||
key.verify(&sig, message).expect("Valid signature must verify");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Verify that a signature does not verify for a different message.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_sign_tampered_message() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let message = b"Authentic message";
|
||||
let sig_len = key.get_sig_len().expect("Error with get_sig_len()");
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
key.sign(message, &mut sig).expect("Error with sign()");
|
||||
|
||||
let result = key.verify(&sig, b"Tampered message");
|
||||
assert!(result.is_err(), "Signature must not verify for a different message");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Verify that a tampered signature is rejected.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_sign_tampered_signature() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let message = b"Message under test";
|
||||
let sig_len = key.get_sig_len().expect("Error with get_sig_len()");
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
key.sign(message, &mut sig).expect("Error with sign()");
|
||||
|
||||
// Flip a byte in the signature body.
|
||||
sig[sig_len / 2] ^= 0xFF;
|
||||
|
||||
let result = key.verify(&sig, message);
|
||||
assert!(result.is_err(), "Tampered signature must be rejected");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Export the raw public key, import it into a fresh key, and verify a
|
||||
/// signature produced by the original key.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_export_pub_raw_import_verify() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut sign_key = Lms::new().expect("Error with Lms::new()");
|
||||
sign_key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut sign_key, ctx);
|
||||
sign_key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let pub_len = sign_key.get_pub_len().expect("Error with get_pub_len()");
|
||||
let sig_len = sign_key.get_sig_len().expect("Error with get_sig_len()");
|
||||
|
||||
let mut pub_buf = vec![0u8; pub_len];
|
||||
let written = sign_key.export_pub_raw(&mut pub_buf)
|
||||
.expect("Error with export_pub_raw()");
|
||||
assert_eq!(written, pub_len, "export_pub_raw must fill the entire buffer");
|
||||
|
||||
let message = b"Public key export/import test";
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
sign_key.sign(message, &mut sig).expect("Error with sign()");
|
||||
|
||||
// Import the raw public key into a new key and verify.
|
||||
// wc_LmsKey_ImportPubRaw requires params to be set first.
|
||||
let mut verify_key = Lms::new().expect("Error with Lms::new() for verify");
|
||||
verify_key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm() for verify key");
|
||||
verify_key.import_pub_raw(&pub_buf)
|
||||
.expect("Error with import_pub_raw()");
|
||||
verify_key.verify(&sig, message)
|
||||
.expect("Signature must verify against imported public key");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Verify that `export_pub_from()` copies the public key into a destination
|
||||
/// key and that signatures from the source verify against that destination.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_export_pub_from() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut sign_key = Lms::new().expect("Error with Lms::new()");
|
||||
sign_key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut sign_key, ctx);
|
||||
sign_key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let message = b"export_pub_from test message";
|
||||
let sig_len = sign_key.get_sig_len().expect("Error with get_sig_len()");
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
sign_key.sign(message, &mut sig).expect("Error with sign()");
|
||||
|
||||
// Copy the public portion into a fresh key.
|
||||
let mut verify_key = Lms::new().expect("Error with Lms::new() for verify");
|
||||
verify_key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm() for verify key");
|
||||
verify_key.export_pub_from(&sign_key)
|
||||
.expect("Error with export_pub_from()");
|
||||
|
||||
verify_key.verify(&sig, message)
|
||||
.expect("Signature must verify against export_pub_from() key");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Verify that `sigs_left()` indicates signatures are available immediately
|
||||
/// after `make_key()`.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_sigs_left_after_make_key() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let remaining = key.sigs_left().expect("Error with sigs_left()");
|
||||
assert!(remaining, "sigs_left must be true immediately after make_key()");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Verify that `get_kid()` returns a slice of exactly `KEY_ID_LEN` bytes
|
||||
/// after key generation.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_get_kid() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let kid = key.get_kid().expect("Error with get_kid()");
|
||||
assert_eq!(kid.len(), Lms::KEY_ID_LEN, "kid must be KEY_ID_LEN bytes");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Verify that `reload()` restores a key to a usable signing state and that
|
||||
/// a signature produced by the reloaded key verifies correctly.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_reload() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
// Generate a key pair and export the public key.
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng).expect("Error with make_key()");
|
||||
|
||||
let pub_len = key.get_pub_len().expect("Error with get_pub_len()");
|
||||
let mut pub_buf = vec![0u8; pub_len];
|
||||
key.export_pub_raw(&mut pub_buf).expect("Error with export_pub_raw()");
|
||||
|
||||
// Create a new key, reload from NV storage (written by make_key above).
|
||||
let mut reloaded = Lms::new().expect("Error with Lms::new() for reload");
|
||||
reloaded.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm() for reload");
|
||||
setup_callbacks(&mut reloaded, ctx);
|
||||
reloaded.reload().expect("Error with reload()");
|
||||
|
||||
// Sign with the reloaded key and verify against the exported public key.
|
||||
let message = b"Reload round-trip message";
|
||||
let sig_len = reloaded.get_sig_len().expect("Error with get_sig_len()");
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
reloaded.sign(message, &mut sig).expect("Error with sign() after reload");
|
||||
|
||||
let mut verify_key = Lms::new().expect("Error with Lms::new() for verify");
|
||||
// wc_LmsKey_ImportPubRaw requires params to be set first.
|
||||
verify_key.set_parm(Lms::PARM_L1_H5_W8).expect("Error with set_parm() for verify key");
|
||||
verify_key.import_pub_raw(&pub_buf).expect("Error with import_pub_raw()");
|
||||
verify_key.verify(&sig, message)
|
||||
.expect("Signature from reloaded key must verify");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Sign and verify round-trips across several predefined parameter sets,
|
||||
/// confirming the implementation is parameter-agnostic.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_sign_verify_multiple_parms() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("Error creating RNG");
|
||||
|
||||
for &parm in &[
|
||||
Lms::PARM_L1_H5_W8,
|
||||
Lms::PARM_L1_H5_W4,
|
||||
Lms::PARM_L1_H5_W2,
|
||||
] {
|
||||
let mut store = Box::new(KeyStore { buf: [0u8; 16384] });
|
||||
let ctx = store.as_mut() as *mut KeyStore as *mut core::ffi::c_void;
|
||||
|
||||
let mut key = Lms::new().expect("Error with Lms::new()");
|
||||
key.set_parm(parm).expect("Error with set_parm()");
|
||||
setup_callbacks(&mut key, ctx);
|
||||
key.make_key(&mut rng)
|
||||
.unwrap_or_else(|e| panic!("make_key failed for parm {}: {}", parm, e));
|
||||
|
||||
let message = b"Parameter set round-trip";
|
||||
let sig_len = key.get_sig_len().expect("Error with get_sig_len()");
|
||||
let mut sig = vec![0u8; sig_len];
|
||||
|
||||
key.sign(message, &mut sig)
|
||||
.unwrap_or_else(|e| panic!("sign failed for parm {}: {}", parm, e));
|
||||
key.verify(&sig, message)
|
||||
.unwrap_or_else(|e| panic!("verify failed for parm {}: {}", parm, e));
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user