quantacore/
keys.rs

1//! HSM Key Storage operations.
2//!
3//! This module provides secure key storage in the hardware HSM.
4
5use crate::device::Device;
6use crate::error::{check_error, Result, QuacError, ErrorCode};
7use crate::ffi;
8use crate::types::{KeyType, KeyUsage};
9
10use std::ffi::CString;
11
12/// Information about a stored key.
13#[derive(Debug, Clone)]
14pub struct KeyInfo {
15    /// Slot number
16    pub slot: u32,
17    /// Key type
18    pub key_type: KeyType,
19    /// Algorithm identifier
20    pub algorithm: i32,
21    /// Usage flags
22    pub usage: KeyUsage,
23    /// Key label
24    pub label: String,
25    /// Creation timestamp (Unix epoch)
26    pub created: u64,
27    /// Whether the key can be exported
28    pub exportable: bool,
29}
30
31impl KeyInfo {
32    pub(crate) fn from_ffi(info: &ffi::quac_key_info_t) -> Self {
33        use std::ffi::CStr;
34        
35        Self {
36            slot: info.slot,
37            key_type: KeyType::from_raw(info.key_type).unwrap_or(KeyType::Secret),
38            algorithm: info.algorithm,
39            usage: KeyUsage::from_bits_truncate(info.usage),
40            label: unsafe {
41                CStr::from_ptr(info.label.as_ptr())
42                    .to_string_lossy()
43                    .into_owned()
44            },
45            created: info.created,
46            exportable: info.exportable != 0,
47        }
48    }
49}
50
51/// Key storage (HSM) subsystem.
52///
53/// Provides secure key storage in the hardware security module.
54///
55/// # Example
56///
57/// ```no_run
58/// use quantacore::{initialize, open_first_device, KeyType, KeyUsage};
59///
60/// initialize().unwrap();
61/// let device = open_first_device().unwrap();
62/// let keys = device.keys();
63///
64/// // Store a key
65/// let key_data = vec![0u8; 32]; // Your key material
66/// keys.store(
67///     0,  // slot
68///     KeyType::Secret,
69///     0,  // algorithm
70///     KeyUsage::ENCRYPT | KeyUsage::DECRYPT,
71///     "my-key",
72///     &key_data,
73/// ).unwrap();
74///
75/// // Load the key
76/// let loaded = keys.load(0).unwrap();
77///
78/// // Get key info
79/// let info = keys.get_info(0).unwrap();
80/// println!("Key label: {}", info.label);
81///
82/// // Delete the key
83/// keys.delete(0).unwrap();
84/// ```
85#[derive(Clone)]
86pub struct Keys {
87    device: Device,
88}
89
90impl Keys {
91    /// Create a new keys subsystem handle.
92    pub(crate) fn new(device: Device) -> Self {
93        Self { device }
94    }
95
96    /// Store a key in the HSM.
97    ///
98    /// # Arguments
99    ///
100    /// * `slot` - The slot number to store the key in
101    /// * `key_type` - The type of key
102    /// * `algorithm` - The algorithm identifier
103    /// * `usage` - Allowed usage flags
104    /// * `label` - A human-readable label (max 63 chars)
105    /// * `key_data` - The raw key material
106    pub fn store(
107        &self,
108        slot: u32,
109        key_type: KeyType,
110        algorithm: i32,
111        usage: KeyUsage,
112        label: &str,
113        key_data: &[u8],
114    ) -> Result<()> {
115        let c_label = CString::new(label).map_err(|_| {
116            QuacError::InvalidParameter("Label contains null bytes".into())
117        })?;
118
119        let result = unsafe {
120            ffi::quac_key_store(
121                self.device.handle(),
122                slot,
123                key_type.to_raw(),
124                algorithm,
125                usage.bits(),
126                c_label.as_ptr(),
127                key_data.as_ptr(),
128                key_data.len(),
129            )
130        };
131
132        check_error(result)
133    }
134
135    /// Load a key from the HSM.
136    ///
137    /// # Arguments
138    ///
139    /// * `slot` - The slot number to load from
140    ///
141    /// # Returns
142    ///
143    /// The raw key material.
144    pub fn load(&self, slot: u32) -> Result<Vec<u8>> {
145        // First get info to determine key size
146        let _info = self.get_info(slot)?;
147        let max_size = 8192; // Maximum reasonable key size
148
149        let mut key_data = vec![0u8; max_size];
150        let mut key_len = max_size;
151
152        let result = unsafe {
153            ffi::quac_key_load(
154                self.device.handle(),
155                slot,
156                key_data.as_mut_ptr(),
157                &mut key_len,
158            )
159        };
160
161        check_error(result)?;
162        key_data.truncate(key_len);
163
164        Ok(key_data)
165    }
166
167    /// Get information about a stored key.
168    ///
169    /// # Arguments
170    ///
171    /// * `slot` - The slot number
172    ///
173    /// # Returns
174    ///
175    /// Information about the key in that slot.
176    pub fn get_info(&self, slot: u32) -> Result<KeyInfo> {
177        let mut info = ffi::quac_key_info_t::default();
178        let result = unsafe {
179            ffi::quac_key_get_info(self.device.handle(), slot, &mut info)
180        };
181        check_error(result)?;
182        Ok(KeyInfo::from_ffi(&info))
183    }
184
185    /// Delete a key from the HSM.
186    ///
187    /// # Arguments
188    ///
189    /// * `slot` - The slot number to delete
190    pub fn delete(&self, slot: u32) -> Result<()> {
191        let result = unsafe { ffi::quac_key_delete(self.device.handle(), slot) };
192        check_error(result)
193    }
194
195    /// List all occupied key slots.
196    ///
197    /// # Returns
198    ///
199    /// A vector of slot numbers that contain keys.
200    pub fn list(&self) -> Result<Vec<u32>> {
201        let capacity = self.get_slot_count()? as usize;
202        let mut slots = vec![0u32; capacity];
203        let mut count = capacity;
204
205        let result = unsafe {
206            ffi::quac_key_list(
207                self.device.handle(),
208                slots.as_mut_ptr(),
209                &mut count,
210            )
211        };
212
213        check_error(result)?;
214        slots.truncate(count);
215
216        Ok(slots)
217    }
218
219    /// Get the total number of key slots.
220    pub fn get_slot_count(&self) -> Result<u32> {
221        let result = unsafe { ffi::quac_key_get_slot_count(self.device.handle()) };
222        if result < 0 {
223            check_error(result)?;
224        }
225        Ok(result as u32)
226    }
227
228    /// Find the first free slot.
229    ///
230    /// # Returns
231    ///
232    /// The slot number, or an error if no free slots.
233    pub fn get_free_slot(&self) -> Result<u32> {
234        let result = unsafe { ffi::quac_key_get_free_slot(self.device.handle()) };
235        if result < 0 {
236            return Err(QuacError::from_code(ErrorCode::KeyNotFound));
237        }
238        Ok(result as u32)
239    }
240
241    /// Clear all keys from the HSM.
242    ///
243    /// **WARNING**: This permanently deletes all stored keys!
244    pub fn clear_all(&self) -> Result<()> {
245        let result = unsafe { ffi::quac_key_clear_all(self.device.handle()) };
246        check_error(result)
247    }
248
249    /// Check if a slot is occupied.
250    pub fn is_slot_occupied(&self, slot: u32) -> Result<bool> {
251        match self.get_info(slot) {
252            Ok(_) => Ok(true),
253            Err(QuacError::Native { code: ErrorCode::KeyNotFound, .. }) => Ok(false),
254            Err(e) => Err(e),
255        }
256    }
257
258    /// Store a key pair (public + secret).
259    ///
260    /// This is a convenience method that stores both keys in adjacent slots.
261    ///
262    /// # Arguments
263    ///
264    /// * `slot` - The base slot (public key goes here, secret at slot+1)
265    /// * `algorithm` - Algorithm identifier
266    /// * `label` - Label prefix
267    /// * `public_key` - Public key bytes
268    /// * `secret_key` - Secret key bytes
269    pub fn store_keypair(
270        &self,
271        slot: u32,
272        algorithm: i32,
273        label: &str,
274        public_key: &[u8],
275        secret_key: &[u8],
276    ) -> Result<()> {
277        self.store(
278            slot,
279            KeyType::Public,
280            algorithm,
281            KeyUsage::VERIFY,
282            &format!("{}-pub", label),
283            public_key,
284        )?;
285
286        self.store(
287            slot + 1,
288            KeyType::Private,
289            algorithm,
290            KeyUsage::SIGN,
291            &format!("{}-priv", label),
292            secret_key,
293        )?;
294
295        Ok(())
296    }
297}
298
299impl std::fmt::Debug for Keys {
300    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301        f.debug_struct("Keys").finish()
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    fn test_key_usage_flags() {
311        let usage = KeyUsage::ENCRYPT | KeyUsage::DECRYPT;
312        assert!(usage.contains(KeyUsage::ENCRYPT));
313        assert!(usage.contains(KeyUsage::DECRYPT));
314        assert!(!usage.contains(KeyUsage::SIGN));
315    }
316}