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    constant_time, 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 constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag.as_ref())
104            .is_err()
105        {
106            // Zero out the plaintext so that it isn't accidentally leaked or used
107            // after verification fails. It would be safest if we could check the
108            // tag before decrypting, but some `open` implementations interleave
109            // authentication with decryption for performance.
110            for b in &mut in_out[..ciphertext_len] {
111                *b = 0;
112            }
113            return Err(error::Unspecified);
114        }
115
116        // `ciphertext_len` is also the plaintext length.
117        Ok(&mut in_out[..ciphertext_len])
118    }
119
120    #[inline]
121    pub(super) fn seal(
122        &self,
123        key: &KeyInner,
124        nonce: Nonce,
125        aad: Aad<&[u8]>,
126        in_out: &mut [u8],
127        cpu_features: cpu::Features,
128    ) -> Result<Tag, error::Unspecified> {
129        (self.seal)(key, nonce, aad, in_out, cpu_features)
130    }
131}
132
133derive_debug_via_id!(Algorithm);
134
135#[derive(Debug, Eq, PartialEq)]
136pub(super) enum AlgorithmID {
137    AES_128_GCM,
138    AES_256_GCM,
139    CHACHA20_POLY1305,
140}
141
142impl PartialEq for Algorithm {
143    fn eq(&self, other: &Self) -> bool {
144        self.id == other.id
145    }
146}
147
148impl Eq for Algorithm {}
149
150/// AES-128 in GCM mode with 128-bit tags and 96 bit nonces.
151pub static AES_128_GCM: Algorithm = Algorithm {
152    key_len: aes::AES_128_KEY_LEN,
153    init: aes_gcm_init_128,
154    seal: aes_gcm_seal,
155    open: aes_gcm_open,
156    id: AlgorithmID::AES_128_GCM,
157};
158
159/// AES-256 in GCM mode with 128-bit tags and 96 bit nonces.
160pub static AES_256_GCM: Algorithm = Algorithm {
161    key_len: aes::AES_256_KEY_LEN,
162    init: aes_gcm_init_256,
163    seal: aes_gcm_seal,
164    open: aes_gcm_open,
165    id: AlgorithmID::AES_256_GCM,
166};
167
168fn aes_gcm_init_128(
169    key: &[u8],
170    cpu_features: cpu::Features,
171) -> Result<KeyInner, error::Unspecified> {
172    let key = key.try_into().map_err(|_| error::Unspecified)?;
173    Ok(KeyInner::AesGcm(aes_gcm::Key::new(
174        aes::KeyBytes::AES_128(key),
175        cpu_features,
176    )?))
177}
178
179fn aes_gcm_init_256(
180    key: &[u8],
181    cpu_features: cpu::Features,
182) -> Result<KeyInner, error::Unspecified> {
183    let key = key.try_into().map_err(|_| error::Unspecified)?;
184    Ok(KeyInner::AesGcm(aes_gcm::Key::new(
185        aes::KeyBytes::AES_256(key),
186        cpu_features,
187    )?))
188}
189
190fn aes_gcm_seal(
191    key: &KeyInner,
192    nonce: Nonce,
193    aad: Aad<&[u8]>,
194    in_out: &mut [u8],
195    _cpu_features: cpu::Features,
196) -> Result<Tag, error::Unspecified> {
197    let key = match key {
198        KeyInner::AesGcm(key) => key,
199        _ => unreachable!(),
200    };
201    aes_gcm::seal(key, nonce, aad, in_out)
202}
203
204pub(super) fn aes_gcm_open(
205    key: &KeyInner,
206    nonce: Nonce,
207    aad: Aad<&[u8]>,
208    in_out: &mut [u8],
209    src: RangeFrom<usize>,
210    _cpu_features: cpu::Features,
211) -> Result<Tag, error::Unspecified> {
212    let key = match key {
213        KeyInner::AesGcm(key) => key,
214        _ => unreachable!(),
215    };
216    aes_gcm::open(key, nonce, aad, in_out, src)
217}
218
219/// ChaCha20-Poly1305 as described in [RFC 8439].
220///
221/// The keys are 256 bits long and the nonces are 96 bits long.
222///
223/// [RFC 8439]: https://tools.ietf.org/html/rfc8439
224pub static CHACHA20_POLY1305: Algorithm = Algorithm {
225    key_len: chacha20_poly1305::KEY_LEN,
226    init: chacha20_poly1305_init,
227    seal: chacha20_poly1305_seal,
228    open: chacha20_poly1305_open,
229    id: AlgorithmID::CHACHA20_POLY1305,
230};
231
232/// Copies |key| into |ctx_buf|.
233fn chacha20_poly1305_init(
234    key: &[u8],
235    _cpu_features: cpu::Features,
236) -> Result<KeyInner, error::Unspecified> {
237    let key: [u8; chacha20_poly1305::KEY_LEN] = key.try_into()?;
238    Ok(KeyInner::ChaCha20Poly1305(chacha20_poly1305::Key::new(key)))
239}
240
241fn chacha20_poly1305_seal(
242    key: &KeyInner,
243    nonce: Nonce,
244    aad: Aad<&[u8]>,
245    in_out: &mut [u8],
246    cpu_features: cpu::Features,
247) -> Result<Tag, error::Unspecified> {
248    let key = match key {
249        KeyInner::ChaCha20Poly1305(key) => key,
250        _ => unreachable!(),
251    };
252    chacha20_poly1305::seal(key, nonce, aad, in_out, cpu_features)
253        .map_err(error::erase::<InputTooLongError>)
254}
255
256fn chacha20_poly1305_open(
257    key: &KeyInner,
258    nonce: Nonce,
259    aad: Aad<&[u8]>,
260    in_out: &mut [u8],
261    src: RangeFrom<usize>,
262    cpu_features: cpu::Features,
263) -> Result<Tag, error::Unspecified> {
264    let key = match key {
265        KeyInner::ChaCha20Poly1305(key) => key,
266        _ => unreachable!(),
267    };
268    let in_out = Overlapping::new(in_out, src).map_err(error::erase::<IndexError>)?;
269    chacha20_poly1305::open(key, nonce, aad, in_out, cpu_features)
270        .map_err(error::erase::<InputTooLongError>)
271}