#![allow(clippy::too_many_arguments)]
#[macro_use]
mod error;
pub use self::error::{ClientError, ClientErrorKind};
use self::error::ClientErrorKind::*;
use crate::{
asymmetric::{
self,
attestation::{self, commands::*},
commands::*,
ecdsa::{self, commands::*},
ed25519::{self, commands::*},
PublicKey,
},
audit::{commands::*, *},
authentication::{self, commands::*, Credentials},
capability::Capability,
command::{self, Command},
connector::Connector,
device::commands::*,
domain::Domain,
hmac::{self, commands::*},
object::{self, commands::*, generate},
opaque::{self, commands::*},
otp::{self, commands::*},
serialization::{deserialize, serialize},
session::{self, Session},
uuid,
wrap::{self, commands::*},
};
use std::time::{Duration, Instant};
#[cfg(feature = "rsa")]
use {
crate::asymmetric::rsa::{self, pkcs1::commands::*, pss::commands::*},
byteorder::{BigEndian, ByteOrder},
sha2::{Digest, Sha256},
};
pub struct Client {
connector: Connector,
session: Option<Session>,
credentials: Option<Credentials>,
}
impl Client {
pub fn open<C>(
connector: C,
credentials: Credentials,
reconnect: bool,
) -> Result<Self, ClientError>
where
C: Into<Connector>,
{
let mut client = Self::create(connector, credentials)?;
client.connect()?;
if !reconnect {
client.credentials = None;
}
Ok(client)
}
pub fn create<C>(connector: C, credentials: Credentials) -> Result<Self, ClientError>
where
C: Into<Connector>,
{
let client = Self {
connector: connector.into(),
session: None,
credentials: Some(credentials),
};
Ok(client)
}
pub fn connect(&mut self) -> Result<(), ClientError> {
self.session()?;
Ok(())
}
pub fn is_connected(&self) -> bool {
self.session.as_ref().map(Session::is_open).unwrap_or(false)
}
pub fn session_id(&self) -> Option<session::Id> {
self.session.as_ref().and_then(|s| Some(s.id()))
}
pub fn session(&mut self) -> Result<&mut Session, ClientError> {
if self.is_connected() {
return Ok(self.session.as_mut().unwrap());
}
let session = Session::open(
self.connector.clone(),
self.credentials
.as_ref()
.ok_or_else(|| err!(AuthenticationError, "session reconnection disabled"))?,
session::Timeout::default(),
)?;
self.session = Some(session);
Ok(self.session.as_mut().unwrap())
}
pub fn ping(&mut self) -> Result<Duration, ClientError> {
let t = Instant::now();
let uuid = uuid::new_v4().to_hyphenated().to_string();
let response = self.echo(uuid.as_bytes())?;
ensure!(
uuid.as_bytes() == response.as_slice(),
ResponseError,
"expected {}, got {}",
uuid,
String::from_utf8_lossy(&response)
);
Ok(Instant::now().duration_since(t))
}
fn send_command<T: Command>(&mut self, command: T) -> Result<T::ResponseType, ClientError> {
Ok(self.session()?.send_command(command)?)
}
pub fn blink_device(&mut self, num_seconds: u8) -> Result<(), ClientError> {
self.send_command(BlinkDeviceCommand { num_seconds })?;
Ok(())
}
pub fn delete_object(
&mut self,
object_id: object::Id,
object_type: object::Type,
) -> Result<(), ClientError> {
self.send_command(DeleteObjectCommand {
object_id,
object_type,
})?;
Ok(())
}
pub fn device_info(&mut self) -> Result<DeviceInfoResponse, ClientError> {
Ok(self.send_command(DeviceInfoCommand {})?)
}
pub fn echo<M>(&mut self, msg: M) -> Result<Vec<u8>, ClientError>
where
M: Into<Vec<u8>>,
{
Ok(self
.send_command(EchoCommand {
message: msg.into(),
})?
.0)
}
pub fn export_wrapped(
&mut self,
wrap_key_id: object::Id,
object_type: object::Type,
object_id: object::Id,
) -> Result<wrap::Message, ClientError> {
Ok(self
.send_command(ExportWrappedCommand {
wrap_key_id,
object_type,
object_id,
})?
.0)
}
pub fn generate_asymmetric_key(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
algorithm: asymmetric::Algorithm,
) -> Result<object::Id, ClientError> {
Ok(self
.send_command(GenAsymmetricKeyCommand(generate::Params {
key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
}))?
.key_id)
}
pub fn generate_hmac_key(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
algorithm: hmac::Algorithm,
) -> Result<object::Id, ClientError> {
Ok(self
.send_command(GenHmacKeyCommand(generate::Params {
key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
}))?
.key_id)
}
pub fn generate_wrap_key(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
delegated_capabilities: Capability,
algorithm: wrap::Algorithm,
) -> Result<object::Id, ClientError> {
Ok(self
.send_command(GenWrapKeyCommand {
params: generate::Params {
key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
},
delegated_capabilities,
})?
.key_id)
}
pub fn get_log_entries(&mut self) -> Result<LogEntries, ClientError> {
Ok(self.send_command(GetLogEntriesCommand {})?)
}
pub fn get_object_info(
&mut self,
object_id: object::Id,
object_type: object::Type,
) -> Result<object::Info, ClientError> {
Ok(self
.send_command(GetObjectInfoCommand(object::Handle::new(
object_id,
object_type,
)))?
.0)
}
pub fn get_opaque(&mut self, object_id: object::Id) -> Result<Vec<u8>, ClientError> {
Ok(self.send_command(GetOpaqueCommand { object_id })?.0)
}
pub fn get_command_audit_option(
&mut self,
command: command::Code,
) -> Result<AuditOption, ClientError> {
let command_audit_options = self.get_commands_audit_options()?;
Ok(command_audit_options
.iter()
.find(|opt| opt.command_type() == command)
.map(|opt| opt.audit_option())
.unwrap_or(AuditOption::Off))
}
pub fn get_commands_audit_options(&mut self) -> Result<Vec<AuditCommand>, ClientError> {
let response = self.send_command(GetOptionCommand {
tag: AuditTag::Command,
})?;
Ok(deserialize(&response.0)?)
}
pub fn get_force_audit_option(&mut self) -> Result<AuditOption, ClientError> {
let response = self.send_command(GetOptionCommand {
tag: AuditTag::Force,
})?;
ensure!(
response.0.len() == 1,
ProtocolError,
"expected 1-byte response, got {}",
response.0.len()
);
AuditOption::from_u8(response.0[0]).map_err(|e| err!(ProtocolError, e))
}
pub fn get_pseudo_random(&mut self, bytes: usize) -> Result<Vec<u8>, ClientError> {
ensure!(
bytes <= MAX_RAND_BYTES,
ProtocolError,
"requested number of bytes too large: {} (max: {})",
bytes,
MAX_RAND_BYTES
);
Ok(self
.send_command(GetPseudoRandomCommand {
bytes: bytes as u16,
})?
.bytes)
}
pub fn get_public_key(&mut self, key_id: object::Id) -> Result<PublicKey, ClientError> {
Ok(self.send_command(GetPublicKeyCommand { key_id })?.into())
}
pub fn get_storage_info(&mut self) -> Result<GetStorageInfoResponse, ClientError> {
Ok(self.send_command(GetStorageInfoCommand {})?)
}
pub fn import_wrapped<M>(
&mut self,
wrap_key_id: object::Id,
wrap_message: M,
) -> Result<ImportWrappedResponse, ClientError>
where
M: Into<wrap::Message>,
{
let wrap::Message { nonce, ciphertext } = wrap_message.into();
Ok(self.send_command(ImportWrappedCommand {
wrap_key_id,
nonce,
ciphertext,
})?)
}
pub fn list_objects(
&mut self,
filters: &[object::Filter],
) -> Result<Vec<object::Entry>, ClientError> {
let mut filter_bytes = vec![];
for filter in filters {
filter.serialize(&mut filter_bytes)?;
}
Ok(self.send_command(ListObjectsCommand(filter_bytes))?.0)
}
pub fn put_asymmetric_key<K>(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
algorithm: asymmetric::Algorithm,
key_bytes: K,
) -> Result<object::Id, ClientError>
where
K: Into<Vec<u8>>,
{
let data = key_bytes.into();
if data.len() != algorithm.key_len() {
fail!(
ProtocolError,
"invalid key length for {:?}: {} (expected {})",
algorithm,
data.len(),
algorithm.key_len()
);
}
Ok(self
.send_command(PutAsymmetricKeyCommand {
params: object::import::Params {
id: key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
},
data,
})?
.key_id)
}
pub fn put_authentication_key<K>(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
delegated_capabilities: Capability,
algorithm: authentication::Algorithm,
authentication_key: K,
) -> Result<object::Id, ClientError>
where
K: Into<authentication::Key>,
{
Ok(self
.send_command(PutAuthenticationKeyCommand {
params: object::import::Params {
id: key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
},
delegated_capabilities,
authentication_key: authentication_key.into(),
})?
.key_id)
}
pub fn put_hmac_key<K>(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
algorithm: hmac::Algorithm,
key_bytes: K,
) -> Result<object::Id, ClientError>
where
K: Into<Vec<u8>>,
{
let hmac_key = key_bytes.into();
if hmac_key.len() < HMAC_MIN_KEY_SIZE || hmac_key.len() > algorithm.max_key_len() {
fail!(
ProtocolError,
"invalid key length for {:?}: {} (min {}, max {})",
algorithm,
hmac_key.len(),
HMAC_MIN_KEY_SIZE,
algorithm.max_key_len()
);
}
Ok(self
.send_command(PutHmacKeyCommand {
params: object::import::Params {
id: key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
},
hmac_key,
})?
.key_id)
}
pub fn put_opaque<B>(
&mut self,
object_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
algorithm: opaque::Algorithm,
opaque_data: B,
) -> Result<object::Id, ClientError>
where
B: Into<Vec<u8>>,
{
Ok(self
.send_command(PutOpaqueCommand {
params: object::import::Params {
id: object_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
},
data: opaque_data.into(),
})?
.object_id)
}
pub fn put_otp_aead_key<K>(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
algorithm: otp::Algorithm,
key_bytes: K,
) -> Result<object::Id, ClientError>
where
K: Into<Vec<u8>>,
{
let data = key_bytes.into();
if data.len() != algorithm.key_len() {
fail!(
ProtocolError,
"invalid key length for {:?}: {} (expected {})",
algorithm,
data.len(),
algorithm.key_len()
);
}
Ok(self
.send_command(PutOTPAEADKeyCommand {
params: object::import::Params {
id: key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
},
data,
})?
.key_id)
}
pub fn put_wrap_key<K>(
&mut self,
key_id: object::Id,
label: object::Label,
domains: Domain,
capabilities: Capability,
delegated_capabilities: Capability,
algorithm: wrap::Algorithm,
key_bytes: K,
) -> Result<object::Id, ClientError>
where
K: Into<Vec<u8>>,
{
let data = key_bytes.into();
if data.len() != algorithm.key_len() {
fail!(
ProtocolError,
"invalid key length for {:?}: {} (expected {})",
algorithm,
data.len(),
algorithm.key_len()
);
}
Ok(self
.send_command(PutWrapKeyCommand {
params: object::import::Params {
id: key_id,
label,
domains,
capabilities,
algorithm: algorithm.into(),
},
delegated_capabilities,
data,
})?
.key_id)
}
pub fn reset_device(&mut self) -> Result<(), ClientError> {
if let Err(e) = self.send_command(ResetDeviceCommand {}) {
debug!("error sending reset command: {}", e);
}
if let Some(ref mut session) = self.session {
session.abort();
}
Ok(())
}
pub fn set_command_audit_option(
&mut self,
command: command::Code,
audit_option: AuditOption,
) -> Result<(), ClientError> {
self.send_command(SetOptionCommand {
tag: AuditTag::Command,
length: 2,
value: serialize(&AuditCommand(command, audit_option))?,
})?;
Ok(())
}
pub fn set_force_audit_option(&mut self, option: AuditOption) -> Result<(), ClientError> {
self.send_command(SetOptionCommand {
tag: AuditTag::Force,
length: 1,
value: vec![option.to_u8()],
})?;
Ok(())
}
pub fn set_log_index(&mut self, log_index: u16) -> Result<(), ClientError> {
self.send_command(SetLogIndexCommand { log_index })?;
Ok(())
}
pub fn sign_attestation_certificate(
&mut self,
key_id: object::Id,
attestation_key_id: Option<object::Id>,
) -> Result<attestation::Certificate, ClientError> {
Ok(self.send_command(SignAttestationCertificateCommand {
key_id,
attestation_key_id: attestation_key_id.unwrap_or(0),
})?)
}
pub fn sign_ecdsa<T>(
&mut self,
key_id: object::Id,
digest: T,
) -> Result<ecdsa::Signature, ClientError>
where
T: Into<Vec<u8>>,
{
Ok(self
.send_command(SignEcdsaCommand {
key_id,
digest: digest.into(),
})?
.into())
}
pub fn sign_ed25519<T>(
&mut self,
key_id: object::Id,
data: T,
) -> Result<ed25519::Signature, ClientError>
where
T: Into<Vec<u8>>,
{
Ok(self
.send_command(SignEddsaCommand {
key_id,
data: data.into(),
})?
.into())
}
pub fn sign_hmac<M>(&mut self, key_id: object::Id, msg: M) -> Result<hmac::Tag, ClientError>
where
M: Into<Vec<u8>>,
{
Ok(self
.send_command(SignHmacCommand {
key_id,
data: msg.into(),
})?
.into())
}
#[cfg(feature = "rsa")]
pub fn sign_rsa_pkcs1v15_sha256(
&mut self,
key_id: object::Id,
data: &[u8],
) -> Result<rsa::pkcs1::Signature, ClientError> {
Ok(self
.send_command(SignPkcs1Command {
key_id,
digest: Sha256::digest(data).as_slice().into(),
})?
.into())
}
#[cfg(feature = "rsa")]
pub fn sign_rsa_pss_sha256(
&mut self,
key_id: object::Id,
data: &[u8],
) -> Result<rsa::pss::Signature, ClientError> {
ensure!(
data.len() > rsa::pss::MAX_MESSAGE_SIZE,
ProtocolError,
"message too large to be signed (max: {})",
rsa::pss::MAX_MESSAGE_SIZE
);
let mut hasher = Sha256::default();
let mut length = [0u8; 2];
BigEndian::write_u16(&mut length, data.len() as u16);
hasher.input(&length);
hasher.input(data);
let digest = hasher.result();
Ok(self
.send_command(SignPssCommand {
key_id,
mgf1_hash_alg: rsa::mgf::Algorithm::SHA256,
salt_len: digest.as_slice().len() as u16,
digest: digest.as_slice().into(),
})?
.into())
}
pub fn unwrap_data<M>(
&mut self,
wrap_key_id: object::Id,
wrap_message: M,
) -> Result<Vec<u8>, ClientError>
where
M: Into<wrap::Message>,
{
let wrap::Message { nonce, ciphertext } = wrap_message.into();
Ok(self
.send_command(UnwrapDataCommand {
wrap_key_id,
nonce,
ciphertext,
})?
.0)
}
pub fn verify_hmac<M, T>(
&mut self,
key_id: object::Id,
msg: M,
tag: T,
) -> Result<(), ClientError>
where
M: Into<Vec<u8>>,
T: Into<hmac::Tag>,
{
let result = self.send_command(VerifyHmacCommand {
key_id,
tag: tag.into(),
data: msg.into(),
})?;
if result.0 == 1 {
Ok(())
} else {
Err(err!(ResponseError, "HMAC verification failure"))
}
}
pub fn wrap_data(
&mut self,
wrap_key_id: object::Id,
plaintext: Vec<u8>,
) -> Result<wrap::Message, ClientError> {
Ok(self
.send_command(WrapDataCommand {
wrap_key_id,
plaintext,
})?
.0)
}
}