use super::Provide;
use crate::authenticators::ApplicationIdentity;
use crate::key_info_managers::{KeyIdentity, KeyInfoManagerClient};
use crate::providers::ProviderIdentity;
use crate::providers::cryptoauthlib::key_slot_storage::KeySlotStorage;
use derivative::Derivative;
use log::{error, trace, warn};
use parsec_interface::operations::list_providers::ProviderInfo;
use parsec_interface::operations::list_providers::Uuid;
use parsec_interface::operations::{list_clients, list_keys};
use parsec_interface::requests::{Opcode, ProviderId, ResponseStatus, Result};
use std::collections::HashSet;
use std::io::{Error, ErrorKind};
use parsec_interface::operations::{
psa_aead_decrypt, psa_aead_encrypt, psa_cipher_decrypt, psa_cipher_encrypt, psa_destroy_key,
psa_export_key, psa_export_public_key, psa_generate_key, psa_generate_random, psa_hash_compare,
psa_hash_compute, psa_import_key, psa_raw_key_agreement, psa_sign_hash, psa_sign_message,
psa_verify_hash, psa_verify_message,
};
mod access_keys;
mod aead;
mod asym_sign;
mod cipher;
mod generate_random;
mod hash;
mod key_agreement;
mod key_management;
mod key_slot;
mod key_slot_storage;
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Provider {
#[derivative(Debug = "ignore")]
device: rust_cryptoauthlib::AteccDevice,
provider_id: ProviderId,
provider_identity: ProviderIdentity,
#[derivative(Debug = "ignore")]
key_info_store: KeyInfoManagerClient,
key_slots: KeySlotStorage,
supported_opcodes: HashSet<Opcode>,
}
impl Provider {
pub const DEFAULT_PROVIDER_NAME: &'static str = "cryptoauthlib-provider";
pub const PROVIDER_UUID: &'static str = "b8ba81e2-e9f7-4bdd-b096-a29d0019960c";
fn new(
provider_name: String,
key_info_store: KeyInfoManagerClient,
atca_iface: rust_cryptoauthlib::AtcaIfaceCfg,
access_key_file_name: Option<String>,
) -> Option<Provider> {
let device = match rust_cryptoauthlib::setup_atecc_device(atca_iface) {
Ok(dev) => dev,
Err(err) => {
error!("ATECC device initialization failed: {}", err);
return None;
}
};
if !device.is_configuration_locked() {
error!("Error: configuration is not locked.");
return None;
}
let mut cryptoauthlib_provider = Provider {
device,
provider_id: ProviderId::CryptoAuthLib,
provider_identity: ProviderIdentity {
name: provider_name,
uuid: String::from(Self::PROVIDER_UUID),
},
key_info_store,
key_slots: KeySlotStorage::new(),
supported_opcodes: HashSet::new(),
};
let mut atecc_config_vec = Vec::<rust_cryptoauthlib::AtcaSlot>::new();
let err = cryptoauthlib_provider
.device
.get_config(&mut atecc_config_vec);
if rust_cryptoauthlib::AtcaStatus::AtcaSuccess != err {
error!("atecc_get_config failed: {}", err);
return None;
}
if let Err(err) = cryptoauthlib_provider
.key_slots
.set_hw_config(&atecc_config_vec)
{
error!("Applying hardware configuration failed: {}", err);
return None;
}
let mut to_remove: Vec<KeyIdentity> = Vec::new();
match cryptoauthlib_provider.key_info_store.get_all() {
Ok(key_identities) => {
for key_identity in key_identities.iter() {
match cryptoauthlib_provider
.key_info_store
.does_not_exist(key_identity)
{
Ok(x) => x,
Err(err) => {
warn!(
"Error getting the Key ID for KeyIdentity:\n{}\n(error: {}), continuing...",
key_identity, err
);
to_remove.push(key_identity.clone());
continue;
}
};
let key_info_id = match cryptoauthlib_provider
.key_info_store
.get_key_id::<u8>(key_identity)
{
Ok(x) => x,
Err(err) => {
warn!(
"Could not get key info id for KeyIdentity {:?} because {}",
key_identity, err
);
to_remove.push(key_identity.clone());
continue;
}
};
let key_info_attributes = match cryptoauthlib_provider
.key_info_store
.get_key_attributes(key_identity)
{
Ok(x) => x,
Err(err) => {
warn!(
"Could not get key attributes for KeyIdentity {:?} because {}",
key_identity, err
);
to_remove.push(key_identity.clone());
continue;
}
};
match cryptoauthlib_provider
.key_slots
.key_validate_and_mark_busy(key_info_id, &key_info_attributes)
{
Ok(None) => (),
Ok(Some(warning)) => {
warn!("{} for KeyIdentity {:?}", warning, key_identity)
}
Err(err) => {
warn!("{} for KeyIdentity {:?}", err, key_identity);
to_remove.push(key_identity.clone());
continue;
}
}
}
}
Err(err) => {
error!("Key Info Manager error: {}", err);
return None;
}
};
for key_identity in to_remove.iter() {
if let Err(err) = cryptoauthlib_provider
.key_info_store
.remove_key_info(key_identity)
{
error!("Key Info Manager error: {}", err);
return None;
}
}
if cryptoauthlib_provider.set_opcodes().is_none() {
warn!("Failed to setup opcodes for cryptoauthlib_provider");
}
let err = cryptoauthlib_provider.set_access_keys(access_key_file_name);
match err {
Some(rust_cryptoauthlib::AtcaStatus::AtcaSuccess) => (),
_ => {
warn!("Unable to set access keys. This is dangerous for a hardware interface.");
}
}
Some(cryptoauthlib_provider)
}
fn set_opcodes(&mut self) -> Option<()> {
match self.device.get_device_type() {
rust_cryptoauthlib::AtcaDeviceType::ATECC508A
| rust_cryptoauthlib::AtcaDeviceType::ATECC608A
| rust_cryptoauthlib::AtcaDeviceType::ATECC108A => {
if self.supported_opcodes.insert(Opcode::PsaGenerateKey)
&& self.supported_opcodes.insert(Opcode::PsaDestroyKey)
&& self.supported_opcodes.insert(Opcode::PsaHashCompute)
&& self.supported_opcodes.insert(Opcode::PsaHashCompare)
&& self.supported_opcodes.insert(Opcode::PsaGenerateRandom)
&& self.supported_opcodes.insert(Opcode::PsaImportKey)
&& self.supported_opcodes.insert(Opcode::PsaSignHash)
&& self.supported_opcodes.insert(Opcode::PsaVerifyHash)
&& self.supported_opcodes.insert(Opcode::PsaCipherEncrypt)
&& self.supported_opcodes.insert(Opcode::PsaCipherDecrypt)
&& self.supported_opcodes.insert(Opcode::PsaSignMessage)
&& self.supported_opcodes.insert(Opcode::PsaVerifyMessage)
&& self.supported_opcodes.insert(Opcode::PsaExportPublicKey)
&& self.supported_opcodes.insert(Opcode::PsaExportKey)
&& self.supported_opcodes.insert(Opcode::PsaAeadEncrypt)
&& self.supported_opcodes.insert(Opcode::PsaAeadDecrypt)
&& self.supported_opcodes.insert(Opcode::PsaRawKeyAgreement)
{
Some(())
} else {
None
}
}
rust_cryptoauthlib::AtcaDeviceType::AtcaTestDevSuccess
| rust_cryptoauthlib::AtcaDeviceType::AtcaTestDevFail
| rust_cryptoauthlib::AtcaDeviceType::AtcaTestDevFailUnimplemented => {
let _ = self.supported_opcodes.insert(Opcode::PsaGenerateRandom);
Some(())
}
_ => None,
}
}
}
impl Provide for Provider {
fn describe(&self) -> Result<(ProviderInfo, HashSet<Opcode>)> {
trace!("describe ingress");
Ok((
ProviderInfo {
uuid: Uuid::parse_str(Provider::PROVIDER_UUID)
.or(Err(ResponseStatus::InvalidEncoding))?,
description: String::from(
"User space hardware provider, utilizing MicrochipTech CryptoAuthentication Library for ATECCx08 chips",
),
vendor: String::from("Arm"),
version_maj: 0,
version_min: 1,
version_rev: 0,
id: ProviderId::CryptoAuthLib,
},
self.supported_opcodes.iter().copied().collect(),
))
}
fn list_keys(
&self,
application_identity: &ApplicationIdentity,
_op: list_keys::Operation,
) -> Result<list_keys::Result> {
Ok(list_keys::Result {
keys: self.key_info_store.list_keys(application_identity)?,
})
}
fn list_clients(&self, _op: list_clients::Operation) -> Result<list_clients::Result> {
Ok(list_clients::Result {
clients: self
.key_info_store
.list_clients()?
.into_iter()
.map(|application_identity| application_identity.name().clone())
.collect(),
})
}
fn psa_hash_compute(
&self,
op: psa_hash_compute::Operation,
) -> Result<psa_hash_compute::Result> {
trace!("psa_hash_compute ingress");
if !self.supported_opcodes.contains(&Opcode::PsaHashCompute) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_hash_compute_internal(op)
}
}
fn psa_hash_compare(
&self,
op: psa_hash_compare::Operation,
) -> Result<psa_hash_compare::Result> {
trace!("psa_hash_compare ingress");
if !self.supported_opcodes.contains(&Opcode::PsaHashCompare) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_hash_compare_internal(op)
}
}
fn psa_generate_random(
&self,
op: psa_generate_random::Operation,
) -> Result<psa_generate_random::Result> {
trace!("psa_generate_random ingress");
if !self.supported_opcodes.contains(&Opcode::PsaGenerateRandom) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_generate_random_internal(op)
}
}
fn psa_generate_key(
&self,
application_identity: &ApplicationIdentity,
op: psa_generate_key::Operation,
) -> Result<psa_generate_key::Result> {
trace!("psa_generate_key ingress");
if !self.supported_opcodes.contains(&Opcode::PsaGenerateKey) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_generate_key_internal(application_identity, op)
}
}
fn psa_destroy_key(
&self,
application_identity: &ApplicationIdentity,
op: psa_destroy_key::Operation,
) -> Result<psa_destroy_key::Result> {
trace!("psa_destroy_key ingress");
if !self.supported_opcodes.contains(&Opcode::PsaDestroyKey) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_destroy_key_internal(application_identity, op)
}
}
fn psa_import_key(
&self,
application_identity: &ApplicationIdentity,
op: psa_import_key::Operation,
) -> Result<psa_import_key::Result> {
trace!("psa_import_key ingress");
if !self.supported_opcodes.contains(&Opcode::PsaImportKey) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_import_key_internal(application_identity, op)
}
}
fn psa_sign_hash(
&self,
application_identity: &ApplicationIdentity,
op: psa_sign_hash::Operation,
) -> Result<psa_sign_hash::Result> {
trace!("psa_sign_hash ingress");
if !self.supported_opcodes.contains(&Opcode::PsaSignHash) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_sign_hash_internal(application_identity, op)
}
}
fn psa_verify_hash(
&self,
application_identity: &ApplicationIdentity,
op: psa_verify_hash::Operation,
) -> Result<psa_verify_hash::Result> {
trace!("psa_verify_hash ingress");
if !self.supported_opcodes.contains(&Opcode::PsaVerifyHash) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_verify_hash_internal(application_identity, op)
}
}
fn psa_cipher_encrypt(
&self,
application_identity: &ApplicationIdentity,
op: psa_cipher_encrypt::Operation,
) -> Result<psa_cipher_encrypt::Result> {
trace!("psa_cipher_encrypt ingress");
if !self.supported_opcodes.contains(&Opcode::PsaCipherEncrypt) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_cipher_encrypt_internal(application_identity, op)
}
}
fn psa_cipher_decrypt(
&self,
application_identity: &ApplicationIdentity,
op: psa_cipher_decrypt::Operation,
) -> Result<psa_cipher_decrypt::Result> {
trace!("psa_cipher_decrypt ingress");
if !self.supported_opcodes.contains(&Opcode::PsaCipherDecrypt) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_cipher_decrypt_internal(application_identity, op)
}
}
fn psa_sign_message(
&self,
application_identity: &ApplicationIdentity,
op: psa_sign_message::Operation,
) -> Result<psa_sign_message::Result> {
trace!("psa_sign_message ingress");
if !self.supported_opcodes.contains(&Opcode::PsaSignMessage) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_sign_message_internal(application_identity, op)
}
}
fn psa_verify_message(
&self,
application_identity: &ApplicationIdentity,
op: psa_verify_message::Operation,
) -> Result<psa_verify_message::Result> {
trace!("psa_verify_message ingress");
if !self.supported_opcodes.contains(&Opcode::PsaVerifyMessage) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_verify_message_internal(application_identity, op)
}
}
fn psa_export_public_key(
&self,
application_identity: &ApplicationIdentity,
op: psa_export_public_key::Operation,
) -> Result<psa_export_public_key::Result> {
trace!("psa_export_public_key ingress");
if !self.supported_opcodes.contains(&Opcode::PsaExportPublicKey) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_export_public_key_internal(application_identity, op)
}
}
fn psa_export_key(
&self,
application_identity: &ApplicationIdentity,
op: psa_export_key::Operation,
) -> Result<psa_export_key::Result> {
trace!("psa_export_key ingress");
if !self.supported_opcodes.contains(&Opcode::PsaExportKey) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_export_key_internal(application_identity, op)
}
}
fn psa_aead_encrypt(
&self,
application_identity: &ApplicationIdentity,
op: psa_aead_encrypt::Operation,
) -> Result<psa_aead_encrypt::Result> {
trace!("psa_aead_encrypt ingress");
if !self.supported_opcodes.contains(&Opcode::PsaAeadEncrypt) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_aead_encrypt_internal(application_identity, op)
}
}
fn psa_aead_decrypt(
&self,
application_identity: &ApplicationIdentity,
op: psa_aead_decrypt::Operation,
) -> Result<psa_aead_decrypt::Result> {
trace!("psa_aead_decrypt ingress");
if !self.supported_opcodes.contains(&Opcode::PsaAeadDecrypt) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_aead_decrypt_internal(application_identity, op)
}
}
fn psa_raw_key_agreement(
&self,
application_identity: &ApplicationIdentity,
op: psa_raw_key_agreement::Operation,
) -> Result<psa_raw_key_agreement::Result> {
trace!("psa_raw_key_agreement ingress");
if !self.supported_opcodes.contains(&Opcode::PsaRawKeyAgreement) {
Err(ResponseStatus::PsaErrorNotSupported)
} else {
self.psa_raw_key_agreement_internal(application_identity, op)
}
}
}
#[derive(Default, Derivative)]
#[derivative(Debug)]
pub struct ProviderBuilder {
provider_name: Option<String>,
#[derivative(Debug = "ignore")]
key_info_store: Option<KeyInfoManagerClient>,
device_type: Option<String>,
iface_type: Option<String>,
wake_delay: Option<u16>,
rx_retries: Option<i32>,
slave_address: Option<u8>,
bus: Option<u8>,
baud: Option<u32>,
access_key_file_name: Option<String>,
}
impl ProviderBuilder {
pub fn new() -> ProviderBuilder {
ProviderBuilder {
provider_name: None,
key_info_store: None,
device_type: None,
iface_type: None,
wake_delay: None,
rx_retries: None,
slave_address: None,
bus: None,
baud: None,
access_key_file_name: None,
}
}
pub fn with_provider_name(mut self, provider_name: String) -> ProviderBuilder {
self.provider_name = Some(provider_name);
self
}
pub fn with_key_info_store(mut self, key_info_store: KeyInfoManagerClient) -> ProviderBuilder {
self.key_info_store = Some(key_info_store);
self
}
pub fn with_device_type(mut self, device_type: String) -> ProviderBuilder {
self.device_type = Some(device_type);
self
}
pub fn with_iface_type(mut self, iface_type: String) -> ProviderBuilder {
self.iface_type = Some(iface_type);
self
}
pub fn with_wake_delay(mut self, wake_delay: Option<u16>) -> ProviderBuilder {
self.wake_delay = wake_delay;
self
}
pub fn with_rx_retries(mut self, rx_retries: Option<i32>) -> ProviderBuilder {
self.rx_retries = rx_retries;
self
}
pub fn with_slave_address(mut self, slave_address: Option<u8>) -> ProviderBuilder {
self.slave_address = slave_address;
self
}
pub fn with_bus(mut self, bus: Option<u8>) -> ProviderBuilder {
self.bus = bus;
self
}
pub fn with_baud(mut self, baud: Option<u32>) -> ProviderBuilder {
self.baud = baud;
self
}
pub fn with_access_key_file(mut self, access_key_file_name: Option<String>) -> ProviderBuilder {
self.access_key_file_name = access_key_file_name;
self
}
pub fn build(self) -> std::io::Result<Provider> {
let iface_cfg = match self.iface_type {
Some(x) => match x.as_str() {
"i2c" => {
let atcai2c_iface_cfg = rust_cryptoauthlib::AtcaIfaceI2c::default()
.set_slave_address(self.slave_address.ok_or_else(|| {
Error::new(ErrorKind::InvalidData, "missing atecc i2c slave address")
})?)
.set_bus(self.bus.ok_or_else(|| {
Error::new(ErrorKind::InvalidData, "missing atecc i2c bus")
})?)
.set_baud(self.baud.ok_or_else(|| {
Error::new(ErrorKind::InvalidData, "missing atecc i2c baud rate")
})?);
rust_cryptoauthlib::AtcaIfaceCfg::default()
.set_iface_type("i2c".to_owned())
.set_devtype(self.device_type.ok_or_else(|| {
Error::new(ErrorKind::InvalidData, "missing atecc device type")
})?)
.set_wake_delay(self.wake_delay.ok_or_else(|| {
Error::new(ErrorKind::InvalidData, "missing atecc wake delay")
})?)
.set_rx_retries(self.rx_retries.ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
"missing rx retries number for atecc",
)
})?)
.set_iface(
rust_cryptoauthlib::AtcaIface::default().set_atcai2c(atcai2c_iface_cfg),
)
}
"test-interface" => rust_cryptoauthlib::AtcaIfaceCfg::default()
.set_iface_type("test-interface".to_owned())
.set_devtype(self.device_type.ok_or_else(|| {
Error::new(ErrorKind::InvalidData, "missing atecc device type")
})?),
_ => {
return Err(Error::new(
ErrorKind::InvalidData,
"Unsupported inteface type",
));
}
},
None => return Err(Error::new(ErrorKind::InvalidData, "Missing inteface type")),
};
Provider::new(
self.provider_name
.ok_or_else(|| Error::new(ErrorKind::InvalidData, "missing provider name"))?,
self.key_info_store
.ok_or_else(|| Error::new(ErrorKind::InvalidData, "missing key info store"))?,
iface_cfg,
self.access_key_file_name,
)
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
"CryptoAuthLib Provider initialization failed",
)
})
}
}