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        if self.data.is_null() {
47            return Vec::new();
48        }
49        slice::from_raw_parts(self.data, self.len).to_vec()
50    }
51}
52
53/// Free a byte buffer allocated by this library
54///
55/// # Safety
56/// The buffer must have been allocated by this library
57#[no_mangle]
58pub unsafe extern "C" fn pqc_free_buffer(buffer: ByteBuffer) {
59    if !buffer.data.is_null() {
60        drop(Vec::from_raw_parts(
61            buffer.data,
62            buffer.len,
63            buffer.capacity,
64        ));
65    }
66}
67
68/// Free a string allocated by this library
69///
70/// # Safety
71/// The string must have been allocated by this library
72#[no_mangle]
73pub unsafe extern "C" fn pqc_free_string(s: *mut c_char) {
74    if !s.is_null() {
75        drop(CString::from_raw(s));
76    }
77}
78
79/// Create a new PQC Binary Format structure
80///
81/// # Parameters
82/// - `algorithm_id`: Algorithm identifier (see Algorithm enum)
83/// - `iv`: IV/nonce bytes
84/// - `iv_len`: Length of IV
85/// - `tag`: Authentication tag bytes
86/// - `tag_len`: Length of tag
87/// - `data`: Encrypted data bytes
88/// - `data_len`: Length of encrypted data
89///
90/// # Returns
91/// Opaque handle to PqcBinaryFormat, or NULL on error
92///
93/// # Safety
94/// All pointers must be valid. The returned handle must be freed with `pqc_format_free`.
95#[no_mangle]
96pub unsafe extern "C" fn pqc_format_new(
97    algorithm_id: u16,
98    iv: *const c_uchar,
99    iv_len: usize,
100    tag: *const c_uchar,
101    tag_len: usize,
102    data: *const c_uchar,
103    data_len: usize,
104) -> *mut PqcFormatHandle {
105    // Validate pointers
106    if iv.is_null() || tag.is_null() || data.is_null() {
107        return ptr::null_mut();
108    }
109
110    // Convert algorithm ID
111    let algorithm = match Algorithm::from_id(algorithm_id) {
112        Some(a) => a,
113        None => return ptr::null_mut(),
114    };
115
116    // Convert byte slices
117    let iv_vec = slice::from_raw_parts(iv, iv_len).to_vec();
118    let tag_vec = slice::from_raw_parts(tag, tag_len).to_vec();
119    let data_vec = slice::from_raw_parts(data, data_len).to_vec();
120
121    // Create metadata
122    let metadata = PqcMetadata {
123        enc_params: EncParameters {
124            iv: iv_vec,
125            tag: tag_vec,
126            params: HashMap::new(),
127        },
128        kem_params: None,
129        sig_params: None,
130        compression_params: None,
131        custom: HashMap::new(),
132    };
133
134    // Create format
135    let format = PqcBinaryFormat::new(algorithm, metadata, data_vec);
136
137    Box::into_raw(Box::new(format)) as *mut PqcFormatHandle
138}
139
140/// Create PQC Binary Format with KEM parameters
141///
142/// # Safety
143/// All pointers must be valid
144#[no_mangle]
145pub unsafe extern "C" fn pqc_format_new_with_kem(
146    algorithm_id: u16,
147    iv: *const c_uchar,
148    iv_len: usize,
149    tag: *const c_uchar,
150    tag_len: usize,
151    kem_public_key: *const c_uchar,
152    kem_public_key_len: usize,
153    kem_ciphertext: *const c_uchar,
154    kem_ciphertext_len: usize,
155    data: *const c_uchar,
156    data_len: usize,
157) -> *mut PqcFormatHandle {
158    if iv.is_null()
159        || tag.is_null()
160        || kem_public_key.is_null()
161        || kem_ciphertext.is_null()
162        || data.is_null()
163    {
164        return ptr::null_mut();
165    }
166
167    let algorithm = match Algorithm::from_id(algorithm_id) {
168        Some(a) => a,
169        None => return ptr::null_mut(),
170    };
171
172    let metadata = PqcMetadata {
173        enc_params: EncParameters {
174            iv: slice::from_raw_parts(iv, iv_len).to_vec(),
175            tag: slice::from_raw_parts(tag, tag_len).to_vec(),
176            params: HashMap::new(),
177        },
178        kem_params: Some(KemParameters {
179            public_key: slice::from_raw_parts(kem_public_key, kem_public_key_len).to_vec(),
180            ciphertext: slice::from_raw_parts(kem_ciphertext, kem_ciphertext_len).to_vec(),
181            params: HashMap::new(),
182        }),
183        sig_params: None,
184        compression_params: None,
185        custom: HashMap::new(),
186    };
187
188    let data_vec = slice::from_raw_parts(data, data_len).to_vec();
189    let format = PqcBinaryFormat::new(algorithm, metadata, data_vec);
190
191    Box::into_raw(Box::new(format)) as *mut PqcFormatHandle
192}
193
194/// Serialize PQC Binary Format to bytes
195///
196/// # Parameters
197/// - `handle`: Handle to PqcBinaryFormat
198///
199/// # Returns
200/// ByteBuffer containing serialized data, or NULL buffer on error
201///
202/// # Safety
203/// Handle must be valid. Returned buffer must be freed with `pqc_free_buffer`.
204#[no_mangle]
205pub unsafe extern "C" fn pqc_format_to_bytes(handle: *const PqcFormatHandle) -> ByteBuffer {
206    if handle.is_null() {
207        return ByteBuffer {
208            data: ptr::null_mut(),
209            len: 0,
210            capacity: 0,
211        };
212    }
213
214    let format = &*(handle as *const PqcBinaryFormat);
215
216    match format.to_bytes() {
217        Ok(bytes) => ByteBuffer::from_vec(bytes),
218        Err(_) => ByteBuffer {
219            data: ptr::null_mut(),
220            len: 0,
221            capacity: 0,
222        },
223    }
224}
225
226/// Deserialize PQC Binary Format from bytes
227///
228/// # Parameters
229/// - `data`: Bytes to deserialize
230/// - `len`: Length of data
231///
232/// # Returns
233/// Handle to PqcBinaryFormat, or NULL on error
234///
235/// # Safety
236/// Data pointer must be valid. Returned handle must be freed with `pqc_format_free`.
237#[no_mangle]
238pub unsafe extern "C" fn pqc_format_from_bytes(
239    data: *const c_uchar,
240    len: usize,
241) -> *mut PqcFormatHandle {
242    if data.is_null() {
243        return ptr::null_mut();
244    }
245
246    let bytes = slice::from_raw_parts(data, len);
247
248    match PqcBinaryFormat::from_bytes(bytes) {
249        Ok(format) => Box::into_raw(Box::new(format)) as *mut PqcFormatHandle,
250        Err(_) => ptr::null_mut(),
251    }
252}
253
254/// Get algorithm ID from format
255///
256/// # Safety
257/// Handle must be valid
258#[no_mangle]
259pub unsafe extern "C" fn pqc_format_get_algorithm_id(handle: *const PqcFormatHandle) -> u16 {
260    if handle.is_null() {
261        return 0;
262    }
263
264    let format = &*(handle as *const PqcBinaryFormat);
265    format.algorithm().as_id()
266}
267
268/// Get algorithm name from format
269///
270/// # Returns
271/// Null-terminated string. Must be freed with `pqc_free_string`.
272///
273/// # Safety
274/// Handle must be valid
275#[no_mangle]
276pub unsafe extern "C" fn pqc_format_get_algorithm_name(
277    handle: *const PqcFormatHandle,
278) -> *mut c_char {
279    if handle.is_null() {
280        return ptr::null_mut();
281    }
282
283    let format = &*(handle as *const PqcBinaryFormat);
284    let name = format.algorithm().name();
285
286    match CString::new(name) {
287        Ok(cstr) => cstr.into_raw(),
288        Err(_) => ptr::null_mut(),
289    }
290}
291
292/// Get encrypted data from format
293///
294/// # Returns
295/// ByteBuffer containing data. Must be freed with `pqc_free_buffer`.
296///
297/// # Safety
298/// Handle must be valid
299#[no_mangle]
300pub unsafe extern "C" fn pqc_format_get_data(handle: *const PqcFormatHandle) -> ByteBuffer {
301    if handle.is_null() {
302        return ByteBuffer {
303            data: ptr::null_mut(),
304            len: 0,
305            capacity: 0,
306        };
307    }
308
309    let format = &*(handle as *const PqcBinaryFormat);
310    ByteBuffer::from_vec(format.data().to_vec())
311}
312
313/// Validate format structure
314///
315/// # Returns
316/// 0 on success, -1 on error
317///
318/// # Safety
319/// Handle must be valid
320#[no_mangle]
321pub unsafe extern "C" fn pqc_format_validate(handle: *const PqcFormatHandle) -> c_int {
322    if handle.is_null() {
323        return -1;
324    }
325
326    let format = &*(handle as *const PqcBinaryFormat);
327
328    match format.validate() {
329        Ok(()) => 0,
330        Err(_) => -1,
331    }
332}
333
334/// Get total size of serialized format
335///
336/// # Safety
337/// Handle must be valid
338#[no_mangle]
339pub unsafe extern "C" fn pqc_format_get_total_size(handle: *const PqcFormatHandle) -> usize {
340    if handle.is_null() {
341        return 0;
342    }
343
344    let format = &*(handle as *const PqcBinaryFormat);
345    format.total_size()
346}
347
348/// Free a PqcBinaryFormat handle
349///
350/// # Safety
351/// Handle must have been allocated by this library and not previously freed
352#[no_mangle]
353pub unsafe extern "C" fn pqc_format_free(handle: *mut PqcFormatHandle) {
354    if !handle.is_null() {
355        drop(Box::from_raw(handle as *mut PqcBinaryFormat));
356    }
357}
358
359/// Get library version string
360///
361/// # Returns
362/// Null-terminated version string. Must be freed with `pqc_free_string`.
363#[no_mangle]
364pub extern "C" fn pqc_get_version() -> *mut c_char {
365    match CString::new(crate::VERSION) {
366        Ok(cstr) => cstr.into_raw(),
367        Err(_) => ptr::null_mut(),
368    }
369}
370
371/// Get binary format version
372#[no_mangle]
373pub extern "C" fn pqc_get_binary_version() -> u8 {
374    crate::PQC_BINARY_VERSION
375}
376
377// Algorithm ID constants for C/C++ convenience
378
379/// Algorithm ID for Classical (X25519 + Ed25519 + AES-256-GCM)
380#[no_mangle]
381pub static PQC_ALGORITHM_CLASSICAL: u16 = 0x0050;
382
383/// Algorithm ID for Password-based Classical encryption
384#[no_mangle]
385pub static PQC_ALGORITHM_PASSWORD_CLASSICAL: u16 = 0x0051;
386
387/// Algorithm ID for Hybrid (ML-KEM-1024 + X25519 + ML-DSA-87 + Ed25519)
388#[no_mangle]
389pub static PQC_ALGORITHM_HYBRID: u16 = 0x0100;
390
391/// Algorithm ID for Post-Quantum (ML-KEM-1024 + ML-DSA-87)
392#[no_mangle]
393pub static PQC_ALGORITHM_POST_QUANTUM: u16 = 0x0200;
394
395/// Algorithm ID for ML-KEM-1024 pure implementation
396#[no_mangle]
397pub static PQC_ALGORITHM_ML_KEM_1024: u16 = 0x0202;
398
399/// Algorithm ID for Multi-KEM (multiple key encapsulation layers)
400#[no_mangle]
401pub static PQC_ALGORITHM_MULTI_KEM: u16 = 0x0203;
402
403/// Algorithm ID for Multi-KEM Triple Layer
404#[no_mangle]
405pub static PQC_ALGORITHM_MULTI_KEM_TRIPLE: u16 = 0x0204;
406
407/// Algorithm ID for Quad-Layer redundant encryption
408#[no_mangle]
409pub static PQC_ALGORITHM_QUAD_LAYER: u16 = 0x0205;
410
411/// Algorithm ID for PQ3-Stack with forward secrecy
412#[no_mangle]
413pub static PQC_ALGORITHM_PQ3_STACK: u16 = 0x0207;
414
415/// Algorithm ID for Lattice-Code Hybrid Stack
416#[no_mangle]
417pub static PQC_ALGORITHM_LATTICE_CODE_HYBRID: u16 = 0x0208;