diff --git a/Cargo.lock b/Cargo.lock index e465fc05..7946911d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,8 +704,7 @@ dependencies = [ [[package]] name = "psa-crypto" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8cd6b7efe9e3853f86c82f89763a070df8b7caf8c289bf0074acbf8470fd11" +source = "git+https://github.com/parallaxsecond/rust-psa-crypto?rev=a1647d64e19e6e46baad708b84c2b5d0d3b543c7#a1647d64e19e6e46baad708b84c2b5d0d3b543c7" dependencies = [ "log", "psa-crypto-sys", @@ -715,9 +714,8 @@ dependencies = [ [[package]] name = "psa-crypto-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63710d569a7919a2a4c130e336953e9970a6f6e1fc94baa6c116fbdfc4f06060" +version = "0.5.1" +source = "git+https://github.com/parallaxsecond/rust-psa-crypto?rev=a1647d64e19e6e46baad708b84c2b5d0d3b543c7#a1647d64e19e6e46baad708b84c2b5d0d3b543c7" dependencies = [ "bindgen", "cc", diff --git a/Cargo.toml b/Cargo.toml index 4e1f93a7..221e53d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,9 @@ users = "0.10.0" libc = "0.2.77" anyhow = "1.0.32" +[patch.crates-io] +psa-crypto = { git = "https://github.com/parallaxsecond/rust-psa-crypto", rev = "a1647d64e19e6e46baad708b84c2b5d0d3b543c7" } + [dev-dependencies] rand = { version = "0.7.3", features = ["small_rng"] } diff --git a/ci.sh b/ci.sh index 2b312a02..d7d8af7d 100755 --- a/ci.sh +++ b/ci.sh @@ -135,8 +135,23 @@ sleep 5 pgrep -f target/debug/parsec >/dev/null if [ "$PROVIDER_NAME" = "all" ]; then - echo "Execute all-providers tests" + echo "Execute all-providers normal tests" RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml all_providers::normal + + echo "Execute all-providers multi-tenancy tests" + su -c "RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml --target-dir /home/parsec-client-1 all_providers::multitenancy::client1_before" parsec-client-1 + su -c "RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml --target-dir /home/parsec-client-2 all_providers::multitenancy::client2" parsec-client-2 + su -c "RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml --target-dir /home/parsec-client-1 all_providers::multitenancy::client1_after" parsec-client-1 + # Change the authentication method + sed -i 's/^\(auth_type\s*=\s*\).*$/\1\"UnixPeerCredentials\"/' $CONFIG_PATH + pkill -SIGHUP parsec + sleep 5 + su -c "RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml --target-dir /home/parsec-client-1 all_providers::multitenancy::client1_before" parsec-client-1 + su -c "RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml --target-dir /home/parsec-client-2 all_providers::multitenancy::client2" parsec-client-2 + su -c "RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml --target-dir /home/parsec-client-1 all_providers::multitenancy::client1_after" parsec-client-1 + + # Last test as it changes the service configuration + echo "Execute all-providers config tests" RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml all_providers::config -- --test-threads=1 else # Per provider tests diff --git a/e2e_tests/Cargo.toml b/e2e_tests/Cargo.toml index 59196caf..607c9b1c 100644 --- a/e2e_tests/Cargo.toml +++ b/e2e_tests/Cargo.toml @@ -14,10 +14,13 @@ publish = false [dependencies] serde = { version = "1.0.115", features = ["derive"] } -parsec-client = { git = "https://github.com/parallaxsecond/parsec-client-rust", rev = "dbd449a9e736f9f972e5489fb2d67949b93484dc", features = ["testing"] } +parsec-client = { git = "https://github.com/parallaxsecond/parsec-client-rust", rev = "3f7dfc7bd06bea7cb3aa38cdcb270af7b8899f89", features = ["testing"] } log = "0.4.11" rand = "0.7.3" +[patch.crates-io] +psa-crypto = { git = "https://github.com/parallaxsecond/rust-psa-crypto", rev = "22a505bedce1c21246ce5c3cf41ea97b0b781830" } + [dev-dependencies] ring = "0.16.15" env_logger = "0.7.1" diff --git a/e2e_tests/provider_cfg/all/Dockerfile b/e2e_tests/provider_cfg/all/Dockerfile index c2f61e92..c132d6c0 100644 --- a/e2e_tests/provider_cfg/all/Dockerfile +++ b/e2e_tests/provider_cfg/all/Dockerfile @@ -6,7 +6,9 @@ RUN apt-get update && \ apt-get install -y git make gcc python3 python curl wget cmake && \ apt-get install -y automake autoconf libtool pkg-config libssl-dev && \ # These libraries are needed for bindgen as it uses libclang.so - apt-get install -y clang libclang-dev libc6-dev-i386 + apt-get install -y clang libclang-dev libc6-dev-i386 && \ + # Install cargo globally to not have to install it for each user for multitenancy tests + apt-get install -y cargo WORKDIR /tmp RUN wget https://github.com/ARMmbed/mbed-crypto/archive/mbedcrypto-2.0.0.tar.gz @@ -44,6 +46,6 @@ RUN cd SoftHSMv2-2.5.0 \ # and is found with the find_slot_number script. RUN softhsm2-util --init-token --slot 0 --label "Parsec Tests" --pin 123456 --so-pin 123456 -# Install Rust toolchain -RUN curl https://sh.rustup.rs -sSf | bash -s -- -y -ENV PATH="/root/.cargo/bin:${PATH}" +# Add users for multitenancy tests +RUN useradd -m parsec-client-1 +RUN useradd -m parsec-client-2 diff --git a/e2e_tests/src/lib.rs b/e2e_tests/src/lib.rs index 35fb5b34..cd228800 100644 --- a/e2e_tests/src/lib.rs +++ b/e2e_tests/src/lib.rs @@ -10,7 +10,7 @@ pub use parsec_client::core::request_client::RequestClient; pub use parsec_client::error; use log::error; -use parsec_client::auth::AuthenticationData; +use parsec_client::auth::Authentication; use parsec_client::core::basic_client::BasicClient; use parsec_client::core::interface::operations::list_authenticators::AuthenticatorInfo; use parsec_client::core::interface::operations::list_keys::KeyInfo; @@ -24,7 +24,6 @@ use parsec_client::core::interface::operations::psa_key_attributes::{ }; use parsec_client::core::interface::requests::{Opcode, ProviderID, ResponseStatus, Result}; use parsec_client::core::ipc_handler::unix_socket; -use parsec_client::core::secrecy::{ExposeSecret, Secret}; use parsec_client::error::Error; use std::collections::HashSet; use std::time::Duration; @@ -36,7 +35,7 @@ const TEST_TIMEOUT: Duration = Duration::from_secs(1); #[derive(Debug)] pub struct TestClient { basic_client: BasicClient, - created_keys: Option>, + created_keys: Option, ProviderID)>>, } fn convert_error(err: Error) -> ResponseStatus { @@ -52,47 +51,26 @@ fn convert_error(err: Error) -> ResponseStatus { impl TestClient { /// Creates a TestClient instance. - /// - /// The implicit provider chosen for servicing cryptographic operations is decided through - /// a call to `list_providers`, followed by choosing the first non-Core provider. pub fn new() -> TestClient { - let mut client = TestClient { - basic_client: BasicClient::new(AuthenticationData::AppIdentity(Secret::new( - String::from("root"), - ))), - created_keys: Some(HashSet::new()), - }; + let mut basic_client = BasicClient::new_naked(); let ipc_handler = unix_socket::Handler::new(TEST_SOCKET_PATH.into(), Some(TEST_TIMEOUT)); - client.basic_client.set_ipc_handler(Box::from(ipc_handler)); + basic_client.set_ipc_handler(Box::from(ipc_handler)); + basic_client.set_timeout(Some(Duration::from_secs(10))); - let crypto_provider = client.find_crypto_provider(); - client.set_provider(crypto_provider); - client - .basic_client - .set_timeout(Some(Duration::from_secs(10))); - - client - } + basic_client.set_default_provider().unwrap(); + basic_client + .set_default_auth(Some(String::from("root"))) + .unwrap(); - fn find_crypto_provider(&self) -> ProviderID { - let providers = self - .basic_client - .list_providers() - .expect("List providers failed"); - for provider in providers { - if provider.id != ProviderID::Core { - return provider.id; - } + TestClient { + basic_client, + created_keys: Some(HashSet::new()), } - - ProviderID::Core } pub fn is_operation_supported(&mut self, op: Opcode) -> bool { - self.list_opcodes(self.provider().unwrap()) - .unwrap() - .contains(&op) + self.list_opcodes(self.provider()).unwrap().contains(&op) } /// Manually set the provider to execute the requests. @@ -101,22 +79,23 @@ impl TestClient { } /// Get client provider - pub fn provider(&self) -> Option { + pub fn provider(&self) -> ProviderID { self.basic_client.implicit_provider() } - /// Set the client authentication string. - pub fn set_auth(&mut self, auth: String) { - self.basic_client - .set_auth_data(AuthenticationData::AppIdentity(Secret::new(auth))); + /// Set the client default authentication method + /// + /// `auth` will get ignored if the default authenticator is not the Direct one. + pub fn set_default_auth(&mut self, auth: Option) { + self.basic_client.set_default_auth(auth).unwrap(); } - /// Get client authentication string. - pub fn auth(&self) -> String { - if let AuthenticationData::AppIdentity(app_name) = self.basic_client.auth_data() { - app_name.expose_secret().to_string() + /// Get client application name if using direct authentication + pub fn get_direct_auth(&self) -> Option { + if let Authentication::Direct(app_name) = self.basic_client.auth_data() { + Some(app_name) } else { - panic!("Client should always be using AppIdentity-based authentication"); + None } } @@ -132,8 +111,8 @@ impl TestClient { .psa_generate_key(key_name.clone(), attributes) .map_err(convert_error)?; - let provider = self.provider().unwrap(); - let auth = self.auth(); + let provider = self.provider(); + let auth = self.get_direct_auth(); if let Some(ref mut created_keys) = self.created_keys { let _ = created_keys.insert((key_name, auth, provider)); @@ -427,8 +406,8 @@ impl TestClient { .psa_import_key(key_name.clone(), &data, attributes) .map_err(convert_error)?; - let provider = self.provider().unwrap(); - let auth = self.auth(); + let provider = self.provider(); + let auth = self.get_direct_auth(); if let Some(ref mut created_keys) = self.created_keys { let _ = created_keys.insert((key_name, auth, provider)); @@ -630,8 +609,8 @@ impl TestClient { .psa_destroy_key(key_name.clone()) .map_err(convert_error)?; - let provider = self.provider().unwrap(); - let auth = self.auth(); + let provider = self.provider(); + let auth = self.get_direct_auth(); if let Some(ref mut created_keys) = self.created_keys { let _ = created_keys.remove(&(key_name, auth, provider)); @@ -927,7 +906,7 @@ impl Drop for TestClient { if let Some(ref mut created_keys) = self.created_keys { for (key_name, auth, provider) in created_keys.clone().iter() { self.set_provider(*provider); - self.set_auth(auth.clone()); + self.set_default_auth(auth.clone()); if self.destroy_key(key_name.clone()).is_err() { error!("Failed to destroy key '{}'", key_name); } diff --git a/e2e_tests/src/stress.rs b/e2e_tests/src/stress.rs index c8355541..52785669 100644 --- a/e2e_tests/src/stress.rs +++ b/e2e_tests/src/stress.rs @@ -107,7 +107,7 @@ impl StressTestWorker { // Create unique client auth let auth = generate_string(10); info!("Worker with auth `{}` starting.", auth); - client.set_auth(auth); + client.set_default_auth(Some(auth)); // Create sign/verify key let sign_key_name = generate_string(10); diff --git a/e2e_tests/tests/all_providers/mod.rs b/e2e_tests/tests/all_providers/mod.rs index 6efce557..ed8749d6 100644 --- a/e2e_tests/tests/all_providers/mod.rs +++ b/e2e_tests/tests/all_providers/mod.rs @@ -3,4 +3,5 @@ mod config; mod cross; +mod multitenancy; mod normal; diff --git a/e2e_tests/tests/all_providers/multitenancy.rs b/e2e_tests/tests/all_providers/multitenancy.rs new file mode 100644 index 00000000..625dabb0 --- /dev/null +++ b/e2e_tests/tests/all_providers/multitenancy.rs @@ -0,0 +1,69 @@ +// Copyright 2020 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +use e2e_tests::TestClient; +use parsec_client::core::interface::requests::{ProviderID, ResponseStatus}; + +// These tests are executed by different users in the following order: +// 1. client1_before is executed as parsec-client-1 +// 2. client2 is executed as parsec-client-2 +// 3. client1_after is executed as parsec-client-1 +// +// They are executed against all possible authenticators in Parsec. + +#[test] +fn client1_before() { + // Create one key on each provider + let mut client = TestClient::new(); + client.do_not_destroy_keys(); + client.set_default_auth(Some("client1".to_string())); + + let key = String::from("multitenant"); + + for provider in [ProviderID::MbedCrypto, ProviderID::Pkcs11, ProviderID::Tpm].iter() { + client.set_provider(*provider); + client.generate_rsa_sign_key(key.clone()).unwrap(); + } +} + +#[test] +fn client2() { + let mut client = TestClient::new(); + client.set_default_auth(Some("client2".to_string())); + + let key = String::from("multitenant"); + + // Try to list those keys + let keys = client.list_keys().unwrap(); + assert!(keys.is_empty()); + + for provider in [ProviderID::MbedCrypto, ProviderID::Pkcs11, ProviderID::Tpm].iter() { + client.set_provider(*provider); + assert_eq!( + client.export_public_key(key.clone()).unwrap_err(), + ResponseStatus::PsaErrorDoesNotExist + ); + assert_eq!( + client.destroy_key(key.clone()).unwrap_err(), + ResponseStatus::PsaErrorDoesNotExist + ); + client.generate_rsa_sign_key(key.clone()).unwrap(); + client.destroy_key(key.clone()).unwrap(); + } +} + +#[test] +fn client1_after() { + let mut client = TestClient::new(); + client.set_default_auth(Some("client1".to_string())); + + // Verify all keys are still there and can be used + let keys = client.list_keys().unwrap(); + assert_eq!(keys.len(), 3); + + // Destroy the keys + let key = String::from("multitenant"); + for provider in [ProviderID::MbedCrypto, ProviderID::Pkcs11, ProviderID::Tpm].iter() { + client.set_provider(*provider); + client.destroy_key(key.clone()).unwrap(); + } +} diff --git a/e2e_tests/tests/all_providers/normal.rs b/e2e_tests/tests/all_providers/normal.rs index b8346218..872be523 100644 --- a/e2e_tests/tests/all_providers/normal.rs +++ b/e2e_tests/tests/all_providers/normal.rs @@ -116,7 +116,7 @@ fn sign_verify_with_provider_discovery() -> Result<()> { #[test] fn list_keys() { let mut client = TestClient::new(); - client.set_auth("list_keys test".to_string()); + client.set_default_auth(Some("list_keys test".to_string())); let keys = client.list_keys().expect("list_keys failed"); diff --git a/e2e_tests/tests/per_provider/normal_tests/auth.rs b/e2e_tests/tests/per_provider/normal_tests/auth.rs index fbbffd25..f4d7dd41 100644 --- a/e2e_tests/tests/per_provider/normal_tests/auth.rs +++ b/e2e_tests/tests/per_provider/normal_tests/auth.rs @@ -11,10 +11,10 @@ fn two_auths_same_key_name() -> Result<()> { let auth1 = String::from("first_client"); let auth2 = String::from("second_client"); - client.set_auth(auth1); + client.set_default_auth(Some(auth1)); client.generate_rsa_sign_key(key_name.clone())?; - client.set_auth(auth2); + client.set_default_auth(Some(auth2)); client.generate_rsa_sign_key(key_name) } @@ -25,10 +25,10 @@ fn delete_wrong_key() -> Result<()> { let auth1 = String::from("first_client"); let auth2 = String::from("second_client"); - client.set_auth(auth1); + client.set_default_auth(Some(auth1)); client.generate_rsa_sign_key(key_name.clone())?; - client.set_auth(auth2); + client.set_default_auth(Some(auth2)); let status = client .destroy_key(key_name) .expect_err("Destroying key should have failed"); diff --git a/e2e_tests/tests/per_provider/normal_tests/key_attributes.rs b/e2e_tests/tests/per_provider/normal_tests/key_attributes.rs index ee6aaf1b..85ed4e5d 100644 --- a/e2e_tests/tests/per_provider/normal_tests/key_attributes.rs +++ b/e2e_tests/tests/per_provider/normal_tests/key_attributes.rs @@ -129,7 +129,7 @@ fn wrong_permitted_algorithm() { // The Mbed Crypto provider currently does not support other algorithms than the RSA PKCS 1v15 // signing algorithm with hash when checking policies only. - if client.provider().unwrap() == ProviderID::MbedCrypto { + if client.provider() == ProviderID::MbedCrypto { return; } diff --git a/e2e_tests/tests/per_provider/persistent_after.rs b/e2e_tests/tests/per_provider/persistent_after.rs index 2ddc9e09..03c982ab 100644 --- a/e2e_tests/tests/per_provider/persistent_after.rs +++ b/e2e_tests/tests/per_provider/persistent_after.rs @@ -28,7 +28,7 @@ fn reuse_to_sign() -> Result<()> { fn should_have_been_deleted() { let mut client = TestClient::new(); - if client.provider().unwrap() == ProviderID::Tpm { + if client.provider() == ProviderID::Tpm { // This test does not make sense for the TPM Provider. return; }