lib_q_core/lib.rs
1//! lib-Q Core - Common types and traits for post-quantum cryptography
2//!
3//! This crate provides the foundational types, traits, and error handling
4//! used across all lib-Q crates.
5//!
6//! ## Context factories
7//!
8//! Core does not provide factories that depend on algorithm implementation crates (no cycles).
9//! For [`AeadContext`], use [`AeadContext::new`](crate::contexts::AeadContext::new),
10//! [`AeadContext::with_aead_operations`](crate::contexts::AeadContext::with_aead_operations), or
11//! [`AeadContext::with_provider`](crate::contexts::AeadContext::with_provider). The `lib-q-aead` crate supplies
12//! `LibQAeadProvider` for [`AeadContext::with_aead_operations`](crate::contexts::AeadContext::with_aead_operations); the umbrella `lib-q` crate exposes `libq::aead::context()` wired to that provider.
13
14#![cfg_attr(not(feature = "std"), no_std)]
15#![deny(unsafe_code)]
16#![deny(unused_qualifications)]
17
18#[cfg(feature = "alloc")]
19extern crate alloc;
20
21pub mod algorithm_registry;
22pub mod api;
23pub mod error;
24pub mod traits;
25pub mod wasm_common;
26
27// New modular architecture
28#[cfg(feature = "alloc")]
29pub mod aead_semantic;
30#[cfg(feature = "alloc")]
31pub mod contexts;
32#[cfg(feature = "alloc")]
33pub mod providers;
34#[cfg(feature = "alloc")]
35pub mod security;
36
37// WASM bindings
38#[cfg(feature = "wasm")]
39pub mod wasm;
40
41// Re-exports
42#[cfg(feature = "alloc")]
43pub use aead_semantic::{
44 AeadDecryptSemantic,
45 DecryptSemanticOutcome,
46};
47pub use algorithm_registry::*;
48pub use api::*;
49#[cfg(feature = "alloc")]
50pub use contexts::{
51 AeadContext,
52 HashContext,
53 KemContext,
54 SignatureContext,
55};
56pub use error::{
57 Error,
58 HexDecodeError,
59 Result,
60};
61// Re-export new modular components
62#[cfg(feature = "alloc")]
63pub use providers::LibQCryptoProvider;
64#[cfg(feature = "alloc")]
65pub use security::SecurityValidator;
66pub use traits::*;
67#[cfg(feature = "wasm")]
68pub use wasm::*;
69
70// Constants
71pub const VERSION: &str = env!("CARGO_PKG_VERSION");
72
73/// Initialize the core library
74pub fn init() -> Result<()> {
75 Ok(())
76}
77
78/// Get library version information
79pub fn version() -> &'static str {
80 VERSION
81}
82
83/// Create a new hash context with no provider installed.
84///
85/// Call [`HashContext::set_provider`] or use [`HashContext::with_provider`] before
86/// [`HashContext::hash`] will succeed. Leaf crates (for example `lib-q-hash`) and the
87/// `libq` umbrella expose factories that install a hash implementation up front.
88#[cfg(feature = "alloc")]
89pub fn create_hash_context() -> HashContext {
90 HashContext::new()
91}
92
93/// Create a new KEM context
94#[cfg(feature = "alloc")]
95pub fn create_kem_context() -> KemContext {
96 KemContext::new()
97}
98
99/// Create a new signature context
100#[cfg(feature = "alloc")]
101pub fn create_signature_context() -> SignatureContext {
102 SignatureContext::new()
103}
104
105// Aborting `#[panic_handler]` for `no_std` builds. Required because `lib-q-core` declares a
106// `cdylib` crate-type (for `wasm-pack`), and Cargo builds that cdylib on every target — a cdylib
107// is a final artifact and so needs a panic handler even when it is merely a transitive dependency
108// of another `no_std` crate (e.g. `lib-q-intrinsics`). Providing it by default keeps standalone
109// `cargo check --profile dev-no-std` of any `no_std` consumer working.
110//
111// It is deliberately excluded when:
112// - `std` is enabled — `std` supplies the handler;
113// - `wasm` is enabled — the `wasm-bindgen` cdylib links `std`, which supplies the handler;
114// defining our own would be a duplicate `panic_impl` lang item;
115// - `no_panic_handler` is set — firmware/integrators that supply their own handler opt out.
116#[cfg(all(
117 not(feature = "std"),
118 not(feature = "wasm"),
119 not(feature = "no_panic_handler")
120))]
121mod no_std_panic_handler {
122 use core::panic::PanicInfo;
123
124 #[panic_handler]
125 #[allow(clippy::empty_loop)]
126 fn panic(_info: &PanicInfo) -> ! {
127 // `panic = "abort"` is required for `no_std` final artifacts (see the `dev-no-std`
128 // profile); spin so the divergent handler is a valid `-> !` with no `std` unwinder.
129 loop {}
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_init() {
139 assert!(init().is_ok());
140 }
141
142 #[test]
143 fn test_version() {
144 assert!(!version().is_empty());
145 assert_eq!(version(), VERSION);
146 }
147
148 #[test]
149 fn test_no_std_compatibility() {
150 #[cfg(not(feature = "std"))]
151 use alloc::string::ToString;
152
153 // Test that core functionality works in no_std mode
154 let error = Error::InvalidKeySize {
155 expected: 32,
156 actual: 16,
157 };
158 assert_eq!(error.to_string(), "Invalid key size: expected 32, got 16");
159
160 // Test that we can create basic structures
161 #[cfg(feature = "alloc")]
162 {
163 let public_key = KemPublicKey::new(vec![1, 2, 3, 4]);
164 assert_eq!(public_key.as_bytes(), &[1, 2, 3, 4]);
165
166 let secret_key = KemSecretKey::new(vec![5, 6, 7, 8]);
167 assert_eq!(secret_key.as_bytes(), &[5, 6, 7, 8]);
168 }
169 }
170}