ncryptf/
token.rs

1use chrono::Utc;
2use dryoc::constants::{
3    CRYPTO_SIGN_PUBLICKEYBYTES,
4    CRYPTO_SIGN_SECRETKEYBYTES
5};
6use dryoc::sign::SigningKeyPair;
7use base64::{Engine as _, engine::general_purpose};
8
9use crate::error::NcryptfError as Error;
10use serde::{Deserialize, Serialize};
11
12/// Authorization token data to be either stored locally, or server side in a cache.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Token {
15    pub access_token: String,
16    pub refresh_token: String,
17    pub ikm: Vec<u8>,
18    pub signature: Vec<u8>,
19    pub expires_at: i64,
20}
21
22impl Token {
23    /// Creates a new token with a given lifetime
24    pub fn new(lifetime: i64) -> Token {
25        let now = Utc::now().timestamp();
26        let expires_at: i64;
27        if lifetime >= 0 {
28            expires_at = now + lifetime;
29        } else {
30            expires_at = now;
31        }
32
33        return Self {
34            access_token: general_purpose::URL_SAFE.encode(crate::util::randombytes_buf(48)),
35            refresh_token: general_purpose::URL_SAFE.encode(crate::util::randombytes_buf(64)),
36            ikm: crate::util::randombytes_buf(32),
37            signature: crate::util::randombytes_buf(64),
38            expires_at: expires_at,
39        };
40    }
41
42    /// Creates a new token from the provided values
43    pub fn from(
44        access_token: String,
45        refresh_token: String,
46        ikm: Vec<u8>,
47        signature: Vec<u8>,
48        expires_at: i64,
49    ) -> Result<Token, Error> {
50        if ikm.len() != 32 {
51            return Err(Error::InvalidArgument(format!(
52                "Initial key material should be {} bytes",
53                32
54            )));
55        }
56
57        if signature.len() != 64 {
58            return Err(Error::InvalidArgument(format!(
59                "Signature secret key should be {} bytes",
60                64
61            )));
62        }
63
64        return Ok(Self {
65            access_token,
66            refresh_token,
67            ikm,
68            signature,
69            expires_at,
70        });
71    }
72
73    /// Given a serde_json::Value, will attempt to return a token
74    pub fn from_json(json: serde_json::Value) -> Result<Self, Error> {
75        if json.get("access_token").is_none()
76            || json.get("refresh_token").is_none()
77            || json.get("ikm").is_none()
78            || json.get("signing").is_none()
79            || json.get("expires_at").is_none()
80        {
81            return Err(Error::InvalidArgument(format!(
82                "The provided JSON object is not valid for tokenization."
83            )));
84        }
85
86        return match Token::from(
87            json.get("access_token").unwrap().to_string(),
88            json.get("refresh_token").unwrap().to_string(),
89            general_purpose::STANDARD.decode(json.get("ikm").unwrap().as_str().unwrap()).unwrap(),
90            general_purpose::STANDARD.decode(json.get("signing").unwrap().as_str().unwrap()).unwrap(),
91            json.get("expires_at").unwrap().as_i64().unwrap(),
92        ) {
93            Ok(token) => return Ok(token),
94            Err(_error) => Err(_error),
95        };
96    }
97
98    /// Returns true of the token is expired
99    /// If the token is expired, it should be discarded
100    pub fn is_expired(&self) -> bool {
101        let now = Utc::now().timestamp();
102        return now > self.expires_at;
103    }
104
105    /// Returns the public key for the signature
106    pub fn get_signature_public_key(&self) -> Result<Vec<u8>, Error> {
107        if self.signature.len() != CRYPTO_SIGN_SECRETKEYBYTES {
108            return Err(Error::TokenSignatureSize(format!(
109                "Signature secret key should be {} bytes",
110                CRYPTO_SIGN_SECRETKEYBYTES
111            )));
112        }
113
114        // Convert Vec<u8> to fixed-size array
115        let mut secret_key_bytes = [0u8; CRYPTO_SIGN_SECRETKEYBYTES];
116        secret_key_bytes.copy_from_slice(&self.signature);
117
118        let keypair: SigningKeyPair<[u8; CRYPTO_SIGN_PUBLICKEYBYTES], [u8; CRYPTO_SIGN_SECRETKEYBYTES]> = SigningKeyPair::from_secret_key(secret_key_bytes);
119
120        return Ok(keypair.public_key.to_vec());
121    }
122}