Add support for random nonce generation to ring::aead.

This commit is contained in:
Brian Smith 2019-08-09 15:00:10 -10:00
parent 63c0364bd3
commit 88d5aa7fd0
3 changed files with 170 additions and 11 deletions

View File

@ -22,7 +22,7 @@
//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
use self::block::{Block, BLOCK_LEN};
use crate::{constant_time, cpu, error, hkdf, polyfill};
use crate::{constant_time, cpu, error, hkdf, polyfill, rand};
use core::ops::RangeFrom;
pub use self::{
@ -445,18 +445,58 @@ impl hkdf::KeyType for &'static Algorithm {
}
}
mod sealed {
pub trait NonceGeneration {
const VALUE: Self;
}
}
/// A nonce generation strategy for `LesssSafeKey`; either `ExplicitNonces` or
/// `RandomNonces`.
pub trait NonceGeneration: sealed::NonceGeneration {}
impl<T> NonceGeneration for T where T: sealed::NonceGeneration {}
/// A nonce generation strategy where random nonces are used.
///
/// These algorithms are *NOT* nonce-misuse-resistant. They use nonces small
/// enough where birthday collisions need to be considered, so even ensuring
/// perfectly-random generation of nonces isn't sufficient to prevent nonce
/// reuse.
pub struct RandomNonces(());
impl sealed::NonceGeneration for RandomNonces {
const VALUE: Self = Self(());
}
/// A nonce generation strategy where explicitly-constructed nonces are used.
pub struct ExplicitNonces(());
impl sealed::NonceGeneration for ExplicitNonces {
const VALUE: Self = Self(());
}
/// Immutable keys for use in situations where `OpeningKey`/`SealingKey` and
/// `NonceSequence` cannot reasonably be used.
///
/// Prefer to use `OpeningKey`/`SealingKey` and `NonceSequence` when practical.
pub struct LessSafeKey {
/// These algorithms are *NOT* nonce-misuse-resistant. They use nonces small
/// enough where the likelihood of collisions must be carefully considered.
pub struct LessSafeKey<N = ExplicitNonces>
where
N: NonceGeneration,
{
key: UnboundKey,
_nonce_generation: N,
}
impl LessSafeKey {
impl<N> LessSafeKey<N>
where
N: NonceGeneration,
{
/// Constructs a `LessSafeKey` from an `UnboundKey`.
#[inline]
pub fn new(key: UnboundKey) -> Self {
Self { key }
Self {
key,
_nonce_generation: N::VALUE,
}
}
/// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce.
@ -492,6 +532,14 @@ impl LessSafeKey {
open_within_(&self.key, nonce, aad, in_out, ciphertext_and_tag)
}
/// The key's AEAD algorithm.
#[inline]
pub fn algorithm(&self) -> &'static Algorithm {
&self.key.algorithm
}
}
impl LessSafeKey<ExplicitNonces> {
/// Deprecated. Renamed to [`seal_in_place_append_tag()`].
#[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
#[inline]
@ -523,6 +571,9 @@ impl LessSafeKey {
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
// Overwrite the plaintext with the ciphertext before extending `in_out`
// so that if the `extend()` causes a reallocation, the ciphertext (not
// the plaintext) will be in the old deallocated buffer.
self.seal_in_place_separate_tag(nonce, aad, in_out.as_mut())
.map(|tag| in_out.extend(tag.as_ref()))
}
@ -543,15 +594,63 @@ impl LessSafeKey {
{
seal_in_place_separate_tag_(&self.key, nonce, Aad::from(aad.as_ref()), in_out)
}
}
/// The key's AEAD algorithm.
impl LessSafeKey<RandomNonces> {
/// Like [`SealingKey::seal_in_place_append_tag()`], except the nonce is
/// randomly generated.
///
/// The randomly-generated nonce is returned on success.
#[inline]
pub fn algorithm(&self) -> &'static Algorithm {
&self.key.algorithm
pub fn seal_with_random_nonce_in_place_append_tag<A, InOut>(
&self,
aad: Aad<A>,
in_out: &mut InOut,
rng: &dyn rand::SecureRandom,
) -> Result<Nonce, error::Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
// Overwrite the plaintext with the ciphertext before extending `in_out`
// so that if the `extend()` causes a reallocation, the ciphertext (not
// the plaintext) will be in the old deallocated buffer.
self.seal_with_random_nonce_in_place_separate_tag(aad, in_out.as_mut(), rng)
.map(|(nonce, tag)| {
in_out.extend(tag.as_ref());
nonce
})
}
/// Like [`SealingKey::seal_in_place_separate_tag()`], except the nonce is
/// randomly generated.
///
/// The randomly-generated nonce and the tag are returned on success.
#[inline]
pub fn seal_with_random_nonce_in_place_separate_tag<A>(
&self,
aad: Aad<A>,
in_out: &mut [u8],
rng: &dyn rand::SecureRandom,
) -> Result<(Nonce, Tag), error::Unspecified>
where
A: AsRef<[u8]>,
{
let nonce = Nonce::assume_unique_for_key(rand::generate(rng)?.expose());
seal_in_place_separate_tag_(
&self.key,
Nonce::assume_unique_for_key(*nonce.as_ref()),
Aad::from(aad.as_ref()),
in_out,
)
.map(|tag| (nonce, tag))
}
}
impl core::fmt::Debug for LessSafeKey {
impl<N> core::fmt::Debug for LessSafeKey<N>
where
N: NonceGeneration,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_struct("LessSafeKey")
.field("algorithm", self.algorithm())

View File

@ -97,7 +97,7 @@ pub(crate) mod sealed {
}
}
impl_random_arrays![4 8 16 32 48 64];
impl_random_arrays![4 8 12 16 24 32 48 64];
}
/// A type that can be returned by `ring::rand::generate()`.

View File

@ -1,4 +1,4 @@
// Copyright 2015-2016 Brian Smith.
// Copyright 2015-2019 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@ -48,6 +48,12 @@ fn aead_aes_gcm_128() {
open_with_less_safe_key,
test_file!("aead_aes_128_gcm_tests.txt"),
);
test_aead(
&aead::AES_128_GCM,
seal_with_less_safe_random_nonce_key,
open_with_less_safe_random_nonce_key,
test_file!("aead_aes_128_gcm_tests.txt"),
);
}
#[test]
@ -64,6 +70,12 @@ fn aead_aes_gcm_256() {
open_with_less_safe_key,
test_file!("aead_aes_256_gcm_tests.txt"),
);
test_aead(
&aead::AES_256_GCM,
seal_with_less_safe_random_nonce_key,
open_with_less_safe_random_nonce_key,
test_file!("aead_aes_256_gcm_tests.txt"),
);
}
#[test]
@ -80,6 +92,12 @@ fn aead_chacha20_poly1305() {
open_with_less_safe_key,
test_file!("aead_chacha20_poly1305_tests.txt"),
);
test_aead(
&aead::CHACHA20_POLY1305,
seal_with_less_safe_random_nonce_key,
open_with_less_safe_random_nonce_key,
test_file!("aead_chacha20_poly1305_tests.txt"),
);
}
fn test_aead<Seal, Open>(
@ -263,6 +281,33 @@ fn open_with_key<'a>(
o_key.open_within(aad, in_out, ciphertext_and_tag)
}
fn seal_with_less_safe_random_nonce_key(
algorithm: &'static aead::Algorithm,
key: &[u8],
nonce: aead::Nonce,
aad: aead::Aad<&[u8]>,
in_out: &mut Vec<u8>,
) -> Result<(), error::Unspecified> {
let rng = test::rand::FixedSliceRandom {
bytes: nonce.as_ref(),
};
let key = make_less_safe_random_nonce_key(algorithm, key);
key.seal_with_random_nonce_in_place_append_tag(aad, in_out, &rng)
.map(|_: aead::Nonce| ())
}
fn open_with_less_safe_random_nonce_key<'a>(
algorithm: &'static aead::Algorithm,
key: &[u8],
nonce: aead::Nonce,
aad: aead::Aad<&[u8]>,
in_out: &'a mut [u8],
ciphertext_and_tag: RangeFrom<usize>,
) -> Result<&'a mut [u8], error::Unspecified> {
let key = make_less_safe_random_nonce_key(algorithm, key);
key.open_within(nonce, aad, in_out, ciphertext_and_tag)
}
fn seal_with_less_safe_key(
algorithm: &'static aead::Algorithm,
key: &[u8],
@ -419,6 +464,13 @@ fn test_aead_key_debug() {
"LessSafeKey { algorithm: CHACHA20_POLY1305 }",
format!("{:?}", key)
);
let key: aead::LessSafeKey<aead::RandomNonces> =
make_less_safe_random_nonce_key(&aead::CHACHA20_POLY1305, &key_bytes);
assert_eq!(
"LessSafeKey { algorithm: CHACHA20_POLY1305 }",
format!("{:?}", key)
);
}
fn make_key<K: aead::BoundKey<OneNonceSequence>>(
@ -431,6 +483,14 @@ fn make_key<K: aead::BoundKey<OneNonceSequence>>(
K::new(key, nonce_sequence)
}
fn make_less_safe_random_nonce_key(
algorithm: &'static aead::Algorithm,
key: &[u8],
) -> aead::LessSafeKey<aead::RandomNonces> {
let key = aead::UnboundKey::new(algorithm, key).unwrap();
aead::LessSafeKey::new(key)
}
fn make_less_safe_key(algorithm: &'static aead::Algorithm, key: &[u8]) -> aead::LessSafeKey {
let key = aead::UnboundKey::new(algorithm, key).unwrap();
aead::LessSafeKey::new(key)