Skip to content

Commit

Permalink
Merge pull request #23 from ionut-arm/option-provider
Browse files Browse the repository at this point in the history
Make `implicit_provider` optional
  • Loading branch information
ionut-arm authored Apr 21, 2020
2 parents 21c406f + 734d3a3 commit 2b27aa9
Showing 4 changed files with 104 additions and 50 deletions.
107 changes: 63 additions & 44 deletions src/core/basic_client.rs
Original file line number Diff line number Diff line change
@@ -24,16 +24,23 @@ use std::collections::HashSet;
/// The client exposes low-level functionality for using the Parsec service.
/// Below you can see code examples for a few of the operations supported.
///
/// For all cryptographic operations an implicit provider is used which can be
/// changed between operations. The client starts with no such defined provider
/// and it is the responsibility of the user to identify and set an appropriate
/// one. As such, it is critical that before attempting to use cryptographic
/// operations users call [`list_providers`](#method.list_providers)
/// and [`list_opcodes`](#method.list_opcodes)
/// in order to figure out if their desired use case and provider are
/// available.
///
/// Creating a `BasicClient` instance:
///```no_run
///use parsec_client::auth::AuthenticationData;
///use parsec_client::BasicClient;
///use parsec_client::core::ProviderID;
///
///let app_name = String::from("app-name");
///let app_auth_data = AuthenticationData::AppIdentity(app_name);
///let desired_provider = ProviderID::Pkcs11;
///let client: BasicClient = BasicClient::new(app_auth_data, desired_provider);
///let client: BasicClient = BasicClient::new(app_auth_data);
///```
///
/// Performing a Ping operation helps to determine if the service is available
@@ -43,7 +50,7 @@ use std::collections::HashSet;
///# use parsec_client::auth::AuthenticationData;
///# use parsec_client::BasicClient;
///# use parsec_client::core::ProviderID;
///# let client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")), ProviderID::Pkcs11);
///# let client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")));
///let res = client.ping();
///
///if let Ok((wire_prot_v_maj, wire_prot_v_min)) = res {
@@ -65,7 +72,7 @@ use std::collections::HashSet;
///# use parsec_client::auth::AuthenticationData;
///# use parsec_client::BasicClient;
///# use parsec_client::core::ProviderID;
///# let client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")), ProviderID::Pkcs11);
///# let client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")));
///use uuid::Uuid;
///
///// Identify provider by its UUID (in this case, the PKCS11 provider)
@@ -82,30 +89,33 @@ use std::collections::HashSet;
///```
///
/// Checking operations supported by the provider we're interested in is done
/// through the `list_provider_operations` method:
/// through the `list_opcodes` method:
///```no_run
///# use parsec_client::auth::AuthenticationData;
///# use parsec_client::BasicClient;
///# use parsec_client::core::ProviderID;
///# let client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")), ProviderID::Pkcs11);
///# let mut client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")));
///use parsec_client::core::Opcode;
///
///let desired_provider = ProviderID::Pkcs11;
///let provider_opcodes = client
/// .list_provider_operations(desired_provider)
/// .list_opcodes(desired_provider)
/// .expect("Failed to list opcodes");
///// Each operation is identified by a specific `Opcode`
///assert!(provider_opcodes.contains(&Opcode::PsaGenerateKey));
///assert!(provider_opcodes.contains(&Opcode::PsaSignHash));
///assert!(provider_opcodes.contains(&Opcode::PsaDestroyKey));
///
///// Now that we're certain our desired provider offers all the functionality we need...
///client.set_implicit_provider(desired_provider);
///```
///
/// Creating a key-pair for signing SHA256 digests with RSA PKCS#1 v1.5:
///```no_run
///# use parsec_client::auth::AuthenticationData;
///# use parsec_client::BasicClient;
///# use parsec_client::core::ProviderID;
///# let client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")), ProviderID::Pkcs11);
///# let client: BasicClient = BasicClient::new(AuthenticationData::AppIdentity(String::from("app-name")));
///use parsec_client::core::psa_algorithm::{Algorithm, AsymmetricSignature, Hash};
///use parsec_client::core::psa_key_attributes::{KeyAttributes, KeyPolicy, KeyType, UsageFlags};
///
@@ -143,35 +153,25 @@ use std::collections::HashSet;
/// .psa_generate_key(key_name, key_attrs)
/// .expect("Failed to create key!");
///```
///
/// It is recommended that before attempting to use cryptographic
/// operations users call [`list_providers`](#method.list_providers)
/// and [`list_provider_operations`](#method.list_provider_operations)
/// in order to figure out if their desired use case and provider are
/// available.
#[derive(Debug)]
pub struct BasicClient {
pub(crate) op_client: OperationClient,
pub(crate) auth_data: AuthenticationData,
pub(crate) implicit_provider: ProviderID,
pub(crate) implicit_provider: Option<ProviderID>,
}

/// Main client functionality.
impl BasicClient {
/// Create a new Parsec client given the authentication data of the app and a provider ID.
///
/// The `implicit_provider` will be used for all non-core operations. Thus, until a
/// different provider ID is set using [`set_implicit_provider`](#method.set_implicit_provider),
/// all cryptographic operations will be executed in the context of this provider (if it is
/// available within the service).
/// Create a new Parsec client given the authentication data of the app.
///
/// In order to get a list of supported providers, call the [`list_providers`](#method.list_providers)
/// method.
pub fn new(auth_data: AuthenticationData, implicit_provider: ProviderID) -> Self {
/// Before you can use this client for cryptographic operations, you first need to call
/// [`set_implicit_provider`](#method.set_implicit_provider). In order to get a list of
/// supported providers, call the [`list_providers`](#method.list_providers) method.
pub fn new(auth_data: AuthenticationData) -> Self {
BasicClient {
op_client: Default::default(),
auth_data,
implicit_provider,
implicit_provider: None,
}
}

@@ -187,16 +187,16 @@ impl BasicClient {

/// Set the provider that the client will be implicitly working with.
pub fn set_implicit_provider(&mut self, provider: ProviderID) {
self.implicit_provider = provider;
self.implicit_provider = Some(provider);
}

/// Retrieve client's implicit provider.
pub fn implicit_provider(&self) -> ProviderID {
pub fn implicit_provider(&self) -> Option<ProviderID> {
self.implicit_provider
}

/// **[Core Operation]** List the opcodes supported by the specified provider.
pub fn list_provider_operations(&self, provider: ProviderID) -> Result<HashSet<Opcode>> {
pub fn list_opcodes(&self, provider: ProviderID) -> Result<HashSet<Opcode>> {
let res = self.op_client.process_operation(
NativeOperation::ListOpcodes(ListOpcodes {
provider_id: provider,
@@ -271,10 +271,13 @@ impl BasicClient {
/// If the implicit client provider is `ProviderID::Core`, a client error
/// of `InvalidProvider` type is returned.
///
/// If the implicit client provider has not been set, a client error of
/// `NoProvider` type is returned.
///
/// See the operation-specific response codes returned by the service
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_generate_key.html#specific-response-status-codes).
pub fn psa_generate_key(&self, key_name: String, key_attributes: KeyAttributes) -> Result<()> {
self.can_provide_crypto()?;
let crypto_provider = self.can_provide_crypto()?;

let op = PsaGenerateKey {
key_name,
@@ -283,7 +286,7 @@ impl BasicClient {

let _ = self.op_client.process_operation(
NativeOperation::PsaGenerateKey(op),
self.implicit_provider,
crypto_provider,
&self.auth_data,
)?;

@@ -301,16 +304,19 @@ impl BasicClient {
/// If the implicit client provider is `ProviderID::Core`, a client error
/// of `InvalidProvider` type is returned.
///
/// If the implicit client provider has not been set, a client error of
/// `NoProvider` type is returned.
///
/// See the operation-specific response codes returned by the service
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_destroy_key.html#specific-response-status-codes).
pub fn psa_destroy_key(&self, key_name: String) -> Result<()> {
self.can_provide_crypto()?;
let crypto_provider = self.can_provide_crypto()?;

let op = PsaDestroyKey { key_name };

let _ = self.op_client.process_operation(
NativeOperation::PsaDestroyKey(op),
self.implicit_provider,
crypto_provider,
&self.auth_data,
)?;

@@ -341,6 +347,9 @@ impl BasicClient {
/// If the implicit client provider is `ProviderID::Core`, a client error
/// of `InvalidProvider` type is returned.
///
/// If the implicit client provider has not been set, a client error of
/// `NoProvider` type is returned.
///
/// See the operation-specific response codes returned by the service
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_import_key.html#specific-response-status-codes).
pub fn psa_import_key(
@@ -349,7 +358,7 @@ impl BasicClient {
key_material: Vec<u8>,
key_attributes: KeyAttributes,
) -> Result<()> {
self.can_provide_crypto()?;
let crypto_provider = self.can_provide_crypto()?;

let op = PsaImportKey {
key_name,
@@ -359,7 +368,7 @@ impl BasicClient {

let _ = self.op_client.process_operation(
NativeOperation::PsaImportKey(op),
self.implicit_provider,
crypto_provider,
&self.auth_data,
)?;

@@ -382,16 +391,19 @@ impl BasicClient {
/// If the implicit client provider is `ProviderID::Core`, a client error
/// of `InvalidProvider` type is returned.
///
/// If the implicit client provider has not been set, a client error of
/// `NoProvider` type is returned.
///
/// See the operation-specific response codes returned by the service
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_export_public_key.html#specific-response-status-codes).
pub fn psa_export_public_key(&self, key_name: String) -> Result<Vec<u8>> {
self.can_provide_crypto()?;
let crypto_provider = self.can_provide_crypto()?;

let op = PsaExportPublicKey { key_name };

let res = self.op_client.process_operation(
NativeOperation::PsaExportPublicKey(op),
self.implicit_provider,
crypto_provider,
&self.auth_data,
)?;

@@ -421,6 +433,9 @@ impl BasicClient {
/// If the implicit client provider is `ProviderID::Core`, a client error
/// of `InvalidProvider` type is returned.
///
/// If the implicit client provider has not been set, a client error of
/// `NoProvider` type is returned.
///
/// See the operation-specific response codes returned by the service
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_sign_hash.html#specific-response-status-codes).
pub fn psa_sign_hash(
@@ -429,7 +444,7 @@ impl BasicClient {
hash: Vec<u8>,
sign_algorithm: AsymmetricSignature,
) -> Result<Vec<u8>> {
self.can_provide_crypto()?;
let crypto_provider = self.can_provide_crypto()?;

let op = PsaSignHash {
key_name,
@@ -439,7 +454,7 @@ impl BasicClient {

let res = self.op_client.process_operation(
NativeOperation::PsaSignHash(op),
self.implicit_provider,
crypto_provider,
&self.auth_data,
)?;

@@ -469,6 +484,9 @@ impl BasicClient {
/// If the implicit client provider is `ProviderID::Core`, a client error
/// of `InvalidProvider` type is returned.
///
/// If the implicit client provider has not been set, a client error of
/// `NoProvider` type is returned.
///
/// See the operation-specific response codes returned by the service
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_verify_hash.html#specific-response-status-codes).
pub fn psa_verify_hash(
@@ -478,7 +496,7 @@ impl BasicClient {
sign_algorithm: AsymmetricSignature,
signature: Vec<u8>,
) -> Result<()> {
self.can_provide_crypto()?;
let crypto_provider = self.can_provide_crypto()?;

let op = PsaVerifyHash {
key_name,
@@ -489,17 +507,18 @@ impl BasicClient {

let _ = self.op_client.process_operation(
NativeOperation::PsaVerifyHash(op),
self.implicit_provider,
crypto_provider,
&self.auth_data,
)?;

Ok(())
}

fn can_provide_crypto(&self) -> Result<()> {
fn can_provide_crypto(&self) -> Result<ProviderID> {
match self.implicit_provider {
ProviderID::Core => Err(Error::Client(ClientErrorKind::InvalidProvider)),
_ => Ok(()),
None => Err(Error::Client(ClientErrorKind::NoProvider)),
Some(ProviderID::Core) => Err(Error::Client(ClientErrorKind::InvalidProvider)),
Some(crypto_provider) => Ok(crypto_provider),
}
}
}
29 changes: 27 additions & 2 deletions src/core/testing/core_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
use super::{FailingMockIpc, TestBasicClient, DEFAULT_APP_NAME};
use crate::auth::AuthenticationData;
use crate::core::ProviderID;
use crate::error::{ClientErrorKind, Error};
use crate::BasicClient;
use mockstream::{FailingMockStream, MockStream};
use parsec_interface::operations;
use parsec_interface::operations::list_providers::ProviderInfo;
@@ -95,7 +97,7 @@ fn list_provider_test() {
}

#[test]
fn list_provider_operations_test() {
fn list_opcodes_test() {
let mut client: TestBasicClient = Default::default();
let mut opcodes = HashSet::new();
let _ = opcodes.insert(Opcode::PsaDestroyKey);
@@ -104,7 +106,7 @@ fn list_provider_operations_test() {
operations::list_opcodes::Result { opcodes },
)));
let opcodes = client
.list_provider_operations(ProviderID::MbedCrypto)
.list_opcodes(ProviderID::MbedCrypto)
.expect("Failed to retrieve opcodes");
// Check request:
// ListOpcodes request is empty so no checking to be done
@@ -114,6 +116,29 @@ fn list_provider_operations_test() {
assert!(opcodes.contains(&Opcode::PsaGenerateKey) && opcodes.contains(&Opcode::PsaDestroyKey));
}

#[test]
fn no_crypto_provider_test() {
let client = BasicClient::new(AuthenticationData::AppIdentity(String::from("oops")));

let res = client
.psa_destroy_key(String::from("random key"))
.expect_err("Expected a failure!!");

assert_eq!(res, Error::Client(ClientErrorKind::NoProvider));
}

#[test]
fn core_provider_for_crypto_test() {
let mut client = BasicClient::new(AuthenticationData::AppIdentity(String::from("oops")));

client.set_implicit_provider(ProviderID::Core);
let res = client
.psa_destroy_key(String::from("random key"))
.expect_err("Expected a failure!!");

assert_eq!(res, Error::Client(ClientErrorKind::InvalidProvider));
}

#[test]
fn psa_generate_key_test() {
let mut client: TestBasicClient = Default::default();
9 changes: 5 additions & 4 deletions src/core/testing/mod.rs
Original file line number Diff line number Diff line change
@@ -61,13 +61,14 @@ impl DerefMut for TestBasicClient {
impl Default for TestBasicClient {
fn default() -> Self {
let mut client = TestBasicClient {
core_client: BasicClient::new(
AuthenticationData::AppIdentity(String::from(DEFAULT_APP_NAME)),
ProviderID::Pkcs11,
),
core_client: BasicClient::new(AuthenticationData::AppIdentity(String::from(
DEFAULT_APP_NAME,
))),
mock_stream: SyncMockStream::new(),
};

client.core_client.set_implicit_provider(ProviderID::Pkcs11);

client
.core_client
.set_ipc_handler(Box::from(MockIpc(client.mock_stream.clone())));
Loading

0 comments on commit 2b27aa9

Please # to comment.