prople_crypto/keysecure/
mod.rs

1//! `keysecure` is a module used to save a critical information such as for generated `Private Key`
2//!
3//! The generated private key will be saved following `Ethereum KeyStorage` strategy where the encryption
4//! key will be using a `password`
5use rst_common::standard::serde::{self, Deserialize, Serialize};
6use rst_common::standard::serde_json;
7use rst_common::standard::uuid::Uuid;
8use rst_common::with_cryptography::hex;
9
10pub mod builder;
11pub mod objects;
12pub mod types;
13
14use crate::aead::{Key, KeyEncryption, KeyNonce, MessageCipher, MessagePlain, AEAD};
15use crate::passphrase::kdf_params::KdfParams as PassphraseKDFParams;
16use crate::passphrase::types::SaltBytes;
17use crate::passphrase::Passphrase;
18
19use objects::*;
20use types::{errors::*, ContextOptions};
21
22/// `KeySecure` is a main entrypoint to generate the data, it will depends to
23/// [`KeySecureCrypto`]
24#[derive(Debug, Serialize, Deserialize, Clone)]
25#[serde(crate = "self::serde")]
26pub struct KeySecure {
27    pub id: Uuid,
28    pub context: ContextOptions,
29    pub crypto: KeySecureCrypto,
30}
31
32impl KeySecure {
33    pub fn new(context: ContextOptions, crypto: KeySecureCrypto) -> Self {
34        let id = Uuid::new_v4();
35        Self {
36            id,
37            context,
38            crypto,
39        }
40    }
41
42    pub fn to_json(&self) -> Result<String, CommonError> {
43        serde_json::to_string(self).map_err(|err| CommonError::BuildJSONError(err.to_string()))
44    }
45
46    pub fn decrypt(&self, password: String) -> Result<MessagePlain, KeySecureError> {
47        let encrypted_data = self.crypto.cipher_text.to_owned();
48        let encrypted_data_decoded = hex::decode(encrypted_data)
49            .map_err(|err| KeySecureError::DecryptError(err.to_string()))?;
50
51        let kdf_params = self.crypto.kdf_params.to_owned();
52        let passphrase_kdf_params = PassphraseKDFParams {
53            m_cost: kdf_params.params.m_cost,
54            p_cost: kdf_params.params.p_cost,
55            t_cost: kdf_params.params.t_cost,
56            output_len: kdf_params.params.output_len,
57        };
58
59        let kdf = Passphrase::new(passphrase_kdf_params);
60        let salt_vec = kdf_params.salt.as_bytes().to_vec();
61        let salt_bytes = SaltBytes::from(salt_vec);
62        let password_hash = kdf
63            .hash(password, salt_bytes)
64            .map_err(|err| KeySecureError::DecryptError(err.to_string()))?;
65
66        let nonce_str = self.crypto.cipher_params.nonce.to_owned();
67        let nonce_str_decoded =
68            hex::decode(nonce_str).map_err(|err| KeySecureError::DecryptError(err.to_string()))?;
69
70        let nonce_value: [u8; 24] = nonce_str_decoded
71            .clone()
72            .try_into()
73            .map_err(|_| KeySecureError::DecryptError("unable to decode nonce".to_string()))?;
74
75        let key = Key::new(
76            KeyEncryption::from(password_hash),
77            KeyNonce::from(nonce_value),
78        );
79        let decrypted = AEAD::decrypt(&key, &MessageCipher::from(encrypted_data_decoded))
80            .map_err(|err| KeySecureError::DecryptError(err.to_string()))?;
81
82        Ok(decrypted)
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    use crate::aead::{Key, KeyEncryption, KeyNonce, AEAD};
91    use crate::ecdh::keypair::KeyPair;
92    use crate::passphrase::kdf_params::KdfParams as PassphraseKDFParams;
93    use crate::passphrase::salt::Salt;
94    use crate::passphrase::Passphrase;
95    use crate::types::{BytesValue, VectorValue};
96    use types::ContextOptions;
97
98    use rst_common::with_cryptography::hex;
99
100    fn generate_alice_key() -> [u8; 32] {
101        let keypair_alice = KeyPair::generate();
102        let keypair_bob = KeyPair::generate();
103
104        let pubkey_bob = keypair_bob.pub_key();
105        let public_bob_hex = pubkey_bob.to_hex();
106
107        let secret_alice = keypair_alice.secret(public_bob_hex);
108        let shared_secret_alice_blake3 = secret_alice.to_blake3();
109        let shared_secret_alice_value: &Result<[u8; 32], _> =
110            &shared_secret_alice_blake3.unwrap().bytes()[..32].try_into();
111
112        shared_secret_alice_value.unwrap()
113    }
114
115    fn generate_ecdh_keysecure() -> (KeySecure, [u8; 32]) {
116        let alice_key = generate_alice_key();
117
118        let nonce = AEAD::nonce();
119        let nonce_value: Result<[u8; 24], _> = nonce.vec().clone().try_into();
120
121        let salt = Salt::generate();
122        let salt_string = Salt::from_vec(salt.clone());
123
124        let kdf_params = PassphraseKDFParams::default();
125        let kdf = Passphrase::new(kdf_params.clone());
126
127        let try_hash = kdf.hash(String::from("password"), salt.clone());
128        let key = Key::new(
129            KeyEncryption::from(try_hash.unwrap()),
130            KeyNonce::from(nonce_value.unwrap()),
131        );
132        let encrypted = AEAD::encrypt(&key, &MessagePlain::from(alice_key.to_vec()));
133
134        let encrypted_hex = hex::encode(encrypted.unwrap().vec());
135        let crypto_nonce_hex = hex::encode(nonce.vec().clone());
136        let keysecure_kdf_params = KdfParams::new(kdf_params, salt_string.unwrap());
137        let crypto = KeySecureCrypto::new(crypto_nonce_hex, encrypted_hex, keysecure_kdf_params);
138
139        (KeySecure::new(ContextOptions::X25519, crypto), alice_key)
140    }
141
142    #[test]
143    fn test_generate_json() {
144        let keysecure = generate_ecdh_keysecure();
145        let keysecure_json = keysecure.0.to_json();
146        assert!(!keysecure_json.is_err());
147    }
148
149    #[test]
150    fn test_decrypt_keysecure() {
151        let keysecure = generate_ecdh_keysecure();
152        let alice_key = keysecure.1;
153        let decrypted = keysecure.0.decrypt(String::from("password"));
154        assert!(!decrypted.is_err());
155        assert_eq!(alice_key.to_vec(), decrypted.unwrap().vec())
156    }
157
158    #[test]
159    fn test_decrypt_keysecure_check_bytes() {
160        let keysecure1 = generate_ecdh_keysecure();
161        let keysecure2 = generate_ecdh_keysecure();
162
163        let alice_key1 = keysecure1.1;
164        let alice_key2 = keysecure2.1;
165        assert_ne!(alice_key1.to_vec(), alice_key2.to_vec());
166
167        let decrypted1 = keysecure1.0.decrypt(String::from("password"));
168        assert!(!decrypted1.is_err());
169
170        let decrypted_value1 = decrypted1.unwrap();
171        assert_eq!(alice_key1.to_vec(), decrypted_value1.vec().clone());
172        assert_ne!(alice_key2.to_vec(), decrypted_value1.vec().clone());
173
174        let decrypted2 = keysecure2.0.decrypt(String::from("password"));
175        assert!(!decrypted2.is_err());
176
177        let decrypted_value2 = decrypted2.unwrap();
178        assert_eq!(alice_key2.to_vec(), decrypted_value2.vec().clone());
179        assert_ne!(alice_key1.to_vec(), decrypted_value2.vec().clone());
180    }
181
182    #[test]
183    fn test_decrypt_keysecure_invalid_password() {
184        let keysecure = generate_ecdh_keysecure();
185        let decrypted = keysecure.0.decrypt(String::from("invalid"));
186        assert!(decrypted.is_err());
187    }
188}