From 364f9f6ef0069e2387d8145f3667c1698ac99741 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 12 Feb 2026 10:28:14 -0500 Subject: [PATCH] Expose single-part crypto operations Refactor encryption, decryption, signing, verification, and digesting operations to separate the initialization logic from the single-part execution logic. This exposes `*_single` methods publicly, allowing greater flexibility in operation flows. For example, this supports context-specific user authentication where the operation must be initialized before the authentication step occurs. Co-authored-by: Gemini Signed-off-by: Simo Sorce --- cryptoki/src/session/decryption.rs | 23 ++--- cryptoki/src/session/digesting.rs | 24 ++--- cryptoki/src/session/encryption.rs | 23 ++--- cryptoki/src/session/signing_macing.rs | 45 ++++----- cryptoki/tests/basic.rs | 123 +++++++++++++++++++++++++ 5 files changed, 183 insertions(+), 55 deletions(-) diff --git a/cryptoki/src/session/decryption.rs b/cryptoki/src/session/decryption.rs index 73e7aab4..05202884 100644 --- a/cryptoki/src/session/decryption.rs +++ b/cryptoki/src/session/decryption.rs @@ -18,17 +18,18 @@ impl Session { key: ObjectHandle, encrypted_data: &[u8], ) -> Result> { - let mut mechanism: CK_MECHANISM = mechanism.into(); - let mut data_len = 0; + self.decrypt_init(mechanism, key)?; + self.decrypt_single(encrypted_data) + } - unsafe { - Rv::from(get_pkcs11!(self.client(), C_DecryptInit)( - self.handle(), - &mut mechanism as CK_MECHANISM_PTR, - key.handle(), - )) - .into_result(Function::DecryptInit)?; - } + /// Single-part decryption operation. + /// + /// This function can be used instead of the single shot related decrypt + /// function when the user needs to perform something like + /// context-specific user authentication after [`Session::decrypt_init`] + /// is called. + pub fn decrypt_single(&self, encrypted_data: &[u8]) -> Result> { + let mut data_len = 0; // Get the output buffer length unsafe { @@ -61,7 +62,7 @@ impl Session { Ok(data) } - /// Starts new multi-part decryption operation + /// Starts new single-part or multi-part decryption operation pub fn decrypt_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { let mut mechanism: CK_MECHANISM = mechanism.into(); diff --git a/cryptoki/src/session/digesting.rs b/cryptoki/src/session/digesting.rs index 615ec89d..8c9c9a52 100644 --- a/cryptoki/src/session/digesting.rs +++ b/cryptoki/src/session/digesting.rs @@ -12,17 +12,19 @@ use std::convert::TryInto; impl Session { /// Single-part digesting operation - pub fn digest(&self, m: &Mechanism, data: &[u8]) -> Result> { - let mut mechanism: CK_MECHANISM = m.into(); - let mut digest_len = 0; + pub fn digest(&self, mechanism: &Mechanism, data: &[u8]) -> Result> { + self.digest_init(mechanism)?; + self.digest_single(data) + } - unsafe { - Rv::from(get_pkcs11!(self.client(), C_DigestInit)( - self.handle(), - &mut mechanism as CK_MECHANISM_PTR, - )) - .into_result(Function::DigestInit)?; - } + /// Digest data in single-part. + /// + /// This function can be used instead of the single shot related digest + /// function when the user needs to perform something like + /// context-specific user authentication after [`Session::digest_init`] + /// is called. + pub fn digest_single(&self, data: &[u8]) -> Result> { + let mut digest_len = 0; // Get the output buffer length unsafe { @@ -54,7 +56,7 @@ impl Session { Ok(digest) } - /// Starts new multi-part digesting operation + /// Starts new single-part or multi-part digesting operation pub fn digest_init(&self, m: &Mechanism) -> Result<()> { let mut mechanism: CK_MECHANISM = m.into(); diff --git a/cryptoki/src/session/encryption.rs b/cryptoki/src/session/encryption.rs index c65a9270..54090f18 100644 --- a/cryptoki/src/session/encryption.rs +++ b/cryptoki/src/session/encryption.rs @@ -18,17 +18,18 @@ impl Session { key: ObjectHandle, data: &[u8], ) -> Result> { - let mut mechanism: CK_MECHANISM = mechanism.into(); - let mut encrypted_data_len = 0; + self.encrypt_init(mechanism, key)?; + self.encrypt_single(data) + } - unsafe { - Rv::from(get_pkcs11!(self.client(), C_EncryptInit)( - self.handle(), - &mut mechanism as CK_MECHANISM_PTR, - key.handle(), - )) - .into_result(Function::EncryptInit)?; - } + /// Single-part encryption operation. + /// + /// This function can be used instead of the single shot related encrypt + /// function when the user needs to perform something like + /// context-specific user authentication after [`Session::encrypt_init`] + /// is called. + pub fn encrypt_single(&self, data: &[u8]) -> Result> { + let mut encrypted_data_len = 0; // Get the output buffer length unsafe { @@ -60,7 +61,7 @@ impl Session { Ok(encrypted_data) } - /// Starts new multi-part encryption operation + /// Starts new single-part or multi-part encryption operation pub fn encrypt_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { let mut mechanism: CK_MECHANISM = mechanism.into(); diff --git a/cryptoki/src/session/signing_macing.rs b/cryptoki/src/session/signing_macing.rs index 0927c5c4..fbc1af44 100644 --- a/cryptoki/src/session/signing_macing.rs +++ b/cryptoki/src/session/signing_macing.rs @@ -13,17 +13,18 @@ use std::convert::TryInto; impl Session { /// Sign data in single-part pub fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result> { - let mut mechanism: CK_MECHANISM = mechanism.into(); - let mut signature_len = 0; + self.sign_init(mechanism, key)?; + self.sign_single(data) + } - unsafe { - Rv::from(get_pkcs11!(self.client(), C_SignInit)( - self.handle(), - &mut mechanism as CK_MECHANISM_PTR, - key.handle(), - )) - .into_result(Function::SignInit)?; - } + /// Sign data in single-part. + /// + /// This function can be used instead of the single shot related sign + /// function when the user needs to perform something like + /// context-specific user authentication after [`Session::sign_init`] + /// is called. + pub fn sign_single(&self, data: &[u8]) -> Result> { + let mut signature_len = 0; // Get the output buffer length unsafe { @@ -56,7 +57,7 @@ impl Session { Ok(signature) } - /// Starts new multi-part signing operation + /// Starts new single-part or multi-part signing operation pub fn sign_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { let mut mechanism: CK_MECHANISM = mechanism.into(); @@ -126,17 +127,17 @@ impl Session { data: &[u8], signature: &[u8], ) -> Result<()> { - let mut mechanism: CK_MECHANISM = mechanism.into(); - - unsafe { - Rv::from(get_pkcs11!(self.client(), C_VerifyInit)( - self.handle(), - &mut mechanism as CK_MECHANISM_PTR, - key.handle(), - )) - .into_result(Function::VerifyInit)?; - } + self.verify_init(mechanism, key)?; + self.verify_single(data, signature) + } + /// Verify data in single-part. + /// + /// This function can be used instead of the single shot related verify + /// function when the user needs to perform something like + /// context-specific user authentication after [`Session::verify_init`] + /// is called. + pub fn verify_single(&self, data: &[u8], signature: &[u8]) -> Result<()> { unsafe { Rv::from(get_pkcs11!(self.client(), C_Verify)( self.handle(), @@ -149,7 +150,7 @@ impl Session { } } - /// Starts new multi-part verifying operation + /// Starts new single-part or multi-part verifying operation pub fn verify_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { let mut mechanism: CK_MECHANISM = mechanism.into(); diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 15cf938e..8eaea028 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -239,6 +239,56 @@ fn sign_verify_eddsa_with_ed448_schemes() -> TestResult { Ok(()) } +#[test] +#[serial] +fn sign_verify_single_part() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Define parameters for keypair + let public_exponent = vec![0x01, 0x00, 0x01]; + let modulus_bits = 2048; + + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::PublicExponent(public_exponent), + Attribute::ModulusBits(modulus_bits.into()), + Attribute::Verify(true), + ]; + let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)]; + + // Generate keypair + let (pub_key, priv_key) = session.generate_key_pair( + &Mechanism::RsaPkcsKeyPairGen, + &pub_key_template, + &priv_key_template, + )?; + + // Data to sign + let data = [0xFF, 0x55, 0xDD, 0x11, 0xBB, 0x33]; + + // Sign data in a single part using separate init and single call + session.sign_init(&Mechanism::Sha256RsaPkcs, priv_key)?; + let signature = session.sign_single(&data)?; + + // Verify signature in a single part using separate init and single call + session.verify_init(&Mechanism::Sha256RsaPkcs, pub_key)?; + session.verify_single(&data, &signature)?; + + // Delete keys + session.destroy_object(pub_key)?; + session.destroy_object(priv_key)?; + + session.close()?; + pkcs11.finalize()?; + + Ok(()) +} + #[test] #[serial] fn sign_verify_multipart() -> TestResult { @@ -480,6 +530,50 @@ fn encrypt_decrypt() -> TestResult { Ok(()) } +#[test] +#[serial] +fn encrypt_decrypt_single_part() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_rw_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Generate key + let template = vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::ValueLen(AES128_BLOCK_SIZE), + Attribute::Encrypt(true), + Attribute::Decrypt(true), + ]; + let key = session.generate_key(&Mechanism::AesKeyGen, &template)?; + + // Data to encrypt + let data = vec![ + 0xFF, 0x55, 0xDD, 0x11, 0xBB, 0x33, 0x99, 0x77, 0xFF, 0x55, 0xDD, 0x11, 0xBB, 0x33, 0x99, + 0x77, + ]; + + // Encrypt data in a single part using separate init and single call + session.encrypt_init(&Mechanism::AesEcb, key)?; + let encrypted_data = session.encrypt_single(&data)?; + + // Decrypt data in a single part using separate init and single call + session.decrypt_init(&Mechanism::AesEcb, key)?; + let decrypted_data = session.decrypt_single(&encrypted_data)?; + + assert_eq!(data, decrypted_data); + + // Delete key + session.destroy_object(key)?; + + session.close()?; + pkcs11.finalize()?; + + Ok(()) +} + #[test] #[serial] fn encrypt_decrypt_multipart() -> TestResult { @@ -2277,6 +2371,35 @@ fn sha256_digest() -> TestResult { Ok(()) } +#[test] +#[serial] +fn sha256_digest_single_part() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // data to digest + let data = vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]; + + let want = [ + 0x17, 0x22, 0x6b, 0x1f, 0x68, 0xae, 0xba, 0xcd, 0xef, 0x07, 0x46, 0x45, 0x0f, 0x64, 0x28, + 0x74, 0x63, 0x8b, 0x29, 0x57, 0x07, 0xef, 0x73, 0xfb, 0x2c, 0x6b, 0xb7, 0xf8, 0x8e, 0x89, + 0x92, 0x9f, + ]; + session.digest_init(&Mechanism::Sha256)?; + let have = session.digest_single(&data)?; + assert_eq!(want[..], have[..]); + + session.close()?; + pkcs11.finalize()?; + + Ok(()) +} + #[test] #[serial] fn sha256_digest_multipart() -> TestResult {