@@ -14,9 +14,13 @@ static CIPHER: LazyLock<Aes256Gcm> = LazyLock::new(|| {
1414} ) ;
1515
1616/// Represents encrypted data with the encrypted string and the nonce used for encryption.
17- pub ( crate ) struct Encrypted {
17+ #[ derive( Clone ) ]
18+ pub ( crate ) struct EncryptedString {
19+ /// The cached decrypted data as a UTF-8 string.
20+ /// This field is only set if exposed using the `expose` method.
21+ decrypted : Option < String > ,
1822 /// The encrypted data as a base64 encoded string.
19- pub ( crate ) data : String ,
23+ pub ( crate ) encrypted : String ,
2024 /// The nonce used for encryption as a base64 encoded string.
2125 pub ( crate ) nonce : String ,
2226}
@@ -49,64 +53,106 @@ fn get_encryption_key() -> Key<Aes256Gcm> {
4953 * Key :: < Aes256Gcm > :: from_slice ( key. as_bytes ( ) )
5054}
5155
52- /// Encrypts the given data using AES-256-GCM.
53- ///
54- /// This function encrypts the provided data using the AES-256-GCM algorithm and a randomly generated nonce.
55- /// The encrypted data and nonce are returned as base64 encoded strings.
56- ///
57- /// # Arguments
58- ///
59- /// * `data` - A byte slice of the data to be encrypted.
60- ///
61- /// # Returns
62- ///
63- /// A [`Result`] containing an [`Encrypted`] struct on success, or a [`String`] error message on failure.
64- pub ( crate ) fn encrypt ( data : & [ u8 ] ) -> Result < Encrypted , String > {
65- let nonce = Aes256Gcm :: generate_nonce ( & mut OsRng ) ;
66- let encrypted_bytes = CIPHER
67- . encrypt ( & nonce, data)
68- . map_err ( |e| format ! ( "Failed to encrypt data: {}" , e) ) ?;
69-
70- Ok ( Encrypted {
71- data : base64:: encode ( & encrypted_bytes) ,
72- nonce : base64:: encode ( nonce. as_slice ( ) ) ,
73- } )
56+ impl EncryptedString {
57+ /// Creates a new [`EncryptedString`] struct with the provided data and nonce.
58+ pub ( crate ) fn new < S > ( data : S , nonce : S ) -> Self
59+ where
60+ S : Into < String > ,
61+ {
62+ EncryptedString {
63+ encrypted : data. into ( ) ,
64+ nonce : nonce. into ( ) ,
65+ decrypted : None ,
66+ }
67+ }
68+
69+ /// Encrypts the given data using AES-256-GCM.
70+ ///
71+ /// This function encrypts the provided data using the AES-256-GCM algorithm and a randomly generated nonce.
72+ /// The encrypted data and nonce are returned as base64 encoded strings.
73+ ///
74+ /// # Arguments
75+ ///
76+ /// * `data` - A byte slice of the data to be encrypted.
77+ ///
78+ /// # Returns
79+ ///
80+ /// A [`Result`] containing an [`EncryptedString`] struct on success, or a [`String`] error message on failure.
81+ pub ( crate ) fn encrypt ( data : & [ u8 ] ) -> Result < Self , String > {
82+ let nonce = Aes256Gcm :: generate_nonce ( & mut OsRng ) ;
83+ let encrypted_bytes = CIPHER
84+ . encrypt ( & nonce, data)
85+ . map_err ( |e| format ! ( "Failed to encrypt data: {}" , e) ) ?;
86+
87+ Ok ( EncryptedString :: new (
88+ base64:: encode ( & encrypted_bytes) ,
89+ base64:: encode ( nonce. as_slice ( ) ) ,
90+ ) )
91+ }
92+
93+ /// Decrypts the given encrypted data using AES-256-GCM.
94+ ///
95+ /// This function decrypts the provided [`EncryptedString`] using the AES-256-GCM algorithm and the nonce.
96+ /// The decrypted data is returned as a UTF-8 string.
97+ ///
98+ /// # Arguments
99+ ///
100+ /// * `data` - A reference to the [`EncryptedString`] struct containing the encrypted data and nonce.
101+ ///
102+ /// # Returns
103+ ///
104+ /// A [`Result`] containing the decrypted data as a [`String`] on success, or a [`String`] error message on failure.
105+ fn decrypt ( & self ) -> Result < String , String > {
106+ // Decode the base64 encoded data and nonce
107+ let encrypted_bytes = base64:: decode ( & self . encrypted )
108+ . map_err ( |e| format ! ( "Failed to decode base64 data: {}" , e) ) ?;
109+ let nonce = base64:: decode ( & self . nonce )
110+ . map_err ( |e| format ! ( "Failed to decode base64 nonce: {}" , e) ) ?;
111+ let nonce = Nonce :: < Aes256Gcm > :: from_slice ( nonce. as_slice ( ) ) ;
112+
113+ // Decrypt the data and convert it to a UTF-8 string
114+ let decrypted_bytes = CIPHER
115+ . decrypt ( nonce, encrypted_bytes. as_slice ( ) )
116+ . map_err ( |e| format ! ( "Failed to decrypt data: {}" , e) ) ?;
117+ let decrypted_data = String :: from_utf8 ( decrypted_bytes)
118+ . map_err ( |e| format ! ( "Failed to convert decrypted data to UTF-8: {}" , e) ) ?;
119+
120+ Ok ( decrypted_data)
121+ }
122+
123+ /// Exposes the decrypted data to the provided function.
124+ /// This method should only be used if the program intends to use the decrypted data **once**.
125+ pub ( crate ) fn expose_once < T > ( & self , f : impl FnOnce ( & str ) -> T ) -> Result < T , String > {
126+ let decrypted_data = self . decrypt ( ) ?;
127+ Ok ( f ( & decrypted_data) )
128+ }
129+
130+ /// Exposes the decrypted data to the provided function.
131+ /// This method should only be used if the program intends to use the decrypted data **multiple times**.
132+ pub ( crate ) fn expose < T > ( & mut self , f : impl FnOnce ( & str ) -> T ) -> Result < T , String > {
133+ if self . decrypted . is_none ( ) {
134+ let decrypted_data = self . decrypt ( ) ?;
135+ self . decrypted = Some ( decrypted_data) ;
136+ }
137+
138+ Ok ( f ( self . decrypted . as_ref ( ) . unwrap ( ) ) )
139+ }
74140}
75141
76- /// Decrypts the given encrypted data using AES-256-GCM.
77- ///
78- /// This function decrypts the provided [`Encrypted`] using the AES-256-GCM algorithm and the nonce.
79- /// The decrypted data is returned as a UTF-8 string.
80- ///
81- /// # Arguments
82- ///
83- /// * `data` - A reference to the [`Encrypted`] struct containing the encrypted data and nonce.
84- ///
85- /// # Returns
86- ///
87- /// A [`Result`] containing the decrypted data as a [`String`] on success, or a [`String`] error message on failure.
88- pub ( crate ) fn decrypt ( data : & Encrypted ) -> Result < String , String > {
89- // Decode the base64 encoded data and nonce
90- let encrypted_bytes =
91- base64:: decode ( & data. data ) . map_err ( |e| format ! ( "Failed to decode base64 data: {}" , e) ) ?;
92- let nonce =
93- base64:: decode ( & data. nonce ) . map_err ( |e| format ! ( "Failed to decode base64 nonce: {}" , e) ) ?;
94- let nonce = Nonce :: < Aes256Gcm > :: from_slice ( nonce. as_slice ( ) ) ;
95-
96- // Decrypt the data and convert it to a UTF-8 string
97- let decrypted_bytes = CIPHER
98- . decrypt ( nonce, encrypted_bytes. as_slice ( ) )
99- . map_err ( |e| format ! ( "Failed to decrypt data: {}" , e) ) ?;
100- let decrypted_data = std:: str:: from_utf8 ( & decrypted_bytes)
101- . map_err ( |e| format ! ( "Failed to convert decrypted data to UTF-8: {}" , e) ) ?;
102-
103- Ok ( decrypted_data. to_string ( ) )
142+ impl < ' de > rocket:: serde:: Deserialize < ' de > for EncryptedString {
143+ fn deserialize < D > ( deserializer : D ) -> Result < EncryptedString , D :: Error >
144+ where
145+ D : rocket:: serde:: Deserializer < ' de > ,
146+ {
147+ let b = <& [ u8 ] >:: deserialize ( deserializer) ?;
148+ Self :: encrypt ( b) . map_err ( rocket:: serde:: de:: Error :: custom)
149+ }
104150}
105151
106152#[ cfg( test) ]
107153#[ serial_test:: file_serial( env) ]
108154mod tests {
109- use super :: { decrypt , encrypt , get_encryption_key, Encrypted } ;
155+ use super :: { get_encryption_key, EncryptedString } ;
110156 use crate :: constants;
111157
112158 const DATA : & [ u8 ] = b"Hello, world!" ;
@@ -158,10 +204,10 @@ mod tests {
158204 constants:: env:: ENCRYPTION_KEY ,
159205 constants:: test:: ENCRYPTION_KEY ,
160206 ) ;
161- let encrypted_data = encrypt ( DATA ) . unwrap ( ) ;
162- let decrypted_data = decrypt ( & encrypted_data) . unwrap ( ) ;
163207
164- assert_eq ! ( decrypted_data. as_bytes( ) , DATA ) ;
208+ let data = EncryptedString :: encrypt ( DATA ) . unwrap ( ) . decrypt ( ) . unwrap ( ) ;
209+ assert_eq ! ( data. as_bytes( ) , DATA ) ;
210+
165211 std:: env:: remove_var ( constants:: env:: ENCRYPTION_KEY ) ;
166212 }
167213
@@ -171,13 +217,10 @@ mod tests {
171217 constants:: env:: ENCRYPTION_KEY ,
172218 constants:: test:: ENCRYPTION_KEY ,
173219 ) ;
174- let encrypted_data = Encrypted {
175- data : "valid+data" . to_string ( ) ,
176- nonce : "valid+nonce" . to_string ( ) ,
177- } ;
178- let result = decrypt ( & encrypted_data) ;
179220
221+ let result = EncryptedString :: new ( "valid+data" , "valid+nonce" ) . decrypt ( ) ;
180222 assert ! ( result. is_err( ) ) ;
223+
181224 std:: env:: remove_var ( constants:: env:: ENCRYPTION_KEY ) ;
182225 }
183226
@@ -188,11 +231,40 @@ mod tests {
188231 constants:: test:: ENCRYPTION_KEY ,
189232 ) ;
190233
191- let mut encrypted_data = encrypt ( DATA ) . unwrap ( ) ;
192- encrypted_data. nonce = "@" . to_string ( ) ;
193- let result = decrypt ( & encrypted_data) ;
194-
234+ let result = EncryptedString :: new ( "valid" , "@" ) . decrypt ( ) ;
195235 assert ! ( result. is_err( ) ) ;
236+
237+ std:: env:: remove_var ( constants:: env:: ENCRYPTION_KEY ) ;
238+ }
239+
240+ #[ test]
241+ fn expose_once ( ) {
242+ std:: env:: set_var (
243+ constants:: env:: ENCRYPTION_KEY ,
244+ constants:: test:: ENCRYPTION_KEY ,
245+ ) ;
246+
247+ let encrypted_data = EncryptedString :: encrypt ( DATA ) . unwrap ( ) ;
248+ let decrypted_data = encrypted_data. expose_once ( |data| data. to_owned ( ) ) . unwrap ( ) ;
249+ assert_eq ! ( decrypted_data. as_bytes( ) , DATA ) ;
250+
251+ std:: env:: remove_var ( constants:: env:: ENCRYPTION_KEY ) ;
252+ }
253+
254+ #[ test]
255+ fn expose ( ) {
256+ std:: env:: set_var (
257+ constants:: env:: ENCRYPTION_KEY ,
258+ constants:: test:: ENCRYPTION_KEY ,
259+ ) ;
260+
261+ let mut encrypted_data = EncryptedString :: encrypt ( DATA ) . unwrap ( ) ;
262+ assert ! ( encrypted_data. decrypted. is_none( ) ) ;
263+
264+ let decrypted_data = encrypted_data. expose ( |data| data. to_owned ( ) ) . unwrap ( ) ;
265+ assert_eq ! ( decrypted_data. as_bytes( ) , DATA ) ;
266+ assert_eq ! ( encrypted_data. decrypted. unwrap( ) . as_bytes( ) , DATA ) ;
267+
196268 std:: env:: remove_var ( constants:: env:: ENCRYPTION_KEY ) ;
197269 }
198270}
0 commit comments