use zeroize::Zeroize;
use crate::component::{self, aethel::core::types::IdentityError, AethelCore, LoadError};
use wasmtime::component::ResourceAny;
use wasmtime::Store;
pub const MIN_ENTROPY_BYTES: usize = 32;
#[derive(Debug)]
pub enum Error {
Load(LoadError),
Component(IdentityError),
Host(wasmtime::Error),
Entropy(getrandom::Error),
UnknownAttribute(String),
TooManyAttributes(usize),
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::Load(e) => write!(f, "{e}"),
Error::Component(e) => write!(f, "aethel-core rejected the call: {e:?}"),
Error::Host(e) => write!(f, "the call into the component failed: {e}"),
Error::Entropy(e) => write!(f, "could not source entropy: {e}"),
Error::UnknownAttribute(name) => write!(
f,
"this credential has no attribute named {name:?}, so it cannot be disclosed"
),
Error::TooManyAttributes(n) => write!(
f,
"a credential carries at most 8 attributes, {n} were supplied"
),
}
}
}
impl std::error::Error for Error {}
impl From<LoadError> for Error {
fn from(e: LoadError) -> Self {
Error::Load(e)
}
}
impl From<IdentityError> for Error {
fn from(e: IdentityError) -> Self {
Error::Component(e)
}
}
impl From<wasmtime::Error> for Error {
fn from(e: wasmtime::Error) -> Self {
Error::Host(e)
}
}
pub struct Identity {
pub(crate) store: Store<()>,
pub(crate) bindings: AethelCore,
pub(crate) handle: ResourceAny,
public_key: Vec<u8>,
}
impl core::fmt::Debug for Identity {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let fingerprint: String = self
.public_key
.iter()
.take(8)
.map(|b| format!("{b:02x}"))
.collect();
f.debug_struct("Identity")
.field("public_key", &format_args!("{fingerprint}..."))
.field("secret", &format_args!("<held in the component, never here>"))
.finish()
}
}
impl Identity {
pub fn generate() -> Result<Self, Error> {
let mut entropy = [0u8; MIN_ENTROPY_BYTES];
getrandom::getrandom(&mut entropy).map_err(Error::Entropy)?;
let identity = Self::from_entropy(&entropy);
entropy.zeroize();
identity
}
pub fn from_entropy(entropy: &[u8]) -> Result<Self, Error> {
let (mut store, bindings) = component::load()?;
let handle = bindings
.aethel_core_identity()
.master_identity()
.call_generate(&mut store, entropy)??;
let public_key = bindings
.aethel_core_identity()
.master_identity()
.call_public_key(&mut store, handle)?;
Ok(Self { store, bindings, handle, public_key })
}
pub fn public_key(&self) -> &[u8] {
&self.public_key
}
pub fn sign(&mut self, message: &[u8]) -> Result<Vec<u8>, Error> {
let signature = self
.bindings
.aethel_core_identity()
.master_identity()
.call_sign(&mut self.store, self.handle, message)??;
Ok(signature)
}
}
pub const MIN_SEAL_KEY_BYTES: usize = 32;
impl Identity {
pub fn export_sealed(&mut self, key: &[u8]) -> Result<Vec<u8>, Error> {
let sealed = self
.bindings
.aethel_core_identity()
.master_identity()
.call_export_sealed(&mut self.store, self.handle, key)??;
Ok(sealed)
}
pub fn open_sealed(sealed: &[u8], key: &[u8]) -> Result<Self, Error> {
let (mut store, bindings) = component::load()?;
let handle = bindings
.aethel_core_identity()
.master_identity()
.call_import_sealed(&mut store, sealed, key)??;
let public_key = bindings
.aethel_core_identity()
.master_identity()
.call_public_key(&mut store, handle)?;
Ok(Self { store, bindings, handle, public_key })
}
}
pub fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<bool, Error> {
let (mut store, bindings) = component::load()?;
let verified = bindings
.aethel_core_identity()
.call_verify_signature(&mut store, public_key, message, signature)??;
Ok(verified)
}