tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

manager.rs (17301B)


      1 /* -*- Mode: rust; rust-indent-offset: 4 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 use pkcs11_bindings::*;
      7 use rsclientcerts_util::error::{Error, ErrorType};
      8 use rsclientcerts_util::error_here;
      9 use std::collections::{BTreeMap, BTreeSet};
     10 use std::convert::TryInto;
     11 use std::marker::PhantomData;
     12 
     13 use crate::cryptoki::CryptokiCert;
     14 
     15 pub trait CryptokiObject {
     16    fn matches(&self, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool;
     17    fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]>;
     18 }
     19 
     20 pub trait Sign {
     21    fn get_signature_length(
     22        &mut self,
     23        data: &[u8],
     24        params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
     25    ) -> Result<usize, Error>;
     26    fn sign(
     27        &mut self,
     28        data: &[u8],
     29        params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
     30    ) -> Result<Vec<u8>, Error>;
     31 }
     32 
     33 pub trait ClientCertsBackend {
     34    type Key: CryptokiObject + Sign;
     35 
     36    #[allow(clippy::type_complexity)]
     37    fn find_objects(&mut self) -> Result<(Vec<CryptokiCert>, Vec<Self::Key>), Error>;
     38    fn get_slot_info(&self) -> CK_SLOT_INFO;
     39    fn get_token_info(&self) -> CK_TOKEN_INFO;
     40    fn get_mechanism_list(&self) -> Vec<CK_MECHANISM_TYPE>;
     41    fn login(&mut self) {}
     42    fn logout(&mut self) {}
     43    fn is_logged_in(&self) -> bool {
     44        false
     45    }
     46 }
     47 
     48 pub trait IsSearchingForClientCerts {
     49    fn is_searching_for_client_certs() -> bool;
     50 }
     51 
     52 const SUPPORTED_ATTRIBUTES: &[CK_ATTRIBUTE_TYPE] = &[
     53    CKA_CLASS,
     54    CKA_TOKEN,
     55    CKA_LABEL,
     56    CKA_ID,
     57    CKA_VALUE,
     58    CKA_ISSUER,
     59    CKA_SERIAL_NUMBER,
     60    CKA_SUBJECT,
     61    CKA_PRIVATE,
     62    CKA_KEY_TYPE,
     63    CKA_MODULUS,
     64    CKA_EC_PARAMS,
     65 ];
     66 
     67 enum Object<B: ClientCertsBackend> {
     68    Cert(CryptokiCert),
     69    Key(B::Key),
     70 }
     71 
     72 impl<B: ClientCertsBackend> Object<B> {
     73    fn matches(&self, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool {
     74        match self {
     75            Object::Cert(cert) => cert.matches(attrs),
     76            Object::Key(key) => key.matches(attrs),
     77        }
     78    }
     79 
     80    fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> {
     81        match self {
     82            Object::Cert(cert) => cert.get_attribute(attribute),
     83            Object::Key(key) => key.get_attribute(attribute),
     84        }
     85    }
     86 
     87    fn id(&self) -> Result<&[u8], Error> {
     88        self.get_attribute(CKA_ID)
     89            .ok_or_else(|| error_here!(ErrorType::LibraryFailure))
     90    }
     91 
     92    fn get_signature_length(
     93        &mut self,
     94        data: Vec<u8>,
     95        params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
     96    ) -> Result<usize, Error> {
     97        match self {
     98            Object::Cert(_) => Err(error_here!(ErrorType::InvalidArgument)),
     99            Object::Key(key) => key.get_signature_length(&data, params),
    100        }
    101    }
    102 
    103    fn sign(
    104        &mut self,
    105        data: Vec<u8>,
    106        params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
    107    ) -> Result<Vec<u8>, Error> {
    108        match self {
    109            Object::Cert(_) => Err(error_here!(ErrorType::InvalidArgument)),
    110            Object::Key(key) => key.sign(&data, params),
    111        }
    112    }
    113 }
    114 
    115 /// Helper struct to manage the state of a single slot, backed by a given `ClientCertsBackend`.
    116 struct Slot<B: ClientCertsBackend> {
    117    /// A map of object handles to the underlying objects.
    118    objects: BTreeMap<CK_OBJECT_HANDLE, Object<B>>,
    119    /// A set of certificate identifiers (not the same as handles).
    120    cert_ids: BTreeSet<Vec<u8>>,
    121    /// A set of key identifiers (not the same as handles). For each id in this set, there should be
    122    /// a corresponding identical id in the `cert_ids` set.
    123    key_ids: BTreeSet<Vec<u8>>,
    124    /// The next object handle to hand out.
    125    next_handle: CK_OBJECT_HANDLE,
    126    /// The backend that provides objects, signing, etc.
    127    backend: B,
    128 }
    129 
    130 impl<B: ClientCertsBackend> Slot<B> {
    131    fn new(backend: B) -> Slot<B> {
    132        Slot {
    133            objects: BTreeMap::new(),
    134            cert_ids: BTreeSet::new(),
    135            key_ids: BTreeSet::new(),
    136            next_handle: 1,
    137            backend,
    138        }
    139    }
    140 
    141    fn get_next_handle(&mut self) -> CK_OBJECT_HANDLE {
    142        let next_handle = self.next_handle;
    143        self.next_handle += 1;
    144        next_handle
    145    }
    146 
    147    /// When a new search session is opened, this searches for certificates and keys to expose. We
    148    /// de-duplicate previously-found certificates and keys by keeping track of their IDs.
    149    fn maybe_find_new_objects(&mut self) -> Result<(), Error> {
    150        let (certs, keys) = self.backend.find_objects()?;
    151        for cert in certs {
    152            let object = Object::Cert(cert);
    153            if self.cert_ids.contains(object.id()?) {
    154                continue;
    155            }
    156            self.cert_ids.insert(object.id()?.to_vec());
    157            let handle = self.get_next_handle();
    158            self.objects.insert(handle, object);
    159        }
    160        for key in keys {
    161            let object = Object::Key(key);
    162            if self.key_ids.contains(object.id()?) {
    163                continue;
    164            }
    165            self.key_ids.insert(object.id()?.to_vec());
    166            let handle = self.get_next_handle();
    167            self.objects.insert(handle, object);
    168        }
    169        Ok(())
    170    }
    171 }
    172 
    173 /// The `Manager` keeps track of the state of this module with respect to the PKCS #11
    174 /// specification. This includes what sessions are open, which search and sign operations are
    175 /// ongoing, and what objects are known and by what handle.
    176 pub struct Manager<B: ClientCertsBackend, S: IsSearchingForClientCerts> {
    177    /// A map of open session handle to slot ID. Sessions can be created (opened) on a particular
    178    /// slot and later closed.
    179    sessions: BTreeMap<CK_SESSION_HANDLE, CK_SLOT_ID>,
    180    /// A map of searches to PKCS #11 object handles that match those searches.
    181    searches: BTreeMap<CK_SESSION_HANDLE, Vec<CK_OBJECT_HANDLE>>,
    182    /// A map of sign operations to a pair of the object handle and optionally some params being
    183    /// used by each one.
    184    signs: BTreeMap<CK_SESSION_HANDLE, (CK_OBJECT_HANDLE, Option<CK_RSA_PKCS_PSS_PARAMS>)>,
    185    /// The next session handle to hand out.
    186    next_session: CK_SESSION_HANDLE,
    187    /// The list of slots managed by this Manager. The slot at index n has slot ID n + 1.
    188    slots: Vec<Slot<B>>,
    189    phantom: PhantomData<S>,
    190 }
    191 
    192 impl<B: ClientCertsBackend, S: IsSearchingForClientCerts> Manager<B, S> {
    193    pub fn new(slots: Vec<B>) -> Manager<B, S> {
    194        Manager {
    195            sessions: BTreeMap::new(),
    196            searches: BTreeMap::new(),
    197            signs: BTreeMap::new(),
    198            next_session: 1,
    199            slots: slots.into_iter().map(Slot::new).collect(),
    200            phantom: PhantomData,
    201        }
    202    }
    203 
    204    /// Get a list of slot IDs. If `token_present` is `true`, returns only the IDs of present
    205    /// slots. Otherwise, returns all slot IDs.
    206    pub fn get_slot_ids(&self, token_present: bool) -> Vec<CK_SLOT_ID> {
    207        let mut slot_ids = Vec::with_capacity(self.slots.len());
    208        for (index, slot) in self.slots.iter().enumerate() {
    209            if slot.backend.get_slot_info().flags & CKF_TOKEN_PRESENT == CKF_TOKEN_PRESENT
    210                || !token_present
    211            {
    212                slot_ids.push((index + 1).try_into().unwrap());
    213            }
    214        }
    215        slot_ids
    216    }
    217 
    218    pub fn get_slot_info(&self, slot_id: CK_SLOT_ID) -> Result<CK_SLOT_INFO, Error> {
    219        let slot = self.slot_id_to_slot(slot_id)?;
    220        Ok(slot.backend.get_slot_info())
    221    }
    222 
    223    pub fn get_token_info(&self, slot_id: CK_SLOT_ID) -> Result<CK_TOKEN_INFO, Error> {
    224        let slot = self.slot_id_to_slot(slot_id)?;
    225        Ok(slot.backend.get_token_info())
    226    }
    227 
    228    pub fn get_mechanism_list(&self, slot_id: CK_SLOT_ID) -> Result<Vec<CK_MECHANISM_TYPE>, Error> {
    229        let slot = self.slot_id_to_slot(slot_id)?;
    230        Ok(slot.backend.get_mechanism_list())
    231    }
    232 
    233    pub fn open_session(&mut self, slot_id: CK_SLOT_ID) -> Result<CK_SESSION_HANDLE, Error> {
    234        let next_session = self.next_session;
    235        self.next_session += 1;
    236        self.sessions.insert(next_session, slot_id);
    237        Ok(next_session)
    238    }
    239 
    240    pub fn close_session(&mut self, session: CK_SESSION_HANDLE) -> Result<(), Error> {
    241        self.sessions
    242            .remove(&session)
    243            .map(|_| ())
    244            .ok_or(error_here!(ErrorType::InvalidInput))
    245    }
    246 
    247    pub fn close_all_sessions(&mut self, slot_id: CK_SLOT_ID) -> Result<(), Error> {
    248        self.sessions
    249            .retain(|_, existing_slot_id| *existing_slot_id != slot_id);
    250        Ok(())
    251    }
    252 
    253    pub fn login(&mut self, session: CK_SESSION_HANDLE) -> Result<(), Error> {
    254        let Some(slot_id) = self.sessions.get(&session) else {
    255            return Err(error_here!(ErrorType::InvalidArgument));
    256        };
    257        let slot = self.slot_id_to_slot_mut(*slot_id)?;
    258        slot.backend.login();
    259        Ok(())
    260    }
    261 
    262    pub fn logout(&mut self, session: CK_SESSION_HANDLE) -> Result<(), Error> {
    263        let Some(slot_id) = self.sessions.get(&session) else {
    264            return Err(error_here!(ErrorType::InvalidArgument));
    265        };
    266        let slot = self.slot_id_to_slot_mut(*slot_id)?;
    267        slot.backend.logout();
    268        Ok(())
    269    }
    270 
    271    pub fn get_session_info(&self, session: CK_SESSION_HANDLE) -> Result<CK_SESSION_INFO, Error> {
    272        let Some(slot_id) = self.sessions.get(&session) else {
    273            return Err(error_here!(ErrorType::InvalidArgument));
    274        };
    275        let slot = self.slot_id_to_slot(*slot_id)?;
    276        Ok(CK_SESSION_INFO {
    277            slotID: *slot_id,
    278            state: if slot.backend.is_logged_in() {
    279                CKS_RO_USER_FUNCTIONS
    280            } else {
    281                CKS_RO_PUBLIC_SESSION
    282            },
    283            flags: CKF_SERIAL_SESSION,
    284            ulDeviceError: 0,
    285        })
    286    }
    287 
    288    fn slot_id_to_slot(&self, slot_id: CK_SLOT_ID) -> Result<&Slot<B>, Error> {
    289        let slot_id: usize = slot_id
    290            .try_into()
    291            .map_err(|_| error_here!(ErrorType::InvalidInput))?;
    292        if slot_id < 1 {
    293            return Err(error_here!(ErrorType::InvalidInput));
    294        }
    295        self.slots
    296            .get(slot_id - 1)
    297            .ok_or(error_here!(ErrorType::InvalidInput))
    298    }
    299 
    300    fn slot_id_to_slot_mut(&mut self, slot_id: CK_SLOT_ID) -> Result<&mut Slot<B>, Error> {
    301        let slot_id: usize = slot_id
    302            .try_into()
    303            .map_err(|_| error_here!(ErrorType::InvalidInput))?;
    304        if slot_id < 1 {
    305            return Err(error_here!(ErrorType::InvalidInput));
    306        }
    307        self.slots
    308            .get_mut(slot_id - 1)
    309            .ok_or(error_here!(ErrorType::InvalidInput))
    310    }
    311 
    312    /// PKCS #11 specifies that search operations happen in three phases: setup, get any matches
    313    /// (this part may be repeated if the caller uses a small buffer), and end. This implementation
    314    /// does all of the work up front and gathers all matching objects during setup and retains them
    315    /// until they are retrieved and consumed via `search`.
    316    pub fn start_search(
    317        &mut self,
    318        session: CK_SESSION_HANDLE,
    319        attrs: Vec<(CK_ATTRIBUTE_TYPE, Vec<u8>)>,
    320    ) -> Result<(), Error> {
    321        let Some(slot_id) = self.sessions.get(&session) else {
    322            return Err(error_here!(ErrorType::InvalidArgument));
    323        };
    324        let slot = self.slot_id_to_slot_mut(*slot_id)?;
    325        // If the search is for an attribute we don't support, no objects will match. This check
    326        // saves us having to look through all of our objects.
    327        for (attr, _) in &attrs {
    328            if !SUPPORTED_ATTRIBUTES.contains(attr) {
    329                self.searches.insert(session, Vec::new());
    330                return Ok(());
    331            }
    332        }
    333        // Only search for new objects when gecko has indicated that it is looking for client
    334        // authentication certificates (or all certificates).
    335        // Since these searches are relatively rare, this minimizes the impact of doing these
    336        // re-scans.
    337        if S::is_searching_for_client_certs() {
    338            slot.maybe_find_new_objects()?;
    339        }
    340        let mut handles = Vec::new();
    341        for (handle, object) in &slot.objects {
    342            if object.matches(&attrs) {
    343                handles.push(*handle);
    344            }
    345        }
    346        self.searches.insert(session, handles);
    347        Ok(())
    348    }
    349 
    350    /// Given a session and a maximum number of object handles to return, attempts to retrieve up to
    351    /// that many objects from the corresponding search. Updates the search so those objects are not
    352    /// returned repeatedly. `max_objects` must be non-zero.
    353    pub fn search(
    354        &mut self,
    355        session: CK_SESSION_HANDLE,
    356        max_objects: usize,
    357    ) -> Result<Vec<CK_OBJECT_HANDLE>, Error> {
    358        if max_objects == 0 {
    359            return Err(error_here!(ErrorType::InvalidArgument));
    360        }
    361        match self.searches.get_mut(&session) {
    362            Some(search) => {
    363                let split_at = if max_objects >= search.len() {
    364                    0
    365                } else {
    366                    search.len() - max_objects
    367                };
    368                let to_return = search.split_off(split_at);
    369                if to_return.len() > max_objects {
    370                    return Err(error_here!(ErrorType::LibraryFailure));
    371                }
    372                Ok(to_return)
    373            }
    374            None => Err(error_here!(ErrorType::InvalidArgument)),
    375        }
    376    }
    377 
    378    pub fn clear_search(&mut self, session: CK_SESSION_HANDLE) -> Result<(), Error> {
    379        self.searches.remove(&session);
    380        Ok(())
    381    }
    382 
    383    pub fn get_attributes(
    384        &self,
    385        session: CK_SESSION_HANDLE,
    386        object_handle: CK_OBJECT_HANDLE,
    387        attr_types: Vec<CK_ATTRIBUTE_TYPE>,
    388    ) -> Result<Vec<Option<Vec<u8>>>, Error> {
    389        let Some(slot_id) = self.sessions.get(&session) else {
    390            return Err(error_here!(ErrorType::InvalidArgument));
    391        };
    392        let slot = self.slot_id_to_slot(*slot_id)?;
    393        let object = match slot.objects.get(&object_handle) {
    394            Some(object) => object,
    395            None => return Err(error_here!(ErrorType::InvalidArgument)),
    396        };
    397        let mut results = Vec::with_capacity(attr_types.len());
    398        for attr_type in attr_types {
    399            let result = object
    400                .get_attribute(attr_type)
    401                .map(|value| value.to_owned());
    402            results.push(result);
    403        }
    404        Ok(results)
    405    }
    406 
    407    /// The way NSS uses PKCS #11 to sign data happens in two phases: setup and sign. This
    408    /// implementation makes a note of which key is to be used (if it exists) during setup. When the
    409    /// caller finishes with the sign operation, this implementation retrieves the key handle and
    410    /// performs the signature.
    411    pub fn start_sign(
    412        &mut self,
    413        session: CK_SESSION_HANDLE,
    414        key_handle: CK_OBJECT_HANDLE,
    415        params: Option<CK_RSA_PKCS_PSS_PARAMS>,
    416    ) -> Result<(), Error> {
    417        if self.signs.contains_key(&session) {
    418            return Err(error_here!(ErrorType::InvalidArgument));
    419        }
    420        self.signs.insert(session, (key_handle, params));
    421        Ok(())
    422    }
    423 
    424    pub fn get_signature_length(
    425        &mut self,
    426        session: CK_SESSION_HANDLE,
    427        data: Vec<u8>,
    428    ) -> Result<usize, Error> {
    429        // Take ownership of the key handle and params of the sign (this has the added benefit of
    430        // removing this data in case of an error).
    431        let (key_handle, params) = match self.signs.remove(&session) {
    432            Some((key_handle, params)) => (key_handle, params),
    433            None => return Err(error_here!(ErrorType::InvalidArgument)),
    434        };
    435        let Some(slot_id) = self.sessions.get(&session) else {
    436            return Err(error_here!(ErrorType::InvalidArgument));
    437        };
    438        let slot = self.slot_id_to_slot_mut(*slot_id)?;
    439        let key = match slot.objects.get_mut(&key_handle) {
    440            Some(key) => key,
    441            None => return Err(error_here!(ErrorType::InvalidArgument)),
    442        };
    443        let signature_length = key.get_signature_length(data, &params)?;
    444        // Re-add the key handle and params if getting the signature length succeeded.
    445        self.signs.insert(session, (key_handle, params));
    446        Ok(signature_length)
    447    }
    448 
    449    pub fn sign(&mut self, session: CK_SESSION_HANDLE, data: Vec<u8>) -> Result<Vec<u8>, Error> {
    450        // Performing the signature (via C_Sign, which is the only way we support) finishes the sign
    451        // operation, so it needs to be removed here.
    452        let (key_handle, params) = match self.signs.remove(&session) {
    453            Some((key_handle, params)) => (key_handle, params),
    454            None => return Err(error_here!(ErrorType::InvalidArgument)),
    455        };
    456        let Some(slot_id) = self.sessions.get(&session) else {
    457            return Err(error_here!(ErrorType::InvalidArgument));
    458        };
    459        let slot = self.slot_id_to_slot_mut(*slot_id)?;
    460        let key = match slot.objects.get_mut(&key_handle) {
    461            Some(key) => key,
    462            None => return Err(error_here!(ErrorType::InvalidArgument)),
    463        };
    464        key.sign(data, &params)
    465    }
    466 }