From 41ec7aff2385e48c755f3f804f8e666960f80520 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Thu, 27 Mar 2025 17:19:00 +0100 Subject: [PATCH 01/10] Use TEST_PKCS11_MODULE environment variable to specify test token Signed-off-by: Jakub Jelen --- .github/actions/ci_script/action.yml | 2 +- .github/workflows/nightly.yml | 2 +- cryptoki/README.md | 4 ++-- cryptoki/examples/generate_key_pair.rs | 2 +- cryptoki/src/context/session_management.rs | 2 +- cryptoki/src/session/object_management.rs | 6 +++--- cryptoki/tests/common.rs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/actions/ci_script/action.yml b/.github/actions/ci_script/action.yml index 679471bf..88bb7297 100644 --- a/.github/actions/ci_script/action.yml +++ b/.github/actions/ci_script/action.yml @@ -30,7 +30,7 @@ runs: - name: Test script env: - PKCS11_SOFTHSM2_MODULE: /usr/lib/softhsm/libsofthsm2.so + TEST_PKCS11_MODULE: /usr/lib/softhsm/libsofthsm2.so SOFTHSM2_CONF: /tmp/softhsm2.conf run: ./ci.sh shell: bash diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b6da54e9..64c5187d 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -62,7 +62,7 @@ jobs: - name: Test script env: - PKCS11_SOFTHSM2_MODULE: /usr/lib/softhsm/libsofthsm2.so + TEST_PKCS11_MODULE: /usr/lib/softhsm/libsofthsm2.so SOFTHSM2_CONF: /tmp/softhsm2.conf run: | rm Cargo.lock diff --git a/cryptoki/README.md b/cryptoki/README.md index 0e1c136d..77f998d5 100644 --- a/cryptoki/README.md +++ b/cryptoki/README.md @@ -22,7 +22,7 @@ You can follow the installation steps directly in the repository's README but he sudo apt install libsofthsm2 mkdir /tmp/tokens echo "directories.tokendir = /tmp/tokens" > /tmp/softhsm2.conf -export PKCS11_SOFTHSM2_MODULE="/usr/lib/softhsm/libsofthsm2.so" +export TEST_PKCS11_MODULE="/usr/lib/softhsm/libsofthsm2.so" export SOFTHSM2_CONF="/tmp/softhsm2.conf" cargo run --example generate_key_pair ``` @@ -43,7 +43,7 @@ use std::env; // initialize a new Pkcs11 object using the module from the env variable let pkcs11 = Pkcs11::new( - env::var("PKCS11_SOFTHSM2_MODULE") + env::var("TEST_PKCS11_MODULE") .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), )?; diff --git a/cryptoki/examples/generate_key_pair.rs b/cryptoki/examples/generate_key_pair.rs index 6c0b785e..87d9bf49 100644 --- a/cryptoki/examples/generate_key_pair.rs +++ b/cryptoki/examples/generate_key_pair.rs @@ -15,7 +15,7 @@ pub static SO_PIN: &str = "abcdef"; fn main() -> testresult::TestResult { // initialize a new Pkcs11 object using the module from the env variable let pkcs11 = Pkcs11::new( - env::var("PKCS11_SOFTHSM2_MODULE") + env::var("TEST_PKCS11_MODULE") .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), )?; diff --git a/cryptoki/src/context/session_management.rs b/cryptoki/src/context/session_management.rs index ee393039..382c45da 100644 --- a/cryptoki/src/context/session_management.rs +++ b/cryptoki/src/context/session_management.rs @@ -50,7 +50,7 @@ impl Pkcs11 { /// use cryptoki::context::Pkcs11; /// /// let mut client = Pkcs11::new( - /// std::env::var("PKCS11_SOFTHSM2_MODULE") + /// std::env::var("TEST_PKCS11_MODULE") /// .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), /// )?; /// client.initialize(cryptoki::context::CInitializeArgs::OsThreads)?; diff --git a/cryptoki/src/session/object_management.rs b/cryptoki/src/session/object_management.rs index 4c676ded..b40533c8 100644 --- a/cryptoki/src/session/object_management.rs +++ b/cryptoki/src/session/object_management.rs @@ -39,7 +39,7 @@ const MAX_OBJECT_COUNT: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) /// /// # fn main() -> testresult::TestResult { /// # let pkcs11 = Pkcs11::new( -/// # env::var("PKCS11_SOFTHSM2_MODULE") +/// # env::var("TEST_PKCS11_MODULE") /// # .unwrap_or_else(|_| "/usr/local/lib/libsofthsm2.so".to_string()), /// # )?; /// # @@ -282,7 +282,7 @@ impl Session { /// # use cryptoki::object::{Attribute, AttributeType, CertificateType, ObjectClass, ObjectHandle}; /// # /// # let mut client = Pkcs11::new( - /// # std::env::var("PKCS11_SOFTHSM2_MODULE") + /// # std::env::var("TEST_PKCS11_MODULE") /// # .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), /// # )?; /// # client.initialize(cryptoki::context::CInitializeArgs::OsThreads)?; @@ -401,7 +401,7 @@ impl Session { /// use std::env; /// /// let mut pkcs11 = Pkcs11::new( - /// env::var("PKCS11_SOFTHSM2_MODULE") + /// env::var("TEST_PKCS11_MODULE") /// .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), /// ) /// .unwrap(); diff --git a/cryptoki/tests/common.rs b/cryptoki/tests/common.rs index 95e4202d..1662a427 100644 --- a/cryptoki/tests/common.rs +++ b/cryptoki/tests/common.rs @@ -13,7 +13,7 @@ pub static SO_PIN: &str = "abcdef"; pub fn get_pkcs11() -> Pkcs11 { Pkcs11::new( - env::var("PKCS11_SOFTHSM2_MODULE") + env::var("TEST_PKCS11_MODULE") .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), ) .unwrap() From be7d8c96c81abaaf0ebf3a927710418648da8e16 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Thu, 13 Mar 2025 14:42:22 +0100 Subject: [PATCH 02/10] cryptoki-sys: Expose GetInterface and GetInterfaceList for PKCS#11 3.0 API Signed-off-by: Jakub Jelen --- cryptoki-sys/build.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cryptoki-sys/build.rs b/cryptoki-sys/build.rs index 49148552..582a4b31 100644 --- a/cryptoki-sys/build.rs +++ b/cryptoki-sys/build.rs @@ -91,8 +91,12 @@ mod generate { .dynamic_library_name("Pkcs11") // The PKCS11 library works in a slightly different way to most shared libraries. We have // to call `C_GetFunctionList`, which returns a list of pointers to the _actual_ library - // functions. This is the only function we need to create a binding for. + // functions (in PKCS #11 before 3.0). The PKCS #11 3.0 introduces the new functions + // `C_GetInterface` and `C_GetInterfaceList` to request the hew functions from 3.0 API. + // These are the only function we need to create a binding for. .allowlist_function("C_GetFunctionList") + .allowlist_function("C_GetInterfaceList") + .allowlist_function("C_GetInterface") // This is needed because no types will be generated if `allowlist_function` is used. // Unsure if this is a bug. .allowlist_type("*") From 32f8c33044c3340671ac8fca31f8f16e12350ff5 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Thu, 13 Mar 2025 16:54:24 +0100 Subject: [PATCH 03/10] ci: Run tests against kryoptic software token in Fedora Signed-off-by: Jakub Jelen --- .github/workflows/ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e2e4a56..3448729d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,24 @@ jobs: - name: "Installs SoftHSM and execute tests" uses: ./.github/actions/ci_script + tests-kryoptic: + name: Run tests against Kryoptic + runs-on: ubuntu-latest + container: fedora:rawhide + steps: + - name: Install dependencies + run: dnf -y install git cargo clang-devel kryoptic + - uses: actions/checkout@v2 + - name: Test script + env: + KRYOPTIC_CONF: /tmp/kryoptic.sql + TEST_PKCS11_MODULE: /usr/lib64/pkcs11/libkryoptic_pkcs11.so + run: | + RUST_BACKTRACE=1 cargo build && + RUST_BACKTRACE=1 cargo build --all-features && + RUST_BACKTRACE=1 cargo test + + build-msrv: name: MSRV - Execute CI script runs-on: ubuntu-latest From b8ba7b1a88a38622935b37e430f2aa6a992636ef Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Fri, 14 Mar 2025 14:57:48 +0100 Subject: [PATCH 04/10] objects: Do not attempt to alloc memory for unavailable attributes Signed-off-by: Jakub Jelen --- cryptoki/src/object.rs | 2 ++ cryptoki/src/session/object_management.rs | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cryptoki/src/object.rs b/cryptoki/src/object.rs index abdefd0a..9f159236 100644 --- a/cryptoki/src/object.rs +++ b/cryptoki/src/object.rs @@ -1378,6 +1378,8 @@ pub enum AttributeInfo { Sensitive, /// The attribute is available to get from the object and has the specified size in bytes. Available(usize), + /// The attribute is not available. + Unavailable, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/cryptoki/src/session/object_management.rs b/cryptoki/src/session/object_management.rs index b40533c8..77de95d4 100644 --- a/cryptoki/src/session/object_management.rs +++ b/cryptoki/src/session/object_management.rs @@ -454,7 +454,11 @@ impl Session { )) } { Rv::Ok => { - results.push(AttributeInfo::Available(template[0].ulValueLen.try_into()?)) + if template[0].ulValueLen == CK_UNAVAILABLE_INFORMATION { + results.push(AttributeInfo::Unavailable) + } else { + results.push(AttributeInfo::Available(template[0].ulValueLen.try_into()?)) + } } Rv::Error(RvError::AttributeSensitive) => results.push(AttributeInfo::Sensitive), Rv::Error(RvError::AttributeTypeInvalid) => { From 51a9e989d9caa466735f3d7c1ac06ab180a8a02e Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 17 Mar 2025 13:41:54 +0100 Subject: [PATCH 05/10] tests: Add missing usage attributes Signed-off-by: Jakub Jelen --- cryptoki/tests/basic.rs | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index c32b14c7..f0fdf281 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -46,10 +46,11 @@ fn sign_verify() -> TestResult { Attribute::Private(false), Attribute::PublicExponent(public_exponent), Attribute::ModulusBits(modulus_bits.into()), + Attribute::Verify(true), ]; // priv key template - let priv_key_template = vec![Attribute::Token(true)]; + let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)]; // generate a key pair let (public, private) = @@ -93,7 +94,7 @@ fn sign_verify_eddsa() -> TestResult { ]), ]; - let priv_key_template = vec![Attribute::Token(true)]; + let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)]; let (public, private) = session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; @@ -136,7 +137,7 @@ fn sign_verify_eddsa_with_ed25519_schemes() -> TestResult { ]), ]; - let priv_key_template = vec![Attribute::Token(true)]; + let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)]; let (public, private) = session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; @@ -186,7 +187,7 @@ fn sign_verify_eddsa_with_ed448_schemes() -> TestResult { ]), ]; - let priv_key_template = vec![Attribute::Token(true)]; + let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)]; let (public, private) = session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; @@ -1353,9 +1354,16 @@ fn rsa_pkcs_oaep_empty() -> TestResult { let session = pkcs11.open_rw_session(slot)?; session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; - let pub_key_template = [Attribute::ModulusBits(2048.into())]; - let (pubkey, privkey) = - session.generate_key_pair(&Mechanism::RsaPkcsKeyPairGen, &pub_key_template, &[])?; + let pub_key_template = [ + Attribute::ModulusBits(2048.into()), + Attribute::Encrypt(true), + ]; + let priv_key_template = [Attribute::Decrypt(true)]; + let (pubkey, privkey) = session.generate_key_pair( + &Mechanism::RsaPkcsKeyPairGen, + &pub_key_template, + &priv_key_template, + )?; let oaep = PkcsOaepParams::new( MechanismType::SHA1, PkcsMgfType::MGF1_SHA1, @@ -1380,9 +1388,16 @@ fn rsa_pkcs_oaep_with_data() -> TestResult { let session = pkcs11.open_rw_session(slot)?; session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; - let pub_key_template = [Attribute::ModulusBits(2048.into())]; - let (pubkey, privkey) = - session.generate_key_pair(&Mechanism::RsaPkcsKeyPairGen, &pub_key_template, &[])?; + let pub_key_template = [ + Attribute::ModulusBits(2048.into()), + Attribute::Encrypt(true), + ]; + let priv_key_template = vec![Attribute::Decrypt(true)]; + let (pubkey, privkey) = session.generate_key_pair( + &Mechanism::RsaPkcsKeyPairGen, + &pub_key_template, + &priv_key_template, + )?; let oaep = PkcsOaepParams::new( MechanismType::SHA1, PkcsMgfType::MGF1_SHA1, @@ -1523,6 +1538,7 @@ fn sign_verify_sha1_hmac() -> TestResult { Attribute::Private(true), Attribute::Sensitive(true), Attribute::Sign(true), + Attribute::Verify(true), Attribute::KeyType(KeyType::GENERIC_SECRET), Attribute::Class(ObjectClass::SECRET_KEY), Attribute::ValueLen(256.into()), @@ -1552,6 +1568,7 @@ fn sign_verify_sha224_hmac() -> TestResult { Attribute::Private(true), Attribute::Sensitive(true), Attribute::Sign(true), + Attribute::Verify(true), Attribute::KeyType(KeyType::GENERIC_SECRET), Attribute::Class(ObjectClass::SECRET_KEY), Attribute::ValueLen(256.into()), @@ -1581,6 +1598,7 @@ fn sign_verify_sha256_hmac() -> TestResult { Attribute::Private(true), Attribute::Sensitive(true), Attribute::Sign(true), + Attribute::Verify(true), Attribute::KeyType(KeyType::GENERIC_SECRET), Attribute::Class(ObjectClass::SECRET_KEY), Attribute::ValueLen(256.into()), @@ -1610,6 +1628,7 @@ fn sign_verify_sha384_hmac() -> TestResult { Attribute::Private(true), Attribute::Sensitive(true), Attribute::Sign(true), + Attribute::Verify(true), Attribute::KeyType(KeyType::GENERIC_SECRET), Attribute::Class(ObjectClass::SECRET_KEY), Attribute::ValueLen(256.into()), @@ -1639,6 +1658,7 @@ fn sign_verify_sha512_hmac() -> TestResult { Attribute::Private(true), Attribute::Sensitive(true), Attribute::Sign(true), + Attribute::Verify(true), Attribute::KeyType(KeyType::GENERIC_SECRET), Attribute::Class(ObjectClass::SECRET_KEY), Attribute::ValueLen(256.into()), From a6ec11a9432d8b4cd7aa9f81839a824462be9ed6 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 17 Mar 2025 16:03:20 +0100 Subject: [PATCH 06/10] tests: Generate AES keys instead of DES3 The DES3 is not usable for anything and modern pkcs11 modules do not implement it. AES is much widely implemented. Signed-off-by: Jakub Jelen --- cryptoki/tests/basic.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index f0fdf281..08fe2ccd 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -437,12 +437,13 @@ fn session_find_objects() -> testresult::TestResult { Attribute::Token(true), Attribute::Encrypt(true), Attribute::Label(format!("key_{}", i).as_bytes().to_vec()), + Attribute::ValueLen(32.into()), Attribute::Id("12345678".as_bytes().to_vec()), // reusing the same CKA_ID ]; // generate a secret key let _key = session - .generate_key(&Mechanism::Des3KeyGen, &key_template) + .generate_key(&Mechanism::AesKeyGen, &key_template) .unwrap(); }); @@ -451,7 +452,7 @@ fn session_find_objects() -> testresult::TestResult { Attribute::Token(true), Attribute::Id("12345678".as_bytes().to_vec()), Attribute::Class(ObjectClass::SECRET_KEY), - Attribute::KeyType(KeyType::DES3), + Attribute::KeyType(KeyType::AES), ]; let mut found_keys = session.find_objects(&key_search_template)?; @@ -486,12 +487,13 @@ fn session_objecthandle_iterator() -> testresult::TestResult { let key_template = vec![ Attribute::Token(true), Attribute::Encrypt(true), + Attribute::ValueLen(32.into()), Attribute::Label(format!("key_{}", i).as_bytes().to_vec()), Attribute::Id("12345678".as_bytes().to_vec()), // reusing the same CKA_ID ]; // generate a secret key - session.generate_key(&Mechanism::Des3KeyGen, &key_template)?; + session.generate_key(&Mechanism::AesKeyGen, &key_template)?; } // retrieve these keys using this template @@ -499,7 +501,7 @@ fn session_objecthandle_iterator() -> testresult::TestResult { Attribute::Token(true), Attribute::Id("12345678".as_bytes().to_vec()), Attribute::Class(ObjectClass::SECRET_KEY), - Attribute::KeyType(KeyType::DES3), + Attribute::KeyType(KeyType::AES), ]; // test iter_objects_with_cache_size() @@ -575,6 +577,7 @@ fn wrap_and_unwrap_key() { let key_to_be_wrapped_template = vec![ Attribute::Token(true), + Attribute::ValueLen(32.into()), // the key needs to be extractable to be suitable for being wrapped Attribute::Extractable(true), Attribute::Encrypt(true), @@ -582,16 +585,15 @@ fn wrap_and_unwrap_key() { // generate a secret key that will be wrapped let key_to_be_wrapped = session - .generate_key(&Mechanism::Des3KeyGen, &key_to_be_wrapped_template) + .generate_key(&Mechanism::AesKeyGen, &key_to_be_wrapped_template) .unwrap(); - // Des3Ecb input length must be a multiple of 8 - // see: PKCS#11 spec Table 10-10, DES-ECB Key And Data Length Constraints + // AesEcb input length must be a multiple of 16 let encrypted_with_original = session .encrypt( - &Mechanism::Des3Ecb, + &Mechanism::AesEcb, key_to_be_wrapped, - &[1, 2, 3, 4, 5, 6, 7, 8], + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], ) .unwrap(); @@ -631,16 +633,16 @@ fn wrap_and_unwrap_key() { Attribute::Private(true), Attribute::Encrypt(true), Attribute::Class(ObjectClass::SECRET_KEY), - Attribute::KeyType(KeyType::DES3), + Attribute::KeyType(KeyType::AES), ], ) .unwrap(); let encrypted_with_unwrapped = session .encrypt( - &Mechanism::Des3Ecb, + &Mechanism::AesEcb, unwrapped_key, - &[1, 2, 3, 4, 5, 6, 7, 8], + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], ) .unwrap(); assert_eq!(encrypted_with_original, encrypted_with_unwrapped); From 2107ae671eb7a3964ba6d003f0da1b3a1324edea Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 17 Mar 2025 16:16:04 +0100 Subject: [PATCH 07/10] context: Try PKCS #11 3.x API to get new interfaces first Fixes: #209 Signed-off-by: Jakub Jelen --- cryptoki/src/context/general_purpose.rs | 61 +++++++++++++++++++++++++ cryptoki/src/context/mod.rs | 37 +++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/cryptoki/src/context/general_purpose.rs b/cryptoki/src/context/general_purpose.rs index a4114371..918f85ad 100644 --- a/cryptoki/src/context/general_purpose.rs +++ b/cryptoki/src/context/general_purpose.rs @@ -44,6 +44,17 @@ macro_rules! check_fn { }}; } +macro_rules! check_30_fn { + ($pkcs11:expr, $func_name:ident) => {{ + let func = paste! { $pkcs11 + .impl_ + .function_list_30 + .map(|f| f.[]) + }; + func.is_some() + }}; +} + #[allow(missing_docs)] #[derive(Debug, Copy, Clone)] /// Enumeration of all functions defined by the PKCS11 spec @@ -116,6 +127,31 @@ pub enum Function { GetFunctionStatus, CancelFunction, WaitForSlotEvent, + /* PKCS #11 3.0 */ + GetInterfaceList, + GetInterface, + LoginUser, + SessionCancel, + MessageEncryptInit, + EncryptMessage, + EncryptMessageBegin, + EncryptMessageNext, + MessageEncryptFinal, + MessageDecryptInit, + DecryptMessage, + DecryptMessageBegin, + DecryptMessageNext, + MessageDecryptFinal, + MessageSignInit, + SignMessage, + SignMessageBegin, + SignMessageNext, + MessageSignFinal, + MessageVerifyInit, + VerifyMessage, + VerifyMessageBegin, + VerifyMessageNext, + MessageVerifyFinal, } impl Display for Function { @@ -195,5 +231,30 @@ pub(super) fn is_fn_supported(ctx: &Pkcs11, function: Function) -> bool { Function::GetFunctionStatus => check_fn!(ctx, GetFunctionStatus), Function::CancelFunction => check_fn!(ctx, CancelFunction), Function::WaitForSlotEvent => check_fn!(ctx, WaitForSlotEvent), + /* PKCS #11 3.0 */ + Function::GetInterfaceList => check_30_fn!(ctx, GetInterfaceList), + Function::GetInterface => check_30_fn!(ctx, GetInterface), + Function::LoginUser => check_30_fn!(ctx, LoginUser), + Function::SessionCancel => check_30_fn!(ctx, SessionCancel), + Function::MessageEncryptInit => check_30_fn!(ctx, MessageEncryptInit), + Function::EncryptMessage => check_30_fn!(ctx, EncryptMessage), + Function::EncryptMessageBegin => check_30_fn!(ctx, EncryptMessageBegin), + Function::EncryptMessageNext => check_30_fn!(ctx, EncryptMessageNext), + Function::MessageEncryptFinal => check_30_fn!(ctx, MessageEncryptFinal), + Function::MessageDecryptInit => check_30_fn!(ctx, MessageDecryptInit), + Function::DecryptMessage => check_30_fn!(ctx, DecryptMessage), + Function::DecryptMessageBegin => check_30_fn!(ctx, DecryptMessageBegin), + Function::DecryptMessageNext => check_30_fn!(ctx, DecryptMessageNext), + Function::MessageDecryptFinal => check_30_fn!(ctx, MessageDecryptFinal), + Function::MessageSignInit => check_30_fn!(ctx, MessageSignInit), + Function::SignMessage => check_30_fn!(ctx, SignMessage), + Function::SignMessageBegin => check_30_fn!(ctx, SignMessageBegin), + Function::SignMessageNext => check_30_fn!(ctx, SignMessageNext), + Function::MessageSignFinal => check_30_fn!(ctx, MessageSignFinal), + Function::MessageVerifyInit => check_30_fn!(ctx, MessageVerifyInit), + Function::VerifyMessage => check_30_fn!(ctx, VerifyMessage), + Function::VerifyMessageBegin => check_30_fn!(ctx, VerifyMessageBegin), + Function::VerifyMessageNext => check_30_fn!(ctx, VerifyMessageNext), + Function::MessageVerifyFinal => check_30_fn!(ctx, MessageVerifyFinal), } } diff --git a/cryptoki/src/context/mod.rs b/cryptoki/src/context/mod.rs index 60f4681a..9527c364 100644 --- a/cryptoki/src/context/mod.rs +++ b/cryptoki/src/context/mod.rs @@ -44,12 +44,14 @@ pub(crate) struct Pkcs11Impl { // valid. _pkcs11_lib: cryptoki_sys::Pkcs11, pub(crate) function_list: cryptoki_sys::CK_FUNCTION_LIST, + pub(crate) function_list_30: Option, } impl fmt::Debug for Pkcs11Impl { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Pkcs11Impl") .field("function_list", &self.function_list) + .field("function_list_30", &self.function_list_30) .finish() } } @@ -111,6 +113,40 @@ impl Pkcs11 { } unsafe fn _new(pkcs11_lib: cryptoki_sys::Pkcs11) -> Result { + /* First try the 3.0 API to get default interface. It might have some more functions than + * the 2.4 API */ + let mut interface = mem::MaybeUninit::uninit(); + if pkcs11_lib.C_GetInterface.is_ok() { + Rv::from(pkcs11_lib.C_GetInterface( + ptr::null_mut(), + ptr::null_mut(), + interface.as_mut_ptr(), + 0, + )) + .into_result(Function::GetInterface)?; + if !interface.as_ptr().is_null() { + let ifce_ptr: *mut cryptoki_sys::CK_INTERFACE = *interface.as_ptr(); + let ifce: cryptoki_sys::CK_INTERFACE = *ifce_ptr; + + let list_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST = + ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST; + let list: cryptoki_sys::CK_FUNCTION_LIST = *list_ptr; + if list.version.major >= 3 { + let list30_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST_3_0 = + ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST_3_0; + return Ok(Pkcs11 { + impl_: Arc::new(Pkcs11Impl { + _pkcs11_lib: pkcs11_lib, + function_list: *list_ptr, /* the function list aliases */ + function_list_30: Some(*list30_ptr), + }), + initialized: Arc::new(RwLock::new(false)), + }); + } + /* fall back to the 2.* API */ + } + } + let mut list = mem::MaybeUninit::uninit(); Rv::from(pkcs11_lib.C_GetFunctionList(list.as_mut_ptr())) @@ -122,6 +158,7 @@ impl Pkcs11 { impl_: Arc::new(Pkcs11Impl { _pkcs11_lib: pkcs11_lib, function_list: *list_ptr, + function_list_30: None, }), initialized: Arc::new(RwLock::new(false)), }) From 311c6a54537ed075bd8a5ece6bd134d85a85c0f0 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Thu, 20 Mar 2025 17:42:05 +0100 Subject: [PATCH 08/10] tests: Test different behavior in different tokens Signed-off-by: Jakub Jelen --- cryptoki/tests/basic.rs | 45 ++++++++++++++++++++++++++++------------ cryptoki/tests/common.rs | 15 +++++++++----- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 08fe2ccd..a32a51c4 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 mod common; -use crate::common::{get_pkcs11, SO_PIN, USER_PIN}; +use crate::common::{get_pkcs11, is_softhsm, SO_PIN, USER_PIN}; use common::init_pins; use cryptoki::context::Function; use cryptoki::error::{Error, RvError}; @@ -411,7 +411,7 @@ fn import_export() -> TestResult { fn get_token_info() -> TestResult { let (pkcs11, slot) = init_pins(); let info = pkcs11.get_token_info(slot)?; - assert_eq!("SoftHSM project", info.manufacturer_id()); + assert_ne!("", info.manufacturer_id()); Ok(()) } @@ -698,9 +698,14 @@ fn get_info_test() -> TestResult { let (pkcs11, _) = init_pins(); let info = pkcs11.get_library_info()?; - assert_eq!(info.cryptoki_version().major(), 2); - assert_eq!(info.cryptoki_version().minor(), 40); - assert_eq!(info.manufacturer_id(), String::from("SoftHSM")); + assert_ne!("", info.manufacturer_id()); + if is_softhsm() { + assert_eq!(info.cryptoki_version().major(), 2); + assert_eq!(info.cryptoki_version().minor(), 40); + } else { + assert_eq!(info.cryptoki_version().major(), 3); + assert_eq!(info.cryptoki_version().minor(), 0); + } Ok(()) } @@ -712,7 +717,7 @@ fn get_slot_info_test() -> TestResult { assert!(slot_info.token_present()); assert!(!slot_info.hardware_slot()); assert!(!slot_info.removable_device()); - assert_eq!(slot_info.manufacturer_id(), String::from("SoftHSM project")); + assert_ne!("", slot_info.manufacturer_id()); Ok(()) } @@ -1286,9 +1291,13 @@ fn gcm_param_graceful_failure() -> TestResult { #[test] #[serial] -// Currently empty AAD crashes SoftHSM, see: https://github.com/opendnssec/SoftHSMv2/issues/605 -#[ignore] fn aes_gcm_no_aad() -> TestResult { + // Currently empty AAD crashes SoftHSM, see: https://github.com/opendnssec/SoftHSMv2/issues/605 + if is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } + // Encrypt two blocks of zeros with AES-128-GCM let key = vec![0; 16]; let mut iv = [0; 12]; @@ -1384,8 +1393,13 @@ fn rsa_pkcs_oaep_empty() -> TestResult { #[test] #[serial] -#[ignore] // it's not clear why the test with data specified fails fn rsa_pkcs_oaep_with_data() -> TestResult { + /* SoftHSM does not support additional OAEP Source */ + if is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } + let (pkcs11, slot) = init_pins(); let session = pkcs11.open_rw_session(slot)?; session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; @@ -1418,11 +1432,16 @@ fn rsa_pkcs_oaep_with_data() -> TestResult { #[test] #[serial] fn get_slot_event() -> TestResult { - // Not implemented in SoftHSMv2 - // https://github.com/opendnssec/SoftHSMv2/issues/370 let (pkcs11, _slot) = init_pins(); - let event = pkcs11.get_slot_event()?; - assert_eq!(None, event); + if is_softhsm() { + // Not implemented in SoftHSMv2 + // https://github.com/opendnssec/SoftHSMv2/issues/370 + let event = pkcs11.get_slot_event()?; + assert_eq!(None, event); + } else { + // Not implemented in Kryoptic + pkcs11.get_slot_event().unwrap_err(); + } Ok(()) } diff --git a/cryptoki/tests/common.rs b/cryptoki/tests/common.rs index 1662a427..65be5c60 100644 --- a/cryptoki/tests/common.rs +++ b/cryptoki/tests/common.rs @@ -11,12 +11,17 @@ pub static USER_PIN: &str = "fedcba"; // The default SO pin pub static SO_PIN: &str = "abcdef"; +fn get_pkcs11_path() -> String { + env::var("TEST_PKCS11_MODULE") + .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()) +} + +pub fn is_softhsm() -> bool { + get_pkcs11_path().contains("softhsm") +} + pub fn get_pkcs11() -> Pkcs11 { - Pkcs11::new( - env::var("TEST_PKCS11_MODULE") - .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), - ) - .unwrap() + Pkcs11::new(get_pkcs11_path()).unwrap() } pub fn init_pins() -> (Pkcs11, Slot) { From d7623ccb380ca346ef5edad20b57e0e8270aceb2 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 24 Mar 2025 15:07:06 +0100 Subject: [PATCH 09/10] test: Skip broken ones in Kryoptic for now Signed-off-by: Jakub Jelen --- cryptoki/tests/basic.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index a32a51c4..6a727fa0 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -268,6 +268,11 @@ fn encrypt_decrypt() -> TestResult { #[test] #[serial] fn derive_key() -> TestResult { + /* FIXME: This is now broken in Kryoptic: https://github.com/latchset/kryoptic/issues/184 */ + if !is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } let (pkcs11, slot) = init_pins(); // open a session @@ -566,6 +571,12 @@ fn session_objecthandle_iterator() -> testresult::TestResult { #[test] #[serial] fn wrap_and_unwrap_key() { + /* FIXME: This is now broken in Kryoptic: https://github.com/latchset/kryoptic/issues/184 */ + if !is_softhsm() { + /* return Ignore(); */ + return; + } + let (pkcs11, slot) = init_pins(); // open a session let session = pkcs11.open_rw_session(slot).unwrap(); @@ -996,6 +1007,11 @@ fn test_clone_initialize() { #[test] #[serial] fn aes_key_attributes_test() -> TestResult { + /* FIXME: This is now broken in Kryoptic: https://github.com/latchset/kryoptic/issues/182 */ + if !is_softhsm() { + /* return Ignore(); */ + return Ok(()); + } let (pkcs11, slot) = init_pins(); // open a session From 72cb497141c78cc24d10e8c722290ce4d7cb03fa Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Tue, 25 Mar 2025 18:44:32 +0100 Subject: [PATCH 10/10] tests: Test coverage for #155 Signed-off-by: Jakub Jelen --- cryptoki/tests/basic.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 6a727fa0..6aa7b31a 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -947,6 +947,18 @@ fn is_fn_supported_test() { pkcs11.is_fn_supported(Function::DigestFinal), "C_DigestFinal function reports as not supported" ); + if is_softhsm() { + // the SoftHSM does not have PKCS#11 3.0 API so this function is not present + assert!( + !pkcs11.is_fn_supported(Function::MessageEncryptInit), + "C_MessageEncryptInit function reports supported for SoftHSM" + ); + } else { + assert!( + pkcs11.is_fn_supported(Function::MessageEncryptInit), + "C_MessageEncryptInit function reports as not supported" + ); + } } #[test]