mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-07 21:50:49 +02:00
Merge pull request #10305 from holtrop-wolfssl/rust-crate-updates
Rust wrapper: add password-hash, kem, mac traits; fix a few Fenrir findings
This commit is contained in:
+71
@@ -22,6 +22,18 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.72.1"
|
||||
@@ -105,6 +117,12 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
@@ -123,6 +141,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"rand_core 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||
dependencies = [
|
||||
"cmov",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -134,6 +162,7 @@ dependencies = [
|
||||
"blobby 0.4.0",
|
||||
"block-buffer",
|
||||
"crypto-common 0.2.1",
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -185,6 +214,16 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kem"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd"
|
||||
dependencies = [
|
||||
"crypto-common 0.2.1",
|
||||
"rand_core 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.175"
|
||||
@@ -229,6 +268,34 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
|
||||
dependencies = [
|
||||
"phc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phc"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
@@ -424,6 +491,10 @@ dependencies = [
|
||||
"bindgen",
|
||||
"cipher",
|
||||
"digest",
|
||||
"hybrid-array",
|
||||
"kem",
|
||||
"num-traits",
|
||||
"password-hash",
|
||||
"rand_core 0.10.0",
|
||||
"regex",
|
||||
"signature",
|
||||
|
||||
@@ -15,8 +15,11 @@ std = []
|
||||
rand_core = ["dep:rand_core"]
|
||||
aead = ["dep:aead"]
|
||||
cipher = ["dep:cipher"]
|
||||
mac = ["digest/mac"]
|
||||
digest = ["dep:digest"]
|
||||
signature = ["dep:signature"]
|
||||
password-hash = ["dep:password-hash", "password-hash/phc"]
|
||||
kem = ["dep:kem", "hybrid-array/extra-sizes"]
|
||||
|
||||
[dependencies]
|
||||
rand_core = { version = "0.10", optional = true, default-features = false }
|
||||
@@ -24,13 +27,19 @@ aead = { version = "0.5", optional = true, default-features = false }
|
||||
cipher = { version = "0.5", optional = true, default-features = false }
|
||||
digest = { version = "0.11", optional = true, default-features = false, features = ["block-api"] }
|
||||
signature = { version = "2.2", optional = true, default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
zeroize = { version = "1.3", default-features = false, features = ["derive"] }
|
||||
password-hash = { version = "0.6.1", optional = true, default-features = false }
|
||||
kem = { version = "0.3", optional = true, default-features = false }
|
||||
hybrid-array = { version = "0.4.7", optional = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
aead = { version = "0.5", features = ["alloc", "dev"] }
|
||||
cipher = "0.5"
|
||||
digest = { version = "0.11", features = ["dev"] }
|
||||
digest = { version = "0.11", features = ["dev", "mac"] }
|
||||
signature = "2.2"
|
||||
password-hash = { version = "0.6.1", features = ["phc"] }
|
||||
kem = "0.3"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FEATURES := rand_core,aead,cipher,digest,signature
|
||||
FEATURES := rand_core,aead,cipher,digest,mac,signature,password-hash,kem
|
||||
CARGO_FEATURE_FLAGS := --features $(FEATURES)
|
||||
|
||||
.PHONY: all
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*!
|
||||
RustCrypto `digest::Mac` trait implementations for the wolfCrypt CMAC types.
|
||||
|
||||
This module provides typed AES-CMAC wrappers with implementations of the
|
||||
traits from the `digest` crate (`MacMarker`, `KeyInit`, `Update`,
|
||||
`FixedOutput`) for each AES key size (128, 192, 256). With these
|
||||
implementations the `digest::Mac` trait becomes available via its blanket
|
||||
implementation, allowing these CMAC types to be used anywhere a RustCrypto
|
||||
`Mac` is accepted.
|
||||
|
||||
Any failure returned by the underlying wolfCrypt call in a trait method will
|
||||
result in a panic, matching the infallible signatures required by the
|
||||
RustCrypto traits.
|
||||
*/
|
||||
|
||||
use digest::consts::{U16, U24, U32};
|
||||
|
||||
macro_rules! impl_cmac_mac {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
$name:ident, key = $key_size:ty
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
pub struct $name {
|
||||
cmac: crate::cmac::CMAC,
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::MacMarker for $name {}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::OutputSizeUser for $name {
|
||||
type OutputSize = U16;
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::common::KeySizeUser for $name {
|
||||
type KeySize = $key_size;
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::KeyInit for $name {
|
||||
fn new(key: &digest::Key<Self>) -> Self {
|
||||
Self {
|
||||
cmac: crate::cmac::CMAC::new(key.as_slice())
|
||||
.expect("wolfCrypt CMAC init failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::Update for $name {
|
||||
fn update(&mut self, data: &[u8]) {
|
||||
crate::cmac::CMAC::update(&mut self.cmac, data)
|
||||
.expect("wolfCrypt CMAC update failed");
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::FixedOutput for $name {
|
||||
fn finalize_into(self, out: &mut digest::Output<Self>) {
|
||||
self.cmac.finalize(out.as_mut_slice())
|
||||
.expect("wolfCrypt CMAC finalize failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_cmac_mac! {
|
||||
CmacAes128, key = U16
|
||||
}
|
||||
|
||||
impl_cmac_mac! {
|
||||
CmacAes192, key = U24
|
||||
}
|
||||
|
||||
impl_cmac_mac! {
|
||||
CmacAes256, key = U32
|
||||
}
|
||||
@@ -1219,6 +1219,9 @@ impl ECC {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn rs_hex_to_sig(r: &[u8], s: &[u8], dout: &mut [u8]) -> Result<usize, i32> {
|
||||
if r.is_empty() || s.is_empty() || r[r.len() - 1] != 0 || s[s.len() - 1] != 0 {
|
||||
return Err(sys::wolfCrypt_ErrorCodes_BAD_FUNC_ARG);
|
||||
}
|
||||
let mut dout_size = crate::buffer_len_to_u32(dout.len())?;
|
||||
let r_ptr = r.as_ptr() as *const core::ffi::c_char;
|
||||
let s_ptr = s.as_ptr() as *const core::ffi::c_char;
|
||||
@@ -1820,7 +1823,7 @@ impl ECC {
|
||||
sys::wc_ecc_shared_secret(&mut self.wc_ecc_key,
|
||||
&mut peer_key.wc_ecc_key, dout.as_mut_ptr(), &mut out_len)
|
||||
};
|
||||
if rc < 0 {
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(out_len as usize)
|
||||
@@ -1961,7 +1964,7 @@ impl ECC {
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(res != 0)
|
||||
Ok(res == 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*!
|
||||
RustCrypto `digest::Mac` trait implementations for the wolfCrypt HMAC types.
|
||||
|
||||
This module provides typed HMAC wrappers with implementations of the traits
|
||||
from the `digest` crate (`MacMarker`, `KeyInit`, `Update`, `FixedOutput`)
|
||||
for each supported hash algorithm. With these implementations the
|
||||
`digest::Mac` trait becomes available via its blanket implementation,
|
||||
allowing these HMAC types to be used anywhere a RustCrypto `Mac` is accepted.
|
||||
|
||||
Any failure returned by the underlying wolfCrypt call in a trait method will
|
||||
result in a panic, matching the infallible signatures required by the
|
||||
RustCrypto traits.
|
||||
*/
|
||||
|
||||
use digest::consts::{
|
||||
U20, U28, U32, U48, U64, U72, U104, U128, U136, U144,
|
||||
};
|
||||
|
||||
macro_rules! impl_hmac_mac {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
$name:ident, hmac_type = $hmac_type:expr, key = $key_size:ty, out = $out_size:ty
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
pub struct $name {
|
||||
hmac: crate::hmac::HMAC,
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::MacMarker for $name {}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::OutputSizeUser for $name {
|
||||
type OutputSize = $out_size;
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::common::KeySizeUser for $name {
|
||||
type KeySize = $key_size;
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::KeyInit for $name {
|
||||
fn new(key: &digest::Key<Self>) -> Self {
|
||||
Self {
|
||||
hmac: crate::hmac::HMAC::new($hmac_type, key.as_slice())
|
||||
.expect("wolfCrypt HMAC init failed"),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_from_slice(key: &[u8]) -> Result<Self, digest::InvalidLength> {
|
||||
crate::hmac::HMAC::new($hmac_type, key)
|
||||
.map(|hmac| Self { hmac })
|
||||
.map_err(|_| digest::InvalidLength)
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::Update for $name {
|
||||
fn update(&mut self, data: &[u8]) {
|
||||
crate::hmac::HMAC::update(&mut self.hmac, data)
|
||||
.expect("wolfCrypt HMAC update failed");
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
impl digest::FixedOutput for $name {
|
||||
fn finalize_into(mut self, out: &mut digest::Output<Self>) {
|
||||
crate::hmac::HMAC::finalize(&mut self.hmac, out.as_mut_slice())
|
||||
.expect("wolfCrypt HMAC finalize failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha)]
|
||||
HmacSha, hmac_type = crate::hmac::HMAC::TYPE_SHA, key = U64, out = U20
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha224)]
|
||||
HmacSha224, hmac_type = crate::hmac::HMAC::TYPE_SHA224, key = U64, out = U28
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha256)]
|
||||
HmacSha256, hmac_type = crate::hmac::HMAC::TYPE_SHA256, key = U64, out = U32
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha384)]
|
||||
HmacSha384, hmac_type = crate::hmac::HMAC::TYPE_SHA384, key = U128, out = U48
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha512)]
|
||||
HmacSha512, hmac_type = crate::hmac::HMAC::TYPE_SHA512, key = U128, out = U64
|
||||
}
|
||||
|
||||
#[cfg(sha512_224)]
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha512_224)]
|
||||
HmacSha512_224, hmac_type = crate::hmac::HMAC::TYPE_SHA512_224, key = U128, out = U28
|
||||
}
|
||||
|
||||
#[cfg(sha512_256)]
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha512_256)]
|
||||
HmacSha512_256, hmac_type = crate::hmac::HMAC::TYPE_SHA512_256, key = U128, out = U32
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha3)]
|
||||
HmacSha3_224, hmac_type = crate::hmac::HMAC::TYPE_SHA3_224, key = U144, out = U28
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha3)]
|
||||
HmacSha3_256, hmac_type = crate::hmac::HMAC::TYPE_SHA3_256, key = U136, out = U32
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha3)]
|
||||
HmacSha3_384, hmac_type = crate::hmac::HMAC::TYPE_SHA3_384, key = U104, out = U48
|
||||
}
|
||||
|
||||
impl_hmac_mac! {
|
||||
#[cfg(sha3)]
|
||||
HmacSha3_512, hmac_type = crate::hmac::HMAC::TYPE_SHA3_512, key = U72, out = U64
|
||||
}
|
||||
@@ -44,6 +44,8 @@ pub mod aes;
|
||||
pub mod blake2;
|
||||
pub mod chacha20_poly1305;
|
||||
pub mod cmac;
|
||||
#[cfg(all(cmac, feature = "mac"))]
|
||||
pub mod cmac_mac;
|
||||
pub mod curve25519;
|
||||
pub mod dh;
|
||||
pub mod dilithium;
|
||||
@@ -55,15 +57,21 @@ pub mod ed448;
|
||||
pub mod fips;
|
||||
pub mod hkdf;
|
||||
pub mod hmac;
|
||||
#[cfg(all(hmac, feature = "mac"))]
|
||||
pub mod hmac_mac;
|
||||
pub mod kdf;
|
||||
pub mod lms;
|
||||
pub mod mlkem;
|
||||
#[cfg(all(feature = "kem", mlkem))]
|
||||
pub mod mlkem_kem;
|
||||
pub mod prf;
|
||||
pub mod random;
|
||||
pub mod rsa;
|
||||
#[cfg(feature = "signature")]
|
||||
pub mod rsa_pkcs1v15;
|
||||
pub mod sha;
|
||||
#[cfg(all(feature = "password-hash", hmac, kdf_pbkdf2))]
|
||||
pub mod pbkdf2_password_hash;
|
||||
#[cfg(feature = "digest")]
|
||||
pub mod sha_digest;
|
||||
|
||||
|
||||
@@ -545,17 +545,17 @@ impl Lms {
|
||||
Ok(sig_sz as usize)
|
||||
}
|
||||
|
||||
/// Return the number of signatures remaining for this key.
|
||||
/// Return whether there are more 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.
|
||||
/// Returns either Ok(true) if any signatures remain, Ok(false) if
|
||||
/// exhausted, or Err(e) containing the wolfSSL library error code value.
|
||||
#[cfg(lms_make_key)]
|
||||
pub fn sigs_left(&mut self) -> Result<bool, i32> {
|
||||
pub fn has_sigs_left(&mut self) -> Result<bool, i32> {
|
||||
let rc = unsafe { sys::wc_LmsKey_SigsLeft(&mut self.ws_key) };
|
||||
if rc < 0 {
|
||||
return Err(rc);
|
||||
|
||||
@@ -637,8 +637,8 @@ impl MlKem {
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(size) containing the number of bytes written or Err(e)
|
||||
/// containing the wolfSSL library error code value.
|
||||
/// Returns either Ok(()) or Err(e) containing the wolfSSL library error
|
||||
/// code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -652,12 +652,10 @@ impl MlKem {
|
||||
/// .expect("Error with generate()");
|
||||
/// let pub_size = key.public_key_size().unwrap();
|
||||
/// let mut pub_buf = vec![0u8; pub_size];
|
||||
/// let written = key.encode_public_key(&mut pub_buf)
|
||||
/// .expect("Error with encode_public_key()");
|
||||
/// assert_eq!(written, pub_size);
|
||||
/// key.encode_public_key(&mut pub_buf).expect("Error with encode_public_key()");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn encode_public_key(&self, out: &mut [u8]) -> Result<usize, i32> {
|
||||
pub fn encode_public_key(&self, out: &mut [u8]) -> Result<(), i32> {
|
||||
let out_size = crate::buffer_len_to_u32(out.len())?;
|
||||
let rc = unsafe {
|
||||
sys::wc_MlKemKey_EncodePublicKey(self.ws_key, out.as_mut_ptr(), out_size)
|
||||
@@ -665,7 +663,7 @@ impl MlKem {
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(out.len())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Encode (export) the private key to a byte buffer.
|
||||
@@ -678,8 +676,8 @@ impl MlKem {
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns either Ok(size) containing the number of bytes written or Err(e)
|
||||
/// containing the wolfSSL library error code value.
|
||||
/// Returns either Ok(()) or Err(e) containing the wolfSSL library error
|
||||
/// code value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -693,12 +691,10 @@ impl MlKem {
|
||||
/// .expect("Error with generate()");
|
||||
/// let priv_size = key.private_key_size().unwrap();
|
||||
/// let mut priv_buf = vec![0u8; priv_size];
|
||||
/// let written = key.encode_private_key(&mut priv_buf)
|
||||
/// .expect("Error with encode_private_key()");
|
||||
/// assert_eq!(written, priv_size);
|
||||
/// key.encode_private_key(&mut priv_buf).expect("Error with encode_private_key()");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn encode_private_key(&self, out: &mut [u8]) -> Result<usize, i32> {
|
||||
pub fn encode_private_key(&self, out: &mut [u8]) -> Result<(), i32> {
|
||||
let out_size = crate::buffer_len_to_u32(out.len())?;
|
||||
let rc = unsafe {
|
||||
sys::wc_MlKemKey_EncodePrivateKey(self.ws_key, out.as_mut_ptr(), out_size)
|
||||
@@ -706,7 +702,7 @@ impl MlKem {
|
||||
if rc != 0 {
|
||||
return Err(rc);
|
||||
}
|
||||
Ok(out.len())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode (import) a public key from a byte buffer.
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*!
|
||||
RustCrypto `kem` trait implementations for the wolfCrypt ML-KEM types.
|
||||
|
||||
Provides [`kem::Kem`] marker types and associated encapsulation/decapsulation
|
||||
key types for ML-KEM-512, ML-KEM-768, and ML-KEM-1024:
|
||||
|
||||
| Marker | Encapsulation key | Decapsulation key |
|
||||
|-----------------|---------------------------------|---------------------------------|
|
||||
| [`MlKem512`] | [`MlKem512EncapsulationKey`] | [`MlKem512DecapsulationKey`] |
|
||||
| [`MlKem768`] | [`MlKem768EncapsulationKey`] | [`MlKem768DecapsulationKey`] |
|
||||
| [`MlKem1024`] | [`MlKem1024EncapsulationKey`] | [`MlKem1024DecapsulationKey`] |
|
||||
|
||||
Each encapsulation key implements [`kem::Encapsulate`] (with
|
||||
[`kem::TryKeyInit`] and [`kem::KeyExport`] for key serialization).
|
||||
|
||||
Each decapsulation key implements [`kem::Decapsulate`] and
|
||||
[`kem::Generate`] (for key generation from a [`rand_core::CryptoRng`]).
|
||||
|
||||
Key generation and encapsulation bridge a caller-supplied
|
||||
[`rand_core::CryptoRng`] to wolfCrypt's deterministic APIs by extracting the
|
||||
required random bytes from the RNG.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
#[cfg(all(mlkem, random, feature = "kem", feature = "rand_core"))]
|
||||
{
|
||||
use kem::{Kem, Encapsulate, Decapsulate};
|
||||
use kem::Generate;
|
||||
use wolfssl_wolfcrypt::random::RNG;
|
||||
use wolfssl_wolfcrypt::mlkem_kem::*;
|
||||
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let (dk, ek) = MlKem768::generate_keypair_from_rng(&mut rng);
|
||||
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
|
||||
let k_recv = dk.decapsulate(&ct);
|
||||
assert_eq!(k_send, k_recv);
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
#![cfg(all(feature = "kem", mlkem))]
|
||||
|
||||
use kem::common::array::Array;
|
||||
use kem::common::typenum::{U32, U768, U800};
|
||||
use hybrid_array::sizes::{U1088, U1184, U1568, U1632, U2400, U3168};
|
||||
|
||||
macro_rules! impl_mlkem_kem {
|
||||
(
|
||||
kem = $kem:ident,
|
||||
ek = $ek:ident,
|
||||
dk = $dk:ident,
|
||||
pk_typenum = $pk_tn:ty,
|
||||
sk_typenum = $sk_tn:ty,
|
||||
ct_typenum = $ct_tn:ty,
|
||||
pk_len = $pk_len:expr,
|
||||
sk_len = $sk_len:expr,
|
||||
ct_len = $ct_len:expr,
|
||||
key_type = $key_type:expr $(,)?
|
||||
) => {
|
||||
/// ML-KEM parameter set marker implementing [`kem::Kem`].
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct $kem;
|
||||
|
||||
impl kem::Kem for $kem {
|
||||
type DecapsulationKey = $dk;
|
||||
type EncapsulationKey = $ek;
|
||||
type SharedKeySize = U32;
|
||||
type CiphertextSize = $ct_tn;
|
||||
}
|
||||
|
||||
/// ML-KEM encapsulation (public) key implementing [`kem::Encapsulate`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct $ek {
|
||||
pk: Array<u8, $pk_tn>,
|
||||
}
|
||||
|
||||
impl kem::KeySizeUser for $ek {
|
||||
type KeySize = $pk_tn;
|
||||
}
|
||||
|
||||
impl kem::TryKeyInit for $ek {
|
||||
fn new(key: &kem::Key<Self>) -> Result<Self, kem::InvalidKey> {
|
||||
let mut wc_key = crate::mlkem::MlKem::new($key_type)
|
||||
.map_err(|_| kem::InvalidKey)?;
|
||||
wc_key.decode_public_key(key.as_ref())
|
||||
.map_err(|_| kem::InvalidKey)?;
|
||||
Ok(Self { pk: key.clone() })
|
||||
}
|
||||
}
|
||||
|
||||
impl kem::KeyExport for $ek {
|
||||
fn to_bytes(&self) -> kem::Key<Self> {
|
||||
self.pk.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl kem::Encapsulate for $ek {
|
||||
type Kem = $kem;
|
||||
|
||||
fn encapsulate_with_rng<R: kem::common::rand_core::CryptoRng + ?Sized>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
) -> (kem::Ciphertext<$kem>, kem::SharedKey<$kem>) {
|
||||
let mut rand = [0u8; crate::mlkem::MlKem::ENC_RAND_SIZE];
|
||||
rng.fill_bytes(&mut rand);
|
||||
|
||||
let mut wc_key = crate::mlkem::MlKem::new($key_type)
|
||||
.expect("MlKem::new failed");
|
||||
wc_key.decode_public_key(self.pk.as_ref())
|
||||
.expect("decode_public_key failed");
|
||||
|
||||
let mut ct = [0u8; $ct_len];
|
||||
let mut ss = [0u8; crate::mlkem::MlKem::SHARED_SECRET_SIZE];
|
||||
wc_key.encapsulate_with_random(&mut ct, &mut ss, &rand)
|
||||
.expect("encapsulate_with_random failed");
|
||||
|
||||
(ct.into(), ss.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// ML-KEM decapsulation (private) key implementing [`kem::Decapsulate`].
|
||||
///
|
||||
/// The private key bytes are securely zeroized on drop.
|
||||
pub struct $dk {
|
||||
sk: Array<u8, $sk_tn>,
|
||||
ek: $ek,
|
||||
}
|
||||
|
||||
impl kem::Decapsulator for $dk {
|
||||
type Kem = $kem;
|
||||
|
||||
fn encapsulation_key(&self) -> &$ek {
|
||||
&self.ek
|
||||
}
|
||||
}
|
||||
|
||||
impl kem::Decapsulate for $dk {
|
||||
fn decapsulate(
|
||||
&self,
|
||||
ct: &kem::Ciphertext<$kem>,
|
||||
) -> kem::SharedKey<$kem> {
|
||||
let mut wc_key = crate::mlkem::MlKem::new($key_type)
|
||||
.expect("MlKem::new failed");
|
||||
wc_key.decode_private_key(self.sk.as_ref())
|
||||
.expect("decode_private_key failed");
|
||||
|
||||
let mut ss = [0u8; crate::mlkem::MlKem::SHARED_SECRET_SIZE];
|
||||
wc_key.decapsulate(&mut ss, ct.as_ref())
|
||||
.expect("decapsulate failed");
|
||||
|
||||
ss.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl kem::Generate for $dk {
|
||||
fn try_generate_from_rng<R: kem::common::rand_core::TryCryptoRng + ?Sized>(
|
||||
rng: &mut R,
|
||||
) -> Result<Self, R::Error> {
|
||||
let mut rand = [0u8; crate::mlkem::MlKem::MAKEKEY_RAND_SIZE];
|
||||
rng.try_fill_bytes(&mut rand)?;
|
||||
|
||||
let wc_key = crate::mlkem::MlKem::generate_with_random(
|
||||
$key_type, &rand,
|
||||
).expect("generate_with_random failed");
|
||||
|
||||
let mut pk = [0u8; $pk_len];
|
||||
let mut sk = [0u8; $sk_len];
|
||||
wc_key.encode_public_key(&mut pk)
|
||||
.expect("encode_public_key failed");
|
||||
wc_key.encode_private_key(&mut sk)
|
||||
.expect("encode_private_key failed");
|
||||
|
||||
Ok(Self {
|
||||
sk: sk.into(),
|
||||
ek: $ek { pk: pk.into() },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for $dk {
|
||||
fn drop(&mut self) {
|
||||
use zeroize::Zeroize;
|
||||
let sk_bytes: &mut [u8] = self.sk.as_mut();
|
||||
sk_bytes.zeroize();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_mlkem_kem! {
|
||||
kem = MlKem512,
|
||||
ek = MlKem512EncapsulationKey,
|
||||
dk = MlKem512DecapsulationKey,
|
||||
pk_typenum = U800,
|
||||
sk_typenum = U1632,
|
||||
ct_typenum = U768,
|
||||
pk_len = 800,
|
||||
sk_len = 1632,
|
||||
ct_len = 768,
|
||||
key_type = crate::mlkem::MlKem::TYPE_512,
|
||||
}
|
||||
|
||||
impl_mlkem_kem! {
|
||||
kem = MlKem768,
|
||||
ek = MlKem768EncapsulationKey,
|
||||
dk = MlKem768DecapsulationKey,
|
||||
pk_typenum = U1184,
|
||||
sk_typenum = U2400,
|
||||
ct_typenum = U1088,
|
||||
pk_len = 1184,
|
||||
sk_len = 2400,
|
||||
ct_len = 1088,
|
||||
key_type = crate::mlkem::MlKem::TYPE_768,
|
||||
}
|
||||
|
||||
impl_mlkem_kem! {
|
||||
kem = MlKem1024,
|
||||
ek = MlKem1024EncapsulationKey,
|
||||
dk = MlKem1024DecapsulationKey,
|
||||
pk_typenum = U1568,
|
||||
sk_typenum = U3168,
|
||||
ct_typenum = U1568,
|
||||
pk_len = 1568,
|
||||
sk_len = 3168,
|
||||
ct_len = 1568,
|
||||
key_type = crate::mlkem::MlKem::TYPE_1024,
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*!
|
||||
RustCrypto `password-hash` trait implementations for wolfCrypt PBKDF2.
|
||||
|
||||
This module provides [`Pbkdf2`], a type that implements the
|
||||
[`PasswordHasher`] and [`CustomizedPasswordHasher`] traits from the
|
||||
`password-hash` crate, backed by the wolfCrypt PBKDF2 implementation.
|
||||
The blanket [`PasswordVerifier`] implementation is also available,
|
||||
allowing verification of existing password hashes.
|
||||
|
||||
Password hashes are represented in the
|
||||
[PHC string format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md):
|
||||
|
||||
```text
|
||||
$pbkdf2-sha256$i=600000$<salt>$<hash>
|
||||
```
|
||||
|
||||
# Supported algorithms
|
||||
|
||||
| Algorithm ID | Hash function |
|
||||
|-----------------|---------------|
|
||||
| `pbkdf2-sha256` | HMAC-SHA-256 |
|
||||
| `pbkdf2-sha384` | HMAC-SHA-384 |
|
||||
| `pbkdf2-sha512` | HMAC-SHA-512 |
|
||||
|
||||
[`PasswordHasher`]: password_hash::PasswordHasher
|
||||
[`CustomizedPasswordHasher`]: password_hash::CustomizedPasswordHasher
|
||||
[`PasswordVerifier`]: password_hash::PasswordVerifier
|
||||
*/
|
||||
|
||||
#![cfg(all(feature = "password-hash", hmac, kdf_pbkdf2))]
|
||||
|
||||
use password_hash::phc::{Ident, Output, ParamsString, PasswordHash, Salt};
|
||||
use password_hash::{CustomizedPasswordHasher, Error, Result, Version};
|
||||
|
||||
use crate::hmac::HMAC;
|
||||
use crate::kdf;
|
||||
|
||||
const PBKDF2_SHA256_IDENT: Ident = Ident::new_unwrap("pbkdf2-sha256");
|
||||
const PBKDF2_SHA384_IDENT: Ident = Ident::new_unwrap("pbkdf2-sha384");
|
||||
const PBKDF2_SHA512_IDENT: Ident = Ident::new_unwrap("pbkdf2-sha512");
|
||||
|
||||
/// Minimum number of PBKDF2 rounds.
|
||||
pub const MIN_ROUNDS: u32 = 1_000;
|
||||
|
||||
/// Default number of PBKDF2 rounds (OWASP recommendation for SHA-256).
|
||||
pub const DEFAULT_ROUNDS: u32 = 600_000;
|
||||
|
||||
/// Default output length in bytes.
|
||||
pub const DEFAULT_OUTPUT_LEN: usize = 32;
|
||||
|
||||
/// PBKDF2 algorithm variant.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub enum Algorithm {
|
||||
/// PBKDF2 with HMAC-SHA-256.
|
||||
#[default]
|
||||
Pbkdf2Sha256,
|
||||
/// PBKDF2 with HMAC-SHA-384.
|
||||
Pbkdf2Sha384,
|
||||
/// PBKDF2 with HMAC-SHA-512.
|
||||
Pbkdf2Sha512,
|
||||
}
|
||||
|
||||
impl Algorithm {
|
||||
/// Get the PHC string format identifier for this algorithm.
|
||||
pub fn ident(self) -> Ident {
|
||||
match self {
|
||||
Algorithm::Pbkdf2Sha256 => PBKDF2_SHA256_IDENT,
|
||||
Algorithm::Pbkdf2Sha384 => PBKDF2_SHA384_IDENT,
|
||||
Algorithm::Pbkdf2Sha512 => PBKDF2_SHA512_IDENT,
|
||||
}
|
||||
}
|
||||
|
||||
fn hmac_type(self) -> i32 {
|
||||
match self {
|
||||
Algorithm::Pbkdf2Sha256 => HMAC::TYPE_SHA256,
|
||||
Algorithm::Pbkdf2Sha384 => HMAC::TYPE_SHA384,
|
||||
Algorithm::Pbkdf2Sha512 => HMAC::TYPE_SHA512,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Ident> for Algorithm {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(ident: Ident) -> Result<Self> {
|
||||
if ident == PBKDF2_SHA256_IDENT {
|
||||
Ok(Algorithm::Pbkdf2Sha256)
|
||||
} else if ident == PBKDF2_SHA384_IDENT {
|
||||
Ok(Algorithm::Pbkdf2Sha384)
|
||||
} else if ident == PBKDF2_SHA512_IDENT {
|
||||
Ok(Algorithm::Pbkdf2Sha512)
|
||||
} else {
|
||||
Err(Error::Algorithm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PBKDF2 parameters.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Params {
|
||||
/// Number of iterations (rounds).
|
||||
pub rounds: u32,
|
||||
/// Desired output hash length in bytes.
|
||||
pub output_len: usize,
|
||||
}
|
||||
|
||||
impl Default for Params {
|
||||
fn default() -> Self {
|
||||
Params {
|
||||
rounds: DEFAULT_ROUNDS,
|
||||
output_len: DEFAULT_OUTPUT_LEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&PasswordHash> for Params {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(hash: &PasswordHash) -> Result<Self> {
|
||||
let rounds = hash
|
||||
.params
|
||||
.get_decimal("i")
|
||||
.ok_or(Error::ParamInvalid { name: "i" })?;
|
||||
|
||||
if rounds < MIN_ROUNDS {
|
||||
return Err(Error::ParamInvalid { name: "i" });
|
||||
}
|
||||
|
||||
let output_len = if let Some(ref h) = hash.hash {
|
||||
h.len()
|
||||
} else if let Some(l) = hash.params.get_decimal("l") &&
|
||||
0 < l && (l as usize) <= Output::MAX_LENGTH {
|
||||
l as usize
|
||||
} else {
|
||||
return Err(Error::ParamInvalid { name: "l" });
|
||||
};
|
||||
|
||||
Ok(Params { rounds, output_len })
|
||||
}
|
||||
}
|
||||
|
||||
/// PBKDF2 password hasher backed by wolfCrypt.
|
||||
///
|
||||
/// Implements the [`PasswordHasher`](password_hash::PasswordHasher) and
|
||||
/// [`CustomizedPasswordHasher`] traits. A blanket
|
||||
/// [`PasswordVerifier`](password_hash::PasswordVerifier) implementation is
|
||||
/// provided by the `password-hash` crate.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(all(hmac, kdf_pbkdf2))]
|
||||
/// {
|
||||
/// use password_hash::PasswordHasher;
|
||||
/// use wolfssl_wolfcrypt::pbkdf2_password_hash::Pbkdf2;
|
||||
///
|
||||
/// let hasher = Pbkdf2::default();
|
||||
/// let salt = b"0123456789abcdef"; // 16 bytes
|
||||
/// let hash = hasher.hash_password_with_salt(b"password", salt)
|
||||
/// .expect("hashing failed");
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Pbkdf2 {
|
||||
/// Algorithm to use for hashing.
|
||||
pub algorithm: Algorithm,
|
||||
/// Default parameters.
|
||||
pub params: Params,
|
||||
}
|
||||
|
||||
impl password_hash::PasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result<PasswordHash> {
|
||||
self.hash_password_customized(password, salt, None, None, self.params.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl password_hash::CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
type Params = Params;
|
||||
|
||||
fn hash_password_customized(
|
||||
&self,
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
algorithm: Option<&str>,
|
||||
version: Option<Version>,
|
||||
params: Params,
|
||||
) -> Result<PasswordHash> {
|
||||
if version.is_some() {
|
||||
return Err(Error::Version);
|
||||
}
|
||||
|
||||
let algorithm = match algorithm {
|
||||
Some(s) => {
|
||||
let ident = Ident::new(s).map_err(|_| Error::Algorithm)?;
|
||||
Algorithm::try_from(ident)?
|
||||
}
|
||||
None => self.algorithm,
|
||||
};
|
||||
|
||||
if params.rounds < MIN_ROUNDS || params.output_len > Output::MAX_LENGTH {
|
||||
return Err(Error::ParamInvalid { name: "i" });
|
||||
}
|
||||
|
||||
let iterations = i32::try_from(params.rounds)
|
||||
.map_err(|_| Error::ParamInvalid { name: "i" })?;
|
||||
|
||||
let salt = Salt::new(salt)?;
|
||||
|
||||
let mut out_buf = [0u8; Output::MAX_LENGTH];
|
||||
let out_slice = &mut out_buf[..params.output_len];
|
||||
kdf::pbkdf2(password, salt.as_ref(), iterations, algorithm.hmac_type(), out_slice)
|
||||
.map_err(|_| Error::Crypto)?;
|
||||
let output = Output::new(out_slice)?;
|
||||
|
||||
let mut phc_params = ParamsString::new();
|
||||
phc_params.add_decimal("i", params.rounds)?;
|
||||
|
||||
Ok(PasswordHash {
|
||||
algorithm: algorithm.ident(),
|
||||
version: None,
|
||||
params: phc_params,
|
||||
salt: Some(salt),
|
||||
hash: Some(output),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ rng.generate_block(&mut buffer).expect("Failed to generate a block");
|
||||
|
||||
use crate::sys;
|
||||
use core::mem::{size_of_val, MaybeUninit};
|
||||
use num_traits::PrimInt;
|
||||
|
||||
/// A cryptographically secure random number generator based on the wolfSSL
|
||||
/// library.
|
||||
@@ -127,7 +128,7 @@ impl RNG {
|
||||
///
|
||||
/// A Result which is Ok(RNG) on success or an Err containing the wolfSSL
|
||||
/// library return code on failure.
|
||||
pub fn new_with_nonce<T>(nonce: &mut [T]) -> Result<Self, i32> {
|
||||
pub fn new_with_nonce<T: PrimInt>(nonce: &mut [T]) -> Result<Self, i32> {
|
||||
RNG::new_with_nonce_ex(nonce, None, None)
|
||||
}
|
||||
|
||||
@@ -146,7 +147,7 @@ impl RNG {
|
||||
///
|
||||
/// A Result which is Ok(RNG) on success or an Err containing the wolfSSL
|
||||
/// library return code on failure.
|
||||
pub fn new_with_nonce_ex<T>(nonce: &mut [T], heap: Option<*mut core::ffi::c_void>, dev_id: Option<i32>) -> Result<Self, i32> {
|
||||
pub fn new_with_nonce_ex<T: PrimInt>(nonce: &mut [T], heap: Option<*mut core::ffi::c_void>, dev_id: Option<i32>) -> Result<Self, i32> {
|
||||
#[cfg(fips)]
|
||||
{
|
||||
let rc = unsafe {
|
||||
@@ -338,7 +339,7 @@ impl RNG {
|
||||
///
|
||||
/// A `Result` which is `Ok(())` on success or an `Err` with the wolfssl
|
||||
/// library return code on failure.
|
||||
pub fn generate_block<T>(&mut self, buf: &mut [T]) -> Result<(), i32> {
|
||||
pub fn generate_block<T: PrimInt>(&mut self, buf: &mut [T]) -> Result<(), i32> {
|
||||
let ptr = buf.as_mut_ptr() as *mut u8;
|
||||
let size = crate::buffer_len_to_u32(size_of_val(buf))?;
|
||||
let rc = unsafe { sys::wc_RNG_GenerateBlock(&mut self.wc_rng, ptr, size) };
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#![cfg(all(cmac, feature = "mac"))]
|
||||
|
||||
use digest::{KeyInit, Mac};
|
||||
use wolfssl_wolfcrypt::cmac_mac::CmacAes128;
|
||||
|
||||
#[test]
|
||||
fn test_cmac_aes128_mac_trait() {
|
||||
let key = [
|
||||
0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
|
||||
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
|
||||
];
|
||||
let message = [
|
||||
0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
|
||||
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
|
||||
];
|
||||
let expected = [
|
||||
0x07u8, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
|
||||
0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c
|
||||
];
|
||||
|
||||
let mut mac = CmacAes128::new_from_slice(&key)
|
||||
.expect("CMAC init failed");
|
||||
mac.update(&message);
|
||||
mac.verify_slice(&expected).expect("CMAC verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmac_aes128_mac_finalize() {
|
||||
let key = [
|
||||
0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
|
||||
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
|
||||
];
|
||||
let message = [
|
||||
0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
|
||||
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
|
||||
];
|
||||
let expected: &[u8] = &[
|
||||
0x07u8, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
|
||||
0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c
|
||||
];
|
||||
|
||||
let mac = CmacAes128::new_from_slice(&key)
|
||||
.expect("CMAC init failed")
|
||||
.chain_update(&message);
|
||||
let result = mac.finalize();
|
||||
assert_eq!(result.as_bytes().as_slice(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmac_aes128_mac_verify_fail() {
|
||||
let key = [
|
||||
0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
|
||||
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
|
||||
];
|
||||
let message = [
|
||||
0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
|
||||
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
|
||||
];
|
||||
let wrong_tag = [0u8; 16];
|
||||
|
||||
let mut mac = CmacAes128::new_from_slice(&key)
|
||||
.expect("CMAC init failed");
|
||||
mac.update(&message);
|
||||
assert!(mac.verify_slice(&wrong_tag).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmac_aes128_wrong_key_size() {
|
||||
let bad_key = [0u8; 15]; // wrong size for AES-128
|
||||
assert!(CmacAes128::new_from_slice(&bad_key).is_err());
|
||||
}
|
||||
@@ -11,6 +11,8 @@ use wolfssl_wolfcrypt::random::RNG;
|
||||
#[test]
|
||||
#[cfg(random)]
|
||||
fn test_ecc_generate() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let mut ecc = ECC::generate(32, &mut rng, None, None).expect("Error with generate()");
|
||||
ecc.check().expect("Error with check()");
|
||||
@@ -19,6 +21,8 @@ fn test_ecc_generate() {
|
||||
#[test]
|
||||
#[cfg(random)]
|
||||
fn test_ecc_generate_ex() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let curve_id = ECC::SECP256R1;
|
||||
let curve_size = ECC::get_curve_size_from_id(curve_id).expect("Error with get_curve_size_from_id()");
|
||||
@@ -30,6 +34,8 @@ fn test_ecc_generate_ex() {
|
||||
#[test]
|
||||
#[cfg(all(ecc_import, ecc_export, random))]
|
||||
fn test_ecc_import_x963() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let curve_id = ECC::SECP256R1;
|
||||
let curve_size = ECC::get_curve_size_from_id(curve_id).expect("Error with get_curve_size_from_id()");
|
||||
@@ -47,6 +53,8 @@ fn test_ecc_import_x963() {
|
||||
#[test]
|
||||
#[cfg(random)]
|
||||
fn test_ecc_generate_ex2() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let curve_id = ECC::SECP256R1;
|
||||
let curve_size = ECC::get_curve_size_from_id(curve_id).expect("Error with get_curve_size_from_id()");
|
||||
@@ -58,6 +66,8 @@ fn test_ecc_generate_ex2() {
|
||||
#[test]
|
||||
#[cfg(all(ecc_import, ecc_export, ecc_sign, ecc_verify, random))]
|
||||
fn test_ecc_import_export_sign_verify() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let key_path = "../../../certs/ecc-client-key.der";
|
||||
let der: Vec<u8> = fs::read(key_path).expect("Error reading key file");
|
||||
@@ -242,6 +252,8 @@ fn test_ecc_import_export_private_ex() {
|
||||
#[test]
|
||||
#[cfg(all(ecc_export, random))]
|
||||
fn test_ecc_export_public() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let mut ecc = ECC::generate(32, &mut rng, None, None).expect("Error with generate()");
|
||||
let mut qx = [0u8; 32];
|
||||
@@ -281,6 +293,8 @@ fn test_ecc_import_unsigned() {
|
||||
#[test]
|
||||
#[cfg(random)]
|
||||
fn test_ecc_make_pub() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let key_path = "../../../certs/ecc-client-key.der";
|
||||
let der: Vec<u8> = fs::read(key_path).expect("Error reading key file");
|
||||
@@ -294,6 +308,8 @@ fn test_ecc_make_pub() {
|
||||
#[test]
|
||||
#[cfg(all(ecc_export, random))]
|
||||
fn test_ecc_point() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let curve_id = ECC::SECP256R1;
|
||||
let curve_size = ECC::get_curve_size_from_id(curve_id).expect("Error with get_curve_size_from_id()");
|
||||
@@ -308,6 +324,8 @@ fn test_ecc_point() {
|
||||
#[test]
|
||||
#[cfg(all(all(ecc_import, ecc_export, random)))]
|
||||
fn test_ecc_point_import() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let curve_id = ECC::SECP256R1;
|
||||
let curve_size = ECC::get_curve_size_from_id(curve_id).expect("Error with get_curve_size_from_id()");
|
||||
@@ -323,6 +341,8 @@ fn test_ecc_point_import() {
|
||||
#[test]
|
||||
#[cfg(all(ecc_import, ecc_export, ecc_comp_key, random))]
|
||||
fn test_ecc_point_import_compressed() {
|
||||
common::setup();
|
||||
|
||||
let mut rng = RNG::new().expect("Failed to create RNG");
|
||||
let curve_id = ECC::SECP256R1;
|
||||
let curve_size = ECC::get_curve_size_from_id(curve_id).expect("Error with get_curve_size_from_id()");
|
||||
@@ -336,9 +356,34 @@ fn test_ecc_point_import_compressed() {
|
||||
#[test]
|
||||
#[cfg(ecc_import)]
|
||||
fn test_ecc_import() {
|
||||
common::setup();
|
||||
|
||||
let qx = b"7a4e287890a1a47ad3457e52f2f76a83ce46cbc947616d0cbaa82323818a793d\0";
|
||||
let qy = b"eec4084f5b29ebf29c44cce3b3059610922f8b30ea6e8811742ac7238fe87308\0";
|
||||
let d = b"8c14b793cb19137e323a6d2e2a870bca2e7a493ec1153b3a95feb8a4873f8d08\0";
|
||||
ECC::import_raw(qx, qy, d, b"SECP256R1\0", None, None).expect("Error with import_raw()");
|
||||
ECC::import_raw_ex(qx, qy, d, ECC::SECP256R1, None, None).expect("Error with import_raw_ex()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ecc_rs_hex_to_sig_not_null_terminated() {
|
||||
common::setup();
|
||||
|
||||
let r_hex = b"AABB\0";
|
||||
let s_hex = b"CCDD\0";
|
||||
let r_hex_no_nul = b"AABB";
|
||||
let s_hex_no_nul = b"CCDD";
|
||||
let mut sig_out = [0u8; 128];
|
||||
|
||||
// Both null-terminated should succeed
|
||||
assert!(ECC::rs_hex_to_sig(r_hex, s_hex, &mut sig_out).is_ok());
|
||||
|
||||
// r not null-terminated should fail
|
||||
assert!(ECC::rs_hex_to_sig(r_hex_no_nul, s_hex, &mut sig_out).is_err());
|
||||
|
||||
// s not null-terminated should fail
|
||||
assert!(ECC::rs_hex_to_sig(r_hex, s_hex_no_nul, &mut sig_out).is_err());
|
||||
|
||||
// Both not null-terminated should fail
|
||||
assert!(ECC::rs_hex_to_sig(r_hex_no_nul, s_hex_no_nul, &mut sig_out).is_err());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#![cfg(all(hmac, sha256, feature = "mac"))]
|
||||
|
||||
use digest::{KeyInit, Mac};
|
||||
use wolfssl_wolfcrypt::hmac_mac::HmacSha256;
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha256_mac_trait() {
|
||||
let key = b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
|
||||
let input = b"Hi There";
|
||||
let expected = b"\xb0\x34\x4c\x61\xd8\xdb\x38\x53\x5c\xa8\xaf\xce\xaf\x0b\xf1\x2b\x88\x1d\xc2\x00\xc9\x83\x3d\xa7\x26\xe9\x37\x6c\x2e\x32\xcf\xf7";
|
||||
|
||||
let mut mac = HmacSha256::new_from_slice(key)
|
||||
.expect("HMAC init failed");
|
||||
mac.update(input);
|
||||
mac.verify_slice(expected).expect("HMAC verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha256_mac_chain() {
|
||||
let key = b"Jefe";
|
||||
let input = b"what do ya want for nothing?";
|
||||
let expected = b"\x5b\xdc\xc1\x46\xbf\x60\x75\x4e\x6a\x04\x24\x26\x08\x95\x75\xc7\x5a\x00\x3f\x08\x9d\x27\x39\x83\x9d\xec\x58\xb9\x64\xec\x38\x43";
|
||||
|
||||
let mac = HmacSha256::new_from_slice(key)
|
||||
.expect("HMAC init failed")
|
||||
.chain_update(input);
|
||||
mac.verify_slice(expected).expect("HMAC verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha256_mac_finalize() {
|
||||
let key = b"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA";
|
||||
let input = b"\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD";
|
||||
let expected: &[u8] = b"\x77\x3e\xa9\x1e\x36\x80\x0e\x46\x85\x4d\xb8\xeb\xd0\x91\x81\xa7\x29\x59\x09\x8b\x3e\xf8\xc1\x22\xd9\x63\x55\x14\xce\xd5\x65\xfe";
|
||||
|
||||
let mut mac = HmacSha256::new_from_slice(key)
|
||||
.expect("HMAC init failed");
|
||||
mac.update(input);
|
||||
let result = mac.finalize();
|
||||
assert_eq!(result.as_bytes().as_slice(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha256_mac_verify_fail() {
|
||||
let key = b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
|
||||
let input = b"Hi There";
|
||||
let wrong_tag = [0u8; 32];
|
||||
|
||||
let mut mac = HmacSha256::new_from_slice(key)
|
||||
.expect("HMAC init failed");
|
||||
mac.update(input);
|
||||
assert!(mac.verify_slice(&wrong_tag).is_err());
|
||||
}
|
||||
@@ -350,11 +350,11 @@ fn test_export_pub_from() {
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
/// Verify that `sigs_left()` indicates signatures are available immediately
|
||||
/// Verify that `has_sigs_left()` indicates signatures are available immediately
|
||||
/// after `make_key()`.
|
||||
#[test]
|
||||
#[cfg(all(lms_make_key, random))]
|
||||
fn test_sigs_left_after_make_key() {
|
||||
fn test_has_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] });
|
||||
@@ -365,8 +365,8 @@ fn test_sigs_left_after_make_key() {
|
||||
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 remaining = key.has_sigs_left().expect("Error with has_sigs_left()");
|
||||
assert!(remaining, "has_sigs_left must be true immediately after make_key()");
|
||||
|
||||
let _ = store;
|
||||
}
|
||||
|
||||
@@ -244,9 +244,7 @@ fn test_encode_decode_public_key() {
|
||||
let ss_size = key.shared_secret_size().expect("Error with shared_secret_size()");
|
||||
|
||||
let mut pub_buf = vec![0u8; pub_size];
|
||||
let written = key.encode_public_key(&mut pub_buf)
|
||||
.expect("Error with encode_public_key()");
|
||||
assert_eq!(written, pub_size);
|
||||
key.encode_public_key(&mut pub_buf).expect("Error with encode_public_key()");
|
||||
|
||||
// Re-import public key and encapsulate.
|
||||
let mut pub_key = MlKem::new(MlKem::TYPE_768).expect("Error with new()");
|
||||
@@ -280,9 +278,7 @@ fn test_encode_decode_private_key() {
|
||||
let ss_size = key.shared_secret_size().expect("Error with shared_secret_size()");
|
||||
|
||||
let mut priv_buf = vec![0u8; priv_size];
|
||||
let written = key.encode_private_key(&mut priv_buf)
|
||||
.expect("Error with encode_private_key()");
|
||||
assert_eq!(written, priv_size);
|
||||
key.encode_private_key(&mut priv_buf).expect("Error with encode_private_key()");
|
||||
|
||||
// Encapsulate with the original key.
|
||||
let mut ct = vec![0u8; ct_size];
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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(all(mlkem, random, feature = "kem", feature = "rand_core"))]
|
||||
|
||||
mod common;
|
||||
|
||||
use kem::{Decapsulate, Decapsulator, Encapsulate, Kem, TryKeyInit, KeyExport};
|
||||
use kem::Generate;
|
||||
use wolfssl_wolfcrypt::mlkem::MlKem;
|
||||
use wolfssl_wolfcrypt::mlkem_kem::*;
|
||||
use wolfssl_wolfcrypt::random::RNG;
|
||||
|
||||
/// Verify that the compile-time sizes used by the kem types match the runtime
|
||||
/// sizes reported by wolfCrypt.
|
||||
#[test]
|
||||
fn test_sizes_match_runtime() {
|
||||
common::setup();
|
||||
|
||||
let key512 = MlKem::new(MlKem::TYPE_512).expect("new TYPE_512");
|
||||
assert_eq!(key512.public_key_size().unwrap(), 800);
|
||||
assert_eq!(key512.private_key_size().unwrap(), 1632);
|
||||
assert_eq!(key512.cipher_text_size().unwrap(), 768);
|
||||
|
||||
let key768 = MlKem::new(MlKem::TYPE_768).expect("new TYPE_768");
|
||||
assert_eq!(key768.public_key_size().unwrap(), 1184);
|
||||
assert_eq!(key768.private_key_size().unwrap(), 2400);
|
||||
assert_eq!(key768.cipher_text_size().unwrap(), 1088);
|
||||
|
||||
let key1024 = MlKem::new(MlKem::TYPE_1024).expect("new TYPE_1024");
|
||||
assert_eq!(key1024.public_key_size().unwrap(), 1568);
|
||||
assert_eq!(key1024.private_key_size().unwrap(), 3168);
|
||||
assert_eq!(key1024.cipher_text_size().unwrap(), 1568);
|
||||
}
|
||||
|
||||
/// Generate, encapsulate, and decapsulate with ML-KEM-512 via the kem traits.
|
||||
#[test]
|
||||
fn test_kem_512_round_trip() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let (dk, ek) = MlKem512::generate_keypair_from_rng(&mut rng);
|
||||
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
|
||||
let k_recv = dk.decapsulate(&ct);
|
||||
assert_eq!(k_send, k_recv);
|
||||
}
|
||||
|
||||
/// Generate, encapsulate, and decapsulate with ML-KEM-768 via the kem traits.
|
||||
#[test]
|
||||
fn test_kem_768_round_trip() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let (dk, ek) = MlKem768::generate_keypair_from_rng(&mut rng);
|
||||
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
|
||||
let k_recv = dk.decapsulate(&ct);
|
||||
assert_eq!(k_send, k_recv);
|
||||
}
|
||||
|
||||
/// Generate, encapsulate, and decapsulate with ML-KEM-1024 via the kem traits.
|
||||
#[test]
|
||||
fn test_kem_1024_round_trip() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let (dk, ek) = MlKem1024::generate_keypair_from_rng(&mut rng);
|
||||
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
|
||||
let k_recv = dk.decapsulate(&ct);
|
||||
assert_eq!(k_send, k_recv);
|
||||
}
|
||||
|
||||
/// Verify that `Generate::generate_from_rng` produces a usable decapsulation
|
||||
/// key and that the associated encapsulation key is consistent.
|
||||
#[test]
|
||||
fn test_generate_from_rng() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let dk = MlKem768DecapsulationKey::generate_from_rng(&mut rng);
|
||||
let ek = dk.encapsulation_key();
|
||||
|
||||
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
|
||||
let k_recv = dk.decapsulate(&ct);
|
||||
assert_eq!(k_send, k_recv);
|
||||
}
|
||||
|
||||
/// Verify that a tampered ciphertext produces a different shared secret
|
||||
/// (ML-KEM implicit rejection).
|
||||
#[test]
|
||||
fn test_implicit_rejection() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let (dk, ek) = MlKem768::generate_keypair_from_rng(&mut rng);
|
||||
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
|
||||
|
||||
let mut ct_tampered = ct.clone();
|
||||
ct_tampered[0] ^= 0xFF;
|
||||
let k_tampered = dk.decapsulate(&ct_tampered);
|
||||
|
||||
assert_eq!(k_send, dk.decapsulate(&ct));
|
||||
assert_ne!(k_send, k_tampered);
|
||||
}
|
||||
|
||||
/// Verify that `TryKeyInit` and `KeyExport` round-trip the encapsulation key.
|
||||
#[test]
|
||||
fn test_ek_export_import() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let (dk, ek) = MlKem768::generate_keypair_from_rng(&mut rng);
|
||||
|
||||
// Export and re-import the encapsulation key.
|
||||
let exported = ek.to_bytes();
|
||||
let ek2 = MlKem768EncapsulationKey::new(&exported)
|
||||
.expect("TryKeyInit failed");
|
||||
assert_eq!(ek, ek2);
|
||||
|
||||
// Encapsulate with the re-imported key; the original DK must decapsulate.
|
||||
let (ct, k_send) = ek2.encapsulate_with_rng(&mut rng);
|
||||
let k_recv = dk.decapsulate(&ct);
|
||||
assert_eq!(k_send, k_recv);
|
||||
}
|
||||
|
||||
/// Verify that `TryKeyInit` doesn't panic on a zeroed key.
|
||||
#[test]
|
||||
fn test_ek_try_new_zeroed_key() {
|
||||
common::setup();
|
||||
|
||||
// A zero-filled buffer of the correct size. Whether this succeeds or fails
|
||||
// depends on wolfCrypt's decode_public_key validation. The key point is it
|
||||
// shouldn't panic.
|
||||
let zeroed = kem::Key::<MlKem768EncapsulationKey>::default();
|
||||
let _ = MlKem768EncapsulationKey::new(&zeroed);
|
||||
}
|
||||
|
||||
/// Verify the `Decapsulator::encapsulation_key` method returns a key that
|
||||
/// can be used for encapsulation.
|
||||
#[test]
|
||||
fn test_decapsulator_encapsulation_key() {
|
||||
common::setup();
|
||||
let mut rng = RNG::new().expect("RNG creation failed");
|
||||
|
||||
let dk = MlKem512DecapsulationKey::generate_from_rng(&mut rng);
|
||||
let ek = dk.encapsulation_key().clone();
|
||||
|
||||
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
|
||||
let k_recv = dk.decapsulate(&ct);
|
||||
assert_eq!(k_send, k_recv);
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
#![cfg(all(feature = "password-hash", hmac, kdf_pbkdf2))]
|
||||
|
||||
mod common;
|
||||
|
||||
use password_hash::phc::PasswordHash;
|
||||
use password_hash::{CustomizedPasswordHasher, PasswordHasher, PasswordVerifier};
|
||||
use wolfssl_wolfcrypt::pbkdf2_password_hash::*;
|
||||
|
||||
#[test]
|
||||
fn test_hash_and_verify() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef"; // 16 bytes
|
||||
let password = b"hunter2";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing failed");
|
||||
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha256.ident());
|
||||
assert!(hash.salt.is_some());
|
||||
assert!(hash.hash.is_some());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 32);
|
||||
|
||||
// Verify correct password succeeds
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("verification of correct password failed");
|
||||
|
||||
// Verify wrong password fails
|
||||
let result = hasher.verify_password(b"wrong_password", &hash);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_roundtrip_phc_string() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing failed");
|
||||
|
||||
// Serialize to PHC string and parse back
|
||||
let phc_string = hash.to_string();
|
||||
assert!(phc_string.starts_with("$pbkdf2-sha256$"));
|
||||
|
||||
let parsed = PasswordHash::new(&phc_string).expect("parsing PHC string failed");
|
||||
|
||||
// Verify with the parsed hash
|
||||
hasher
|
||||
.verify_password(password, &parsed)
|
||||
.expect("verification of parsed hash failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_params() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
assert_eq!(hasher.algorithm, Algorithm::Pbkdf2Sha256);
|
||||
assert_eq!(hasher.params.rounds, DEFAULT_ROUNDS);
|
||||
assert_eq!(hasher.params.output_len, DEFAULT_OUTPUT_LEN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(sha384)]
|
||||
fn test_sha384_algorithm() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha384,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 48,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing with SHA-384 failed");
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha384.ident());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 48);
|
||||
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("SHA-384 verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(sha512)]
|
||||
fn test_sha512_algorithm() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha512,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 64,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing with SHA-512 failed");
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha512.ident());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 64);
|
||||
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("SHA-512 verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_customized_hash() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
let custom_params = Params {
|
||||
rounds: 8192,
|
||||
output_len: 48,
|
||||
};
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_params(password, salt, custom_params)
|
||||
.expect("customized hashing failed");
|
||||
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 48);
|
||||
assert_eq!(hash.params.get_decimal("i"), Some(8192));
|
||||
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("customized hash verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(sha512)]
|
||||
fn test_customized_hash_with_algorithm_override() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
let params = Params {
|
||||
rounds: 4096,
|
||||
output_len: 64,
|
||||
};
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_customized(password, salt, Some("pbkdf2-sha512"), None, params)
|
||||
.expect("algorithm override failed");
|
||||
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha512.ident());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 64);
|
||||
|
||||
// Verify with a Pbkdf2 instance using the matching algorithm
|
||||
let verifier = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha512,
|
||||
..Pbkdf2::default()
|
||||
};
|
||||
verifier
|
||||
.verify_password(password, &hash)
|
||||
.expect("verification with algorithm override failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_rejected() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
let salt = b"0123456789abcdef";
|
||||
|
||||
let result =
|
||||
hasher.hash_password_customized(b"password", salt, None, Some(1), Params::default());
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_algorithm_rejected() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
let salt = b"0123456789abcdef";
|
||||
|
||||
let result = hasher.hash_password_customized(
|
||||
b"password",
|
||||
salt,
|
||||
Some("argon2id"),
|
||||
None,
|
||||
Params::default(),
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deterministic_output() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash1 = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("first hash failed");
|
||||
let hash2 = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("second hash failed");
|
||||
|
||||
assert_eq!(hash1.hash, hash2.hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_salts_produce_different_hashes() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let password = b"password";
|
||||
|
||||
let hash1 = hasher
|
||||
.hash_password_with_salt(password, b"salt_aaaaaaaaaa01")
|
||||
.expect("first hash failed");
|
||||
let hash2 = hasher
|
||||
.hash_password_with_salt(password, b"salt_aaaaaaaaaa02")
|
||||
.expect("second hash failed");
|
||||
|
||||
assert_ne!(hash1.hash, hash2.hash);
|
||||
}
|
||||
Reference in New Issue
Block a user