1 // Copyright 2023 Google LLC
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 //! Common externally-accessible FFI constructs which are needed
15 //! in order to define the interfaces in this crate's various modules.
16
17 use array_view::ArrayView;
18 use crypto_provider::{CryptoProvider, CryptoRng};
19 use crypto_provider_default::CryptoProviderImpl;
20 use handle_map::HandleNotPresentError;
21 use lock_adapter::stdlib::{RwLock, RwLockWriteGuard};
22 use lock_adapter::RwLock as _;
23
24 const MAX_HANDLES: u32 = u32::MAX - 1;
25
26 /// Configuration for top-level constants to be used
27 /// by the rest of the FFI which are independent of
28 /// the programming language which we ultimately
29 /// interface with at a higher level.
30 #[repr(C)]
31 pub struct CommonConfig {
32 /// The number of shards to employ in all handle-maps,
33 /// or zero if we want to use the default.
34 ///
35 /// The default number of shards will depend on whether
36 /// this crate was compiled with the `std` feature or not:
37 /// - If compiled with the `std` feature, the default number
38 /// of shards will be set to
39 /// `min(16, std::thread::available_parallelism().unwrap())`,
40 /// assuming that that call completes successfully.
41 /// - In all other cases, 16 shards will be used by default.
42 num_shards: u8,
43 }
44
45 impl Default for CommonConfig {
default() -> Self46 fn default() -> Self {
47 Self::new()
48 }
49 }
50
51 impl CommonConfig {
new() -> Self52 pub(crate) const fn new() -> Self {
53 Self { num_shards: 0 }
54 }
55 #[cfg(feature = "std")]
num_shards(&self) -> u856 pub(crate) fn num_shards(&self) -> u8 {
57 if self.num_shards == 0 {
58 match std::thread::available_parallelism() {
59 Ok(parallelism) => 16u8.min(parallelism),
60 Err(_) => 16u8,
61 }
62 } else {
63 self.num_shards
64 }
65 }
66 #[cfg(not(feature = "std"))]
num_shards(&self) -> u867 pub(crate) fn num_shards(&self) -> u8 {
68 if self.num_shards == 0 {
69 16u8
70 } else {
71 self.num_shards
72 }
73 }
set_num_shards(&mut self, num_shards: u8)74 pub(crate) fn set_num_shards(&mut self, num_shards: u8) {
75 self.num_shards = num_shards
76 }
77 }
78
default_handle_map_dimensions() -> handle_map::HandleMapDimensions79 pub(crate) fn default_handle_map_dimensions() -> handle_map::HandleMapDimensions {
80 handle_map::HandleMapDimensions {
81 num_shards: global_num_shards(),
82 max_active_handles: MAX_HANDLES,
83 }
84 }
85
86 static COMMON_CONFIG: RwLock<CommonConfig> = RwLock::new(CommonConfig::new());
87
global_num_shards() -> u888 fn global_num_shards() -> u8 {
89 COMMON_CONFIG.read().num_shards()
90 }
91
92 /// Sets an override to the number of shards to employ in the NP FFI's
93 /// internal handle-maps, which places an upper bound on the number
94 /// of writing threads which may make progress at any one time
95 /// when concurrently accessing handles of the same type.
96 ///
97 /// By default, this value will be set to 16, or in `std` environments,
98 /// the minimum of 16 and the number of available hardware threads.
99 /// A shard value override of zero will be interpreted the same
100 /// as this default.
101 ///
102 /// Setting this value will have no effect if the handle-maps for the
103 /// API have already begun being used by the client code, and any
104 /// values set will take effect upon the first usage of _any_ non-`global_config_set`
105 /// API call.
global_config_set_num_shards(num_shards: u8)106 pub fn global_config_set_num_shards(num_shards: u8) {
107 let mut config = COMMON_CONFIG.write();
108 config.set_num_shards(num_shards);
109 }
110
111 /// Holds the count of handles currently allocated for each handle type
112 #[repr(C)]
113 pub struct CurrentHandleAllocations {
114 cred_book: u32,
115 cred_slab: u32,
116 decrypted_metadata: u32,
117 v0_payload: u32,
118 legible_v1_sections: u32,
119 v0_advertisement_builder: u32,
120 v1_advertisement_builder: u32,
121 }
122
123 /// Returns the count of currently allocated handle types being held by the rust layer. Useful
124 /// for debugging, logging, and testing.
global_config_get_current_allocation_count() -> CurrentHandleAllocations125 pub fn global_config_get_current_allocation_count() -> CurrentHandleAllocations {
126 CurrentHandleAllocations {
127 cred_book: crate::credentials::credential_book::get_current_allocation_count(),
128 cred_slab: crate::credentials::credential_slab::get_current_allocation_count(),
129 decrypted_metadata: crate::deserialize::decrypted_metadata::get_current_allocation_count(),
130 v0_payload: crate::deserialize::v0::v0_payload::get_current_allocation_count(),
131 legible_v1_sections:
132 crate::deserialize::v1::legible_v1_sections::get_current_allocation_count(),
133 v0_advertisement_builder:
134 crate::serialize::v0::advertisement_builder::get_current_allocation_count(),
135 v1_advertisement_builder:
136 crate::serialize::v1::advertisement_builder::get_current_allocation_count(),
137 }
138 }
139
140 /// A result-type enum which tells the caller whether/not a deallocation
141 /// succeeded or failed due to the requested handle not being present.
142 #[repr(C)]
143 pub enum DeallocateResult {
144 /// The requested handle to deallocate was not present in the map
145 NotPresent = 1,
146 /// The object behind the handle was successfully deallocated
147 Success = 2,
148 }
149
150 impl From<Result<(), HandleNotPresentError>> for DeallocateResult {
from(result: Result<(), HandleNotPresentError>) -> Self151 fn from(result: Result<(), HandleNotPresentError>) -> Self {
152 match result {
153 Ok(_) => DeallocateResult::Success,
154 Err(_) => DeallocateResult::NotPresent,
155 }
156 }
157 }
158
159 /// Represents the raw contents of the service payload data
160 /// under the Nearby Presence service UUID
161 #[repr(C)]
162 pub struct RawAdvertisementPayload {
163 bytes: ByteBuffer<255>,
164 }
165
166 impl RawAdvertisementPayload {
167 /// Yields a slice of the bytes in this raw advertisement payload.
168 #[allow(clippy::unwrap_used)]
as_slice(&self) -> &[u8]169 pub fn as_slice(&self) -> &[u8] {
170 // The unwrapping here will never trigger a panic,
171 // because the byte-buffer is 255 bytes, the byte-length
172 // of which is the maximum value storable in a u8.
173 self.bytes.as_slice().unwrap()
174 }
175 }
176
177 /// A byte-string with a maximum size of N,
178 /// where only the first `len` bytes are considered
179 /// to contain the actual payload. N is only
180 /// permitted to be between 0 and 255.
181 #[derive(Clone)]
182 #[repr(C)]
183 // TODO: Once generic const exprs are stabilized,
184 // we could instead make N into a compile-time u8.
185 pub struct ByteBuffer<const N: usize> {
186 len: u8,
187 bytes: [u8; N],
188 }
189
190 /// A FFI safe wrapper of a fixed size array
191 #[derive(Clone)]
192 #[repr(C)]
193 pub struct FixedSizeArray<const N: usize>([u8; N]);
194
195 impl<const N: usize> FixedSizeArray<N> {
196 /// Constructs a byte-buffer from a Rust-side-derived owned array
from_array(bytes: [u8; N]) -> Self197 pub(crate) fn from_array(bytes: [u8; N]) -> Self {
198 Self(bytes)
199 }
200 /// Yields a slice of the bytes
as_slice(&self) -> &[u8]201 pub fn as_slice(&self) -> &[u8] {
202 self.0.as_slice()
203 }
204 /// De-structures this FFI-compatible fixed-size array
205 /// into a bare Rust fixed size array.
into_array(self) -> [u8; N]206 pub fn into_array(self) -> [u8; N] {
207 self.0
208 }
209 }
210
211 impl<const N: usize> ByteBuffer<N> {
212 /// Constructs a byte-buffer from a Rust-side-derived
213 /// ArrayView, which is assumed to be trusted to be
214 /// properly initialized, and with a size-bound
215 /// under 255 bytes.
from_array_view(array_view: ArrayView<u8, N>) -> Self216 pub(crate) fn from_array_view(array_view: ArrayView<u8, N>) -> Self {
217 let (len, bytes) = array_view.into_raw_parts();
218 let len = len as u8;
219 Self { len, bytes }
220 }
221 /// Yields a slice of the first `self.len` bytes of `self.bytes`.
as_slice(&self) -> Option<&[u8]>222 pub fn as_slice(&self) -> Option<&[u8]> {
223 if self.len as usize <= N {
224 Some(&self.bytes[..(self.len as usize)])
225 } else {
226 None
227 }
228 }
229 }
230
231 pub(crate) type CryptoRngImpl = <CryptoProviderImpl as CryptoProvider>::CryptoRng;
232
233 pub(crate) struct LazyInitCryptoRng {
234 maybe_rng: Option<CryptoRngImpl>,
235 }
236
237 impl LazyInitCryptoRng {
new() -> Self238 const fn new() -> Self {
239 Self { maybe_rng: None }
240 }
get_rng(&mut self) -> &mut CryptoRngImpl241 pub(crate) fn get_rng(&mut self) -> &mut CryptoRngImpl {
242 self.maybe_rng.get_or_insert_with(CryptoRngImpl::new)
243 }
244 }
245
246 /// Shared, lazily-initialized cryptographically-secure
247 /// RNG for all operations in the FFI core.
248 static CRYPTO_RNG: RwLock<LazyInitCryptoRng> = RwLock::new(LazyInitCryptoRng::new());
249
250 /// Gets a write guard to the (lazily-init) library-global crypto rng.
get_global_crypto_rng() -> RwLockWriteGuard<'static, LazyInitCryptoRng>251 pub(crate) fn get_global_crypto_rng() -> RwLockWriteGuard<'static, LazyInitCryptoRng> {
252 CRYPTO_RNG.write()
253 }
254
255 /// Error returned if the bit representation of a supposedly-Rust-constructed
256 /// -and-validated type actually doesn't correspond to the format of the
257 /// data structure expected on the Rust side of the boundary, and performing
258 /// further operations on the structure would yield unintended behavior.
259 /// If this kind of error is being raised, the foreign lang code must
260 /// be messing with stack-allocated data structures for this library
261 /// in an entirely unexpected way.
262 #[derive(Debug)]
263 pub struct InvalidStackDataStructure;
264
265 /// Error raised when attempting to cast an enum to
266 /// one of its variants, but the value is actually
267 /// of a different variant than the requested one.
268 pub struct EnumCastError {
269 pub(crate) projection_method_name: String,
270 pub(crate) variant_enum_name: String,
271 pub(crate) variant_type_name: String,
272 }
273
274 impl core::fmt::Debug for EnumCastError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result275 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
276 write!(
277 f,
278 "Attempted to cast a non-{} to a {} via {}",
279 &self.variant_enum_name, &self.variant_type_name, &self.projection_method_name
280 )
281 }
282 }
283