blob: 8f315532a228091a8629eb2a1de53a9ac6d5a465 [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;
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000034use bssl_ffi::EVP_AEAD;
35use bssl_ffi::EVP_AEAD_CTX;
Pierre-Clément Tosi1bf532b2023-11-13 11:06:20 +000036use cstr::cstr;
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000037
38#[derive(Debug)]
39pub struct Error {
40 packed: NonZeroU32,
41 file: Option<&'static CStr>,
42 line: c_int,
43}
44
45impl Error {
46 fn get() -> Option<Self> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +010047 let mut file = ptr::null();
48 let mut line = 0;
49 // SAFETY: The function writes to the provided pointers, which are valid because they come
50 // from references. It doesn't retain them after it returns.
51 let packed = unsafe { ERR_get_error_line(&mut file, &mut line) };
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000052
53 let packed = packed.try_into().ok()?;
Andrew Walbran20bb4e42023-07-07 13:55:55 +010054 // SAFETY: Any non-NULL result is expected to point to a global const C string.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000055 let file = unsafe { as_static_cstr(file) };
56
57 Some(Self { packed, file, line })
58 }
59
60 fn packed_value(&self) -> u32 {
61 self.packed.get()
62 }
63
64 fn library_name(&self) -> Option<&'static CStr> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +010065 // SAFETY: Call to a pure function.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000066 let name = unsafe { ERR_lib_error_string(self.packed_value()) };
Andrew Walbran20bb4e42023-07-07 13:55:55 +010067 // SAFETY: Any non-NULL result is expected to point to a global const C string.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000068 unsafe { as_static_cstr(name) }
69 }
70
71 fn reason(&self) -> Option<&'static CStr> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +010072 // SAFETY: Call to a pure function.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000073 let reason = unsafe { ERR_reason_error_string(self.packed_value()) };
Andrew Walbran20bb4e42023-07-07 13:55:55 +010074 // SAFETY: Any non-NULL result is expected to point to a global const C string.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000075 unsafe { as_static_cstr(reason) }
76 }
77}
78
79impl fmt::Display for Error {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000081 let packed = self.packed_value();
Jiyong Parkb87f3302023-03-21 10:03:11 +090082 let library = self.library_name().unwrap_or(cstr!("{unknown library}")).to_str().unwrap();
83 let reason = self.reason().unwrap_or(cstr!("{unknown reason}")).to_str().unwrap();
84 let file = self.file.unwrap_or(cstr!("??")).to_str().unwrap();
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +000085 let line = self.line;
86
87 write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
88 }
89}
90
91#[derive(Copy, Clone)]
92pub struct ErrorIterator {}
93
94impl Iterator for ErrorIterator {
95 type Item = Error;
96
97 fn next(&mut self) -> Option<Self::Item> {
98 Self::Item::get()
99 }
100}
101
102pub type Result<T> = core::result::Result<T, ErrorIterator>;
103
104#[repr(transparent)]
105pub struct Aead(EVP_AEAD);
106
107impl Aead {
108 pub fn aes_256_gcm_randnonce() -> Option<&'static Self> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100109 // SAFETY: Returned pointer is checked below.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000110 let aead = unsafe { EVP_aead_aes_256_gcm_randnonce() };
111 if aead.is_null() {
112 None
113 } else {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100114 // 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 +0000115 Some(unsafe { &*(aead as *const _) })
116 }
117 }
118
119 pub fn max_overhead(&self) -> usize {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100120 // SAFETY: Function should only read from self.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000121 unsafe { EVP_AEAD_max_overhead(self.as_ref() as *const _) }
122 }
123}
124
125#[repr(transparent)]
126pub struct AeadCtx(EVP_AEAD_CTX);
127
128impl AeadCtx {
129 pub fn new_aes_256_gcm_randnonce(key: &[u8]) -> Result<Self> {
130 let aead = Aead::aes_256_gcm_randnonce().unwrap();
131
132 Self::new(aead, key)
133 }
134
135 fn new(aead: &'static Aead, key: &[u8]) -> Result<Self> {
136 const DEFAULT_TAG_LENGTH: usize = 0;
137 let engine = ptr::null_mut(); // Use default implementation.
138 let mut ctx = MaybeUninit::zeroed();
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100139 // SAFETY: Initialize the EVP_AEAD_CTX with const pointers to the AEAD and key.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000140 let result = unsafe {
141 EVP_AEAD_CTX_init(
142 ctx.as_mut_ptr(),
143 aead.as_ref() as *const _,
144 key.as_ptr(),
145 key.len(),
146 DEFAULT_TAG_LENGTH,
147 engine,
148 )
149 };
150
151 if result == 1 {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100152 // 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 +0000153 Ok(Self(unsafe { ctx.assume_init() }))
154 } else {
155 Err(ErrorIterator {})
156 }
157 }
158
159 pub fn aead(&self) -> Option<&'static Aead> {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100160 // SAFETY: The function should only read from self.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000161 let aead = unsafe { EVP_AEAD_CTX_aead(self.as_ref() as *const _) };
162 if aead.is_null() {
163 None
164 } else {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100165 // 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 +0000166 Some(unsafe { &*(aead as *const _) })
167 }
168 }
169
170 pub fn open<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
171 let nonce = ptr::null_mut();
172 let nonce_len = 0;
173 let ad = ptr::null_mut();
174 let ad_len = 0;
175 let mut out_len = MaybeUninit::uninit();
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100176 // 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 +0000177 // number of bytes) and out_len while reading from data (at most the provided number of
178 // bytes), ignoring any NULL input.
179 let result = unsafe {
180 EVP_AEAD_CTX_open(
181 self.as_ref() as *const _,
182 out.as_mut_ptr(),
183 out_len.as_mut_ptr(),
184 out.len(),
185 nonce,
186 nonce_len,
187 data.as_ptr(),
188 data.len(),
189 ad,
190 ad_len,
191 )
192 };
193
194 if result == 1 {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100195 // 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 +0000196 // validated as being a proper slice length by panicking in the following indexing
197 // otherwise.
198 let out_len = unsafe { out_len.assume_init() };
199 Ok(&mut out[..out_len])
200 } else {
201 Err(ErrorIterator {})
202 }
203 }
204
205 pub fn seal<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
206 let nonce = ptr::null_mut();
207 let nonce_len = 0;
208 let ad = ptr::null_mut();
209 let ad_len = 0;
210 let mut out_len = MaybeUninit::uninit();
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100211 // 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 +0000212 // number of bytes) while reading from data (at most the provided number of bytes),
213 // ignoring any NULL input.
214 let result = unsafe {
215 EVP_AEAD_CTX_seal(
216 self.as_ref() as *const _,
217 out.as_mut_ptr(),
218 out_len.as_mut_ptr(),
219 out.len(),
220 nonce,
221 nonce_len,
222 data.as_ptr(),
223 data.len(),
224 ad,
225 ad_len,
226 )
227 };
228
229 if result == 1 {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100230 // 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 +0000231 // validated as being a proper slice length by panicking in the following indexing
232 // otherwise.
233 let out_len = unsafe { out_len.assume_init() };
234 Ok(&mut out[..out_len])
235 } else {
236 Err(ErrorIterator {})
237 }
238 }
239}
240
241/// Cast a C string pointer to a static non-mutable reference.
242///
243/// # Safety
244///
Alan Stokesa0e42962023-04-14 17:59:50 +0100245/// The caller needs to ensure that the pointer is null or points to a valid C string and that the
246/// C lifetime of the string is compatible with a static Rust lifetime.
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000247unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
248 if p.is_null() {
249 None
250 } else {
Alan Stokesa0e42962023-04-14 17:59:50 +0100251 // Safety: Safe given the requirements of this function.
252 Some(unsafe { CStr::from_ptr(p) })
Pierre-Clément Tosi90cd4f12023-02-17 11:19:56 +0000253 }
254}
255
256impl AsRef<EVP_AEAD> for Aead {
257 fn as_ref(&self) -> &EVP_AEAD {
258 &self.0
259 }
260}
261
262impl AsRef<EVP_AEAD_CTX> for AeadCtx {
263 fn as_ref(&self) -> &EVP_AEAD_CTX {
264 &self.0
265 }
266}
267
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100268pub fn init() {
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100269 // SAFETY: Configures the internal state of the library - may be called multiple times.
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100270 unsafe { CRYPTO_library_init() }
271}