Skip to main content

tower_defense/auth/
mod.rs

1use std::borrow::Cow;
2
3use jsonrpsee::{
4    core::{params::ObjectParams, traits::ToRpcParams},
5    types::Id,
6};
7use serde::{Deserialize, Serialize};
8use serde_json::value::RawValue;
9
10use crate::{
11    crypto::{Keypair, PeerId, PublicKey, Signature},
12    error::Error,
13};
14
15pub mod timestamp;
16pub use timestamp::VerifyTimestamp;
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct AuthenticatedParams<'a> {
20    pub(crate) signer: PublicKey,
21    pub(crate) signature: Signature,
22    pub(crate) timestamp: u64,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub(crate) inner: Option<Cow<'a, RawValue>>,
25}
26
27#[derive(Debug, Clone)]
28pub struct VerifiedParams<'a> {
29    pub peer: PeerId,
30    pub inner: Option<Cow<'a, RawValue>>,
31}
32
33impl<'a> AuthenticatedParams<'a> {
34    /// Prepare authenticated params at a given timestamp.
35    #[must_use]
36    pub fn prepare(
37        keypair: &Keypair,
38        id: &Id<'_>,
39        method: &str,
40        params: Option<Cow<'a, RawValue>>,
41        now: u64,
42    ) -> Self {
43        let signing_metarial = signing_material(now, id, method, params.as_ref());
44        let signature = keypair.sign(&signing_metarial);
45
46        Self {
47            signer: keypair.public(),
48            signature,
49            timestamp: now,
50            inner: params,
51        }
52    }
53
54    /// Prepare authenticated params using the current `SystemTime`.
55    ///
56    /// # Errors
57    ///
58    /// This function will return an error if current timpestamp generation fails.
59    pub fn prepare_now(
60        keypair: &Keypair,
61        id: &Id<'a>,
62        method: &'a str,
63        params: Option<Cow<'a, RawValue>>,
64    ) -> Result<Self, Error> {
65        Ok(Self::prepare(
66            keypair,
67            id,
68            method,
69            params,
70            timestamp::now()?,
71        ))
72    }
73
74    /// Verify authenticated params at a given timestamp.
75    ///
76    /// # Errors
77    ///
78    /// This function will return an error if the signature or timestamp verification fails.
79    pub fn verify<VT: VerifyTimestamp>(
80        self,
81        id: &Id<'_>,
82        method: &str,
83        verify_timestamp: &VT,
84    ) -> Result<VerifiedParams<'a>, Error> {
85        let signing_metarial = signing_material(self.timestamp, id, method, self.inner.as_ref());
86        self.signer.verify(&signing_metarial, &self.signature)?;
87
88        let peer = PeerId::from(self.signer);
89
90        if !verify_timestamp.verify(&peer, self.timestamp) {
91            return Err(Error::InvalidTimestamp);
92        }
93
94        Ok(VerifiedParams {
95            peer,
96            inner: self.inner,
97        })
98    }
99}
100
101impl ToRpcParams for AuthenticatedParams<'_> {
102    fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, serde_json::Error> {
103        let serde_json::Value::Object(obj) = serde_json::to_value(self)? else {
104            unreachable!("this type will always be serialize as a map");
105        };
106
107        let mut p = ObjectParams::new();
108        for (k, v) in obj {
109            p.insert(&k, v)?;
110        }
111
112        p.to_rpc_params()
113    }
114}
115
116fn signing_material(
117    ts: u64,
118    id: &Id<'_>,
119    method: &str,
120    params: Option<&Cow<'_, RawValue>>,
121) -> Vec<u8> {
122    // The raw json str or "" as bytes.
123    let param_bytes = params.map(|i| i.get()).unwrap_or_default().as_bytes();
124    [
125        id.to_string().as_bytes(),
126        ts.to_be_bytes().as_slice(),
127        method.as_bytes(),
128        param_bytes,
129    ]
130    .concat()
131}