1 // 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 //! Wrapper around BoringSSL/OpenSSL symbols.
16
17 use core::convert::AsRef;
18 use core::ffi::{c_char, c_int, CStr};
19 use core::fmt;
20 use core::mem::MaybeUninit;
21 use core::num::NonZeroU32;
22 use core::ptr;
23
24 use crate::cstr;
25
26 use bssl_ffi::CRYPTO_library_init;
27 use bssl_ffi::ERR_get_error_line;
28 use bssl_ffi::ERR_lib_error_string;
29 use bssl_ffi::ERR_reason_error_string;
30 use bssl_ffi::EVP_AEAD_CTX_aead;
31 use bssl_ffi::EVP_AEAD_CTX_init;
32 use bssl_ffi::EVP_AEAD_CTX_open;
33 use bssl_ffi::EVP_AEAD_CTX_seal;
34 use bssl_ffi::EVP_AEAD_max_overhead;
35 use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
36 use bssl_ffi::EVP_sha512;
37 use bssl_ffi::EVP_AEAD;
38 use bssl_ffi::EVP_AEAD_CTX;
39 use bssl_ffi::HKDF;
40
41 #[derive(Debug)]
42 pub struct Error {
43 packed: NonZeroU32,
44 file: Option<&'static CStr>,
45 line: c_int,
46 }
47
48 impl Error {
get() -> Option<Self>49 fn get() -> Option<Self> {
50 let mut file = MaybeUninit::uninit();
51 let mut line = MaybeUninit::uninit();
52 // SAFETY - The function writes to the provided pointers, validated below.
53 let packed = unsafe { ERR_get_error_line(file.as_mut_ptr(), line.as_mut_ptr()) };
54 // SAFETY - Any possible value returned could be considered a valid *const c_char.
55 let file = unsafe { file.assume_init() };
56 // SAFETY - Any possible value returned could be considered a valid c_int.
57 let line = unsafe { line.assume_init() };
58
59 let packed = packed.try_into().ok()?;
60 // SAFETY - Any non-NULL result is expected to point to a global const C string.
61 let file = unsafe { as_static_cstr(file) };
62
63 Some(Self { packed, file, line })
64 }
65
packed_value(&self) -> u3266 fn packed_value(&self) -> u32 {
67 self.packed.get()
68 }
69
library_name(&self) -> Option<&'static CStr>70 fn library_name(&self) -> Option<&'static CStr> {
71 // SAFETY - Call to a pure function.
72 let name = unsafe { ERR_lib_error_string(self.packed_value()) };
73 // SAFETY - Any non-NULL result is expected to point to a global const C string.
74 unsafe { as_static_cstr(name) }
75 }
76
reason(&self) -> Option<&'static CStr>77 fn reason(&self) -> Option<&'static CStr> {
78 // SAFETY - Call to a pure function.
79 let reason = unsafe { ERR_reason_error_string(self.packed_value()) };
80 // SAFETY - Any non-NULL result is expected to point to a global const C string.
81 unsafe { as_static_cstr(reason) }
82 }
83 }
84
85 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87 let packed = self.packed_value();
88 let library = self.library_name().unwrap_or(cstr!("{unknown library}")).to_str().unwrap();
89 let reason = self.reason().unwrap_or(cstr!("{unknown reason}")).to_str().unwrap();
90 let file = self.file.unwrap_or(cstr!("??")).to_str().unwrap();
91 let line = self.line;
92
93 write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
94 }
95 }
96
97 #[derive(Copy, Clone)]
98 pub struct ErrorIterator {}
99
100 impl Iterator for ErrorIterator {
101 type Item = Error;
102
next(&mut self) -> Option<Self::Item>103 fn next(&mut self) -> Option<Self::Item> {
104 Self::Item::get()
105 }
106 }
107
108 pub type Result<T> = core::result::Result<T, ErrorIterator>;
109
110 #[repr(transparent)]
111 pub struct Aead(EVP_AEAD);
112
113 impl Aead {
aes_256_gcm_randnonce() -> Option<&'static Self>114 pub fn aes_256_gcm_randnonce() -> Option<&'static Self> {
115 // SAFETY - Returned pointer is checked below.
116 let aead = unsafe { EVP_aead_aes_256_gcm_randnonce() };
117 if aead.is_null() {
118 None
119 } else {
120 // SAFETY - We assume that the non-NULL value points to a valid and static EVP_AEAD.
121 Some(unsafe { &*(aead as *const _) })
122 }
123 }
124
max_overhead(&self) -> usize125 pub fn max_overhead(&self) -> usize {
126 // SAFETY - Function should only read from self.
127 unsafe { EVP_AEAD_max_overhead(self.as_ref() as *const _) }
128 }
129 }
130
131 #[repr(transparent)]
132 pub struct AeadCtx(EVP_AEAD_CTX);
133
134 impl AeadCtx {
new_aes_256_gcm_randnonce(key: &[u8]) -> Result<Self>135 pub fn new_aes_256_gcm_randnonce(key: &[u8]) -> Result<Self> {
136 let aead = Aead::aes_256_gcm_randnonce().unwrap();
137
138 Self::new(aead, key)
139 }
140
new(aead: &'static Aead, key: &[u8]) -> Result<Self>141 fn new(aead: &'static Aead, key: &[u8]) -> Result<Self> {
142 const DEFAULT_TAG_LENGTH: usize = 0;
143 let engine = ptr::null_mut(); // Use default implementation.
144 let mut ctx = MaybeUninit::zeroed();
145 // SAFETY - Initialize the EVP_AEAD_CTX with const pointers to the AEAD and key.
146 let result = unsafe {
147 EVP_AEAD_CTX_init(
148 ctx.as_mut_ptr(),
149 aead.as_ref() as *const _,
150 key.as_ptr(),
151 key.len(),
152 DEFAULT_TAG_LENGTH,
153 engine,
154 )
155 };
156
157 if result == 1 {
158 // SAFETY - We assume that the non-NULL value points to a valid and static EVP_AEAD.
159 Ok(Self(unsafe { ctx.assume_init() }))
160 } else {
161 Err(ErrorIterator {})
162 }
163 }
164
aead(&self) -> Option<&'static Aead>165 pub fn aead(&self) -> Option<&'static Aead> {
166 // SAFETY - The function should only read from self.
167 let aead = unsafe { EVP_AEAD_CTX_aead(self.as_ref() as *const _) };
168 if aead.is_null() {
169 None
170 } else {
171 // SAFETY - We assume that the non-NULL value points to a valid and static EVP_AEAD.
172 Some(unsafe { &*(aead as *const _) })
173 }
174 }
175
open<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]>176 pub fn open<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
177 let nonce = ptr::null_mut();
178 let nonce_len = 0;
179 let ad = ptr::null_mut();
180 let ad_len = 0;
181 let mut out_len = MaybeUninit::uninit();
182 // SAFETY - The function should only read from self and write to out (at most the provided
183 // number of bytes) and out_len while reading from data (at most the provided number of
184 // bytes), ignoring any NULL input.
185 let result = unsafe {
186 EVP_AEAD_CTX_open(
187 self.as_ref() as *const _,
188 out.as_mut_ptr(),
189 out_len.as_mut_ptr(),
190 out.len(),
191 nonce,
192 nonce_len,
193 data.as_ptr(),
194 data.len(),
195 ad,
196 ad_len,
197 )
198 };
199
200 if result == 1 {
201 // SAFETY - Any value written to out_len could be a valid usize. The value itself is
202 // validated as being a proper slice length by panicking in the following indexing
203 // otherwise.
204 let out_len = unsafe { out_len.assume_init() };
205 Ok(&mut out[..out_len])
206 } else {
207 Err(ErrorIterator {})
208 }
209 }
210
seal<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]>211 pub fn seal<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
212 let nonce = ptr::null_mut();
213 let nonce_len = 0;
214 let ad = ptr::null_mut();
215 let ad_len = 0;
216 let mut out_len = MaybeUninit::uninit();
217 // SAFETY - The function should only read from self and write to out (at most the provided
218 // number of bytes) while reading from data (at most the provided number of bytes),
219 // ignoring any NULL input.
220 let result = unsafe {
221 EVP_AEAD_CTX_seal(
222 self.as_ref() as *const _,
223 out.as_mut_ptr(),
224 out_len.as_mut_ptr(),
225 out.len(),
226 nonce,
227 nonce_len,
228 data.as_ptr(),
229 data.len(),
230 ad,
231 ad_len,
232 )
233 };
234
235 if result == 1 {
236 // SAFETY - Any value written to out_len could be a valid usize. The value itself is
237 // validated as being a proper slice length by panicking in the following indexing
238 // otherwise.
239 let out_len = unsafe { out_len.assume_init() };
240 Ok(&mut out[..out_len])
241 } else {
242 Err(ErrorIterator {})
243 }
244 }
245 }
246
247 /// Cast a C string pointer to a static non-mutable reference.
248 ///
249 /// # Safety
250 ///
251 /// The caller needs to ensure that the pointer is null or points to a valid C string and that the
252 /// C lifetime of the string is compatible with a static Rust lifetime.
as_static_cstr(p: *const c_char) -> Option<&'static CStr>253 unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
254 if p.is_null() {
255 None
256 } else {
257 // Safety: Safe given the requirements of this function.
258 Some(unsafe { CStr::from_ptr(p) })
259 }
260 }
261
262 impl AsRef<EVP_AEAD> for Aead {
as_ref(&self) -> &EVP_AEAD263 fn as_ref(&self) -> &EVP_AEAD {
264 &self.0
265 }
266 }
267
268 impl AsRef<EVP_AEAD_CTX> for AeadCtx {
as_ref(&self) -> &EVP_AEAD_CTX269 fn as_ref(&self) -> &EVP_AEAD_CTX {
270 &self.0
271 }
272 }
273
hkdf_sh512<const N: usize>(secret: &[u8], salt: &[u8], info: &[u8]) -> Result<[u8; N]>274 pub fn hkdf_sh512<const N: usize>(secret: &[u8], salt: &[u8], info: &[u8]) -> Result<[u8; N]> {
275 let mut key = [0; N];
276 // SAFETY - The function shouldn't access any Rust variable and the returned value is accepted
277 // as a potentially NULL pointer.
278 let digest = unsafe { EVP_sha512() };
279
280 assert!(!digest.is_null());
281 // SAFETY - Only reads from/writes to the provided slices and supports digest was checked not
282 // be NULL.
283 let result = unsafe {
284 HKDF(
285 key.as_mut_ptr(),
286 key.len(),
287 digest,
288 secret.as_ptr(),
289 secret.len(),
290 salt.as_ptr(),
291 salt.len(),
292 info.as_ptr(),
293 info.len(),
294 )
295 };
296
297 if result == 1 {
298 Ok(key)
299 } else {
300 Err(ErrorIterator {})
301 }
302 }
303
init()304 pub fn init() {
305 // SAFETY - Configures the internal state of the library - may be called multiple times.
306 unsafe { CRYPTO_library_init() }
307 }
308