/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use pkcs11_bindings::*; use rsclientcerts_util::error::{Error, ErrorType}; use rsclientcerts_util::error_here; use std::collections::{BTreeMap, BTreeSet}; use std::convert::TryInto; use std::marker::PhantomData;
/// Helper struct to manage the state of a single slot, backed by a given `ClientCertsBackend`. struct Slot<B: ClientCertsBackend> { /// A map of object handles to the underlying objects.
objects: BTreeMap<CK_OBJECT_HANDLE, Object<B>>, /// A set of certificate identifiers (not the same as handles).
cert_ids: BTreeSet<Vec<u8>>, /// A set of key identifiers (not the same as handles). For each id in this set, there should be /// a corresponding identical id in the `cert_ids` set.
key_ids: BTreeSet<Vec<u8>>, /// The next object handle to hand out.
next_handle: CK_OBJECT_HANDLE, /// The backend that provides objects, signing, etc.
backend: B,
}
/// When a new search session is opened, this searches for certificates and keys to expose. We /// de-duplicate previously-found certificates and keys by keeping track of their IDs. fn maybe_find_new_objects(&mutself) -> Result<(), Error> { let (certs, keys) = self.backend.find_objects()?; for cert in certs { let object = Object::Cert(cert); ifself.cert_ids.contains(object.id()?) { continue;
} self.cert_ids.insert(object.id()?.to_vec()); let handle = self.get_next_handle(); self.objects.insert(handle, object);
} for key in keys { let object = Object::Key(key); ifself.key_ids.contains(object.id()?) { continue;
} self.key_ids.insert(object.id()?.to_vec()); let handle = self.get_next_handle(); self.objects.insert(handle, object);
}
Ok(())
}
}
/// The `Manager` keeps track of the state of this module with respect to the PKCS #11 /// specification. This includes what sessions are open, which search and sign operations are /// ongoing, and what objects are known and by what handle. pubstruct Manager<B: ClientCertsBackend, S: IsSearchingForClientCerts> { /// A map of open session handle to slot ID. Sessions can be created (opened) on a particular /// slot and later closed.
sessions: BTreeMap<CK_SESSION_HANDLE, CK_SLOT_ID>, /// A map of searches to PKCS #11 object handles that match those searches.
searches: BTreeMap<CK_SESSION_HANDLE, Vec<CK_OBJECT_HANDLE>>, /// A map of sign operations to a pair of the object handle and optionally some params being /// used by each one.
signs: BTreeMap<CK_SESSION_HANDLE, (CK_OBJECT_HANDLE, Option<CK_RSA_PKCS_PSS_PARAMS>)>, /// The next session handle to hand out.
next_session: CK_SESSION_HANDLE, /// The list of slots managed by this Manager. The slot at index n has slot ID n + 1.
slots: Vec<Slot<B>>,
phantom: PhantomData<S>,
}
/// Get a list of slot IDs. If `token_present` is `true`, returns only the IDs of present /// slots. Otherwise, returns all slot IDs. pubfn get_slot_ids(&self, token_present: bool) -> Vec<CK_SLOT_ID> { letmut slot_ids = Vec::with_capacity(self.slots.len()); for (index, slot) inself.slots.iter().enumerate() { if slot.backend.get_slot_info().flags & CKF_TOKEN_PRESENT == CKF_TOKEN_PRESENT
|| !token_present
{
slot_ids.push((index + 1).try_into().unwrap());
}
}
slot_ids
}
/// PKCS #11 specifies that search operations happen in three phases: setup, get any matches /// (this part may be repeated if the caller uses a small buffer), and end. This implementation /// does all of the work up front and gathers all matching objects during setup and retains them /// until they are retrieved and consumed via `search`. pubfn start_search(
&mutself,
session: CK_SESSION_HANDLE,
attrs: Vec<(CK_ATTRIBUTE_TYPE, Vec<u8>)>,
) -> Result<(), Error> { let Some(slot_id) = self.sessions.get(&session) else { return Err(error_here!(ErrorType::InvalidArgument));
}; let slot = self.slot_id_to_slot_mut(*slot_id)?; // If the search is for an attribute we don't support, no objects will match. This check // saves us having to look through all of our objects. for (attr, _) in &attrs { if !SUPPORTED_ATTRIBUTES.contains(attr) { self.searches.insert(session, Vec::new()); return Ok(());
}
} // Only search for new objects when gecko has indicated that it is looking for client // authentication certificates (or all certificates). // Since these searches are relatively rare, this minimizes the impact of doing these // re-scans. if S::is_searching_for_client_certs() {
slot.maybe_find_new_objects()?;
} letmut handles = Vec::new(); for (handle, object) in &slot.objects { if object.matches(&attrs) {
handles.push(*handle);
}
} self.searches.insert(session, handles);
Ok(())
}
/// Given a session and a maximum number of object handles to return, attempts to retrieve up to /// that many objects from the corresponding search. Updates the search so those objects are not /// returned repeatedly. `max_objects` must be non-zero. pubfn search(
&mutself,
session: CK_SESSION_HANDLE,
max_objects: usize,
) -> Result<Vec<CK_OBJECT_HANDLE>, Error> { if max_objects == 0 { return Err(error_here!(ErrorType::InvalidArgument));
} matchself.searches.get_mut(&session) {
Some(search) => { let split_at = if max_objects >= search.len() { 0
} else {
search.len() - max_objects
}; let to_return = search.split_off(split_at); if to_return.len() > max_objects { return Err(error_here!(ErrorType::LibraryFailure));
}
Ok(to_return)
}
None => Err(error_here!(ErrorType::InvalidArgument)),
}
}
pubfn get_attributes(
&self,
session: CK_SESSION_HANDLE,
object_handle: CK_OBJECT_HANDLE,
attr_types: Vec<CK_ATTRIBUTE_TYPE>,
) -> Result<Vec<Option<Vec<u8>>>, Error> { let Some(slot_id) = self.sessions.get(&session) else { return Err(error_here!(ErrorType::InvalidArgument));
}; let slot = self.slot_id_to_slot(*slot_id)?; let object = match slot.objects.get(&object_handle) {
Some(object) => object,
None => return Err(error_here!(ErrorType::InvalidArgument)),
}; letmut results = Vec::with_capacity(attr_types.len()); for attr_type in attr_types { let result = object
.get_attribute(attr_type)
.map(|value| value.to_owned());
results.push(result);
}
Ok(results)
}
/// The way NSS uses PKCS #11 to sign data happens in two phases: setup and sign. This /// implementation makes a note of which key is to be used (if it exists) during setup. When the /// caller finishes with the sign operation, this implementation retrieves the key handle and /// performs the signature. pubfn start_sign(
&mutself,
session: CK_SESSION_HANDLE,
key_handle: CK_OBJECT_HANDLE,
params: Option<CK_RSA_PKCS_PSS_PARAMS>,
) -> Result<(), Error> { ifself.signs.contains_key(&session) { return Err(error_here!(ErrorType::InvalidArgument));
} self.signs.insert(session, (key_handle, params));
Ok(())
}
pubfn get_signature_length(
&mutself,
session: CK_SESSION_HANDLE,
data: Vec<u8>,
) -> Result<usize, Error> { // Take ownership of the key handle and params of the sign (this has the added benefit of // removing this data in case of an error). let (key_handle, params) = matchself.signs.remove(&session) {
Some((key_handle, params)) => (key_handle, params),
None => return Err(error_here!(ErrorType::InvalidArgument)),
}; let Some(slot_id) = self.sessions.get(&session) else { return Err(error_here!(ErrorType::InvalidArgument));
}; let slot = self.slot_id_to_slot_mut(*slot_id)?; let key = match slot.objects.get_mut(&key_handle) {
Some(key) => key,
None => return Err(error_here!(ErrorType::InvalidArgument)),
}; let signature_length = key.get_signature_length(data, ¶ms)?; // Re-add the key handle and params if getting the signature length succeeded. self.signs.insert(session, (key_handle, params));
Ok(signature_length)
}
pubfn sign(&mutself, session: CK_SESSION_HANDLE, data: Vec<u8>) -> Result<Vec<u8>, Error> { // Performing the signature (via C_Sign, which is the only way we support) finishes the sign // operation, so it needs to be removed here. let (key_handle, params) = matchself.signs.remove(&session) {
Some((key_handle, params)) => (key_handle, params),
None => return Err(error_here!(ErrorType::InvalidArgument)),
}; let Some(slot_id) = self.sessions.get(&session) else { return Err(error_here!(ErrorType::InvalidArgument));
}; let slot = self.slot_id_to_slot_mut(*slot_id)?; let key = match slot.objects.get_mut(&key_handle) {
Some(key) => key,
None => return Err(error_here!(ErrorType::InvalidArgument)),
};
key.sign(data, ¶ms)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.