1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9#[derive(Default)]
10pub enum MrvbMode {
11 #[default]
13 ClassicalOnly,
14 #[cfg(feature = "pqc")]
16 PqcOnly,
17 #[cfg(feature = "hybrid")]
19 Hybrid,
20}
21
22impl std::fmt::Display for MrvbMode {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 match self {
25 MrvbMode::ClassicalOnly => write!(f, "ClassicalOnly"),
26 #[cfg(feature = "pqc")]
27 MrvbMode::PqcOnly => write!(f, "PqcOnly"),
28 #[cfg(feature = "hybrid")]
29 MrvbMode::Hybrid => write!(f, "Hybrid"),
30 }
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct MrvbConfig {
37 pub mode: MrvbMode,
39 pub keyset_id: String,
41}
42
43impl Default for MrvbConfig {
44 fn default() -> Self {
45 Self {
46 mode: MrvbMode::default(),
47 keyset_id: "default".to_string(),
48 }
49 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ClassicalKeyPair {
55 pub key_id: String,
56 pub algorithm: String,
58 #[serde(with = "base64_bytes")]
60 pub private_key: Vec<u8>,
61 #[serde(with = "base64_bytes")]
63 pub public_key: Vec<u8>,
64}
65
66#[cfg(feature = "pqc")]
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct PqcKeyPair {
70 pub key_id: String,
71 pub algorithm: String,
73 #[serde(with = "base64_bytes")]
75 pub private_key: Vec<u8>,
76 #[serde(with = "base64_bytes")]
78 pub public_key: Vec<u8>,
79}
80
81#[cfg(not(feature = "pqc"))]
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct PqcKeyPair {
85 pub key_id: String,
86 pub algorithm: String,
87 #[serde(with = "base64_bytes")]
88 pub private_key: Vec<u8>,
89 #[serde(with = "base64_bytes")]
90 pub public_key: Vec<u8>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct KeyPairSet {
98 pub keyset_id: String,
99 pub classical: Option<ClassicalKeyPair>,
101 pub pqc: Option<PqcKeyPair>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub created_at: Option<chrono::DateTime<chrono::Utc>>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub rotate_after: Option<chrono::DateTime<chrono::Utc>>,
109}
110
111impl KeyPairSet {
112 pub fn supports_mode(&self, mode: MrvbMode) -> bool {
114 match mode {
115 MrvbMode::ClassicalOnly => self.classical.is_some(),
116 #[cfg(feature = "pqc")]
117 MrvbMode::PqcOnly => self.pqc.is_some(),
118 #[cfg(feature = "hybrid")]
119 MrvbMode::Hybrid => self.classical.is_some() && self.pqc.is_some(),
120 }
121 }
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct HybridSignature {
127 #[serde(
129 default,
130 skip_serializing_if = "Option::is_none",
131 with = "option_base64_bytes"
132 )]
133 pub classical_sig: Option<Vec<u8>>,
134 #[serde(
136 default,
137 skip_serializing_if = "Option::is_none",
138 with = "option_base64_bytes"
139 )]
140 pub pqc_sig: Option<Vec<u8>>,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub alg_classical: Option<String>,
144 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub alg_pqc: Option<String>,
147 pub keyset_id: String,
149 pub mode: MrvbMode,
151}
152
153impl HybridSignature {
154 pub fn has_any_signature(&self) -> bool {
156 self.classical_sig.is_some() || self.pqc_sig.is_some()
157 }
158
159 #[cfg(feature = "hybrid")]
161 pub fn has_both_signatures(&self) -> bool {
162 self.classical_sig.is_some() && self.pqc_sig.is_some()
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
170pub struct AssertionClaims {
171 pub session_id: String,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub user_id: Option<String>,
176 pub rail: String,
178 pub verification_level: String,
180 #[serde(with = "chrono::serde::ts_seconds")]
182 pub issued_at: chrono::DateTime<chrono::Utc>,
183 #[serde(with = "chrono::serde::ts_seconds")]
185 pub expires_at: chrono::DateTime<chrono::Utc>,
186 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
188 pub metadata: HashMap<String, serde_json::Value>,
189}
190
191impl AssertionClaims {
192 pub fn is_expired(&self) -> bool {
194 chrono::Utc::now() > self.expires_at
195 }
196
197 pub fn is_valid(&self) -> bool {
199 let now = chrono::Utc::now();
200 now >= self.issued_at && now <= self.expires_at
201 }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct SignedAssertion {
209 pub claims: AssertionClaims,
211 pub signature: HybridSignature,
213 #[serde(default = "default_version")]
215 pub version: String,
216}
217
218fn default_version() -> String {
219 "1.0".to_string()
220}
221
222impl SignedAssertion {
223 pub fn to_json(&self) -> Result<String, serde_json::Error> {
225 serde_json::to_string(self)
226 }
227
228 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
230 serde_json::from_str(json)
231 }
232
233 pub fn to_compact_json(&self) -> Result<String, serde_json::Error> {
235 serde_json::to_string(self)
236 }
237}
238
239mod base64_bytes {
244 use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
245 use serde::{Deserialize, Deserializer, Serializer};
246
247 pub fn serialize<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
248 where
249 S: Serializer,
250 {
251 serializer.serialize_str(&BASE64.encode(bytes))
252 }
253
254 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
255 where
256 D: Deserializer<'de>,
257 {
258 let s = String::deserialize(deserializer)?;
259 BASE64.decode(&s).map_err(serde::de::Error::custom)
260 }
261}
262
263mod option_base64_bytes {
264 use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
265 use serde::{Deserialize, Deserializer, Serializer};
266
267 pub fn serialize<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
268 where
269 S: Serializer,
270 {
271 match bytes {
272 Some(b) => serializer.serialize_some(&BASE64.encode(b)),
273 None => serializer.serialize_none(),
274 }
275 }
276
277 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
278 where
279 D: Deserializer<'de>,
280 {
281 let opt: Option<String> = Option::deserialize(deserializer)?;
282 match opt {
283 Some(s) => BASE64
284 .decode(&s)
285 .map(Some)
286 .map_err(serde::de::Error::custom),
287 None => Ok(None),
288 }
289 }
290}