blob: 55bb6162de0ec92e414893fceac18eda6d36ee3c [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.
use super::{Aad, Algorithm, KeyInner, Nonce, Tag, UnboundKey, TAG_LEN};
use crate::{constant_time, cpu, error, polyfill};
use core::ops::RangeFrom;
/// 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 {
inner: KeyInner,
algorithm: &'static Algorithm,
}
impl LessSafeKey {
/// Constructs a `LessSafeKey`.
#[inline]
pub fn new(key: UnboundKey) -> Self {
key.into_inner()
}
pub(super) fn new_(
algorithm: &'static Algorithm,
key_bytes: &[u8],
) -> Result<Self, error::Unspecified> {
let cpu_features = cpu::features();
Ok(Self {
inner: (algorithm.init)(key_bytes, cpu_features)?,
algorithm,
})
}
/// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce.
///
/// `nonce` must be unique for every use of the key to open data.
#[inline]
pub fn open_in_place<'in_out, A>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &'in_out mut [u8],
) -> Result<&'in_out mut [u8], error::Unspecified>
where
A: AsRef<[u8]>,
{
self.open_within(nonce, aad, in_out, 0..)
}
/// Like [`OpeningKey::open_within()`], except it accepts an arbitrary nonce.
///
/// `nonce` must be unique for every use of the key to open data.
#[inline]
pub fn open_within<'in_out, A>(
&self,
nonce: Nonce,
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]>,
{
open_within_(
self,
nonce,
Aad::from(aad.as_ref()),
in_out,
ciphertext_and_tag,
)
}
/// Like [`SealingKey::seal_in_place_append_tag()`], except it accepts an
/// arbitrary nonce.
///
/// `nonce` must be unique for every use of the key to seal data.
#[inline]
pub fn seal_in_place_append_tag<A, InOut>(
&self,
nonce: Nonce,
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.seal_in_place_separate_tag(nonce, aad, in_out.as_mut())
.map(|tag| in_out.extend(tag.as_ref()))
}
/// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an
/// arbitrary nonce.
///
/// `nonce` must be unique for every use of the key to seal data.
#[inline]
pub fn seal_in_place_separate_tag<A>(
&self,
nonce: Nonce,
aad: Aad<A>,
in_out: &mut [u8],
) -> Result<Tag, error::Unspecified>
where
A: AsRef<[u8]>,
{
seal_in_place_separate_tag_(&self, nonce, Aad::from(aad.as_ref()), in_out)
}
/// The key's AEAD algorithm.
#[inline]
pub fn algorithm(&self) -> &'static Algorithm {
&self.algorithm
}
pub(super) fn fmt_debug(
&self,
type_name: &'static str,
f: &mut core::fmt::Formatter,
) -> Result<(), core::fmt::Error> {
f.debug_struct(type_name)
.field("algorithm", &self.algorithm())
.finish()
}
}
fn open_within_<'in_out>(
key: &LessSafeKey,
nonce: Nonce,
aad: Aad<&[u8]>,
in_out: &'in_out mut [u8],
src: RangeFrom<usize>,
) -> Result<&'in_out mut [u8], error::Unspecified> {
let ciphertext_and_tag_len = in_out
.len()
.checked_sub(src.start)
.ok_or(error::Unspecified)?;
let ciphertext_len = ciphertext_and_tag_len
.checked_sub(TAG_LEN)
.ok_or(error::Unspecified)?;
check_per_nonce_max_bytes(key.algorithm, ciphertext_len)?;
let (in_out, received_tag) = in_out.split_at_mut(src.start + ciphertext_len);
let Tag(calculated_tag) = (key.algorithm.open)(&key.inner, nonce, aad, in_out, src);
if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag).is_err() {
// Zero out the plaintext so that it isn't accidentally leaked or used
// after verification fails. It would be safest if we could check the
// tag before decrypting, but some `open` implementations interleave
// authentication with decryption for performance.
for b in &mut in_out[..ciphertext_len] {
*b = 0;
}
return Err(error::Unspecified);
}
// `ciphertext_len` is also the plaintext length.
Ok(&mut in_out[..ciphertext_len])
}
#[inline]
pub(super) fn seal_in_place_separate_tag_(
key: &LessSafeKey,
nonce: Nonce,
aad: Aad<&[u8]>,
in_out: &mut [u8],
) -> Result<Tag, error::Unspecified> {
check_per_nonce_max_bytes(key.algorithm(), in_out.len())?;
Ok((key.algorithm.seal)(&key.inner, nonce, aad, in_out))
}
fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), error::Unspecified> {
if polyfill::u64_from_usize(in_out_len) > alg.max_input_len {
return Err(error::Unspecified);
}
Ok(())
}
impl core::fmt::Debug for LessSafeKey {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.fmt_debug("LessSafeKey", f)
}
}