saa_custom/passkey/
passkey.rs#[cfg(feature = "cosmwasm")]
use saa_common::cosmwasm::{Api, Env};
use saa_curves::secp256r1::secp256r1_verify;
use saa_schema::wasm_serde;
use saa_common::{
ensure, hashes::sha256, AuthError, Binary, CredentialId, String, Verifiable
};
use sha2::{Digest, Sha256};
#[wasm_serde]
pub struct ClientData {
#[serde(rename = "type")]
pub ty: String,
pub challenge: Binary,
pub origin: String,
#[serde(rename = "crossOrigin")]
pub cross_origin: bool
}
#[wasm_serde]
pub struct PasskeyExtension {
#[serde(rename = "type")]
pub ty: String,
pub origin: String,
pub pubkey: Option<Binary>,
pub user_handle: Option<String>,
}
#[wasm_serde]
pub struct PasskeyPayload {
pub authenticator_data: Binary,
pub client_data: ClientData,
pub pubkey: Option<Binary>,
}
#[wasm_serde]
pub struct PasskeyCredential {
pub id : String,
pub signature : Binary,
pub authenticator_data : Binary,
pub client_data : ClientData,
pub user_handle : Option<String>,
pub pubkey : Option<Binary>,
}
impl PasskeyCredential {
pub fn message_digest(&self) -> Result<Vec<u8>, AuthError> {
let client_data_hash = sha256(saa_common::to_json_binary(&self.client_data)?.as_slice());
let mut hasher = Sha256::new();
hasher.update(&self.authenticator_data);
hasher.update(&client_data_hash);
let hash = hasher.finalize();
Ok(hash.to_vec())
}
}
impl Verifiable for PasskeyCredential {
fn id(&self) -> CredentialId {
self.id.as_bytes().to_vec()
}
fn validate(&self) -> Result<(), AuthError> {
ensure!(self.authenticator_data.len() >= 37, AuthError::generic("Invalid authenticator data"));
ensure!(self.signature.len() > 0, AuthError::generic("Empty signature"));
ensure!(self.client_data.challenge.len() > 0, AuthError::generic("Empty challenge"));
ensure!(self.client_data.ty == "webauthn.get", AuthError::generic("Invalid client data type"));
ensure!(self.pubkey.is_some(), AuthError::generic("Missing public key"));
Ok(())
}
#[cfg(feature = "native")]
fn verify(&self) -> Result<(), AuthError> {
let res = secp256r1_verify(
&self.message_digest()?,
&self.signature,
self.pubkey.as_ref().unwrap()
)?;
ensure!(res, AuthError::generic("Signature verification failed"));
Ok(())
}
#[cfg(feature = "cosmwasm")]
fn verify_cosmwasm(&self, _: &dyn Api, _: &Env) -> Result<(), AuthError> {
let res = secp256r1_verify(
&self.message_digest()?,
&self.signature,
self.pubkey.as_ref().unwrap()
)?;
ensure!(res, AuthError::generic("Signature verification failed"));
Ok(())
}
}