s2n_tls/init.rs
1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5 enums::FipsMode,
6 error::{Error, Fallible},
7};
8use s2n_tls_sys::*;
9use std::sync::Once;
10
11static S2N_INIT: Once = Once::new();
12
13/// # Safety
14///
15/// This function should only be called once
16unsafe fn global_init() -> Result<(), Error> {
17 mem::init()?;
18 s2n_init().into_result()?;
19 Ok(())
20}
21
22thread_local! {
23 static S2N_THREAD: Thread = {
24 S2N_INIT.call_once(|| unsafe {
25 // Safety: by using `Once` we can ensure the library is initialized once
26 global_init().expect("could not initialize s2n-tls");
27 });
28 Thread
29 };
30}
31
32struct Thread;
33
34impl Drop for Thread {
35 /// Corresponds to [s2n_cleanup].
36 fn drop(&mut self) {
37 // https://doc.rust-lang.org/std/thread/struct.LocalKey.html#platform-specific-behavior
38 // Note that a "best effort" is made to ensure that destructors for types stored in thread local storage are run, but not all platforms can guarantee that destructors will be run for all types in thread local storage.
39 let _ = unsafe { s2n_cleanup().into_result() };
40 }
41}
42
43/// Corresponds to [s2n_init].
44pub fn init() {
45 S2N_THREAD.with(|_| ());
46}
47
48/// Determines whether s2n-tls is operating in FIPS mode.
49///
50/// It is possible to enable FIPS mode by enabling the `fips` feature flag.
51///
52/// s2n-tls MUST be linked to a FIPS libcrypto and MUST be in FIPS mode in order to comply with
53/// FIPS requirements. Applications desiring FIPS compliance should use this API to ensure that
54/// s2n-tls has been properly linked with a FIPS libcrypto and has successfully entered FIPS mode.
55///
56/// Corresponds to [s2n_get_fips_mode].
57pub fn fips_mode() -> Result<FipsMode, Error> {
58 let mut fips_mode = s2n_fips_mode::FIPS_MODE_DISABLED;
59 unsafe {
60 s2n_get_fips_mode(&mut fips_mode as *mut _).into_result()?;
61 }
62 fips_mode.try_into()
63}
64
65mod mem {
66 use super::*;
67 use alloc::alloc::{alloc, dealloc, Layout};
68 use core::{ffi::c_void, mem::align_of};
69
70 /// Corresponds to [s2n_mem_set_callbacks].
71 pub unsafe fn init() -> Result<(), Error> {
72 s2n_mem_set_callbacks(
73 Some(mem_init_callback),
74 Some(mem_cleanup_callback),
75 Some(mem_malloc_callback),
76 Some(mem_free_callback),
77 )
78 .into_result()?;
79 Ok(())
80 }
81
82 unsafe extern "C" fn mem_init_callback() -> s2n_status_code::Type {
83 // no-op: the global allocator is already initialized
84 s2n_status_code::SUCCESS
85 }
86
87 unsafe extern "C" fn mem_cleanup_callback() -> s2n_status_code::Type {
88 // no-op: the global allocator is already initialized
89 s2n_status_code::SUCCESS
90 }
91
92 unsafe extern "C" fn mem_malloc_callback(
93 ptr: *mut *mut c_void,
94 requested_len: u32,
95 allocated_len: *mut u32,
96 ) -> s2n_status_code::Type {
97 let layout = if let Some(layout) = layout(requested_len) {
98 layout
99 } else {
100 return s2n_status_code::SUCCESS;
101 };
102 *ptr = alloc(layout) as *mut _;
103
104 if ptr.is_null() {
105 s2n_status_code::FAILURE
106 } else {
107 *allocated_len = requested_len;
108 s2n_status_code::SUCCESS
109 }
110 }
111
112 unsafe extern "C" fn mem_free_callback(ptr: *mut c_void, len: u32) -> s2n_status_code::Type {
113 let layout = if let Some(layout) = layout(len) {
114 layout
115 } else {
116 return s2n_status_code::FAILURE;
117 };
118
119 if !ptr.is_null() {
120 dealloc(ptr as *mut _, layout);
121 }
122
123 s2n_status_code::SUCCESS
124 }
125
126 unsafe fn layout(len: u32) -> Option<Layout> {
127 // https://linux.die.net/man/3/malloc
128 //# The malloc() and calloc() functions return a pointer to the
129 //# allocated memory, which is suitably aligned for any built-in
130 //# type.
131
132 // `max_align_t` is a type with the largest alignment of any scalar
133 // type, so aligning to its requirement will produce an alignment
134 // suitable to the C requirement for malloc.
135 const ALIGNMENT: usize = align_of::<libc::max_align_t>();
136
137 // * align must not be zero,
138 //
139 // * align must be a power of two,
140 //
141 // * size, when rounded up to the nearest multiple of align, must not overflow (i.e., the rounded value must be less than or equal to usize::MAX).
142
143 Layout::from_size_align(len as usize, ALIGNMENT).ok()
144 }
145}