blob: d607bee3aac4971ea3277a0517acef986ca030a7 [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 crate::cstr;
25
26use bssl_ffi::CRYPTO_library_init;
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000027use bssl_ffi::ERR_get_error_line;
28use bssl_ffi::ERR_lib_error_string;
29use bssl_ffi::ERR_reason_error_string;
30use bssl_ffi::EVP_AEAD_CTX_aead;
31use bssl_ffi::EVP_AEAD_CTX_init;
32use bssl_ffi::EVP_AEAD_CTX_open;
33use bssl_ffi::EVP_AEAD_CTX_seal;
34use bssl_ffi::EVP_AEAD_max_overhead;
35use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
36use bssl_ffi::EVP_sha512;
37use bssl_ffi::EVP_AEAD;
38use bssl_ffi::EVP_AEAD_CTX;
39use bssl_ffi::HKDF;
40
41#[derive(Debug)]
42pub struct Error {
43 packed: NonZeroU32,
44 file: Option<&'static CStr>,
45 line: c_int,
46}
47
48impl Error {
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
66 fn packed_value(&self) -> u32 {
67 self.packed.get()
68 }
69
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
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
85impl fmt::Display for Error {
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000087 let packed = self.packed_value();
Jiyong Parkb87f3302023-03-21 10:03:11 +090088 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();
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000091 let line = self.line;
92
93 write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
94 }
95}
96
97#[derive(Copy, Clone)]
98pub struct ErrorIterator {}
99
100impl Iterator for ErrorIterator {
101 type Item = Error;
102
103 fn next(&mut self) -> Option<Self::Item> {
104 Self::Item::get()
105 }
106}
107
108pub type Result<T> = core::result::Result<T, ErrorIterator>;
109
110#[repr(transparent)]
111pub struct Aead(EVP_AEAD);
112
113impl Aead {
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
125 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)]
132pub struct AeadCtx(EVP_AEAD_CTX);
133
134impl AeadCtx {
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
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
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
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
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///
Alan Stokesa0e42962023-04-14 17:59:50 +0100251/// 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.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000253unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
254 if p.is_null() {
255 None
256 } else {
Alan Stokesa0e42962023-04-14 17:59:50 +0100257 // Safety: Safe given the requirements of this function.
258 Some(unsafe { CStr::from_ptr(p) })
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000259 }
260}
261
262impl AsRef<EVP_AEAD> for Aead {
263 fn as_ref(&self) -> &EVP_AEAD {
264 &self.0
265 }
266}
267
268impl AsRef<EVP_AEAD_CTX> for AeadCtx {
269 fn as_ref(&self) -> &EVP_AEAD_CTX {
270 &self.0
271 }
272}
273
274pub 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}
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100303
304pub fn init() {
305 // SAFETY - Configures the internal state of the library - may be called multiple times.
306 unsafe { CRYPTO_library_init() }
307}