1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2020-2021 Ian Jackson and contributors to Otter
// SPDX-License-Identifier: AGPL-3.0-or-later
// There is NO WARRANTY.

use crate::prelude::*;

use bundles::{DigestWrite, Digester};

//---------- public types ----------

#[derive(Clone,Serialize,Deserialize)]
pub enum AssetUrlKey {
  Dummy,
  Y(AssetUrlKeyRaw),
}

#[derive(Error,Debug,Copy,Clone,Serialize)]
pub struct BadAssetUrlToken;
display_as_debug!{BadAssetUrlToken}

#[derive(Clone)]
pub struct AssetUrlToken(AssetUrlTokenRaw);

//---------- private types ----------

type AssetUrlKeyRaw = [u8; 32];

//---------- primary functionality ----------

impl AssetUrlKey {
  pub fn token<V>(&self, what: &str, v: V) -> AssetUrlToken
  where V: Serialize {
    let k = match self {
      AssetUrlKey::Y(k) => k,
      _ => panic!("dummy AssetUrlKey being used!"),
    };
    let mut dw = DigestWrite::sink();
    write!(dw, "{}\0", what).unwrap();
    dw.write_all(&k[..]).unwrap();
    rmp_serde::encode::write(&mut dw, &v).expect("serialize failed!");
    AssetUrlToken(dw.finish().0)
  }

  #[throws(BadAssetUrlToken)]
  pub fn check<V>(&self, what: &str, v: &V, got: &AssetUrlToken)
                  -> Authorisation<V>
  where V: Serialize {
    let exp = self.token(what, v);
    if ! bool::from(ConstantTimeEq::ct_eq(
      &exp.0[..],
      &got.0[..],
    )) { throw!(BadAssetUrlToken) }
    else { Authorisation::promise_for(v) }
  }
}

//---------- AssetUrlKey impl's ----------

impl Debug for AssetUrlKey {
  #[throws(fmt::Error)]
  fn fmt(&self, f: &mut Formatter) {
    use AssetUrlKey::*;
    match self {
      Y(_) => write!(f, "AssetUrlKey::Y{{..}}")?,
      Dummy => write!(f, "AssetUrlKey::Dummy")?,
    }
  }
}

impl Default for AssetUrlKey { fn default() -> Self { Self::Dummy } }

impl AssetUrlKey {
  #[throws(IE)]
  pub fn new_random() -> AssetUrlKey {
    let mut buf: AssetUrlKeyRaw = default();
    let mut rng: rand::rngs::ThreadRng = thread_rng();
    rand::RngCore::try_fill_bytes(&mut rng, &mut buf)
      .context("generate new AssetUrlKey")?;
    AssetUrlKey::Y(buf)
  }
}

//---------- AssetUrlToken impl's ----------

type AssetUrlTokenRaw = digest::Output<Digester>;

impl Debug for AssetUrlToken {
  #[throws(fmt::Error)]
  fn fmt(&self, f: &mut Formatter) { write!(f, "AssetUrlToken{{..}}")?; }
}

impl Display for AssetUrlToken {
  #[throws(fmt::Error)]
  fn fmt(&self, f: &mut Formatter) {
    f.write_str(&base64::encode_config(&self.0, base64::URL_SAFE_NO_PAD))?
  }
}
impl FromStr for AssetUrlToken {
  type Err = BadAssetUrlToken;
  #[throws(BadAssetUrlToken)]
  fn from_str(s: &str) -> Self {
    let mut buf: AssetUrlTokenRaw = default();
    let l = base64::decode_config_slice(
      s.as_bytes(), base64::URL_SAFE_NO_PAD, &mut buf)
      .map_err(|_| BadAssetUrlToken)?;
    if l != buf.len() { throw!(BadAssetUrlToken) }
    AssetUrlToken(buf)
  }
}
hformat_as_display!{AssetUrlToken}