pub struct BasicClient { /* private fields */ }
Expand description

Core client for the Parsec service

The client exposes low-level functionality for using the Parsec service. Below you can see code examples for a few of the operations supported.

Providers are abstracted representations of the secure elements that Parsec offers abstraction over. Providers are the ones to execute the cryptographic operations requested by the user.

For all cryptographic operations an implicit provider is used which can be changed between operations. The client starts with the default provider, the first one returned by the ListProviders operation.

For crypto operations, if the implicit client provider is ProviderId::Core, a client error of InvalidProvider type is returned. See the operation-specific response codes returned by the service in the operation’s page here.

Implementations§

source§

impl BasicClient

Main client functionality.

source

pub fn new(app_name: Option<String>) -> Result<Self>

Create a new Parsec client.

The client will be initialised with default values obtained from the service for the implicit provider and for application identity.

  • app_name is the application name to be used if direct authentication is the default authentication choice

This client will use the default configuration. That includes using a Protobuf converter for message bodies and a Unix Domain Socket IPC handler. The default timeout length is 60 seconds.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
source

pub fn new_naked() -> Result<Self>

Create a client that can initially only be used with Core operations not necessitating authentication (eg ping).

Setting an authentication method and an implicit provider is needed before calling crypto operations.

Example
use parsec_client::BasicClient;
let client = BasicClient::new_naked()?;
let (major, minor) = client.ping()?;
source

pub fn set_default_auth(&mut self, app_name: Option<String>) -> Result<()>

Query the service for the list of authenticators provided and use the first one as default

  • app_name is to be used if direct authentication is the default choice
Errors

If no authenticator is reported by the service, a NoAuthenticator error kind is returned.

If the default authenticator is DirectAuthenticator and app_name was set to None, an error of type MissingParam is returned.

If none of the authenticators returned by the service is supported, NoAuthenticator is returned.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::requests::ProviderId;
let mut client = BasicClient::new_naked()?;
client.set_implicit_provider(ProviderId::Pkcs11);
client.set_default_auth(Some("main_client".to_string()))?;
source

pub fn set_auth_data(&mut self, auth_data: Authentication)

Update the authentication data of the client.

This is useful if you want to use a different authentication method than the default one.

Example

See [set_default_provider].

source

pub fn auth_data(&self) -> Authentication

Retrieve authentication data of the client.

Example
use parsec_client::BasicClient;
use parsec_client::auth::Authentication;
let mut client = BasicClient::new_naked()?;
client.set_auth_data(Authentication::UnixPeerCredentials);
assert_eq!(Authentication::UnixPeerCredentials, client.auth_data());
source

pub fn set_default_provider(&mut self) -> Result<()>

Query for the service provider list and set the default one as implicit

Errors

If no provider is returned by the service, an client error of NoProvider type is returned.

Example
use parsec_client::BasicClient;
use parsec_client::auth::Authentication;
let mut client = BasicClient::new_naked()?;
client.set_default_provider()?;
client.set_auth_data(Authentication::UnixPeerCredentials);
source

pub fn set_implicit_provider(&mut self, provider: ProviderId)

Set the provider that the client will be implicitly working with.

Example

See [set_default_auth].

source

pub fn implicit_provider(&self) -> ProviderId

Retrieve client’s implicit provider.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::requests::ProviderId;
let mut client = BasicClient::new_naked()?;
client.set_implicit_provider(ProviderId::Pkcs11);
assert_eq!(ProviderId::Pkcs11, client.implicit_provider());
source

pub fn list_opcodes(&self, provider: ProviderId) -> Result<HashSet<Opcode>>

[Core Operation] List the opcodes supported by the specified provider.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::requests::{Opcode, ProviderId};

let client: BasicClient = BasicClient::new(None)?;
let opcodes = client.list_opcodes(ProviderId::Pkcs11)?;
if opcodes.contains(&Opcode::PsaGenerateRandom) {
    let random_bytes = client.psa_generate_random(10)?;
}
source

pub fn list_providers(&self) -> Result<Vec<ProviderInfo>>

[Core Operation] List the providers that are supported by the service.

Example
use parsec_client::BasicClient;

let mut client: BasicClient = BasicClient::new_naked()?;
let providers = client.list_providers()?;
client.set_implicit_provider(providers[1].id);
source

pub fn list_authenticators(&self) -> Result<Vec<AuthenticatorInfo>>

[Core Operation] List the authenticators that are supported by the service.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
let opcodes = client.list_authenticators()?;
source

pub fn list_keys(&self) -> Result<Vec<KeyInfo>>

[Core Operation] List all keys belonging to the application.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
let keys = client.list_keys()?;
source

pub fn key_attributes(&self, key_name: &str) -> Result<Attributes>

Get the key attributes.

This is a convenience method that uses list_keys underneath.

Errors

Returns NotFound if a key with this name does not exist.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
let attributes = client.key_attributes("my_key")?;
source

pub fn list_clients(&self) -> Result<Vec<String>>

[Core Operation, Admin Operation] Lists all clients currently having data in the service.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
let clients = client.list_clients()?;
source

pub fn delete_client(&self, client: &str) -> Result<()>

[Core Operation, Admin Operation] Delete all data a client has in the service.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
client.delete_client("main_client")?;
source

pub fn ping(&self) -> Result<(u8, u8)>

[Core Operation] Send a ping request to the service.

This operation is intended for testing connectivity to the service and for retrieving the maximum wire protocol version it supports.

Example

See [new_naked].

source

pub fn psa_generate_key( &self, key_name: &str, key_attributes: Attributes ) -> Result<()>

[Cryptographic Operation] Generate a key.

Creates a new key with the given name within the namespace of the implicit client provider. Any UTF-8 string is considered a valid key name, however names must be unique per provider.

Persistence of keys is implemented at provider level, and currently all providers persist all the keys users create.

If this method returns an error, no key will have been generated and the name used will still be available for another key.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::operations::psa_key_attributes::{Attributes, Lifetime, Policy, Type, UsageFlags};
use parsec_client::core::interface::operations::psa_algorithm::{AsymmetricSignature, Hash};

let client: BasicClient = BasicClient::new(None)?;
let key_attrs = Attributes {
    lifetime: Lifetime::Persistent,
    key_type: Type::RsaKeyPair,
    bits: 2048,
    policy: Policy {
        usage_flags: UsageFlags::default(),
        permitted_algorithms: AsymmetricSignature::RsaPkcs1v15Sign {
            hash_alg: Hash::Sha256.into(),
        }.into(),
    },
};
client.psa_generate_key("my_key", key_attrs)?;
source

pub fn psa_destroy_key(&self, key_name: &str) -> Result<()>

[Cryptographic Operation] Destroy a key.

Given that keys are namespaced at a provider level, it is important to call psa_destroy_key on the correct combination of implicit client provider and key_name.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
client.psa_destroy_key("my_key")?;
source

pub fn psa_import_key( &self, key_name: &str, key_material: &[u8], key_attributes: Attributes ) -> Result<()>

[Cryptographic Operation] Import a key.

Creates a new key with the given name within the namespace of the implicit client provider using the user-provided data. Any UTF-8 string is considered a valid key name, however names must be unique per provider.

The key material should follow the appropriate binary format expressed here. Several crates (e.g. picky-asn1) can greatly help in dealing with binary encodings.

If this method returns an error, no key will have been imported and the name used will still be available for another key.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::operations::psa_key_attributes::{Attributes, Lifetime, Policy, Type, UsageFlags, EccFamily};
use parsec_client::core::interface::operations::psa_algorithm::{AsymmetricSignature, Hash};

let client: BasicClient = BasicClient::new(None)?;
let ecc_private_key = vec![
    0x26, 0xc8, 0x82, 0x9e, 0x22, 0xe3, 0x0c, 0xa6, 0x3d, 0x29, 0xf5, 0xf7, 0x27, 0x39, 0x58, 0x47,
    0x41, 0x81, 0xf6, 0x57, 0x4f, 0xdb, 0xcb, 0x4d, 0xbb, 0xdd, 0x52, 0xff, 0x3a, 0xc0, 0xf6, 0x0d,
];
let key_attrs = Attributes {
    lifetime: Lifetime::Persistent,
    key_type: Type::EccKeyPair {
        curve_family: EccFamily::SecpR1,
    },
    bits: 256,
    policy: Policy {
        usage_flags: UsageFlags::default(),
        permitted_algorithms: AsymmetricSignature::RsaPkcs1v15Sign {
            hash_alg: Hash::Sha256.into(),
        }.into(),
    },
};
client.psa_import_key("my_key", &ecc_private_key, key_attrs)?;
source

pub fn psa_export_public_key(&self, key_name: &str) -> Result<Vec<u8>>

[Cryptographic Operation] Export a public key or the public part of a key pair.

The returned key material will follow the appropriate binary format expressed here. Several crates (e.g. picky-asn1) can greatly help in dealing with binary encodings.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
let public_key_data = client.psa_export_public_key("my_key");
source

pub fn psa_export_key(&self, key_name: &str) -> Result<Vec<u8>>

[Cryptographic Operation] Export a key.

The returned key material will follow the appropriate binary format expressed here. Several crates (e.g. picky-asn1) can greatly help in dealing with binary encodings.

Example
use parsec_client::BasicClient;

let client: BasicClient = BasicClient::new(None)?;
let key_data = client.psa_export_key("my_key");
source

pub fn psa_sign_hash( &self, key_name: &str, hash: &[u8], sign_algorithm: AsymmetricSignature ) -> Result<Vec<u8>>

[Cryptographic Operation] Create an asymmetric signature on a pre-computed message digest.

The key intended for signing must have its sign_hash flag set to true in its key policy.

The signature will be created with the algorithm defined in sign_algorithm, but only after checking that the key policy and type conform with it.

hash must be a hash pre-computed over the message of interest with the algorithm specified within sign_algorithm.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::operations::psa_key_attributes::{Attributes, Lifetime, Policy, Type, UsageFlags};
use parsec_client::core::interface::operations::psa_algorithm::{AsymmetricSignature, Hash};

let client: BasicClient = BasicClient::new(None)?;
let hash = vec![
  0x69, 0x3E, 0xDB, 0x1B, 0x22, 0x79, 0x03, 0xF4, 0xC0, 0xBF, 0xD6, 0x91, 0x76, 0x37, 0x84, 0xA2,
  0x94, 0x8E, 0x92, 0x50, 0x35, 0xC2, 0x8C, 0x5C, 0x3C, 0xCA, 0xFE, 0x18, 0xE8, 0x81, 0x37, 0x78,
];
let signature = client.psa_sign_hash("my_key", &hash, AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256.into(),
})?;
source

pub fn psa_verify_hash( &self, key_name: &str, hash: &[u8], sign_algorithm: AsymmetricSignature, signature: &[u8] ) -> Result<()>

[Cryptographic Operation] Verify an existing asymmetric signature over a pre-computed message digest.

The key intended for signing must have its verify_hash flag set to true in its key policy.

The signature will be verifyied with the algorithm defined in sign_algorithm, but only after checking that the key policy and type conform with it.

hash must be a hash pre-computed over the message of interest with the algorithm specified within sign_algorithm.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::operations::psa_key_attributes::{Attributes, Lifetime, Policy, Type, UsageFlags};
use parsec_client::core::interface::operations::psa_algorithm::{AsymmetricSignature, Hash};

let client: BasicClient = BasicClient::new(None)?;
let hash = vec![
    0x69, 0x3E, 0xDB, 0x1B, 0x22, 0x79, 0x03, 0xF4, 0xC0, 0xBF, 0xD6, 0x91, 0x76, 0x37, 0x84, 0xA2,
    0x94, 0x8E, 0x92, 0x50, 0x35, 0xC2, 0x8C, 0x5C, 0x3C, 0xCA, 0xFE, 0x18, 0xE8, 0x81, 0x37, 0x78,
];
let alg = AsymmetricSignature::RsaPkcs1v15Sign {
    hash_alg: Hash::Sha256.into(),
};
let signature = client.psa_sign_hash("my_key", &hash, alg)?;
client.psa_verify_hash("my_key", &hash, alg, &signature)?;
source

pub fn psa_sign_message( &self, key_name: &str, msg: &[u8], sign_algorithm: AsymmetricSignature ) -> Result<Vec<u8>>

[Cryptographic Operation] Create an asymmetric signature on a message.

The key intended for signing must have its sign_message flag set to true in its key policy.

The signature will be created with the algorithm defined in sign_algorithm, but only after checking that the key policy and type conform with it.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::operations::psa_key_attributes::{Attributes, Lifetime, Policy, Type, UsageFlags};
use parsec_client::core::interface::operations::psa_algorithm::{AsymmetricSignature, Hash};

let client: BasicClient = BasicClient::new(None)?;
let message = "This is the message to sign which can be of any size!".as_bytes();
let signature = client.psa_sign_message(
    "my_key",
    message,
    AsymmetricSignature::RsaPkcs1v15Sign {
        hash_alg: Hash::Sha256.into(),
    }
)?;
source

pub fn psa_verify_message( &self, key_name: &str, msg: &[u8], sign_algorithm: AsymmetricSignature, signature: &[u8] ) -> Result<()>

[Cryptographic Operation] Verify an existing asymmetric signature over a message.

The key intended for signing must have its verify_message flag set to true in its key policy.

The signature will be verifyied with the algorithm defined in sign_algorithm, but only after checking that the key policy and type conform with it.

Example
use parsec_client::BasicClient;
use parsec_client::core::interface::operations::psa_key_attributes::{Attributes, Lifetime, Policy, Type, UsageFlags};
use parsec_client::core::interface::operations::psa_algorithm::{AsymmetricSignature, Hash};

let client: BasicClient = BasicClient::new(None)?;
let message = "This is the message to sign which can be of any size!".as_bytes();
let alg = AsymmetricSignature::RsaPkcs1v15Sign {
    hash_alg: Hash::Sha256.into(),
};
let signature = client.psa_sign_message("my_key", message, alg)?;
client.psa_verify_message("my_key", message, alg, &signature)?;
source

pub fn psa_asymmetric_encrypt( &self, key_name: &str, encrypt_alg: AsymmetricEncryption, plaintext: &[u8], salt: Option<&[u8]> ) -> Result<Vec<u8>>

[Cryptographic Operation] Encrypt a short message.

The key intended for encrypting must have its encrypt flag set to true in its key policy.

The encryption will be performed with the algorithm defined in alg, but only after checking that the key policy and type conform with it.

salt can be provided if supported by the algorithm. If the algorithm does not support salt, pass an empty vector. If the algorithm supports optional salt, pass an empty vector to indicate no salt. For RSA PKCS#1 v1.5 encryption, no salt is supported.

source

pub fn psa_asymmetric_decrypt( &self, key_name: &str, encrypt_alg: AsymmetricEncryption, ciphertext: &[u8], salt: Option<&[u8]> ) -> Result<Vec<u8>>

[Cryptographic Operation] Decrypt a short message.

The key intended for decrypting must have its decrypt flag set to true in its key policy.

salt can be provided if supported by the algorithm. If the algorithm does not support salt, pass an empty vector. If the algorithm supports optional salt, pass an empty vector to indicate no salt. For RSA PKCS#1 v1.5 encryption, no salt is supported.

The decryption will be performed with the algorithm defined in alg, but only after checking that the key policy and type conform with it.

source

pub fn psa_hash_compute(&self, alg: Hash, input: &[u8]) -> Result<Vec<u8>>

[Cryptographic Operation] Compute hash of a message.

The hash computation will be performed with the algorithm defined in alg.

source

pub fn psa_hash_compare( &self, alg: Hash, input: &[u8], hash: &[u8] ) -> Result<()>

[Cryptographic Operation] Compute hash of a message and compare it with a reference value.

The hash computation will be performed with the algorithm defined in alg.

If this operation returns no error, the hash was computed successfully and it matches the reference value.

source

pub fn psa_aead_encrypt( &self, key_name: &str, encrypt_alg: Aead, nonce: &[u8], additional_data: &[u8], plaintext: &[u8] ) -> Result<Vec<u8>>

[Cryptographic Operation] Authenticate and encrypt a short message.

The key intended for decrypting must have its encrypt flag set to true in its key policy.

The encryption will be performed with the algorithm defined in alg, but only after checking that the key policy and type conform with it.

nonce must be appropriate for the selected alg.

For algorithms where the encrypted data and the authentication tag are defined as separate outputs, the returned buffer will contain the encrypted data followed by the authentication data.

source

pub fn psa_aead_decrypt( &self, key_name: &str, encrypt_alg: Aead, nonce: &[u8], additional_data: &[u8], ciphertext: &[u8] ) -> Result<Vec<u8>>

[Cryptographic Operation] Decrypt and authenticate a short message.

The key intended for decrypting must have its decrypt flag set to true in its key policy.

The decryption will be performed with the algorithm defined in alg, but only after checking that the key policy and type conform with it.

nonce must be appropriate for the selected alg.

For algorithms where the encrypted data and the authentication tag are defined as separate inputs, ciphertext must contain the encrypted data followed by the authentication data.

source

pub fn psa_cipher_encrypt( &self, key_name: String, alg: Cipher, plaintext: &[u8] ) -> Result<Vec<u8>>

[Cryptographic Operation] Encrypt a short message with a symmetric cipher.

The key intended for encrypting must have its encrypt flag set to true in its key policy.

This function will encrypt a short message with a random initialisation vector (IV).

source

pub fn psa_cipher_decrypt( &self, key_name: String, alg: Cipher, ciphertext: &[u8] ) -> Result<Vec<u8>>

[Cryptographic Operation] Decrypt a short message with a symmetric cipher.

The key intended for decrypting must have its decrypt flag set to true in its key policy.

ciphertext must be the IV followed by the ciphertext.

This function will decrypt a short message using the provided initialisation vector (IV).

source

pub fn psa_raw_key_agreement( &self, alg: RawKeyAgreement, private_key_name: &str, peer_key: &[u8] ) -> Result<Vec<u8>>

[Cryptographic Operation] Perform a raw key agreement.

The provided private key must have its derive flag set to true in its key policy.

The raw_key_agreement will be performed with the algorithm defined in alg, but only after checking that the key policy and type conform with it.

peer_key must be the peer public key to use in the raw key derivation. It must be in a format supported by PsaImportKey.

source

pub fn psa_generate_random(&self, nbytes: usize) -> Result<Vec<u8>>

[Cryptographic Operation] Generate some random bytes.

Generates a sequence of random bytes and returns them to the user.

If this method returns an error, no bytes will have been generated.

Example

See [list_opcodes].

source

pub fn can_do_crypto( &self, check_type: CheckType, attributes: Attributes ) -> Result<()>

[Capability Discovery Operation] Check if attributes are supported.

Checks if the given attributes are supported for the given type of operation.

#Errors

This operation will either return Ok(()) or Err(PsaErrorNotSupported) indicating whether the attributes are supported.

See the operation-specific response codes returned by the service here.

source

pub fn prepare_activate_credential( &self, attested_key_name: String, attesting_key_name: Option<String> ) -> Result<PrepareActivateCredential>

[Cryptographic Operation] Get data required to prepare an ActivateCredential key attestation.

Retrieve the binary blobs required by a third party to perform a MakeCredential operation, in preparation for a key attestation using ActivateCredential.

This key attestation method is TPM-specific

source

pub fn activate_credential_attestation( &self, attested_key_name: String, attesting_key_name: Option<String>, credential_blob: Vec<u8>, secret: Vec<u8> ) -> Result<Vec<u8>>

[Cryptographic Operation] Perform a key attestation operation via ActivateCredential

This key attestation method is TPM-specific

You can see more details on the inner-workings, and on the requirements for this operation here.

Before performing an ActivateCredential attestation you must compute the credential_blob and secret parameters using the outputs from the prepare_activate_credential method.

source§

impl BasicClient

Configuration methods for controlling communication with the service.

source

pub fn set_request_body_converter( &mut self, content_converter: Box<dyn Convert + Send + Sync> )

Set the converter used for request bodies handled by this client.

By default Protobuf will be used for this.

source

pub fn set_response_body_converter( &mut self, accept_converter: Box<dyn Convert + Send + Sync> )

Set the converter used for response bodies handled by this client.

By default Protobuf will be used for this.

source§

impl BasicClient

Configuration methods for controlling IPC-level options.

source

pub fn set_max_body_size(&mut self, max_body_size: usize)

Set the maximum body size allowed for requests.

Defaults to the maximum value of usize.

source

pub fn set_ipc_handler(&mut self, ipc_handler: Box<dyn Connect + Send + Sync>)

Set the IPC handler used for communication with the service.

By default the Unix domain socket client is used.

source

pub fn set_timeout(&mut self, timeout: Option<Duration>)

Set the timeout for operations on the IPC stream.

The value defaults to 1 second.

Trait Implementations§

source§

impl Debug for BasicClient

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for BasicClient

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.