sos_core/crypto/
private_key.rs

1//! Private key types.
2use age::x25519::Identity;
3use argon2::password_hash::SaltString;
4use secrecy::{ExposeSecret, SecretBox, SecretString};
5use std::convert::AsRef;
6use std::fmt;
7
8use crate::{
9    crypto::{KeyDerivation, Seed},
10    Result,
11};
12
13/// Access key used to unlock a vault.
14#[derive(Clone)]
15pub enum AccessKey {
16    /// Password access.
17    Password(SecretString),
18    /// Asymmetric private key.
19    Identity(Identity),
20}
21
22impl From<SecretString> for AccessKey {
23    fn from(value: SecretString) -> Self {
24        Self::Password(value)
25    }
26}
27
28impl From<Identity> for AccessKey {
29    fn from(value: Identity) -> Self {
30        Self::Identity(value)
31    }
32}
33
34impl From<AccessKey> for SecretString {
35    fn from(value: AccessKey) -> Self {
36        match value {
37            AccessKey::Password(password) => password,
38            AccessKey::Identity(id) => id.to_string().into(),
39        }
40    }
41}
42
43impl AccessKey {
44    /// Convert this access key into a private key.
45    pub fn into_private(
46        self,
47        kdf: &KeyDerivation,
48        salt: &SaltString,
49        seed: Option<&Seed>,
50    ) -> Result<PrivateKey> {
51        match self {
52            Self::Password(ref password) => {
53                let deriver = kdf.deriver();
54                Ok(PrivateKey::Symmetric(
55                    deriver.derive(password, salt, seed)?,
56                ))
57            }
58            Self::Identity(id) => Ok(PrivateKey::Asymmetric(id)),
59        }
60    }
61}
62
63impl fmt::Debug for AccessKey {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        match self {
66            Self::Password(_) => f.debug_struct("Password").finish(),
67            Self::Identity(_) => f.debug_struct("Identity").finish(),
68        }
69    }
70}
71
72impl PartialEq for AccessKey {
73    fn eq(&self, other: &Self) -> bool {
74        match (self, other) {
75            (Self::Password(a), Self::Password(b)) => {
76                a.expose_secret() == b.expose_secret()
77            }
78            (Self::Identity(a), Self::Identity(b)) => {
79                a.to_string().expose_secret() == b.to_string().expose_secret()
80            }
81            _ => false,
82        }
83    }
84}
85
86impl Eq for AccessKey {}
87
88/// Private key variants.
89pub enum PrivateKey {
90    /// Private key used for symmetric ciphers.
91    Symmetric(DerivedPrivateKey),
92    /// Private key used for asymmetric ciphers.
93    Asymmetric(Identity),
94}
95
96/// Encapsulates the bytes for a derived symmetric secret key.
97pub struct DerivedPrivateKey {
98    inner: SecretBox<Vec<u8>>,
99}
100
101impl DerivedPrivateKey {
102    /// Create a new random 32-byte secret key.
103    pub fn generate() -> Self {
104        use crate::crypto::csprng;
105        use rand::Rng;
106        let bytes: [u8; 32] = csprng().gen();
107        Self {
108            inner: SecretBox::new(Box::new(bytes.to_vec())),
109        }
110    }
111
112    /// Convert from a PEM-encoded key.
113    pub fn from_pem(key: &str) -> Result<Self> {
114        let pem = pem::parse(key)?;
115        let contents = pem.contents();
116        Ok(Self {
117            inner: SecretBox::new(Box::new(contents.to_vec())),
118        })
119    }
120
121    /// Convert this key to a PEM-encoded string.
122    pub fn to_pem(&self) -> String {
123        pem::encode(&pem::Pem::new(
124            "PRIVATE KEY",
125            self.inner.expose_secret().as_slice(),
126        ))
127    }
128
129    /// Create a new derived private key.
130    pub(crate) fn new(inner: SecretBox<Vec<u8>>) -> Self {
131        Self { inner }
132    }
133}
134
135impl AsRef<[u8]> for DerivedPrivateKey {
136    fn as_ref(&self) -> &[u8] {
137        self.inner.expose_secret()
138    }
139}
140
141impl From<Vec<u8>> for DerivedPrivateKey {
142    fn from(value: Vec<u8>) -> Self {
143        Self {
144            inner: SecretBox::new(value.into()),
145        }
146    }
147}