diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /security/manager/ssl/ipcclientcerts/src/backend.rs | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/manager/ssl/ipcclientcerts/src/backend.rs')
-rw-r--r-- | security/manager/ssl/ipcclientcerts/src/backend.rs | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/security/manager/ssl/ipcclientcerts/src/backend.rs b/security/manager/ssl/ipcclientcerts/src/backend.rs new file mode 100644 index 0000000000..534eecd47e --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/src/backend.rs @@ -0,0 +1,373 @@ +/* -*- Mode: rust; rust-indent-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use pkcs11_bindings::*; +use rsclientcerts::error::{Error, ErrorType}; +use rsclientcerts::manager::{ClientCertsBackend, CryptokiObject, Sign, SlotType}; +use rsclientcerts::util::*; +use sha2::{Digest, Sha256}; +use std::ffi::c_void; + +use crate::FindObjectsFunction; +use crate::SignFunction; + +pub struct Cert { + class: Vec<u8>, + token: Vec<u8>, + id: Vec<u8>, + label: Vec<u8>, + value: Vec<u8>, + issuer: Vec<u8>, + serial_number: Vec<u8>, + subject: Vec<u8>, + slot_type: SlotType, +} + +impl Cert { + fn new(der: &[u8], slot_type: SlotType) -> Result<Cert, Error> { + let (serial_number, issuer, subject) = read_encoded_certificate_identifiers(der)?; + let id = Sha256::digest(der).to_vec(); + Ok(Cert { + class: serialize_uint(CKO_CERTIFICATE)?, + token: serialize_uint(CK_TRUE)?, + id, + label: b"IPC certificate".to_vec(), + value: der.to_vec(), + issuer, + serial_number, + subject, + slot_type, + }) + } + + fn class(&self) -> &[u8] { + &self.class + } + + fn token(&self) -> &[u8] { + &self.token + } + + fn id(&self) -> &[u8] { + &self.id + } + + fn label(&self) -> &[u8] { + &self.label + } + + fn value(&self) -> &[u8] { + &self.value + } + + fn issuer(&self) -> &[u8] { + &self.issuer + } + + fn serial_number(&self) -> &[u8] { + &self.serial_number + } + + fn subject(&self) -> &[u8] { + &self.subject + } +} + +impl CryptokiObject for Cert { + fn matches(&self, slot_type: SlotType, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool { + if self.slot_type != slot_type { + return false; + } + for (attr_type, attr_value) in attrs { + let comparison = match *attr_type { + CKA_CLASS => self.class(), + CKA_TOKEN => self.token(), + CKA_LABEL => self.label(), + CKA_ID => self.id(), + CKA_VALUE => self.value(), + CKA_ISSUER => self.issuer(), + CKA_SERIAL_NUMBER => self.serial_number(), + CKA_SUBJECT => self.subject(), + _ => return false, + }; + if attr_value.as_slice() != comparison { + return false; + } + } + true + } + + fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> { + let result = match attribute { + CKA_CLASS => self.class(), + CKA_TOKEN => self.token(), + CKA_LABEL => self.label(), + CKA_ID => self.id(), + CKA_VALUE => self.value(), + CKA_ISSUER => self.issuer(), + CKA_SERIAL_NUMBER => self.serial_number(), + CKA_SUBJECT => self.subject(), + _ => return None, + }; + Some(result) + } +} + +pub struct Key { + cert: Vec<u8>, + class: Vec<u8>, + token: Vec<u8>, + id: Vec<u8>, + private: Vec<u8>, + key_type: Vec<u8>, + modulus: Option<Vec<u8>>, + ec_params: Option<Vec<u8>>, + slot_type: SlotType, + sign: SignFunction, +} + +impl Key { + fn new( + modulus: Option<&[u8]>, + ec_params: Option<&[u8]>, + cert: &[u8], + slot_type: SlotType, + sign: SignFunction, + ) -> Result<Key, Error> { + let id = Sha256::digest(cert).to_vec(); + let key_type = if modulus.is_some() { CKK_RSA } else { CKK_EC }; + Ok(Key { + cert: cert.to_vec(), + class: serialize_uint(CKO_PRIVATE_KEY)?, + token: serialize_uint(CK_TRUE)?, + id, + private: serialize_uint(CK_TRUE)?, + key_type: serialize_uint(key_type)?, + modulus: modulus.map(|b| b.to_vec()), + ec_params: ec_params.map(|b| b.to_vec()), + slot_type, + sign, + }) + } + + fn class(&self) -> &[u8] { + &self.class + } + + fn token(&self) -> &[u8] { + &self.token + } + + pub fn id(&self) -> &[u8] { + &self.id + } + + fn private(&self) -> &[u8] { + &self.private + } + + fn key_type(&self) -> &[u8] { + &self.key_type + } + + fn modulus(&self) -> Option<&[u8]> { + match &self.modulus { + Some(modulus) => Some(modulus.as_slice()), + None => None, + } + } + + fn ec_params(&self) -> Option<&[u8]> { + match &self.ec_params { + Some(ec_params) => Some(ec_params.as_slice()), + None => None, + } + } +} + +impl CryptokiObject for Key { + fn matches(&self, slot_type: SlotType, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool { + if self.slot_type != slot_type { + return false; + } + for (attr_type, attr_value) in attrs { + let comparison = match *attr_type { + CKA_CLASS => self.class(), + CKA_TOKEN => self.token(), + CKA_ID => self.id(), + CKA_PRIVATE => self.private(), + CKA_KEY_TYPE => self.key_type(), + CKA_MODULUS => { + if let Some(modulus) = self.modulus() { + modulus + } else { + return false; + } + } + CKA_EC_PARAMS => { + if let Some(ec_params) = self.ec_params() { + ec_params + } else { + return false; + } + } + _ => return false, + }; + if attr_value.as_slice() != comparison { + return false; + } + } + true + } + + fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> { + match attribute { + CKA_CLASS => Some(self.class()), + CKA_TOKEN => Some(self.token()), + CKA_ID => Some(self.id()), + CKA_PRIVATE => Some(self.private()), + CKA_KEY_TYPE => Some(self.key_type()), + CKA_MODULUS => self.modulus(), + CKA_EC_PARAMS => self.ec_params(), + _ => None, + } + } +} + +impl Sign for Key { + fn get_signature_length( + &mut self, + data: &[u8], + params: &Option<CK_RSA_PKCS_PSS_PARAMS>, + ) -> Result<usize, Error> { + // Unfortunately we don't have a way of getting the length of a signature without creating + // one. + let dummy_signature_bytes = self.sign(data, params)?; + Ok(dummy_signature_bytes.len()) + } + + fn sign( + &mut self, + data: &[u8], + params: &Option<CK_RSA_PKCS_PSS_PARAMS>, + ) -> Result<Vec<u8>, Error> { + let mut signature = Vec::new(); + let (params_len, params) = match params { + Some(params) => ( + std::mem::size_of::<CK_RSA_PKCS_PSS_PARAMS>(), + params as *const _ as *const u8, + ), + None => (0, std::ptr::null()), + }; + (self.sign)( + self.cert.len(), + self.cert.as_ptr(), + data.len(), + data.as_ptr(), + params_len, + params, + Some(sign_callback), + &mut signature as *mut _ as *mut c_void, + ); + if signature.len() > 0 { + Ok(signature) + } else { + Err(error_here!(ErrorType::LibraryFailure)) + } + } +} + +unsafe extern "C" fn sign_callback(data_len: usize, data: *const u8, ctx: *mut c_void) { + let signature: &mut Vec<u8> = std::mem::transmute(ctx); + signature.clear(); + signature.extend_from_slice(std::slice::from_raw_parts(data, data_len)); +} + +unsafe extern "C" fn find_objects_callback( + typ: u8, + data_len: usize, + data: *const u8, + extra_len: usize, + extra: *const u8, + slot_type: u32, + ctx: *mut c_void, +) { + let data = std::slice::from_raw_parts(data, data_len); + let extra = std::slice::from_raw_parts(extra, extra_len); + let slot_type = match slot_type { + 1 => SlotType::Modern, + 2 => SlotType::Legacy, + _ => return, + }; + let find_objects_context: &mut FindObjectsContext = std::mem::transmute(ctx); + match typ { + 1 => match Cert::new(data, slot_type) { + Ok(cert) => find_objects_context.certs.push(cert), + Err(_) => {} + }, + 2 => match Key::new( + Some(data), + None, + extra, + slot_type, + find_objects_context.sign, + ) { + Ok(key) => find_objects_context.keys.push(key), + Err(_) => {} + }, + 3 => match Key::new( + None, + Some(data), + extra, + slot_type, + find_objects_context.sign, + ) { + Ok(key) => find_objects_context.keys.push(key), + Err(_) => {} + }, + _ => {} + } +} + +struct FindObjectsContext { + certs: Vec<Cert>, + keys: Vec<Key>, + sign: SignFunction, +} + +impl FindObjectsContext { + fn new(sign: SignFunction) -> FindObjectsContext { + FindObjectsContext { + certs: Vec::new(), + keys: Vec::new(), + sign, + } + } +} + +pub struct Backend { + find_objects: FindObjectsFunction, + sign: SignFunction, +} + +impl Backend { + pub fn new(find_objects: FindObjectsFunction, sign: SignFunction) -> Backend { + Backend { find_objects, sign } + } +} + +impl ClientCertsBackend for Backend { + type Cert = Cert; + type Key = Key; + + fn find_objects(&self) -> Result<(Vec<Cert>, Vec<Key>), Error> { + let mut find_objects_context = FindObjectsContext::new(self.sign); + (self.find_objects)( + Some(find_objects_callback), + &mut find_objects_context as *mut _ as *mut c_void, + ); + Ok((find_objects_context.certs, find_objects_context.keys)) + } +} |