4
4
5
5
use std:: io;
6
6
7
+ use argon2:: RECOMMENDED_SALT_LEN ;
7
8
use argon2:: { password_hash:: SaltString , Argon2 } ;
8
9
use chacha20poly1305:: aead:: Aead ;
9
10
use chacha20poly1305:: { Key , KeyInit , XChaCha20Poly1305 , XNonce } ;
11
+ use hkdf:: Hkdf ;
10
12
use rand:: { thread_rng, RngCore } ;
13
+ use sha2:: Sha256 ;
11
14
12
15
use crate :: errors:: { IronfishError , IronfishErrorKind } ;
13
16
14
- const KEY_LENGTH : usize = 32 ;
15
- const NONCE_LENGTH : usize = 24 ;
17
+ pub const KEY_LENGTH : usize = 32 ;
18
+ pub const SALT_LENGTH : usize = RECOMMENDED_SALT_LEN ;
19
+ pub const XNONCE_LENGTH : usize = 24 ;
20
+
21
+ #[ derive( Debug ) ]
22
+ pub struct XChaCha20Poly1305Key {
23
+ pub key : [ u8 ; KEY_LENGTH ] ,
24
+
25
+ pub nonce : [ u8 ; XNONCE_LENGTH ] ,
26
+
27
+ pub salt : [ u8 ; SALT_LENGTH ] ,
28
+ }
29
+
30
+ impl XChaCha20Poly1305Key {
31
+ pub fn generate ( passphrase : & [ u8 ] ) -> Result < XChaCha20Poly1305Key , IronfishError > {
32
+ let mut nonce = [ 0u8 ; XNONCE_LENGTH ] ;
33
+ thread_rng ( ) . fill_bytes ( & mut nonce) ;
34
+
35
+ let mut salt = [ 0u8 ; SALT_LENGTH ] ;
36
+ thread_rng ( ) . fill_bytes ( & mut salt) ;
37
+
38
+ XChaCha20Poly1305Key :: from_parts ( passphrase, salt, nonce)
39
+ }
40
+
41
+ pub fn from_parts (
42
+ passphrase : & [ u8 ] ,
43
+ salt : [ u8 ; SALT_LENGTH ] ,
44
+ nonce : [ u8 ; XNONCE_LENGTH ] ,
45
+ ) -> Result < XChaCha20Poly1305Key , IronfishError > {
46
+ let mut key = [ 0u8 ; KEY_LENGTH ] ;
47
+ let argon2 = Argon2 :: default ( ) ;
48
+
49
+ argon2
50
+ . hash_password_into ( passphrase, & salt, & mut key)
51
+ . map_err ( |_| IronfishError :: new ( IronfishErrorKind :: FailedArgon2Hash ) ) ?;
52
+
53
+ Ok ( XChaCha20Poly1305Key { key, salt, nonce } )
54
+ }
55
+
56
+ pub fn derive_key ( & self , salt : [ u8 ; SALT_LENGTH ] ) -> Result < [ u8 ; KEY_LENGTH ] , IronfishError > {
57
+ let hkdf = Hkdf :: < Sha256 > :: new ( None , & self . key ) ;
58
+
59
+ let mut okm = [ 0u8 ; KEY_LENGTH ] ;
60
+ hkdf. expand ( & salt, & mut okm)
61
+ . map_err ( |_| IronfishError :: new ( IronfishErrorKind :: FailedHkdfExpansion ) ) ?;
62
+
63
+ Ok ( okm)
64
+ }
65
+
66
+ pub fn derive_new_key ( & self ) -> Result < XChaCha20Poly1305Key , IronfishError > {
67
+ let mut nonce = [ 0u8 ; XNONCE_LENGTH ] ;
68
+ thread_rng ( ) . fill_bytes ( & mut nonce) ;
69
+
70
+ let mut salt = [ 0u8 ; SALT_LENGTH ] ;
71
+ thread_rng ( ) . fill_bytes ( & mut salt) ;
72
+
73
+ let hkdf = Hkdf :: < Sha256 > :: new ( None , & self . key ) ;
74
+
75
+ let mut okm = [ 0u8 ; KEY_LENGTH ] ;
76
+ hkdf. expand ( & salt, & mut okm)
77
+ . map_err ( |_| IronfishError :: new ( IronfishErrorKind :: FailedHkdfExpansion ) ) ?;
78
+
79
+ Ok ( XChaCha20Poly1305Key {
80
+ key : okm,
81
+ salt,
82
+ nonce,
83
+ } )
84
+ }
85
+
86
+ pub fn read < R : io:: Read > ( mut reader : R ) -> Result < Self , IronfishError > {
87
+ let mut salt = [ 0u8 ; SALT_LENGTH ] ;
88
+ reader. read_exact ( & mut salt) ?;
89
+
90
+ let mut nonce = [ 0u8 ; XNONCE_LENGTH ] ;
91
+ reader. read_exact ( & mut nonce) ?;
92
+
93
+ let mut key = [ 0u8 ; KEY_LENGTH ] ;
94
+ reader. read_exact ( & mut key) ?;
95
+
96
+ Ok ( XChaCha20Poly1305Key { salt, nonce, key } )
97
+ }
98
+
99
+ pub fn write < W : io:: Write > ( & self , mut writer : W ) -> Result < ( ) , IronfishError > {
100
+ writer. write_all ( & self . salt ) ?;
101
+ writer. write_all ( & self . nonce ) ?;
102
+ writer. write_all ( & self . key ) ?;
103
+
104
+ Ok ( ( ) )
105
+ }
106
+
107
+ pub fn destroy ( & mut self ) {
108
+ self . key . fill ( 0 ) ;
109
+ self . nonce . fill ( 0 ) ;
110
+ self . salt . fill ( 0 ) ;
111
+ }
112
+
113
+ pub fn encrypt ( & self , plaintext : & [ u8 ] ) -> Result < Vec < u8 > , IronfishError > {
114
+ let nonce = XNonce :: from_slice ( & self . nonce ) ;
115
+ let key = Key :: from ( self . key ) ;
116
+ let cipher = XChaCha20Poly1305 :: new ( & key) ;
117
+
118
+ let ciphertext = cipher. encrypt ( nonce, plaintext) . map_err ( |_| {
119
+ IronfishError :: new ( IronfishErrorKind :: FailedXChaCha20Poly1305Encryption )
120
+ } ) ?;
121
+
122
+ Ok ( ciphertext)
123
+ }
124
+
125
+ pub fn decrypt ( & self , ciphertext : Vec < u8 > ) -> Result < Vec < u8 > , IronfishError > {
126
+ let nonce = XNonce :: from_slice ( & self . nonce ) ;
127
+ let key = Key :: from ( self . key ) ;
128
+ let cipher = XChaCha20Poly1305 :: new ( & key) ;
129
+
130
+ cipher
131
+ . decrypt ( nonce, ciphertext. as_ref ( ) )
132
+ . map_err ( |_| IronfishError :: new ( IronfishErrorKind :: FailedXChaCha20Poly1305Decryption ) )
133
+ }
134
+ }
16
135
17
136
#[ derive( Debug ) ]
18
137
pub struct EncryptOutput {
19
138
pub salt : Vec < u8 > ,
20
139
21
- pub nonce : [ u8 ; NONCE_LENGTH ] ,
140
+ pub nonce : [ u8 ; XNONCE_LENGTH ] ,
22
141
23
142
pub ciphertext : Vec < u8 > ,
24
143
}
@@ -46,7 +165,7 @@ impl EncryptOutput {
46
165
let mut salt = vec ! [ 0u8 ; salt_len] ;
47
166
reader. read_exact ( & mut salt) ?;
48
167
49
- let mut nonce = [ 0u8 ; NONCE_LENGTH ] ;
168
+ let mut nonce = [ 0u8 ; XNONCE_LENGTH ] ;
50
169
reader. read_exact ( & mut nonce) ?;
51
170
52
171
let mut ciphertext_len = [ 0u8 ; 4 ] ;
@@ -88,7 +207,7 @@ pub fn encrypt(plaintext: &[u8], passphrase: &[u8]) -> Result<EncryptOutput, Iro
88
207
let key = derive_key ( passphrase, salt_bytes) ?;
89
208
90
209
let cipher = XChaCha20Poly1305 :: new ( & key) ;
91
- let mut nonce_bytes = [ 0u8 ; NONCE_LENGTH ] ;
210
+ let mut nonce_bytes = [ 0u8 ; XNONCE_LENGTH ] ;
92
211
thread_rng ( ) . fill_bytes ( & mut nonce_bytes) ;
93
212
let nonce = XNonce :: from_slice ( & nonce_bytes) ;
94
213
@@ -119,19 +238,22 @@ pub fn decrypt(
119
238
120
239
#[ cfg( test) ]
121
240
mod test {
122
- use crate :: xchacha20poly1305:: { decrypt, encrypt} ;
123
-
124
- use super :: EncryptOutput ;
241
+ use crate :: xchacha20poly1305:: XChaCha20Poly1305Key ;
125
242
126
243
#[ test]
127
244
fn test_valid_passphrase ( ) {
128
245
let plaintext = "thisissensitivedata" ;
129
246
let passphrase = "supersecretpassword" ;
130
247
131
- let encrypted_output = encrypt ( plaintext. as_bytes ( ) , passphrase. as_bytes ( ) )
248
+ let key =
249
+ XChaCha20Poly1305Key :: generate ( passphrase. as_bytes ( ) ) . expect ( "should generate key" ) ;
250
+
251
+ let encrypted_output = key
252
+ . encrypt ( plaintext. as_bytes ( ) )
132
253
. expect ( "should successfully encrypt" ) ;
133
- let decrypted =
134
- decrypt ( encrypted_output, passphrase. as_bytes ( ) ) . expect ( "should decrypt successfully" ) ;
254
+ let decrypted = key
255
+ . decrypt ( encrypted_output)
256
+ . expect ( "should decrypt successfully" ) ;
135
257
136
258
assert_eq ! ( decrypted, plaintext. as_bytes( ) ) ;
137
259
}
@@ -142,28 +264,33 @@ mod test {
142
264
let passphrase = "supersecretpassword" ;
143
265
let incorrect_passphrase = "foobar" ;
144
266
145
- let encrypted_output = encrypt ( plaintext. as_bytes ( ) , passphrase. as_bytes ( ) )
267
+ let key =
268
+ XChaCha20Poly1305Key :: generate ( passphrase. as_bytes ( ) ) . expect ( "should generate key" ) ;
269
+
270
+ let encrypted_output = key
271
+ . encrypt ( plaintext. as_bytes ( ) )
146
272
. expect ( "should successfully encrypt" ) ;
147
273
148
- decrypt ( encrypted_output, incorrect_passphrase. as_bytes ( ) )
274
+ let incorrect_key = XChaCha20Poly1305Key :: generate ( incorrect_passphrase. as_bytes ( ) )
275
+ . expect ( "should generate key" ) ;
276
+
277
+ incorrect_key
278
+ . decrypt ( encrypted_output)
149
279
. expect_err ( "should fail decryption" ) ;
150
280
}
151
281
152
282
#[ test]
153
- fn test_encrypt_output_serialization ( ) {
154
- let plaintext = "thisissensitivedata" ;
283
+ fn test_derive_key ( ) {
155
284
let passphrase = "supersecretpassword" ;
156
285
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" ) ;
286
+ let encryption_key = XChaCha20Poly1305Key :: generate ( passphrase. as_bytes ( ) )
287
+ . expect ( "should successfully generate key" ) ;
164
288
165
- let deserialized = EncryptOutput :: read ( & vec[ ..] ) . expect ( "should deserialize successfully" ) ;
289
+ let key = encryption_key. derive_new_key ( ) . expect ( "should derive key" ) ;
290
+ let derived_key = encryption_key
291
+ . derive_key ( key. salt )
292
+ . expect ( "should derive key" ) ;
166
293
167
- assert_eq ! ( encrypted_output , deserialized ) ;
294
+ assert_eq ! ( key . key , derived_key ) ;
168
295
}
169
296
}
0 commit comments