Skip to main content

bssl_crypto/
lib.rs

1// Copyright 2023 The BoringSSL Authors
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//     https://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#![deny(
16    missing_docs,
17    unsafe_op_in_unsafe_fn,
18    clippy::indexing_slicing,
19    clippy::unwrap_used,
20    clippy::panic,
21    clippy::expect_used
22)]
23#![cfg_attr(not(any(feature = "std", test)), no_std)]
24
25//! Rust BoringSSL bindings
26
27extern crate alloc;
28extern crate core;
29
30use alloc::boxed::Box;
31use alloc::vec::Vec;
32use core::ffi::c_void;
33
34#[macro_use]
35mod macros;
36
37pub mod aead;
38pub mod aes;
39
40/// Ciphers.
41pub mod cipher;
42
43pub mod digest;
44pub mod ec;
45pub mod ecdh;
46pub mod ecdsa;
47pub mod ed25519;
48pub mod hkdf;
49pub mod hmac;
50pub mod hpke;
51pub mod mldsa;
52pub mod mlkem;
53pub mod pkcs8;
54pub mod rsa;
55pub mod slhdsa;
56pub mod tls12_prf;
57pub mod x25519;
58
59mod scoped;
60
61#[cfg(test)]
62mod test_helpers;
63
64mod mem;
65pub use mem::constant_time_compare;
66
67mod rand;
68pub use rand::{rand_array, rand_bytes};
69
70/// Error type for when a "signature" (either a public-key signature or a MAC)
71/// is incorrect.
72#[derive(Debug)]
73pub struct InvalidSignatureError;
74
75/// FfiSlice exists to provide `as_ffi_ptr` on slices. Calling `as_ptr` on an
76/// empty Rust slice may return the alignment of the type, rather than NULL, as
77/// the pointer. When passing pointers into C/C++ code, that is not a valid
78/// pointer. Thus this method should be used whenever passing a pointer to a
79/// slice into BoringSSL code.
80pub trait FfiSlice<T> {
81    /// Cast the slice into a valid raw pointer for FFI.
82    fn as_ffi_ptr(&self) -> *const T;
83    /// Cast the slice into a valid `const void *` pointer for FFI.
84    fn as_ffi_void_ptr(&self) -> *const c_void {
85        self.as_ffi_ptr() as *const c_void
86    }
87}
88
89impl<T> FfiSlice<T> for [T] {
90    fn as_ffi_ptr(&self) -> *const T {
91        if self.is_empty() {
92            core::ptr::null()
93        } else {
94            self.as_ptr()
95        }
96    }
97}
98
99impl<T, const N: usize> FfiSlice<T> for [T; N] {
100    fn as_ffi_ptr(&self) -> *const T {
101        if N == 0 {
102            core::ptr::null()
103        } else {
104            self.as_ptr()
105        }
106    }
107}
108
109/// See the comment [`FfiSlice`].
110pub trait FfiMutSlice {
111    /// Cast the mutable slice as a valid `uint8_t*` pointer for FFI.
112    fn as_mut_ffi_ptr(&mut self) -> *mut u8;
113}
114
115impl FfiMutSlice for [u8] {
116    fn as_mut_ffi_ptr(&mut self) -> *mut u8 {
117        if self.is_empty() {
118            core::ptr::null_mut()
119        } else {
120            self.as_mut_ptr()
121        }
122    }
123}
124
125impl<const N: usize> FfiMutSlice for [u8; N] {
126    fn as_mut_ffi_ptr(&mut self) -> *mut u8 {
127        if N == 0 {
128            core::ptr::null_mut()
129        } else {
130            self.as_mut_ptr()
131        }
132    }
133}
134
135/// This is a helper struct which provides functions for passing slices over FFI.
136///
137/// Deprecated: use `FfiSlice` which adds less noise and lets one grep for `as_ptr`
138/// as a sign of something to check.
139struct CSlice<'a>(&'a [u8]);
140
141impl<'a> From<&'a [u8]> for CSlice<'a> {
142    fn from(value: &'a [u8]) -> Self {
143        Self(value)
144    }
145}
146
147impl CSlice<'_> {
148    /// Returns a raw pointer to the value, which is safe to pass over FFI.
149    pub fn as_ptr<T>(&self) -> *const T {
150        if self.0.is_empty() {
151            core::ptr::null()
152        } else {
153            self.0.as_ptr() as *const T
154        }
155    }
156
157    pub fn len(&self) -> usize {
158        self.0.len()
159    }
160}
161
162/// This is a helper struct which provides functions for passing mutable slices over FFI.
163///
164/// Deprecated: use `FfiMutSlice` which adds less noise and lets one grep for
165/// `as_ptr` as a sign of something to check.
166struct CSliceMut<'a>(&'a mut [u8]);
167
168impl CSliceMut<'_> {
169    /// Returns a raw pointer to the value, which is safe to pass over FFI.
170    pub fn as_mut_ptr<T>(&mut self) -> *mut T {
171        if self.0.is_empty() {
172            core::ptr::null_mut()
173        } else {
174            self.0.as_mut_ptr() as *mut T
175        }
176    }
177
178    pub fn len(&self) -> usize {
179        self.0.len()
180    }
181}
182
183impl<'a> From<&'a mut [u8]> for CSliceMut<'a> {
184    fn from(value: &'a mut [u8]) -> Self {
185        Self(value)
186    }
187}
188
189/// A helper trait implemented by types which reference borrowed foreign types.
190///
191/// # Safety
192///
193/// Implementations of `ForeignTypeRef` must guarantee the following:
194///
195/// - `Self::from_ptr(x).as_ptr() == x`
196/// - `Self::from_ptr_mut(x).as_ptr() == x`
197unsafe trait ForeignTypeRef: Sized {
198    /// The raw C type.
199    type CType;
200
201    /// Constructs a shared instance of this type from its raw type.
202    ///
203    /// # Safety
204    ///
205    /// `ptr` must be a valid, immutable, instance of the type for the `'a` lifetime.
206    #[inline]
207    unsafe fn from_ptr<'a>(ptr: *mut Self::CType) -> &'a Self {
208        debug_assert!(!ptr.is_null());
209        unsafe { &*(ptr as *mut _) }
210    }
211
212    /// Returns a raw pointer to the wrapped value.
213    #[inline]
214    fn as_ptr(&self) -> *mut Self::CType {
215        self as *const _ as *mut _
216    }
217}
218
219/// Returns a BoringSSL structure that is initialized by some function.
220/// Requires that the given function completely initializes the value.
221///
222/// (Tagged `unsafe` because a no-op argument would otherwise expose
223/// uninitialized memory.)
224unsafe fn initialized_struct<T, F>(init: F) -> T
225where
226    F: FnOnce(*mut T),
227{
228    let mut out_uninit = core::mem::MaybeUninit::<T>::uninit();
229    init(out_uninit.as_mut_ptr());
230    unsafe { out_uninit.assume_init() }
231}
232
233/// Returns a BoringSSL structure that is initialized by some function.
234/// Requires that the given function completely initializes the value or else
235/// returns false.
236///
237/// (Tagged `unsafe` because a no-op argument would otherwise expose
238/// uninitialized memory.)
239unsafe fn initialized_struct_fallible<T, F>(init: F) -> Option<T>
240where
241    F: FnOnce(*mut T) -> bool,
242{
243    let mut out_uninit = core::mem::MaybeUninit::<T>::uninit();
244    if init(out_uninit.as_mut_ptr()) {
245        Some(unsafe { out_uninit.assume_init() })
246    } else {
247        None
248    }
249}
250
251/// Returns a boxed BoringSSL structure that is initialized by some function.
252/// Requires that the given function completely initializes the value.
253///
254/// Safety: the argument must fully initialize the pointed-to `T`.
255unsafe fn initialized_boxed_struct<T, F>(init: F) -> Box<T>
256where
257    F: FnOnce(*mut T),
258{
259    let mut out_uninit = Box::new(core::mem::MaybeUninit::<T>::uninit());
260    init(out_uninit.as_mut_ptr());
261    unsafe { out_uninit.assume_init() }
262}
263
264/// Returns a boxed BoringSSL structure that is initialized by some function.
265/// Requires that the given function completely initializes the value or else
266/// returns false.
267///
268/// Safety: the argument must fully initialize the pointed-to `T` if it returns
269/// true. If it returns false then there are no safety requirements.
270unsafe fn initialized_boxed_struct_fallible<T, F>(init: F) -> Option<Box<T>>
271where
272    F: FnOnce(*mut T) -> bool,
273{
274    let mut out_uninit = Box::new(core::mem::MaybeUninit::<T>::uninit());
275    if init(out_uninit.as_mut_ptr()) {
276        Some(unsafe { out_uninit.assume_init() })
277    } else {
278        None
279    }
280}
281
282/// Wrap a closure that initializes an output buffer and return that buffer as
283/// an array. Requires that the closure fully initialize the given buffer.
284///
285/// Safety: the closure must fully initialize the array.
286unsafe fn with_output_array<const N: usize, F>(func: F) -> [u8; N]
287where
288    F: FnOnce(*mut u8, usize),
289{
290    let mut out_uninit = core::mem::MaybeUninit::<[u8; N]>::uninit();
291    let out_ptr = if N != 0 {
292        out_uninit.as_mut_ptr() as *mut u8
293    } else {
294        core::ptr::null_mut()
295    };
296    func(out_ptr, N);
297    // Safety: `func` promises to fill all of `out_uninit`.
298    unsafe { out_uninit.assume_init() }
299}
300
301/// Wrap a closure that initializes an output buffer and return that buffer as
302/// an array. The closure returns a [`core::ffi::c_int`] and, if the return value
303/// is not one, then the initialization is assumed to have failed and [None] is
304/// returned. Otherwise, this function requires that the closure fully
305/// initialize the given buffer.
306///
307/// Safety: the closure must fully initialize the array if it returns one.
308unsafe fn with_output_array_fallible<const N: usize, F>(func: F) -> Option<[u8; N]>
309where
310    F: FnOnce(*mut u8, usize) -> bool,
311{
312    let mut out_uninit = core::mem::MaybeUninit::<[u8; N]>::uninit();
313    let out_ptr = if N != 0 {
314        out_uninit.as_mut_ptr() as *mut u8
315    } else {
316        core::ptr::null_mut()
317    };
318    if func(out_ptr, N) {
319        // Safety: `func` promises to fill all of `out_uninit` if it returns one.
320        unsafe { Some(out_uninit.assume_init()) }
321    } else {
322        None
323    }
324}
325
326/// Wrap a closure that writes at most `max_output` bytes to fill a vector.
327/// It must return the number of bytes written.
328///
329/// Safety: `F` must not write more than `max_output` bytes and must return
330/// the number of bytes written.
331#[allow(clippy::unwrap_used)]
332unsafe fn with_output_vec<F>(max_output: usize, func: F) -> Vec<u8>
333where
334    F: FnOnce(*mut u8) -> usize,
335{
336    unsafe {
337        with_output_vec_fallible(max_output, |out_buf| Some(func(out_buf)))
338            // The closure cannot fail and thus neither can
339            // `with_output_array_fallible`.
340            .unwrap()
341    }
342}
343
344/// Wrap a closure that writes at most `max_output` bytes to fill a vector.
345/// If successful, it must return the number of bytes written.
346///
347/// Safety: `F` must not write more than `max_output` bytes and must return
348/// the number of bytes written or else return `None` to indicate failure.
349unsafe fn with_output_vec_fallible<F>(max_output: usize, func: F) -> Option<Vec<u8>>
350where
351    F: FnOnce(*mut u8) -> Option<usize>,
352{
353    let mut ret = Vec::with_capacity(max_output);
354    let out = ret.spare_capacity_mut();
355    let out_buf = out
356        .get_mut(0)
357        .map_or(core::ptr::null_mut(), |x| x.as_mut_ptr());
358
359    let num_written = func(out_buf)?;
360    assert!(num_written <= ret.capacity());
361
362    unsafe {
363        // Safety: `num_written` bytes have been written to.
364        ret.set_len(num_written);
365    }
366
367    Some(ret)
368}
369
370/// Buffer represents an owned chunk of memory on the BoringSSL heap.
371/// Call `as_ref()` to get a `&[u8]` from it.
372pub struct Buffer {
373    // This pointer is always allocated by BoringSSL and must be freed using
374    // `OPENSSL_free`.
375    ptr: *mut u8,
376    len: usize,
377}
378
379impl Buffer {
380    /// Safety: `ptr` must point to `len` bytes, allocated by BoringSSL.
381    unsafe fn new(ptr: *mut u8, len: usize) -> Buffer {
382        Buffer { ptr, len }
383    }
384}
385
386impl AsRef<[u8]> for Buffer {
387    fn as_ref(&self) -> &[u8] {
388        if self.len == 0 {
389            return &[];
390        }
391        // Safety: `ptr` and `len` describe a valid area of memory and `ptr`
392        // must be Rust-valid because `len` is non-zero.
393        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
394    }
395}
396
397impl Drop for Buffer {
398    fn drop(&mut self) {
399        // Safety: `ptr` is owned by this object and is on the BoringSSL heap.
400        unsafe {
401            bssl_sys::OPENSSL_free(self.ptr as *mut core::ffi::c_void);
402        }
403    }
404}
405
406fn as_cbs(buf: &[u8]) -> bssl_sys::CBS {
407    bssl_sys::CBS {
408        data: buf.as_ffi_ptr(),
409        len: buf.len(),
410    }
411}
412
413/// Calls `parse_func` with a `CBS` structure pointing at `data`.
414/// If that returns a null pointer then it returns [None].
415/// Otherwise, if there's still data left in CBS, it calls `free_func` on the
416/// pointer and returns [None]. Otherwise it returns the pointer.
417fn parse_with_cbs<T, Parse, Free>(data: &[u8], free_func: Free, parse_func: Parse) -> Option<*mut T>
418where
419    Parse: FnOnce(*mut bssl_sys::CBS) -> *mut T,
420    Free: FnOnce(*mut T),
421{
422    // Safety: type checking ensures that `cbs` is the correct size.
423    let mut cbs =
424        unsafe { initialized_struct(|cbs| bssl_sys::CBS_init(cbs, data.as_ffi_ptr(), data.len())) };
425    let ptr = parse_func(&mut cbs);
426    if ptr.is_null() {
427        return None;
428    }
429    // Safety: `cbs` is still valid after parsing.
430    if unsafe { bssl_sys::CBS_len(&cbs) } != 0 {
431        // Safety: `ptr` is still owned by this function.
432        free_func(ptr);
433        return None;
434    }
435    Some(ptr)
436}
437
438/// Calls `func` with a `CBB` pointer and returns a [Buffer] of the ultimate
439/// contents of that CBB.
440#[allow(clippy::unwrap_used)]
441pub fn cbb_to_buffer<F: FnOnce(*mut bssl_sys::CBB)>(initial_capacity: usize, func: F) -> Buffer {
442    // Safety: type checking ensures that `cbb` is the correct size.
443    let mut cbb = unsafe {
444        initialized_struct_fallible(|cbb| bssl_sys::CBB_init(cbb, initial_capacity) == 1)
445    }
446    // `CBB_init` only fails if out of memory, which isn't something that this crate handles.
447    .unwrap();
448    func(&mut cbb);
449
450    let mut ptr: *mut u8 = core::ptr::null_mut();
451    let mut len: usize = 0;
452    // `CBB_finish` only fails on programming error, which we convert into a
453    // panic.
454    assert_eq!(1, unsafe {
455        bssl_sys::CBB_finish(&mut cbb, &mut ptr, &mut len)
456    });
457
458    // Safety: `ptr` is on the BoringSSL heap and ownership is returned by
459    // `CBB_finish`.
460    unsafe { Buffer::new(ptr, len) }
461}
462
463/// Calls `func` with a `CBB` pointer that has been initialized to a vector
464/// of `len` bytes. That function must write exactly `len` bytes to the
465/// `CBB`. Those bytes are then returned as a vector.
466#[allow(clippy::unwrap_used)]
467fn cbb_to_vec<F: FnOnce(*mut bssl_sys::CBB)>(len: usize, func: F) -> Vec<u8> {
468    let mut boxed = Box::new_uninit_slice(len);
469    // Safety: type checking ensures that `cbb` is the correct size.
470    let mut cbb = unsafe {
471        initialized_struct_fallible(|cbb| {
472            bssl_sys::CBB_init_fixed(cbb, boxed.as_mut_ptr() as *mut u8, len) == 1
473        })
474    }
475    // `CBB_init_fixed` never fails and does not allocate.
476    .unwrap();
477
478    func(&mut cbb);
479
480    unsafe {
481        assert_eq!(bssl_sys::CBB_len(&cbb), len);
482        // `boxed` has been fully written, as checked on the previous line.
483        boxed.assume_init().into()
484    }
485}
486
487/// Used to prevent external implementations of internal traits.
488mod sealed {
489    pub struct SealedType;
490    pub trait Sealed {}
491}