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