ring/aead/
less_safe_key.rs

1// Copyright 2015-2021 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15use super::{Aad, Algorithm, KeyInner, Nonce, Tag, UnboundKey, TAG_LEN};
16use crate::{constant_time, cpu, error, polyfill};
17use core::ops::RangeFrom;
18
19/// Immutable keys for use in situations where `OpeningKey`/`SealingKey` and
20/// `NonceSequence` cannot reasonably be used.
21///
22/// Prefer to use `OpeningKey`/`SealingKey` and `NonceSequence` when practical.
23pub struct LessSafeKey {
24    inner: KeyInner,
25    algorithm: &'static Algorithm,
26}
27
28impl LessSafeKey {
29    /// Constructs a `LessSafeKey`.
30    #[inline]
31    pub fn new(key: UnboundKey) -> Self {
32        key.into_inner()
33    }
34
35    pub(super) fn new_(
36        algorithm: &'static Algorithm,
37        key_bytes: &[u8],
38    ) -> Result<Self, error::Unspecified> {
39        let cpu_features = cpu::features();
40        Ok(Self {
41            inner: (algorithm.init)(key_bytes, cpu_features)?,
42            algorithm,
43        })
44    }
45
46    /// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce.
47    ///
48    /// `nonce` must be unique for every use of the key to open data.
49    #[inline]
50    pub fn open_in_place<'in_out, A>(
51        &self,
52        nonce: Nonce,
53        aad: Aad<A>,
54        in_out: &'in_out mut [u8],
55    ) -> Result<&'in_out mut [u8], error::Unspecified>
56    where
57        A: AsRef<[u8]>,
58    {
59        self.open_within(nonce, aad, in_out, 0..)
60    }
61
62    /// Like [`OpeningKey::open_within()`], except it accepts an arbitrary nonce.
63    ///
64    /// `nonce` must be unique for every use of the key to open data.
65    #[inline]
66    pub fn open_within<'in_out, A>(
67        &self,
68        nonce: Nonce,
69        aad: Aad<A>,
70        in_out: &'in_out mut [u8],
71        ciphertext_and_tag: RangeFrom<usize>,
72    ) -> Result<&'in_out mut [u8], error::Unspecified>
73    where
74        A: AsRef<[u8]>,
75    {
76        open_within_(
77            self,
78            nonce,
79            Aad::from(aad.as_ref()),
80            in_out,
81            ciphertext_and_tag,
82        )
83    }
84
85    /// Like [`SealingKey::seal_in_place_append_tag()`], except it accepts an
86    /// arbitrary nonce.
87    ///
88    /// `nonce` must be unique for every use of the key to seal data.
89    #[inline]
90    pub fn seal_in_place_append_tag<A, InOut>(
91        &self,
92        nonce: Nonce,
93        aad: Aad<A>,
94        in_out: &mut InOut,
95    ) -> Result<(), error::Unspecified>
96    where
97        A: AsRef<[u8]>,
98        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
99    {
100        self.seal_in_place_separate_tag(nonce, aad, in_out.as_mut())
101            .map(|tag| in_out.extend(tag.as_ref()))
102    }
103
104    /// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an
105    /// arbitrary nonce.
106    ///
107    /// `nonce` must be unique for every use of the key to seal data.
108    #[inline]
109    pub fn seal_in_place_separate_tag<A>(
110        &self,
111        nonce: Nonce,
112        aad: Aad<A>,
113        in_out: &mut [u8],
114    ) -> Result<Tag, error::Unspecified>
115    where
116        A: AsRef<[u8]>,
117    {
118        seal_in_place_separate_tag_(&self, nonce, Aad::from(aad.as_ref()), in_out)
119    }
120
121    /// The key's AEAD algorithm.
122    #[inline]
123    pub fn algorithm(&self) -> &'static Algorithm {
124        &self.algorithm
125    }
126
127    pub(super) fn fmt_debug(
128        &self,
129        type_name: &'static str,
130        f: &mut core::fmt::Formatter,
131    ) -> Result<(), core::fmt::Error> {
132        f.debug_struct(type_name)
133            .field("algorithm", &self.algorithm())
134            .finish()
135    }
136}
137
138fn open_within_<'in_out>(
139    key: &LessSafeKey,
140    nonce: Nonce,
141    aad: Aad<&[u8]>,
142    in_out: &'in_out mut [u8],
143    src: RangeFrom<usize>,
144) -> Result<&'in_out mut [u8], error::Unspecified> {
145    let ciphertext_and_tag_len = in_out
146        .len()
147        .checked_sub(src.start)
148        .ok_or(error::Unspecified)?;
149    let ciphertext_len = ciphertext_and_tag_len
150        .checked_sub(TAG_LEN)
151        .ok_or(error::Unspecified)?;
152    check_per_nonce_max_bytes(key.algorithm, ciphertext_len)?;
153    let (in_out, received_tag) = in_out.split_at_mut(src.start + ciphertext_len);
154    let Tag(calculated_tag) = (key.algorithm.open)(&key.inner, nonce, aad, in_out, src);
155    if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag).is_err() {
156        // Zero out the plaintext so that it isn't accidentally leaked or used
157        // after verification fails. It would be safest if we could check the
158        // tag before decrypting, but some `open` implementations interleave
159        // authentication with decryption for performance.
160        for b in &mut in_out[..ciphertext_len] {
161            *b = 0;
162        }
163        return Err(error::Unspecified);
164    }
165    // `ciphertext_len` is also the plaintext length.
166    Ok(&mut in_out[..ciphertext_len])
167}
168
169#[inline]
170pub(super) fn seal_in_place_separate_tag_(
171    key: &LessSafeKey,
172    nonce: Nonce,
173    aad: Aad<&[u8]>,
174    in_out: &mut [u8],
175) -> Result<Tag, error::Unspecified> {
176    check_per_nonce_max_bytes(key.algorithm(), in_out.len())?;
177    Ok((key.algorithm.seal)(&key.inner, nonce, aad, in_out))
178}
179
180fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), error::Unspecified> {
181    if polyfill::u64_from_usize(in_out_len) > alg.max_input_len {
182        return Err(error::Unspecified);
183    }
184    Ok(())
185}
186
187impl core::fmt::Debug for LessSafeKey {
188    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
189        self.fmt_debug("LessSafeKey", f)
190    }
191}