diff options
Diffstat (limited to 'security/manager/ssl/ipcclientcerts')
8 files changed, 1556 insertions, 0 deletions
diff --git a/security/manager/ssl/ipcclientcerts/Cargo.toml b/security/manager/ssl/ipcclientcerts/Cargo.toml new file mode 100644 index 0000000000..3ed9e61907 --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ipcclientcerts-static" +version = "0.1.0" +authors = ["Dana Keeler <dkeeler@mozilla.com>"] +edition = "2018" +license = "MPL-2.0" + +[dependencies] +byteorder = "1.3" +pkcs11-bindings = "0.1" +rsclientcerts = { path = "../rsclientcerts" } +sha2 = "0.10.2" + +[lib] +crate-type = ["staticlib"] diff --git a/security/manager/ssl/ipcclientcerts/dynamic-library/ipcclientcerts.symbols b/security/manager/ssl/ipcclientcerts/dynamic-library/ipcclientcerts.symbols new file mode 100644 index 0000000000..562ecea21d --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/dynamic-library/ipcclientcerts.symbols @@ -0,0 +1 @@ +C_GetFunctionList diff --git a/security/manager/ssl/ipcclientcerts/dynamic-library/moz.build b/security/manager/ssl/ipcclientcerts/dynamic-library/moz.build new file mode 100644 index 0000000000..12ae33de7b --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/dynamic-library/moz.build @@ -0,0 +1,39 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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_LIBS += ["ipcclientcerts-static"] + +# On Linux (but not when building for Android), this needs to use the C++ +# version to avoid linking against the wrong libc symbols. +# On Android, this needs to use the C version to avoid multiple definitions +# of symbols caused by their presence in libgcc and ipcclientcerts-static. +if CONFIG["OS_ARCH"] == "Linux" and CONFIG["OS_TARGET"] != "Android": + SOURCES += [ + "stub.cpp", + ] +else: + SOURCES += [ + "stub.c", + ] + +if CONFIG["OS_TARGET"] == "Android": + OS_LIBS += ["m"] + +if CONFIG["OS_ARCH"] == "WINNT": + OS_LIBS += [ + "advapi32", + "userenv", + "ws2_32", + ] + OS_LIBS += [ + "bcrypt", + ] + + +SharedLibrary("ipcclientcerts") + +NoVisibilityFlags() +SYMBOLS_FILE = "ipcclientcerts.symbols" diff --git a/security/manager/ssl/ipcclientcerts/dynamic-library/stub.c b/security/manager/ssl/ipcclientcerts/dynamic-library/stub.c new file mode 100644 index 0000000000..f679e5dfd4 --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/dynamic-library/stub.c @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "pkcs11.h" + +// The build system builds the rust library ipcclientcerts as a static library +// called ipcclientcerts_static. On macOS and Windows, that static library can +// be linked with an empty file and turned into a shared library with the +// function C_GetFunctionList exposed. This allows that shared library to be +// used as a PKCS#11 module (see osclientcerts). +// Unfortunately, on Linux, exposing the C_GetFunctionList in the static +// library doesn't work for some unknown reason. As a workaround, this file +// declares its own C_GetFunctionList that can be exposed in the shared +// library. It then calls the function IPCCC_GetFunctionList exposed +// (internally to the linkage in question) by ipcclientcerts. This enables +// the build system to ultimately turn ipcclientcerts into a shared library +// that exposes a C_GetFunctionList function, meaning it can be used as a +// PKCS#11 module. + +CK_RV IPCCC_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList); + +CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { + return IPCCC_GetFunctionList(ppFunctionList); +} + +// On MinGW there's a toolchain mismatch that results in _Unwind_Resume being +// undefined. It's never going to get called here anyway, so this defines it to +// satisfy the linker. See bug 1745855 and +// https://github.com/rust-lang/rust/issues/79609#issuecomment-987107562. +#ifdef __MINGW32__ +# include "mozilla/Assertions.h" +void _Unwind_Resume() { MOZ_CRASH("Unexpected call to _Unwind_*"); } +void _Unwind_GetDataRelBase() { _Unwind_Resume(); } +void _Unwind_GetTextRelBase() { _Unwind_Resume(); } +void _Unwind_GetLanguageSpecificData() { _Unwind_Resume(); } +void _Unwind_GetIPInfo() { _Unwind_Resume(); } +void _Unwind_GetRegionStart() { _Unwind_Resume(); } +void _Unwind_SetGR() { _Unwind_Resume(); } +void _Unwind_SetIP() { _Unwind_Resume(); } +void _GCC_specific_handler() { _Unwind_Resume(); } +#endif diff --git a/security/manager/ssl/ipcclientcerts/dynamic-library/stub.cpp b/security/manager/ssl/ipcclientcerts/dynamic-library/stub.cpp new file mode 100644 index 0000000000..e50f867574 --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/dynamic-library/stub.cpp @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "pkcs11.h" + +// The build system builds the rust library ipcclientcerts as a static library +// called ipcclientcerts_static. On macOS and Windows, that static library can +// be linked with an empty file and turned into a shared library with the +// function C_GetFunctionList exposed. This allows that shared library to be +// used as a PKCS#11 module (see osclientcerts). +// Unfortunately, on Linux, exposing the C_GetFunctionList in the static +// library doesn't work for some unknown reason. As a workaround, this file +// declares its own C_GetFunctionList that can be exposed in the shared +// library. It then calls the function IPCCC_GetFunctionList exposed +// (internally to the linkage in question) by ipcclientcerts. This enables +// the build system to ultimately turn ipcclientcerts into a shared library +// that exposes a C_GetFunctionList function, meaning it can be used as a +// PKCS#11 module. + +extern "C" { + +CK_RV IPCCC_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList); + +CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { + return IPCCC_GetFunctionList(ppFunctionList); +} +} diff --git a/security/manager/ssl/ipcclientcerts/moz.build b/security/manager/ssl/ipcclientcerts/moz.build new file mode 100644 index 0000000000..1d24bdea31 --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DIRS += ["dynamic-library"] + +RustLibrary("ipcclientcerts-static") 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)) + } +} diff --git a/security/manager/ssl/ipcclientcerts/src/lib.rs b/security/manager/ssl/ipcclientcerts/src/lib.rs new file mode 100644 index 0000000000..a1a276df9d --- /dev/null +++ b/security/manager/ssl/ipcclientcerts/src/lib.rs @@ -0,0 +1,1047 @@ +/* -*- 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/. */ + +#![allow(non_snake_case)] + +extern crate byteorder; +extern crate pkcs11_bindings; +#[macro_use] +extern crate rsclientcerts; +extern crate sha2; + +use pkcs11_bindings::*; +use rsclientcerts::manager::{Manager, SlotType}; +use std::ffi::{c_void, CStr}; +use std::sync::Mutex; + +mod backend; + +use backend::Backend; + +type FindObjectsCallback = Option< + unsafe extern "C" fn( + typ: u8, + data_len: usize, + data: *const u8, + extra_len: usize, + extra: *const u8, + slot_type: u32, + ctx: *mut c_void, + ), +>; + +type FindObjectsFunction = extern "C" fn(callback: FindObjectsCallback, ctx: *mut c_void); + +type SignCallback = + Option<unsafe extern "C" fn(data_len: usize, data: *const u8, ctx: *mut c_void)>; + +type SignFunction = extern "C" fn( + cert_len: usize, + cert: *const u8, + data_len: usize, + data: *const u8, + params_len: usize, + params: *const u8, + callback: SignCallback, + ctx: *mut c_void, +); + +/// The singleton `Manager` that handles state with respect to PKCS #11. Only one thread +/// may use it at a time, but there is no restriction on which threads may use it. +static MANAGER: Mutex<Option<Manager<Backend>>> = Mutex::new(None); + +// Obtaining a handle on the manager is a two-step process. First the mutex must be locked, which +// (if successful), results in a mutex guard object. We must then get a mutable refence to the +// underlying manager (if set - otherwise we return an error). This can't happen all in one macro +// without dropping a reference that needs to live long enough for this to be safe. In +// practice, this looks like: +// let mut manager_guard = try_to_get_manager_guard!(); +// let manager = manager_guard_to_manager!(manager_guard); +macro_rules! try_to_get_manager_guard { + () => { + match MANAGER.lock() { + Ok(maybe_manager) => maybe_manager, + Err(_) => return CKR_DEVICE_ERROR, + } + }; +} + +macro_rules! manager_guard_to_manager { + ($manager_guard:ident) => { + match $manager_guard.as_mut() { + Some(manager) => manager, + None => return CKR_DEVICE_ERROR, + } + }; +} + +/// This gets called to initialize the module. For this implementation, this consists of +/// instantiating the `Manager`. +extern "C" fn C_Initialize(pInitArgs: CK_VOID_PTR) -> CK_RV { + // pInitArgs.pReserved will be a c-string containing the base-16 + // stringification of the addresses of the functions to call to communicate + // with the main process. + if pInitArgs.is_null() { + return CKR_DEVICE_ERROR; + } + let serialized_addresses_ptr = unsafe { (*(pInitArgs as CK_C_INITIALIZE_ARGS_PTR)).pReserved }; + if serialized_addresses_ptr.is_null() { + return CKR_DEVICE_ERROR; + } + let serialized_addresses_cstr = + unsafe { CStr::from_ptr(serialized_addresses_ptr as *mut std::os::raw::c_char) }; + let serialized_addresses = match serialized_addresses_cstr.to_str() { + Ok(serialized_addresses) => serialized_addresses, + Err(_) => return CKR_DEVICE_ERROR, + }; + let function_addresses: Vec<usize> = serialized_addresses + .split(',') + .filter_map(|serialized_address| usize::from_str_radix(serialized_address, 16).ok()) + .collect(); + if function_addresses.len() != 2 { + return CKR_DEVICE_ERROR; + } + let find_objects: FindObjectsFunction = unsafe { std::mem::transmute(function_addresses[0]) }; + let sign: SignFunction = unsafe { std::mem::transmute(function_addresses[1]) }; + let mut manager_guard = try_to_get_manager_guard!(); + let _unexpected_previous_manager = + manager_guard.replace(Manager::new(Backend::new(find_objects, sign))); + CKR_OK +} + +extern "C" fn C_Finalize(_pReserved: CK_VOID_PTR) -> CK_RV { + // Drop the manager. When C_Finalize is called, there should be only one + // reference to this module (which is going away), so there shouldn't be + // any concurrency issues. + let mut manager_guard = try_to_get_manager_guard!(); + match manager_guard.take() { + Some(_) => CKR_OK, + None => CKR_CRYPTOKI_NOT_INITIALIZED, + } +} + +// The specification mandates that these strings be padded with spaces to the appropriate length. +// Since the length of fixed-size arrays in rust is part of the type, the compiler enforces that +// these byte strings are of the correct length. +const MANUFACTURER_ID_BYTES: &[u8; 32] = b"Mozilla Corporation "; +const LIBRARY_DESCRIPTION_BYTES: &[u8; 32] = b"IPC Client Cert Module "; + +/// This gets called to gather some information about the module. In particular, this implementation +/// supports (portions of) cryptoki (PKCS #11) version 2.2. +extern "C" fn C_GetInfo(pInfo: CK_INFO_PTR) -> CK_RV { + if pInfo.is_null() { + return CKR_ARGUMENTS_BAD; + } + let mut info = CK_INFO::default(); + info.cryptokiVersion.major = 2; + info.cryptokiVersion.minor = 2; + info.manufacturerID = *MANUFACTURER_ID_BYTES; + info.libraryDescription = *LIBRARY_DESCRIPTION_BYTES; + unsafe { + *pInfo = info; + } + CKR_OK +} + +/// This module has two slots. +const SLOT_COUNT: CK_ULONG = 2; +/// The slot with ID 1 supports modern mechanisms like RSA-PSS. +const SLOT_ID_MODERN: CK_SLOT_ID = 1; +/// The slot with ID 2 only supports legacy mechanisms. +const SLOT_ID_LEGACY: CK_SLOT_ID = 2; + +/// This gets called twice: once with a null `pSlotList` to get the number of slots (returned via +/// `pulCount`) and a second time to get the ID for each slot. +extern "C" fn C_GetSlotList( + _tokenPresent: CK_BBOOL, + pSlotList: CK_SLOT_ID_PTR, + pulCount: CK_ULONG_PTR, +) -> CK_RV { + if pulCount.is_null() { + return CKR_ARGUMENTS_BAD; + } + if !pSlotList.is_null() { + if unsafe { *pulCount } < SLOT_COUNT { + return CKR_BUFFER_TOO_SMALL; + } + unsafe { + *pSlotList = SLOT_ID_MODERN; + *pSlotList.offset(1) = SLOT_ID_LEGACY; + } + }; + unsafe { + *pulCount = SLOT_COUNT; + } + CKR_OK +} + +const SLOT_DESCRIPTION_MODERN_BYTES: &[u8; 64] = + b"IPC Client Cert Slot (Modern) "; +const SLOT_DESCRIPTION_LEGACY_BYTES: &[u8; 64] = + b"IPC Client Cert Slot (Legacy) "; + +/// This gets called to obtain information about slots. In this implementation, the tokens are +/// always present in the slots. +extern "C" fn C_GetSlotInfo(slotID: CK_SLOT_ID, pInfo: CK_SLOT_INFO_PTR) -> CK_RV { + if (slotID != SLOT_ID_MODERN && slotID != SLOT_ID_LEGACY) || pInfo.is_null() { + return CKR_ARGUMENTS_BAD; + } + let description = if slotID == SLOT_ID_MODERN { + SLOT_DESCRIPTION_MODERN_BYTES + } else { + SLOT_DESCRIPTION_LEGACY_BYTES + }; + let slot_info = CK_SLOT_INFO { + slotDescription: *description, + manufacturerID: *MANUFACTURER_ID_BYTES, + flags: CKF_TOKEN_PRESENT, + hardwareVersion: CK_VERSION::default(), + firmwareVersion: CK_VERSION::default(), + }; + unsafe { + *pInfo = slot_info; + } + CKR_OK +} + +const TOKEN_LABEL_MODERN_BYTES: &[u8; 32] = b"IPC Client Cert Token (Modern) "; +const TOKEN_LABEL_LEGACY_BYTES: &[u8; 32] = b"IPC Client Cert Token (Legacy) "; +const TOKEN_MODEL_BYTES: &[u8; 16] = b"ipcclientcerts "; +const TOKEN_SERIAL_NUMBER_BYTES: &[u8; 16] = b"0000000000000000"; + +/// This gets called to obtain some information about tokens. This implementation has two slots, +/// so it has two tokens. This information is primarily for display purposes. +extern "C" fn C_GetTokenInfo(slotID: CK_SLOT_ID, pInfo: CK_TOKEN_INFO_PTR) -> CK_RV { + if (slotID != SLOT_ID_MODERN && slotID != SLOT_ID_LEGACY) || pInfo.is_null() { + return CKR_ARGUMENTS_BAD; + } + let mut token_info = CK_TOKEN_INFO::default(); + let label = if slotID == SLOT_ID_MODERN { + TOKEN_LABEL_MODERN_BYTES + } else { + TOKEN_LABEL_LEGACY_BYTES + }; + token_info.label = *label; + token_info.manufacturerID = *MANUFACTURER_ID_BYTES; + token_info.model = *TOKEN_MODEL_BYTES; + token_info.serialNumber = *TOKEN_SERIAL_NUMBER_BYTES; + unsafe { + *pInfo = token_info; + } + CKR_OK +} + +/// This gets called to determine what mechanisms a slot supports. The modern slot supports ECDSA, +/// RSA PKCS, and RSA PSS. The legacy slot only supports RSA PKCS. +extern "C" fn C_GetMechanismList( + slotID: CK_SLOT_ID, + pMechanismList: CK_MECHANISM_TYPE_PTR, + pulCount: CK_ULONG_PTR, +) -> CK_RV { + if (slotID != SLOT_ID_MODERN && slotID != SLOT_ID_LEGACY) || pulCount.is_null() { + return CKR_ARGUMENTS_BAD; + } + let mechanisms = if slotID == SLOT_ID_MODERN { + vec![CKM_ECDSA, CKM_RSA_PKCS, CKM_RSA_PKCS_PSS] + } else { + vec![CKM_RSA_PKCS] + }; + if !pMechanismList.is_null() { + if unsafe { *pulCount as usize } < mechanisms.len() { + return CKR_ARGUMENTS_BAD; + } + for i in 0..mechanisms.len() { + unsafe { + *pMechanismList.offset(i as isize) = mechanisms[i]; + } + } + } + unsafe { + *pulCount = mechanisms.len() as CK_ULONG; + } + CKR_OK +} + +extern "C" fn C_GetMechanismInfo( + _slotID: CK_SLOT_ID, + _type: CK_MECHANISM_TYPE, + _pInfo: CK_MECHANISM_INFO_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_InitToken( + _slotID: CK_SLOT_ID, + _pPin: CK_UTF8CHAR_PTR, + _ulPinLen: CK_ULONG, + _pLabel: CK_UTF8CHAR_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_InitPIN( + _hSession: CK_SESSION_HANDLE, + _pPin: CK_UTF8CHAR_PTR, + _ulPinLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_SetPIN( + _hSession: CK_SESSION_HANDLE, + _pOldPin: CK_UTF8CHAR_PTR, + _ulOldLen: CK_ULONG, + _pNewPin: CK_UTF8CHAR_PTR, + _ulNewLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +/// This gets called to create a new session. This module defers to the `ManagerProxy` to implement +/// this. +extern "C" fn C_OpenSession( + slotID: CK_SLOT_ID, + _flags: CK_FLAGS, + _pApplication: CK_VOID_PTR, + _Notify: CK_NOTIFY, + phSession: CK_SESSION_HANDLE_PTR, +) -> CK_RV { + if (slotID != SLOT_ID_MODERN && slotID != SLOT_ID_LEGACY) || phSession.is_null() { + return CKR_ARGUMENTS_BAD; + } + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + let slot_type = if slotID == SLOT_ID_MODERN { + SlotType::Modern + } else { + SlotType::Legacy + }; + let session_handle = match manager.open_session(slot_type) { + Ok(session_handle) => session_handle, + Err(_) => return CKR_DEVICE_ERROR, + }; + unsafe { + *phSession = session_handle; + } + CKR_OK +} + +/// This gets called to close a session. This is handled by the `ManagerProxy`. +extern "C" fn C_CloseSession(hSession: CK_SESSION_HANDLE) -> CK_RV { + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + if manager.close_session(hSession).is_err() { + return CKR_SESSION_HANDLE_INVALID; + } + CKR_OK +} + +/// This gets called to close all open sessions at once. This is handled by the `ManagerProxy`. +extern "C" fn C_CloseAllSessions(slotID: CK_SLOT_ID) -> CK_RV { + if slotID != SLOT_ID_MODERN && slotID != SLOT_ID_LEGACY { + return CKR_ARGUMENTS_BAD; + } + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + let slot_type = if slotID == SLOT_ID_MODERN { + SlotType::Modern + } else { + SlotType::Legacy + }; + match manager.close_all_sessions(slot_type) { + Ok(()) => CKR_OK, + Err(_) => CKR_DEVICE_ERROR, + } +} + +extern "C" fn C_GetSessionInfo(_hSession: CK_SESSION_HANDLE, _pInfo: CK_SESSION_INFO_PTR) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_GetOperationState( + _hSession: CK_SESSION_HANDLE, + _pOperationState: CK_BYTE_PTR, + _pulOperationStateLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_SetOperationState( + _hSession: CK_SESSION_HANDLE, + _pOperationState: CK_BYTE_PTR, + _ulOperationStateLen: CK_ULONG, + _hEncryptionKey: CK_OBJECT_HANDLE, + _hAuthenticationKey: CK_OBJECT_HANDLE, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_Login( + _hSession: CK_SESSION_HANDLE, + _userType: CK_USER_TYPE, + _pPin: CK_UTF8CHAR_PTR, + _ulPinLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +/// This gets called to log out and drop any authenticated resources. Because this module does not +/// hold on to authenticated resources, this module "implements" this by doing nothing and +/// returning a success result. +extern "C" fn C_Logout(_hSession: CK_SESSION_HANDLE) -> CK_RV { + CKR_OK +} + +extern "C" fn C_CreateObject( + _hSession: CK_SESSION_HANDLE, + _pTemplate: CK_ATTRIBUTE_PTR, + _ulCount: CK_ULONG, + _phObject: CK_OBJECT_HANDLE_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_CopyObject( + _hSession: CK_SESSION_HANDLE, + _hObject: CK_OBJECT_HANDLE, + _pTemplate: CK_ATTRIBUTE_PTR, + _ulCount: CK_ULONG, + _phNewObject: CK_OBJECT_HANDLE_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DestroyObject(_hSession: CK_SESSION_HANDLE, _hObject: CK_OBJECT_HANDLE) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_GetObjectSize( + _hSession: CK_SESSION_HANDLE, + _hObject: CK_OBJECT_HANDLE, + _pulSize: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +/// This gets called to obtain the values of a number of attributes of an object identified by the +/// given handle. This module implements this by requesting that the `ManagerProxy` find the object +/// and attempt to get the value of each attribute. If a specified attribute is not defined on the +/// object, the length of that attribute is set to -1 to indicate that it is not available. +/// This gets called twice: once to obtain the lengths of the attributes and again to get the +/// values. +extern "C" fn C_GetAttributeValue( + _hSession: CK_SESSION_HANDLE, + hObject: CK_OBJECT_HANDLE, + pTemplate: CK_ATTRIBUTE_PTR, + ulCount: CK_ULONG, +) -> CK_RV { + if pTemplate.is_null() { + return CKR_ARGUMENTS_BAD; + } + let mut attr_types = Vec::with_capacity(ulCount as usize); + for i in 0..ulCount { + let attr = unsafe { &*pTemplate.offset(i as isize) }; + attr_types.push(attr.type_); + } + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + let values = match manager.get_attributes(hObject, attr_types) { + Ok(values) => values, + Err(_) => return CKR_ARGUMENTS_BAD, + }; + if values.len() != ulCount as usize { + return CKR_DEVICE_ERROR; + } + for i in 0..ulCount as usize { + let mut attr = unsafe { &mut *pTemplate.offset(i as isize) }; + // NB: the safety of this array access depends on the length check above + if let Some(attr_value) = &values[i] { + if attr.pValue.is_null() { + attr.ulValueLen = attr_value.len() as CK_ULONG; + } else { + let ptr: *mut u8 = attr.pValue as *mut u8; + if attr_value.len() != attr.ulValueLen as usize { + return CKR_ARGUMENTS_BAD; + } + unsafe { + std::ptr::copy_nonoverlapping(attr_value.as_ptr(), ptr, attr_value.len()); + } + } + } else { + attr.ulValueLen = (0 - 1) as CK_ULONG; + } + } + CKR_OK +} + +extern "C" fn C_SetAttributeValue( + _hSession: CK_SESSION_HANDLE, + _hObject: CK_OBJECT_HANDLE, + _pTemplate: CK_ATTRIBUTE_PTR, + _ulCount: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +/// This gets called to initialize a search for objects matching a given list of attributes. This +/// module implements this by gathering the attributes and passing them to the `ManagerProxy` to +/// start the search. +extern "C" fn C_FindObjectsInit( + hSession: CK_SESSION_HANDLE, + pTemplate: CK_ATTRIBUTE_PTR, + ulCount: CK_ULONG, +) -> CK_RV { + if pTemplate.is_null() { + return CKR_ARGUMENTS_BAD; + } + let mut attrs = Vec::new(); + for i in 0..ulCount { + let attr = unsafe { &*pTemplate.offset(i as isize) }; + let slice = unsafe { + std::slice::from_raw_parts(attr.pValue as *const u8, attr.ulValueLen as usize) + }; + attrs.push((attr.type_, slice.to_owned())); + } + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + match manager.start_search(hSession, attrs) { + Ok(()) => {} + Err(_) => return CKR_ARGUMENTS_BAD, + } + CKR_OK +} + +/// This gets called after `C_FindObjectsInit` to get the results of a search. This module +/// implements this by looking up the search in the `ManagerProxy` and copying out the matching +/// object handles. +extern "C" fn C_FindObjects( + hSession: CK_SESSION_HANDLE, + phObject: CK_OBJECT_HANDLE_PTR, + ulMaxObjectCount: CK_ULONG, + pulObjectCount: CK_ULONG_PTR, +) -> CK_RV { + if phObject.is_null() || pulObjectCount.is_null() || ulMaxObjectCount == 0 { + return CKR_ARGUMENTS_BAD; + } + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + let handles = match manager.search(hSession, ulMaxObjectCount as usize) { + Ok(handles) => handles, + Err(_) => return CKR_ARGUMENTS_BAD, + }; + if handles.len() > ulMaxObjectCount as usize { + return CKR_DEVICE_ERROR; + } + unsafe { + *pulObjectCount = handles.len() as CK_ULONG; + } + for (index, handle) in handles.iter().enumerate() { + if index < ulMaxObjectCount as usize { + unsafe { + *(phObject.add(index)) = *handle; + } + } + } + CKR_OK +} + +/// This gets called after `C_FindObjectsInit` and `C_FindObjects` to finish a search. The module +/// tells the `ManagerProxy` to clear the search. +extern "C" fn C_FindObjectsFinal(hSession: CK_SESSION_HANDLE) -> CK_RV { + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + // It would be an error if there were no search for this session, but we can be permissive here. + match manager.clear_search(hSession) { + Ok(()) => CKR_OK, + Err(_) => CKR_DEVICE_ERROR, + } +} + +extern "C" fn C_EncryptInit( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hKey: CK_OBJECT_HANDLE, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_Encrypt( + _hSession: CK_SESSION_HANDLE, + _pData: CK_BYTE_PTR, + _ulDataLen: CK_ULONG, + _pEncryptedData: CK_BYTE_PTR, + _pulEncryptedDataLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_EncryptUpdate( + _hSession: CK_SESSION_HANDLE, + _pPart: CK_BYTE_PTR, + _ulPartLen: CK_ULONG, + _pEncryptedPart: CK_BYTE_PTR, + _pulEncryptedPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_EncryptFinal( + _hSession: CK_SESSION_HANDLE, + _pLastEncryptedPart: CK_BYTE_PTR, + _pulLastEncryptedPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DecryptInit( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hKey: CK_OBJECT_HANDLE, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_Decrypt( + _hSession: CK_SESSION_HANDLE, + _pEncryptedData: CK_BYTE_PTR, + _ulEncryptedDataLen: CK_ULONG, + _pData: CK_BYTE_PTR, + _pulDataLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DecryptUpdate( + _hSession: CK_SESSION_HANDLE, + _pEncryptedPart: CK_BYTE_PTR, + _ulEncryptedPartLen: CK_ULONG, + _pPart: CK_BYTE_PTR, + _pulPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DecryptFinal( + _hSession: CK_SESSION_HANDLE, + _pLastPart: CK_BYTE_PTR, + _pulLastPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DigestInit(_hSession: CK_SESSION_HANDLE, _pMechanism: CK_MECHANISM_PTR) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_Digest( + _hSession: CK_SESSION_HANDLE, + _pData: CK_BYTE_PTR, + _ulDataLen: CK_ULONG, + _pDigest: CK_BYTE_PTR, + _pulDigestLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DigestUpdate( + _hSession: CK_SESSION_HANDLE, + _pPart: CK_BYTE_PTR, + _ulPartLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DigestKey(_hSession: CK_SESSION_HANDLE, _hKey: CK_OBJECT_HANDLE) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DigestFinal( + _hSession: CK_SESSION_HANDLE, + _pDigest: CK_BYTE_PTR, + _pulDigestLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +/// This gets called to set up a sign operation. The module essentially defers to the +/// `ManagerProxy`. +extern "C" fn C_SignInit( + hSession: CK_SESSION_HANDLE, + pMechanism: CK_MECHANISM_PTR, + hKey: CK_OBJECT_HANDLE, +) -> CK_RV { + if pMechanism.is_null() { + return CKR_ARGUMENTS_BAD; + } + // Presumably we should validate the mechanism against hKey, but the specification doesn't + // actually seem to require this. + let mechanism = unsafe { *pMechanism }; + let mechanism_params = if mechanism.mechanism == CKM_RSA_PKCS_PSS { + if mechanism.ulParameterLen as usize != std::mem::size_of::<CK_RSA_PKCS_PSS_PARAMS>() { + return CKR_ARGUMENTS_BAD; + } + Some(unsafe { *(mechanism.pParameter as *const CK_RSA_PKCS_PSS_PARAMS) }) + } else { + None + }; + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + match manager.start_sign(hSession, hKey, mechanism_params) { + Ok(()) => {} + Err(_) => return CKR_GENERAL_ERROR, + }; + CKR_OK +} + +/// NSS calls this after `C_SignInit` (there are more ways in the PKCS #11 specification to sign +/// data, but this is the only way supported by this module). The module essentially defers to the +/// `ManagerProxy` and copies out the resulting signature. +extern "C" fn C_Sign( + hSession: CK_SESSION_HANDLE, + pData: CK_BYTE_PTR, + ulDataLen: CK_ULONG, + pSignature: CK_BYTE_PTR, + pulSignatureLen: CK_ULONG_PTR, +) -> CK_RV { + if pData.is_null() || pulSignatureLen.is_null() { + return CKR_ARGUMENTS_BAD; + } + let data = unsafe { std::slice::from_raw_parts(pData, ulDataLen as usize) }; + if pSignature.is_null() { + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + match manager.get_signature_length(hSession, data.to_vec()) { + Ok(signature_length) => unsafe { + *pulSignatureLen = signature_length as CK_ULONG; + }, + Err(_) => return CKR_GENERAL_ERROR, + } + } else { + let mut manager_guard = try_to_get_manager_guard!(); + let manager = manager_guard_to_manager!(manager_guard); + match manager.sign(hSession, data.to_vec()) { + Ok(signature) => { + let signature_capacity = unsafe { *pulSignatureLen } as usize; + if signature_capacity < signature.len() { + return CKR_ARGUMENTS_BAD; + } + let ptr: *mut u8 = pSignature as *mut u8; + unsafe { + std::ptr::copy_nonoverlapping(signature.as_ptr(), ptr, signature.len()); + *pulSignatureLen = signature.len() as CK_ULONG; + } + } + Err(_) => return CKR_GENERAL_ERROR, + } + } + CKR_OK +} + +extern "C" fn C_SignUpdate( + _hSession: CK_SESSION_HANDLE, + _pPart: CK_BYTE_PTR, + _ulPartLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_SignFinal( + _hSession: CK_SESSION_HANDLE, + _pSignature: CK_BYTE_PTR, + _pulSignatureLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_SignRecoverInit( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hKey: CK_OBJECT_HANDLE, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_SignRecover( + _hSession: CK_SESSION_HANDLE, + _pData: CK_BYTE_PTR, + _ulDataLen: CK_ULONG, + _pSignature: CK_BYTE_PTR, + _pulSignatureLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_VerifyInit( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hKey: CK_OBJECT_HANDLE, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_Verify( + _hSession: CK_SESSION_HANDLE, + _pData: CK_BYTE_PTR, + _ulDataLen: CK_ULONG, + _pSignature: CK_BYTE_PTR, + _ulSignatureLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_VerifyUpdate( + _hSession: CK_SESSION_HANDLE, + _pPart: CK_BYTE_PTR, + _ulPartLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_VerifyFinal( + _hSession: CK_SESSION_HANDLE, + _pSignature: CK_BYTE_PTR, + _ulSignatureLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_VerifyRecoverInit( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hKey: CK_OBJECT_HANDLE, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_VerifyRecover( + _hSession: CK_SESSION_HANDLE, + _pSignature: CK_BYTE_PTR, + _ulSignatureLen: CK_ULONG, + _pData: CK_BYTE_PTR, + _pulDataLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DigestEncryptUpdate( + _hSession: CK_SESSION_HANDLE, + _pPart: CK_BYTE_PTR, + _ulPartLen: CK_ULONG, + _pEncryptedPart: CK_BYTE_PTR, + _pulEncryptedPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DecryptDigestUpdate( + _hSession: CK_SESSION_HANDLE, + _pEncryptedPart: CK_BYTE_PTR, + _ulEncryptedPartLen: CK_ULONG, + _pPart: CK_BYTE_PTR, + _pulPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_SignEncryptUpdate( + _hSession: CK_SESSION_HANDLE, + _pPart: CK_BYTE_PTR, + _ulPartLen: CK_ULONG, + _pEncryptedPart: CK_BYTE_PTR, + _pulEncryptedPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DecryptVerifyUpdate( + _hSession: CK_SESSION_HANDLE, + _pEncryptedPart: CK_BYTE_PTR, + _ulEncryptedPartLen: CK_ULONG, + _pPart: CK_BYTE_PTR, + _pulPartLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_GenerateKey( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _pTemplate: CK_ATTRIBUTE_PTR, + _ulCount: CK_ULONG, + _phKey: CK_OBJECT_HANDLE_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_GenerateKeyPair( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _pPublicKeyTemplate: CK_ATTRIBUTE_PTR, + _ulPublicKeyAttributeCount: CK_ULONG, + _pPrivateKeyTemplate: CK_ATTRIBUTE_PTR, + _ulPrivateKeyAttributeCount: CK_ULONG, + _phPublicKey: CK_OBJECT_HANDLE_PTR, + _phPrivateKey: CK_OBJECT_HANDLE_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_WrapKey( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hWrappingKey: CK_OBJECT_HANDLE, + _hKey: CK_OBJECT_HANDLE, + _pWrappedKey: CK_BYTE_PTR, + _pulWrappedKeyLen: CK_ULONG_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_UnwrapKey( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hUnwrappingKey: CK_OBJECT_HANDLE, + _pWrappedKey: CK_BYTE_PTR, + _ulWrappedKeyLen: CK_ULONG, + _pTemplate: CK_ATTRIBUTE_PTR, + _ulAttributeCount: CK_ULONG, + _phKey: CK_OBJECT_HANDLE_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_DeriveKey( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hBaseKey: CK_OBJECT_HANDLE, + _pTemplate: CK_ATTRIBUTE_PTR, + _ulAttributeCount: CK_ULONG, + _phKey: CK_OBJECT_HANDLE_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_SeedRandom( + _hSession: CK_SESSION_HANDLE, + _pSeed: CK_BYTE_PTR, + _ulSeedLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_GenerateRandom( + _hSession: CK_SESSION_HANDLE, + _RandomData: CK_BYTE_PTR, + _ulRandomLen: CK_ULONG, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_GetFunctionStatus(_hSession: CK_SESSION_HANDLE) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_CancelFunction(_hSession: CK_SESSION_HANDLE) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +extern "C" fn C_WaitForSlotEvent( + _flags: CK_FLAGS, + _pSlot: CK_SLOT_ID_PTR, + _pRserved: CK_VOID_PTR, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +/// To be a valid PKCS #11 module, this list of functions must be supported. At least cryptoki 2.2 +/// must be supported for this module to work in NSS. +static mut FUNCTION_LIST: CK_FUNCTION_LIST = CK_FUNCTION_LIST { + version: CK_VERSION { major: 2, minor: 2 }, + C_Initialize: Some(C_Initialize), + C_Finalize: Some(C_Finalize), + C_GetInfo: Some(C_GetInfo), + C_GetFunctionList: None, + C_GetSlotList: Some(C_GetSlotList), + C_GetSlotInfo: Some(C_GetSlotInfo), + C_GetTokenInfo: Some(C_GetTokenInfo), + C_GetMechanismList: Some(C_GetMechanismList), + C_GetMechanismInfo: Some(C_GetMechanismInfo), + C_InitToken: Some(C_InitToken), + C_InitPIN: Some(C_InitPIN), + C_SetPIN: Some(C_SetPIN), + C_OpenSession: Some(C_OpenSession), + C_CloseSession: Some(C_CloseSession), + C_CloseAllSessions: Some(C_CloseAllSessions), + C_GetSessionInfo: Some(C_GetSessionInfo), + C_GetOperationState: Some(C_GetOperationState), + C_SetOperationState: Some(C_SetOperationState), + C_Login: Some(C_Login), + C_Logout: Some(C_Logout), + C_CreateObject: Some(C_CreateObject), + C_CopyObject: Some(C_CopyObject), + C_DestroyObject: Some(C_DestroyObject), + C_GetObjectSize: Some(C_GetObjectSize), + C_GetAttributeValue: Some(C_GetAttributeValue), + C_SetAttributeValue: Some(C_SetAttributeValue), + C_FindObjectsInit: Some(C_FindObjectsInit), + C_FindObjects: Some(C_FindObjects), + C_FindObjectsFinal: Some(C_FindObjectsFinal), + C_EncryptInit: Some(C_EncryptInit), + C_Encrypt: Some(C_Encrypt), + C_EncryptUpdate: Some(C_EncryptUpdate), + C_EncryptFinal: Some(C_EncryptFinal), + C_DecryptInit: Some(C_DecryptInit), + C_Decrypt: Some(C_Decrypt), + C_DecryptUpdate: Some(C_DecryptUpdate), + C_DecryptFinal: Some(C_DecryptFinal), + C_DigestInit: Some(C_DigestInit), + C_Digest: Some(C_Digest), + C_DigestUpdate: Some(C_DigestUpdate), + C_DigestKey: Some(C_DigestKey), + C_DigestFinal: Some(C_DigestFinal), + C_SignInit: Some(C_SignInit), + C_Sign: Some(C_Sign), + C_SignUpdate: Some(C_SignUpdate), + C_SignFinal: Some(C_SignFinal), + C_SignRecoverInit: Some(C_SignRecoverInit), + C_SignRecover: Some(C_SignRecover), + C_VerifyInit: Some(C_VerifyInit), + C_Verify: Some(C_Verify), + C_VerifyUpdate: Some(C_VerifyUpdate), + C_VerifyFinal: Some(C_VerifyFinal), + C_VerifyRecoverInit: Some(C_VerifyRecoverInit), + C_VerifyRecover: Some(C_VerifyRecover), + C_DigestEncryptUpdate: Some(C_DigestEncryptUpdate), + C_DecryptDigestUpdate: Some(C_DecryptDigestUpdate), + C_SignEncryptUpdate: Some(C_SignEncryptUpdate), + C_DecryptVerifyUpdate: Some(C_DecryptVerifyUpdate), + C_GenerateKey: Some(C_GenerateKey), + C_GenerateKeyPair: Some(C_GenerateKeyPair), + C_WrapKey: Some(C_WrapKey), + C_UnwrapKey: Some(C_UnwrapKey), + C_DeriveKey: Some(C_DeriveKey), + C_SeedRandom: Some(C_SeedRandom), + C_GenerateRandom: Some(C_GenerateRandom), + C_GetFunctionStatus: Some(C_GetFunctionStatus), + C_CancelFunction: Some(C_CancelFunction), + C_WaitForSlotEvent: Some(C_WaitForSlotEvent), +}; + +/// This is the only function this module exposes. The C stub calls it when NSS +/// calls its exposed C_GetFunctionList function to obtain the list of functions +/// comprising this module. +#[no_mangle] +pub extern "C" fn IPCCC_GetFunctionList(ppFunctionList: CK_FUNCTION_LIST_PTR_PTR) -> CK_RV { + if ppFunctionList.is_null() { + return CKR_ARGUMENTS_BAD; + } + unsafe { + *ppFunctionList = &mut FUNCTION_LIST; + } + CKR_OK +} + +#[cfg_attr(target_os = "macos", link(name = "Security", kind = "framework"))] +extern "C" {} |