2
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
4
5
+ use std:: io;
6
+
5
7
use argon2:: { password_hash:: SaltString , Argon2 } ;
6
8
use chacha20poly1305:: aead:: Aead ;
7
9
use chacha20poly1305:: { Key , KeyInit , XChaCha20Poly1305 , XNonce } ;
@@ -12,14 +14,62 @@ use crate::errors::{IronfishError, IronfishErrorKind};
12
14
const KEY_LENGTH : usize = 32 ;
13
15
const NONCE_LENGTH : usize = 24 ;
14
16
17
+ #[ derive( Debug ) ]
15
18
pub struct EncryptOutput {
16
- pub salt : SaltString ,
19
+ pub salt : Vec < u8 > ,
17
20
18
21
pub nonce : [ u8 ; NONCE_LENGTH ] ,
19
22
20
23
pub ciphertext : Vec < u8 > ,
21
24
}
22
25
26
+ impl EncryptOutput {
27
+ pub fn write < W : io:: Write > ( & self , mut writer : W ) -> Result < ( ) , IronfishError > {
28
+ let salt_len = u32:: try_from ( self . salt . len ( ) ) ?. to_le_bytes ( ) ;
29
+ writer. write_all ( & salt_len) ?;
30
+ writer. write_all ( & self . salt ) ?;
31
+
32
+ writer. write_all ( & self . nonce ) ?;
33
+
34
+ let ciphertext_len = u32:: try_from ( self . ciphertext . len ( ) ) ?. to_le_bytes ( ) ;
35
+ writer. write_all ( & ciphertext_len) ?;
36
+ writer. write_all ( & self . ciphertext ) ?;
37
+
38
+ Ok ( ( ) )
39
+ }
40
+
41
+ pub fn read < R : io:: Read > ( mut reader : R ) -> Result < Self , IronfishError > {
42
+ let mut salt_len = [ 0u8 ; 4 ] ;
43
+ reader. read_exact ( & mut salt_len) ?;
44
+ let salt_len = u32:: from_le_bytes ( salt_len) as usize ;
45
+
46
+ let mut salt = vec ! [ 0u8 ; salt_len] ;
47
+ reader. read_exact ( & mut salt) ?;
48
+
49
+ let mut nonce = [ 0u8 ; NONCE_LENGTH ] ;
50
+ reader. read_exact ( & mut nonce) ?;
51
+
52
+ let mut ciphertext_len = [ 0u8 ; 4 ] ;
53
+ reader. read_exact ( & mut ciphertext_len) ?;
54
+ let ciphertext_len = u32:: from_le_bytes ( ciphertext_len) as usize ;
55
+
56
+ let mut ciphertext = vec ! [ 0u8 ; ciphertext_len] ;
57
+ reader. read_exact ( & mut ciphertext) ?;
58
+
59
+ Ok ( EncryptOutput {
60
+ salt,
61
+ nonce,
62
+ ciphertext,
63
+ } )
64
+ }
65
+ }
66
+
67
+ impl PartialEq for EncryptOutput {
68
+ fn eq ( & self , other : & EncryptOutput ) -> bool {
69
+ self . salt == other. salt && self . nonce == other. nonce && self . ciphertext == other. ciphertext
70
+ }
71
+ }
72
+
23
73
fn derive_key ( passphrase : & [ u8 ] , salt : & [ u8 ] ) -> Result < Key , IronfishError > {
24
74
let mut key = [ 0u8 ; KEY_LENGTH ] ;
25
75
let argon2 = Argon2 :: default ( ) ;
@@ -33,7 +83,9 @@ fn derive_key(passphrase: &[u8], salt: &[u8]) -> Result<Key, IronfishError> {
33
83
34
84
pub fn encrypt ( plaintext : & [ u8 ] , passphrase : & [ u8 ] ) -> Result < EncryptOutput , IronfishError > {
35
85
let salt = SaltString :: generate ( & mut thread_rng ( ) ) ;
36
- let key = derive_key ( passphrase, salt. to_string ( ) . as_bytes ( ) ) ?;
86
+ let salt_str = salt. to_string ( ) ;
87
+ let salt_bytes = salt_str. as_bytes ( ) ;
88
+ let key = derive_key ( passphrase, salt_bytes) ?;
37
89
38
90
let cipher = XChaCha20Poly1305 :: new ( & key) ;
39
91
let mut nonce_bytes = [ 0u8 ; NONCE_LENGTH ] ;
@@ -45,7 +97,7 @@ pub fn encrypt(plaintext: &[u8], passphrase: &[u8]) -> Result<EncryptOutput, Iro
45
97
. map_err ( |_| IronfishError :: new ( IronfishErrorKind :: FailedXChaCha20Poly1305Encryption ) ) ?;
46
98
47
99
Ok ( EncryptOutput {
48
- salt,
100
+ salt : salt_bytes . to_vec ( ) ,
49
101
nonce : nonce_bytes,
50
102
ciphertext,
51
103
} )
@@ -57,7 +109,7 @@ pub fn decrypt(
57
109
) -> Result < Vec < u8 > , IronfishError > {
58
110
let nonce = XNonce :: from_slice ( & encrypted_output. nonce ) ;
59
111
60
- let key = derive_key ( passphrase, encrypted_output. salt . to_string ( ) . as_bytes ( ) ) ?;
112
+ let key = derive_key ( passphrase, & encrypted_output. salt [ .. ] ) ?;
61
113
let cipher = XChaCha20Poly1305 :: new ( & key) ;
62
114
63
115
cipher
@@ -69,6 +121,8 @@ pub fn decrypt(
69
121
mod test {
70
122
use crate :: xchacha20poly1305:: { decrypt, encrypt} ;
71
123
124
+ use super :: EncryptOutput ;
125
+
72
126
#[ test]
73
127
fn test_valid_passphrase ( ) {
74
128
let plaintext = "thisissensitivedata" ;
@@ -94,4 +148,22 @@ mod test {
94
148
decrypt ( encrypted_output, incorrect_passphrase. as_bytes ( ) )
95
149
. expect_err ( "should fail decryption" ) ;
96
150
}
151
+
152
+ #[ test]
153
+ fn test_encrypt_output_serialization ( ) {
154
+ let plaintext = "thisissensitivedata" ;
155
+ let passphrase = "supersecretpassword" ;
156
+
157
+ let encrypted_output = encrypt ( plaintext. as_bytes ( ) , passphrase. as_bytes ( ) )
158
+ . expect ( "should successfully encrypt" ) ;
159
+
160
+ let mut vec: Vec < u8 > = vec ! [ ] ;
161
+ encrypted_output
162
+ . write ( & mut vec)
163
+ . expect ( "should serialize successfully" ) ;
164
+
165
+ let deserialized = EncryptOutput :: read ( & vec[ ..] ) . expect ( "should deserialize successfully" ) ;
166
+
167
+ assert_eq ! ( encrypted_output, deserialized) ;
168
+ }
97
169
}
0 commit comments