AEAD: Move each key type into its own submodule.

Encapsulate each key type in its own module.
This commit is contained in:
Brian Smith 2021-02-24 11:21:35 -08:00
parent 47edace294
commit e8620224e6
6 changed files with 331 additions and 242 deletions

View File

@ -117,10 +117,13 @@ include = [
"src/aead/gcm/gcm_nohw.rs",
"src/aead/less_safe_key.rs",
"src/aead/nonce.rs",
"src/aead/opening_key.rs",
"src/aead/poly1305.rs",
"src/aead/poly1305_test.txt",
"src/aead/quic.rs",
"src/aead/sealing_key.rs",
"src/aead/shift.rs",
"src/aead/unbound_key.rs",
"src/agreement.rs",
"src/arithmetic.rs",
"src/arithmetic/bigint.rs",

View File

@ -22,13 +22,15 @@
//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
use crate::{cpu, error, hkdf, polyfill};
use core::ops::RangeFrom;
pub use self::{
aes_gcm::{AES_128_GCM, AES_256_GCM},
chacha20_poly1305::CHACHA20_POLY1305,
less_safe_key::LessSafeKey,
nonce::{Nonce, NONCE_LEN},
opening_key::OpeningKey,
sealing_key::SealingKey,
unbound_key::UnboundKey,
};
/// A sequences of unique nonces.
@ -60,202 +62,6 @@ pub trait BoundKey<N: NonceSequence>: core::fmt::Debug {
fn algorithm(&self) -> &'static Algorithm;
}
/// An AEAD key for authenticating and decrypting ("opening"), bound to a nonce
/// sequence.
///
/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
/// of the nonce sequence.
pub struct OpeningKey<N: NonceSequence> {
key: LessSafeKey,
nonce_sequence: N,
}
impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
fn new(key: UnboundKey, nonce_sequence: N) -> Self {
Self {
key: key.inner,
nonce_sequence,
}
}
#[inline]
fn algorithm(&self) -> &'static Algorithm {
self.key.algorithm()
}
}
impl<N: NonceSequence> core::fmt::Debug for OpeningKey<N> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.key.fmt_debug("OpeningKey", f)
}
}
impl<N: NonceSequence> OpeningKey<N> {
/// Authenticates and decrypts (“opens”) data in place.
///
/// `aad` is the additional authenticated data (AAD), if any.
///
/// On input, `in_out` must be the ciphertext followed by the tag. When
/// `open_in_place()` returns `Ok(plaintext)`, the input ciphertext
/// has been overwritten by the plaintext; `plaintext` will refer to the
/// plaintext without the tag.
///
/// When `open_in_place()` returns `Err(..)`, `in_out` may have been
/// overwritten in an unspecified way.
#[inline]
pub fn open_in_place<'in_out, A>(
&mut self,
aad: Aad<A>,
in_out: &'in_out mut [u8],
) -> Result<&'in_out mut [u8], error::Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.open_in_place(self.nonce_sequence.advance()?, aad, in_out)
}
/// Authenticates and decrypts (“opens”) data in place, with a shift.
///
/// `aad` is the additional authenticated data (AAD), if any.
///
/// On input, `in_out[ciphertext_and_tag]` must be the ciphertext followed
/// by the tag. When `open_within()` returns `Ok(plaintext)`, the plaintext
/// will be at `in_out[0..plaintext.len()]`. In other words, the following
/// two code fragments are equivalent for valid values of
/// `ciphertext_and_tag`, except `open_within` will often be more efficient:
///
///
/// ```skip
/// let plaintext = key.open_within(aad, in_out, cipertext_and_tag)?;
/// ```
///
/// ```skip
/// let ciphertext_and_tag_len = in_out[ciphertext_and_tag].len();
/// in_out.copy_within(ciphertext_and_tag, 0);
/// let plaintext = key.open_in_place(aad, &mut in_out[..ciphertext_and_tag_len])?;
/// ```
///
/// Similarly, `key.open_within(aad, in_out, 0..)` is equivalent to
/// `key.open_in_place(aad, in_out)`.
///
/// When `open_in_place()` returns `Err(..)`, `in_out` may have been
/// overwritten in an unspecified way.
///
/// The shifting feature is useful in the case where multiple packets are
/// being reassembled in place. Consider this example where the peer has
/// sent the message “Split stream reassembled in place” split into
/// three sealed packets:
///
/// ```ascii-art
/// Packet 1 Packet 2 Packet 3
/// Input: [Header][Ciphertext][Tag][Header][Ciphertext][Tag][Header][Ciphertext][Tag]
/// | +--------------+ |
/// +------+ +-----+ +----------------------------------+
/// v v v
/// Output: [Plaintext][Plaintext][Plaintext]
/// “Split stream reassembled in place”
/// ```
///
/// This reassembly be accomplished with three calls to `open_within()`.
#[inline]
pub fn open_within<'in_out, A>(
&mut self,
aad: Aad<A>,
in_out: &'in_out mut [u8],
ciphertext_and_tag: RangeFrom<usize>,
) -> Result<&'in_out mut [u8], error::Unspecified>
where
A: AsRef<[u8]>,
{
self.key.open_within(
self.nonce_sequence.advance()?,
aad,
in_out,
ciphertext_and_tag,
)
}
}
/// An AEAD key for encrypting and signing ("sealing"), bound to a nonce
/// sequence.
///
/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
/// of the nonce sequence.
pub struct SealingKey<N: NonceSequence> {
key: LessSafeKey,
nonce_sequence: N,
}
impl<N: NonceSequence> BoundKey<N> for SealingKey<N> {
fn new(key: UnboundKey, nonce_sequence: N) -> Self {
Self {
key: key.inner,
nonce_sequence,
}
}
#[inline]
fn algorithm(&self) -> &'static Algorithm {
self.key.algorithm()
}
}
impl<N: NonceSequence> core::fmt::Debug for SealingKey<N> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.key.fmt_debug("SealingKey", f)
}
}
impl<N: NonceSequence> SealingKey<N> {
/// Encrypts and signs (“seals”) data in place, appending the tag to the
/// resulting ciphertext.
///
/// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to:
///
/// ```skip
/// key.seal_in_place_separate_tag(aad, in_out.as_mut())
/// .map(|tag| in_out.extend(tag.as_ref()))
/// ```
#[inline]
pub fn seal_in_place_append_tag<A, InOut>(
&mut self,
aad: Aad<A>,
in_out: &mut InOut,
) -> Result<(), error::Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
self.key
.seal_in_place_append_tag(self.nonce_sequence.advance()?, aad, in_out)
}
/// Encrypts and signs (“seals”) data in place.
///
/// `aad` is the additional authenticated data (AAD), if any. This is
/// authenticated but not encrypted. The type `A` could be a byte slice
/// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
/// If there is no AAD then use `Aad::empty()`.
///
/// The plaintext is given as the input value of `in_out`. `seal_in_place()`
/// will overwrite the plaintext with the ciphertext and return the tag.
/// For most protocols, the caller must append the tag to the ciphertext.
/// The tag will be `self.algorithm.tag_len()` bytes long.
#[inline]
pub fn seal_in_place_separate_tag<A>(
&mut self,
aad: Aad<A>,
in_out: &mut [u8],
) -> Result<Tag, error::Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.seal_in_place_separate_tag(self.nonce_sequence.advance()?, aad, in_out)
}
}
/// The additionally authenticated data (AAD) for an opening or sealing
/// operation. This data is authenticated but is **not** encrypted.
///
@ -320,56 +126,12 @@ where
impl<A> Eq for Aad<A> where A: Eq {}
/// An AEAD key without a designated role or nonce sequence.
pub struct UnboundKey {
inner: LessSafeKey,
}
impl core::fmt::Debug for UnboundKey {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.inner.fmt_debug("UnboundKey", f)
}
}
#[allow(clippy::large_enum_variant, variant_size_differences)]
enum KeyInner {
AesGcm(aes_gcm::Key),
ChaCha20Poly1305(chacha20_poly1305::Key),
}
impl UnboundKey {
/// Constructs a `UnboundKey`.
///
/// Fails if `key_bytes.len() != algorithm.key_len()`.
#[inline]
pub fn new(
algorithm: &'static Algorithm,
key_bytes: &[u8],
) -> Result<Self, error::Unspecified> {
Ok(Self {
inner: LessSafeKey::new_(algorithm, key_bytes)?,
})
}
/// The key's AEAD algorithm.
#[inline]
pub fn algorithm(&self) -> &'static Algorithm {
self.inner.algorithm()
}
}
impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundKey {
fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
let mut key_bytes = [0; MAX_KEY_LEN];
let key_bytes = &mut key_bytes[..okm.len().key_len];
let algorithm = *okm.len();
okm.fill(key_bytes).unwrap();
Self {
inner: LessSafeKey::new_(algorithm, key_bytes).unwrap(),
}
}
}
impl hkdf::KeyType for &'static Algorithm {
#[inline]
fn len(&self) -> usize {
@ -485,6 +247,9 @@ pub mod chacha20_poly1305_openssh;
mod gcm;
mod less_safe_key;
mod nonce;
mod opening_key;
mod poly1305;
pub mod quic;
mod sealing_key;
mod shift;
mod unbound_key;

View File

@ -30,7 +30,7 @@ impl LessSafeKey {
/// Constructs a `LessSafeKey`.
#[inline]
pub fn new(key: UnboundKey) -> Self {
key.inner
key.into_inner()
}
pub(super) fn new_(

143
src/aead/opening_key.rs Normal file
View File

@ -0,0 +1,143 @@
// Copyright 2015-2021 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//! Authenticated Encryption with Associated Data (AEAD).
//!
//! See [Authenticated encryption: relations among notions and analysis of the
//! generic composition paradigm][AEAD] for an introduction to the concept of
//! AEADs.
//!
//! [AEAD]: http://www-cse.ucsd.edu/~mihir/papers/oem.html
//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
use super::{Aad, Algorithm, BoundKey, LessSafeKey, NonceSequence, UnboundKey};
use crate::error;
use core::ops::RangeFrom;
/// An AEAD key for authenticating and decrypting ("opening"), bound to a nonce
/// sequence.
///
/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
/// of the nonce sequence.
pub struct OpeningKey<N: NonceSequence> {
key: LessSafeKey,
nonce_sequence: N,
}
impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
fn new(key: UnboundKey, nonce_sequence: N) -> Self {
Self {
key: key.into_inner(),
nonce_sequence,
}
}
#[inline]
fn algorithm(&self) -> &'static Algorithm {
self.key.algorithm()
}
}
impl<N: NonceSequence> core::fmt::Debug for OpeningKey<N> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.key.fmt_debug("OpeningKey", f)
}
}
impl<N: NonceSequence> OpeningKey<N> {
/// Authenticates and decrypts (“opens”) data in place.
///
/// `aad` is the additional authenticated data (AAD), if any.
///
/// On input, `in_out` must be the ciphertext followed by the tag. When
/// `open_in_place()` returns `Ok(plaintext)`, the input ciphertext
/// has been overwritten by the plaintext; `plaintext` will refer to the
/// plaintext without the tag.
///
/// When `open_in_place()` returns `Err(..)`, `in_out` may have been
/// overwritten in an unspecified way.
#[inline]
pub fn open_in_place<'in_out, A>(
&mut self,
aad: Aad<A>,
in_out: &'in_out mut [u8],
) -> Result<&'in_out mut [u8], error::Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.open_in_place(self.nonce_sequence.advance()?, aad, in_out)
}
/// Authenticates and decrypts (“opens”) data in place, with a shift.
///
/// `aad` is the additional authenticated data (AAD), if any.
///
/// On input, `in_out[ciphertext_and_tag]` must be the ciphertext followed
/// by the tag. When `open_within()` returns `Ok(plaintext)`, the plaintext
/// will be at `in_out[0..plaintext.len()]`. In other words, the following
/// two code fragments are equivalent for valid values of
/// `ciphertext_and_tag`, except `open_within` will often be more efficient:
///
///
/// ```skip
/// let plaintext = key.open_within(aad, in_out, cipertext_and_tag)?;
/// ```
///
/// ```skip
/// let ciphertext_and_tag_len = in_out[ciphertext_and_tag].len();
/// in_out.copy_within(ciphertext_and_tag, 0);
/// let plaintext = key.open_in_place(aad, &mut in_out[..ciphertext_and_tag_len])?;
/// ```
///
/// Similarly, `key.open_within(aad, in_out, 0..)` is equivalent to
/// `key.open_in_place(aad, in_out)`.
///
/// When `open_in_place()` returns `Err(..)`, `in_out` may have been
/// overwritten in an unspecified way.
///
/// The shifting feature is useful in the case where multiple packets are
/// being reassembled in place. Consider this example where the peer has
/// sent the message “Split stream reassembled in place” split into
/// three sealed packets:
///
/// ```ascii-art
/// Packet 1 Packet 2 Packet 3
/// Input: [Header][Ciphertext][Tag][Header][Ciphertext][Tag][Header][Ciphertext][Tag]
/// | +--------------+ |
/// +------+ +-----+ +----------------------------------+
/// v v v
/// Output: [Plaintext][Plaintext][Plaintext]
/// “Split stream reassembled in place”
/// ```
///
/// This reassembly be accomplished with three calls to `open_within()`.
#[inline]
pub fn open_within<'in_out, A>(
&mut self,
aad: Aad<A>,
in_out: &'in_out mut [u8],
ciphertext_and_tag: RangeFrom<usize>,
) -> Result<&'in_out mut [u8], error::Unspecified>
where
A: AsRef<[u8]>,
{
self.key.open_within(
self.nonce_sequence.advance()?,
aad,
in_out,
ciphertext_and_tag,
)
}
}

104
src/aead/sealing_key.rs Normal file
View File

@ -0,0 +1,104 @@
// Copyright 2015-2021 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//! Authenticated Encryption with Associated Data (AEAD).
//!
//! See [Authenticated encryption: relations among notions and analysis of the
//! generic composition paradigm][AEAD] for an introduction to the concept of
//! AEADs.
//!
//! [AEAD]: http://www-cse.ucsd.edu/~mihir/papers/oem.html
//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
use super::{Aad, Algorithm, BoundKey, LessSafeKey, NonceSequence, Tag, UnboundKey};
use crate::error;
/// An AEAD key for encrypting and signing ("sealing"), bound to a nonce
/// sequence.
///
/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
/// of the nonce sequence.
pub struct SealingKey<N: NonceSequence> {
key: LessSafeKey,
nonce_sequence: N,
}
impl<N: NonceSequence> BoundKey<N> for SealingKey<N> {
fn new(key: UnboundKey, nonce_sequence: N) -> Self {
Self {
key: key.into_inner(),
nonce_sequence,
}
}
#[inline]
fn algorithm(&self) -> &'static Algorithm {
self.key.algorithm()
}
}
impl<N: NonceSequence> core::fmt::Debug for SealingKey<N> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.key.fmt_debug("SealingKey", f)
}
}
impl<N: NonceSequence> SealingKey<N> {
/// Encrypts and signs (“seals”) data in place, appending the tag to the
/// resulting ciphertext.
///
/// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to:
///
/// ```skip
/// key.seal_in_place_separate_tag(aad, in_out.as_mut())
/// .map(|tag| in_out.extend(tag.as_ref()))
/// ```
#[inline]
pub fn seal_in_place_append_tag<A, InOut>(
&mut self,
aad: Aad<A>,
in_out: &mut InOut,
) -> Result<(), error::Unspecified>
where
A: AsRef<[u8]>,
InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
{
self.key
.seal_in_place_append_tag(self.nonce_sequence.advance()?, aad, in_out)
}
/// Encrypts and signs (“seals”) data in place.
///
/// `aad` is the additional authenticated data (AAD), if any. This is
/// authenticated but not encrypted. The type `A` could be a byte slice
/// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
/// If there is no AAD then use `Aad::empty()`.
///
/// The plaintext is given as the input value of `in_out`. `seal_in_place()`
/// will overwrite the plaintext with the ciphertext and return the tag.
/// For most protocols, the caller must append the tag to the ciphertext.
/// The tag will be `self.algorithm.tag_len()` bytes long.
#[inline]
pub fn seal_in_place_separate_tag<A>(
&mut self,
aad: Aad<A>,
in_out: &mut [u8],
) -> Result<Tag, error::Unspecified>
where
A: AsRef<[u8]>,
{
self.key
.seal_in_place_separate_tag(self.nonce_sequence.advance()?, aad, in_out)
}
}

74
src/aead/unbound_key.rs Normal file
View File

@ -0,0 +1,74 @@
// Copyright 2015-2021 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//! Authenticated Encryption with Associated Data (AEAD).
//!
//! See [Authenticated encryption: relations among notions and analysis of the
//! generic composition paradigm][AEAD] for an introduction to the concept of
//! AEADs.
//!
//! [AEAD]: http://www-cse.ucsd.edu/~mihir/papers/oem.html
//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
use super::{Algorithm, LessSafeKey, MAX_KEY_LEN};
use crate::{error, hkdf};
/// An AEAD key without a designated role or nonce sequence.
pub struct UnboundKey {
inner: LessSafeKey,
}
impl UnboundKey {
/// Constructs a `UnboundKey`.
///
/// Fails if `key_bytes.len() != algorithm.key_len()`.
#[inline]
pub fn new(
algorithm: &'static Algorithm,
key_bytes: &[u8],
) -> Result<Self, error::Unspecified> {
Ok(Self {
inner: LessSafeKey::new_(algorithm, key_bytes)?,
})
}
/// The key's AEAD algorithm.
#[inline]
pub fn algorithm(&self) -> &'static Algorithm {
self.inner.algorithm()
}
#[inline]
pub(super) fn into_inner(self) -> LessSafeKey {
self.inner
}
}
impl core::fmt::Debug for UnboundKey {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.inner.fmt_debug("UnboundKey", f)
}
}
impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundKey {
fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
let mut key_bytes = [0; MAX_KEY_LEN];
let key_bytes = &mut key_bytes[..okm.len().key_len];
let algorithm = *okm.len();
okm.fill(key_bytes).unwrap();
Self {
inner: LessSafeKey::new_(algorithm, key_bytes).unwrap(),
}
}
}