chacha20blake3 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Cargo.lock +11 -13
- data/ext/chacha20blake3/Cargo.toml +1 -1
- data/ext/chacha20blake3/src/lib.rs +127 -138
- data/lib/chacha20blake3/version.rb +1 -1
- data/tmp/x86_64-linux/stage/ext/chacha20blake3/Cargo.toml +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6bc223f7d13a7c25813924f8de95fd9b98e335a76ed9781fa434a0884d0bfd3a
|
|
4
|
+
data.tar.gz: 43789cf6dbed44f5347a51812d1e181c27d45e91124e59c1c14f24f9c778dfec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b94fd99f4c90238517f12e816d8a34fb620746674aab9f5d942258ac7e3c3769b3fe5b44f3282c6d29bafb24cab7ba10e63525c2769ddd6cd3f658fd058671d
|
|
7
|
+
data.tar.gz: bbeeee226c47688e6398fab4bfc3b17025d2629d3e8e54a9e52f013b751be2be377b8ddd996b730cab208249cf0c94770cf067c92a36e35ecbbf90e54ce8869e
|
data/Cargo.lock
CHANGED
|
@@ -60,7 +60,7 @@ dependencies = [
|
|
|
60
60
|
"arrayvec",
|
|
61
61
|
"cc",
|
|
62
62
|
"cfg-if",
|
|
63
|
-
"constant_time_eq",
|
|
63
|
+
"constant_time_eq 0.4.2",
|
|
64
64
|
"cpufeatures",
|
|
65
65
|
"zeroize",
|
|
66
66
|
]
|
|
@@ -90,22 +90,14 @@ version = "1.0.4"
|
|
|
90
90
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
91
91
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
92
92
|
|
|
93
|
-
[[package]]
|
|
94
|
-
name = "chacha"
|
|
95
|
-
version = "0.1.0"
|
|
96
|
-
source = "git+https://github.com/skerkour/chacha20-blake3?rev=9942bf34c77b03896bb1733079256076ca823e58#9942bf34c77b03896bb1733079256076ca823e58"
|
|
97
|
-
dependencies = [
|
|
98
|
-
"zeroize",
|
|
99
|
-
]
|
|
100
|
-
|
|
101
93
|
[[package]]
|
|
102
94
|
name = "chacha20-blake3"
|
|
103
|
-
version = "0.
|
|
104
|
-
source = "
|
|
95
|
+
version = "0.10.0"
|
|
96
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
97
|
+
checksum = "55d7729707a8889ea911e9c775c5af9eba82e98ed1588e18b0ec7ae3a51c71bd"
|
|
105
98
|
dependencies = [
|
|
106
99
|
"blake3",
|
|
107
|
-
"
|
|
108
|
-
"constant_time_eq",
|
|
100
|
+
"constant_time_eq 0.5.0",
|
|
109
101
|
"zeroize",
|
|
110
102
|
]
|
|
111
103
|
|
|
@@ -137,6 +129,12 @@ version = "0.4.2"
|
|
|
137
129
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
138
130
|
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
|
|
139
131
|
|
|
132
|
+
[[package]]
|
|
133
|
+
name = "constant_time_eq"
|
|
134
|
+
version = "0.5.0"
|
|
135
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
136
|
+
checksum = "1a1ec0cfec728a79a5109075543131387f911cb4d07716436d7ae20533657a96"
|
|
137
|
+
|
|
140
138
|
[[package]]
|
|
141
139
|
name = "cpufeatures"
|
|
142
140
|
version = "0.3.0"
|
|
@@ -8,7 +8,7 @@ name = "chacha20blake3"
|
|
|
8
8
|
crate-type = ["cdylib", "rlib"]
|
|
9
9
|
|
|
10
10
|
[dependencies]
|
|
11
|
-
chacha20-blake3 =
|
|
11
|
+
chacha20-blake3 = "0.10.0"
|
|
12
12
|
blake3 = { version = "1", features = ["std"] }
|
|
13
13
|
magnus = "0.8"
|
|
14
14
|
getrandom = "0.2"
|
|
@@ -13,9 +13,9 @@ use std::sync::{Mutex, OnceLock};
|
|
|
13
13
|
|
|
14
14
|
const KEY_SIZE: usize = 32;
|
|
15
15
|
const NONCE_SIZE: usize = 24;
|
|
16
|
+
const SESSION_NONCE_SIZE: usize = 8;
|
|
16
17
|
const TAG_SIZE: usize = 32;
|
|
17
18
|
|
|
18
|
-
// Opaque<T> is Send+Sync and is designed for storing Ruby values in statics.
|
|
19
19
|
static DECRYPTION_ERROR: OnceLock<Opaque<ExceptionClass>> = OnceLock::new();
|
|
20
20
|
|
|
21
21
|
fn decryption_error(ruby: &Ruby) -> ExceptionClass {
|
|
@@ -29,27 +29,14 @@ struct Cipher(chacha20_blake3::ChaCha20Blake3);
|
|
|
29
29
|
unsafe impl Send for Cipher {}
|
|
30
30
|
unsafe impl Sync for Cipher {}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
struct Stream {
|
|
36
|
-
cipher: chacha20_blake3::ChaCha20Blake3,
|
|
37
|
-
nonce_prefix: [u8; 16],
|
|
38
|
-
counter_base: u64,
|
|
39
|
-
counter: Mutex<u64>,
|
|
32
|
+
#[magnus::wrap(class = "ChaCha20Blake3::Session", free_immediately, size)]
|
|
33
|
+
struct Session {
|
|
34
|
+
inner: Mutex<chacha20_blake3::Session20>,
|
|
40
35
|
}
|
|
41
36
|
|
|
42
|
-
// Safety:
|
|
43
|
-
|
|
44
|
-
unsafe impl
|
|
45
|
-
unsafe impl Sync for Stream {}
|
|
46
|
-
|
|
47
|
-
fn nonce_for_counter(s: &Stream, counter: u64) -> [u8; 24] {
|
|
48
|
-
let mut nonce = [0u8; 24];
|
|
49
|
-
nonce[..16].copy_from_slice(&s.nonce_prefix);
|
|
50
|
-
nonce[16..].copy_from_slice(&s.counter_base.wrapping_add(counter).to_le_bytes());
|
|
51
|
-
nonce
|
|
52
|
-
}
|
|
37
|
+
// Safety: Mutex<Session20> provides Send+Sync via interior synchronization.
|
|
38
|
+
unsafe impl Send for Session {}
|
|
39
|
+
unsafe impl Sync for Session {}
|
|
53
40
|
|
|
54
41
|
fn validate_key(ruby: &Ruby, key: &[u8]) -> Result<[u8; KEY_SIZE], Error> {
|
|
55
42
|
if key.len() != KEY_SIZE {
|
|
@@ -71,6 +58,19 @@ fn validate_nonce(ruby: &Ruby, nonce: &[u8]) -> Result<[u8; NONCE_SIZE], Error>
|
|
|
71
58
|
Ok(nonce.try_into().unwrap())
|
|
72
59
|
}
|
|
73
60
|
|
|
61
|
+
fn validate_session_nonce(ruby: &Ruby, nonce: &[u8]) -> Result<[u8; SESSION_NONCE_SIZE], Error> {
|
|
62
|
+
if nonce.len() != SESSION_NONCE_SIZE {
|
|
63
|
+
return Err(Error::new(
|
|
64
|
+
ruby.exception_arg_error(),
|
|
65
|
+
format!(
|
|
66
|
+
"session nonce must be exactly {SESSION_NONCE_SIZE} bytes, got {}",
|
|
67
|
+
nonce.len()
|
|
68
|
+
),
|
|
69
|
+
));
|
|
70
|
+
}
|
|
71
|
+
Ok(nonce.try_into().unwrap())
|
|
72
|
+
}
|
|
73
|
+
|
|
74
74
|
fn validate_tag(ruby: &Ruby, tag: &[u8]) -> Result<[u8; TAG_SIZE], Error> {
|
|
75
75
|
if tag.len() != TAG_SIZE {
|
|
76
76
|
return Err(Error::new(
|
|
@@ -222,46 +222,41 @@ fn cipher_decrypt_detached(
|
|
|
222
222
|
Ok(output)
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
fn
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
225
|
+
fn session_initialize(
|
|
226
|
+
ruby: &Ruby,
|
|
227
|
+
rb_enc_key: RString,
|
|
228
|
+
rb_auth_key: RString,
|
|
229
|
+
rb_nonce: RString,
|
|
230
|
+
) -> Result<Session, Error> {
|
|
231
|
+
let (enc_key, auth_key, nonce) = unsafe {
|
|
232
|
+
(
|
|
233
|
+
validate_key(ruby, rb_enc_key.as_slice())?,
|
|
234
|
+
validate_key(ruby, rb_auth_key.as_slice())?,
|
|
235
|
+
validate_session_nonce(ruby, rb_nonce.as_slice())?,
|
|
236
|
+
)
|
|
229
237
|
};
|
|
230
|
-
|
|
238
|
+
rb_enc_key.freeze();
|
|
239
|
+
rb_auth_key.freeze();
|
|
231
240
|
rb_nonce.freeze();
|
|
232
|
-
Ok(
|
|
233
|
-
|
|
234
|
-
nonce_prefix: nonce_arr[..16].try_into().unwrap(),
|
|
235
|
-
counter_base: u64::from_le_bytes(nonce_arr[16..].try_into().unwrap()),
|
|
236
|
-
counter: Mutex::new(0),
|
|
241
|
+
Ok(Session {
|
|
242
|
+
inner: Mutex::new(chacha20_blake3::Session20::new(enc_key, auth_key, nonce)),
|
|
237
243
|
})
|
|
238
244
|
}
|
|
239
245
|
|
|
240
|
-
fn
|
|
246
|
+
fn session_encrypt(ruby: &Ruby, rb_self: &Session, args: &[Value]) -> Result<RString, Error> {
|
|
241
247
|
let parsed = scan_args::<(RString,), (), (), (), RHash, ()>(args)?;
|
|
242
248
|
let (rb_plaintext,) = parsed.required;
|
|
243
249
|
let kw = get_kwargs::<_, (), (Option<RString>,), ()>(parsed.keywords, &[], &["aad"])?;
|
|
244
250
|
let (opt_aad,) = kw.optional;
|
|
245
251
|
|
|
246
|
-
|
|
247
|
-
// with the same nonce.
|
|
248
|
-
let mut counter = rb_self.counter.lock().unwrap();
|
|
252
|
+
let mut session = rb_self.inner.lock().unwrap();
|
|
249
253
|
|
|
250
254
|
let (buf, tag) = unsafe {
|
|
251
|
-
let nonce = nonce_for_counter(rb_self, *counter);
|
|
252
255
|
let mut buf = rb_plaintext.as_slice().to_vec();
|
|
253
256
|
let aad = opt_aad.as_ref().map_or_else(Vec::new, |s| s.as_slice().to_vec());
|
|
254
|
-
let tag =
|
|
257
|
+
let tag = session.encrypt_in_place_detached(&mut buf, &aad);
|
|
255
258
|
(buf, tag)
|
|
256
259
|
};
|
|
257
|
-
// Advance counter. Overflow would reuse the initial nonce, which is
|
|
258
|
-
// catastrophic for a stream cipher.
|
|
259
|
-
*counter = counter.checked_add(1).ok_or_else(|| {
|
|
260
|
-
Error::new(
|
|
261
|
-
ruby.exception_runtime_error(),
|
|
262
|
-
"stream counter exhausted (2^64 messages); create a new Stream to continue",
|
|
263
|
-
)
|
|
264
|
-
})?;
|
|
265
260
|
|
|
266
261
|
let output = ruby.str_buf_new(buf.len() + TAG_SIZE);
|
|
267
262
|
output.cat(&buf);
|
|
@@ -269,18 +264,15 @@ fn stream_encrypt(ruby: &Ruby, rb_self: &Stream, args: &[Value]) -> Result<RStri
|
|
|
269
264
|
Ok(output)
|
|
270
265
|
}
|
|
271
266
|
|
|
272
|
-
fn
|
|
267
|
+
fn session_decrypt(ruby: &Ruby, rb_self: &Session, args: &[Value]) -> Result<RString, Error> {
|
|
273
268
|
let parsed = scan_args::<(RString,), (), (), (), RHash, ()>(args)?;
|
|
274
269
|
let (rb_ciphertext,) = parsed.required;
|
|
275
270
|
let kw = get_kwargs::<_, (), (Option<RString>,), ()>(parsed.keywords, &[], &["aad"])?;
|
|
276
271
|
let (opt_aad,) = kw.optional;
|
|
277
272
|
|
|
278
|
-
|
|
279
|
-
// after successful authentication.
|
|
280
|
-
let mut counter = rb_self.counter.lock().unwrap();
|
|
273
|
+
let mut session = rb_self.inner.lock().unwrap();
|
|
281
274
|
|
|
282
275
|
let buf = unsafe {
|
|
283
|
-
let nonce = nonce_for_counter(rb_self, *counter);
|
|
284
276
|
let mut buf = rb_ciphertext.as_slice().to_vec();
|
|
285
277
|
let aad = opt_aad.as_ref().map_or_else(Vec::new, |s| s.as_slice().to_vec());
|
|
286
278
|
if buf.len() < TAG_SIZE {
|
|
@@ -289,25 +281,19 @@ fn stream_decrypt(ruby: &Ruby, rb_self: &Stream, args: &[Value]) -> Result<RStri
|
|
|
289
281
|
let tag_start = buf.len() - TAG_SIZE;
|
|
290
282
|
let tag: [u8; TAG_SIZE] = buf[tag_start..].try_into().unwrap();
|
|
291
283
|
buf.truncate(tag_start);
|
|
292
|
-
|
|
284
|
+
session
|
|
285
|
+
.decrypt_in_place_detached(&mut buf, &tag, &aad)
|
|
293
286
|
.map_err(|_| Error::new(decryption_error(ruby), "decryption failed"))?;
|
|
294
287
|
buf
|
|
295
288
|
};
|
|
296
|
-
// Advance counter only after successful MAC verification.
|
|
297
|
-
*counter = counter.checked_add(1).ok_or_else(|| {
|
|
298
|
-
Error::new(
|
|
299
|
-
ruby.exception_runtime_error(),
|
|
300
|
-
"stream counter exhausted (2^64 messages); create a new Stream to continue",
|
|
301
|
-
)
|
|
302
|
-
})?;
|
|
303
289
|
|
|
304
290
|
let output = ruby.str_buf_new(buf.len());
|
|
305
291
|
output.cat(&buf);
|
|
306
292
|
Ok(output)
|
|
307
293
|
}
|
|
308
294
|
|
|
309
|
-
fn
|
|
310
|
-
|
|
295
|
+
fn session_block_counter(rb_self: &Session) -> u64 {
|
|
296
|
+
rb_self.inner.lock().unwrap().block_counter()
|
|
311
297
|
}
|
|
312
298
|
|
|
313
299
|
fn blake3_derive_key(ruby: &Ruby, args: &[Value]) -> Result<RString, Error> {
|
|
@@ -375,6 +361,7 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
375
361
|
|
|
376
362
|
module.const_set("KEY_SIZE", KEY_SIZE as u64)?;
|
|
377
363
|
module.const_set("NONCE_SIZE", NONCE_SIZE as u64)?;
|
|
364
|
+
module.const_set("SESSION_NONCE_SIZE", SESSION_NONCE_SIZE as u64)?;
|
|
378
365
|
module.const_set("TAG_SIZE", TAG_SIZE as u64)?;
|
|
379
366
|
|
|
380
367
|
let decryption_error_class =
|
|
@@ -390,11 +377,11 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
390
377
|
cipher_class.define_method("encrypt_detached", method!(cipher_encrypt_detached, -1))?;
|
|
391
378
|
cipher_class.define_method("decrypt_detached", method!(cipher_decrypt_detached, -1))?;
|
|
392
379
|
|
|
393
|
-
let
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
380
|
+
let session_class = module.define_class("Session", ruby.class_object())?;
|
|
381
|
+
session_class.define_singleton_method("new", function!(session_initialize, 3))?;
|
|
382
|
+
session_class.define_method("encrypt", method!(session_encrypt, -1))?;
|
|
383
|
+
session_class.define_method("decrypt", method!(session_decrypt, -1))?;
|
|
384
|
+
session_class.define_method("block_counter", method!(session_block_counter, 0))?;
|
|
398
385
|
|
|
399
386
|
module.define_module_function("generate_key", function!(generate_key, 0))?;
|
|
400
387
|
module.define_module_function("generate_nonce", function!(generate_nonce, 0))?;
|
|
@@ -405,108 +392,110 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
405
392
|
|
|
406
393
|
#[cfg(test)]
|
|
407
394
|
mod tests {
|
|
408
|
-
use
|
|
409
|
-
use chacha20_blake3::ChaCha20Blake3;
|
|
410
|
-
|
|
411
|
-
fn make_stream(key: [u8; 32], nonce: [u8; 24]) -> Stream {
|
|
412
|
-
Stream {
|
|
413
|
-
cipher: ChaCha20Blake3::new(key),
|
|
414
|
-
nonce_prefix: nonce[..16].try_into().unwrap(),
|
|
415
|
-
counter_base: u64::from_le_bytes(nonce[16..].try_into().unwrap()),
|
|
416
|
-
counter: Mutex::new(0),
|
|
417
|
-
}
|
|
418
|
-
}
|
|
395
|
+
use chacha20_blake3::{ChaCha20Blake3, Session20};
|
|
419
396
|
|
|
420
397
|
#[test]
|
|
421
|
-
fn
|
|
422
|
-
let
|
|
423
|
-
let
|
|
424
|
-
let
|
|
425
|
-
let
|
|
398
|
+
fn session_multi_message_roundtrip() {
|
|
399
|
+
let enc_key = [0x11u8; 32];
|
|
400
|
+
let auth_key = [0x22u8; 32];
|
|
401
|
+
let nonce = [0x33u8; 8];
|
|
402
|
+
let mut enc = Session20::new(enc_key, auth_key, nonce);
|
|
403
|
+
let mut dec = Session20::new(enc_key, auth_key, nonce);
|
|
426
404
|
|
|
427
405
|
let messages: &[&[u8]] = &[b"alpha", b"beta", b"gamma"];
|
|
428
|
-
let ciphertexts: Vec<Vec<u8>> = messages
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
*counter += 1;
|
|
433
|
-
ct
|
|
434
|
-
}).collect();
|
|
406
|
+
let ciphertexts: Vec<Vec<u8>> = messages
|
|
407
|
+
.iter()
|
|
408
|
+
.map(|m| enc.encrypt(m, b""))
|
|
409
|
+
.collect();
|
|
435
410
|
|
|
436
411
|
for (ct, expected) in ciphertexts.iter().zip(messages.iter()) {
|
|
437
|
-
let
|
|
438
|
-
let n = nonce_for_counter(&dec, *counter);
|
|
439
|
-
let pt = dec.cipher.decrypt(&n, ct, b"").expect("decrypt failed");
|
|
440
|
-
*counter += 1;
|
|
412
|
+
let pt = dec.decrypt(ct, b"").expect("decrypt failed");
|
|
441
413
|
assert_eq!(pt.as_slice(), *expected);
|
|
442
414
|
}
|
|
443
415
|
}
|
|
444
416
|
|
|
445
417
|
#[test]
|
|
446
|
-
fn
|
|
447
|
-
let
|
|
448
|
-
let
|
|
449
|
-
let
|
|
450
|
-
|
|
451
|
-
let mut counter = s.counter.lock().unwrap();
|
|
452
|
-
let n1 = nonce_for_counter(&s, *counter);
|
|
453
|
-
let ct1 = s.cipher.encrypt(&n1, b"repeat", b"");
|
|
454
|
-
*counter += 1;
|
|
418
|
+
fn session_same_message_different_ciphertext() {
|
|
419
|
+
let enc_key = [0x44u8; 32];
|
|
420
|
+
let auth_key = [0x55u8; 32];
|
|
421
|
+
let nonce = [0x66u8; 8];
|
|
422
|
+
let mut session = Session20::new(enc_key, auth_key, nonce);
|
|
455
423
|
|
|
456
|
-
let
|
|
457
|
-
let ct2 =
|
|
424
|
+
let ct1 = session.encrypt(b"repeat", b"");
|
|
425
|
+
let ct2 = session.encrypt(b"repeat", b"");
|
|
458
426
|
|
|
459
427
|
assert_ne!(ct1, ct2);
|
|
460
428
|
}
|
|
461
429
|
|
|
462
430
|
#[test]
|
|
463
|
-
fn
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
let
|
|
467
|
-
let mut
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
431
|
+
fn session_block_counter_advances_by_blocks() {
|
|
432
|
+
let enc_key = [0x77u8; 32];
|
|
433
|
+
let auth_key = [0x88u8; 32];
|
|
434
|
+
let nonce = [0x99u8; 8];
|
|
435
|
+
let mut session = Session20::new(enc_key, auth_key, nonce);
|
|
436
|
+
|
|
437
|
+
assert_eq!(session.block_counter(), 0);
|
|
438
|
+
|
|
439
|
+
// 100 bytes = ceil(100/64) = 2 blocks
|
|
440
|
+
session.encrypt(&[0u8; 100], b"");
|
|
441
|
+
assert_eq!(session.block_counter(), 2);
|
|
442
|
+
|
|
443
|
+
// 64 bytes = exactly 1 block
|
|
444
|
+
session.encrypt(&[0u8; 64], b"");
|
|
445
|
+
assert_eq!(session.block_counter(), 3);
|
|
446
|
+
|
|
447
|
+
// 0 bytes = 0 blocks
|
|
448
|
+
session.encrypt(b"", b"");
|
|
449
|
+
assert_eq!(session.block_counter(), 3);
|
|
475
450
|
}
|
|
476
451
|
|
|
477
452
|
#[test]
|
|
478
|
-
fn
|
|
479
|
-
let
|
|
480
|
-
let
|
|
481
|
-
let
|
|
482
|
-
let
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
let
|
|
486
|
-
let n = nonce_for_counter(&enc, *enc_counter);
|
|
487
|
-
let ct = enc.cipher.encrypt(&n, b"hello", b"");
|
|
488
|
-
*enc_counter = enc_counter.wrapping_add(1);
|
|
489
|
-
drop(enc_counter);
|
|
453
|
+
fn session_failed_decrypt_does_not_advance_counter() {
|
|
454
|
+
let enc_key = [0xAAu8; 32];
|
|
455
|
+
let auth_key = [0xBBu8; 32];
|
|
456
|
+
let nonce = [0xCCu8; 8];
|
|
457
|
+
let mut enc = Session20::new(enc_key, auth_key, nonce);
|
|
458
|
+
let mut dec = Session20::new(enc_key, auth_key, nonce);
|
|
459
|
+
|
|
460
|
+
let ct = enc.encrypt(b"hello", b"");
|
|
490
461
|
|
|
491
462
|
// Tamper with ciphertext
|
|
492
463
|
let mut tampered = ct.clone();
|
|
493
464
|
tampered[0] ^= 0xFF;
|
|
494
465
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// Counter must NOT have advanced
|
|
501
|
-
assert_eq!(*dec_counter, 0);
|
|
502
|
-
|
|
503
|
-
// Original ciphertext should still decrypt at counter 0
|
|
504
|
-
let n = nonce_for_counter(&dec, *dec_counter);
|
|
505
|
-
let pt = dec.cipher.decrypt(&n, &ct, b"").expect("decrypt failed");
|
|
506
|
-
*dec_counter = dec_counter.wrapping_add(1);
|
|
466
|
+
assert!(dec.decrypt(&tampered, b"").is_err());
|
|
467
|
+
assert_eq!(dec.block_counter(), 0);
|
|
468
|
+
|
|
469
|
+
// Original still decrypts
|
|
470
|
+
let pt = dec.decrypt(&ct, b"").expect("decrypt failed");
|
|
507
471
|
assert_eq!(pt.as_slice(), b"hello");
|
|
508
472
|
}
|
|
509
473
|
|
|
474
|
+
#[test]
|
|
475
|
+
fn session_with_aad() {
|
|
476
|
+
let enc_key = [0xDDu8; 32];
|
|
477
|
+
let auth_key = [0xEEu8; 32];
|
|
478
|
+
let nonce = [0xFFu8; 8];
|
|
479
|
+
let mut enc = Session20::new(enc_key, auth_key, nonce);
|
|
480
|
+
let mut dec = Session20::new(enc_key, auth_key, nonce);
|
|
481
|
+
|
|
482
|
+
let ct = enc.encrypt(b"payload", b"header");
|
|
483
|
+
let pt = dec.decrypt(&ct, b"header").expect("decrypt with aad failed");
|
|
484
|
+
assert_eq!(pt.as_slice(), b"payload");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
#[test]
|
|
488
|
+
fn session_wrong_aad_fails() {
|
|
489
|
+
let enc_key = [0x01u8; 32];
|
|
490
|
+
let auth_key = [0x02u8; 32];
|
|
491
|
+
let nonce = [0x03u8; 8];
|
|
492
|
+
let mut enc = Session20::new(enc_key, auth_key, nonce);
|
|
493
|
+
let mut dec = Session20::new(enc_key, auth_key, nonce);
|
|
494
|
+
|
|
495
|
+
let ct = enc.encrypt(b"payload", b"correct");
|
|
496
|
+
assert!(dec.decrypt(&ct, b"wrong").is_err());
|
|
497
|
+
}
|
|
498
|
+
|
|
510
499
|
#[test]
|
|
511
500
|
fn round_trip_encrypt_decrypt() {
|
|
512
501
|
let key = [0x42u8; 32];
|
|
@@ -8,7 +8,7 @@ name = "chacha20blake3"
|
|
|
8
8
|
crate-type = ["cdylib", "rlib"]
|
|
9
9
|
|
|
10
10
|
[dependencies]
|
|
11
|
-
chacha20-blake3 =
|
|
11
|
+
chacha20-blake3 = "0.10.0"
|
|
12
12
|
blake3 = { version = "1", features = ["std"] }
|
|
13
13
|
magnus = "0.8"
|
|
14
14
|
getrandom = "0.2"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: chacha20blake3
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrik Wenger
|
|
@@ -107,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
107
107
|
- !ruby/object:Gem::Version
|
|
108
108
|
version: '0'
|
|
109
109
|
requirements: []
|
|
110
|
-
rubygems_version: 4.0.
|
|
110
|
+
rubygems_version: 4.0.10
|
|
111
111
|
specification_version: 4
|
|
112
112
|
summary: 'Fast DJB-family authenticated encryption: ChaCha20 + BLAKE3 MAC'
|
|
113
113
|
test_files: []
|