summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/ipcclientcerts
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/ipcclientcerts')
-rw-r--r--security/manager/ssl/ipcclientcerts/Cargo.toml15
-rw-r--r--security/manager/ssl/ipcclientcerts/dynamic-library/ipcclientcerts.symbols1
-rw-r--r--security/manager/ssl/ipcclientcerts/dynamic-library/moz.build40
-rw-r--r--security/manager/ssl/ipcclientcerts/dynamic-library/stub.c43
-rw-r--r--security/manager/ssl/ipcclientcerts/dynamic-library/stub.cpp29
-rw-r--r--security/manager/ssl/ipcclientcerts/moz.build9
-rw-r--r--security/manager/ssl/ipcclientcerts/src/backend.rs373
-rw-r--r--security/manager/ssl/ipcclientcerts/src/lib.rs1047
8 files changed, 1557 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..7f2ab5e27f
--- /dev/null
+++ b/security/manager/ssl/ipcclientcerts/dynamic-library/moz.build
@@ -0,0 +1,40 @@
+# -*- 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",
+ "ntdll",
+ ]
+
+
+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" {}