blob: 74bde84444d157123eff17fc02e8f2deaf1c37eb [file] [log] [blame]
Alice Wang69b088f2023-09-27 12:54:05 +00001// Copyright 2023, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Wrappers of the AEAD functions in BoringSSL aead.h.
16
17use crate::util::{check_int_result, to_call_failed_error};
18use bssl_avf_error::{ApiName, Result};
19use bssl_ffi::{
20 EVP_AEAD_CTX_free, EVP_AEAD_CTX_new, EVP_AEAD_CTX_open, EVP_AEAD_CTX_seal,
21 EVP_AEAD_max_overhead, EVP_AEAD_nonce_length, EVP_aead_aes_256_gcm, EVP_AEAD, EVP_AEAD_CTX,
22 EVP_AEAD_DEFAULT_TAG_LENGTH,
23};
24use core::ptr::NonNull;
25
Alice Wang8b8e6e62023-10-02 09:10:13 +000026/// BoringSSL spec recommends to use 12-byte nonces.
27///
28/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_aead_aes_256_gcm
29pub const AES_GCM_NONCE_LENGTH: usize = 12;
30
Alice Wang69b088f2023-09-27 12:54:05 +000031/// Magic value indicating that the default tag length for an AEAD should be used to
32/// initialize `AeadCtx`.
33const AEAD_DEFAULT_TAG_LENGTH: usize = EVP_AEAD_DEFAULT_TAG_LENGTH as usize;
34
35/// Represents an AEAD algorithm.
36#[derive(Clone, Copy, Debug)]
37pub struct Aead(&'static EVP_AEAD);
38
39impl Aead {
40 /// This is AES-256 in Galois Counter Mode.
41 /// AES-GCM should only be used with 12-byte (96-bit) nonces as suggested in the
42 /// BoringSSL spec:
43 ///
44 /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html
45 pub fn aes_256_gcm() -> Self {
46 // SAFETY: This function does not access any Rust variables and simply returns
47 // a pointer to the static variable in BoringSSL.
48 let p = unsafe { EVP_aead_aes_256_gcm() };
49 // SAFETY: The returned pointer should always be valid and points to a static
50 // `EVP_AEAD`.
51 Self(unsafe { &*p })
52 }
53
54 /// Returns the maximum number of additional bytes added by the act of sealing data.
55 pub fn max_overhead(&self) -> usize {
56 // SAFETY: This function only reads from self.
57 unsafe { EVP_AEAD_max_overhead(self.0) }
58 }
59
60 /// Returns the length, in bytes, of the per-message nonce.
61 pub fn nonce_length(&self) -> usize {
62 // SAFETY: This function only reads from self.
63 unsafe { EVP_AEAD_nonce_length(self.0) }
64 }
65}
66
67/// Represents an AEAD algorithm configuration.
68pub struct AeadCtx {
69 ctx: NonNull<EVP_AEAD_CTX>,
70 aead: Aead,
71}
72
73impl Drop for AeadCtx {
74 fn drop(&mut self) {
75 // SAFETY: It is safe because the pointer has been created with `EVP_AEAD_CTX_new`
76 // and isn't used after this.
77 unsafe { EVP_AEAD_CTX_free(self.ctx.as_ptr()) }
78 }
79}
80
81impl AeadCtx {
82 /// Creates a new `AeadCtx` with the given `Aead` algorithm, `key` and `tag_len`.
83 ///
84 /// The default tag length will be used if `tag_len` is None.
85 pub fn new(aead: Aead, key: &[u8], tag_len: Option<usize>) -> Result<Self> {
86 let tag_len = tag_len.unwrap_or(AEAD_DEFAULT_TAG_LENGTH);
87 // SAFETY: This function only reads the given data and the returned pointer is
88 // checked below.
89 let ctx = unsafe { EVP_AEAD_CTX_new(aead.0, key.as_ptr(), key.len(), tag_len) };
90 let ctx = NonNull::new(ctx).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_new))?;
91 Ok(Self { ctx, aead })
92 }
93
94 /// Encrypts and authenticates `data` and writes the result to `out`.
95 /// The `out` length should be at least the `data` length plus the `max_overhead` of the
96 /// `aead` and the length of `nonce` should match the `nonce_length` of the `aead`.
97 /// Otherwise, an error will be returned.
98 ///
99 /// The output is returned as a subslice of `out`.
100 pub fn seal<'b>(
101 &self,
102 data: &[u8],
103 nonce: &[u8],
104 ad: &[u8],
105 out: &'b mut [u8],
106 ) -> Result<&'b [u8]> {
107 let mut out_len = 0;
108 // SAFETY: Only reads from/writes to the provided slices.
109 let ret = unsafe {
110 EVP_AEAD_CTX_seal(
111 self.ctx.as_ptr(),
112 out.as_mut_ptr(),
113 &mut out_len,
114 out.len(),
115 nonce.as_ptr(),
116 nonce.len(),
117 data.as_ptr(),
118 data.len(),
119 ad.as_ptr(),
120 ad.len(),
121 )
122 };
123 check_int_result(ret, ApiName::EVP_AEAD_CTX_seal)?;
124 out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_seal))
125 }
126
127 /// Authenticates `data` and decrypts it to `out`.
128 /// The `out` length should be at least the `data` length, and the length of `nonce` should
129 /// match the `nonce_length` of the `aead`.
130 /// Otherwise, an error will be returned.
131 ///
132 /// The output is returned as a subslice of `out`.
133 pub fn open<'b>(
134 &self,
135 data: &[u8],
136 nonce: &[u8],
137 ad: &[u8],
138 out: &'b mut [u8],
139 ) -> Result<&'b [u8]> {
140 let mut out_len = 0;
141 // SAFETY: Only reads from/writes to the provided slices.
142 // `data` and `out` are checked to be non-alias internally.
143 let ret = unsafe {
144 EVP_AEAD_CTX_open(
145 self.ctx.as_ptr(),
146 out.as_mut_ptr(),
147 &mut out_len,
148 out.len(),
149 nonce.as_ptr(),
150 nonce.len(),
151 data.as_ptr(),
152 data.len(),
153 ad.as_ptr(),
154 ad.len(),
155 )
156 };
157 check_int_result(ret, ApiName::EVP_AEAD_CTX_open)?;
158 out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_open))
159 }
160
161 /// Returns the `Aead` represented by this `AeadCtx`.
162 pub fn aead(&self) -> Aead {
163 self.aead
164 }
165}