ring/aead/
algorithm.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 AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 crate::{
16    bb, cpu,
17    error::{self, InputTooLongError},
18    hkdf,
19};
20use core::ops::RangeFrom;
21
22use super::{
23    aes, aes_gcm, chacha20_poly1305,
24    nonce::{Nonce, NONCE_LEN},
25    overlapping::{IndexError, Overlapping},
26    Aad, KeyInner, Tag, TAG_LEN,
27};
28
29impl hkdf::KeyType for &'static Algorithm {
30    #[inline]
31    fn len(&self) -> usize {
32        self.key_len()
33    }
34}
35
36/// An AEAD Algorithm.
37pub struct Algorithm {
38    init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
39
40    seal: fn(
41        key: &KeyInner,
42        nonce: Nonce,
43        aad: Aad<&[u8]>,
44        in_out: &mut [u8],
45        cpu_features: cpu::Features,
46    ) -> Result<Tag, error::Unspecified>,
47    open: fn(
48        key: &KeyInner,
49        nonce: Nonce,
50        aad: Aad<&[u8]>,
51        in_out: &mut [u8],
52        src: RangeFrom<usize>,
53        cpu_features: cpu::Features,
54    ) -> Result<Tag, error::Unspecified>,
55
56    key_len: usize,
57    id: AlgorithmID,
58}
59
60impl Algorithm {
61    /// The length of the key.
62    #[inline(always)]
63    pub fn key_len(&self) -> usize {
64        self.key_len
65    }
66
67    /// The length of a tag.
68    ///
69    /// See also `MAX_TAG_LEN`.
70    #[inline(always)]
71    pub fn tag_len(&self) -> usize {
72        TAG_LEN
73    }
74
75    /// The length of the nonces.
76    #[inline(always)]
77    pub fn nonce_len(&self) -> usize {
78        NONCE_LEN
79    }
80
81    pub(super) fn new_key(
82        &self,
83        key_bytes: &[u8],
84        cpu_features: cpu::Features,
85    ) -> Result<KeyInner, error::Unspecified> {
86        (self.init)(key_bytes, cpu_features)
87    }
88
89    pub(super) fn open_within<'io>(
90        &self,
91        key: &KeyInner,
92        nonce: Nonce,
93        aad: Aad<&[u8]>,
94        received_tag: Tag,
95        in_out: &'io mut [u8],
96        src: RangeFrom<usize>,
97        cpu_features: cpu::Features,
98    ) -> Result<&'io mut [u8], error::Unspecified> {
99        let ciphertext_len = in_out.get(src.clone()).ok_or(error::Unspecified)?.len();
100
101        let Tag(calculated_tag) = (self.open)(key, nonce, aad, in_out, src, cpu_features)?;
102
103        if bb::verify_slices_are_equal(calculated_tag.as_ref(), received_tag.as_ref()).is_err() {
104            // Zero out the plaintext so that it isn't accidentally leaked or used
105            // after verification fails. It would be safest if we could check the
106            // tag before decrypting, but some `open` implementations interleave
107            // authentication with decryption for performance.
108            for b in &mut in_out[..ciphertext_len] {
109                *b = 0;
110            }
111            return Err(error::Unspecified);
112        }
113
114        // `ciphertext_len` is also the plaintext length.
115        Ok(&mut in_out[..ciphertext_len])
116    }
117
118    #[inline]
119    pub(super) fn seal(
120        &self,
121        key: &KeyInner,
122        nonce: Nonce,
123        aad: Aad<&[u8]>,
124        in_out: &mut [u8],
125        cpu_features: cpu::Features,
126    ) -> Result<Tag, error::Unspecified> {
127        (self.seal)(key, nonce, aad, in_out, cpu_features)
128    }
129}
130
131derive_debug_via_id!(Algorithm);
132
133#[derive(Debug, Eq, PartialEq)]
134pub(super) enum AlgorithmID {
135    AES_128_GCM,
136    AES_256_GCM,
137    CHACHA20_POLY1305,
138}
139
140impl PartialEq for Algorithm {
141    fn eq(&self, other: &Self) -> bool {
142        self.id == other.id
143    }
144}
145
146impl Eq for Algorithm {}
147
148/// AES-128 in GCM mode with 128-bit tags and 96 bit nonces.
149pub static AES_128_GCM: Algorithm = Algorithm {
150    key_len: aes::AES_128_KEY_LEN,
151    init: aes_gcm_init_128,
152    seal: aes_gcm_seal,
153    open: aes_gcm_open,
154    id: AlgorithmID::AES_128_GCM,
155};
156
157/// AES-256 in GCM mode with 128-bit tags and 96 bit nonces.
158pub static AES_256_GCM: Algorithm = Algorithm {
159    key_len: aes::AES_256_KEY_LEN,
160    init: aes_gcm_init_256,
161    seal: aes_gcm_seal,
162    open: aes_gcm_open,
163    id: AlgorithmID::AES_256_GCM,
164};
165
166fn aes_gcm_init_128(
167    key: &[u8],
168    cpu_features: cpu::Features,
169) -> Result<KeyInner, error::Unspecified> {
170    let key = key.try_into().map_err(|_| error::Unspecified)?;
171    Ok(KeyInner::AesGcm(aes_gcm::Key::new(
172        aes::KeyBytes::AES_128(key),
173        cpu_features,
174    )?))
175}
176
177fn aes_gcm_init_256(
178    key: &[u8],
179    cpu_features: cpu::Features,
180) -> Result<KeyInner, error::Unspecified> {
181    let key = key.try_into().map_err(|_| error::Unspecified)?;
182    Ok(KeyInner::AesGcm(aes_gcm::Key::new(
183        aes::KeyBytes::AES_256(key),
184        cpu_features,
185    )?))
186}
187
188fn aes_gcm_seal(
189    key: &KeyInner,
190    nonce: Nonce,
191    aad: Aad<&[u8]>,
192    in_out: &mut [u8],
193    _cpu_features: cpu::Features,
194) -> Result<Tag, error::Unspecified> {
195    let key = match key {
196        KeyInner::AesGcm(key) => key,
197        _ => unreachable!(),
198    };
199    aes_gcm::seal(key, nonce, aad, in_out)
200}
201
202pub(super) fn aes_gcm_open(
203    key: &KeyInner,
204    nonce: Nonce,
205    aad: Aad<&[u8]>,
206    in_out: &mut [u8],
207    src: RangeFrom<usize>,
208    _cpu_features: cpu::Features,
209) -> Result<Tag, error::Unspecified> {
210    let key = match key {
211        KeyInner::AesGcm(key) => key,
212        _ => unreachable!(),
213    };
214    aes_gcm::open(key, nonce, aad, in_out, src)
215}
216
217/// ChaCha20-Poly1305 as described in [RFC 8439].
218///
219/// The keys are 256 bits long and the nonces are 96 bits long.
220///
221/// [RFC 8439]: https://tools.ietf.org/html/rfc8439
222pub static CHACHA20_POLY1305: Algorithm = Algorithm {
223    key_len: chacha20_poly1305::KEY_LEN,
224    init: chacha20_poly1305_init,
225    seal: chacha20_poly1305_seal,
226    open: chacha20_poly1305_open,
227    id: AlgorithmID::CHACHA20_POLY1305,
228};
229
230/// Copies |key| into |ctx_buf|.
231fn chacha20_poly1305_init(
232    key: &[u8],
233    _cpu_features: cpu::Features,
234) -> Result<KeyInner, error::Unspecified> {
235    let key: [u8; chacha20_poly1305::KEY_LEN] = key.try_into()?;
236    Ok(KeyInner::ChaCha20Poly1305(chacha20_poly1305::Key::new(key)))
237}
238
239fn chacha20_poly1305_seal(
240    key: &KeyInner,
241    nonce: Nonce,
242    aad: Aad<&[u8]>,
243    in_out: &mut [u8],
244    cpu_features: cpu::Features,
245) -> Result<Tag, error::Unspecified> {
246    let key = match key {
247        KeyInner::ChaCha20Poly1305(key) => key,
248        _ => unreachable!(),
249    };
250    chacha20_poly1305::seal(key, nonce, aad, in_out, cpu_features)
251        .map_err(error::erase::<InputTooLongError>)
252}
253
254fn chacha20_poly1305_open(
255    key: &KeyInner,
256    nonce: Nonce,
257    aad: Aad<&[u8]>,
258    in_out: &mut [u8],
259    src: RangeFrom<usize>,
260    cpu_features: cpu::Features,
261) -> Result<Tag, error::Unspecified> {
262    let key = match key {
263        KeyInner::ChaCha20Poly1305(key) => key,
264        _ => unreachable!(),
265    };
266    let in_out = Overlapping::new(in_out, src).map_err(error::erase::<IndexError>)?;
267    chacha20_poly1305::open(key, nonce, aad, in_out, cpu_features)
268        .map_err(error::erase::<InputTooLongError>)
269}