blob: 94714c040d9ff112ed5ffa50d3870ff82fc1079a [file] [log] [blame]
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +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//! Wrapper around BoringSSL/OpenSSL symbols.
16
17use core::convert::AsRef;
18use core::ffi::{c_char, c_int, CStr};
19use core::fmt;
20use core::mem::MaybeUninit;
21use core::num::NonZeroU32;
22use core::ptr;
23
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +010024use bssl_ffi::CRYPTO_library_init;
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000025use bssl_ffi::ERR_get_error_line;
26use bssl_ffi::ERR_lib_error_string;
27use bssl_ffi::ERR_reason_error_string;
28use bssl_ffi::EVP_AEAD_CTX_aead;
29use bssl_ffi::EVP_AEAD_CTX_init;
30use bssl_ffi::EVP_AEAD_CTX_open;
31use bssl_ffi::EVP_AEAD_CTX_seal;
32use bssl_ffi::EVP_AEAD_max_overhead;
33use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
34use bssl_ffi::EVP_sha512;
35use bssl_ffi::EVP_AEAD;
36use bssl_ffi::EVP_AEAD_CTX;
37use bssl_ffi::HKDF;
Alice Wanga3971062023-06-13 11:48:53 +000038use vmbase::cstr;
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000039
40#[derive(Debug)]
41pub struct Error {
42 packed: NonZeroU32,
43 file: Option<&'static CStr>,
44 line: c_int,
45}
46
47impl Error {
48 fn get() -> Option<Self> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +010049 let mut file = ptr::null();
50 let mut line = 0;
51 // SAFETY: The function writes to the provided pointers, which are valid because they come
52 // from references. It doesn't retain them after it returns.
53 let packed = unsafe { ERR_get_error_line(&mut file, &mut line) };
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000054
55 let packed = packed.try_into().ok()?;
Andrew Walbran20bb4e42023-07-07 13:55:55 +010056 // SAFETY: Any non-NULL result is expected to point to a global const C string.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000057 let file = unsafe { as_static_cstr(file) };
58
59 Some(Self { packed, file, line })
60 }
61
62 fn packed_value(&self) -> u32 {
63 self.packed.get()
64 }
65
66 fn library_name(&self) -> Option<&'static CStr> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +010067 // SAFETY: Call to a pure function.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000068 let name = unsafe { ERR_lib_error_string(self.packed_value()) };
Andrew Walbran20bb4e42023-07-07 13:55:55 +010069 // SAFETY: Any non-NULL result is expected to point to a global const C string.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000070 unsafe { as_static_cstr(name) }
71 }
72
73 fn reason(&self) -> Option<&'static CStr> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +010074 // SAFETY: Call to a pure function.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000075 let reason = unsafe { ERR_reason_error_string(self.packed_value()) };
Andrew Walbran20bb4e42023-07-07 13:55:55 +010076 // SAFETY: Any non-NULL result is expected to point to a global const C string.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000077 unsafe { as_static_cstr(reason) }
78 }
79}
80
81impl fmt::Display for Error {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000083 let packed = self.packed_value();
Jiyong Parkb87f3302023-03-21 10:03:11 +090084 let library = self.library_name().unwrap_or(cstr!("{unknown library}")).to_str().unwrap();
85 let reason = self.reason().unwrap_or(cstr!("{unknown reason}")).to_str().unwrap();
86 let file = self.file.unwrap_or(cstr!("??")).to_str().unwrap();
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000087 let line = self.line;
88
89 write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
90 }
91}
92
93#[derive(Copy, Clone)]
94pub struct ErrorIterator {}
95
96impl Iterator for ErrorIterator {
97 type Item = Error;
98
99 fn next(&mut self) -> Option<Self::Item> {
100 Self::Item::get()
101 }
102}
103
104pub type Result<T> = core::result::Result<T, ErrorIterator>;
105
106#[repr(transparent)]
107pub struct Aead(EVP_AEAD);
108
109impl Aead {
110 pub fn aes_256_gcm_randnonce() -> Option<&'static Self> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100111 // SAFETY: Returned pointer is checked below.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000112 let aead = unsafe { EVP_aead_aes_256_gcm_randnonce() };
113 if aead.is_null() {
114 None
115 } else {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100116 // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000117 Some(unsafe { &*(aead as *const _) })
118 }
119 }
120
121 pub fn max_overhead(&self) -> usize {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100122 // SAFETY: Function should only read from self.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000123 unsafe { EVP_AEAD_max_overhead(self.as_ref() as *const _) }
124 }
125}
126
127#[repr(transparent)]
128pub struct AeadCtx(EVP_AEAD_CTX);
129
130impl AeadCtx {
131 pub fn new_aes_256_gcm_randnonce(key: &[u8]) -> Result<Self> {
132 let aead = Aead::aes_256_gcm_randnonce().unwrap();
133
134 Self::new(aead, key)
135 }
136
137 fn new(aead: &'static Aead, key: &[u8]) -> Result<Self> {
138 const DEFAULT_TAG_LENGTH: usize = 0;
139 let engine = ptr::null_mut(); // Use default implementation.
140 let mut ctx = MaybeUninit::zeroed();
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100141 // SAFETY: Initialize the EVP_AEAD_CTX with const pointers to the AEAD and key.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000142 let result = unsafe {
143 EVP_AEAD_CTX_init(
144 ctx.as_mut_ptr(),
145 aead.as_ref() as *const _,
146 key.as_ptr(),
147 key.len(),
148 DEFAULT_TAG_LENGTH,
149 engine,
150 )
151 };
152
153 if result == 1 {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100154 // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000155 Ok(Self(unsafe { ctx.assume_init() }))
156 } else {
157 Err(ErrorIterator {})
158 }
159 }
160
161 pub fn aead(&self) -> Option<&'static Aead> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100162 // SAFETY: The function should only read from self.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000163 let aead = unsafe { EVP_AEAD_CTX_aead(self.as_ref() as *const _) };
164 if aead.is_null() {
165 None
166 } else {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100167 // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000168 Some(unsafe { &*(aead as *const _) })
169 }
170 }
171
172 pub fn open<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
173 let nonce = ptr::null_mut();
174 let nonce_len = 0;
175 let ad = ptr::null_mut();
176 let ad_len = 0;
177 let mut out_len = MaybeUninit::uninit();
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100178 // SAFETY: The function should only read from self and write to out (at most the provided
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000179 // number of bytes) and out_len while reading from data (at most the provided number of
180 // bytes), ignoring any NULL input.
181 let result = unsafe {
182 EVP_AEAD_CTX_open(
183 self.as_ref() as *const _,
184 out.as_mut_ptr(),
185 out_len.as_mut_ptr(),
186 out.len(),
187 nonce,
188 nonce_len,
189 data.as_ptr(),
190 data.len(),
191 ad,
192 ad_len,
193 )
194 };
195
196 if result == 1 {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100197 // SAFETY: Any value written to out_len could be a valid usize. The value itself is
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000198 // validated as being a proper slice length by panicking in the following indexing
199 // otherwise.
200 let out_len = unsafe { out_len.assume_init() };
201 Ok(&mut out[..out_len])
202 } else {
203 Err(ErrorIterator {})
204 }
205 }
206
207 pub fn seal<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
208 let nonce = ptr::null_mut();
209 let nonce_len = 0;
210 let ad = ptr::null_mut();
211 let ad_len = 0;
212 let mut out_len = MaybeUninit::uninit();
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100213 // SAFETY: The function should only read from self and write to out (at most the provided
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000214 // number of bytes) while reading from data (at most the provided number of bytes),
215 // ignoring any NULL input.
216 let result = unsafe {
217 EVP_AEAD_CTX_seal(
218 self.as_ref() as *const _,
219 out.as_mut_ptr(),
220 out_len.as_mut_ptr(),
221 out.len(),
222 nonce,
223 nonce_len,
224 data.as_ptr(),
225 data.len(),
226 ad,
227 ad_len,
228 )
229 };
230
231 if result == 1 {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100232 // SAFETY: Any value written to out_len could be a valid usize. The value itself is
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000233 // validated as being a proper slice length by panicking in the following indexing
234 // otherwise.
235 let out_len = unsafe { out_len.assume_init() };
236 Ok(&mut out[..out_len])
237 } else {
238 Err(ErrorIterator {})
239 }
240 }
241}
242
243/// Cast a C string pointer to a static non-mutable reference.
244///
245/// # Safety
246///
Alan Stokesa0e42962023-04-14 17:59:50 +0100247/// The caller needs to ensure that the pointer is null or points to a valid C string and that the
248/// C lifetime of the string is compatible with a static Rust lifetime.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000249unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
250 if p.is_null() {
251 None
252 } else {
Alan Stokesa0e42962023-04-14 17:59:50 +0100253 // Safety: Safe given the requirements of this function.
254 Some(unsafe { CStr::from_ptr(p) })
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000255 }
256}
257
258impl AsRef<EVP_AEAD> for Aead {
259 fn as_ref(&self) -> &EVP_AEAD {
260 &self.0
261 }
262}
263
264impl AsRef<EVP_AEAD_CTX> for AeadCtx {
265 fn as_ref(&self) -> &EVP_AEAD_CTX {
266 &self.0
267 }
268}
269
270pub fn hkdf_sh512<const N: usize>(secret: &[u8], salt: &[u8], info: &[u8]) -> Result<[u8; N]> {
271 let mut key = [0; N];
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100272 // SAFETY: The function shouldn't access any Rust variable and the returned value is accepted
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000273 // as a potentially NULL pointer.
274 let digest = unsafe { EVP_sha512() };
275
276 assert!(!digest.is_null());
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100277 // SAFETY: Only reads from/writes to the provided slices and supports digest was checked not
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000278 // be NULL.
279 let result = unsafe {
280 HKDF(
281 key.as_mut_ptr(),
282 key.len(),
283 digest,
284 secret.as_ptr(),
285 secret.len(),
286 salt.as_ptr(),
287 salt.len(),
288 info.as_ptr(),
289 info.len(),
290 )
291 };
292
293 if result == 1 {
294 Ok(key)
295 } else {
296 Err(ErrorIterator {})
297 }
298}
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100299
300pub fn init() {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100301 // SAFETY: Configures the internal state of the library - may be called multiple times.
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100302 unsafe { CRYPTO_library_init() }
303}