diff --git a/cryptoki/src/session/decryption.rs b/cryptoki/src/session/decryption.rs index 59745fd8..6d4358df 100644 --- a/cryptoki/src/session/decryption.rs +++ b/cryptoki/src/session/decryption.rs @@ -60,4 +60,84 @@ impl Session { Ok(data) } + + /// Starts new multi-part decryption operation + pub fn decrypt_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { + let mut mechanism: CK_MECHANISM = mechanism.into(); + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DecryptInit)( + self.handle(), + &mut mechanism as CK_MECHANISM_PTR, + key.handle(), + )) + .into_result(Function::DecryptInit)?; + } + + Ok(()) + } + + /// Continues an ongoing multi-part decryption operation, + /// taking in the next part of the encrypted data and returning its decryption + pub fn decrypt_update(&self, encrypted_data: &[u8]) -> Result> { + let mut data_len = 0; + + // Get the output buffer length + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DecryptUpdate)( + self.handle(), + encrypted_data.as_ptr() as *mut u8, + encrypted_data.len().try_into()?, + std::ptr::null_mut(), + &mut data_len, + )) + .into_result(Function::DecryptUpdate)?; + } + + let mut data = vec![0; data_len.try_into()?]; + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DecryptUpdate)( + self.handle(), + encrypted_data.as_ptr() as *mut u8, + encrypted_data.len().try_into()?, + data.as_mut_ptr(), + &mut data_len, + )) + .into_result(Function::DecryptUpdate)?; + } + + Ok(data) + } + + /// Finalizes ongoing multi-part decryption operation, + /// returning any remaining bytes in the decrypted data + pub fn decrypt_final(&self) -> Result> { + let mut data_len = 0; + + // Get the output buffer length + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DecryptFinal)( + self.handle(), + std::ptr::null_mut(), + &mut data_len, + )) + .into_result(Function::DecryptFinal)?; + } + + let mut data = vec![0; data_len.try_into()?]; + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DecryptFinal)( + self.handle(), + data.as_mut_ptr(), + &mut data_len, + )) + .into_result(Function::DecryptFinal)?; + } + + data.resize(data_len.try_into()?, 0); + + Ok(data) + } } diff --git a/cryptoki/src/session/digesting.rs b/cryptoki/src/session/digesting.rs index ba5e7617..fc0974d5 100644 --- a/cryptoki/src/session/digesting.rs +++ b/cryptoki/src/session/digesting.rs @@ -5,6 +5,7 @@ use crate::context::Function; use crate::error::{Result, Rv}; use crate::mechanism::Mechanism; +use crate::object::ObjectHandle; use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; @@ -52,4 +53,79 @@ impl Session { Ok(digest) } + + /// Starts new multi-part digesting operation + pub fn digest_init(&self, m: &Mechanism) -> Result<()> { + let mut mechanism: CK_MECHANISM = m.into(); + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DigestInit)( + self.handle(), + &mut mechanism as CK_MECHANISM_PTR, + )) + .into_result(Function::DigestInit)?; + } + + Ok(()) + } + + /// Continues an ongoing multi-part digesting operation, + /// taking in the next part of the data to digest + pub fn digest_update(&self, data: &[u8]) -> Result<()> { + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DigestUpdate)( + self.handle(), + data.as_ptr() as *mut u8, + data.len().try_into()?, + )) + .into_result(Function::DigestUpdate)?; + } + + Ok(()) + } + + /// Continues an ongoing multi-part digesting operation, + /// using the value of a secret key as input + pub fn digest_key(&self, key: ObjectHandle) -> Result<()> { + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DigestKey)( + self.handle(), + key.handle(), + )) + .into_result(Function::DigestKey)?; + } + + Ok(()) + } + + /// Finalizes ongoing multi-part digest operation, + /// returning the digest + pub fn digest_final(&self) -> Result> { + let mut digest_len = 0; + + // Get the output buffer length + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DigestFinal)( + self.handle(), + std::ptr::null_mut(), + &mut digest_len, + )) + .into_result(Function::DigestFinal)?; + } + + let mut digest = vec![0; digest_len.try_into()?]; + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_DigestFinal)( + self.handle(), + digest.as_mut_ptr(), + &mut digest_len, + )) + .into_result(Function::DigestFinal)?; + } + + digest.resize(digest_len.try_into()?, 0); + + Ok(digest) + } } diff --git a/cryptoki/src/session/encryption.rs b/cryptoki/src/session/encryption.rs index 24e56ed4..e47cfefd 100644 --- a/cryptoki/src/session/encryption.rs +++ b/cryptoki/src/session/encryption.rs @@ -59,4 +59,84 @@ impl Session { Ok(encrypted_data) } + + /// Starts new multi-part encryption operation + pub fn encrypt_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { + let mut mechanism: CK_MECHANISM = mechanism.into(); + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_EncryptInit)( + self.handle(), + &mut mechanism as CK_MECHANISM_PTR, + key.handle(), + )) + .into_result(Function::EncryptInit)?; + } + + Ok(()) + } + + /// Continues an ongoing multi-part encryption operation, + /// taking in the next part of the data and returning its encryption + pub fn encrypt_update(&self, data: &[u8]) -> Result> { + let mut encrypted_data_len = 0; + + // Get the output buffer length + unsafe { + Rv::from(get_pkcs11!(self.client(), C_EncryptUpdate)( + self.handle(), + data.as_ptr() as *mut u8, + data.len().try_into()?, + std::ptr::null_mut(), + &mut encrypted_data_len, + )) + .into_result(Function::EncryptUpdate)?; + } + + let mut encrypted_data = vec![0; encrypted_data_len.try_into()?]; + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_EncryptUpdate)( + self.handle(), + data.as_ptr() as *mut u8, + data.len().try_into()?, + encrypted_data.as_mut_ptr(), + &mut encrypted_data_len, + )) + .into_result(Function::EncryptUpdate)?; + } + + Ok(encrypted_data) + } + + /// Finalizes ongoing multi-part encryption operation, + /// returning any remaining bytes in the encrypted data + pub fn encrypt_final(&self) -> Result> { + let mut encrypted_data_len = 0; + + // Get the output buffer length + unsafe { + Rv::from(get_pkcs11!(self.client(), C_EncryptFinal)( + self.handle(), + std::ptr::null_mut(), + &mut encrypted_data_len, + )) + .into_result(Function::EncryptFinal)?; + } + + let mut encrypted_data = vec![0; encrypted_data_len.try_into()?]; + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_EncryptFinal)( + self.handle(), + encrypted_data.as_mut_ptr(), + &mut encrypted_data_len, + )) + .into_result(Function::EncryptFinal)?; + } + + encrypted_data.resize(encrypted_data_len.try_into()?, 0); + + Ok(encrypted_data) + } } diff --git a/cryptoki/src/session/signing_macing.rs b/cryptoki/src/session/signing_macing.rs index a3fd6f6d..9deed03d 100644 --- a/cryptoki/src/session/signing_macing.rs +++ b/cryptoki/src/session/signing_macing.rs @@ -56,6 +56,68 @@ impl Session { Ok(signature) } + /// Starts new multi-part signing operation + pub fn sign_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { + let mut mechanism: CK_MECHANISM = mechanism.into(); + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_SignInit)( + self.handle(), + &mut mechanism as CK_MECHANISM_PTR, + key.handle(), + )) + .into_result(Function::SignInit)?; + } + + Ok(()) + } + + /// Continues an ongoing multi-part signing operation, + /// taking in the next part of the data to sign + pub fn sign_update(&self, data: &[u8]) -> Result<()> { + unsafe { + Rv::from(get_pkcs11!(self.client(), C_SignUpdate)( + self.handle(), + data.as_ptr() as *mut u8, + data.len().try_into()?, + )) + .into_result(Function::SignUpdate)?; + } + + Ok(()) + } + + /// Finalizes ongoing multi-part signing operation, + /// returning the signature + pub fn sign_final(&self) -> Result> { + let mut signature_len = 0; + + // Get the output buffer length + unsafe { + Rv::from(get_pkcs11!(self.client(), C_SignFinal)( + self.handle(), + std::ptr::null_mut(), + &mut signature_len, + )) + .into_result(Function::SignFinal)?; + } + + let mut signature = vec![0; signature_len.try_into()?]; + + unsafe { + Rv::from(get_pkcs11!(self.client(), C_SignFinal)( + self.handle(), + signature.as_mut_ptr(), + &mut signature_len, + )) + .into_result(Function::SignFinal)?; + } + + signature.resize(signature_len.try_into()?, 0); + + Ok(signature) + } + /// Verify data in single-part pub fn verify( &self, @@ -86,4 +148,50 @@ impl Session { .into_result(Function::Verify) } } + + /// Starts new multi-part verifying operation + pub fn verify_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> 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)?; + } + + Ok(()) + } + + /// Continues an ongoing multi-part verifying operation, + /// taking in the next part of the data to verify + pub fn verify_update(&self, data: &[u8]) -> Result<()> { + unsafe { + Rv::from(get_pkcs11!(self.client(), C_VerifyUpdate)( + self.handle(), + data.as_ptr() as *mut u8, + data.len().try_into()?, + )) + .into_result(Function::VerifyUpdate)?; + } + + Ok(()) + } + + /// Finalizes ongoing multi-part verifying operation, + /// returning Ok only if the signature verifies + pub fn verify_final(&self, signature: &[u8]) -> Result<()> { + unsafe { + Rv::from(get_pkcs11!(self.client(), C_VerifyFinal)( + self.handle(), + signature.as_ptr() as *mut u8, + signature.len().try_into()?, + )) + .into_result(Function::VerifyFinal)?; + } + + Ok(()) + } } diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 6aa7b31a..82b6dd57 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -213,6 +213,166 @@ fn sign_verify_eddsa_with_ed448_schemes() -> TestResult { Ok(()) } +#[test] +#[serial] +fn sign_verify_multipart() -> 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 = 1024; + + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::PublicExponent(public_exponent), + Attribute::ModulusBits(modulus_bits.into()), + ]; + let priv_key_template = vec![Attribute::Token(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 parts (standard RsaPkcs doesn't support this) + session.sign_init(&Mechanism::Sha256RsaPkcs, priv_key)?; + for part in data.chunks(3) { + session.sign_update(part)?; + } + let signature = session.sign_final()?; + + // Verify signature in parts (standard RsaPkcs doesn't support this) + session.verify_init(&Mechanism::Sha256RsaPkcs, pub_key)?; + for part in data.chunks(3) { + session.verify_update(part)?; + } + session.verify_final(&signature)?; + + // Delete keys + session.destroy_object(pub_key)?; + session.destroy_object(priv_key)?; + + Ok(()) +} + +#[test] +#[serial] +fn sign_verify_multipart_not_initialized() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_ro_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Data to sign/verify + let data = vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]; + let signature = vec![0x66, 0x55, 0x44, 0x33, 0x22, 0x11]; + + // Attempt to update signing without an operation having been initialized + let result = session.sign_update(&data); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::SignUpdate) + )); + + // Attempt to finalize signing without an operation having been initialized + let result = session.sign_final(); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::SignFinal) + )); + + // Attempt to update verification without an operation having been initialized + let result = session.verify_update(&data); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::VerifyUpdate) + )); + + // Attempt to finalize verification without an operation having been initialized + let result = session.verify_final(&signature); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::VerifyFinal) + )); + + Ok(()) +} + +#[test] +#[serial] +fn sign_verify_multipart_already_initialized() -> 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 = 1024; + + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::PublicExponent(public_exponent), + Attribute::ModulusBits(modulus_bits.into()), + ]; + let priv_key_template = vec![Attribute::Token(true)]; + + // Generate keypair + let (pub_key, priv_key) = session.generate_key_pair( + &Mechanism::RsaPkcsKeyPairGen, + &pub_key_template, + &priv_key_template, + )?; + + // Initialize signing operation twice in a row + session.sign_init(&Mechanism::Sha256RsaPkcs, priv_key)?; + let result = session.sign_init(&Mechanism::Sha256RsaPkcs, priv_key); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationActive, Function::SignInit) + )); + + // Make sure signing operation is over before trying same with verification + session.sign_final()?; + + // Initialize verification operation twice in a row + session.verify_init(&Mechanism::Sha256RsaPkcs, pub_key)?; + let result = session.verify_init(&Mechanism::Sha256RsaPkcs, pub_key); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationActive, Function::VerifyInit) + )); + + // Delete keys + session.destroy_object(pub_key)?; + session.destroy_object(priv_key)?; + + Ok(()) +} + #[test] #[serial] fn encrypt_decrypt() -> TestResult { @@ -265,6 +425,153 @@ fn encrypt_decrypt() -> TestResult { Ok(()) } +#[test] +#[serial] +fn encrypt_decrypt_multipart() -> 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 (currently SoftHSM only supports multi-part encrypt/decrypt for symmetric crypto) + let template = vec![ + Attribute::Token(true), + Attribute::ValueLen((128 / 8).into()), + ]; + 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 parts + session.encrypt_init(&Mechanism::AesEcb, key)?; + + let mut encrypted_data = vec![]; + for part in data.chunks(3) { + encrypted_data.extend(session.encrypt_update(part)?); + } + encrypted_data.extend(session.encrypt_final()?); + + // Decrypt data in parts + session.decrypt_init(&Mechanism::AesEcb, key)?; + + let mut decrypted_data = vec![]; + for part in encrypted_data.chunks(3) { + decrypted_data.extend(session.decrypt_update(part)?); + } + decrypted_data.extend(session.decrypt_final()?); + + assert_eq!(data, decrypted_data); + + // Delete key + session.destroy_object(key)?; + + Ok(()) +} + +#[test] +#[serial] +fn encrypt_decrypt_multipart_not_initialized() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_ro_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Data to encrypt/decrypt + let data = vec![ + 0xFF, 0x55, 0xDD, 0x11, 0xBB, 0x33, 0x99, 0x77, 0xFF, 0x55, 0xDD, 0x11, 0xBB, 0x33, 0x99, + 0x77, + ]; + + // Attempt to update encryption without an operation having been initialized + let result = session.encrypt_update(&data); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::EncryptUpdate) + )); + + // Attempt to finalize encryption without an operation having been initialized + let result = session.encrypt_final(); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::EncryptFinal) + )); + + // Attempt to update decryption without an operation having been initialized + let result = session.decrypt_update(&data); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::DecryptUpdate) + )); + + // Attempt to finalize decryption without an operation having been initialized + let result = session.decrypt_final(); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::DecryptFinal) + )); + + Ok(()) +} + +#[test] +#[serial] +fn encrypt_decrypt_multipart_already_initialized() -> 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 (currently SoftHSM only supports multi-part encrypt/decrypt for symmetric crypto) + let template = vec![ + Attribute::Token(true), + Attribute::ValueLen((128 / 8).into()), + ]; + let key = session.generate_key(&Mechanism::AesKeyGen, &template)?; + + // Initialize encryption operation twice in a row + session.encrypt_init(&Mechanism::AesEcb, key)?; + let result = session.encrypt_init(&Mechanism::AesEcb, key); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationActive, Function::EncryptInit) + )); + + // Make sure encryption operation is over before trying same with decryption + session.encrypt_final()?; + + // Initialize encryption operation twice in a row + session.decrypt_init(&Mechanism::AesEcb, key)?; + let result = session.decrypt_init(&Mechanism::AesEcb, key); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationActive, Function::DecryptInit) + )); + + // Delete key + session.destroy_object(key)?; + + Ok(()) +} + #[test] #[serial] fn derive_key() -> TestResult { @@ -1304,6 +1611,141 @@ fn sha256_digest() -> TestResult { Ok(()) } +#[test] +#[serial] +fn sha256_digest_multipart() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_ro_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Data to digest + let data = [ + 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, + ]; + + // Digest data in parts + session.digest_init(&Mechanism::Sha256)?; + for part in data.chunks(3) { + session.digest_update(part)?; + } + + let have = session.digest_final()?; + let want = vec![ + 0x8c, 0x18, 0xb1, 0x5f, 0x01, 0x47, 0x13, 0x2a, 0x03, 0xc2, 0xe3, 0xfd, 0x4f, 0x29, 0xb7, + 0x75, 0x80, 0x19, 0xb5, 0x58, 0x5e, 0xfc, 0xeb, 0x45, 0x18, 0x33, 0x2b, 0x2f, 0xa7, 0xa4, + 0x1f, 0x6e, + ]; + + assert_eq!(have, want); + + Ok(()) +} + +#[test] +#[serial] +fn sha256_digest_multipart_with_key() -> 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())))?; + + // Create a key to add to the digest + let key_template = vec![ + Attribute::Token(true), + Attribute::ValueLen((256 / 8).into()), + // Key must be non-sensitive and extractable to get its bytes and digest them directly, for comparison + Attribute::Sensitive(false), + Attribute::Extractable(true), + ]; + let key = session.generate_key(&Mechanism::AesKeyGen, &key_template)?; + + // Data and key bytes to digest + let mut data = vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]; + + let attributes = session.get_attributes(key, &[AttributeType::Value])?; + let key_data = attributes.first().unwrap(); + let mut key_data = match key_data { + Attribute::Value(key_data) => key_data.to_owned(), + _ => unreachable!(), + }; + + // Digest data in parts + session.digest_init(&Mechanism::Sha256)?; + session.digest_update(&data)?; + session.digest_key(key)?; + + // Create digests to compare + let have = session.digest_final()?; + + data.append(&mut key_data); + let want = session.digest(&Mechanism::Sha256, &data)?; + + assert_eq!(have, want); + + // Delete key + session.destroy_object(key)?; + + Ok(()) +} + +#[test] +#[serial] +fn sha256_digest_multipart_not_initialized() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_ro_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Data to digest + let data = vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]; + + // Attempt to update digest without an operation having been initialized + let result = session.digest_update(&data); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::DigestUpdate) + )); + + // Attempt to finalize digest without an operation having been initialized + let result = session.digest_final(); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationNotInitialized, Function::DigestFinal) + )); + + Ok(()) +} + +#[test] +#[serial] +fn sha256_digest_multipart_already_initialized() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // Open a session and log in + let session = pkcs11.open_ro_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // Initialize digesting operation twice in a row + session.digest_init(&Mechanism::Sha256)?; + let result = session.digest_init(&Mechanism::Sha256); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + Error::Pkcs11(RvError::OperationActive, Function::DigestInit) + )); + + Ok(()) +} + #[test] #[serial] fn gcm_param_graceful_failure() -> TestResult {