Skip to content

Commit 88d5aa7

Browse files
committed
Add support for random nonce generation to ring::aead.
1 parent 63c0364 commit 88d5aa7

File tree

3 files changed

+170
-11
lines changed

3 files changed

+170
-11
lines changed

src/aead.rs

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
2323
2424
use self::block::{Block, BLOCK_LEN};
25-
use crate::{constant_time, cpu, error, hkdf, polyfill};
25+
use crate::{constant_time, cpu, error, hkdf, polyfill, rand};
2626
use core::ops::RangeFrom;
2727

2828
pub use self::{
@@ -445,18 +445,58 @@ impl hkdf::KeyType for &'static Algorithm {
445445
}
446446
}
447447

448+
mod sealed {
449+
pub trait NonceGeneration {
450+
const VALUE: Self;
451+
}
452+
}
453+
454+
/// A nonce generation strategy for `LesssSafeKey`; either `ExplicitNonces` or
455+
/// `RandomNonces`.
456+
pub trait NonceGeneration: sealed::NonceGeneration {}
457+
impl<T> NonceGeneration for T where T: sealed::NonceGeneration {}
458+
459+
/// A nonce generation strategy where random nonces are used.
460+
///
461+
/// These algorithms are *NOT* nonce-misuse-resistant. They use nonces small
462+
/// enough where birthday collisions need to be considered, so even ensuring
463+
/// perfectly-random generation of nonces isn't sufficient to prevent nonce
464+
/// reuse.
465+
pub struct RandomNonces(());
466+
impl sealed::NonceGeneration for RandomNonces {
467+
const VALUE: Self = Self(());
468+
}
469+
470+
/// A nonce generation strategy where explicitly-constructed nonces are used.
471+
pub struct ExplicitNonces(());
472+
impl sealed::NonceGeneration for ExplicitNonces {
473+
const VALUE: Self = Self(());
474+
}
475+
448476
/// Immutable keys for use in situations where `OpeningKey`/`SealingKey` and
449477
/// `NonceSequence` cannot reasonably be used.
450478
///
451-
/// Prefer to use `OpeningKey`/`SealingKey` and `NonceSequence` when practical.
452-
pub struct LessSafeKey {
479+
/// These algorithms are *NOT* nonce-misuse-resistant. They use nonces small
480+
/// enough where the likelihood of collisions must be carefully considered.
481+
pub struct LessSafeKey<N = ExplicitNonces>
482+
where
483+
N: NonceGeneration,
484+
{
453485
key: UnboundKey,
486+
_nonce_generation: N,
454487
}
455488

456-
impl LessSafeKey {
489+
impl<N> LessSafeKey<N>
490+
where
491+
N: NonceGeneration,
492+
{
457493
/// Constructs a `LessSafeKey` from an `UnboundKey`.
494+
#[inline]
458495
pub fn new(key: UnboundKey) -> Self {
459-
Self { key }
496+
Self {
497+
key,
498+
_nonce_generation: N::VALUE,
499+
}
460500
}
461501

462502
/// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce.
@@ -492,6 +532,14 @@ impl LessSafeKey {
492532
open_within_(&self.key, nonce, aad, in_out, ciphertext_and_tag)
493533
}
494534

535+
/// The key's AEAD algorithm.
536+
#[inline]
537+
pub fn algorithm(&self) -> &'static Algorithm {
538+
&self.key.algorithm
539+
}
540+
}
541+
542+
impl LessSafeKey<ExplicitNonces> {
495543
/// Deprecated. Renamed to [`seal_in_place_append_tag()`].
496544
#[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
497545
#[inline]
@@ -523,6 +571,9 @@ impl LessSafeKey {
523571
A: AsRef<[u8]>,
524572
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
525573
{
574+
// Overwrite the plaintext with the ciphertext before extending `in_out`
575+
// so that if the `extend()` causes a reallocation, the ciphertext (not
576+
// the plaintext) will be in the old deallocated buffer.
526577
self.seal_in_place_separate_tag(nonce, aad, in_out.as_mut())
527578
.map(|tag| in_out.extend(tag.as_ref()))
528579
}
@@ -543,15 +594,63 @@ impl LessSafeKey {
543594
{
544595
seal_in_place_separate_tag_(&self.key, nonce, Aad::from(aad.as_ref()), in_out)
545596
}
597+
}
546598

547-
/// The key's AEAD algorithm.
599+
impl LessSafeKey<RandomNonces> {
600+
/// Like [`SealingKey::seal_in_place_append_tag()`], except the nonce is
601+
/// randomly generated.
602+
///
603+
/// The randomly-generated nonce is returned on success.
548604
#[inline]
549-
pub fn algorithm(&self) -> &'static Algorithm {
550-
&self.key.algorithm
605+
pub fn seal_with_random_nonce_in_place_append_tag<A, InOut>(
606+
&self,
607+
aad: Aad<A>,
608+
in_out: &mut InOut,
609+
rng: &dyn rand::SecureRandom,
610+
) -> Result<Nonce, error::Unspecified>
611+
where
612+
A: AsRef<[u8]>,
613+
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
614+
{
615+
// Overwrite the plaintext with the ciphertext before extending `in_out`
616+
// so that if the `extend()` causes a reallocation, the ciphertext (not
617+
// the plaintext) will be in the old deallocated buffer.
618+
self.seal_with_random_nonce_in_place_separate_tag(aad, in_out.as_mut(), rng)
619+
.map(|(nonce, tag)| {
620+
in_out.extend(tag.as_ref());
621+
nonce
622+
})
623+
}
624+
625+
/// Like [`SealingKey::seal_in_place_separate_tag()`], except the nonce is
626+
/// randomly generated.
627+
///
628+
/// The randomly-generated nonce and the tag are returned on success.
629+
#[inline]
630+
pub fn seal_with_random_nonce_in_place_separate_tag<A>(
631+
&self,
632+
aad: Aad<A>,
633+
in_out: &mut [u8],
634+
rng: &dyn rand::SecureRandom,
635+
) -> Result<(Nonce, Tag), error::Unspecified>
636+
where
637+
A: AsRef<[u8]>,
638+
{
639+
let nonce = Nonce::assume_unique_for_key(rand::generate(rng)?.expose());
640+
seal_in_place_separate_tag_(
641+
&self.key,
642+
Nonce::assume_unique_for_key(*nonce.as_ref()),
643+
Aad::from(aad.as_ref()),
644+
in_out,
645+
)
646+
.map(|tag| (nonce, tag))
551647
}
552648
}
553649

554-
impl core::fmt::Debug for LessSafeKey {
650+
impl<N> core::fmt::Debug for LessSafeKey<N>
651+
where
652+
N: NonceGeneration,
653+
{
555654
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
556655
f.debug_struct("LessSafeKey")
557656
.field("algorithm", self.algorithm())

src/rand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub(crate) mod sealed {
9797
}
9898
}
9999

100-
impl_random_arrays![4 8 16 32 48 64];
100+
impl_random_arrays![4 8 12 16 24 32 48 64];
101101
}
102102

103103
/// A type that can be returned by `ring::rand::generate()`.

tests/aead_tests.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2015-2016 Brian Smith.
1+
// Copyright 2015-2019 Brian Smith.
22
//
33
// Permission to use, copy, modify, and/or distribute this software for any
44
// purpose with or without fee is hereby granted, provided that the above
@@ -48,6 +48,12 @@ fn aead_aes_gcm_128() {
4848
open_with_less_safe_key,
4949
test_file!("aead_aes_128_gcm_tests.txt"),
5050
);
51+
test_aead(
52+
&aead::AES_128_GCM,
53+
seal_with_less_safe_random_nonce_key,
54+
open_with_less_safe_random_nonce_key,
55+
test_file!("aead_aes_128_gcm_tests.txt"),
56+
);
5157
}
5258

5359
#[test]
@@ -64,6 +70,12 @@ fn aead_aes_gcm_256() {
6470
open_with_less_safe_key,
6571
test_file!("aead_aes_256_gcm_tests.txt"),
6672
);
73+
test_aead(
74+
&aead::AES_256_GCM,
75+
seal_with_less_safe_random_nonce_key,
76+
open_with_less_safe_random_nonce_key,
77+
test_file!("aead_aes_256_gcm_tests.txt"),
78+
);
6779
}
6880

6981
#[test]
@@ -80,6 +92,12 @@ fn aead_chacha20_poly1305() {
8092
open_with_less_safe_key,
8193
test_file!("aead_chacha20_poly1305_tests.txt"),
8294
);
95+
test_aead(
96+
&aead::CHACHA20_POLY1305,
97+
seal_with_less_safe_random_nonce_key,
98+
open_with_less_safe_random_nonce_key,
99+
test_file!("aead_chacha20_poly1305_tests.txt"),
100+
);
83101
}
84102

85103
fn test_aead<Seal, Open>(
@@ -263,6 +281,33 @@ fn open_with_key<'a>(
263281
o_key.open_within(aad, in_out, ciphertext_and_tag)
264282
}
265283

284+
fn seal_with_less_safe_random_nonce_key(
285+
algorithm: &'static aead::Algorithm,
286+
key: &[u8],
287+
nonce: aead::Nonce,
288+
aad: aead::Aad<&[u8]>,
289+
in_out: &mut Vec<u8>,
290+
) -> Result<(), error::Unspecified> {
291+
let rng = test::rand::FixedSliceRandom {
292+
bytes: nonce.as_ref(),
293+
};
294+
let key = make_less_safe_random_nonce_key(algorithm, key);
295+
key.seal_with_random_nonce_in_place_append_tag(aad, in_out, &rng)
296+
.map(|_: aead::Nonce| ())
297+
}
298+
299+
fn open_with_less_safe_random_nonce_key<'a>(
300+
algorithm: &'static aead::Algorithm,
301+
key: &[u8],
302+
nonce: aead::Nonce,
303+
aad: aead::Aad<&[u8]>,
304+
in_out: &'a mut [u8],
305+
ciphertext_and_tag: RangeFrom<usize>,
306+
) -> Result<&'a mut [u8], error::Unspecified> {
307+
let key = make_less_safe_random_nonce_key(algorithm, key);
308+
key.open_within(nonce, aad, in_out, ciphertext_and_tag)
309+
}
310+
266311
fn seal_with_less_safe_key(
267312
algorithm: &'static aead::Algorithm,
268313
key: &[u8],
@@ -419,6 +464,13 @@ fn test_aead_key_debug() {
419464
"LessSafeKey { algorithm: CHACHA20_POLY1305 }",
420465
format!("{:?}", key)
421466
);
467+
468+
let key: aead::LessSafeKey<aead::RandomNonces> =
469+
make_less_safe_random_nonce_key(&aead::CHACHA20_POLY1305, &key_bytes);
470+
assert_eq!(
471+
"LessSafeKey { algorithm: CHACHA20_POLY1305 }",
472+
format!("{:?}", key)
473+
);
422474
}
423475

424476
fn make_key<K: aead::BoundKey<OneNonceSequence>>(
@@ -431,6 +483,14 @@ fn make_key<K: aead::BoundKey<OneNonceSequence>>(
431483
K::new(key, nonce_sequence)
432484
}
433485

486+
fn make_less_safe_random_nonce_key(
487+
algorithm: &'static aead::Algorithm,
488+
key: &[u8],
489+
) -> aead::LessSafeKey<aead::RandomNonces> {
490+
let key = aead::UnboundKey::new(algorithm, key).unwrap();
491+
aead::LessSafeKey::new(key)
492+
}
493+
434494
fn make_less_safe_key(algorithm: &'static aead::Algorithm, key: &[u8]) -> aead::LessSafeKey {
435495
let key = aead::UnboundKey::new(algorithm, key).unwrap();
436496
aead::LessSafeKey::new(key)

0 commit comments

Comments
 (0)