cypheron_core/
security.rs

1// Copyright 2025 Cypheron Labs, Inc.
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
15pub trait FfiSafe {
16    fn is_valid_for_ffi(&self) -> bool;
17}
18
19impl<T> FfiSafe for &[T] {
20    fn is_valid_for_ffi(&self) -> bool {
21        !self.is_empty()
22    }
23}
24
25impl<T> FfiSafe for &mut [T] {
26    fn is_valid_for_ffi(&self) -> bool {
27        !self.is_empty()
28    }
29}
30
31impl<T, const N: usize> FfiSafe for &[T; N] {
32    fn is_valid_for_ffi(&self) -> bool {
33        true
34    }
35}
36
37impl<T, const N: usize> FfiSafe for &mut [T; N] {
38    fn is_valid_for_ffi(&self) -> bool {
39        true
40    }
41}
42
43impl<T> FfiSafe for &Vec<T> {
44    fn is_valid_for_ffi(&self) -> bool {
45        !self.is_empty()
46    }
47}
48
49impl<T> FfiSafe for Vec<T> {
50    fn is_valid_for_ffi(&self) -> bool {
51        !self.is_empty()
52    }
53}
54
55macro_rules! validate_ffi_fixed_buffer {
56    ($buffer:expr, $expected_size:expr) => {
57        if $buffer.len() != $expected_size {
58            return Err(
59                crate::sig::falcon::errors::FalconErrors::FfiValidationError(
60                    "Buffer size mismatch for FFI operation".to_string(),
61                ),
62            );
63        }
64        if !$buffer.is_valid_for_ffi() {
65            return Err(
66                crate::sig::falcon::errors::FalconErrors::FfiValidationError(
67                    "Invalid buffer for FFI operation".to_string(),
68                ),
69            );
70        }
71    };
72}
73
74macro_rules! validate_message_bounds {
75    ($msg:expr) => {
76        if $msg.len() > usize::MAX / 2 {
77            return Err(
78                crate::sig::falcon::errors::FalconErrors::FfiValidationError(
79                    "Message too large for safe processing".to_string(),
80                ),
81            );
82        }
83        if !$msg.is_valid_for_ffi() && !$msg.is_empty() {
84            return Err(
85                crate::sig::falcon::errors::FalconErrors::FfiValidationError(
86                    "Invalid message buffer".to_string(),
87                ),
88            );
89        }
90    };
91}
92
93macro_rules! safe_cast_to_c_void {
94    ($ptr:expr) => {
95        $ptr as *const c_void
96    };
97    (mut $ptr:expr) => {
98        $ptr as *mut c_void
99    };
100}
101
102pub(crate) use safe_cast_to_c_void;
103pub(crate) use validate_ffi_fixed_buffer;
104pub(crate) use validate_message_bounds;
105
106pub fn sanitize_buffer_for_ffi<T>(buffer: &mut [T]) -> bool {
107    !buffer.is_empty()
108}
109
110pub fn verify_buffer_initialized<T: PartialEq + Default + Copy>(
111    buffer: &[T],
112    expected_init_len: usize,
113) -> bool {
114    if buffer.len() < expected_init_len {
115        return false;
116    }
117
118    let default_val = T::default();
119    let initialized_portion = &buffer[..expected_init_len];
120
121    !initialized_portion.iter().all(|&x| x == default_val)
122}
123
124#[cfg(not(debug_assertions))]
125macro_rules! secure_debug {
126    ($($arg:tt)*) => {};
127}
128
129#[cfg(debug_assertions)]
130macro_rules! secure_warn {
131    ($($arg:tt)*) => {
132        eprintln!("[WARN] {}", format_args!($($arg)*));
133    };
134}
135
136#[cfg(not(debug_assertions))]
137macro_rules! secure_warn {
138    ($($arg:tt)*) => {};
139}
140
141pub(crate) use secure_warn;