confium_mock_plugin/lib.rs
1//! Mock plugin built entirely with the `confium-api` SDK proc-macros.
2//!
3//! This is the proof-of-concept that the macro-generated FFI symbols
4//! load through the standard Confium plugin loader. The plugin
5//! implements two interfaces:
6//!
7//! - A trivial XOR-fold hash: the digest is one byte, the XOR of every
8//! input byte. Fully deterministic and dependency-free.
9//! - A trivial XOR-stream cipher: each input byte is XOR-folded with a
10//! running key byte derived from the key material. The cipher is a
11//! stand-in for a real symmetric cipher — it exercises the full
12//! `cfmp_cipher_*` symbol set (create, block/key/iv size, update,
13//! finalize, reset, destroy) end to end.
14//!
15//! The crate compiles to a `cdylib` so the loader can `dlopen` it.
16//! The `cfmp_*` symbols are emitted by:
17//!
18//! - `#[plugin_interface(name = "hash", version = 0)]` on the
19//! `impl HashPlugin for XorHash` block (eight `cfmp_hash_*` symbols).
20//! - `#[plugin_interface(name = "cipher", version = 0)]` on the
21//! `impl CipherPlugin for XorCipher` block (eight `cfmp_cipher_*`
22//! symbols).
23//! - `#[export(metadata(...))]` on the plugin marker struct. Emits the
24//! lifecycle + metadata symbols. The interface list is auto-discovered
25//! from the `#[plugin_interface]` attributes above; no explicit
26//! `interfaces(...)` argument is needed.
27//!
28//! The integration test in `tests/loader.rs` loads this crate's
29//! cdylib artifact and runs an end-to-end hash and cipher through the
30//! loader.
31
32use confium_api::CipherPlugin;
33use confium_api::HashPlugin;
34use confium_api::error::PluginResult;
35use confium_api::options::OptionView;
36use confium_macros::{export, plugin_interface};
37
38// =====================================================================
39// Hash interface — XOR-fold hash
40// =====================================================================
41
42/// One-byte XOR-fold hash. State is a single accumulator byte.
43pub struct XorHash {
44 acc: u8,
45}
46
47impl XorHash {
48 /// Construct an empty accumulator. Exposed for tests that want to
49 /// bypass the trait `create_with_opts` path.
50 pub fn new() -> Self {
51 Self { acc: 0 }
52 }
53}
54
55impl Default for XorHash {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61#[plugin_interface(name = "hash", version = 0)]
62impl HashPlugin for XorHash {
63 fn create_with_opts(_name: &str, _opts: Option<OptionView<'_>>) -> PluginResult<Self> {
64 Ok(Self::new())
65 }
66
67 fn output_size(&self) -> u32 {
68 1
69 }
70
71 fn block_size(&self) -> u32 {
72 // The XOR hash has no meaningful block size. Return 1 so callers
73 // that try to chunk input still make progress.
74 1
75 }
76
77 fn update(&mut self, data: &[u8]) -> PluginResult<()> {
78 for &b in data {
79 self.acc ^= b;
80 }
81 Ok(())
82 }
83
84 fn reset(&mut self) -> PluginResult<()> {
85 self.acc = 0;
86 Ok(())
87 }
88
89 fn try_clone(&self) -> PluginResult<Self> {
90 Ok(Self { acc: self.acc })
91 }
92
93 fn finalize(&mut self, out: &mut [u8]) -> PluginResult<()> {
94 if out.is_empty() {
95 return Err(confium_api::PluginError::new(
96 confium_api::ErrorCode::INSUFFICIENT_BUFFER,
97 "xor hash needs at least 1 byte of output buffer",
98 ));
99 }
100 out[0] = self.acc;
101 Ok(())
102 }
103}
104
105// =====================================================================
106// Cipher interface — XOR-stream cipher
107// =====================================================================
108
109/// Trivial XOR-stream cipher used as a mock. The "key schedule" is a
110/// single running byte derived by XOR-folding the supplied key; each
111/// input byte is XORed with that running byte. This is not secure
112/// cryptography — it exists only to exercise the full cipher FFI
113/// surface without pulling in a real cipher library.
114pub struct XorCipher {
115 /// The running key byte. XORed against every input byte.
116 keystream: u8,
117}
118
119impl XorCipher {
120 /// Construct from key + iv. The key is XOR-folded into a single
121 /// byte; the IV is folded in on top so different IVs produce
122 /// different keystreams for the same key.
123 pub fn from_key_iv(key: &[u8], iv: &[u8]) -> Self {
124 let mut k: u8 = 0;
125 for &b in key {
126 k ^= b;
127 }
128 for &b in iv {
129 k ^= b;
130 }
131 Self { keystream: k }
132 }
133}
134
135#[plugin_interface(name = "cipher", version = 0)]
136impl CipherPlugin for XorCipher {
137 fn create_with_key(
138 _algorithm: &str,
139 key: &[u8],
140 iv: &[u8],
141 _opts: Option<OptionView<'_>>,
142 ) -> PluginResult<Self> {
143 Ok(Self::from_key_iv(key, iv))
144 }
145
146 fn block_size(&self) -> u32 {
147 // A stream cipher has no meaningful block size. Return 1 so the
148 // loader's buffer-allocation logic doesn't divide by zero.
149 1
150 }
151
152 fn key_size(&self) -> u32 {
153 // The mock accepts any key length; report 0 to signal "variable".
154 0
155 }
156
157 fn iv_size(&self) -> u32 {
158 // The mock accepts any IV length; report 0 to signal "variable".
159 0
160 }
161
162 fn update(&mut self, input: &[u8], output: &mut [u8]) -> PluginResult<usize> {
163 let n = input.len().min(output.len());
164 for i in 0..n {
165 output[i] = input[i] ^ self.keystream;
166 }
167 Ok(n)
168 }
169
170 fn finalize(&mut self, _output: &mut [u8]) -> PluginResult<usize> {
171 // A stream cipher has no buffered final block.
172 Ok(0)
173 }
174
175 fn reset(&mut self) -> PluginResult<()> {
176 // Reset is a no-op for this mock: the keystream byte is derived
177 // from the key, not from accumulated state.
178 Ok(())
179 }
180}
181
182// `#[export]` emits the four plugin lifecycle symbols plus the optional
183// `cfmp_metadata` symbol (because `metadata(...)` is supplied). The
184// interface list is auto-discovered from the `#[plugin_interface]`
185// attributes above — `hash` and `symmetric` (cipher's wire name) are
186// registered at link time and surfaced through
187// `cfmp_query_interfaces` without an explicit `interfaces(...)` arg.
188#[export(metadata(
189 name = "confium-mock-plugin",
190 version = "0.1.0",
191 vendor = "confium",
192 license = "BSD-2-Clause",
193 description = "XOR-fold mock hash + cipher for SDK loader tests",
194))]
195pub struct Plugin;