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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d875e3dca3b733cb6a6cac6f3c00539e5ea670e4128a5b2107da9c99cee63e9
4
- data.tar.gz: 9c1b971c5f768b1ac59d0195e6c979f8979571eebebac242e1cd4a516b250e44
3
+ metadata.gz: 6bc223f7d13a7c25813924f8de95fd9b98e335a76ed9781fa434a0884d0bfd3a
4
+ data.tar.gz: 43789cf6dbed44f5347a51812d1e181c27d45e91124e59c1c14f24f9c778dfec
5
5
  SHA512:
6
- metadata.gz: 9a140c81d80fb12ce9176a5fee9e2b828d9f32ff8ba27033ea7dd593d4a8584dc94373302ded86f2d1c9d20a5b5af8a5c789ed5327174fb9ad88aac46c6988d4
7
- data.tar.gz: 01c1369aaea777a152ce78f1c0f14cb6b214faadf2e5dffdf38a68ece66922b92bfd087385f37b0be3cf3e4fb3055176b47602a009f440fe1f4d348f9c564fa1
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.9.11"
104
- source = "git+https://github.com/skerkour/chacha20-blake3?rev=9942bf34c77b03896bb1733079256076ca823e58#9942bf34c77b03896bb1733079256076ca823e58"
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
- "chacha",
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 = { git = "https://github.com/skerkour/chacha20-blake3", rev = "9942bf34c77b03896bb1733079256076ca823e58" }
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
- // No #reset or #rewind method by design: allowing the counter to go backwards
33
- // would silently reuse (key, nonce) pairs, which is catastrophic for a stream cipher.
34
- #[magnus::wrap(class = "ChaCha20Blake3::Stream", free_immediately, size)]
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: all fields are Send. The Mutex<u64> provides interior mutability with
43
- // synchronization, making Stream both Send and Sync (safe for Ractors).
44
- unsafe impl Send for Stream {}
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 stream_initialize(ruby: &Ruby, rb_key: RString, rb_nonce: RString) -> Result<Stream, Error> {
226
- let (key_arr, nonce_arr) = unsafe {
227
- (validate_key(ruby, rb_key.as_slice())?,
228
- validate_nonce(ruby, rb_nonce.as_slice())?)
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
- rb_key.freeze();
238
+ rb_enc_key.freeze();
239
+ rb_auth_key.freeze();
231
240
  rb_nonce.freeze();
232
- Ok(Stream {
233
- cipher: chacha20_blake3::ChaCha20Blake3::new(key_arr),
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 stream_encrypt(ruby: &Ruby, rb_self: &Stream, args: &[Value]) -> Result<RString, Error> {
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
- // Hold the lock for the entire operation so no two threads can encrypt
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 = rb_self.cipher.encrypt_in_place_detached(&nonce, &mut buf, &aad);
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 stream_decrypt(ruby: &Ruby, rb_self: &Stream, args: &[Value]) -> Result<RString, Error> {
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
- // Hold the lock for the entire operation so the counter only advances
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
- rb_self.cipher.decrypt_in_place_detached(&nonce, &mut buf, &tag, &aad)
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 stream_message_index(rb_self: &Stream) -> u64 {
310
- *rb_self.counter.lock().unwrap()
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 stream_class = module.define_class("Stream", ruby.class_object())?;
394
- stream_class.define_singleton_method("new", function!(stream_initialize, 2))?;
395
- stream_class.define_method("encrypt", method!(stream_encrypt, -1))?;
396
- stream_class.define_method("decrypt", method!(stream_decrypt, -1))?;
397
- stream_class.define_method("message_index", method!(stream_message_index, 0))?;
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 super::{nonce_for_counter, Mutex, Stream};
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 stream_multi_message_roundtrip() {
422
- let key = [0x11u8; 32];
423
- let nonce = [0x22u8; 24];
424
- let enc = make_stream(key, nonce);
425
- let dec = make_stream(key, nonce);
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.iter().map(|m| {
429
- let mut counter = enc.counter.lock().unwrap();
430
- let n = nonce_for_counter(&enc, *counter);
431
- let ct = enc.cipher.encrypt(&n, m, b"");
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 mut counter = dec.counter.lock().unwrap();
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 stream_same_message_different_ciphertext() {
447
- let key = [0x33u8; 32];
448
- let nonce = [0x44u8; 24];
449
- let s = make_stream(key, nonce);
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 n2 = nonce_for_counter(&s, *counter);
457
- let ct2 = s.cipher.encrypt(&n2, b"repeat", b"");
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 stream_nonce_suffix_wraps() {
464
- // When counter_base is near u64::MAX, the nonce suffix (counter_base + counter)
465
- // wraps around. This is fine - it's the counter *index* that must never wrap.
466
- let key = [0x55u8; 32];
467
- let mut nonce = [0u8; 24];
468
- nonce[16..].copy_from_slice(&u64::MAX.to_le_bytes());
469
-
470
- let s = make_stream(key, nonce);
471
- *s.counter.lock().unwrap() = 1; // counter_base(MAX) + 1 wraps nonce suffix to 0
472
-
473
- let n = nonce_for_counter(&s, *s.counter.lock().unwrap());
474
- assert_eq!(&n[16..], &0u64.to_le_bytes());
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 stream_failed_decrypt_does_not_advance_counter() {
479
- let key = [0x66u8; 32];
480
- let nonce = [0x77u8; 24];
481
- let enc = make_stream(key, nonce);
482
- let dec = make_stream(key, nonce);
483
-
484
- // Encrypt a message
485
- let mut enc_counter = enc.counter.lock().unwrap();
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
- // Decrypt should fail
496
- let mut dec_counter = dec.counter.lock().unwrap();
497
- let n = nonce_for_counter(&dec, *dec_counter);
498
- let result = dec.cipher.decrypt(&n, &tampered, b"");
499
- assert!(result.is_err());
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];
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ChaCha20Blake3
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -8,7 +8,7 @@ name = "chacha20blake3"
8
8
  crate-type = ["cdylib", "rlib"]
9
9
 
10
10
  [dependencies]
11
- chacha20-blake3 = { git = "https://github.com/skerkour/chacha20-blake3", rev = "9942bf34c77b03896bb1733079256076ca823e58" }
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.2.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.6
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: []