blob: 275de7a5ffa25268be7bef024d95b59600133915 [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
Jiyong Parkb87f3302023-03-21 10:03:11 +090017use crate::cstr;
18
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000019use core::convert::AsRef;
20use core::ffi::{c_char, c_int, CStr};
21use core::fmt;
22use core::mem::MaybeUninit;
23use core::num::NonZeroU32;
24use core::ptr;
25
26use bssl_ffi::ERR_get_error_line;
27use bssl_ffi::ERR_lib_error_string;
28use bssl_ffi::ERR_reason_error_string;
29use bssl_ffi::EVP_AEAD_CTX_aead;
30use bssl_ffi::EVP_AEAD_CTX_init;
31use bssl_ffi::EVP_AEAD_CTX_open;
32use bssl_ffi::EVP_AEAD_CTX_seal;
33use bssl_ffi::EVP_AEAD_max_overhead;
34use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
35use bssl_ffi::EVP_sha512;
36use bssl_ffi::EVP_AEAD;
37use bssl_ffi::EVP_AEAD_CTX;
38use bssl_ffi::HKDF;
39
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///
250/// The caller needs to ensure that the pointer points to a valid C string and that the C lifetime
251/// of the string is compatible with a static Rust lifetime.
252unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
253 if p.is_null() {
254 None
255 } else {
256 Some(CStr::from_ptr(p))
257 }
258}
259
260impl AsRef<EVP_AEAD> for Aead {
261 fn as_ref(&self) -> &EVP_AEAD {
262 &self.0
263 }
264}
265
266impl AsRef<EVP_AEAD_CTX> for AeadCtx {
267 fn as_ref(&self) -> &EVP_AEAD_CTX {
268 &self.0
269 }
270}
271
272pub fn hkdf_sh512<const N: usize>(secret: &[u8], salt: &[u8], info: &[u8]) -> Result<[u8; N]> {
273 let mut key = [0; N];
274 // SAFETY - The function shouldn't access any Rust variable and the returned value is accepted
275 // as a potentially NULL pointer.
276 let digest = unsafe { EVP_sha512() };
277
278 assert!(!digest.is_null());
279 // SAFETY - Only reads from/writes to the provided slices and supports digest was checked not
280 // be NULL.
281 let result = unsafe {
282 HKDF(
283 key.as_mut_ptr(),
284 key.len(),
285 digest,
286 secret.as_ptr(),
287 secret.len(),
288 salt.as_ptr(),
289 salt.len(),
290 info.as_ptr(),
291 info.len(),
292 )
293 };
294
295 if result == 1 {
296 Ok(key)
297 } else {
298 Err(ErrorIterator {})
299 }
300}