Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ jobs:
elif [ "${{ matrix.name }}" = "minimal" ]; then
FEATURE_SET="minimal"
elif [ "${{ matrix.name }}" = "pqc" ]; then
FEATURES="${FEATURES},pqc"
FEATURES="${FEATURES},pqc,slow"
elif [ "${{ matrix.name }}" = "no_sha1" ]; then
FEATURES="${FEATURES},no_sha1"
fi
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/openssl_versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ jobs:
elif [ "${{ matrix.version }}" = "3.2" ]; then
FEATURES="minimal,eddsa"
elif [ "${{ matrix.version }}" = "3.5" ]; then
FEATURES="minimal,eddsa,mldsa,no_sha1"
FEATURES="minimal,eddsa,mldsa,slhdsa,no_sha1"
fi

if [ "${{ matrix.build }}" = "false" ]; then
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ All notable changes to this project should be documented in this file.
a base key with provided data.
- [Implement simple KDFs](https://github.com/latchset/kryoptic/pull/278)

* Added support for SLH-DSA keys and operations
- [Add support for SLH-DSA](https://github.com/latchset/kryoptic/pull/316)

# [1.2.0]
## 2025-06-09

Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ slow = [] # Enables slow tests

mlkem = ["ossl/ossl350"]
mldsa = ["hash", "ossl/ossl350"]
slhdsa = ["hash", "ossl/ossl350"]

pqc = ["mlkem", "mldsa"]
pqc = ["mlkem", "mldsa", "slhdsa"]

minimal = ["sqlitedb", "aes", "ecc_min", "hash_all", "rsa"]
5 changes: 4 additions & 1 deletion ossl/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ impl bindgen::callbacks::ParseCallbacks for OsslCallbacks {
}

fn str_macro(&self, name: &str, _value: &[u8]) {
if name == "OSSL_PKEY_PARAM_SLH_DSA_SEED" {
println!("cargo::rustc-cfg=ossl_slhdsa")
}
if name == "OSSL_PKEY_PARAM_ML_DSA_SEED" {
println!("cargo::rustc-cfg=ossl_mldsa")
}
Expand Down Expand Up @@ -282,7 +285,7 @@ fn main() {
let ossl_bindings = out_path.join("ossl_bindings.rs");

/* Always emit known configs */
println!("cargo::rustc-check-cfg=cfg(ossl_v307,ossl_v320,ossl_v350,ossl_mldsa,ossl_mlkem)");
println!("cargo::rustc-check-cfg=cfg(ossl_v307,ossl_v320,ossl_v350,ossl_mldsa,ossl_mlkem,ossl_slhdsa)");

/* OpenSSL Cryptography */
if cfg!(feature = "dynamic") {
Expand Down
12 changes: 12 additions & 0 deletions ossl/src/fips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,18 @@ fn sigalg_to_legacy_name(alg: SigAlg) -> &'static CStr {
| SigAlg::RsaPssSha3_384
| SigAlg::RsaPssSha3_512 => c"RSA",
SigAlg::Mldsa44 | SigAlg::Mldsa65 | SigAlg::Mldsa87 => c"",
SigAlg::SlhdsaSha2_128s
| SigAlg::SlhdsaShake128s
| SigAlg::SlhdsaSha2_128f
| SigAlg::SlhdsaShake128f
| SigAlg::SlhdsaSha2_192s
| SigAlg::SlhdsaShake192s
| SigAlg::SlhdsaSha2_192f
| SigAlg::SlhdsaShake192f
| SigAlg::SlhdsaSha2_256s
| SigAlg::SlhdsaShake256s
| SigAlg::SlhdsaSha2_256f
| SigAlg::SlhdsaShake256f => c"",
}
}

Expand Down
142 changes: 141 additions & 1 deletion ossl/src/pkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,27 @@ pub enum EvpPkeyType {
BrainpoolP256r1,
BrainpoolP384r1,
BrainpoolP512r1,
/* ML */
/* ML-DSA */
Mldsa44,
Mldsa65,
Mldsa87,
/* ML-KEM */
MlKem512,
MlKem768,
MlKem1024,
/* SLH-DSA */
SlhdsaSha2_128s,
SlhdsaShake128s,
SlhdsaSha2_128f,
SlhdsaShake128f,
SlhdsaSha2_192s,
SlhdsaShake192s,
SlhdsaSha2_192f,
SlhdsaShake192f,
SlhdsaSha2_256s,
SlhdsaShake256s,
SlhdsaSha2_256f,
SlhdsaShake256f,
/* RSA */
Rsa(usize, Vec<u8>),
}
Expand Down Expand Up @@ -239,6 +253,18 @@ fn pkey_type_to_params(
EvpPkeyType::MlKem512 => c"ML-KEM-512",
EvpPkeyType::MlKem768 => c"ML-KEM-768",
EvpPkeyType::MlKem1024 => c"ML-KEM-1024",
EvpPkeyType::SlhdsaSha2_128f => c"SLH-DSA-SHA2-128f",
EvpPkeyType::SlhdsaSha2_128s => c"SLH-DSA-SHA2-128s",
EvpPkeyType::SlhdsaSha2_192f => c"SLH-DSA-SHA2-192f",
EvpPkeyType::SlhdsaSha2_192s => c"SLH-DSA-SHA2-192s",
EvpPkeyType::SlhdsaSha2_256f => c"SLH-DSA-SHA2-256f",
EvpPkeyType::SlhdsaSha2_256s => c"SLH-DSA-SHA2-256s",
EvpPkeyType::SlhdsaShake128f => c"SLH-DSA-SHAKE-128f",
EvpPkeyType::SlhdsaShake128s => c"SLH-DSA-SHAKE-128s",
EvpPkeyType::SlhdsaShake192f => c"SLH-DSA-SHAKE-192f",
EvpPkeyType::SlhdsaShake192s => c"SLH-DSA-SHAKE-192s",
EvpPkeyType::SlhdsaShake256f => c"SLH-DSA-SHAKE-256f",
EvpPkeyType::SlhdsaShake256s => c"SLH-DSA-SHAKE-256s",
EvpPkeyType::Rsa(size, exp) => {
params.add_bn(cstr!(OSSL_PKEY_PARAM_RSA_E), &exp)?;
params.add_owned_uint(
Expand Down Expand Up @@ -308,6 +334,18 @@ fn pkey_to_type(
b"ML-KEM-512" => Ok(EvpPkeyType::MlKem512),
b"ML-KEM-768" => Ok(EvpPkeyType::MlKem768),
b"ML-KEM-1024" => Ok(EvpPkeyType::MlKem1024),
b"SLH-DSA-SHA2-128f" => Ok(EvpPkeyType::SlhdsaSha2_128f),
b"SLH-DSA-SHA2-128s" => Ok(EvpPkeyType::SlhdsaSha2_128s),
b"SLH-DSA-SHA2-192f" => Ok(EvpPkeyType::SlhdsaSha2_192f),
b"SLH-DSA-SHA2-192s" => Ok(EvpPkeyType::SlhdsaSha2_192s),
b"SLH-DSA-SHA2-256f" => Ok(EvpPkeyType::SlhdsaSha2_256f),
b"SLH-DSA-SHA2-256s" => Ok(EvpPkeyType::SlhdsaSha2_256s),
b"SLH-DSA-SHAKE-128f" => Ok(EvpPkeyType::SlhdsaShake128f),
b"SLH-DSA-SHAKE-128s" => Ok(EvpPkeyType::SlhdsaShake128s),
b"SLH-DSA-SHAKE-192f" => Ok(EvpPkeyType::SlhdsaShake192f),
b"SLH-DSA-SHAKE-192s" => Ok(EvpPkeyType::SlhdsaShake192s),
b"SLH-DSA-SHAKE-256f" => Ok(EvpPkeyType::SlhdsaShake256f),
b"SLH-DSA-SHAKE-256s" => Ok(EvpPkeyType::SlhdsaShake256s),
b"RSA" => {
let e = params.get_bn(cstr!(OSSL_PKEY_PARAM_RSA_E))?;
let n = params.get_bn(cstr!(OSSL_PKEY_PARAM_RSA_N))?;
Expand Down Expand Up @@ -370,6 +408,23 @@ impl Drop for MlkeyData {
}
}

/// Structure that holds SLH-DSA Keys data
#[derive(Debug)]
pub struct SlhDsaKeyData {
pub pubkey: Option<Vec<u8>>,
pub prikey: Option<OsslSecret>,
}

impl Drop for SlhDsaKeyData {
fn drop(&mut self) {
if let Some(mut v) = self.pubkey.take() {
unsafe {
OPENSSL_cleanse(v.as_mut_ptr() as *mut _, v.len());
}
}
}
}

/// Structure that holds RSA key data
#[derive(Debug)]
pub struct RsaData {
Expand Down Expand Up @@ -591,6 +646,7 @@ pub enum PkeyData {
Ecc(EccData),
Ffdh(FfdhData),
Mlkey(MlkeyData),
SlhDsaKey(SlhDsaKeyData),
Rsa(RsaData),
}

Expand Down Expand Up @@ -666,6 +722,42 @@ fn params_to_mlkem_data(params: &OsslParam) -> Result<PkeyData, Error> {
}))
}

#[cfg(ossl_slhdsa)]
fn params_to_slhdsa_data(
pkey: &EvpPkey,
params: &OsslParam,
) -> Result<PkeyData, Error> {
Ok(PkeyData::SlhDsaKey(SlhDsaKeyData {
pubkey: match params.get_octet_string(cstr!(OSSL_PKEY_PARAM_PUB_KEY)) {
Ok(p) => Some(p.to_vec()),
Err(e) => match e.kind() {
ErrorKind::NullPtr => {
// OpenSSL does not always provide public key when
// asked for key pair here so if it not available,
// retry exporting just public key part
// https://github.com/openssl/openssl/issues/27542
let p2 = pkey.export_params(EVP_PKEY_PUBLIC_KEY)?;
match p2.get_octet_string(cstr!(OSSL_PKEY_PARAM_PUB_KEY)) {
Ok(p) => Some(p.to_vec()),
Err(e) => match e.kind() {
ErrorKind::NullPtr => None,
_ => return Err(e),
},
}
}
_ => return Err(e),
},
},
prikey: match params.get_octet_string(cstr!(OSSL_PKEY_PARAM_PRIV_KEY)) {
Ok(p) => Some(OsslSecret::from_slice(p)),
Err(e) => match e.kind() {
ErrorKind::NullPtr => None,
_ => return Err(e),
},
},
}))
}

fn params_to_rsa_data(params: &OsslParam) -> Result<PkeyData, Error> {
Ok(PkeyData::Rsa(RsaData {
n: params.get_bn(cstr!(OSSL_PKEY_PARAM_RSA_N))?,
Expand Down Expand Up @@ -1071,6 +1163,37 @@ impl EvpPkey {
}
_ => return Err(Error::new(ErrorKind::WrapperError)),
},
EvpPkeyType::SlhdsaSha2_128s
| EvpPkeyType::SlhdsaShake128s
| EvpPkeyType::SlhdsaSha2_128f
| EvpPkeyType::SlhdsaShake128f
| EvpPkeyType::SlhdsaSha2_192s
| EvpPkeyType::SlhdsaShake192s
| EvpPkeyType::SlhdsaSha2_192f
| EvpPkeyType::SlhdsaShake192f
| EvpPkeyType::SlhdsaSha2_256s
| EvpPkeyType::SlhdsaShake256s
| EvpPkeyType::SlhdsaSha2_256f
| EvpPkeyType::SlhdsaShake256f => match &data {
#[cfg(ossl_slhdsa)]
PkeyData::SlhDsaKey(sdk) => {
if let Some(p) = &sdk.pubkey {
pkey_class |= EVP_PKEY_PUBLIC_KEY;
params_builder.add_octet_slice(
cstr!(OSSL_PKEY_PARAM_PUB_KEY),
p.as_slice(),
)?
}
if let Some(p) = &sdk.prikey {
pkey_class |= EVP_PKEY_PRIVATE_KEY;
params_builder.add_octet_slice(
cstr!(OSSL_PKEY_PARAM_PRIV_KEY),
p,
)?
}
}
_ => return Err(Error::new(ErrorKind::WrapperError)),
},
EvpPkeyType::Rsa(_, _) => match &data {
PkeyData::Rsa(rsa) => {
if rsa_data_to_params(&rsa, &mut params_builder)? {
Expand Down Expand Up @@ -1179,6 +1302,23 @@ impl EvpPkey {
#[cfg(not(ossl_mlkem))]
return Err(Error::new(ErrorKind::WrapperError));
}
EvpPkeyType::SlhdsaSha2_128s
| EvpPkeyType::SlhdsaShake128s
| EvpPkeyType::SlhdsaSha2_128f
| EvpPkeyType::SlhdsaShake128f
| EvpPkeyType::SlhdsaSha2_192s
| EvpPkeyType::SlhdsaShake192s
| EvpPkeyType::SlhdsaSha2_192f
| EvpPkeyType::SlhdsaShake192f
| EvpPkeyType::SlhdsaSha2_256s
| EvpPkeyType::SlhdsaShake256s
| EvpPkeyType::SlhdsaSha2_256f
| EvpPkeyType::SlhdsaShake256f => {
#[cfg(ossl_slhdsa)]
return params_to_slhdsa_data(&self, &params);
#[cfg(not(ossl_slhdsa))]
return Err(Error::new(ErrorKind::WrapperError));
}
EvpPkeyType::Rsa(_, _) => return params_to_rsa_data(&params),
})
}
Expand Down
Loading
Loading