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 }