Skip to main content

pqc_binary_format/
ffi.rs

1//! C/C++ FFI bindings for PQC Binary Format
2//!
3//! This module provides C-compatible foreign function interface (FFI)
4//! for use in C/C++ applications and Go (via cgo).
5
6#![allow(missing_docs)]
7
8use std::collections::HashMap;
9use std::ffi::CString;
10use std::os::raw::{c_char, c_int, c_uchar};
11use std::ptr;
12use std::slice;
13
14use crate::{Algorithm, EncParameters, KemParameters, PqcBinaryFormat, PqcMetadata};
15
16/// Opaque handle to PqcBinaryFormat
17#[repr(C)]
18pub struct PqcFormatHandle {
19    _private: [u8; 0],
20}
21
22/// C-compatible byte buffer
23#[repr(C)]
24pub struct ByteBuffer {
25    pub data: *mut c_uchar,
26    pub len: usize,
27    pub capacity: usize,
28}
29
30impl ByteBuffer {
31    fn from_vec(vec: Vec<u8>) -> Self {
32        let mut vec = vec;
33        let data = vec.as_mut_ptr();
34        let len = vec.len();
35        let capacity = vec.capacity();
36        std::mem::forget(vec);
37        Self {
38            data,
39            len,
40            capacity,
41        }
42    }
43
44    #[allow(dead_code)]
45    unsafe fn to_vec(&self) -> Vec<u8> {
46        unsafe {
47            if self.data.is_null() {
48                return Vec::new();
49            }
50            slice::from_raw_parts(self.data, self.len).to_vec()
51        }
52    }
53}
54
55/// Free a byte buffer allocated by this library
56///
57/// # Safety
58/// The buffer must have been allocated by this library
59#[unsafe(no_mangle)]
60pub unsafe extern "C" fn pqc_free_buffer(buffer: ByteBuffer) {
61    unsafe {
62        if !buffer.data.is_null() {
63            drop(Vec::from_raw_parts(
64                buffer.data,
65                buffer.len,
66                buffer.capacity,
67            ));
68        }
69    }
70}
71
72/// Free a string allocated by this library
73///
74/// # Safety
75/// The string must have been allocated by this library
76#[unsafe(no_mangle)]
77pub unsafe extern "C" fn pqc_free_string(s: *mut c_char) {
78    unsafe {
79        if !s.is_null() {
80            drop(CString::from_raw(s));
81        }
82    }
83}
84
85/// Create a new PQC Binary Format structure
86///
87/// # Parameters
88/// - `algorithm_id`: Algorithm identifier (see Algorithm enum)
89/// - `iv`: IV/nonce bytes
90/// - `iv_len`: Length of IV
91/// - `tag`: Authentication tag bytes
92/// - `tag_len`: Length of tag
93/// - `data`: Encrypted data bytes
94/// - `data_len`: Length of encrypted data
95///
96/// # Returns
97/// Opaque handle to PqcBinaryFormat, or NULL on error
98///
99/// # Safety
100/// All pointers must be valid. The returned handle must be freed with `pqc_format_free`.
101#[unsafe(no_mangle)]
102pub unsafe extern "C" fn pqc_format_new(
103    algorithm_id: u16,
104    iv: *const c_uchar,
105    iv_len: usize,
106    tag: *const c_uchar,
107    tag_len: usize,
108    data: *const c_uchar,
109    data_len: usize,
110) -> *mut PqcFormatHandle {
111    unsafe {
112        // Validate pointers
113        if iv.is_null() || tag.is_null() || data.is_null() {
114            return ptr::null_mut();
115        }
116
117        // Convert algorithm ID
118        let algorithm = match Algorithm::from_id(algorithm_id) {
119            Some(a) => a,
120            None => return ptr::null_mut(),
121        };
122
123        // Convert byte slices
124        let iv_vec = slice::from_raw_parts(iv, iv_len).to_vec();
125        let tag_vec = slice::from_raw_parts(tag, tag_len).to_vec();
126        let data_vec = slice::from_raw_parts(data, data_len).to_vec();
127
128        // Create metadata
129        let metadata = PqcMetadata {
130            enc_params: EncParameters {
131                iv: iv_vec,
132                tag: tag_vec,
133                params: HashMap::new(),
134            },
135            kem_params: None,
136            sig_params: None,
137            compression_params: None,
138            custom: HashMap::new(),
139        };
140
141        // Create format
142        let format = PqcBinaryFormat::new(algorithm, metadata, data_vec);
143
144        Box::into_raw(Box::new(format)) as *mut PqcFormatHandle
145    }
146}
147
148/// Create PQC Binary Format with KEM parameters
149///
150/// # Safety
151/// All pointers must be valid
152#[unsafe(no_mangle)]
153pub unsafe extern "C" fn pqc_format_new_with_kem(
154    algorithm_id: u16,
155    iv: *const c_uchar,
156    iv_len: usize,
157    tag: *const c_uchar,
158    tag_len: usize,
159    kem_public_key: *const c_uchar,
160    kem_public_key_len: usize,
161    kem_ciphertext: *const c_uchar,
162    kem_ciphertext_len: usize,
163    data: *const c_uchar,
164    data_len: usize,
165) -> *mut PqcFormatHandle {
166    unsafe {
167        if iv.is_null()
168            || tag.is_null()
169            || kem_public_key.is_null()
170            || kem_ciphertext.is_null()
171            || data.is_null()
172        {
173            return ptr::null_mut();
174        }
175
176        let algorithm = match Algorithm::from_id(algorithm_id) {
177            Some(a) => a,
178            None => return ptr::null_mut(),
179        };
180
181        let metadata = PqcMetadata {
182            enc_params: EncParameters {
183                iv: slice::from_raw_parts(iv, iv_len).to_vec(),
184                tag: slice::from_raw_parts(tag, tag_len).to_vec(),
185                params: HashMap::new(),
186            },
187            kem_params: Some(KemParameters {
188                public_key: slice::from_raw_parts(kem_public_key, kem_public_key_len).to_vec(),
189                ciphertext: slice::from_raw_parts(kem_ciphertext, kem_ciphertext_len).to_vec(),
190                params: HashMap::new(),
191            }),
192            sig_params: None,
193            compression_params: None,
194            custom: HashMap::new(),
195        };
196
197        let data_vec = slice::from_raw_parts(data, data_len).to_vec();
198        let format = PqcBinaryFormat::new(algorithm, metadata, data_vec);
199
200        Box::into_raw(Box::new(format)) as *mut PqcFormatHandle
201    }
202}
203
204/// Serialize PQC Binary Format to bytes
205///
206/// # Parameters
207/// - `handle`: Handle to PqcBinaryFormat
208///
209/// # Returns
210/// ByteBuffer containing serialized data, or NULL buffer on error
211///
212/// # Safety
213/// Handle must be valid. Returned buffer must be freed with `pqc_free_buffer`.
214#[unsafe(no_mangle)]
215pub unsafe extern "C" fn pqc_format_to_bytes(handle: *const PqcFormatHandle) -> ByteBuffer {
216    unsafe {
217        if handle.is_null() {
218            return ByteBuffer {
219                data: ptr::null_mut(),
220                len: 0,
221                capacity: 0,
222            };
223        }
224
225        let format = &*(handle as *const PqcBinaryFormat);
226
227        match format.to_bytes() {
228            Ok(bytes) => ByteBuffer::from_vec(bytes),
229            Err(_) => ByteBuffer {
230                data: ptr::null_mut(),
231                len: 0,
232                capacity: 0,
233            },
234        }
235    }
236}
237
238/// Deserialize PQC Binary Format from bytes
239///
240/// # Parameters
241/// - `data`: Bytes to deserialize
242/// - `len`: Length of data
243///
244/// # Returns
245/// Handle to PqcBinaryFormat, or NULL on error
246///
247/// # Safety
248/// Data pointer must be valid. Returned handle must be freed with `pqc_format_free`.
249#[unsafe(no_mangle)]
250pub unsafe extern "C" fn pqc_format_from_bytes(
251    data: *const c_uchar,
252    len: usize,
253) -> *mut PqcFormatHandle {
254    unsafe {
255        if data.is_null() {
256            return ptr::null_mut();
257        }
258
259        let bytes = slice::from_raw_parts(data, len);
260
261        match PqcBinaryFormat::from_bytes(bytes) {
262            Ok(format) => Box::into_raw(Box::new(format)) as *mut PqcFormatHandle,
263            Err(_) => ptr::null_mut(),
264        }
265    }
266}
267
268/// Get algorithm ID from format
269///
270/// # Safety
271/// Handle must be valid
272#[unsafe(no_mangle)]
273pub unsafe extern "C" fn pqc_format_get_algorithm_id(handle: *const PqcFormatHandle) -> u16 {
274    unsafe {
275        if handle.is_null() {
276            return 0;
277        }
278
279        let format = &*(handle as *const PqcBinaryFormat);
280        format.algorithm().as_id()
281    }
282}
283
284/// Get algorithm name from format
285///
286/// # Returns
287/// Null-terminated string. Must be freed with `pqc_free_string`.
288///
289/// # Safety
290/// Handle must be valid
291#[unsafe(no_mangle)]
292pub unsafe extern "C" fn pqc_format_get_algorithm_name(
293    handle: *const PqcFormatHandle,
294) -> *mut c_char {
295    unsafe {
296        if handle.is_null() {
297            return ptr::null_mut();
298        }
299
300        let format = &*(handle as *const PqcBinaryFormat);
301        let name = format.algorithm().name();
302
303        match CString::new(name) {
304            Ok(cstr) => cstr.into_raw(),
305            Err(_) => ptr::null_mut(),
306        }
307    }
308}
309
310/// Get encrypted data from format
311///
312/// # Returns
313/// ByteBuffer containing data. Must be freed with `pqc_free_buffer`.
314///
315/// # Safety
316/// Handle must be valid
317#[unsafe(no_mangle)]
318pub unsafe extern "C" fn pqc_format_get_data(handle: *const PqcFormatHandle) -> ByteBuffer {
319    unsafe {
320        if handle.is_null() {
321            return ByteBuffer {
322                data: ptr::null_mut(),
323                len: 0,
324                capacity: 0,
325            };
326        }
327
328        let format = &*(handle as *const PqcBinaryFormat);
329        ByteBuffer::from_vec(format.data().to_vec())
330    }
331}
332
333/// Validate format structure
334///
335/// # Returns
336/// 0 on success, -1 on error
337///
338/// # Safety
339/// Handle must be valid
340#[unsafe(no_mangle)]
341pub unsafe extern "C" fn pqc_format_validate(handle: *const PqcFormatHandle) -> c_int {
342    unsafe {
343        if handle.is_null() {
344            return -1;
345        }
346
347        let format = &*(handle as *const PqcBinaryFormat);
348
349        match format.validate() {
350            Ok(()) => 0,
351            Err(_) => -1,
352        }
353    }
354}
355
356/// Get total size of serialized format
357///
358/// # Safety
359/// Handle must be valid
360#[unsafe(no_mangle)]
361pub unsafe extern "C" fn pqc_format_get_total_size(handle: *const PqcFormatHandle) -> usize {
362    unsafe {
363        if handle.is_null() {
364            return 0;
365        }
366
367        let format = &*(handle as *const PqcBinaryFormat);
368        format.total_size()
369    }
370}
371
372/// Free a PqcBinaryFormat handle
373///
374/// # Safety
375/// Handle must have been allocated by this library and not previously freed
376#[unsafe(no_mangle)]
377pub unsafe extern "C" fn pqc_format_free(handle: *mut PqcFormatHandle) {
378    unsafe {
379        if !handle.is_null() {
380            drop(Box::from_raw(handle as *mut PqcBinaryFormat));
381        }
382    }
383}
384
385/// Get library version string
386///
387/// # Returns
388/// Null-terminated version string. Must be freed with `pqc_free_string`.
389#[unsafe(no_mangle)]
390pub extern "C" fn pqc_get_version() -> *mut c_char {
391    match CString::new(crate::VERSION) {
392        Ok(cstr) => cstr.into_raw(),
393        Err(_) => ptr::null_mut(),
394    }
395}
396
397/// Get binary format version
398#[unsafe(no_mangle)]
399pub extern "C" fn pqc_get_binary_version() -> u8 {
400    crate::PQC_BINARY_VERSION
401}
402
403// Algorithm ID constants for C/C++: generated from the registry in
404// `algorithm.rs`, one per algorithm, and re-exported here.
405pub use crate::algorithm::c_constants::*;