diff --git a/crates/build-helper/Cargo.toml b/crates/build-helper/Cargo.toml index 5f3ccdd..6796f26 100644 --- a/crates/build-helper/Cargo.toml +++ b/crates/build-helper/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -cbindgen = "0.26.0" +cbindgen = "0.29.2" diff --git a/crates/c_hash_sig/Cargo.toml b/crates/c_hash_sig/Cargo.toml index 5cbc7ef..dad9efb 100644 --- a/crates/c_hash_sig/Cargo.toml +++ b/crates/c_hash_sig/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c_hash_sig_crust" -version = "0.1.0" +version = "0.2.0" edition = "2021" [lib] @@ -8,9 +8,10 @@ name = "c_hash_sig_crust" crate-type = ["cdylib", "staticlib"] [dependencies] -hashsig = { git = "https://github.com/b-wagn/hash-sig" } +cpp = { path = "../cpp" } +leansig = { git = "https://github.com/leanEthereum/leanSig", rev = "1f8763951c55e5575c9bb9827cf6031005a4bee6" } rand = "0.9" -bincode = { version = "2.0.1", features = ["serde"] } +serde_json = "1.0" [build-dependencies] build-helper = { path = "../build-helper" } diff --git a/crates/c_hash_sig/README.md b/crates/c_hash_sig/README.md index 050a9af..85ca83c 100644 --- a/crates/c_hash_sig/README.md +++ b/crates/c_hash_sig/README.md @@ -132,9 +132,15 @@ All messages must be **exactly 32 bytes**. To sign longer messages, first use a ## Scheme Parameters Current implementation uses: -- **Scheme**: Generalized XMSS (Winternitz encoding, w=4) -- **Hash function**: SHA-3 (SHAKE) -- **Lifetime**: 2^18 epochs (262,144 epochs) +- **Scheme**: Generalized XMSS (Target Sum encoding) +- **Scheme alias**: `SIGTopLevelTargetSumLifetime32Dim64Base8` +- **Hash function**: Poseidon2 (ZK-friendly) +- **Encoding**: Target Sum +- **Dimension**: 64 +- **Base**: 8 +- **Final Layer**: 77 +- **Target Sum**: 375 +- **Lifetime**: 2^32 epochs (4,294,967,296 epochs) - **Message length**: 32 bytes ## Project Statistics diff --git a/crates/c_hash_sig/src/lib.rs b/crates/c_hash_sig/src/lib.rs index 27573ce..4c7136c 100644 --- a/crates/c_hash_sig/src/lib.rs +++ b/crates/c_hash_sig/src/lib.rs @@ -1,54 +1,56 @@ -use std::ffi::CString; +use cpp::from_raw_parts; +use cpp::from_raw_parts_mut; +use cpp::Opaque; use std::ops::Range; -use std::os::raw::{c_char, c_int}; -use std::ptr; -use std::slice; +use std::os::raw::{c_char, c_int, c_uchar}; -use hashsig::signature::generalized_xmss::instantiations_sha::lifetime_2_to_the_18::winternitz::SIGWinternitzLifetime18W4; -use hashsig::signature::{SignatureScheme, SignatureSchemeSecretKey}; -use hashsig::MESSAGE_LENGTH; +#[cfg(test)] +mod scheme_impl { + use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_8::SIGTopLevelTargetSumLifetime8Dim64Base8; + + pub type SignatureSchemeType = SIGTopLevelTargetSumLifetime8Dim64Base8; + pub const PQ_SIGNATURE_SIZE: usize = 2344; +} + +#[cfg(not(test))] +mod scheme_impl { + use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8; + + pub type SignatureSchemeType = SIGTopLevelTargetSumLifetime32Dim64Base8; + pub const PQ_SIGNATURE_SIZE: usize = 3112; +} + +use scheme_impl::SignatureSchemeType; +pub use scheme_impl::PQ_SIGNATURE_SIZE; + +use leansig::serialization::Serializable; +use leansig::signature::{SignatureScheme, SignatureSchemeSecretKey}; +use leansig::MESSAGE_LENGTH; // Type aliases for convenience -type SignatureSchemeType = SIGWinternitzLifetime18W4; type PublicKeyType = ::PublicKey; type SecretKeyType = ::SecretKey; type SignatureType = ::Signature; -/// Wrapper for signature scheme secret key -/// -/// This is an opaque structure whose fields are not accessible from C code -#[repr(C)] -pub struct PQSignatureSchemeSecretKey { - _private: [u8; 0], -} +pub const PQ_PUBLIC_KEY_SIZE: usize = 52; +pub const PQ_MESSAGE_SIZE: usize = 32; // cbindgen bug, MESSAGE_LENGTH -/// Wrapper for signature scheme public key -/// -/// This is an opaque structure whose fields are not accessible from C code -#[repr(C)] -pub struct PQSignatureSchemePublicKey { - _private: [u8; 0], +/// Opaque type for secret key +pub struct PQSecretKey; +impl Opaque for PQSecretKey { + type Type = SecretKeyType; } -/// Wrapper for signature -/// -/// This is an opaque structure whose fields are not accessible from C code -#[repr(C)] -pub struct PQSignature { - _private: [u8; 0], +/// Opaque type for public key +pub struct PQPublicKey; +impl Opaque for PQPublicKey { + type Type = PublicKeyType; } -// Internal wrappers (not exported to C) -struct PQSignatureSchemeSecretKeyInner { - inner: Box, -} - -struct PQSignatureSchemePublicKeyInner { - inner: Box, -} - -struct PQSignatureInner { - inner: Box, +/// Opaque type for signature +pub struct PQSignature; +impl Opaque for PQSignature { + type Type = SignatureType; } /// Range representation for C @@ -77,54 +79,116 @@ pub enum PQSigningError { EncodingAttemptsExceeded = 1, /// Invalid pointer (null pointer) InvalidPointer = 2, - /// Invalid message length - InvalidMessageLength = 3, /// Unknown error UnknownError = 99, } +/// Owned byte vector. +/// Deallocated with `PQByteVec_drop`. +#[repr(C)] +pub struct PQByteVec { + pub ptr: *mut c_uchar, + pub size: usize, +} +impl PQByteVec { + /// Allocate byte vector and copy bytes from slice. + fn new(bytes: &[u8]) -> Self { + let mut vec = bytes.to_vec(); + assert_eq!(vec.capacity(), vec.len()); + let ptr = vec.as_mut_ptr(); + std::mem::forget(vec); + Self { + ptr, + size: bytes.len(), + } + } +} + +/// Destroy `PQByteVec`. +#[no_mangle] +pub unsafe extern "C" fn PQByteVec_drop(bytes: PQByteVec) { + drop(Vec::from_raw_parts(bytes.ptr, bytes.size, bytes.size)) +} + +/// Convert message from byte slice to byte array. +unsafe fn get_message(message: *const u8) -> [u8; MESSAGE_LENGTH] { + from_raw_parts(message, MESSAGE_LENGTH).try_into().unwrap() +} + +/// Encode value to json. +fn to_json(value: &T) -> PQByteVec { + let json = serde_json::to_string(value).unwrap(); + PQByteVec::new(json.as_ref()) +} + +/// Decode value from json. +unsafe fn from_json>( + json_ptr: *const c_uchar, + json_size: usize, + value_out: *mut *mut T, +) -> PQSigningError { + if json_ptr.is_null() || value_out.is_null() { + return PQSigningError::InvalidPointer; + } + let json = from_raw_parts(json_ptr, json_size); + let Ok(value) = serde_json::from_slice::(json) else { + return PQSigningError::UnknownError; + }; + *value_out = Opaque::leak(value); + PQSigningError::Success +} + +/// Encode value to bytes. +unsafe fn to_bytes(value: &T, bytes_out: &mut [u8]) { + let bytes = value.to_bytes(); + assert_eq!(bytes.len(), bytes_out.len()); + bytes_out.copy_from_slice(&bytes); +} + +/// Decode value from bytes. +unsafe fn from_bytes>( + bytes: &[u8], + value_out: *mut *mut T, +) -> PQSigningError { + if value_out.is_null() { + return PQSigningError::InvalidPointer; + } + let Ok(value) = T::Type::from_bytes(bytes) else { + return PQSigningError::UnknownError; + }; + *value_out = Opaque::leak(value); + PQSigningError::Success +} + // ============================================================================ // Memory management functions // ============================================================================ /// Frees memory allocated for secret key -/// # Safety -/// Pointer must be valid and created via pq_key_gen #[no_mangle] -pub unsafe extern "C" fn pq_secret_key_free(key: *mut PQSignatureSchemeSecretKey) { - if !key.is_null() { - let _ = Box::from_raw(key as *mut PQSignatureSchemeSecretKeyInner); +pub unsafe extern "C" fn pq_secret_key_free(secret_key: *mut PQSecretKey) { + if secret_key.is_null() { + return; } + Opaque::drop(secret_key); } /// Frees memory allocated for public key -/// # Safety -/// Pointer must be valid and created via pq_key_gen #[no_mangle] -pub unsafe extern "C" fn pq_public_key_free(key: *mut PQSignatureSchemePublicKey) { - if !key.is_null() { - let _ = Box::from_raw(key as *mut PQSignatureSchemePublicKeyInner); +pub unsafe extern "C" fn pq_public_key_free(public_key: *mut PQPublicKey) { + if public_key.is_null() { + return; } + Opaque::drop(public_key); } /// Frees memory allocated for signature -/// # Safety -/// Pointer must be valid and created via pq_sign #[no_mangle] pub unsafe extern "C" fn pq_signature_free(signature: *mut PQSignature) { - if !signature.is_null() { - let _ = Box::from_raw(signature as *mut PQSignatureInner); - } -} - -/// Frees memory allocated for error description string -/// # Safety -/// Pointer must be valid and created via pq_error_description -#[no_mangle] -pub unsafe extern "C" fn pq_string_free(s: *mut c_char) { - if !s.is_null() { - let _ = CString::from_raw(s); + if signature.is_null() { + return; } + Opaque::drop(signature); } // ============================================================================ @@ -132,43 +196,33 @@ pub unsafe extern "C" fn pq_string_free(s: *mut c_char) { // ============================================================================ /// Get key activation interval -/// # Safety -/// Pointer must be valid #[no_mangle] -pub unsafe extern "C" fn pq_get_activation_interval( - key: *const PQSignatureSchemeSecretKey, -) -> PQRange { - if key.is_null() { +pub unsafe extern "C" fn pq_get_activation_interval(secret_key: *const PQSecretKey) -> PQRange { + if secret_key.is_null() { return PQRange { start: 0, end: 0 }; } - let key = &*(key as *const PQSignatureSchemeSecretKeyInner); - key.inner.get_activation_interval().into() + let secret_key = Opaque::arg(secret_key); + secret_key.get_activation_interval().into() } /// Get prepared interval of the key -/// # Safety -/// Pointer must be valid #[no_mangle] -pub unsafe extern "C" fn pq_get_prepared_interval( - key: *const PQSignatureSchemeSecretKey, -) -> PQRange { - if key.is_null() { +pub unsafe extern "C" fn pq_get_prepared_interval(secret_key: *const PQSecretKey) -> PQRange { + if secret_key.is_null() { return PQRange { start: 0, end: 0 }; } - let key = &*(key as *const PQSignatureSchemeSecretKeyInner); - key.inner.get_prepared_interval().into() + let secret_key = Opaque::arg(secret_key); + secret_key.get_prepared_interval().into() } /// Advance key preparation to next interval -/// # Safety -/// Pointer must be valid and mutable #[no_mangle] -pub unsafe extern "C" fn pq_advance_preparation(key: *mut PQSignatureSchemeSecretKey) { - if key.is_null() { +pub unsafe extern "C" fn pq_advance_preparation(secret_key: *mut PQSecretKey) { + if secret_key.is_null() { return; } - let key = &mut *(key as *mut PQSignatureSchemeSecretKeyInner); - key.inner.advance_preparation(); + let secret_key = Opaque::arg_mut(secret_key); + secret_key.advance_preparation(); } // ============================================================================ @@ -182,52 +236,43 @@ pub extern "C" fn pq_get_lifetime() -> u64 { } /// Generate key pair (public and secret) -/// +/// /// # Parameters /// - `activation_epoch`: starting epoch for key activation /// - `num_active_epochs`: number of active epochs -/// - `pk_out`: pointer to write public key (output) -/// - `sk_out`: pointer to write secret key (output) +/// - `public_key_out`: pointer to write public key (output) +/// - `secret_key_out`: pointer to write secret key (output) /// /// # Returns /// Error code (Success = 0 on success) /// /// # Safety -/// Pointers pk_out and sk_out must be valid +/// Pointers public_key_out and secret_key_out must be valid #[no_mangle] pub unsafe extern "C" fn pq_key_gen( activation_epoch: usize, num_active_epochs: usize, - pk_out: *mut *mut PQSignatureSchemePublicKey, - sk_out: *mut *mut PQSignatureSchemeSecretKey, + public_key_out: *mut *mut PQPublicKey, + secret_key_out: *mut *mut PQSecretKey, ) -> PQSigningError { - if pk_out.is_null() || sk_out.is_null() { + if public_key_out.is_null() || secret_key_out.is_null() { return PQSigningError::InvalidPointer; } let mut rng = rand::rng(); - let (pk, sk) = SignatureSchemeType::key_gen(&mut rng, activation_epoch, num_active_epochs); - - let pk_wrapper = Box::new(PQSignatureSchemePublicKeyInner { - inner: Box::new(pk), - }); - let sk_wrapper = Box::new(PQSignatureSchemeSecretKeyInner { - inner: Box::new(sk), - }); - - *pk_out = Box::into_raw(pk_wrapper) as *mut PQSignatureSchemePublicKey; - *sk_out = Box::into_raw(sk_wrapper) as *mut PQSignatureSchemeSecretKey; - + let (public_key, secret_key) = + SignatureSchemeType::key_gen(&mut rng, activation_epoch, num_active_epochs); + *public_key_out = Opaque::leak(public_key); + *secret_key_out = Opaque::leak(secret_key); PQSigningError::Success } /// Sign a message /// /// # Parameters -/// - `sk`: secret key for signing +/// - `secret_key`: secret key for signing /// - `epoch`: epoch for which signature is created /// - `message`: pointer to message -/// - `message_len`: message length (must be MESSAGE_LENGTH = 32) /// - `signature_out`: pointer to write signature (output) /// /// # Returns @@ -237,36 +282,22 @@ pub unsafe extern "C" fn pq_key_gen( /// All pointers must be valid #[no_mangle] pub unsafe extern "C" fn pq_sign( - sk: *const PQSignatureSchemeSecretKey, + secret_key: *const PQSecretKey, epoch: u32, message: *const u8, - message_len: usize, signature_out: *mut *mut PQSignature, ) -> PQSigningError { - if sk.is_null() || message.is_null() || signature_out.is_null() { + if secret_key.is_null() || message.is_null() || signature_out.is_null() { return PQSigningError::InvalidPointer; } - - if message_len != MESSAGE_LENGTH { - return PQSigningError::InvalidMessageLength; - } - - let sk = &*(sk as *const PQSignatureSchemeSecretKeyInner); - let message_slice = slice::from_raw_parts(message, message_len); - - // Convert slice to fixed-size array - let mut message_array = [0u8; MESSAGE_LENGTH]; - message_array.copy_from_slice(message_slice); - - match SignatureSchemeType::sign(&sk.inner, epoch, &message_array) { + let secret_key = Opaque::arg(secret_key); + let message = get_message(message); + match SignatureSchemeType::sign(&secret_key, epoch, &message) { Ok(signature) => { - let sig_wrapper = Box::new(PQSignatureInner { - inner: Box::new(signature), - }); - *signature_out = Box::into_raw(sig_wrapper) as *mut PQSignature; + *signature_out = Opaque::leak(signature); PQSigningError::Success } - Err(hashsig::signature::SigningError::EncodingAttemptsExceeded { .. }) => { + Err(leansig::signature::SigningError::EncodingAttemptsExceeded { .. }) => { PQSigningError::EncodingAttemptsExceeded } } @@ -275,10 +306,9 @@ pub unsafe extern "C" fn pq_sign( /// Verify a signature /// /// # Parameters -/// - `pk`: public key +/// - `public_key`: public key /// - `epoch`: signature epoch /// - `message`: pointer to message -/// - `message_len`: message length (must be MESSAGE_LENGTH = 32) /// - `signature`: signature to verify /// /// # Returns @@ -288,30 +318,21 @@ pub unsafe extern "C" fn pq_sign( /// All pointers must be valid #[no_mangle] pub unsafe extern "C" fn pq_verify( - pk: *const PQSignatureSchemePublicKey, + public_key: *const PQPublicKey, epoch: u32, message: *const u8, - message_len: usize, signature: *const PQSignature, ) -> c_int { - if pk.is_null() || message.is_null() || signature.is_null() { + if public_key.is_null() || message.is_null() || signature.is_null() { return -1; // Error: invalid pointer } - if message_len != MESSAGE_LENGTH { - return -2; // Error: invalid message length - } + let public_key = Opaque::arg(public_key); + let signature = Opaque::arg(signature); + let message = get_message(message); - let pk = &*(pk as *const PQSignatureSchemePublicKeyInner); - let signature = &*(signature as *const PQSignatureInner); - let message_slice = slice::from_raw_parts(message, message_len); - - // Convert slice to fixed-size array - let mut message_array = [0u8; MESSAGE_LENGTH]; - message_array.copy_from_slice(message_slice); + let is_valid = SignatureSchemeType::verify(&public_key, epoch, &message, &signature); - let is_valid = SignatureSchemeType::verify(&pk.inner, epoch, &message_array, &signature.inner); - if is_valid { 1 } else { @@ -330,41 +351,28 @@ pub unsafe extern "C" fn pq_verify( /// /// # Returns /// Pointer to C-string with error description. -/// Memory must be freed using pq_string_free -/// -/// # Safety -/// Returned pointer must be freed by caller #[no_mangle] -pub extern "C" fn pq_error_description(error: PQSigningError) -> *mut c_char { - let description = match error { - PQSigningError::Success => "Success", +pub extern "C" fn pq_error_description(error: PQSigningError) -> *const c_char { + match error { + PQSigningError::Success => c"Success", PQSigningError::EncodingAttemptsExceeded => { - "Failed to encode message after maximum number of attempts" + c"Failed to encode message after maximum number of attempts" } - PQSigningError::InvalidPointer => "Invalid pointer (null pointer passed)", - PQSigningError::InvalidMessageLength => { - "Invalid message length (must be 32 bytes)" - } - PQSigningError::UnknownError => "Unknown error", - }; - - match CString::new(description) { - Ok(c_str) => c_str.into_raw(), - Err(_) => ptr::null_mut(), + PQSigningError::InvalidPointer => c"Invalid pointer (null pointer passed)", + _ => c"Unknown error", } + .as_ptr() } // ============================================================================ // Serialization functions // ============================================================================ -/// Serialize secret key to bytes +/// Serialize public key to bytes /// /// # Parameters -/// - `sk`: secret key -/// - `buffer`: buffer for writing -/// - `buffer_len`: buffer size -/// - `written_len`: pointer to write actual data size (output) +/// - `public_key`: public key +/// - `public_key_bytes_out`: buffer for writing /// /// # Returns /// Error code @@ -372,40 +380,21 @@ pub extern "C" fn pq_error_description(error: PQSigningError) -> *mut c_char { /// # Safety /// All pointers must be valid #[no_mangle] -pub unsafe extern "C" fn pq_secret_key_serialize( - sk: *const PQSignatureSchemeSecretKey, - buffer: *mut u8, - buffer_len: usize, - written_len: *mut usize, -) -> PQSigningError { - if sk.is_null() || buffer.is_null() || written_len.is_null() { - return PQSigningError::InvalidPointer; - } - - let sk = &*(sk as *const PQSignatureSchemeSecretKeyInner); - - // Use bincode for serialization - match bincode::serde::encode_to_vec(&*sk.inner, bincode::config::standard()) { - Ok(bytes) => { - if bytes.len() > buffer_len { - *written_len = bytes.len(); - return PQSigningError::UnknownError; // Buffer too small - } - let buffer_slice = slice::from_raw_parts_mut(buffer, buffer_len); - buffer_slice[..bytes.len()].copy_from_slice(&bytes); - *written_len = bytes.len(); - PQSigningError::Success - } - Err(_) => PQSigningError::UnknownError, - } +pub unsafe extern "C" fn pq_public_key_to_bytes( + public_key: *const PQPublicKey, + public_key_bytes_out: *mut u8, +) { + to_bytes( + Opaque::arg(public_key), + from_raw_parts_mut(public_key_bytes_out, PQ_PUBLIC_KEY_SIZE), + ); } -/// Deserialize secret key from bytes +/// Deserialize public key from bytes /// /// # Parameters -/// - `buffer`: buffer with data -/// - `buffer_len`: buffer size -/// - `sk_out`: pointer to write secret key (output) +/// - `public_key_bytes`: buffer with data +/// - `public_key_out`: pointer to write public key (output) /// /// # Returns /// Error code @@ -413,36 +402,21 @@ pub unsafe extern "C" fn pq_secret_key_serialize( /// # Safety /// All pointers must be valid #[no_mangle] -pub unsafe extern "C" fn pq_secret_key_deserialize( - buffer: *const u8, - buffer_len: usize, - sk_out: *mut *mut PQSignatureSchemeSecretKey, +pub unsafe extern "C" fn pq_public_key_from_bytes( + public_key_bytes: *const u8, + public_key_out: *mut *mut PQPublicKey, ) -> PQSigningError { - if buffer.is_null() || sk_out.is_null() { - return PQSigningError::InvalidPointer; - } - - let buffer_slice = slice::from_raw_parts(buffer, buffer_len); - - match bincode::serde::decode_from_slice(buffer_slice, bincode::config::standard()) { - Ok((sk, _)) => { - let sk_wrapper = Box::new(PQSignatureSchemeSecretKeyInner { - inner: Box::new(sk), - }); - *sk_out = Box::into_raw(sk_wrapper) as *mut PQSignatureSchemeSecretKey; - PQSigningError::Success - } - Err(_) => PQSigningError::UnknownError, - } + from_bytes( + from_raw_parts(public_key_bytes, PQ_PUBLIC_KEY_SIZE), + public_key_out, + ) } -/// Serialize public key to bytes +/// Serialize signature to bytes /// /// # Parameters -/// - `pk`: public key -/// - `buffer`: buffer for writing -/// - `buffer_len`: buffer size -/// - `written_len`: pointer to write actual data size (output) +/// - `signature`: signature +/// - `signature_bytes_out`: buffer for writing /// /// # Returns /// Error code @@ -450,39 +424,21 @@ pub unsafe extern "C" fn pq_secret_key_deserialize( /// # Safety /// All pointers must be valid #[no_mangle] -pub unsafe extern "C" fn pq_public_key_serialize( - pk: *const PQSignatureSchemePublicKey, - buffer: *mut u8, - buffer_len: usize, - written_len: *mut usize, -) -> PQSigningError { - if pk.is_null() || buffer.is_null() || written_len.is_null() { - return PQSigningError::InvalidPointer; - } - - let pk = &*(pk as *const PQSignatureSchemePublicKeyInner); - - match bincode::serde::encode_to_vec(&*pk.inner, bincode::config::standard()) { - Ok(bytes) => { - if bytes.len() > buffer_len { - *written_len = bytes.len(); - return PQSigningError::UnknownError; // Буфер слишком мал - } - let buffer_slice = slice::from_raw_parts_mut(buffer, buffer_len); - buffer_slice[..bytes.len()].copy_from_slice(&bytes); - *written_len = bytes.len(); - PQSigningError::Success - } - Err(_) => PQSigningError::UnknownError, - } +pub unsafe extern "C" fn pq_signature_to_bytes( + signature: *const PQSignature, + signature_bytes_out: *mut u8, +) { + to_bytes( + Opaque::arg(signature), + from_raw_parts_mut(signature_bytes_out, PQ_SIGNATURE_SIZE), + ); } -/// Deserialize public key from bytes +/// Deserialize signature from bytes /// /// # Parameters -/// - `buffer`: buffer with data -/// - `buffer_len`: buffer size -/// - `pk_out`: pointer to write public key (output) +/// - `signature_bytes`: buffer with data +/// - `signature_out`: pointer to write signature (output) /// /// # Returns /// Error code @@ -490,104 +446,70 @@ pub unsafe extern "C" fn pq_public_key_serialize( /// # Safety /// All pointers must be valid #[no_mangle] -pub unsafe extern "C" fn pq_public_key_deserialize( - buffer: *const u8, - buffer_len: usize, - pk_out: *mut *mut PQSignatureSchemePublicKey, +pub unsafe extern "C" fn pq_signature_from_bytes( + signature_bytes: *const u8, + signature_out: *mut *mut PQSignature, ) -> PQSigningError { - if buffer.is_null() || pk_out.is_null() { - return PQSigningError::InvalidPointer; - } - - let buffer_slice = slice::from_raw_parts(buffer, buffer_len); - - match bincode::serde::decode_from_slice(buffer_slice, bincode::config::standard()) { - Ok((pk, _)) => { - let pk_wrapper = Box::new(PQSignatureSchemePublicKeyInner { - inner: Box::new(pk), - }); - *pk_out = Box::into_raw(pk_wrapper) as *mut PQSignatureSchemePublicKey; - PQSigningError::Success - } - Err(_) => PQSigningError::UnknownError, - } + from_bytes( + from_raw_parts(signature_bytes, PQ_SIGNATURE_SIZE), + signature_out, + ) } -/// Serialize signature to bytes +// ============================================================================ +// JSON deserialization functions +// ============================================================================ + +/// Deserialize public key from JSON string /// /// # Parameters -/// - `signature`: signature -/// - `buffer`: buffer for writing -/// - `buffer_len`: buffer size -/// - `written_len`: pointer to write actual data size (output) +/// - `json_ptr`: JSON string pointer +/// - `json_size`: JSON string size +/// - `public_key_out`: pointer to write public key (output) /// /// # Returns /// Error code /// /// # Safety -/// All pointers must be valid #[no_mangle] -pub unsafe extern "C" fn pq_signature_serialize( - signature: *const PQSignature, - buffer: *mut u8, - buffer_len: usize, - written_len: *mut usize, +pub unsafe extern "C" fn pq_public_key_from_json( + json_ptr: *const c_uchar, + json_size: usize, + public_key_out: *mut *mut PQPublicKey, ) -> PQSigningError { - if signature.is_null() || buffer.is_null() || written_len.is_null() { - return PQSigningError::InvalidPointer; - } - - let signature = &*(signature as *const PQSignatureInner); - - match bincode::serde::encode_to_vec(&*signature.inner, bincode::config::standard()) { - Ok(bytes) => { - if bytes.len() > buffer_len { - *written_len = bytes.len(); - return PQSigningError::UnknownError; // Buffer too small - } - let buffer_slice = slice::from_raw_parts_mut(buffer, buffer_len); - buffer_slice[..bytes.len()].copy_from_slice(&bytes); - *written_len = bytes.len(); - PQSigningError::Success - } - Err(_) => PQSigningError::UnknownError, - } + from_json(json_ptr, json_size, public_key_out) } -/// Deserialize signature from bytes +/// Deserialize secret key from JSON string /// /// # Parameters -/// - `buffer`: buffer with data -/// - `buffer_len`: buffer size -/// - `signature_out`: pointer to write signature (output) +/// - `json_ptr`: JSON string pointer +/// - `json_size`: JSON string size +/// - `secret_key_out`: pointer to write secret key (output) /// /// # Returns /// Error code /// /// # Safety -/// All pointers must be valid #[no_mangle] -pub unsafe extern "C" fn pq_signature_deserialize( - buffer: *const u8, - buffer_len: usize, - signature_out: *mut *mut PQSignature, +pub unsafe extern "C" fn pq_secret_key_from_json( + json_ptr: *const c_uchar, + json_size: usize, + secret_key_out: *mut *mut PQSecretKey, ) -> PQSigningError { - if buffer.is_null() || signature_out.is_null() { - return PQSigningError::InvalidPointer; - } + from_json(json_ptr, json_size, secret_key_out) +} - let buffer_slice = slice::from_raw_parts(buffer, buffer_len); - - match bincode::serde::decode_from_slice(buffer_slice, bincode::config::standard()) { - Ok((signature, _)) => { - let sig_wrapper = Box::new(PQSignatureInner { - inner: Box::new(signature), - }); - *signature_out = Box::into_raw(sig_wrapper) as *mut PQSignature; - PQSigningError::Success - } - Err(_) => PQSigningError::UnknownError, - } +/// Encode secret key to json +#[no_mangle] +pub unsafe extern "C" fn pq_secret_key_to_json(secret_key: *const PQSecretKey) -> PQByteVec { + to_json(Opaque::arg(secret_key)) +} + +/// Encode public key to json +#[no_mangle] +pub unsafe extern "C" fn pq_public_key_to_json(public_key: *const PQPublicKey) -> PQByteVec { + to_json(Opaque::arg(public_key)) } #[cfg(test)] @@ -597,37 +519,37 @@ mod tests { #[test] fn test_key_gen_and_sign() { unsafe { - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); + let mut public_key = PQPublicKey::null(); + let mut secret_key = PQSecretKey::null(); // Key generation - let result = pq_key_gen(0, 1000, &mut pk, &mut sk); + let result = pq_key_gen(0, 200, &mut public_key, &mut secret_key); assert_eq!(result, PQSigningError::Success); - assert!(!pk.is_null()); - assert!(!sk.is_null()); + assert!(!public_key.is_null()); + assert!(!secret_key.is_null()); // Check intervals - let activation = pq_get_activation_interval(sk); + let activation = pq_get_activation_interval(secret_key); assert!(activation.start < activation.end); - let prepared = pq_get_prepared_interval(sk); + let prepared = pq_get_prepared_interval(secret_key); assert!(prepared.start < prepared.end); // Sign message let message = [0u8; MESSAGE_LENGTH]; - let mut signature: *mut PQSignature = ptr::null_mut(); - let sign_result = pq_sign(sk, 10, message.as_ptr(), MESSAGE_LENGTH, &mut signature); + let mut signature = PQSignature::null(); + let sign_result = pq_sign(secret_key, 10, message.as_ptr(), &mut signature); assert_eq!(sign_result, PQSigningError::Success); assert!(!signature.is_null()); // Verify signature - let verify_result = pq_verify(pk, 10, message.as_ptr(), MESSAGE_LENGTH, signature); + let verify_result = pq_verify(public_key, 10, message.as_ptr(), signature); assert_eq!(verify_result, 1); // Cleanup pq_signature_free(signature); - pq_public_key_free(pk); - pq_secret_key_free(sk); + pq_public_key_free(public_key); + pq_secret_key_free(secret_key); } } @@ -635,282 +557,217 @@ mod tests { fn test_error_description() { let desc = pq_error_description(PQSigningError::Success); assert!(!desc.is_null()); - unsafe { - pq_string_free(desc); - } } #[test] fn test_invalid_pointers() { unsafe { // Test with null pointers - let result = pq_key_gen(0, 1000, ptr::null_mut(), ptr::null_mut()); + let result = pq_key_gen(0, 1000, std::ptr::null_mut(), std::ptr::null_mut()); assert_eq!(result, PQSigningError::InvalidPointer); - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let result = pq_key_gen(0, 1000, &mut pk, ptr::null_mut()); + let mut public_key = PQPublicKey::null(); + let result = pq_key_gen(0, 1000, &mut public_key, std::ptr::null_mut()); assert_eq!(result, PQSigningError::InvalidPointer); // pq_sign with null pointers - let message = [0u8; MESSAGE_LENGTH]; - let result = pq_sign( - ptr::null(), - 0, - message.as_ptr(), - MESSAGE_LENGTH, - ptr::null_mut(), - ); + let message = [0u8; PQ_MESSAGE_SIZE]; + let result = pq_sign(std::ptr::null(), 0, message.as_ptr(), std::ptr::null_mut()); assert_eq!(result, PQSigningError::InvalidPointer); // pq_verify with null pointers - let verify_result = pq_verify(ptr::null(), 0, message.as_ptr(), MESSAGE_LENGTH, ptr::null()); + let verify_result = pq_verify(std::ptr::null(), 0, message.as_ptr(), std::ptr::null()); assert_eq!(verify_result, -1); // Freeing null pointers should not panic - pq_secret_key_free(ptr::null_mut()); - pq_public_key_free(ptr::null_mut()); - pq_signature_free(ptr::null_mut()); - pq_string_free(ptr::null_mut()); - } - } - - #[test] - fn test_invalid_message_length() { - unsafe { - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - pq_key_gen(0, 1000, &mut pk, &mut sk); - - // Test with incorrect message length for signing - let short_message = [0u8; 16]; // Incorrect length - let mut signature: *mut PQSignature = ptr::null_mut(); - let result = pq_sign(sk, 10, short_message.as_ptr(), 16, &mut signature); - assert_eq!(result, PQSigningError::InvalidMessageLength); - - // Create valid signature - let valid_message = [0u8; MESSAGE_LENGTH]; - let result = pq_sign(sk, 10, valid_message.as_ptr(), MESSAGE_LENGTH, &mut signature); - assert_eq!(result, PQSigningError::Success); - - // Test verify with incorrect message length - let verify_result = pq_verify(pk, 10, short_message.as_ptr(), 16, signature); - assert_eq!(verify_result, -2); - - // Test verify with long message - let long_message = [0u8; 64]; - let verify_result = pq_verify(pk, 10, long_message.as_ptr(), 64, signature); - assert_eq!(verify_result, -2); - - pq_signature_free(signature); - pq_public_key_free(pk); - pq_secret_key_free(sk); + pq_secret_key_free(std::ptr::null_mut()); + pq_public_key_free(std::ptr::null_mut()); + pq_signature_free(std::ptr::null_mut()); } } #[test] fn test_signature_verification_with_wrong_data() { unsafe { - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - pq_key_gen(0, 1000, &mut pk, &mut sk); + let mut public_key = PQPublicKey::null(); + let mut secret_key = PQSecretKey::null(); + pq_key_gen(0, 200, &mut public_key, &mut secret_key); - let message = [1u8; MESSAGE_LENGTH]; - let mut signature: *mut PQSignature = ptr::null_mut(); - pq_sign(sk, 10, message.as_ptr(), MESSAGE_LENGTH, &mut signature); + let message = [1u8; PQ_MESSAGE_SIZE]; + let mut signature = PQSignature::null(); + pq_sign(secret_key, 10, message.as_ptr(), &mut signature); // Check with correct data - let result = pq_verify(pk, 10, message.as_ptr(), MESSAGE_LENGTH, signature); + let result = pq_verify(public_key, 10, message.as_ptr(), signature); assert_eq!(result, 1); // Check with wrong epoch - let result = pq_verify(pk, 11, message.as_ptr(), MESSAGE_LENGTH, signature); + let result = pq_verify(public_key, 11, message.as_ptr(), signature); assert_eq!(result, 0); // Check with modified message - let wrong_message = [2u8; MESSAGE_LENGTH]; - let result = pq_verify(pk, 10, wrong_message.as_ptr(), MESSAGE_LENGTH, signature); + let wrong_message = [2u8; PQ_MESSAGE_SIZE]; + let result = pq_verify(public_key, 10, wrong_message.as_ptr(), signature); assert_eq!(result, 0); pq_signature_free(signature); - pq_public_key_free(pk); - pq_secret_key_free(sk); + pq_public_key_free(public_key); + pq_secret_key_free(secret_key); } } #[test] fn test_advance_preparation() { unsafe { - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - pq_key_gen(0, 10000, &mut pk, &mut sk); + let mut public_key = PQPublicKey::null(); + let mut secret_key = PQSecretKey::null(); + pq_key_gen(0, 192, &mut public_key, &mut secret_key); - let initial_prepared = pq_get_prepared_interval(sk); + let initial_prepared = pq_get_prepared_interval(secret_key); assert!(initial_prepared.start < initial_prepared.end); // Advance preparation - pq_advance_preparation(sk); - let new_prepared = pq_get_prepared_interval(sk); + pq_advance_preparation(secret_key); + let new_prepared = pq_get_prepared_interval(secret_key); // New interval should be shifted assert!(new_prepared.start > initial_prepared.start); assert!(new_prepared.end > initial_prepared.end); // Advance again - pq_advance_preparation(sk); - let newer_prepared = pq_get_prepared_interval(sk); + pq_advance_preparation(secret_key); + let newer_prepared = pq_get_prepared_interval(secret_key); assert!(newer_prepared.start > new_prepared.start); - pq_public_key_free(pk); - pq_secret_key_free(sk); + pq_public_key_free(public_key); + pq_secret_key_free(secret_key); } } #[test] fn test_serialization_deserialization() { unsafe { - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - pq_key_gen(0, 1000, &mut pk, &mut sk); + let mut public_key = PQPublicKey::null(); + let mut secret_key = PQSecretKey::null(); + pq_key_gen(0, 200, &mut public_key, &mut secret_key); - let message = [42u8; MESSAGE_LENGTH]; - let mut signature: *mut PQSignature = ptr::null_mut(); - pq_sign(sk, 10, message.as_ptr(), MESSAGE_LENGTH, &mut signature); + let message = [42u8; PQ_MESSAGE_SIZE]; + let mut signature = PQSignature::null(); + pq_sign(secret_key, 10, message.as_ptr(), &mut signature); // Test public key serialization/deserialization - let mut pk_buffer = vec![0u8; 10000]; - let mut pk_written = 0; - let result = pq_public_key_serialize( - pk, - pk_buffer.as_mut_ptr(), - pk_buffer.len(), - &mut pk_written, - ); - assert_eq!(result, PQSigningError::Success); - assert!(pk_written > 0); + let mut public_key_bytes = vec![0u8; PQ_PUBLIC_KEY_SIZE]; + pq_public_key_to_bytes(public_key, public_key_bytes.as_mut_ptr()); - let mut pk_restored: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let result = pq_public_key_deserialize( - pk_buffer.as_ptr(), - pk_written, - &mut pk_restored, - ); + let mut public_key_restored = PQPublicKey::null(); + let result = + pq_public_key_from_bytes(public_key_bytes.as_ptr(), &mut public_key_restored); assert_eq!(result, PQSigningError::Success); - assert!(!pk_restored.is_null()); + assert!(!public_key_restored.is_null()); // Check that restored key works - let verify_result = pq_verify(pk_restored, 10, message.as_ptr(), MESSAGE_LENGTH, signature); + let verify_result = pq_verify(public_key_restored, 10, message.as_ptr(), signature); assert_eq!(verify_result, 1); // Test secret key serialization/deserialization - let mut sk_buffer = vec![0u8; 100000]; - let mut sk_written = 0; - let result = pq_secret_key_serialize( - sk, - sk_buffer.as_mut_ptr(), - sk_buffer.len(), - &mut sk_written, - ); - assert_eq!(result, PQSigningError::Success); - assert!(sk_written > 0); - - let mut sk_restored: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - let result = pq_secret_key_deserialize( - sk_buffer.as_ptr(), - sk_written, - &mut sk_restored, + let secret_key_json = pq_secret_key_to_json(secret_key); + let mut secret_key_restored = PQSecretKey::null(); + let result = pq_secret_key_from_json( + secret_key_json.ptr, + secret_key_json.size, + &mut secret_key_restored, ); assert_eq!(result, PQSigningError::Success); - assert!(!sk_restored.is_null()); + PQByteVec_drop(secret_key_json); + assert!(!secret_key_restored.is_null()); // Check that restored key can sign - let mut new_signature: *mut PQSignature = ptr::null_mut(); - let result = pq_sign(sk_restored, 20, message.as_ptr(), MESSAGE_LENGTH, &mut new_signature); + let mut new_signature = PQSignature::null(); + let result = pq_sign( + secret_key_restored, + 20, + message.as_ptr(), + &mut new_signature, + ); assert_eq!(result, PQSigningError::Success); // Test signature serialization/deserialization - let mut sig_buffer = vec![0u8; 100000]; - let mut sig_written = 0; - let result = pq_signature_serialize( - signature, - sig_buffer.as_mut_ptr(), - sig_buffer.len(), - &mut sig_written, - ); - assert_eq!(result, PQSigningError::Success); - assert!(sig_written > 0); + let mut signature_bytes = vec![0u8; PQ_SIGNATURE_SIZE]; + pq_signature_to_bytes(signature, signature_bytes.as_mut_ptr()); - let mut sig_restored: *mut PQSignature = ptr::null_mut(); - let result = pq_signature_deserialize( - sig_buffer.as_ptr(), - sig_written, - &mut sig_restored, - ); + let mut signature_restored = PQSignature::null(); + let result = pq_signature_from_bytes(signature_bytes.as_ptr(), &mut signature_restored); assert_eq!(result, PQSigningError::Success); - assert!(!sig_restored.is_null()); + assert!(!signature_restored.is_null()); // Check restored signature - let verify_result = pq_verify(pk, 10, message.as_ptr(), MESSAGE_LENGTH, sig_restored); + let verify_result = pq_verify(public_key, 10, message.as_ptr(), signature_restored); assert_eq!(verify_result, 1); // Cleanup - pq_signature_free(sig_restored); + pq_signature_free(signature_restored); pq_signature_free(new_signature); pq_signature_free(signature); - pq_secret_key_free(sk_restored); - pq_secret_key_free(sk); - pq_public_key_free(pk_restored); - pq_public_key_free(pk); + pq_secret_key_free(secret_key_restored); + pq_secret_key_free(secret_key); + pq_public_key_free(public_key_restored); + pq_public_key_free(public_key); } } #[test] fn test_multiple_signatures() { unsafe { - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - pq_key_gen(0, 1000, &mut pk, &mut sk); + let mut public_key = PQPublicKey::null(); + let mut secret_key = PQSecretKey::null(); + pq_key_gen(0, 200, &mut public_key, &mut secret_key); // Sign several different messages with different epochs for epoch in [5, 10, 15, 20, 25] { - let message = [epoch as u8; MESSAGE_LENGTH]; - let mut signature: *mut PQSignature = ptr::null_mut(); - - let result = pq_sign(sk, epoch, message.as_ptr(), MESSAGE_LENGTH, &mut signature); + let message = [epoch as u8; PQ_MESSAGE_SIZE]; + let mut signature = PQSignature::null(); + + let result = pq_sign(secret_key, epoch, message.as_ptr(), &mut signature); assert_eq!(result, PQSigningError::Success); - let verify_result = pq_verify(pk, epoch, message.as_ptr(), MESSAGE_LENGTH, signature); + let verify_result = pq_verify(public_key, epoch, message.as_ptr(), signature); assert_eq!(verify_result, 1); // Verification with wrong epoch should fail - let wrong_verify = pq_verify(pk, epoch + 1, message.as_ptr(), MESSAGE_LENGTH, signature); + let wrong_verify = pq_verify(public_key, epoch + 1, message.as_ptr(), signature); assert_eq!(wrong_verify, 0); pq_signature_free(signature); } - pq_public_key_free(pk); - pq_secret_key_free(sk); + pq_public_key_free(public_key); + pq_secret_key_free(secret_key); } } #[test] fn test_get_lifetime() { let lifetime = pq_get_lifetime(); - assert_eq!(lifetime, 262144); // 2^18 + assert_eq!(lifetime, 256); // 2^8 } #[test] fn test_activation_and_prepared_intervals() { unsafe { - let activation_epoch = 100; - let num_active_epochs = 5000; - - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - pq_key_gen(activation_epoch, num_active_epochs, &mut pk, &mut sk); + let activation_epoch = 0; + let num_active_epochs = 200; + + let mut public_key = PQPublicKey::null(); + let mut secret_key = PQSecretKey::null(); + pq_key_gen( + activation_epoch, + num_active_epochs, + &mut public_key, + &mut secret_key, + ); - let activation = pq_get_activation_interval(sk); - let prepared = pq_get_prepared_interval(sk); + let activation = pq_get_activation_interval(secret_key); + let prepared = pq_get_prepared_interval(secret_key); // Activation interval should contain prepared interval assert!(activation.start <= prepared.start); @@ -922,8 +779,8 @@ mod tests { assert!(activation_size >= prepared_size); - pq_public_key_free(pk); - pq_secret_key_free(sk); + pq_public_key_free(public_key); + pq_secret_key_free(secret_key); } } @@ -934,7 +791,6 @@ mod tests { PQSigningError::Success, PQSigningError::EncodingAttemptsExceeded, PQSigningError::InvalidPointer, - PQSigningError::InvalidMessageLength, PQSigningError::UnknownError, ]; @@ -945,34 +801,7 @@ mod tests { let c_str = std::ffi::CStr::from_ptr(desc); let desc_str = c_str.to_str().unwrap(); assert!(!desc_str.is_empty()); - pq_string_free(desc); } } } - - #[test] - fn test_serialization_buffer_too_small() { - unsafe { - let mut pk: *mut PQSignatureSchemePublicKey = ptr::null_mut(); - let mut sk: *mut PQSignatureSchemeSecretKey = ptr::null_mut(); - pq_key_gen(0, 1000, &mut pk, &mut sk); - - // Try to serialize into too small buffer - let mut small_buffer = [0u8; 10]; - let mut written = 0; - let result = pq_public_key_serialize( - pk, - small_buffer.as_mut_ptr(), - small_buffer.len(), - &mut written, - ); - - // Should be error, but written should contain required size - assert_eq!(result, PQSigningError::UnknownError); - assert!(written > small_buffer.len()); - - pq_public_key_free(pk); - pq_secret_key_free(sk); - } - } } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ca58912..fbe89e2 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -16,19 +16,28 @@ pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] core::slice::from_raw_parts_mut(data, len) } -/// @turuslan please add comments +/// Trait to expose rust type as C opaque type. pub trait Opaque: Sized { + /// Rust type. type Type; + /// Null C pointer. fn null() -> *mut Self { std::ptr::null_mut() } + /// Return rust value as C pointer. fn leak(value: Self::Type) -> *mut Self { - Box::leak(Box::new(value)) as *mut _ as *mut _ + Box::leak(Box::new(value)) as *mut Self::Type as *mut Self } + /// Destroy rust value from C pointer. fn drop(ptr: *mut Self) { unsafe { drop(Box::from_raw(ptr)) } } - fn arg(ptr: *mut Self) -> &'static mut Self::Type { - unsafe { &mut *(ptr as *mut _) } + /// Get reference to rust value from C pointer. + fn arg(ptr: *const Self) -> &'static Self::Type { + unsafe { &*(ptr as *const Self::Type) } } -} \ No newline at end of file + /// Get mutable reference to rust value from C pointer. + fn arg_mut(ptr: *mut Self) -> &'static mut Self::Type { + unsafe { &mut *(ptr as *mut Self::Type) } + } +}