blob: a7d03b931151b98894f244e881cb5a6cbe0fa40a [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
26/// Magic value indicating that the default tag length for an AEAD should be used to
27/// initialize `AeadCtx`.
28const AEAD_DEFAULT_TAG_LENGTH: usize = EVP_AEAD_DEFAULT_TAG_LENGTH as usize;
29
30/// Represents an AEAD algorithm.
31#[derive(Clone, Copy, Debug)]
32pub struct Aead(&'static EVP_AEAD);
33
34impl Aead {
35 /// This is AES-256 in Galois Counter Mode.
36 /// AES-GCM should only be used with 12-byte (96-bit) nonces as suggested in the
37 /// BoringSSL spec:
38 ///
39 /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html
40 pub fn aes_256_gcm() -> Self {
41 // SAFETY: This function does not access any Rust variables and simply returns
42 // a pointer to the static variable in BoringSSL.
43 let p = unsafe { EVP_aead_aes_256_gcm() };
44 // SAFETY: The returned pointer should always be valid and points to a static
45 // `EVP_AEAD`.
46 Self(unsafe { &*p })
47 }
48
49 /// Returns the maximum number of additional bytes added by the act of sealing data.
50 pub fn max_overhead(&self) -> usize {
51 // SAFETY: This function only reads from self.
52 unsafe { EVP_AEAD_max_overhead(self.0) }
53 }
54
55 /// Returns the length, in bytes, of the per-message nonce.
56 pub fn nonce_length(&self) -> usize {
57 // SAFETY: This function only reads from self.
58 unsafe { EVP_AEAD_nonce_length(self.0) }
59 }
60}
61
62/// Represents an AEAD algorithm configuration.
63pub struct AeadCtx {
64 ctx: NonNull<EVP_AEAD_CTX>,
65 aead: Aead,
66}
67
68impl Drop for AeadCtx {
69 fn drop(&mut self) {
70 // SAFETY: It is safe because the pointer has been created with `EVP_AEAD_CTX_new`
71 // and isn't used after this.
72 unsafe { EVP_AEAD_CTX_free(self.ctx.as_ptr()) }
73 }
74}
75
76impl AeadCtx {
77 /// Creates a new `AeadCtx` with the given `Aead` algorithm, `key` and `tag_len`.
78 ///
79 /// The default tag length will be used if `tag_len` is None.
80 pub fn new(aead: Aead, key: &[u8], tag_len: Option<usize>) -> Result<Self> {
81 let tag_len = tag_len.unwrap_or(AEAD_DEFAULT_TAG_LENGTH);
82 // SAFETY: This function only reads the given data and the returned pointer is
83 // checked below.
84 let ctx = unsafe { EVP_AEAD_CTX_new(aead.0, key.as_ptr(), key.len(), tag_len) };
85 let ctx = NonNull::new(ctx).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_new))?;
86 Ok(Self { ctx, aead })
87 }
88
89 /// Encrypts and authenticates `data` and writes the result to `out`.
90 /// The `out` length should be at least the `data` length plus the `max_overhead` of the
91 /// `aead` and the length of `nonce` should match the `nonce_length` of the `aead`.
92 /// Otherwise, an error will be returned.
93 ///
94 /// The output is returned as a subslice of `out`.
95 pub fn seal<'b>(
96 &self,
97 data: &[u8],
98 nonce: &[u8],
99 ad: &[u8],
100 out: &'b mut [u8],
101 ) -> Result<&'b [u8]> {
102 let mut out_len = 0;
103 // SAFETY: Only reads from/writes to the provided slices.
104 let ret = unsafe {
105 EVP_AEAD_CTX_seal(
106 self.ctx.as_ptr(),
107 out.as_mut_ptr(),
108 &mut out_len,
109 out.len(),
110 nonce.as_ptr(),
111 nonce.len(),
112 data.as_ptr(),
113 data.len(),
114 ad.as_ptr(),
115 ad.len(),
116 )
117 };
118 check_int_result(ret, ApiName::EVP_AEAD_CTX_seal)?;
119 out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_seal))
120 }
121
122 /// Authenticates `data` and decrypts it to `out`.
123 /// The `out` length should be at least the `data` length, and the length of `nonce` should
124 /// match the `nonce_length` of the `aead`.
125 /// Otherwise, an error will be returned.
126 ///
127 /// The output is returned as a subslice of `out`.
128 pub fn open<'b>(
129 &self,
130 data: &[u8],
131 nonce: &[u8],
132 ad: &[u8],
133 out: &'b mut [u8],
134 ) -> Result<&'b [u8]> {
135 let mut out_len = 0;
136 // SAFETY: Only reads from/writes to the provided slices.
137 // `data` and `out` are checked to be non-alias internally.
138 let ret = unsafe {
139 EVP_AEAD_CTX_open(
140 self.ctx.as_ptr(),
141 out.as_mut_ptr(),
142 &mut out_len,
143 out.len(),
144 nonce.as_ptr(),
145 nonce.len(),
146 data.as_ptr(),
147 data.len(),
148 ad.as_ptr(),
149 ad.len(),
150 )
151 };
152 check_int_result(ret, ApiName::EVP_AEAD_CTX_open)?;
153 out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_open))
154 }
155
156 /// Returns the `Aead` represented by this `AeadCtx`.
157 pub fn aead(&self) -> Aead {
158 self.aead
159 }
160}