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