culper_lib/vault/
mod.rs

1use base64::{decode, encode};
2use failure::*;
3
4#[derive(Debug)]
5#[allow(non_camel_case_types)]
6pub enum EncryptionFormat {
7    GPG_KEY,
8}
9
10impl EncryptionFormat {
11    pub fn as_str(&self) -> String {
12        match *self {
13            EncryptionFormat::GPG_KEY => String::from("GPG_KEY"),
14        }
15    }
16    pub fn from_str(value: &str) -> Result<EncryptionFormat, failure::Error> {
17        match value {
18            "GPG_KEY" => Ok(EncryptionFormat::GPG_KEY),
19            _ => Err(format_err!("Unknown encryption format given: {}", value).into()),
20        }
21    }
22}
23
24pub struct UnsealedVault {
25    pub plain_secret: String,
26    pub format: EncryptionFormat,
27}
28
29pub trait SealableVault {
30    fn seal<F>(self, f: &F) -> Result<SealedVault, failure::Error>
31    where
32        F: Fn(UnsealedVault) -> Result<SealedVault, failure::Error>;
33}
34
35impl UnsealedVault {
36    pub fn new(plain_secret: String, format: EncryptionFormat) -> UnsealedVault {
37        UnsealedVault {
38            plain_secret,
39            format,
40        }
41    }
42}
43
44impl SealableVault for UnsealedVault {
45    fn seal<F>(self, f: &F) -> Result<SealedVault, failure::Error>
46    where
47        F: Fn(UnsealedVault) -> Result<SealedVault, failure::Error>,
48    {
49        f(self)
50    }
51}
52
53pub struct SealedVault {
54    pub secret: Vec<u8>,
55    pub format: EncryptionFormat,
56}
57
58pub trait OpenableVault {
59    fn unseal<F>(self, f: &F) -> Result<UnsealedVault, failure::Error>
60    where
61        F: Fn(SealedVault) -> Result<UnsealedVault, failure::Error>;
62    fn to_string(&self) -> String;
63}
64
65impl SealedVault {
66    pub fn new(secret: Vec<u8>, format: EncryptionFormat) -> SealedVault {
67        SealedVault { secret, format }
68    }
69}
70
71impl OpenableVault for SealedVault {
72    fn unseal<F>(self, f: &F) -> Result<UnsealedVault, failure::Error>
73    where
74        F: Fn(SealedVault) -> Result<UnsealedVault, failure::Error>,
75    {
76        f(self)
77    }
78
79    fn to_string(&self) -> String {
80        format!("CULPER.{}.{}", self.format.as_str(), encode(&self.secret),)
81    }
82}
83
84pub trait VaultHandler {
85    fn encrypt(&self, u: UnsealedVault) -> Result<SealedVault, failure::Error>;
86    fn decrypt(&self, s: SealedVault) -> Result<UnsealedVault, failure::Error>;
87}
88
89pub fn parse(value: &str) -> Result<SealedVault, failure::Error> {
90    let value_list: Vec<&str> = value.split('.').collect();
91    match value_list.as_slice() {
92        ["CULPER", encryption_format, secret_bytes] => Ok(SealedVault::new(
93            decode(secret_bytes).context("Failed to decode base64 payload")?,
94            EncryptionFormat::from_str(&encryption_format.to_string())?,
95        )),
96        _ => Err(format_err!("Could not parse string into Culper vault.")),
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn can_encrypt() {
106        let nuclear_codes =
107            UnsealedVault::new("zerozerozerozero".to_string(), EncryptionFormat::GPG_KEY);
108        let secret_nuclear_codes = nuclear_codes
109            .seal(&|vault: UnsealedVault| {
110                let secret = vault.plain_secret.chars().map(|c| match c {
111                    'A'...'M' | 'a'...'m' => ((c as u8) + 13),
112                    'N'...'Z' | 'n'...'z' => ((c as u8) - 13),
113                    _ => c as u8,
114                });
115
116                Ok(SealedVault::new(secret.collect(), vault.format))
117            })
118            .unwrap();
119        assert_eq!(
120            "mrebmrebmrebmreb",
121            String::from_utf8(secret_nuclear_codes.secret).unwrap()
122        );
123    }
124}