confium_pkcs11_server/
dispatch.rs1use crate::slot::{SlotId, SlotInfo};
4use crate::token::TokenInfo;
5use std::collections::HashMap;
6
7#[derive(Debug, thiserror::Error)]
9pub enum Pkcs11Error {
10 #[error("slot {0:?} not present")]
12 SlotNotPresent(SlotId),
13 #[error("threshold signing failed: {0}")]
15 SignFailed(String),
16 #[error("threshold decryption failed: {0}")]
18 DecryptFailed(String),
19 #[error("function {0} not supported")]
21 UnsupportedFunction(String),
22 #[error("PIN incorrect")]
24 BadPin,
25}
26
27pub trait QuorumDispatcher: Send + Sync {
29 fn sign(&self, slot: SlotId, data: &[u8]) -> Result<Vec<u8>, String>;
31
32 fn decrypt(&self, slot: SlotId, ciphertext: &[u8]) -> Result<Vec<u8>, String>;
34
35 fn generate_keypair(&self, slot: SlotId) -> Result<Vec<u8>, String>;
37}
38
39pub struct Pkcs11Server {
41 slots: HashMap<SlotId, SlotInfo>,
42 tokens: HashMap<SlotId, TokenInfo>,
43 dispatcher: Box<dyn QuorumDispatcher>,
44}
45
46impl Pkcs11Server {
47 pub fn new(dispatcher: Box<dyn QuorumDispatcher>) -> Self {
49 Self {
50 slots: HashMap::new(),
51 tokens: HashMap::new(),
52 dispatcher,
53 }
54 }
55
56 pub fn register_quorum(&mut self, slot: SlotId, slot_info: SlotInfo, token_info: TokenInfo) {
58 self.slots.insert(slot.clone(), slot_info);
59 self.tokens.insert(slot, token_info);
60 }
61
62 pub fn c_sign(&self, slot: SlotId, data: &[u8]) -> Result<Vec<u8>, Pkcs11Error> {
64 if !self.slots.contains_key(&slot) {
65 return Err(Pkcs11Error::SlotNotPresent(slot));
66 }
67 self.dispatcher
68 .sign(slot.clone(), data)
69 .map_err(Pkcs11Error::SignFailed)
70 }
71
72 pub fn c_decrypt(&self, slot: SlotId, ciphertext: &[u8]) -> Result<Vec<u8>, Pkcs11Error> {
74 if !self.slots.contains_key(&slot) {
75 return Err(Pkcs11Error::SlotNotPresent(slot));
76 }
77 self.dispatcher
78 .decrypt(slot.clone(), ciphertext)
79 .map_err(Pkcs11Error::DecryptFailed)
80 }
81
82 pub fn c_generate_keypair(&self, slot: SlotId) -> Result<Vec<u8>, Pkcs11Error> {
84 if !self.slots.contains_key(&slot) {
85 return Err(Pkcs11Error::SlotNotPresent(slot));
86 }
87 self.dispatcher
88 .generate_keypair(slot)
89 .map_err(Pkcs11Error::SignFailed)
90 }
91
92 pub fn slot_info(&self, slot: &SlotId) -> Option<&SlotInfo> {
94 self.slots.get(slot)
95 }
96
97 pub fn token_info(&self, slot: &SlotId) -> Option<&TokenInfo> {
99 self.tokens.get(slot)
100 }
101
102 pub fn slot_count(&self) -> usize {
104 self.slots.len()
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 struct MockDispatcher;
113 impl QuorumDispatcher for MockDispatcher {
114 fn sign(&self, _slot: SlotId, data: &[u8]) -> Result<Vec<u8>, String> {
115 Ok(data.iter().map(|b| !b).collect())
116 }
117 fn decrypt(&self, _slot: SlotId, ct: &[u8]) -> Result<Vec<u8>, String> {
118 Ok(ct.to_vec())
119 }
120 fn generate_keypair(&self, _slot: SlotId) -> Result<Vec<u8>, String> {
121 Ok(vec![0u8; 32])
122 }
123 }
124
125 #[test]
126 fn full_pkcs11_lifecycle() {
127 let mut server = Pkcs11Server::new(Box::new(MockDispatcher));
128 let slot = SlotId(1);
129 server.register_quorum(
130 slot,
131 SlotInfo::for_quorum("test-quorum"),
132 TokenInfo::for_quorum(
133 SlotId(1),
134 "test-quorum",
135 2,
136 3,
137 "FROST-P256",
138 "coordinator.example.com:443",
139 ),
140 );
141 assert_eq!(server.slot_count(), 1);
142
143 let sig = server.c_sign(SlotId(1), b"hello").unwrap();
144 assert_eq!(sig, vec![0x97, 0x9a, 0x93, 0x93, 0x90]);
146
147 let pt = server.c_decrypt(SlotId(1), b"cipher").unwrap();
148 assert_eq!(pt, b"cipher");
149
150 let pk = server.c_generate_keypair(SlotId(1)).unwrap();
151 assert_eq!(pk.len(), 32);
152 }
153
154 #[test]
155 fn unknown_slot_fails() {
156 let server = Pkcs11Server::new(Box::new(MockDispatcher));
157 let result = server.c_sign(SlotId(99), b"data");
158 assert!(matches!(result, Err(Pkcs11Error::SlotNotPresent(_))));
159 }
160}