blob: f3e53d48b7c1542ea75ae0d79872b53c0731e453 [file] [log] [blame]
// 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 can 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,
)
}
}