use super::Provide;
use crate::authenticators::ApplicationIdentity;
use derivative::Derivative;
use log::{error, trace};
use parsec_interface::operations::list_providers::Uuid;
use parsec_interface::operations::{
delete_client, list_authenticators, list_clients, list_keys, list_opcodes, list_providers,
ping, psa_destroy_key,
};
use parsec_interface::operations::{
list_authenticators::AuthenticatorInfo, list_keys::KeyInfo, list_providers::ProviderInfo,
};
use parsec_interface::requests::{Opcode, ProviderId, ResponseStatus, Result};
use std::collections::{HashMap, HashSet};
use std::io::{Error, ErrorKind};
use std::num::ParseIntError;
use std::sync::Arc;
const SUPPORTED_OPCODES: [Opcode; 5] = [
Opcode::ListProviders,
Opcode::ListOpcodes,
Opcode::Ping,
Opcode::ListAuthenticators,
Opcode::ListKeys,
];
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Provider {
wire_protocol_version_min: u8,
wire_protocol_version_maj: u8,
provider_info: Vec<ProviderInfo>,
provider_opcodes: HashMap<ProviderId, HashSet<Opcode>>,
authenticator_info: Vec<AuthenticatorInfo>,
#[derivative(Debug = "ignore")]
prov_list: Vec<Arc<dyn Provide + Send + Sync>>,
}
impl Provider {
pub const DEFAULT_PROVIDER_NAME: &'static str = "core-provider";
pub const PROVIDER_UUID: &'static str = "47049873-2a43-4845-9d72-831eab668784";
}
impl Provide for Provider {
fn list_opcodes(&self, op: list_opcodes::Operation) -> Result<list_opcodes::Result> {
trace!("list_opcodes ingress");
Ok(list_opcodes::Result {
opcodes: self
.provider_opcodes
.get(&op.provider_id)
.ok_or(ResponseStatus::ProviderNotRegistered)?
.clone(),
})
}
fn list_providers(&self, _op: list_providers::Operation) -> Result<list_providers::Result> {
trace!("list_providers ingress");
Ok(list_providers::Result {
providers: self.provider_info.clone(),
})
}
fn list_authenticators(
&self,
_op: list_authenticators::Operation,
) -> Result<list_authenticators::Result> {
trace!("list_authenticators ingress");
Ok(list_authenticators::Result {
authenticators: self.authenticator_info.clone(),
})
}
fn list_keys(
&self,
application_identity: &ApplicationIdentity,
_op: list_keys::Operation,
) -> Result<list_keys::Result> {
trace!("list_keys ingress");
let mut keys: Vec<KeyInfo> = Vec::new();
for provider in &self.prov_list {
let id = if let Ok((provider_info, _)) = provider.describe() {
provider_info.id.to_string()
} else {
"unknown".to_string()
};
let mut result = provider
.list_keys(application_identity, _op)
.unwrap_or_else(|e| {
error!("list_keys failed on provider {} with {}", id, e);
list_keys::Result { keys: Vec::new() }
});
keys.append(&mut result.keys);
}
Ok(list_keys::Result { keys })
}
fn list_clients(&self, _op: list_clients::Operation) -> Result<list_clients::Result> {
trace!("list_clients ingress");
let mut clients: Vec<String> = Vec::new();
for provider in &self.prov_list {
let mut result = provider.list_clients(_op).unwrap_or_else(|e| {
let id = if let Ok((provider_info, _)) = provider.describe() {
provider_info.id.to_string()
} else {
"unknown".to_string()
};
error!("list_clients failed on provider {} with {}", id, e);
list_clients::Result {
clients: Vec::new(),
}
});
clients.append(&mut result.clients);
}
clients.sort();
clients.dedup();
Ok(list_clients::Result { clients })
}
fn delete_client(
&self,
application_identity: &ApplicationIdentity,
op: delete_client::Operation,
) -> Result<delete_client::Result> {
trace!("delete_client ingress");
let client = op.client;
for provider in &self.prov_list {
let id = if let Ok((provider_info, _)) = provider.describe() {
provider_info.id.to_string()
} else {
"unknown".to_string()
};
let keys = provider
.list_keys(
&ApplicationIdentity::new_with_auth(
client.clone(),
*application_identity.auth(),
),
list_keys::Operation {},
)
.unwrap_or_else(|e| {
error!("list_keys failed on provider {} with {}", id, e);
list_keys::Result { keys: Vec::new() }
})
.keys;
for key in keys {
let key_name = key.name;
let _ = provider
.psa_destroy_key(
&ApplicationIdentity::new_with_auth(
client.clone(),
*application_identity.auth(),
),
psa_destroy_key::Operation { key_name },
)
.unwrap_or_else(|e| {
error!("psa_destroy_key failed on provider {} with {}", id, e);
psa_destroy_key::Result {}
});
}
}
Ok(delete_client::Result {})
}
fn ping(&self, _op: ping::Operation) -> Result<ping::Result> {
trace!("ping ingress");
let result = ping::Result {
wire_protocol_version_maj: self.wire_protocol_version_maj,
wire_protocol_version_min: self.wire_protocol_version_min,
};
Ok(result)
}
fn describe(&self) -> Result<(ProviderInfo, HashSet<Opcode>)> {
trace!("describe ingress");
unreachable!()
}
}
#[derive(Derivative, Default)]
#[derivative(Debug)]
pub struct ProviderBuilder {
version_maj: Option<u8>,
version_min: Option<u8>,
#[derivative(Debug = "ignore")]
prov_list: Vec<Arc<dyn Provide + Send + Sync>>,
#[derivative(Debug = "ignore")]
authenticator_info: Vec<AuthenticatorInfo>,
}
impl ProviderBuilder {
pub fn new() -> Self {
ProviderBuilder {
version_maj: None,
version_min: None,
prov_list: Vec::new(),
authenticator_info: Vec::new(),
}
}
pub fn with_wire_protocol_version(mut self, version_min: u8, version_maj: u8) -> Self {
self.version_maj = Some(version_maj);
self.version_min = Some(version_min);
self
}
pub fn with_provider(mut self, provider: Arc<dyn Provide + Send + Sync>) -> Self {
self.prov_list.push(provider);
self
}
pub fn with_authenticator_info(mut self, authenticator_info: AuthenticatorInfo) -> Self {
self.authenticator_info.push(authenticator_info);
self
}
pub fn build(self) -> std::io::Result<Provider> {
let mut provider_opcodes = HashMap::new();
let _ = provider_opcodes.insert(
ProviderId::Core,
SUPPORTED_OPCODES.iter().copied().collect(),
);
let mut provider_info_vec = Vec::new();
for provider in &self.prov_list {
let (provider_info, opcodes) = provider
.describe()
.map_err(|_| Error::other("Failed to describe provider"))?;
let _ = provider_opcodes.insert(provider_info.id, opcodes);
provider_info_vec.push(provider_info);
}
let crate_version: std::result::Result<Vec<u32>, ParseIntError> = env!("CARGO_PKG_VERSION")
.split('.')
.map(|v| v.parse())
.collect();
let crate_version = crate_version.map_err(|e| {
format_error!("Invalid CARGO_PKG_VERSION format", e);
Error::new(
ErrorKind::InvalidData,
"crate version number has invalid format",
)
})?;
if crate_version.len() != 3 {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"Invalid CARGO_PKG_VERSION format: expected 3 components, got {}.",
crate_version.len()
),
));
}
provider_info_vec.push(ProviderInfo {
uuid: Uuid::parse_str("47049873-2a43-4845-9d72-831eab668784").map_err(|_| Error::new(
ErrorKind::InvalidData,
"provider UUID is invalid",
))?,
description: String::from("Software provider that implements only administrative (i.e. no cryptographic) operations"),
vendor: String::new(),
version_maj: crate_version[0],
version_min: crate_version[1],
version_rev: crate_version[2],
id: ProviderId::Core,
});
let core_provider = Provider {
wire_protocol_version_maj: self
.version_maj
.ok_or_else(|| Error::new(ErrorKind::InvalidData, "version maj is missing"))?,
wire_protocol_version_min: self
.version_min
.ok_or_else(|| Error::new(ErrorKind::InvalidData, "version min is missing"))?,
provider_opcodes,
provider_info: provider_info_vec,
authenticator_info: self.authenticator_info,
prov_list: self.prov_list,
};
Ok(core_provider)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ping() {
let provider = Provider {
wire_protocol_version_min: 8,
wire_protocol_version_maj: 10,
provider_info: Vec::new(),
authenticator_info: Vec::new(),
provider_opcodes: HashMap::new(),
prov_list: Vec::new(),
};
let op = ping::Operation {};
let result = provider.ping(op).unwrap();
assert_eq!(
result.wire_protocol_version_maj,
provider.wire_protocol_version_maj
);
assert_eq!(
result.wire_protocol_version_min,
provider.wire_protocol_version_min
);
}
#[test]
fn test_build() {
let provider_builder = ProviderBuilder::new().with_wire_protocol_version(42, 12);
assert!(
provider_builder.build().is_ok(),
"error building a CoreProvider"
)
}
}