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};
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.
23#[derive(Clone)]
24pub struct LessSafeKey {
25    inner: KeyInner,
26    algorithm: &'static Algorithm,
27}
28
29impl LessSafeKey {
30    /// Constructs a `LessSafeKey`.
31    #[inline]
32    pub fn new(key: UnboundKey) -> Self {
33        key.into_inner()
34    }
35
36    pub(super) fn new_(
37        algorithm: &'static Algorithm,
38        key_bytes: &[u8],
39    ) -> Result<Self, error::Unspecified> {
40        let cpu_features = cpu::features();
41        Ok(Self {
42            inner: (algorithm.init)(key_bytes, cpu_features)?,
43            algorithm,
44        })
45    }
46
47    /// Like [open_in_place](Self::open_in_place), except the authentication tag is
48    /// passed separately.
49    #[inline]
50    pub fn open_in_place_separate_tag<'in_out, A>(
51        &self,
52        nonce: Nonce,
53        aad: Aad<A>,
54        tag: Tag,
55        in_out: &'in_out mut [u8],
56        ciphertext: RangeFrom<usize>,
57    ) -> Result<&'in_out mut [u8], error::Unspecified>
58    where
59        A: AsRef<[u8]>,
60    {
61        let aad = Aad::from(aad.as_ref());
62        open_within_(self, nonce, aad, tag, in_out, ciphertext)
63    }
64
65    /// Like [`super::OpeningKey::open_in_place()`], except it accepts an
66    /// arbitrary nonce.
67    ///
68    /// `nonce` must be unique for every use of the key to open data.
69    #[inline]
70    pub fn open_in_place<'in_out, A>(
71        &self,
72        nonce: Nonce,
73        aad: Aad<A>,
74        in_out: &'in_out mut [u8],
75    ) -> Result<&'in_out mut [u8], error::Unspecified>
76    where
77        A: AsRef<[u8]>,
78    {
79        self.open_within(nonce, aad, in_out, 0..)
80    }
81
82    /// Like [`super::OpeningKey::open_within()`], except it accepts an
83    /// arbitrary nonce.
84    ///
85    /// `nonce` must be unique for every use of the key to open data.
86    #[inline]
87    pub fn open_within<'in_out, A>(
88        &self,
89        nonce: Nonce,
90        aad: Aad<A>,
91        in_out: &'in_out mut [u8],
92        ciphertext_and_tag: RangeFrom<usize>,
93    ) -> Result<&'in_out mut [u8], error::Unspecified>
94    where
95        A: AsRef<[u8]>,
96    {
97        let tag_offset = in_out
98            .len()
99            .checked_sub(TAG_LEN)
100            .ok_or(error::Unspecified)?;
101
102        // Split the tag off the end of `in_out`.
103        let (in_out, received_tag) = in_out.split_at_mut(tag_offset);
104        let received_tag = (*received_tag).try_into()?;
105        let ciphertext = ciphertext_and_tag;
106
107        self.open_in_place_separate_tag(nonce, aad, received_tag, in_out, ciphertext)
108    }
109
110    /// Like [`super::SealingKey::seal_in_place_append_tag()`], except it
111    /// accepts an arbitrary nonce.
112    ///
113    /// `nonce` must be unique for every use of the key to seal data.
114    #[inline]
115    pub fn seal_in_place_append_tag<A, InOut>(
116        &self,
117        nonce: Nonce,
118        aad: Aad<A>,
119        in_out: &mut InOut,
120    ) -> Result<(), error::Unspecified>
121    where
122        A: AsRef<[u8]>,
123        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
124    {
125        self.seal_in_place_separate_tag(nonce, aad, in_out.as_mut())
126            .map(|tag| in_out.extend(tag.as_ref()))
127    }
128
129    /// Like `super::SealingKey::seal_in_place_separate_tag()`, except it
130    /// accepts an arbitrary nonce.
131    ///
132    /// `nonce` must be unique for every use of the key to seal data.
133    #[inline]
134    pub fn seal_in_place_separate_tag<A>(
135        &self,
136        nonce: Nonce,
137        aad: Aad<A>,
138        in_out: &mut [u8],
139    ) -> Result<Tag, error::Unspecified>
140    where
141        A: AsRef<[u8]>,
142    {
143        seal_in_place_separate_tag_(self, nonce, Aad::from(aad.as_ref()), in_out)
144    }
145
146    /// The key's AEAD algorithm.
147    #[inline]
148    pub fn algorithm(&self) -> &'static Algorithm {
149        self.algorithm
150    }
151
152    pub(super) fn fmt_debug(
153        &self,
154        type_name: &'static str,
155        f: &mut core::fmt::Formatter,
156    ) -> Result<(), core::fmt::Error> {
157        f.debug_struct(type_name)
158            .field("algorithm", &self.algorithm())
159            .finish()
160    }
161}
162
163fn open_within_<'in_out>(
164    key: &LessSafeKey,
165    nonce: Nonce,
166    aad: Aad<&[u8]>,
167    received_tag: Tag,
168    in_out: &'in_out mut [u8],
169    src: RangeFrom<usize>,
170) -> Result<&'in_out mut [u8], error::Unspecified> {
171    let ciphertext_len = in_out.get(src.clone()).ok_or(error::Unspecified)?.len();
172
173    let Tag(calculated_tag) =
174        (key.algorithm.open)(&key.inner, nonce, aad, in_out, src, cpu::features())?;
175
176    if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag.as_ref())
177        .is_err()
178    {
179        // Zero out the plaintext so that it isn't accidentally leaked or used
180        // after verification fails. It would be safest if we could check the
181        // tag before decrypting, but some `open` implementations interleave
182        // authentication with decryption for performance.
183        for b in &mut in_out[..ciphertext_len] {
184            *b = 0;
185        }
186        return Err(error::Unspecified);
187    }
188
189    // `ciphertext_len` is also the plaintext length.
190    Ok(&mut in_out[..ciphertext_len])
191}
192
193#[inline]
194pub(super) fn seal_in_place_separate_tag_(
195    key: &LessSafeKey,
196    nonce: Nonce,
197    aad: Aad<&[u8]>,
198    in_out: &mut [u8],
199) -> Result<Tag, error::Unspecified> {
200    (key.algorithm.seal)(&key.inner, nonce, aad, in_out, cpu::features())
201}
202
203impl core::fmt::Debug for LessSafeKey {
204    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
205        self.fmt_debug("LessSafeKey", f)
206    }
207}