tor-browser

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

backend.rs (8232B)


      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::cryptoki::*;
      8 use rsclientcerts::manager::{ClientCertsBackend, CryptokiObject, Sign};
      9 use rsclientcerts_util::error::{Error, ErrorType};
     10 use rsclientcerts_util::*;
     11 use std::ffi::c_void;
     12 
     13 type FindObjectsCallback = Option<
     14    unsafe extern "C" fn(
     15        typ: u8,
     16        data_len: usize,
     17        data: *const u8,
     18        extra_len: usize,
     19        extra: *const u8,
     20        ctx: *mut c_void,
     21    ),
     22 >;
     23 
     24 // Wrapper of C DoFindObject function implemented in nsNSSIOLayer.h
     25 fn DoFindObjectsWrapper(callback: FindObjectsCallback, ctx: &mut FindObjectsContext) {
     26    // The function makes the parent process to find certificates and keys and send identifying
     27    // information about them over IPC.
     28    extern "C" {
     29        fn DoFindObjects(callback: FindObjectsCallback, ctx: *mut c_void);
     30    }
     31 
     32    unsafe {
     33        DoFindObjects(callback, ctx as *mut _ as *mut c_void);
     34    }
     35 }
     36 
     37 type SignCallback =
     38    Option<unsafe extern "C" fn(data_len: usize, data: *const u8, ctx: *mut c_void)>;
     39 
     40 // Wrapper of C DoSign function implemented in nsNSSIOLayer.h
     41 fn DoSignWrapper(
     42    cert_len: usize,
     43    cert: *const u8,
     44    data_len: usize,
     45    data: *const u8,
     46    params_len: usize,
     47    params: *const u8,
     48    callback: SignCallback,
     49    ctx: &mut Vec<u8>,
     50 ) {
     51    // The function makes the parent to sign the given data using the key corresponding to the
     52    // given certificate, using the given parameters.
     53    extern "C" {
     54        fn DoSign(
     55            cert_len: usize,
     56            cert: *const u8,
     57            data_len: usize,
     58            data: *const u8,
     59            params_len: usize,
     60            params: *const u8,
     61            callback: SignCallback,
     62            ctx: *mut c_void,
     63        );
     64    }
     65 
     66    unsafe {
     67        DoSign(
     68            cert_len,
     69            cert,
     70            data_len,
     71            data,
     72            params_len,
     73            params,
     74            callback,
     75            ctx as *mut _ as *mut c_void,
     76        );
     77    }
     78 }
     79 
     80 pub struct Key {
     81    cryptoki_key: CryptokiKey,
     82    cert: Vec<u8>,
     83 }
     84 
     85 impl Key {
     86    fn new(
     87        modulus: Option<Vec<u8>>,
     88        ec_params: Option<Vec<u8>>,
     89        cert: Vec<u8>,
     90    ) -> Result<Key, Error> {
     91        Ok(Key {
     92            cryptoki_key: CryptokiKey::new(modulus, ec_params, &cert)?,
     93            cert,
     94        })
     95    }
     96 }
     97 
     98 impl CryptokiObject for Key {
     99    fn matches(&self, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool {
    100        self.cryptoki_key.matches(attrs)
    101    }
    102 
    103    fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> {
    104        self.cryptoki_key.get_attribute(attribute)
    105    }
    106 }
    107 
    108 impl Sign for Key {
    109    fn get_signature_length(
    110        &mut self,
    111        data: &[u8],
    112        params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
    113    ) -> Result<usize, Error> {
    114        // Unfortunately we don't have a way of getting the length of a signature without creating
    115        // one.
    116        let dummy_signature_bytes = self.sign(data, params)?;
    117        Ok(dummy_signature_bytes.len())
    118    }
    119 
    120    fn sign(
    121        &mut self,
    122        data: &[u8],
    123        params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
    124    ) -> Result<Vec<u8>, Error> {
    125        let mut signature = Vec::new();
    126        let (sign_params_len, sign_params) = match params {
    127            Some(params) => (
    128                std::mem::size_of::<CK_RSA_PKCS_PSS_PARAMS>(),
    129                params as *const _ as *const u8,
    130            ),
    131            None => (0, std::ptr::null()),
    132        };
    133        DoSignWrapper(
    134            self.cert.len(),
    135            self.cert.as_ptr(),
    136            data.len(),
    137            data.as_ptr(),
    138            sign_params_len,
    139            sign_params,
    140            Some(sign_callback),
    141            &mut signature,
    142        );
    143        // If this succeeded, return the result.
    144        if signature.len() > 0 {
    145            return Ok(signature);
    146        }
    147        // If signing failed and this is an RSA-PSS signature, perhaps the token the key is on does
    148        // not support RSA-PSS. In that case, emsa-pss-encode the data (hash, really) and try
    149        // signing with raw RSA.
    150        let Some(params) = params.as_ref() else {
    151            return Err(error_here!(ErrorType::LibraryFailure));
    152        };
    153        // `params` should only be `Some` if this is an RSA key.
    154        let Some(modulus) = self.cryptoki_key.modulus().as_ref() else {
    155            return Err(error_here!(ErrorType::LibraryFailure));
    156        };
    157        let emsa_pss_encoded = emsa_pss_encode(data, modulus_bit_length(modulus) - 1, params)?;
    158        DoSignWrapper(
    159            self.cert.len(),
    160            self.cert.as_ptr(),
    161            emsa_pss_encoded.len(),
    162            emsa_pss_encoded.as_ptr(),
    163            0,
    164            std::ptr::null(),
    165            Some(sign_callback),
    166            &mut signature,
    167        );
    168        if signature.len() > 0 {
    169            Ok(signature)
    170        } else {
    171            Err(error_here!(ErrorType::LibraryFailure))
    172        }
    173    }
    174 }
    175 
    176 unsafe extern "C" fn sign_callback(data_len: usize, data: *const u8, ctx: *mut c_void) {
    177    let signature: &mut Vec<u8> = std::mem::transmute(ctx);
    178    signature.clear();
    179    if data_len != 0 {
    180        signature.extend_from_slice(std::slice::from_raw_parts(data, data_len));
    181    }
    182 }
    183 
    184 unsafe extern "C" fn find_objects_callback(
    185    typ: u8,
    186    data_len: usize,
    187    data: *const u8,
    188    extra_len: usize,
    189    extra: *const u8,
    190    ctx: *mut c_void,
    191 ) {
    192    let data = if data_len == 0 {
    193        &[]
    194    } else {
    195        std::slice::from_raw_parts(data, data_len)
    196    }
    197    .to_vec();
    198    let extra = if extra_len == 0 {
    199        &[]
    200    } else {
    201        std::slice::from_raw_parts(extra, extra_len)
    202    }
    203    .to_vec();
    204    let find_objects_context: &mut FindObjectsContext = std::mem::transmute(ctx);
    205    match typ {
    206        1 => match CryptokiCert::new(data, b"IPC certificate".to_vec()) {
    207            Ok(cert) => find_objects_context.certs.push(cert),
    208            Err(_) => {}
    209        },
    210        2 => match Key::new(Some(data), None, extra) {
    211            Ok(key) => find_objects_context.keys.push(key),
    212            Err(_) => {}
    213        },
    214        3 => match Key::new(None, Some(data), extra) {
    215            Ok(key) => find_objects_context.keys.push(key),
    216            Err(_) => {}
    217        },
    218        _ => {}
    219    }
    220 }
    221 
    222 struct FindObjectsContext {
    223    certs: Vec<CryptokiCert>,
    224    keys: Vec<Key>,
    225 }
    226 
    227 impl FindObjectsContext {
    228    fn new() -> FindObjectsContext {
    229        FindObjectsContext {
    230            certs: Vec::new(),
    231            keys: Vec::new(),
    232        }
    233    }
    234 }
    235 
    236 pub struct Backend {}
    237 
    238 impl Backend {
    239    pub fn new() -> Backend {
    240        Backend {}
    241    }
    242 }
    243 
    244 const SLOT_DESCRIPTION_BYTES: &[u8; 64] =
    245    b"IPC Client Cert Slot                                            ";
    246 const TOKEN_LABEL_BYTES: &[u8; 32] = b"IPC Client Cert Token           ";
    247 const TOKEN_MODEL_BYTES: &[u8; 16] = b"ipcclientcerts  ";
    248 const TOKEN_SERIAL_NUMBER_BYTES: &[u8; 16] = b"0000000000000000";
    249 
    250 impl ClientCertsBackend for Backend {
    251    type Key = Key;
    252 
    253    fn find_objects(&mut self) -> Result<(Vec<CryptokiCert>, Vec<Key>), Error> {
    254        let mut find_objects_context = FindObjectsContext::new();
    255        DoFindObjectsWrapper(Some(find_objects_callback), &mut find_objects_context);
    256        Ok((find_objects_context.certs, find_objects_context.keys))
    257    }
    258 
    259    fn get_slot_info(&self) -> CK_SLOT_INFO {
    260        CK_SLOT_INFO {
    261            slotDescription: *SLOT_DESCRIPTION_BYTES,
    262            manufacturerID: *crate::MANUFACTURER_ID_BYTES,
    263            flags: CKF_TOKEN_PRESENT,
    264            ..Default::default()
    265        }
    266    }
    267 
    268    fn get_token_info(&self) -> CK_TOKEN_INFO {
    269        CK_TOKEN_INFO {
    270            label: *TOKEN_LABEL_BYTES,
    271            manufacturerID: *crate::MANUFACTURER_ID_BYTES,
    272            model: *TOKEN_MODEL_BYTES,
    273            serialNumber: *TOKEN_SERIAL_NUMBER_BYTES,
    274            ..Default::default()
    275        }
    276    }
    277 
    278    fn get_mechanism_list(&self) -> Vec<CK_MECHANISM_TYPE> {
    279        vec![CKM_ECDSA, CKM_RSA_PKCS, CKM_RSA_PKCS_PSS]
    280    }
    281 }