tor-browser

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

cryptoki.rs (14110B)


      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 byteorder::{NativeEndian, WriteBytesExt};
      7 use digest::{Digest, DynDigest};
      8 use pkcs11_bindings::*;
      9 use rand::rngs::OsRng;
     10 use rand::RngCore;
     11 use rsclientcerts_util::error::{Error, ErrorType};
     12 use rsclientcerts_util::{error_here, read_encoded_certificate_identifiers};
     13 use std::convert::TryInto;
     14 use std::iter::zip;
     15 
     16 use crate::manager::CryptokiObject;
     17 
     18 // The following ENCODED_OID_BYTES_* consist of the encoded bytes of an ASN.1
     19 // OBJECT IDENTIFIER specifying the indicated OID (in other words, the full
     20 // tag, length, and value).
     21 pub const ENCODED_OID_BYTES_SECP256R1: &[u8] =
     22    &[0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07];
     23 pub const ENCODED_OID_BYTES_SECP384R1: &[u8] = &[0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22];
     24 pub const ENCODED_OID_BYTES_SECP521R1: &[u8] = &[0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23];
     25 
     26 // This is a helper function to take a value and lay it out in memory how
     27 // PKCS#11 is expecting it.
     28 pub fn serialize_uint<T: TryInto<u64>>(value: T) -> Result<Vec<u8>, Error> {
     29    let value_size = std::mem::size_of::<T>();
     30    let mut value_buf = Vec::with_capacity(value_size);
     31    let value_as_u64 = value
     32        .try_into()
     33        .map_err(|_| error_here!(ErrorType::ValueTooLarge))?;
     34    value_buf
     35        .write_uint::<NativeEndian>(value_as_u64, value_size)
     36        .map_err(|_| error_here!(ErrorType::LibraryFailure))?;
     37    Ok(value_buf)
     38 }
     39 
     40 fn make_hasher(params: &CK_RSA_PKCS_PSS_PARAMS) -> Result<Box<dyn DynDigest>, Error> {
     41    match params.hashAlg {
     42        CKM_SHA256 => Ok(Box::new(sha2::Sha256::new())),
     43        CKM_SHA384 => Ok(Box::new(sha2::Sha384::new())),
     44        CKM_SHA512 => Ok(Box::new(sha2::Sha512::new())),
     45        _ => Err(error_here!(ErrorType::LibraryFailure)),
     46    }
     47 }
     48 
     49 // Implements MGF1 as per RFC 8017 appendix B.2.1.
     50 fn mgf(
     51    mgf_seed: &[u8],
     52    mask_len: usize,
     53    h_len: usize,
     54    params: &CK_RSA_PKCS_PSS_PARAMS,
     55 ) -> Result<Vec<u8>, Error> {
     56    // 1.  If maskLen > 2^32 hLen, output "mask too long" and stop.
     57    // (in practice, `mask_len` is going to be much smaller than this, so use a
     58    // smaller, fixed limit to avoid problems on systems where usize is 32
     59    // bits)
     60    if mask_len > 1 << 30 {
     61        return Err(error_here!(ErrorType::LibraryFailure));
     62    }
     63    // 2.  Let T be the empty octet string.
     64    let mut t = Vec::with_capacity(mask_len);
     65    // 3.  For counter from 0 to \ceil (maskLen / hLen) - 1, do the
     66    //     following:
     67    for counter in 0..mask_len.div_ceil(h_len) {
     68        // A.  Convert counter to an octet string C of length 4 octets:
     69        //     C = I2OSP (counter, 4)
     70        // (counter fits in u32 due to the length check earlier)
     71        let c = u32::to_be_bytes(counter.try_into().unwrap());
     72        // B.  Concatenate the hash of the seed mgfSeed and C to the octet
     73        //     string T: T = T || Hash(mgfSeed || C)
     74        let mut hasher = make_hasher(params)?;
     75        hasher.update(mgf_seed);
     76        hasher.update(&c);
     77        t.extend_from_slice(&mut hasher.finalize());
     78    }
     79    // 4.  Output the leading maskLen octets of T as the octet string mask.
     80    t.truncate(mask_len);
     81    Ok(t)
     82 }
     83 
     84 pub fn modulus_bit_length(modulus: &[u8]) -> usize {
     85    let mut bit_length = modulus.len() * 8;
     86    for byte in modulus {
     87        if *byte != 0 {
     88            // `byte` is a u8, so `leading_zeros()` will be at most 7.
     89            let leading_zeros: usize = byte.leading_zeros().try_into().unwrap();
     90            bit_length -= leading_zeros;
     91            return bit_length;
     92        }
     93        bit_length -= 8;
     94    }
     95    bit_length
     96 }
     97 
     98 // Implements EMSA-PSS-ENCODE as per RFC 8017 section 9.1.1.
     99 // This is necessary because while Android does support RSA-PSS, it expects to
    100 // be given the entire message to be signed, not just the hash of the message,
    101 // which is what NSS gives us.
    102 // Additionally, this is useful for tokens that do not support RSA-PSS.
    103 pub fn emsa_pss_encode(
    104    m_hash: &[u8],
    105    em_bits: usize,
    106    params: &CK_RSA_PKCS_PSS_PARAMS,
    107 ) -> Result<Vec<u8>, Error> {
    108    let em_len = em_bits.div_ceil(8);
    109    let s_len: usize = params
    110        .sLen
    111        .try_into()
    112        .map_err(|_| error_here!(ErrorType::LibraryFailure))?;
    113 
    114    //  1.   If the length of M is greater than the input limitation for
    115    //       the hash function (2^61 - 1 octets for SHA-1), output
    116    //       "message too long" and stop.
    117    // 2.   Let mHash = Hash(M), an octet string of length hLen.
    118 
    119    // 1 and 2 can be skipped because the message is already hashed as m_hash.
    120 
    121    // 3.   If emLen < hLen + sLen + 2, output "encoding error" and stop.
    122    if em_len < m_hash.len() + s_len + 2 {
    123        return Err(error_here!(ErrorType::LibraryFailure));
    124    }
    125 
    126    // 4.   Generate a random octet string salt of length sLen; if sLen =
    127    //      0, then salt is the empty string.
    128    let salt = {
    129        let mut salt = vec![0u8; s_len];
    130        OsRng.fill_bytes(&mut salt);
    131        salt
    132    };
    133 
    134    // 5.   Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
    135    //      M' is an octet string of length 8 + hLen + sLen with eight
    136    //      initial zero octets.
    137    // 6.   Let H = Hash(M'), an octet string of length hLen.
    138    let mut hasher = make_hasher(params)?;
    139    let h_len = hasher.output_size();
    140    hasher.update(&[0, 0, 0, 0, 0, 0, 0, 0]);
    141    hasher.update(m_hash);
    142    hasher.update(&salt);
    143    let h = hasher.finalize().to_vec();
    144 
    145    // 7.   Generate an octet string PS consisting of emLen - sLen - hLen
    146    //      - 2 zero octets.  The length of PS may be 0.
    147    // 8.   Let DB = PS || 0x01 || salt; DB is an octet string of length
    148    //      emLen - hLen - 1.
    149    // (7 and 8 are unnecessary as separate steps - see step 10)
    150 
    151    // 9.   Let dbMask = MGF(H, emLen - hLen - 1).
    152    let mut db_mask = mgf(&h, em_len - h_len - 1, h_len, params)?;
    153 
    154    // 10.  Let maskedDB = DB \xor dbMask.
    155    // (in practice, this means xoring `0x01 || salt` with the last `s_len + 1`
    156    // bytes of `db_mask`)
    157    let salt_index = db_mask.len() - s_len;
    158    db_mask[salt_index - 1] ^= 1;
    159    for (db_mask_byte, salt_byte) in zip(&mut db_mask[salt_index..], &salt) {
    160        *db_mask_byte ^= salt_byte;
    161    }
    162    let mut masked_db = db_mask;
    163 
    164    // 11.  Set the leftmost 8emLen - emBits bits of the leftmost octet
    165    //      in maskedDB to zero.
    166    // (bit_diff can only be 0 through 7, so it fits in u32)
    167    let bit_diff: u32 = ((8 * em_len) - em_bits).try_into().unwrap();
    168    // (again, bit_diff can only b 0 through 7, so the shift is sound)
    169    masked_db[0] &= 0xffu8.checked_shr(bit_diff).unwrap();
    170 
    171    // 12.  Let EM = maskedDB || H || 0xbc.
    172    let mut em = masked_db;
    173    em.extend_from_slice(&h);
    174    em.push(0xbc);
    175 
    176    Ok(em)
    177 }
    178 
    179 /// A `CryptokiCert` holds all relevant information for a `CryptokiObject` with class
    180 /// `CKO_CERTIFICATE`.
    181 #[derive(Clone)]
    182 pub struct CryptokiCert {
    183    /// PKCS #11 object class. Will be `CKO_CERTIFICATE`.
    184    class: Vec<u8>,
    185    /// Whether or not this is on a token. Will be `CK_TRUE`.
    186    token: Vec<u8>,
    187    /// An identifier unique to this certificate. This must be the same as the ID for the private
    188    /// key, so for simplicity, this will be the sha256 hash of the bytes of the certificate.
    189    id: Vec<u8>,
    190    /// The bytes of a human-readable label for this certificate.
    191    label: Vec<u8>,
    192    /// The DER bytes of the certificate.
    193    value: Vec<u8>,
    194    /// The DER bytes of the issuer distinguished name of the certificate.
    195    issuer: Vec<u8>,
    196    /// The DER bytes of the serial number of the certificate.
    197    serial_number: Vec<u8>,
    198    /// The DER bytes of the subject distinguished name of the certificate.
    199    subject: Vec<u8>,
    200 }
    201 
    202 impl CryptokiCert {
    203    pub fn new(der: Vec<u8>, label: Vec<u8>) -> Result<CryptokiCert, Error> {
    204        let id = sha2::Sha256::digest(&der).to_vec();
    205        let (serial_number, issuer, subject) = read_encoded_certificate_identifiers(&der)?;
    206        Ok(CryptokiCert {
    207            class: serialize_uint(CKO_CERTIFICATE)?,
    208            token: serialize_uint(CK_TRUE)?,
    209            id,
    210            label,
    211            value: der,
    212            issuer,
    213            serial_number,
    214            subject,
    215        })
    216    }
    217 }
    218 
    219 impl CryptokiObject for CryptokiCert {
    220    fn matches(&self, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool {
    221        for (attr_type, attr_value) in attrs {
    222            let comparison = match *attr_type {
    223                CKA_CLASS => &self.class,
    224                CKA_TOKEN => &self.token,
    225                CKA_LABEL => &self.label,
    226                CKA_ID => &self.id,
    227                CKA_VALUE => &self.value,
    228                CKA_ISSUER => &self.issuer,
    229                CKA_SERIAL_NUMBER => &self.serial_number,
    230                CKA_SUBJECT => &self.subject,
    231                _ => return false,
    232            };
    233            if attr_value.as_slice() != comparison {
    234                return false;
    235            }
    236        }
    237        true
    238    }
    239 
    240    fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> {
    241        let result = match attribute {
    242            CKA_CLASS => &self.class,
    243            CKA_TOKEN => &self.token,
    244            CKA_LABEL => &self.label,
    245            CKA_ID => &self.id,
    246            CKA_VALUE => &self.value,
    247            CKA_ISSUER => &self.issuer,
    248            CKA_SERIAL_NUMBER => &self.serial_number,
    249            CKA_SUBJECT => &self.subject,
    250            _ => return None,
    251        };
    252        Some(result)
    253    }
    254 }
    255 
    256 #[allow(clippy::upper_case_acronyms)]
    257 #[derive(Clone, Copy, Debug)]
    258 pub enum KeyType {
    259    EC(usize),
    260    RSA,
    261 }
    262 
    263 /// A `CryptokiKey` holds all relevant information for a `CryptokiObject` with class
    264 /// `CKO_PRIVATE_KEY`.
    265 #[derive(Clone)]
    266 pub struct CryptokiKey {
    267    /// PKCS #11 object class. Will be `CKO_PRIVATE_KEY`.
    268    class: Vec<u8>,
    269    /// Whether or not this is on a token. Will be `CK_TRUE`.
    270    token: Vec<u8>,
    271    /// An identifier unique to this key. This must be the same as the ID for a corresponding
    272    /// certificate, so for simplicity, this will be the sha256 hash of the bytes of the
    273    /// certificate.
    274    id: Vec<u8>,
    275    /// Whether or not this key is "private" (can it be exported?). Will be CK_TRUE (it can't be
    276    /// exported).
    277    private: Vec<u8>,
    278    /// PKCS #11 key type. Will be `CKK_EC` for EC, and `CKK_RSA` for RSA.
    279    key_type_attribute: Vec<u8>,
    280    /// If this is an RSA key, this is the value of the modulus as an unsigned integer.
    281    modulus: Option<Vec<u8>>,
    282    /// If this is an EC key, this is the DER bytes of the OID identifying the curve the key is on.
    283    ec_params: Option<Vec<u8>>,
    284    /// An enum identifying this key's type.
    285    key_type: KeyType,
    286 }
    287 
    288 impl CryptokiKey {
    289    pub fn new(
    290        modulus: Option<Vec<u8>>,
    291        ec_params: Option<Vec<u8>>,
    292        cert: &[u8],
    293    ) -> Result<CryptokiKey, Error> {
    294        let (key_type, key_type_attribute) = if modulus.is_some() {
    295            (KeyType::RSA, CKK_RSA)
    296        } else if let Some(ec_params) = ec_params.as_ref() {
    297            // Only secp256r1, secp384r1, and secp521r1 are supported.
    298            let coordinate_width = match ec_params.as_slice() {
    299                ENCODED_OID_BYTES_SECP256R1 => 32,
    300                ENCODED_OID_BYTES_SECP384R1 => 48,
    301                ENCODED_OID_BYTES_SECP521R1 => 66,
    302                _ => return Err(error_here!(ErrorType::UnsupportedInput)),
    303            };
    304            (KeyType::EC(coordinate_width), CKK_EC)
    305        } else {
    306            return Err(error_here!(ErrorType::LibraryFailure));
    307        };
    308        let id = sha2::Sha256::digest(cert).to_vec();
    309        Ok(CryptokiKey {
    310            class: serialize_uint(CKO_PRIVATE_KEY)?,
    311            token: serialize_uint(CK_TRUE)?,
    312            id,
    313            private: serialize_uint(CK_TRUE)?,
    314            key_type_attribute: serialize_uint(key_type_attribute)?,
    315            modulus,
    316            ec_params,
    317            key_type,
    318        })
    319    }
    320 
    321    pub fn modulus(&self) -> &Option<Vec<u8>> {
    322        &self.modulus
    323    }
    324 
    325    pub fn ec_params(&self) -> &Option<Vec<u8>> {
    326        &self.ec_params
    327    }
    328 
    329    pub fn key_type(&self) -> KeyType {
    330        self.key_type
    331    }
    332 }
    333 
    334 impl CryptokiObject for CryptokiKey {
    335    fn matches(&self, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool {
    336        for (attr_type, attr_value) in attrs {
    337            let comparison = match *attr_type {
    338                CKA_CLASS => &self.class,
    339                CKA_TOKEN => &self.token,
    340                CKA_ID => &self.id,
    341                CKA_PRIVATE => &self.private,
    342                CKA_KEY_TYPE => &self.key_type_attribute,
    343                CKA_MODULUS => {
    344                    if let Some(modulus) = &self.modulus {
    345                        modulus
    346                    } else {
    347                        return false;
    348                    }
    349                }
    350                CKA_EC_PARAMS => {
    351                    if let Some(ec_params) = &self.ec_params {
    352                        ec_params
    353                    } else {
    354                        return false;
    355                    }
    356                }
    357                _ => return false,
    358            };
    359            if attr_value.as_slice() != comparison {
    360                return false;
    361            }
    362        }
    363        true
    364    }
    365 
    366    fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> {
    367        match attribute {
    368            CKA_CLASS => Some(&self.class),
    369            CKA_TOKEN => Some(&self.token),
    370            CKA_ID => Some(&self.id),
    371            CKA_PRIVATE => Some(&self.private),
    372            CKA_KEY_TYPE => Some(&self.key_type_attribute),
    373            CKA_MODULUS => match &self.modulus {
    374                Some(modulus) => Some(modulus.as_slice()),
    375                None => None,
    376            },
    377            CKA_EC_PARAMS => match &self.ec_params {
    378                Some(ec_params) => Some(ec_params.as_slice()),
    379                None => None,
    380            },
    381            _ => None,
    382        }
    383    }
    384 }