use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(transparent)]
pub struct Base64UrlEncoded(String);
impl Base64UrlEncoded {
pub fn from_raw(s: String) -> Self {
Self(s)
}
pub fn encode(bytes: &[u8]) -> Self {
Self(URL_SAFE_NO_PAD.encode(bytes))
}
pub fn decode(&self) -> Result<Vec<u8>, base64::DecodeError> {
URL_SAFE_NO_PAD.decode(&self.0)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::ops::Deref for Base64UrlEncoded {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for Base64UrlEncoded {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "lowercase")]
pub enum SessionStatus {
Pending,
Responded,
Confirmed,
Aborted,
Completed,
Cancelled,
Expired,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CreateSessionRequest {
pub session_id: String,
pub controller_did: String,
pub ephemeral_pubkey: Base64UrlEncoded,
pub short_code: String,
#[serde(default)]
pub capabilities: Vec<String>,
pub expires_at: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recovery_target: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CreateSessionResponse {
pub session_id: String,
pub status: SessionStatus,
pub short_code: String,
pub uri: String,
pub ttl_seconds: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SubmitResponseRequest {
pub device_ephemeral_pubkey: Base64UrlEncoded,
pub device_signing_pubkey: Base64UrlEncoded,
#[serde(default)]
pub curve: crate::response::CurveTag,
pub device_did: String,
pub signature: Base64UrlEncoded,
#[serde(skip_serializing_if = "Option::is_none")]
pub device_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subkey_chain: Option<crate::subkey_chain::SubkeyChain>,
#[serde(default)]
pub initiator_inception_event: String,
#[serde(default)]
pub responder_inception_event: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shared_kel_inception_event: Option<String>,
}
pub const SHARED_KEL_INCEPTION_EVENT_MAX_BYTES: usize = 1024;
impl SubmitResponseRequest {
pub fn validate(&self) -> Result<(), String> {
if let Some(ref inc) = self.shared_kel_inception_event
&& inc.len() > SHARED_KEL_INCEPTION_EVENT_MAX_BYTES
{
return Err(format!(
"shared_kel_inception_event is {} bytes, exceeds cap of {} — multi-sig KEL inception would overflow QR capacity",
inc.len(),
SHARED_KEL_INCEPTION_EVENT_MAX_BYTES,
));
}
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct GetSessionResponse {
pub session_id: String,
pub status: SessionStatus,
pub ttl_seconds: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<CreateSessionRequest>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response: Option<SubmitResponseRequest>,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SuccessResponse {
pub success: bool,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SubmitConfirmationRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub encrypted_attestation: Option<String>,
#[serde(default)]
pub aborted: bool,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct GetConfirmationResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub encrypted_attestation: Option<String>,
#[serde(default)]
pub aborted: bool,
}