• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
17 use core::convert::AsRef;
18 use core::ffi::{c_char, c_int, CStr};
19 use core::fmt;
20 use core::mem::MaybeUninit;
21 use core::num::NonZeroU32;
22 use core::ptr;
23 
24 use crate::cstr;
25 
26 use bssl_ffi::CRYPTO_library_init;
27 use bssl_ffi::ERR_get_error_line;
28 use bssl_ffi::ERR_lib_error_string;
29 use bssl_ffi::ERR_reason_error_string;
30 use bssl_ffi::EVP_AEAD_CTX_aead;
31 use bssl_ffi::EVP_AEAD_CTX_init;
32 use bssl_ffi::EVP_AEAD_CTX_open;
33 use bssl_ffi::EVP_AEAD_CTX_seal;
34 use bssl_ffi::EVP_AEAD_max_overhead;
35 use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
36 use bssl_ffi::EVP_sha512;
37 use bssl_ffi::EVP_AEAD;
38 use bssl_ffi::EVP_AEAD_CTX;
39 use bssl_ffi::HKDF;
40 
41 #[derive(Debug)]
42 pub struct Error {
43     packed: NonZeroU32,
44     file: Option<&'static CStr>,
45     line: c_int,
46 }
47 
48 impl Error {
get() -> Option<Self>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 
packed_value(&self) -> u3266     fn packed_value(&self) -> u32 {
67         self.packed.get()
68     }
69 
library_name(&self) -> Option<&'static CStr>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 
reason(&self) -> Option<&'static CStr>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 
85 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result86     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87         let packed = self.packed_value();
88         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();
91         let line = self.line;
92 
93         write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
94     }
95 }
96 
97 #[derive(Copy, Clone)]
98 pub struct ErrorIterator {}
99 
100 impl Iterator for ErrorIterator {
101     type Item = Error;
102 
next(&mut self) -> Option<Self::Item>103     fn next(&mut self) -> Option<Self::Item> {
104         Self::Item::get()
105     }
106 }
107 
108 pub type Result<T> = core::result::Result<T, ErrorIterator>;
109 
110 #[repr(transparent)]
111 pub struct Aead(EVP_AEAD);
112 
113 impl Aead {
aes_256_gcm_randnonce() -> Option<&'static Self>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 
max_overhead(&self) -> usize125     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)]
132 pub struct AeadCtx(EVP_AEAD_CTX);
133 
134 impl AeadCtx {
new_aes_256_gcm_randnonce(key: &[u8]) -> Result<Self>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 
new(aead: &'static Aead, key: &[u8]) -> Result<Self>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 
aead(&self) -> Option<&'static Aead>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 
open<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]>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 
seal<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]>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 ///
251 /// 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.
as_static_cstr(p: *const c_char) -> Option<&'static CStr>253 unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
254     if p.is_null() {
255         None
256     } else {
257         // Safety: Safe given the requirements of this function.
258         Some(unsafe { CStr::from_ptr(p) })
259     }
260 }
261 
262 impl AsRef<EVP_AEAD> for Aead {
as_ref(&self) -> &EVP_AEAD263     fn as_ref(&self) -> &EVP_AEAD {
264         &self.0
265     }
266 }
267 
268 impl AsRef<EVP_AEAD_CTX> for AeadCtx {
as_ref(&self) -> &EVP_AEAD_CTX269     fn as_ref(&self) -> &EVP_AEAD_CTX {
270         &self.0
271     }
272 }
273 
hkdf_sh512<const N: usize>(secret: &[u8], salt: &[u8], info: &[u8]) -> Result<[u8; N]>274 pub 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 }
303 
init()304 pub fn init() {
305     // SAFETY - Configures the internal state of the library - may be called multiple times.
306     unsafe { CRYPTO_library_init() }
307 }
308