1use crate::error::{HsmError, HsmResult};
4use crate::pkcs11::Pkcs11Context;
5use cryptoki::session::{Session, UserType};
6use cryptoki::slot::Slot;
7use cryptoki::types::AuthPin;
8
9pub struct HsmSlot {
11 context: Pkcs11Context,
12 slot: Slot,
13}
14
15impl HsmSlot {
16 pub fn new(context: Pkcs11Context, slot: Slot) -> Self {
23 Self { context, slot }
24 }
25
26 pub fn slot(&self) -> Slot {
28 self.slot
29 }
30
31 pub fn slot_info(&self) -> HsmResult<String> {
33 self.context.with_pkcs11(|pkcs11| {
34 let info = pkcs11.get_slot_info(self.slot)?;
35 Ok(format!(
36 "Slot: {} ({})",
37 info.slot_description(),
38 if info.token_present() {
39 "token present"
40 } else {
41 "no token"
42 }
43 ))
44 })
45 }
46
47 pub fn token_info(&self) -> HsmResult<String> {
49 self.context.with_pkcs11(|pkcs11| {
50 let info = pkcs11.get_token_info(self.slot)?;
51 Ok(format!("Token: {}", info.label()))
52 })
53 }
54
55 pub fn token_label(&self) -> HsmResult<String> {
57 self.context.with_pkcs11(|pkcs11| {
58 let info = pkcs11.get_token_info(self.slot)?;
59 Ok(info.label().trim().to_string())
60 })
61 }
62
63 pub fn open_ro_session(&self) -> HsmResult<Session> {
65 self.context.with_pkcs11(|pkcs11| {
66 pkcs11
67 .open_ro_session(self.slot)
68 .map_err(|e| HsmError::SessionCreate(format!("Failed to open RO session: {e}")))
69 })
70 }
71
72 pub fn open_rw_session(&self) -> HsmResult<Session> {
74 self.context.with_pkcs11(|pkcs11| {
75 pkcs11
76 .open_rw_session(self.slot)
77 .map_err(|e| HsmError::SessionCreate(format!("Failed to open RW session: {e}")))
78 })
79 }
80
81 pub fn login(&self, session: &Session, pin: &str) -> HsmResult<()> {
88 session
89 .login(UserType::User, Some(&AuthPin::new(pin.to_owned())))
90 .map_err(|e| HsmError::Login(format!("User login failed: {e}")))
91 }
92
93 pub fn login_so(&self, session: &Session, pin: &str) -> HsmResult<()> {
100 session
101 .login(UserType::So, Some(&AuthPin::new(pin.to_owned())))
102 .map_err(|e| HsmError::Login(format!("SO login failed: {e}")))
103 }
104
105 pub fn enumerate_slots_with_tokens(context: &Pkcs11Context) -> HsmResult<Vec<Slot>> {
111 context.with_pkcs11(|pkcs11| {
112 pkcs11
113 .get_slots_with_token()
114 .map_err(|e| HsmError::SlotAccess(format!("Failed to enumerate slots: {e}")))
115 })
116 }
117
118 pub fn find_first_slot(context: &Pkcs11Context) -> HsmResult<Self> {
124 let slots = Self::enumerate_slots_with_tokens(context)?;
125
126 let slot = slots
127 .into_iter()
128 .next()
129 .ok_or_else(|| HsmError::SlotAccess("No slots with tokens found".to_string()))?;
130
131 Ok(Self::new(context.clone(), slot))
132 }
133
134 pub fn find_by_label(context: &Pkcs11Context, label: &str) -> HsmResult<Self> {
141 let slots = Self::enumerate_slots_with_tokens(context)?;
142
143 for slot_id in slots {
144 let slot = Self::new(context.clone(), slot_id);
145 if let Ok(token_label) = slot.token_label()
146 && token_label == label
147 {
148 return Ok(slot);
149 }
150 }
151
152 Err(HsmError::SlotAccess(format!(
153 "No slot found with token label '{label}'"
154 )))
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 #[ignore = "requires HSM hardware"]
164 fn test_slot_enumeration() {
165 }
167}