Skip to main content

authnz_common/
utils.rs

1#![allow(unused_imports)]
2
3use crate::{Email, IdenticationRequirement, TokenBundle};
4use crate::{LightMPAATHeader, LightMPAATPayload, LightMPAATSignature, MPAATHeader, MPAATPayload, MPAATSignature};
5use crate::{MResult, ServerError};
6
7#[cfg(any(feature = "pqc-utils", feature = "ed25519-utils"))]
8use crate::SignKeypair;
9
10#[cfg(feature = "crypt-utils")]
11use crate::CipherKey;
12
13/// Encodes data by `URL_SAFE` base64 engine.
14pub fn base64_encode(data: &[u8]) -> String {
15  use base64::{Engine as _, engine::general_purpose::URL_SAFE};
16  URL_SAFE.encode(data)
17}
18
19/// Decodes data with `URL_SAFE` base64 engine.
20pub fn base64_decode(data: &str) -> MResult<Vec<u8>> {
21  use base64::{Engine as _, engine::general_purpose::URL_SAFE};
22  URL_SAFE.decode(data).map_err(ServerError::from_private)
23}
24
25/// Generates `SZ` of random bytes.
26pub fn generate<const SZ: usize>() -> [u8; SZ] {
27  use rand::Rng;
28
29  let mut arr: [u8; SZ] = [0; SZ];
30  let mut rng = rand::rng();
31  rng.fill(arr.as_mut_slice());
32  arr
33}
34
35#[cfg(all(
36  feature = "crypt-utils",
37  feature = "authnz-utils",
38  any(feature = "pqc-utils", feature = "ed25519-utils")
39))]
40/// Creates MPAAT from given data.
41pub fn deploy_mpaat<U: serde::Serialize, T: serde::Serialize>(
42  payload: T,
43  common_fields: Option<U>,
44  exp: chrono::DateTime<chrono::Utc>,
45  client_public: &[u8],
46  server_enc: Option<&CipherKey>,
47  server_keys: &SignKeypair,
48) -> MResult<String> {
49  use base64::{
50    Engine as _,
51    engine::general_purpose::{STANDARD, URL_SAFE},
52  };
53  use impulse_server_kit::tracing;
54
55  tracing::debug!("MPAAT (deploy): Got client public: {:?}", client_public);
56  tracing::debug!("MPAAT (deploy): Expires at: {:?}", exp);
57
58  let payload = MPAATPayload {
59    cli_pkey: client_public.to_vec(),
60    exp,
61    container: payload,
62  };
63  let (enc_payload, nonce) = if let Some(server_enc) = server_enc {
64    tracing::debug!("MPAAT (deploy): Encrypting with a `ekey`...");
65    server_enc.encrypt(&payload).map_err(|e| {
66      ServerError::from_private(e)
67        .with_private_str("Can't encrypt payload with `server_enc` key!")
68        .with_500()
69    })?
70  } else {
71    tracing::debug!("MPAAT (deploy): Skip encryption...");
72    (
73      rmp_serde::to_vec(&payload).map_err(|e| {
74        ServerError::from_private(e)
75          .with_private_str("Can't serialize MPAAT payload!")
76          .with_500()
77      })?,
78      vec![],
79    )
80  };
81
82  let header = MPAATHeader {
83    authnz_pkey: server_keys.public(),
84    nonce,
85    common_public_fields: common_fields,
86  };
87
88  let sig = header.sign_mpaat(&enc_payload, server_keys).map_err(|e| {
89    ServerError::from_private(e)
90      .with_private_str("Can't sign MPAAT with server keyring!")
91      .with_500()
92  })?;
93  tracing::debug!("MPAAT (deploy): Signature: {:?}", sig);
94
95  let sig = MPAATSignature { sig };
96  let sig = STANDARD.encode(rmp_serde::to_vec(&sig).map_err(|e| {
97    ServerError::from_private(e)
98      .with_private_str("Can't serialize MPAAT signature!")
99      .with_500()
100  })?);
101
102  let header = STANDARD.encode(rmp_serde::to_vec(&header).map_err(|e| {
103    ServerError::from_private(e)
104      .with_private_str("Can't serialize MPAAT header!")
105      .with_500()
106  })?);
107  let enc_payload = URL_SAFE.encode(&enc_payload);
108
109  Ok(format!("{enc_payload}.{sig}.{header}"))
110}
111
112#[cfg(all(
113  feature = "crypt-utils",
114  feature = "authnz-utils",
115  any(feature = "pqc-utils", feature = "ed25519-utils")
116))]
117/// Creates LMPAAT from given data.
118pub fn deploy_lmpaat<U: serde::Serialize, T: serde::Serialize>(
119  payload: T,
120  common_fields: Option<U>,
121  exp: chrono::DateTime<chrono::Utc>,
122  client_public: &[u8],
123  server_keys: &SignKeypair,
124) -> MResult<String> {
125  use base64::{
126    Engine as _,
127    engine::general_purpose::{STANDARD, URL_SAFE},
128  };
129  use impulse_server_kit::tracing;
130
131  tracing::debug!("LMPAAT (deploy): Got client public: {:?}", client_public);
132  tracing::debug!("LMPAAT (deploy): Expires at: {:?}", exp);
133
134  let payload = LightMPAATPayload { exp, container: payload };
135  let enc_payload = rmp_serde::to_vec(&payload).map_err(|e| {
136    ServerError::from_private(e)
137      .with_private_str("Can't serialize LMPAAT payload!")
138      .with_500()
139  })?;
140  let enc_payload = URL_SAFE.encode(&enc_payload);
141
142  let header = LightMPAATHeader {
143    cli_pkey: client_public.to_vec(),
144    common_public_fields: common_fields,
145  };
146
147  let sig = header.sign_lmpaat(&payload, server_keys).map_err(|e| {
148    ServerError::from_private(e)
149      .with_private_str("Can't sign LMPAAT header!")
150      .with_500()
151  })?;
152  tracing::debug!("LMPAAT (deploy): Signature: {:?}", sig);
153
154  let sig = MPAATSignature { sig };
155  let sig = STANDARD.encode(rmp_serde::to_vec(&sig).map_err(|e| {
156    ServerError::from_private(e)
157      .with_private_str("Can't serialize LMPAAT signature!")
158      .with_500()
159  })?);
160
161  let header = STANDARD.encode(rmp_serde::to_vec(&header).map_err(|e| {
162    ServerError::from_private(e)
163      .with_private_str("Can't serialize LMPAAT header!")
164      .with_500()
165  })?);
166
167  Ok(format!("{enc_payload}.{sig}.{header}"))
168}
169
170#[cfg(all(
171  feature = "crypt-utils",
172  feature = "authnz-utils",
173  any(feature = "pqc-utils", feature = "ed25519-utils")
174))]
175/// Extracts `common_fields` from MPAAT token.
176pub fn mpaat_extract_common_fields<U: serde::de::DeserializeOwned>(token: &str, server_keys: &SignKeypair) -> MResult<Option<U>> {
177  use base64::{
178    Engine as _,
179    engine::general_purpose::{STANDARD, URL_SAFE},
180  };
181  use impulse_server_kit::tracing;
182
183  let parts = token.split('.').collect::<Vec<_>>();
184
185  let sig = parts.get(1).ok_or(ServerError::from_public("Invalid token!").with_401())?;
186  let sig = STANDARD.decode(sig).map_err(|e| {
187    ServerError::from_private(e)
188      .with_private_str("Can't decode MPAAT signature!")
189      .with_500()
190  })?;
191  let sig = rmp_serde::from_slice::<MPAATSignature>(&sig).map_err(|e| {
192    ServerError::from_private(e)
193      .with_private_str("Can't deserialize MPAAT signature!")
194      .with_500()
195  })?;
196
197  tracing::debug!("MPAAT: Got signature: {:?}", sig.sig);
198
199  let payload = parts.first().ok_or(ServerError::from_public("Invalid token!").with_401())?;
200  let payload = URL_SAFE.decode(payload).map_err(|e| {
201    ServerError::from_private(e)
202      .with_private_str("Can't decode MPAAT payload!")
203      .with_500()
204  })?;
205
206  let header = parts.get(2).ok_or(ServerError::from_public("Invalid token!").with_401())?;
207  let header = STANDARD.decode(header).map_err(|e| {
208    ServerError::from_private(e)
209      .with_private_str("Can't decode MPAAT header!")
210      .with_500()
211  })?;
212
213  let authnz_pkey = server_keys.public();
214
215  SignKeypair::verify_token(&header, &payload, &sig.sig, &authnz_pkey).map_err(|e| {
216    ServerError::from_private(e)
217      .with_private_str("Token is unverified!")
218      .with_public("Invalid token signature, consider to sign in again.")
219      .with_401()
220  })?;
221
222  let header = rmp_serde::from_slice::<MPAATHeader<U>>(&header).map_err(|e| {
223    ServerError::from_private(e)
224      .with_private_str("Can't deserialize MPAAT header!")
225      .with_500()
226  })?;
227  if header.authnz_pkey != authnz_pkey.as_slice() {
228    return Err(
229      ServerError::from_private_str("Header's C3A public key and actual C3A public key are not equal!")
230        .with_public("Invalid token signature, consider to sign in again.")
231        .with_401(),
232    );
233  }
234
235  Ok(header.common_public_fields)
236}
237
238#[cfg(all(
239  feature = "crypt-utils",
240  feature = "authnz-utils",
241  any(feature = "pqc-utils", feature = "ed25519-utils")
242))]
243/// Extracts `payload` from MPAAT token.
244///
245/// This method checks the expiration of token.
246pub fn mpaat_extract_payload<T, U>(
247  token: &str,
248  server_enc: Option<&CipherKey>,
249  server_keys: &SignKeypair,
250  current_dt: chrono::DateTime<chrono::Utc>,
251) -> MResult<T>
252where
253  T: serde::de::DeserializeOwned,
254  U: serde::de::DeserializeOwned,
255{
256  use base64::{
257    Engine as _,
258    engine::general_purpose::{STANDARD, URL_SAFE},
259  };
260  use impulse_server_kit::tracing;
261
262  let parts = token.split('.').collect::<Vec<_>>();
263
264  let sig = parts.get(1).ok_or(ServerError::from_public("Invalid token!").with_401())?;
265  let sig = STANDARD.decode(sig).map_err(|e| {
266    ServerError::from_private(e)
267      .with_private_str("Can't decode MPAAT signature!")
268      .with_500()
269  })?;
270  let sig = rmp_serde::from_slice::<MPAATSignature>(&sig).map_err(|e| {
271    ServerError::from_private(e)
272      .with_private_str("Can't deserialize MPAAT signature!")
273      .with_500()
274  })?;
275
276  tracing::debug!("MPAAT: Got signature: {:?}", sig.sig);
277
278  let payload = parts.first().ok_or(ServerError::from_public("Invalid token!").with_401())?;
279  let payload = URL_SAFE.decode(payload).map_err(|e| {
280    ServerError::from_private(e)
281      .with_private_str("Can't decode MPAAT payload!")
282      .with_500()
283  })?;
284
285  let header = parts.get(2).ok_or(ServerError::from_public("Invalid token!").with_401())?;
286  let header = STANDARD.decode(header).map_err(|e| {
287    ServerError::from_private(e)
288      .with_private_str("Can't decode MPAAT header!")
289      .with_500()
290  })?;
291
292  let authnz_pkey = server_keys.public();
293
294  SignKeypair::verify_token(&header, &payload, &sig.sig, &authnz_pkey).map_err(|e| {
295    ServerError::from_private(e)
296      .with_private_str("Token is unverified!")
297      .with_public("Invalid token signature, consider to sign in again.")
298      .with_401()
299  })?;
300
301  let header = rmp_serde::from_slice::<MPAATHeader<U>>(&header).map_err(|e| {
302    ServerError::from_private(e)
303      .with_private_str("Can't deserialize MPAAT header!")
304      .with_500()
305  })?;
306  if header.authnz_pkey != authnz_pkey.as_slice() {
307    return Err(
308      ServerError::from_private_str("Header's C3A public key and actual C3A public key are not equal!")
309        .with_public("Invalid token signature, consider to sign in again.")
310        .with_401(),
311    );
312  }
313
314  let payload = if let Some(server_enc) = server_enc {
315    tracing::debug!("MPAAT: Decrypting with given `ekey`...");
316    server_enc.decrypt::<MPAATPayload<T>>(&payload, &header.nonce).map_err(|e| {
317      ServerError::from_private(e)
318        .with_private_str("Can't decrypt payload with given `server_enc` key!")
319        .with_public("Invalid token, consider to sign in again.")
320        .with_401()
321    })?
322  } else {
323    tracing::debug!("MPAAT: Skip decryption...");
324    rmp_serde::from_slice::<MPAATPayload<T>>(&payload).map_err(|e| {
325      ServerError::from_private(e)
326        .with_private_str("Can't deserialize payload!")
327        .with_public("Invalid token, consider to sign in again.")
328        .with_401()
329    })?
330  };
331
332  tracing::debug!("MPAAT: Expires at: {:?}", payload.exp);
333
334  if current_dt >= payload.exp {
335    return Err(
336      ServerError::from_private_str("Token is expired!")
337        .with_public("Invalid token, consider to sign in again.")
338        .with_401(),
339    );
340  }
341
342  Ok(payload.container)
343}
344
345#[cfg(all(
346  feature = "crypt-utils",
347  feature = "authnz-utils",
348  any(feature = "pqc-utils", feature = "ed25519-utils")
349))]
350/// Extracts `payload` with its metadata from MPAAT token.
351///
352/// This method checks the expiration of token.
353pub fn mpaat_extract_payload_with_metadata<T, U>(
354  token: &str,
355  server_enc: Option<&CipherKey>,
356  server_keys: &SignKeypair,
357  current_dt: chrono::DateTime<chrono::Utc>,
358) -> MResult<MPAATPayload<T>>
359where
360  T: serde::de::DeserializeOwned,
361  U: serde::de::DeserializeOwned,
362{
363  use base64::{
364    Engine as _,
365    engine::general_purpose::{STANDARD, URL_SAFE},
366  };
367  use impulse_server_kit::tracing;
368
369  let parts = token.split('.').collect::<Vec<_>>();
370
371  let sig = parts.get(1).ok_or(ServerError::from_public("Invalid token!").with_401())?;
372  let sig = STANDARD.decode(sig).map_err(|e| {
373    ServerError::from_private(e)
374      .with_private_str("Can't decode MPAAT signature!")
375      .with_500()
376  })?;
377  let sig = rmp_serde::from_slice::<MPAATSignature>(&sig).map_err(|e| {
378    ServerError::from_private(e)
379      .with_private_str("Can't deserialize MPAAT signature!")
380      .with_500()
381  })?;
382
383  tracing::debug!("MPAAT: Got signature: {:?}", sig.sig);
384
385  let payload = parts.first().ok_or(ServerError::from_public("Invalid token!").with_401())?;
386  let payload = URL_SAFE.decode(payload).map_err(|e| {
387    ServerError::from_private(e)
388      .with_private_str("Can't decode MPAAT payload!")
389      .with_500()
390  })?;
391
392  let header = parts.get(2).ok_or(ServerError::from_public("Invalid token!").with_401())?;
393  let header = STANDARD.decode(header).map_err(|e| {
394    ServerError::from_private(e)
395      .with_private_str("Can't decode MPAAT header!")
396      .with_500()
397  })?;
398
399  let authnz_pkey = server_keys.public();
400
401  SignKeypair::verify_token(&header, &payload, &sig.sig, &authnz_pkey).map_err(|e| {
402    ServerError::from_private(e)
403      .with_private_str("Token is unverified!")
404      .with_public("Invalid token signature, consider to sign in again.")
405      .with_401()
406  })?;
407
408  let header = rmp_serde::from_slice::<MPAATHeader<U>>(&header).map_err(|e| {
409    ServerError::from_private(e)
410      .with_private_str("Can't deserialize MPAAT header!")
411      .with_500()
412  })?;
413  if header.authnz_pkey != authnz_pkey.as_slice() {
414    return Err(
415      ServerError::from_private_str("Header's C3A public key and actual C3A public key are not equal!")
416        .with_public("Invalid token signature, consider to sign in again.")
417        .with_401(),
418    );
419  }
420
421  let payload = if let Some(server_enc) = server_enc {
422    tracing::debug!("MPAAT: Decrypting with given `ekey`...");
423    server_enc.decrypt::<MPAATPayload<T>>(&payload, &header.nonce).map_err(|e| {
424      ServerError::from_private(e)
425        .with_private_str("Can't decrypt payload with given `server_enc` key!")
426        .with_public("Invalid token, consider to sign in again.")
427        .with_401()
428    })?
429  } else {
430    tracing::debug!("MPAAT: Skip decryption...");
431    rmp_serde::from_slice::<MPAATPayload<T>>(&payload).map_err(|e| {
432      ServerError::from_private(e)
433        .with_private_str("Can't deserialize payload!")
434        .with_public("Invalid token, consider to sign in again.")
435        .with_401()
436    })?
437  };
438
439  tracing::debug!("MPAAT: Expires at: {:?}", payload.exp);
440
441  if current_dt >= payload.exp {
442    return Err(
443      ServerError::from_private_str("Token is expired!")
444        .with_public("Invalid token, consider to sign in again.")
445        .with_401(),
446    );
447  }
448
449  Ok(payload)
450}
451
452#[cfg(all(
453  feature = "crypt-utils",
454  feature = "authnz-utils",
455  any(feature = "pqc-utils", feature = "ed25519-utils")
456))]
457/// Extracts `payload` with its metadata from MPAAT token.
458///
459/// This method doesn't check the expiration of token!
460pub fn mpaat_extract_payload_with_metadata_exp_ignored<T, U>(
461  token: &str,
462  server_enc: Option<&CipherKey>,
463  server_keys: &SignKeypair,
464) -> MResult<MPAATPayload<T>>
465where
466  T: serde::de::DeserializeOwned,
467  U: serde::de::DeserializeOwned,
468{
469  use base64::{
470    Engine as _,
471    engine::general_purpose::{STANDARD, URL_SAFE},
472  };
473  use impulse_server_kit::tracing;
474
475  let parts = token.split('.').collect::<Vec<_>>();
476
477  let sig = parts.get(1).ok_or(ServerError::from_public("Invalid token!").with_401())?;
478  let sig = STANDARD.decode(sig).map_err(|e| {
479    ServerError::from_private(e)
480      .with_private_str("Can't decode MPAAT signature!")
481      .with_500()
482  })?;
483  let sig = rmp_serde::from_slice::<MPAATSignature>(&sig).map_err(|e| {
484    ServerError::from_private(e)
485      .with_private_str("Can't deserialize MPAAT signature!")
486      .with_500()
487  })?;
488
489  tracing::debug!("MPAAT: Got signature: {:?}", sig.sig);
490
491  let payload = parts.first().ok_or(ServerError::from_public("Invalid token!").with_401())?;
492  let payload = URL_SAFE.decode(payload).map_err(|e| {
493    ServerError::from_private(e)
494      .with_private_str("Can't decode MPAAT payload!")
495      .with_500()
496  })?;
497
498  let header = parts.get(2).ok_or(ServerError::from_public("Invalid token!").with_401())?;
499  let header = STANDARD.decode(header).map_err(|e| {
500    ServerError::from_private(e)
501      .with_private_str("Can't decode MPAAT header!")
502      .with_500()
503  })?;
504
505  let authnz_pkey = server_keys.public();
506
507  SignKeypair::verify_token(&header, &payload, &sig.sig, &authnz_pkey).map_err(|e| {
508    ServerError::from_private(e)
509      .with_private_str("Token is unverified!")
510      .with_public("Invalid token signature, consider to sign in again.")
511      .with_401()
512  })?;
513
514  let header = rmp_serde::from_slice::<MPAATHeader<U>>(&header).map_err(|e| {
515    ServerError::from_private(e)
516      .with_private_str("Can't deserialize MPAAT header!")
517      .with_500()
518  })?;
519  if header.authnz_pkey != authnz_pkey.as_slice() {
520    return Err(
521      ServerError::from_private_str("Header's C3A public key and actual C3A public key are not equal!")
522        .with_public("Invalid token signature, consider to sign in again.")
523        .with_401(),
524    );
525  }
526
527  let payload = if let Some(server_enc) = server_enc {
528    tracing::debug!("MPAAT: Decrypting with given `ekey`...");
529    server_enc.decrypt::<MPAATPayload<T>>(&payload, &header.nonce).map_err(|e| {
530      ServerError::from_private(e)
531        .with_private_str("Can't decrypt payload with given `server_enc` key!")
532        .with_public("Invalid token, consider to sign in again.")
533        .with_401()
534    })?
535  } else {
536    tracing::debug!("MPAAT: Skip decryption...");
537    rmp_serde::from_slice::<MPAATPayload<T>>(&payload).map_err(|e| {
538      ServerError::from_private(e)
539        .with_private_str("Can't deserialize payload!")
540        .with_public("Invalid token, consider to sign in again.")
541        .with_401()
542    })?
543  };
544
545  Ok(payload)
546}
547
548#[cfg(all(
549  feature = "crypt-utils",
550  feature = "authnz-utils",
551  any(feature = "pqc-utils", feature = "ed25519-utils")
552))]
553/// Extracts `common_fields` from LMPAAT token.
554pub fn lmpaat_extract_common_fields<U: serde::de::DeserializeOwned>(token: &str, server_keys: &SignKeypair) -> MResult<Option<U>> {
555  use base64::{
556    Engine as _,
557    engine::general_purpose::{STANDARD, URL_SAFE},
558  };
559  use impulse_server_kit::tracing;
560
561  let parts = token.split('.').collect::<Vec<_>>();
562
563  let sig = parts.get(1).ok_or(ServerError::from_public("Invalid token!").with_401())?;
564  let sig = STANDARD.decode(sig).map_err(|e| {
565    ServerError::from_private(e)
566      .with_private_str("Can't decode LMPAAT signature!")
567      .with_500()
568  })?;
569  let sig = rmp_serde::from_slice::<LightMPAATSignature>(&sig).map_err(|e| {
570    ServerError::from_private(e)
571      .with_private_str("Can't deserialize LMPAAT signature!")
572      .with_500()
573  })?;
574
575  tracing::debug!("LMPAAT: Got signature: {:?}", sig.sig);
576
577  let payload = parts.first().ok_or(ServerError::from_public("Invalid token!").with_401())?;
578  let payload = URL_SAFE.decode(payload).map_err(|e| {
579    ServerError::from_private(e)
580      .with_private_str("Can't decode LMPAAT payload!")
581      .with_500()
582  })?;
583
584  let header = parts.get(2).ok_or(ServerError::from_public("Invalid token!").with_401())?;
585  let header = STANDARD.decode(header).map_err(|e| {
586    ServerError::from_private(e)
587      .with_private_str("Can't decode LMPAAT header!")
588      .with_500()
589  })?;
590
591  SignKeypair::verify_token(&header, &payload, &sig.sig, &server_keys.public()).map_err(|e| {
592    ServerError::from_private(e)
593      .with_private_str("LMPAAT token is unverified!")
594      .with_public("Invalid token signature, consider to sign in again.")
595      .with_401()
596  })?;
597
598  let header = rmp_serde::from_slice::<LightMPAATHeader<U>>(&header).map_err(|e| {
599    ServerError::from_private(e)
600      .with_private_str("Can't deserialize LMPAAT header!")
601      .with_500()
602  })?;
603  Ok(header.common_public_fields)
604}
605
606#[cfg(all(
607  feature = "crypt-utils",
608  feature = "authnz-utils",
609  any(feature = "pqc-utils", feature = "ed25519-utils")
610))]
611/// Extracts `payload` from LMPAAT token.
612///
613/// This method checks the expiration of token.
614pub fn lmpaat_extract_payload<T, U>(token: &str, server_keys: &SignKeypair, current_dt: chrono::DateTime<chrono::Utc>) -> MResult<T>
615where
616  T: serde::de::DeserializeOwned,
617  U: serde::de::DeserializeOwned,
618{
619  use base64::{
620    Engine as _,
621    engine::general_purpose::{STANDARD, URL_SAFE},
622  };
623  use impulse_server_kit::tracing;
624
625  let parts = token.split('.').collect::<Vec<_>>();
626
627  let sig = parts.get(1).ok_or(ServerError::from_public("Invalid token!").with_401())?;
628  let sig = STANDARD.decode(sig).map_err(|e| {
629    ServerError::from_private(e)
630      .with_private_str("Can't decode LMPAAT signature!")
631      .with_500()
632  })?;
633  let sig = rmp_serde::from_slice::<LightMPAATSignature>(&sig).map_err(|e| {
634    ServerError::from_private(e)
635      .with_private_str("Can't deserialize LMPAAT signature!")
636      .with_500()
637  })?;
638
639  tracing::debug!("LMPAAT: Got signature: {:?}", sig.sig);
640
641  let payload = parts.first().ok_or(ServerError::from_public("Invalid token!").with_401())?;
642  let payload = URL_SAFE.decode(payload).map_err(|e| {
643    ServerError::from_private(e)
644      .with_private_str("Can't decode LMPAAT payload!")
645      .with_500()
646  })?;
647
648  let header = parts.get(2).ok_or(ServerError::from_public("Invalid token!").with_401())?;
649  let header = STANDARD.decode(header).map_err(|e| {
650    ServerError::from_private(e)
651      .with_private_str("Can't decode LMPAAT header!")
652      .with_500()
653  })?;
654
655  SignKeypair::verify_token(&header, &payload, &sig.sig, &server_keys.public()).map_err(|e| {
656    ServerError::from_private(e)
657      .with_private_str("LMPAAT token is unverified!")
658      .with_public("Invalid token signature, consider to sign in again.")
659      .with_401()
660  })?;
661
662  let payload = rmp_serde::from_slice::<LightMPAATPayload<T>>(&payload).map_err(|e| {
663    ServerError::from_private(e)
664      .with_private_str("Can't deserialize LMPAAT header!")
665      .with_500()
666  })?;
667
668  tracing::debug!("LMPAAT: Expires at: {:?}", payload.exp);
669
670  if current_dt >= payload.exp {
671    return Err(
672      ServerError::from_private_str("LMPAAT token is expired!")
673        .with_public("Invalid token, consider to sign in again.")
674        .with_401(),
675    );
676  }
677
678  Ok(payload.container)
679}
680
681/// Splits the token if it exceeds `max_len`.
682pub fn split_token(token: &str, max_len: usize) -> Vec<String> {
683  let mut result = Vec::new();
684  let bytes = token.as_bytes();
685  let mut start = 0;
686
687  while start < bytes.len() {
688    let end = std::cmp::min(start + max_len, bytes.len());
689    result.push(token[start..end].to_string());
690    start = end;
691  }
692
693  result
694}
695
696/// Unites a token from its parts.
697pub fn unite_token(parts: Vec<String>) -> String {
698  parts.join("")
699}
700
701/// Makes timestamp of expiration from duration and current system time.
702pub fn take_exp_from_duration(duration: chrono::TimeDelta) -> chrono::DateTime<chrono::Utc> {
703  chrono::Utc::now() + duration
704}
705
706#[cfg(test)]
707mod tests {
708  use std::collections::HashSet;
709
710  use crate::{
711    AppAuthConfiguration, CipherKey, MPAATHeader, MPAATPayload, SignKeypair, TokenBundle, deploy_mpaat, mpaat_extract_common_fields,
712    mpaat_extract_payload,
713  };
714  use chrono::Utc;
715  use serde::{Deserialize, Serialize};
716
717  #[test]
718  fn verify_restore() {
719    let keypair = SignKeypair::new_ed25519().unwrap();
720    let cert = keypair.pack_keypair();
721    assert!(SignKeypair::unpack_keypair(cert).is_ok());
722  }
723
724  #[test]
725  fn verify_sign() {
726    let keypair = SignKeypair::new_ed25519().unwrap();
727    let sig = keypair.sign_raw(b"123456");
728    assert!(SignKeypair::verify_raw(b"123456", &sig, &keypair.public()).is_ok());
729  }
730
731  #[test]
732  fn verify_sign_eq() {
733    let keypair = SignKeypair::new_ed25519().unwrap();
734
735    let val = AppAuthConfiguration {
736      app_name: "default".into(),
737      allowed_tags: vec![("user", "default").into(), ("user", "main").into()],
738      sign_up_opts: crate::SignUpOpts {
739        identify_by: crate::IdenticationRequirement::Nickname {
740          spaces: false,
741          upper_registry: true,
742          characters: false,
743        },
744        allow_sign_up: true,
745        auto_assign_tags: vec![("user", "default").into(), ("user", "main").into()],
746        allowed_authn: vec![],
747        required_authn: vec![],
748      },
749      sign_in_opts: crate::SignInOpts {
750        enable_fail_to_ban: None,
751        token_encryption_type: crate::TokenEncryptionType::ChaCha20Poly1305,
752      },
753      client_based_auth_opts: crate::ClientBasedAuthorizationOpts { enable_cba: true },
754      tokens_lifetime: crate::TokenLifetimes::default(),
755      app_pkey: None,
756    };
757
758    for _ in 0..1000 {
759      let sign1 = keypair.sign(&val).unwrap();
760      let sign2 = keypair.sign(&val).unwrap();
761
762      assert_eq!(sign1.as_slice(), sign2.as_slice());
763    }
764  }
765
766  #[test]
767  fn verify_token_signs2() {
768    let keypair = SignKeypair::new_ed25519().unwrap();
769    let dt = Utc::now().checked_add_months(chrono::Months::new(1)).unwrap();
770
771    println!("Public key: {:?}", keypair.public());
772    println!("Secret key: {:?}", unsafe { keypair.private() });
773    println!();
774
775    #[derive(Deserialize, Serialize)]
776    struct Payload {
777      data: Vec<usize>,
778    }
779
780    let act = deploy_mpaat(
781      Payload { data: vec![123] },
782      Some(Payload { data: vec![456] }),
783      dt.clone(),
784      &[0, 1, 2],
785      None,
786      &keypair,
787    )
788    .unwrap();
789    println!("{}", act);
790    let cf = mpaat_extract_common_fields::<Payload>(&act, &keypair);
791    cf.unwrap();
792    let pld = mpaat_extract_payload::<Payload, Payload>(&act, None, &keypair, Utc::now());
793    pld.unwrap();
794  }
795
796  #[test]
797  fn triple_pack_unpack() {
798    let triple = TokenBundle::new_with_cba("123", "456", "789");
799    assert_eq!(TokenBundle::unpack(&triple.pack()), Some(triple));
800
801    let triple = TokenBundle::new_basic("123", "456");
802    assert_eq!(TokenBundle::unpack(&triple.pack()), Some(triple));
803  }
804}