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: usize = in_out.get(src.clone()).ok_or(err:error::Unspecified)?.len();
172
173 let Tag(calculated_tag: [u8; 16]) =
174 (key.algorithm.open)(&key.inner, nonce, aad, in_out, src, cpu::features())?;
175
176 if constant_timeResult<(), Unspecified>::verify_slices_are_equal(a:calculated_tag.as_ref(), b: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: &mut u8 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(type_name:"LessSafeKey", f)
206 }
207}
208