diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56ec25e3..ad48b74c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ jobs: # run: pushd e2e_tests/docker_image && docker build -t parsec-service-test-all -f parsec-service-test-all.Dockerfile . && popd - name: Run the container to execute the test script run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec ghcr.io/parallaxsecond/parsec-service-test-all /tmp/parsec/ci.sh all - # When running the container built on the CI - # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh all + # When running the container built on the CI + # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh all mbed-crypto-provider: name: Integration tests using Mbed Crypto provider @@ -26,8 +26,8 @@ jobs: # run: pushd e2e_tests/docker_image && docker build -t parsec-service-test-all -f parsec-service-test-all.Dockerfile . && popd - name: Run the container to execute the test script run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec ghcr.io/parallaxsecond/parsec-service-test-all /tmp/parsec/ci.sh mbed-crypto - # When running the container built on the CI - # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh mbed-crypto + # When running the container built on the CI + # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh mbed-crypto pkcs11-provider: name: Integration tests using PKCS 11 provider @@ -40,8 +40,8 @@ jobs: - name: Run the container to execute the test script # Not running stress tests because they fail, presumably because of the same issue as #264 run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec ghcr.io/parallaxsecond/parsec-service-test-all /tmp/parsec/ci.sh pkcs11 --no-stress-test - # When running the container built on the CI - # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh pkcs11 --no-stress-test + # When running the container built on the CI + # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh pkcs11 --no-stress-test tpm-provider: name: Integration tests using TPM provider @@ -53,8 +53,8 @@ jobs: # run: pushd e2e_tests/docker_image && docker build -t parsec-service-test-all -f parsec-service-test-all.Dockerfile . && popd - name: Run the container to execute the test script run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec ghcr.io/parallaxsecond/parsec-service-test-all /tmp/parsec/ci.sh tpm - # When running the container built on the CI - # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh tpm + # When running the container built on the CI + # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh tpm trusted-service-provider: name: Integration tests using Cypto Trusted Service provider @@ -62,12 +62,12 @@ jobs: steps: - uses: actions/checkout@v2 # Use the following step when updating the `parsec-service-test-all` image - # - name: Build the container - # run: pushd e2e_tests/docker_image && docker build -t parsec-service-test-all -f parsec-service-test-all.Dockerfile . && popd + - name: Build the container + run: pushd e2e_tests/docker_image && docker build -t parsec-service-test-all -f parsec-service-test-all.Dockerfile . && popd - name: Run the container to execute the test script - run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec ghcr.io/parallaxsecond/parsec-service-test-all /tmp/parsec/ci.sh trusted-service + # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec ghcr.io/parallaxsecond/parsec-service-test-all /tmp/parsec/ci.sh trusted-service # When running the container built on the CI - # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh trusted-service + run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh trusted-service cryptoauthlib-provider: name: Integration tests using CryptoAuthentication Library provider @@ -80,8 +80,8 @@ jobs: - name: Run the container to execute the test script # Not running stress tests because rust-cryptoauthlib test-interface does not support required calls run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec ghcr.io/parallaxsecond/parsec-service-test-all /tmp/parsec/ci.sh cryptoauthlib --no-stress-test - # When running the container built on the CI - # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh cryptoauthlib --no-stress-test + # When running the container built on the CI + # run: docker run -v $(pwd):/tmp/parsec -w /tmp/parsec -t parsec-service-test-all /tmp/parsec/ci.sh cryptoauthlib --no-stress-test fuzz-test-checker: name: Check that the fuzz testing framework is still working diff --git a/Cargo.toml b/Cargo.toml index c2dec2f1..2b3fc6e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ mbed-crypto-provider = ["psa-crypto"] pkcs11-provider = ["cryptoki", "picky-asn1-der", "picky-asn1", "picky-asn1-x509", "psa-crypto", "rand"] tpm-provider = ["tss-esapi", "picky-asn1-der", "picky-asn1", "picky-asn1-x509", "hex"] cryptoauthlib-provider = ["rust-cryptoauthlib"] -trusted-service-provider = ["mbed-crypto-provider", "bindgen", "prost-build", "prost"] +trusted-service-provider = ["psa-crypto", "bindgen", "prost-build", "prost"] all-providers = ["tpm-provider", "pkcs11-provider", "mbed-crypto-provider", "cryptoauthlib-provider"] # Authenticators diff --git a/e2e_tests/docker_image/parsec-service-test-all.Dockerfile b/e2e_tests/docker_image/parsec-service-test-all.Dockerfile index 84106e8e..97c10bc4 100644 --- a/e2e_tests/docker_image/parsec-service-test-all.Dockerfile +++ b/e2e_tests/docker_image/parsec-service-test-all.Dockerfile @@ -94,15 +94,18 @@ RUN cd nanopb-0.4.4-linux-x86 \ RUN rm -rf nanopb-0.4.4-linux-x86 nanopb-0.4.4-linux-x86.tar.gz # Install mock Trusted Services -RUN git clone https://git.trustedfirmware.org/TS/trusted-services.git --branch main \ +# Setup git config for patching dependencies +RUN git config --global user.email "some@email.com" +RUN git config --global user.name "Parsec Team" +RUN git clone https://git.trustedfirmware.org/TS/trusted-services.git --branch integration \ && cd trusted-services \ - && git reset --hard 2fc7e10c7c21e4dafbf63dc9d00dfc2a7a7fddad + && git reset --hard c1cf9120e4ab0b359a27176b079769b9a7e6bb87 # Install correct python dependencies RUN pip3 install -r trusted-services/requirements.txt RUN cd trusted-services/deployments/libts/linux-pc/ \ && cmake . \ && make \ - && cp libts.so nanopb_install/lib/libprotobuf-nanopb.a mbedcrypto_install/lib/libmbedcrypto.a /usr/local/lib/ + && cp libts.so nanopb_install/lib/libprotobuf-nanopb.a mbedtls_install/lib/libmbedcrypto.a /usr/local/lib/ RUN rm -rf trusted-services # Create a new token in a new slot. The slot number assigned will be random diff --git a/src/providers/trusted_service/asym_encryption.rs b/src/providers/trusted_service/asym_encryption.rs new file mode 100644 index 00000000..287a89a4 --- /dev/null +++ b/src/providers/trusted_service/asym_encryption.rs @@ -0,0 +1,64 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +use super::Provider; +use crate::authenticators::ApplicationName; +use crate::key_info_managers::KeyTriple; +use log::error; +use parsec_interface::operations::{psa_asymmetric_decrypt, psa_asymmetric_encrypt}; +use parsec_interface::requests::{ProviderId, Result}; + +impl Provider { + pub(super) fn psa_asymmetric_encrypt_internal( + &self, + app_name: ApplicationName, + op: psa_asymmetric_encrypt::Operation, + ) -> Result { + let key_name = op.key_name.clone(); + + let key_triple = KeyTriple::new(app_name, ProviderId::TrustedService, key_name); + let key_id = self.key_info_store.get_key_id(&key_triple)?; + let salt_buff = match &op.salt { + Some(salt) => salt.to_vec(), + None => Vec::new(), + }; + + match self + .context + .asym_encrypt(key_id, op.alg, op.plaintext.to_vec(), salt_buff) + { + Ok(ciphertext) => Ok(psa_asymmetric_encrypt::Result { + ciphertext: ciphertext.into(), + }), + Err(error) => { + error!("Encrypt failed with status: {}", error); + Err(error) + } + } + } + + pub(super) fn psa_asymmetric_decrypt_internal( + &self, + app_name: ApplicationName, + op: psa_asymmetric_decrypt::Operation, + ) -> Result { + let key_triple = KeyTriple::new(app_name, ProviderId::TrustedService, op.key_name.clone()); + let key_id = self.key_info_store.get_key_id(&key_triple)?; + let salt_buff = match &op.salt { + Some(salt) => salt.to_vec(), + None => Vec::new(), + }; + + match self + .context + .asym_decrypt(key_id, op.alg, op.ciphertext.to_vec(), salt_buff) + { + Ok(plaintext) => Ok(psa_asymmetric_decrypt::Result { + plaintext: plaintext.into(), + }), + Err(error) => { + error!("Decrypt failed with status: {}", error); + Err(error) + } + } + } +} diff --git a/src/providers/trusted_service/context/asym_encryption.rs b/src/providers/trusted_service/context/asym_encryption.rs new file mode 100644 index 00000000..7b53e0ee --- /dev/null +++ b/src/providers/trusted_service/context/asym_encryption.rs @@ -0,0 +1,58 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +use super::ts_protobuf::{ + AsymmetricDecryptIn, AsymmetricDecryptOut, AsymmetricEncryptIn, AsymmetricEncryptOut, +}; +use super::Context; +use parsec_interface::operations::psa_algorithm::AsymmetricEncryption; +use parsec_interface::requests::ResponseStatus; +use std::convert::TryInto; +use zeroize::Zeroize; + +impl Context { + pub fn asym_encrypt( + &self, + key_id: u32, + alg: AsymmetricEncryption, + mut plaintext: Vec, + mut salt: Vec, + ) -> Result, ResponseStatus> { + let alg = alg.try_into().map_err(|e| { + plaintext.zeroize(); + salt.zeroize(); + e + })?; + let req = AsymmetricEncryptIn { + id: key_id, + alg, + plaintext, + salt, + }; + let AsymmetricEncryptOut { ciphertext } = self.send_request(&req)?; + + Ok(ciphertext) + } + + pub fn asym_decrypt( + &self, + key_id: u32, + alg: AsymmetricEncryption, + mut ciphertext: Vec, + mut salt: Vec, + ) -> Result, ResponseStatus> { + let alg = alg.try_into().map_err(|e| { + ciphertext.zeroize(); + salt.zeroize(); + e + })?; + let req = AsymmetricDecryptIn { + id: key_id, + alg, + ciphertext, + salt, + }; + let AsymmetricDecryptOut { plaintext } = self.send_request(&req)?; + + Ok(plaintext) + } +} diff --git a/src/providers/trusted_service/context/asym_sign.rs b/src/providers/trusted_service/context/asym_sign.rs index e8c239b2..d6791d7e 100644 --- a/src/providers/trusted_service/context/asym_sign.rs +++ b/src/providers/trusted_service/context/asym_sign.rs @@ -17,11 +17,11 @@ impl Context { ) -> Result, Error> { info!("Handling SignHash request"); let proto_req = SignHashIn { - handle: 0, + id: key_id, hash, alg: algorithm.try_into()?, }; - let SignHashOut { signature } = self.send_request_with_key(proto_req, key_id)?; + let SignHashOut { signature } = self.send_request(&proto_req)?; Ok(signature) } @@ -36,12 +36,12 @@ impl Context { ) -> Result<(), Error> { info!("Handling VerifyHash request"); let proto_req = VerifyHashIn { - handle: 0, + id: key_id, hash, signature, alg: algorithm.try_into()?, }; - self.send_request_with_key(proto_req, key_id)?; + self.send_request(&proto_req)?; Ok(()) } diff --git a/src/providers/trusted_service/context/key_management.rs b/src/providers/trusted_service/context/key_management.rs index ca56494e..79188c23 100644 --- a/src/providers/trusted_service/context/key_management.rs +++ b/src/providers/trusted_service/context/key_management.rs @@ -2,13 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use super::error::Error; use super::ts_protobuf::{ - CloseKeyIn, DestroyKeyIn, DestroyKeyOut, ExportPublicKeyIn, GenerateKeyIn, GenerateKeyOut, - ImportKeyIn, ImportKeyOut, KeyAttributes, KeyLifetime, KeyPolicy, OpenKeyIn, OpenKeyOut, + DestroyKeyIn, DestroyKeyOut, ExportPublicKeyIn, GenerateKeyIn, ImportKeyIn, KeyAttributes, + KeyLifetime, KeyPolicy, }; use super::Context; use log::info; use psa_crypto::types::key::Attributes; -use psa_crypto::types::status::Error as PsaError; use std::convert::{TryFrom, TryInto}; use zeroize::Zeroize; @@ -31,10 +30,7 @@ impl Context { }), }), }; - let GenerateKeyOut { handle } = self.send_request(&generate_req)?; - - let close_req = CloseKeyIn { handle }; - self.send_request(&close_req)?; + let _ = self.send_request(&generate_req)?; Ok(()) } @@ -74,10 +70,7 @@ impl Context { }), data, }; - let ImportKeyOut { handle } = self.send_request(&import_req)?; - - let close_req = CloseKeyIn { handle }; - self.send_request(&close_req)?; + let _ = self.send_request(&import_req)?; Ok(()) } @@ -88,37 +81,16 @@ impl Context { /// format. pub fn export_public_key(&self, id: u32) -> Result, Error> { info!("Handling ExportPublicKey request"); - self.send_request_with_key(ExportPublicKeyIn::default(), id) + let req = ExportPublicKeyIn { id }; + self.send_request(&req) } /// Destroy a key given its ID. pub fn destroy_key(&self, key_id: u32) -> Result<(), Error> { info!("Handling DestroyKey request"); - let open_req = OpenKeyIn { id: key_id }; - let OpenKeyOut { handle } = self.send_request(&open_req)?; - let destroy_req = DestroyKeyIn { handle }; + let destroy_req = DestroyKeyIn { id: key_id }; let _proto_resp: DestroyKeyOut = self.send_request(&destroy_req)?; Ok(()) } - - /// Verify if a key with a given ID exists. - pub fn check_key_exists(&self, key_id: u32) -> Result { - info!("Handling CheckKey request"); - let open_req = OpenKeyIn { id: key_id }; - match self.send_request(&open_req) { - Ok(OpenKeyOut { handle }) => { - let close_req = CloseKeyIn { handle }; - self.send_request(&close_req)?; - Ok(true) - } - Err(e) => { - if e == Error::PsaCrypto(PsaError::DoesNotExist) { - Ok(false) - } else { - Err(e) - } - } - } - } } diff --git a/src/providers/trusted_service/context/mod.rs b/src/providers/trusted_service/context/mod.rs index 0f05dcb3..ef18960b 100644 --- a/src/providers/trusted_service/context/mod.rs +++ b/src/providers/trusted_service/context/mod.rs @@ -10,7 +10,10 @@ use std::ptr::null_mut; use std::slice; use std::sync::Mutex; use ts_binding::*; -use ts_protobuf::{CloseKeyIn, GetOpcode, OpenKeyIn, OpenKeyOut, SetHandle}; +use ts_protobuf::GetOpcode; + +// Bindgen fails to generate this constant. +pub const PSA_KEY_ID_USER_MAX: u32 = 0x3fffffff; #[allow( non_snake_case, @@ -30,6 +33,7 @@ pub mod ts_binding { include!(concat!(env!("OUT_DIR"), "/ts_bindings.rs")); } +mod asym_encryption; mod asym_sign; pub mod error; mod key_management; @@ -176,27 +180,6 @@ impl Context { Ok(resp) } - - // Send a request that requires a key, given the key's ID. - // This function is responsible for opening the key, for sending the - // request with `send_request` and for closing the key afterwards. - fn send_request_with_key( - &self, - mut req: impl Message + GetOpcode + SetHandle, - key_id: u32, - ) -> Result { - let open_req = OpenKeyIn { id: key_id }; - let OpenKeyOut { handle } = self.send_request(&open_req)?; - - req.set_handle(handle); - let res = self.send_request(&req); - let close_req = CloseKeyIn { handle }; - - let res_close = self.send_request(&close_req); - let res = res?; - res_close?; - Ok(res) - } } impl Drop for Context { diff --git a/src/providers/trusted_service/context/ts_protobuf.rs b/src/providers/trusted_service/context/ts_protobuf.rs index f7a1cd41..8ae0a88c 100644 --- a/src/providers/trusted_service/context/ts_protobuf.rs +++ b/src/providers/trusted_service/context/ts_protobuf.rs @@ -48,37 +48,14 @@ macro_rules! opcode_impl { }; } -opcode_impl!(OpenKeyIn, OpenKeyOut, OpenKey); -opcode_impl!(CloseKeyIn, CloseKey); opcode_impl!(GenerateKeyIn, GenerateKeyOut, GenerateKey); opcode_impl!(DestroyKeyIn, DestroyKeyOut, DestroyKey); opcode_impl!(SignHashIn, SignHashOut, SignHash); opcode_impl!(VerifyHashIn, VerifyHashOut, VerifyHash); opcode_impl!(ImportKeyIn, ImportKeyOut, ImportKey); opcode_impl!(ExportPublicKeyIn, ExportPublicKeyOut, ExportPublicKey); - -/// Trait allowing the handle of opened-key-dependent operations -/// to be set in a generic way. -pub trait SetHandle { - fn set_handle(&mut self, handle: u32); -} - -macro_rules! set_handle_impl { - ($type:ty) => { - impl SetHandle for $type { - fn set_handle(&mut self, handle: u32) { - self.handle = handle; - } - } - }; -} - -set_handle_impl!(DestroyKeyIn); -set_handle_impl!(SignHashIn); -set_handle_impl!(VerifyHashIn); -set_handle_impl!(AsymmetricEncryptIn); -set_handle_impl!(AsymmetricDecryptIn); -set_handle_impl!(ExportPublicKeyIn); +opcode_impl!(AsymmetricDecryptIn, AsymmetricDecryptOut, AsymmetricDecrypt); +opcode_impl!(AsymmetricEncryptIn, AsymmetricEncryptOut, AsymmetricEncrypt); impl Drop for ImportKeyIn { fn drop(&mut self) { diff --git a/src/providers/trusted_service/key_management.rs b/src/providers/trusted_service/key_management.rs index 3946b5b6..1e8877ef 100644 --- a/src/providers/trusted_service/key_management.rs +++ b/src/providers/trusted_service/key_management.rs @@ -3,13 +3,31 @@ use super::Provider; use crate::authenticators::ApplicationName; use crate::key_info_managers::KeyTriple; -use crate::providers::mbed_crypto::key_management::create_key_id; use log::error; use parsec_interface::operations::{ psa_destroy_key, psa_export_public_key, psa_generate_key, psa_import_key, }; use parsec_interface::requests::{ProviderId, ResponseStatus, Result}; use parsec_interface::secrecy::ExposeSecret; +use std::sync::atomic::{AtomicU32, Ordering::Relaxed}; + +/// Creates a new PSA Key ID +pub fn create_key_id(max_current_id: &AtomicU32) -> Result { + // fetch_add adds 1 to the old value and returns the old value, so add 1 to local value for new ID + let new_key_id = max_current_id.fetch_add(1, Relaxed) + 1; + if new_key_id > super::context::PSA_KEY_ID_USER_MAX { + // If storing key failed and no other keys were created in the mean time, it is safe to + // decrement the key counter. + let _ = max_current_id.store(super::context::PSA_KEY_ID_USER_MAX, Relaxed); + error!( + "PSA max key ID limit of {} reached", + super::context::PSA_KEY_ID_USER_MAX + ); + return Err(ResponseStatus::PsaErrorInsufficientMemory); + } + + Ok(new_key_id) +} impl Provider { pub(super) fn psa_generate_key_internal( diff --git a/src/providers/trusted_service/mod.rs b/src/providers/trusted_service/mod.rs index c5306fff..dcb6e750 100644 --- a/src/providers/trusted_service/mod.rs +++ b/src/providers/trusted_service/mod.rs @@ -11,8 +11,8 @@ use derivative::Derivative; use log::{error, trace}; use parsec_interface::operations::list_providers::ProviderInfo; use parsec_interface::operations::{ - list_clients, list_keys, psa_destroy_key, psa_export_public_key, psa_generate_key, - psa_import_key, psa_sign_hash, psa_verify_hash, + list_clients, list_keys, psa_asymmetric_decrypt, psa_asymmetric_encrypt, psa_destroy_key, + psa_export_public_key, psa_generate_key, psa_import_key, psa_sign_hash, psa_verify_hash, }; use parsec_interface::requests::{Opcode, ProviderId, Result}; use psa_crypto::types::key; @@ -20,18 +20,21 @@ use std::collections::HashSet; use std::sync::atomic::{AtomicU32, Ordering}; use uuid::Uuid; +mod asym_encryption; mod asym_sign; mod context; mod error; mod key_management; -const SUPPORTED_OPCODES: [Opcode; 6] = [ +const SUPPORTED_OPCODES: [Opcode; 8] = [ Opcode::PsaDestroyKey, Opcode::PsaGenerateKey, Opcode::PsaSignHash, Opcode::PsaVerifyHash, Opcode::PsaImportKey, Opcode::PsaExportPublicKey, + Opcode::PsaAsymmetricEncrypt, + Opcode::PsaAsymmetricDecrypt, ]; /// Trusted Service provider structure @@ -80,12 +83,8 @@ impl Provider { } }; - if ts_provider.context.check_key_exists(key_id)? { - if key_id > max_key_id { - max_key_id = key_id; - } - } else { - to_remove.push(key_triple.clone()); + if key_id > max_key_id { + max_key_id = key_id; } } } @@ -193,6 +192,24 @@ impl Provide for Provider { trace!("psa_verify_hash ingress"); self.psa_verify_hash_internal(app_name, op) } + + fn psa_asymmetric_encrypt( + &self, + app_name: ApplicationName, + op: psa_asymmetric_encrypt::Operation, + ) -> Result { + trace!("psa_asymmetric_encrypt ingress"); + self.psa_asymmetric_encrypt_internal(app_name, op) + } + + fn psa_asymmetric_decrypt( + &self, + app_name: ApplicationName, + op: psa_asymmetric_decrypt::Operation, + ) -> Result { + trace!("psa_asymmetric_decrypt ingress"); + self.psa_asymmetric_decrypt_internal(app_name, op) + } } /// Trusted Service provider builder diff --git a/trusted-services-vendor b/trusted-services-vendor index 2fc7e10c..c1cf9120 160000 --- a/trusted-services-vendor +++ b/trusted-services-vendor @@ -1 +1 @@ -Subproject commit 2fc7e10c7c21e4dafbf63dc9d00dfc2a7a7fddad +Subproject commit c1cf9120e4ab0b359a27176b079769b9a7e6bb87