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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
use serde_derive::{Serialize, Deserialize};
use chrono::prelude::*;
use sled::Db;
use secrecy::{Secret, ExposeSecret};
use zeroize::Zeroize;

use crate::{Role, Target, secrets, SGSecret, SGError};

fn sg_auth() -> &'static str {
    "./SchemeGuardianDB/SG_AUTH"
}
    /// `AuthPayload` creates and authenticates auth values
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthPayload<R> {
    role: Role<R>,
    target: Target,
    lease: Lease,
    random_key: String,
}

    /// `AuthEngine` creates and authenticates authorization/authentication secrets
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthEngine<R> {
    bearer: SGSecret,
    payload: AuthPayload<R>,
}

impl<'e, R> AuthEngine<R> where R: std::fmt::Debug + std::cmp::PartialEq + std::cmp::Eq + serde::Serialize + serde::de::DeserializeOwned {
        /// Initialize a AuthEngine for creating and authenticating branca secrets
    pub fn new() -> Self {
        Self { 
            bearer: Default::default(), 
            payload: AuthPayload {
                role: Default::default(), 
                target: Default::default(), 
                lease: Default::default(),
                random_key: secrets::random64alpha().expose_secret().to_owned(),
            }
        }
    }
        /// The username or client name  
    pub fn bearer(mut self, bearer: Secret<String>) -> Self {
        self.bearer = SGSecret(bearer.expose_secret().clone().to_owned());
        
        self
    }
        /// The role of the client  
    pub fn role(mut self, role: Role<R>) -> Self {
        self.payload.role = role;
        
        self
    }
        /// The expiry date or time for the secret
    pub fn expiry(mut self, expiry: chrono::Duration) -> Self  {
        self.payload.lease = Lease::DateExpiry(Utc::now() + expiry);
        
        self
    }
        /// Target for the operation
    pub fn target(mut self, attr: Target) -> Self {
        self.payload.random_key = secrets::random64alpha().expose_secret().to_owned();
        self.payload.target = attr;
        
        self
    }

        /// Insert new token
    pub fn insert(self) -> Result<(custom_codes::DbOps, Secret<String>, Option<AuthPayload<R>>), SGError> {
        let auth_db = sg_auth();
        let db = Db::open(auth_db)?;

        let key = bincode::serialize(&self.bearer.0)?; 

        let value = bincode::serialize::<AuthPayload<R>>(&self.payload)?; //TODO: Should I encrypt bearer with branca in index

        let dbop = db.insert(key, value)?;

        let bearer_key = self.bearer.0.clone() + ":::" + &self.payload.random_key;

        if let Some(updated) = dbop {
            let data = bincode::deserialize::<AuthPayload<R>>(&updated)?;
            Ok((custom_codes::DbOps::Modified, Secret::new(bearer_key), Some(data)))
        }else {
            Ok((custom_codes::DbOps::Inserted, Secret::new(bearer_key), None))
        }        
    }
        /// Create a new branca encoded token
    pub fn issue(self) -> Result<(custom_codes::DbOps, SGSecret, Option<AuthPayload<R>>), SGError> {
        let auth_db = sg_auth();
        let db = Db::open(auth_db)?;

        let key = bincode::serialize(&self.bearer.0)?; 

        let value = bincode::serialize::<AuthPayload<R>>(&self.payload)?; //TODO: Should I encrypt bearer with branca in index

        let dbop = db.insert(key, value)?;

        let raw_key = self.bearer.0.clone() + ":::" + &self.payload.random_key;
        let bearer_key = crate::secrets::branca_encode(Secret::new(raw_key))?;

        if let Some(updated) = dbop {
            let data = bincode::deserialize::<AuthPayload<R>>(&updated)?;
            Ok((custom_codes::DbOps::Modified, bearer_key, Some(data)))
        }else {
            Ok((custom_codes::DbOps::Inserted, bearer_key, None))
        }        
    }
        
        /// Authenticate an existing token
    pub fn get(self, raw_key: Secret<String>) -> Result<(custom_codes::DbOps, Option<Payload<R>>), SGError> {
        let auth_db = sg_auth();
        let db = Db::open(auth_db)?;

        let raw_key = raw_key.expose_secret();
        let dual = raw_key.split(":::").collect::<Vec<&str>>();
        let key = bincode::serialize(dual[0])?;

        let check_key = db.get(key)?;

        if let Some(binary) = check_key {
            let data = bincode::deserialize::<AuthPayload<R>>(&binary)?;
            Ok((custom_codes::DbOps::KeyFound, Some((data.role, data.target))))
        }else {
            Ok((custom_codes::DbOps::KeyNotFound, None))
        } 
    }
        
        /// Authenticate an existing token
        /// Currently returns:
        ///     `custom_codes::AccessStatus::Expired` for an secret that has reached end of life
        ///     `custom_codes::AccessStatus::Granted` for a secret that is live and RAC is authenticated
        ///     `custom_codes::AccessStatus::RejectedRAC` for a secret that is live but the RAC is not authentic
        ///     `custom_codes::AccessStatus::Rejected` for a secret that cannot be authenticated
    pub fn authenticate(self, raw_key: Secret<String>) -> Result<(custom_codes::AccessStatus, Option<Payload<R>>), SGError> {
        let auth_db = sg_auth();
        let db = Db::open(auth_db)?;

        let raw_key = raw_key.expose_secret();
        let dual = raw_key.split(":::").collect::<Vec<&str>>();

        let key = bincode::serialize(dual[0])?;
        let user_random_key = dual[1];

        let check_key = db.get(key)?;

        if let Some(binary) = check_key {
            let payload = bincode::deserialize::<AuthPayload<R>>(&binary)?;
            match payload.lease {
                Lease::DateExpiry(datetime) => {
                    if Utc::now() > datetime {
                        Ok((custom_codes::AccessStatus::Expired, None))
                    }else {
                        if payload.random_key == user_random_key {
                            Ok((custom_codes::AccessStatus::Granted, Some((payload.role, payload.target))))
                        }else {
                            Ok((custom_codes::AccessStatus::RejectedRAC, None))
                        }
                    }
                },
                Lease::Lifetime => Ok((custom_codes::AccessStatus::Granted, Some((payload.role, payload.target)))),
                _ => Ok((custom_codes::AccessStatus::Rejected, None))
            }            
        }else {
            Ok((custom_codes::AccessStatus::Rejected, None))
        } 
    }

        /// Remove a secret from the database
    pub fn rm<'d>(self, raw_key: Secret<String>) -> Result<(custom_codes::DbOps, Option<FullPayload<R>>), SGError> {
        let auth_db = sg_auth();
        let db = Db::open(auth_db)?;

        let raw_key = raw_key.expose_secret();
        let dual = raw_key.split(":::").collect::<Vec<&str>>();
        let key = bincode::serialize(dual[0])?;

        let check_key = db.remove(key)?;

        if let Some(binary) = check_key {
            let data = bincode::deserialize::<AuthPayload<R>>(&binary)?;
            Ok((custom_codes::DbOps::Deleted, Some((data.role, data.lease, data.target, data.random_key))))
        }else {
            Ok((custom_codes::DbOps::KeyNotFound, None))
        } 
    }

       /// Show all database entries
    pub fn list_keys(self) -> Result<Vec<u8>, SGError> {
        let auth_db = sg_auth();
        let db = Db::open(auth_db)?;
        
        let mut sled_vec = vec![];

        db.iter().keys().for_each(|data| {
            if let Ok(inner) = data {
                sled_vec.push(inner);
            }else {
                sled_vec.clear();
            }
        });

        Ok(vec![])
    }
    
       /// Show all database entries
    pub fn list_values(self) -> Result<Vec<u8>, SGError> {
        let auth_db = sg_auth();
        let db = Db::open(auth_db)?;
        
        let mut sled_vec = vec![];

        db.iter().values().for_each(|data| {
            if let Ok(inner) = data {
                sled_vec.push(inner);
            }else {
                sled_vec.clear();
            }
        });

        Ok(vec![])
    }
}

    /// A return value to an of the operation. It `contains the payload of the AuthPayload` from `AuthEngine`
pub type FullPayload<R> = (Role<R>, Lease, Target, String);

    /// A return value to an of the operation. It contains the payload of the AuthPayload values `Role` & `Target`
pub type Payload<R> = (Role<R>, Target);

    /// A an expiry date to lease a secret
#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, Clone, Eq)]
pub enum Lease {
        /// Has an expiry date of DateTime<Utc>
    DateExpiry(DateTime<Utc>),
        /// This is a lease to a secret that will never expire
    Lifetime,
        /// This is a lease to a secret that will expire after the download is completed
    OnDownload,
        /// This is a lease to a secret that will expire after the upload is completed
    OnUpload,
        /// This is a lease to a secret that will expire after the network is disconnected
    OnDisconnection,
        /// The lease time has not been specified by the user
    UnSpecified,
}

impl Default for Lease {
    fn default() -> Self{ Lease::DateExpiry(Utc::now() + chrono::Duration::hours(24)) }
}

    /// Type of `secret`
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Zeroize)]
#[zeroize(drop)]
pub enum SecretType {
        /// A normal cookie
    Cookie,
        /// A branca token
    Branca,
        /// Time based auth
    TOTP,
        /// USB hardware auth
    USBkey,
        /// Near Field Communication
    NFC,
        /// Fingerprint Auth
    Fingerprint,
        /// Eye Iris scan
    Iris,
        /// Infrared scan
    IRscan,
        /// Noise Protocol Secret
    Noise,
        /// TLS Certificate
    TlsCert,
        /// DMARC for Email
    DMARC,
        /// DANE for Email
    DANE,
        /// A passphrase/ Password or PIN
    Passphrase,
        /// Bluetooth
    BluetoothPairKey,
        /// Username authenticator
    Bearer,
        /// A custom token authentication mechanism 
    CustomToken,
        /// An API key
    ApiKey,
        /// An email address
    EmailAddr,
        /// A DNS Security(DNSSEC, RPKI, ESNI, DoT, DoH, DoN<DNS over Noise>) -- `TO be confirmed`
    /* DNS... */
        /// A blockchain transaction
    BlockchainTx,
        /// A QR code
    QrCode,
        /// A barcode
    Barcode,
        /// Access to a hardware address, usable in a firewall router
    HardwareMacAddr,
        /// Access to an IP Address, usable in a firewall router
    IpAddress,
        /// Access to a Subnet, usable in a firewall router
    Subnet,
        /// Access to a port, usable in a firewall router
    Port,
        /// The type of secret is yet to be specified, usefull especially when you want to initialize something
    UnSpecified,
}