1use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
7#[serde(transparent)]
8pub struct Base64UrlEncoded(String);
9
10impl Base64UrlEncoded {
11 pub fn from_raw(s: String) -> Self {
12 Self(s)
13 }
14
15 pub fn encode(bytes: &[u8]) -> Self {
16 Self(URL_SAFE_NO_PAD.encode(bytes))
17 }
18
19 pub fn decode(&self) -> Result<Vec<u8>, base64::DecodeError> {
20 URL_SAFE_NO_PAD.decode(&self.0)
21 }
22
23 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26}
27
28impl std::ops::Deref for Base64UrlEncoded {
29 type Target = str;
30 fn deref(&self) -> &str {
31 &self.0
32 }
33}
34
35impl std::fmt::Display for Base64UrlEncoded {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 f.write_str(&self.0)
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
44#[serde(rename_all = "lowercase")]
45pub enum SessionStatus {
46 Pending,
47 Responded,
48 Confirmed,
49 Aborted,
50 Completed,
51 Cancelled,
52 Expired,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
61#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
62pub struct CreateSessionRequest {
63 pub session_id: String,
64 pub controller_did: String,
65 pub ephemeral_pubkey: Base64UrlEncoded,
66 pub short_code: String,
67 #[serde(default)]
68 pub capabilities: Vec<String>,
69 pub expires_at: i64,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub recovery_target: Option<String>,
76}
77
78#[derive(Debug, Serialize, Deserialize)]
80#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
81pub struct CreateSessionResponse {
82 pub session_id: String,
83 pub status: SessionStatus,
84 pub short_code: String,
85 pub uri: String,
86 pub ttl_seconds: u64,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
92pub struct SubmitResponseRequest {
93 pub device_ephemeral_pubkey: Base64UrlEncoded,
94 pub device_signing_pubkey: Base64UrlEncoded,
95 #[serde(default)]
99 pub curve: crate::response::CurveTag,
100 pub device_did: String,
102 pub signature: Base64UrlEncoded,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub device_name: Option<String>,
105 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub subkey_chain: Option<crate::subkey_chain::SubkeyChain>,
115 #[serde(default)]
124 pub initiator_inception_event: String,
125 #[serde(default)]
127 pub responder_inception_event: String,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub shared_kel_inception_event: Option<String>,
135}
136
137pub const SHARED_KEL_INCEPTION_EVENT_MAX_BYTES: usize = 1024;
143
144impl SubmitResponseRequest {
145 pub fn validate(&self) -> Result<(), String> {
158 if let Some(ref inc) = self.shared_kel_inception_event
159 && inc.len() > SHARED_KEL_INCEPTION_EVENT_MAX_BYTES
160 {
161 return Err(format!(
162 "shared_kel_inception_event is {} bytes, exceeds cap of {} — multi-sig KEL inception would overflow QR capacity",
163 inc.len(),
164 SHARED_KEL_INCEPTION_EVENT_MAX_BYTES,
165 ));
166 }
167 Ok(())
168 }
169}
170
171#[derive(Debug, Serialize, Deserialize)]
173#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
174pub struct GetSessionResponse {
175 pub session_id: String,
176 pub status: SessionStatus,
177 pub ttl_seconds: u64,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub token: Option<CreateSessionRequest>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub response: Option<SubmitResponseRequest>,
182}
183
184#[derive(Debug, Serialize, Deserialize)]
186#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
187pub struct SuccessResponse {
188 pub success: bool,
189 pub message: String,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
195pub struct SubmitConfirmationRequest {
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub encrypted_attestation: Option<String>,
198 #[serde(default)]
199 pub aborted: bool,
200}
201
202#[derive(Debug, Serialize, Deserialize)]
204#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
205pub struct GetConfirmationResponse {
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub encrypted_attestation: Option<String>,
208 #[serde(default)]
209 pub aborted: bool,
210}