Backed out changeset f326ce493602 (bug 1962280) for causing build bustages. CLOSED TREE
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -4972,16 +4972,14 @@ dependencies = [
|
||||
"byteorder",
|
||||
"core-foundation 0.9.999",
|
||||
"env_logger",
|
||||
"futures-executor",
|
||||
"lazy_static",
|
||||
"libloading",
|
||||
"log",
|
||||
"moz_task",
|
||||
"pkcs11-bindings",
|
||||
"rsclientcerts",
|
||||
"sha2",
|
||||
"static_prefs",
|
||||
"winapi",
|
||||
"xpcom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -102,6 +102,11 @@ bool LoadOSClientCertsModule();
|
||||
*/
|
||||
bool LoadIPCClientCertsModule();
|
||||
|
||||
/**
|
||||
* Unloads the loadable roots module and os client certs module, if loaded.
|
||||
*/
|
||||
void UnloadUserModules();
|
||||
|
||||
nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& nickname);
|
||||
|
||||
|
||||
@@ -1618,15 +1618,8 @@ void nsNSSComponent::PrepareForShutdown() {
|
||||
|
||||
// Release the default CertVerifier. This will cause any held NSS resources
|
||||
// to be released.
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mDefaultCertVerifier = nullptr;
|
||||
}
|
||||
|
||||
// Unload osclientcerts so it drops any held resources and stops its
|
||||
// background thread.
|
||||
AsyncLoadOrUnloadOSClientCertsModule(false);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
mDefaultCertVerifier = nullptr;
|
||||
// We don't actually shut down NSS - XPCOM does, after all threads have been
|
||||
// joined and the component manager has been shut down (and so there shouldn't
|
||||
// be any XPCOM objects holding NSS resources).
|
||||
|
||||
@@ -9,14 +9,13 @@ license = "MPL-2.0"
|
||||
[dependencies]
|
||||
byteorder = "1.3"
|
||||
env_logger = {version = "0.10", default-features = false } # disable `regex` to reduce code size
|
||||
futures-executor = { version = "0.3" }
|
||||
lazy_static = "1"
|
||||
log = "0.4"
|
||||
moz_task = { path = "../../../../xpcom/rust/moz_task" }
|
||||
|
||||
pkcs11-bindings = "0.1"
|
||||
rsclientcerts = { path = "../rsclientcerts" }
|
||||
sha2 = "0.10.2"
|
||||
xpcom = { path = "../../../../xpcom/rust/xpcom" }
|
||||
static_prefs = { path = "../../../../modules/libpref/init/static_prefs"}
|
||||
|
||||
[target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.core-foundation]
|
||||
version = "0.9"
|
||||
|
||||
@@ -7,7 +7,7 @@ use pkcs11_bindings::*;
|
||||
use rsclientcerts::error::{Error, ErrorType};
|
||||
use rsclientcerts::manager::{ClientCertsBackend, CryptokiObject, Sign};
|
||||
use rsclientcerts::util::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
use sha2::{Sha256, Digest};
|
||||
use std::ffi::{c_char, c_void, CString};
|
||||
|
||||
type FindObjectsCallback = Option<
|
||||
@@ -184,7 +184,11 @@ pub struct Key {
|
||||
}
|
||||
|
||||
impl Key {
|
||||
fn new(modulus: Option<&[u8]>, ec_params: Option<&[u8]>, cert: &[u8]) -> Result<Key, Error> {
|
||||
fn new(
|
||||
modulus: Option<&[u8]>,
|
||||
ec_params: Option<&[u8]>,
|
||||
cert: &[u8],
|
||||
) -> Result<Key, Error> {
|
||||
let id = Sha256::digest(cert).to_vec();
|
||||
let key_type = if modulus.is_some() { CKK_RSA } else { CKK_EC };
|
||||
// If this is an EC key, the frontend will have provided an SPKI.
|
||||
@@ -409,12 +413,6 @@ impl FindObjectsContext {
|
||||
|
||||
pub struct Backend {}
|
||||
|
||||
impl Backend {
|
||||
pub fn new() -> Result<Backend, Error> {
|
||||
Ok(Backend {})
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientCertsBackend for Backend {
|
||||
type Cert = Cert;
|
||||
type Key = Key;
|
||||
|
||||
@@ -22,8 +22,6 @@ use sha2::{Digest, Sha256};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::c_void;
|
||||
use xpcom::interfaces::nsIEventTarget;
|
||||
use xpcom::{RefPtr, XpCom};
|
||||
|
||||
// Normally we would generate this with a build script, but macos is
|
||||
// cross-compiled on linux, and we'd have to figure out e.g. include paths,
|
||||
@@ -36,12 +34,6 @@ pub type SecIdentityRef = *const __SecIdentity;
|
||||
declare_TCFType!(SecIdentity, SecIdentityRef);
|
||||
impl_TCFType!(SecIdentity, SecIdentityRef, SecIdentityGetTypeID);
|
||||
|
||||
/// Safety: strictly speaking, it isn't safe to send `SecIdentity` across threads. The
|
||||
/// implementation handles this by wrapping `SecIdentity` in `ThreadSpecificHandles`. However, in
|
||||
/// order to implement `Drop` for `ThreadSpecificHandles`, the `SecIdentity` it holds must be sent
|
||||
/// to the appropriate thread, hence this impl.
|
||||
unsafe impl Send for SecIdentity {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct __SecCertificate(c_void);
|
||||
pub type SecCertificateRef = *const __SecCertificate;
|
||||
@@ -54,9 +46,6 @@ pub type SecKeyRef = *const __SecKey;
|
||||
declare_TCFType!(SecKey, SecKeyRef);
|
||||
impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
|
||||
|
||||
/// Safety: see the comment for the `Send` impl for `SecIdentity`.
|
||||
unsafe impl Send for SecKey {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct __SecPolicy(c_void);
|
||||
pub type SecPolicyRef = *const __SecPolicy;
|
||||
@@ -503,141 +492,8 @@ impl<'a> SignParams<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct to hold onto OS-specific handles that must only be used on a particular thread.
|
||||
struct ThreadSpecificHandles {
|
||||
/// The only thread that these handles may be used on.
|
||||
thread: RefPtr<nsIEventTarget>,
|
||||
/// OS handle on a certificate and corresponding private key.
|
||||
identity: Option<SecIdentity>,
|
||||
/// OS handle on a private key.
|
||||
key: Option<SecKey>,
|
||||
}
|
||||
|
||||
impl ThreadSpecificHandles {
|
||||
fn new(identity: SecIdentity, thread: &nsIEventTarget) -> ThreadSpecificHandles {
|
||||
ThreadSpecificHandles {
|
||||
thread: RefPtr::new(thread),
|
||||
identity: Some(identity),
|
||||
key: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(
|
||||
&mut self,
|
||||
key_type_enum: KeyType,
|
||||
maybe_modulus: Option<Vec<u8>>,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let Some(identity) = self.identity.take() else {
|
||||
return Err(error_here!(ErrorType::LibraryFailure));
|
||||
};
|
||||
let mut maybe_key = self.key.take();
|
||||
let thread = self.thread.clone();
|
||||
let data = data.to_vec();
|
||||
let params = params.clone();
|
||||
let task = moz_task::spawn_onto("sign", &thread, async move {
|
||||
let result = sign_internal(&identity, &mut maybe_key, key_type_enum, &data, ¶ms);
|
||||
if result.is_ok() {
|
||||
return (result, identity, maybe_key);
|
||||
}
|
||||
// Some devices appear to not work well when the key handle is held for too long or if a
|
||||
// card is inserted/removed while Firefox is running. Try refreshing the key handle.
|
||||
let _ = maybe_key.take();
|
||||
let result = sign_internal(&identity, &mut maybe_key, key_type_enum, &data, ¶ms);
|
||||
// If this succeeded, return the result.
|
||||
if result.is_ok() {
|
||||
return (result, identity, maybe_key);
|
||||
}
|
||||
// If signing failed and this is an RSA-PSS signature, perhaps the token the key is on does
|
||||
// not support RSA-PSS. In that case, emsa-pss-encode the data (hash, really) and try
|
||||
// signing with raw RSA.
|
||||
let Some(params) = params.as_ref() else {
|
||||
return (result, identity, maybe_key);
|
||||
};
|
||||
// `params` should only be `Some` if this is an RSA key.
|
||||
let Some(modulus) = maybe_modulus.as_ref() else {
|
||||
return (
|
||||
Err(error_here!(ErrorType::LibraryFailure)),
|
||||
identity,
|
||||
maybe_key,
|
||||
);
|
||||
};
|
||||
let emsa_pss_encoded =
|
||||
match emsa_pss_encode(&data, modulus_bit_length(modulus) - 1, ¶ms) {
|
||||
Ok(emsa_pss_encoded) => emsa_pss_encoded,
|
||||
Err(e) => return (Err(e), identity, maybe_key),
|
||||
};
|
||||
(
|
||||
sign_internal(
|
||||
&identity,
|
||||
&mut maybe_key,
|
||||
key_type_enum,
|
||||
&emsa_pss_encoded,
|
||||
&None,
|
||||
),
|
||||
identity,
|
||||
maybe_key,
|
||||
)
|
||||
});
|
||||
let (signature_result, identity, mut maybe_key) = futures_executor::block_on(task);
|
||||
self.identity = Some(identity);
|
||||
self.key = maybe_key;
|
||||
signature_result
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_internal(
|
||||
identity: &SecIdentity,
|
||||
maybe_key: &mut Option<SecKey>,
|
||||
key_type_enum: KeyType,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
// If this key hasn't been used for signing yet, there won't be a cached key handle. Obtain
|
||||
// and cache it if this is the case. Doing so can cause the underlying implementation to
|
||||
// show an authentication or pin prompt to the user. Caching the handle can avoid causing
|
||||
// multiple prompts to be displayed in some cases.
|
||||
if maybe_key.is_none() {
|
||||
let _ = maybe_key.replace(sec_identity_copy_private_key(identity)?);
|
||||
}
|
||||
let Some(key) = maybe_key.as_ref() else {
|
||||
return Err(error_here!(ErrorType::LibraryFailure));
|
||||
};
|
||||
let sign_params = SignParams::new(key_type_enum, data, params)?;
|
||||
let signing_algorithm = sign_params.get_algorithm();
|
||||
let data_to_sign = CFData::from_buffer(sign_params.get_data_to_sign());
|
||||
let signature = sec_key_create_signature(key, signing_algorithm, &data_to_sign)?;
|
||||
let signature_value = match key_type_enum {
|
||||
KeyType::EC(coordinate_width) => {
|
||||
// We need to convert the DER Ecdsa-Sig-Value to the
|
||||
// concatenation of r and s, the coordinates of the point on
|
||||
// the curve. r and s must be 0-padded to be coordinate_width
|
||||
// total bytes.
|
||||
der_ec_sig_to_raw(signature.bytes(), coordinate_width)?
|
||||
}
|
||||
KeyType::RSA => signature.bytes().to_vec(),
|
||||
};
|
||||
Ok(signature_value)
|
||||
}
|
||||
|
||||
impl Drop for ThreadSpecificHandles {
|
||||
fn drop(&mut self) {
|
||||
// Ensure any OS handles are dropped on the appropriate thread.
|
||||
let identity = self.identity.take();
|
||||
let key = self.key.take();
|
||||
let thread = self.thread.clone();
|
||||
let task = moz_task::spawn_onto("drop", &thread, async move {
|
||||
// `key` is obtained from `identity`, so drop it first, out of an abundance of caution.
|
||||
drop(key);
|
||||
drop(identity);
|
||||
});
|
||||
futures_executor::block_on(task)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Key {
|
||||
handles: ThreadSpecificHandles,
|
||||
identity: SecIdentity,
|
||||
class: Vec<u8>,
|
||||
token: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
@@ -646,10 +502,11 @@ pub struct Key {
|
||||
modulus: Option<Vec<u8>>,
|
||||
ec_params: Option<Vec<u8>>,
|
||||
key_type_enum: KeyType,
|
||||
key_handle: Option<SecKey>,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
fn new(identity: &SecIdentity, thread: &nsIEventTarget) -> Result<Key, Error> {
|
||||
fn new(identity: &SecIdentity) -> Result<Key, Error> {
|
||||
let certificate = sec_identity_copy_certificate(identity)?;
|
||||
let der = sec_certificate_copy_data(&certificate)?;
|
||||
let id = Sha256::digest(der.bytes()).to_vec();
|
||||
@@ -689,7 +546,7 @@ impl Key {
|
||||
};
|
||||
|
||||
Ok(Key {
|
||||
handles: ThreadSpecificHandles::new(identity.clone(), thread),
|
||||
identity: identity.clone(),
|
||||
class: serialize_uint(CKO_PRIVATE_KEY)?,
|
||||
token: serialize_uint(CK_TRUE)?,
|
||||
id,
|
||||
@@ -698,6 +555,7 @@ impl Key {
|
||||
modulus,
|
||||
ec_params,
|
||||
key_type_enum,
|
||||
key_handle: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -734,6 +592,41 @@ impl Key {
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_internal(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
// If this key hasn't been used for signing yet, there won't be a cached key handle. Obtain
|
||||
// and cache it if this is the case. Doing so can cause the underlying implementation to
|
||||
// show an authentication or pin prompt to the user. Caching the handle can avoid causing
|
||||
// multiple prompts to be displayed in some cases.
|
||||
if self.key_handle.is_none() {
|
||||
let _ = self
|
||||
.key_handle
|
||||
.replace(sec_identity_copy_private_key(&self.identity)?);
|
||||
}
|
||||
let key = match &self.key_handle {
|
||||
Some(key) => key,
|
||||
None => return Err(error_here!(ErrorType::LibraryFailure)),
|
||||
};
|
||||
let sign_params = SignParams::new(self.key_type_enum, data, params)?;
|
||||
let signing_algorithm = sign_params.get_algorithm();
|
||||
let data_to_sign = CFData::from_buffer(sign_params.get_data_to_sign());
|
||||
let signature = sec_key_create_signature(key, signing_algorithm, &data_to_sign)?;
|
||||
let signature_value = match self.key_type_enum {
|
||||
KeyType::EC(coordinate_width) => {
|
||||
// We need to convert the DER Ecdsa-Sig-Value to the
|
||||
// concatenation of r and s, the coordinates of the point on
|
||||
// the curve. r and s must be 0-padded to be coordinate_width
|
||||
// total bytes.
|
||||
der_ec_sig_to_raw(signature.bytes(), coordinate_width)?
|
||||
}
|
||||
KeyType::RSA => signature.bytes().to_vec(),
|
||||
};
|
||||
Ok(signature_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptokiObject for Key {
|
||||
@@ -800,8 +693,30 @@ impl Sign for Key {
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
self.handles
|
||||
.sign(self.key_type_enum, self.modulus.clone(), data, params)
|
||||
let result = self.sign_internal(data, params);
|
||||
if result.is_ok() {
|
||||
return result;
|
||||
}
|
||||
// Some devices appear to not work well when the key handle is held for too long or if a
|
||||
// card is inserted/removed while Firefox is running. Try refreshing the key handle.
|
||||
let _ = self.key_handle.take();
|
||||
let result = self.sign_internal(data, params);
|
||||
// If this succeeded, return the result.
|
||||
if result.is_ok() {
|
||||
return result;
|
||||
}
|
||||
// If signing failed and this is an RSA-PSS signature, perhaps the token the key is on does
|
||||
// not support RSA-PSS. In that case, emsa-pss-encode the data (hash, really) and try
|
||||
// signing with raw RSA.
|
||||
let Some(params) = params.as_ref() else {
|
||||
return result;
|
||||
};
|
||||
// `params` should only be `Some` if this is an RSA key.
|
||||
let Some(modulus) = self.modulus.as_ref() else {
|
||||
return Err(error_here!(ErrorType::LibraryFailure));
|
||||
};
|
||||
let emsa_pss_encoded = emsa_pss_encode(data, modulus_bit_length(modulus) - 1, params)?;
|
||||
self.sign_internal(&emsa_pss_encoded, &None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -868,84 +783,59 @@ fn get_issuers(identity: &SecIdentity) -> Result<Vec<SecCertificate>, Error> {
|
||||
Ok(certificates)
|
||||
}
|
||||
|
||||
pub struct Backend {
|
||||
/// A background thread that all OS API calls will be done on. This is to prevent issues with
|
||||
/// modules or implementations using thread-local state.
|
||||
thread: RefPtr<nsIEventTarget>,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
pub fn new() -> Result<Backend, Error> {
|
||||
let thread = moz_task::create_thread("osclientcerts").map_err(|nsresult| {
|
||||
error_here!(ErrorType::LibraryFailure, nsresult.error_name().to_string())
|
||||
})?;
|
||||
Ok(Backend {
|
||||
thread: thread
|
||||
.query_interface::<nsIEventTarget>()
|
||||
.ok_or(error_here!(ErrorType::LibraryFailure))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub struct Backend {}
|
||||
|
||||
impl ClientCertsBackend for Backend {
|
||||
type Cert = Cert;
|
||||
type Key = Key;
|
||||
|
||||
fn find_objects(&self) -> Result<(Vec<Cert>, Vec<Key>), Error> {
|
||||
let thread = self.thread.clone();
|
||||
let task = moz_task::spawn_onto("find_objects", &self.thread, async move {
|
||||
find_objects(&thread)
|
||||
});
|
||||
futures_executor::block_on(task)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_objects(thread: &nsIEventTarget) -> Result<(Vec<Cert>, Vec<Key>), Error> {
|
||||
let mut certs = Vec::new();
|
||||
let mut keys = Vec::new();
|
||||
let identities = unsafe {
|
||||
let class_key = CFString::wrap_under_get_rule(kSecClass);
|
||||
let class_value = CFString::wrap_under_get_rule(kSecClassIdentity);
|
||||
let return_ref_key = CFString::wrap_under_get_rule(kSecReturnRef);
|
||||
let return_ref_value = CFBoolean::wrap_under_get_rule(kCFBooleanTrue);
|
||||
let match_key = CFString::wrap_under_get_rule(kSecMatchLimit);
|
||||
let match_value = CFString::wrap_under_get_rule(kSecMatchLimitAll);
|
||||
let vals = vec![
|
||||
(class_key.as_CFType(), class_value.as_CFType()),
|
||||
(return_ref_key.as_CFType(), return_ref_value.as_CFType()),
|
||||
(match_key.as_CFType(), match_value.as_CFType()),
|
||||
];
|
||||
let dict = CFDictionary::from_CFType_pairs(&vals);
|
||||
let mut result = std::ptr::null();
|
||||
let status = SecItemCopyMatching(dict.as_CFTypeRef() as CFDictionaryRef, &mut result);
|
||||
if status == errSecItemNotFound {
|
||||
return Ok((certs, keys));
|
||||
}
|
||||
if status != errSecSuccess {
|
||||
return Err(error_here!(ErrorType::ExternalError, status.to_string()));
|
||||
}
|
||||
if result.is_null() {
|
||||
return Err(error_here!(ErrorType::ExternalError));
|
||||
}
|
||||
CFArray::<SecIdentityRef>::wrap_under_create_rule(result as CFArrayRef)
|
||||
};
|
||||
for identity in identities.get_all_values().iter() {
|
||||
let identity = unsafe { SecIdentity::wrap_under_get_rule(*identity as SecIdentityRef) };
|
||||
let cert = Cert::new_from_identity(&identity);
|
||||
let key = Key::new(&identity, thread);
|
||||
if let (Ok(cert), Ok(key)) = (cert, key) {
|
||||
certs.push(cert);
|
||||
keys.push(key);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if let Ok(issuers) = get_issuers(&identity) {
|
||||
for issuer in issuers {
|
||||
if let Ok(cert) = Cert::new_from_certificate(&issuer) {
|
||||
certs.push(cert);
|
||||
let mut certs = Vec::new();
|
||||
let mut keys = Vec::new();
|
||||
let identities = unsafe {
|
||||
let class_key = CFString::wrap_under_get_rule(kSecClass);
|
||||
let class_value = CFString::wrap_under_get_rule(kSecClassIdentity);
|
||||
let return_ref_key = CFString::wrap_under_get_rule(kSecReturnRef);
|
||||
let return_ref_value = CFBoolean::wrap_under_get_rule(kCFBooleanTrue);
|
||||
let match_key = CFString::wrap_under_get_rule(kSecMatchLimit);
|
||||
let match_value = CFString::wrap_under_get_rule(kSecMatchLimitAll);
|
||||
let vals = vec![
|
||||
(class_key.as_CFType(), class_value.as_CFType()),
|
||||
(return_ref_key.as_CFType(), return_ref_value.as_CFType()),
|
||||
(match_key.as_CFType(), match_value.as_CFType()),
|
||||
];
|
||||
let dict = CFDictionary::from_CFType_pairs(&vals);
|
||||
let mut result = std::ptr::null();
|
||||
let status = SecItemCopyMatching(dict.as_CFTypeRef() as CFDictionaryRef, &mut result);
|
||||
if status == errSecItemNotFound {
|
||||
return Ok((certs, keys));
|
||||
}
|
||||
if status != errSecSuccess {
|
||||
return Err(error_here!(ErrorType::ExternalError, status.to_string()));
|
||||
}
|
||||
if result.is_null() {
|
||||
return Err(error_here!(ErrorType::ExternalError));
|
||||
}
|
||||
CFArray::<SecIdentityRef>::wrap_under_create_rule(result as CFArrayRef)
|
||||
};
|
||||
for identity in identities.get_all_values().iter() {
|
||||
let identity = unsafe { SecIdentity::wrap_under_get_rule(*identity as SecIdentityRef) };
|
||||
let cert = Cert::new_from_identity(&identity);
|
||||
let key = Key::new(&identity);
|
||||
if let (Ok(cert), Ok(key)) = (cert, key) {
|
||||
certs.push(cert);
|
||||
keys.push(key);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if let Ok(issuers) = get_issuers(&identity) {
|
||||
for issuer in issuers {
|
||||
if let Ok(cert) = Cert::new_from_certificate(&issuer) {
|
||||
certs.push(cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((certs, keys))
|
||||
}
|
||||
Ok((certs, keys))
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ use winapi::shared::minwindef::{DWORD, PBYTE};
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::ncrypt::*;
|
||||
use winapi::um::wincrypt::{HCRYPTHASH, HCRYPTPROV, *};
|
||||
use xpcom::interfaces::nsIEventTarget;
|
||||
use xpcom::{RefPtr, XpCom};
|
||||
|
||||
// winapi has some support for ncrypt.h, but not for this function.
|
||||
extern "system" {
|
||||
@@ -206,12 +204,6 @@ impl Deref for CertContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety: strictly speaking, it isn't safe to send `CertContext` across threads. The
|
||||
/// implementation handles this by wrapping `CertContext` in `ThreadSpecificHandles`. However, in
|
||||
/// order to implement `Drop` for `ThreadSpecificHandles`, the `CertContext` it holds must be sent
|
||||
/// to the appropriate thread, hence this impl.
|
||||
unsafe impl Send for CertContext {}
|
||||
|
||||
enum KeyHandle {
|
||||
NCrypt(NCRYPT_KEY_HANDLE),
|
||||
CryptoAPI(HCRYPTPROV, DWORD),
|
||||
@@ -279,9 +271,6 @@ impl Drop for KeyHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety: see the comment for the `Send` impl for `CertContext`.
|
||||
unsafe impl Send for KeyHandle {}
|
||||
|
||||
fn sign_ncrypt(
|
||||
ncrypt_handle: &NCRYPT_KEY_HANDLE,
|
||||
data: &[u8],
|
||||
@@ -528,112 +517,6 @@ impl SignParams {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct to hold onto OS-specific handles that must only be used on a particular thread.
|
||||
struct ThreadSpecificHandles {
|
||||
/// The only thread that these handles may be used on.
|
||||
thread: RefPtr<nsIEventTarget>,
|
||||
/// A handle on the OS mechanism that represents the certificate for a key.
|
||||
cert: Option<CertContext>,
|
||||
/// A handle on the OS mechanism that represents a key.
|
||||
key: Option<KeyHandle>,
|
||||
}
|
||||
|
||||
impl ThreadSpecificHandles {
|
||||
fn new(cert: CertContext, thread: &nsIEventTarget) -> ThreadSpecificHandles {
|
||||
ThreadSpecificHandles {
|
||||
thread: RefPtr::new(thread),
|
||||
cert: Some(cert),
|
||||
key: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_or_get_signature_length(
|
||||
&mut self,
|
||||
key_type_enum: KeyType,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
do_signature: bool,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let Some(cert) = self.cert.take() else {
|
||||
return Err(error_here!(ErrorType::LibraryFailure));
|
||||
};
|
||||
let mut maybe_key = self.key.take();
|
||||
let thread = self.thread.clone();
|
||||
let data = data.to_vec();
|
||||
let params = params.clone();
|
||||
let task = moz_task::spawn_onto("sign", &thread, async move {
|
||||
let result = sign_internal(
|
||||
&cert,
|
||||
&mut maybe_key,
|
||||
key_type_enum,
|
||||
&data,
|
||||
¶ms,
|
||||
do_signature,
|
||||
);
|
||||
if result.is_ok() {
|
||||
return (result, cert, maybe_key);
|
||||
}
|
||||
// Some devices appear to not work well when the key handle is held for too long or if a
|
||||
// card is inserted/removed while Firefox is running. Try refreshing the key handle.
|
||||
let _ = maybe_key.take();
|
||||
(
|
||||
sign_internal(
|
||||
&cert,
|
||||
&mut maybe_key,
|
||||
key_type_enum,
|
||||
&data,
|
||||
¶ms,
|
||||
do_signature,
|
||||
),
|
||||
cert,
|
||||
maybe_key,
|
||||
)
|
||||
});
|
||||
let (signature_result, cert, mut maybe_key) = futures_executor::block_on(task);
|
||||
self.cert = Some(cert);
|
||||
self.key = maybe_key;
|
||||
signature_result
|
||||
}
|
||||
}
|
||||
|
||||
/// data: the data to sign
|
||||
/// do_signature: if true, actually perform the signature. Otherwise, return a `Vec<u8>` of the
|
||||
/// length the signature would be, if performed.
|
||||
fn sign_internal(
|
||||
cert: &CertContext,
|
||||
maybe_key: &mut Option<KeyHandle>,
|
||||
key_type_enum: KeyType,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
do_signature: bool,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
// If this key hasn't been used for signing yet, there won't be a cached key handle. Obtain
|
||||
// and cache it if this is the case. Doing so can cause the underlying implementation to
|
||||
// show an authentication or pin prompt to the user. Caching the handle can avoid causing
|
||||
// multiple prompts to be displayed in some cases.
|
||||
if maybe_key.is_none() {
|
||||
let _ = maybe_key.replace(KeyHandle::from_cert(cert)?);
|
||||
}
|
||||
let Some(key) = maybe_key.as_ref() else {
|
||||
return Err(error_here!(ErrorType::LibraryFailure));
|
||||
};
|
||||
key.sign(data, params, do_signature, key_type_enum)
|
||||
}
|
||||
|
||||
impl Drop for ThreadSpecificHandles {
|
||||
fn drop(&mut self) {
|
||||
// Ensure any OS handles are dropped on the appropriate thread.
|
||||
let cert = self.cert.take();
|
||||
let key = self.key.take();
|
||||
let thread = self.thread.clone();
|
||||
let task = moz_task::spawn_onto("drop", &thread, async move {
|
||||
drop(cert);
|
||||
drop(key);
|
||||
});
|
||||
futures_executor::block_on(task)
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper enum to identify a private key's type. We support EC and RSA.
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -644,8 +527,8 @@ pub enum KeyType {
|
||||
|
||||
/// Represents a private key for which there exists a corresponding certificate.
|
||||
pub struct Key {
|
||||
/// The OS handles for this key. May only be used on the thread they were created on.
|
||||
handles: ThreadSpecificHandles,
|
||||
/// A handle on the OS mechanism that represents the certificate for this key.
|
||||
cert: CertContext,
|
||||
/// PKCS #11 object class. Will be `CKO_PRIVATE_KEY`.
|
||||
class: Vec<u8>,
|
||||
/// Whether or not this is on a token. Will be `CK_TRUE`.
|
||||
@@ -663,10 +546,12 @@ pub struct Key {
|
||||
ec_params: Option<Vec<u8>>,
|
||||
/// An enum identifying this key's type.
|
||||
key_type_enum: KeyType,
|
||||
/// A handle on the OS mechanism that represents this key.
|
||||
key_handle: Option<KeyHandle>,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
fn new(cert_context: PCCERT_CONTEXT, thread: &nsIEventTarget) -> Result<Key, Error> {
|
||||
fn new(cert_context: PCCERT_CONTEXT) -> Result<Key, Error> {
|
||||
let cert = unsafe { *cert_context };
|
||||
let cert_der =
|
||||
unsafe { slice::from_raw_parts(cert.pbCertEncoded, cert.cbCertEncoded as usize) };
|
||||
@@ -701,7 +586,7 @@ impl Key {
|
||||
};
|
||||
let cert = CertContext::new(cert_context);
|
||||
Ok(Key {
|
||||
handles: ThreadSpecificHandles::new(cert, thread),
|
||||
cert,
|
||||
class: serialize_uint(CKO_PRIVATE_KEY)?,
|
||||
token: serialize_uint(CK_TRUE)?,
|
||||
id,
|
||||
@@ -710,6 +595,7 @@ impl Key {
|
||||
modulus,
|
||||
ec_params,
|
||||
key_type_enum,
|
||||
key_handle: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -747,14 +633,44 @@ impl Key {
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_or_get_signature_length(
|
||||
fn sign_with_retry(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
do_signature: bool,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
self.handles
|
||||
.sign_or_get_signature_length(self.key_type_enum, data, params, do_signature)
|
||||
let result = self.sign_internal(data, params, do_signature);
|
||||
if result.is_ok() {
|
||||
return result;
|
||||
}
|
||||
// Some devices appear to not work well when the key handle is held for too long or if a
|
||||
// card is inserted/removed while Firefox is running. Try refreshing the key handle.
|
||||
debug!("sign failed: refreshing key handle");
|
||||
let _ = self.key_handle.take();
|
||||
self.sign_internal(data, params, do_signature)
|
||||
}
|
||||
|
||||
/// data: the data to sign
|
||||
/// do_signature: if true, actually perform the signature. Otherwise, return a `Vec<u8>` of the
|
||||
/// length the signature would be, if performed.
|
||||
fn sign_internal(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
do_signature: bool,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
// If this key hasn't been used for signing yet, there won't be a cached key handle. Obtain
|
||||
// and cache it if this is the case. Doing so can cause the underlying implementation to
|
||||
// show an authentication or pin prompt to the user. Caching the handle can avoid causing
|
||||
// multiple prompts to be displayed in some cases.
|
||||
if self.key_handle.is_none() {
|
||||
let _ = self.key_handle.replace(KeyHandle::from_cert(&self.cert)?);
|
||||
}
|
||||
let key = match &self.key_handle {
|
||||
Some(key) => key,
|
||||
None => return Err(error_here!(ErrorType::LibraryFailure)),
|
||||
};
|
||||
key.sign(data, params, do_signature, self.key_type_enum)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,8 +726,10 @@ impl Sign for Key {
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<usize, Error> {
|
||||
self.sign_or_get_signature_length(data, params, false)
|
||||
.map(|signature| signature.len())
|
||||
match self.sign_with_retry(data, params, false) {
|
||||
Ok(dummy_signature_bytes) => Ok(dummy_signature_bytes.len()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(
|
||||
@@ -819,7 +737,7 @@ impl Sign for Key {
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
self.sign_or_get_signature_length(data, params, true)
|
||||
self.sign_with_retry(data, params, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -891,117 +809,94 @@ fn gather_cert_contexts(cert_chain_context: *const CERT_CHAIN_CONTEXT) -> Vec<*c
|
||||
cert_contexts
|
||||
}
|
||||
|
||||
pub struct Backend {
|
||||
/// A background thread that all OS API calls will be done on. This is to prevent issues with
|
||||
/// modules or implementations using thread-local state.
|
||||
thread: RefPtr<nsIEventTarget>,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
pub fn new() -> Result<Backend, Error> {
|
||||
let thread = moz_task::create_thread("osclientcerts").map_err(|nsresult| {
|
||||
error_here!(ErrorType::LibraryFailure, nsresult.error_name().to_string())
|
||||
})?;
|
||||
Ok(Backend {
|
||||
thread: thread
|
||||
.query_interface::<nsIEventTarget>()
|
||||
.ok_or(error_here!(ErrorType::LibraryFailure))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub struct Backend {}
|
||||
|
||||
impl ClientCertsBackend for Backend {
|
||||
type Cert = Cert;
|
||||
type Key = Key;
|
||||
|
||||
/// Attempts to enumerate certificates with private keys exposed by the OS. Currently only looks in
|
||||
/// the "My" cert store of the current user. In the future this may look in more locations.
|
||||
fn find_objects(&self) -> Result<(Vec<Cert>, Vec<Key>), Error> {
|
||||
let thread = self.thread.clone();
|
||||
let task = moz_task::spawn_onto("find_objects", &self.thread, async move {
|
||||
find_objects(&thread)
|
||||
});
|
||||
futures_executor::block_on(task)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to enumerate certificates with private keys exposed by the OS. Currently only looks in
|
||||
/// the "My" cert store of the current user. In the future this may look in more locations.
|
||||
fn find_objects(thread: &nsIEventTarget) -> Result<(Vec<Cert>, Vec<Key>), Error> {
|
||||
let mut certs = Vec::new();
|
||||
let mut keys = Vec::new();
|
||||
let location_flags =
|
||||
CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
|
||||
let store_name = match CString::new("My") {
|
||||
Ok(store_name) => store_name,
|
||||
Err(_) => return Err(error_here!(ErrorType::LibraryFailure)),
|
||||
};
|
||||
let store = CertStore::new(unsafe {
|
||||
CertOpenStore(
|
||||
CERT_STORE_PROV_SYSTEM_REGISTRY_A,
|
||||
0,
|
||||
0,
|
||||
location_flags,
|
||||
store_name.as_ptr() as *const winapi::ctypes::c_void,
|
||||
)
|
||||
});
|
||||
if store.is_null() {
|
||||
return Err(error_here!(ErrorType::ExternalError));
|
||||
}
|
||||
let find_params = CERT_CHAIN_FIND_ISSUER_PARA {
|
||||
cbSize: std::mem::size_of::<CERT_CHAIN_FIND_ISSUER_PARA>() as u32,
|
||||
pszUsageIdentifier: std::ptr::null(),
|
||||
dwKeySpec: 0,
|
||||
dwAcquirePrivateKeyFlags: 0,
|
||||
cIssuer: 0,
|
||||
rgIssuer: std::ptr::null_mut(),
|
||||
pfnFindCallback: None,
|
||||
pvFindArg: std::ptr::null_mut(),
|
||||
pdwIssuerChainIndex: std::ptr::null_mut(),
|
||||
pdwIssuerElementIndex: std::ptr::null_mut(),
|
||||
};
|
||||
let mut cert_chain_context: PCCERT_CHAIN_CONTEXT = std::ptr::null_mut();
|
||||
loop {
|
||||
// CertFindChainInStore finds all certificates with private keys in the store. It also
|
||||
// attempts to build a verified certificate chain to a trust anchor for each certificate.
|
||||
// We gather and hold onto these extra certificates so that gecko can use them when
|
||||
// filtering potential client certificates according to the acceptable CAs list sent by
|
||||
// servers when they request client certificates.
|
||||
cert_chain_context = unsafe {
|
||||
CertFindChainInStore(
|
||||
*store,
|
||||
X509_ASN_ENCODING,
|
||||
CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG
|
||||
| CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG,
|
||||
CERT_CHAIN_FIND_BY_ISSUER,
|
||||
&find_params as *const CERT_CHAIN_FIND_ISSUER_PARA as *const winapi::ctypes::c_void,
|
||||
cert_chain_context,
|
||||
let mut certs = Vec::new();
|
||||
let mut keys = Vec::new();
|
||||
let location_flags = CERT_SYSTEM_STORE_CURRENT_USER
|
||||
| CERT_STORE_OPEN_EXISTING_FLAG
|
||||
| CERT_STORE_READONLY_FLAG;
|
||||
let store_name = match CString::new("My") {
|
||||
Ok(store_name) => store_name,
|
||||
Err(_) => return Err(error_here!(ErrorType::LibraryFailure)),
|
||||
};
|
||||
let store = CertStore::new(unsafe {
|
||||
CertOpenStore(
|
||||
CERT_STORE_PROV_SYSTEM_REGISTRY_A,
|
||||
0,
|
||||
0,
|
||||
location_flags,
|
||||
store_name.as_ptr() as *const winapi::ctypes::c_void,
|
||||
)
|
||||
};
|
||||
if cert_chain_context.is_null() {
|
||||
break;
|
||||
});
|
||||
if store.is_null() {
|
||||
return Err(error_here!(ErrorType::ExternalError));
|
||||
}
|
||||
let cert_contexts = gather_cert_contexts(cert_chain_context);
|
||||
// The 0th CERT_CONTEXT is the end-entity (i.e. the certificate with the private key we're
|
||||
// after).
|
||||
match cert_contexts.get(0) {
|
||||
Some(cert_context) => {
|
||||
let key = match Key::new(*cert_context, thread) {
|
||||
Ok(key) => key,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let cert = match Cert::new(*cert_context) {
|
||||
Ok(cert) => cert,
|
||||
Err(_) => continue,
|
||||
};
|
||||
certs.push(cert);
|
||||
keys.push(key);
|
||||
}
|
||||
None => {}
|
||||
let find_params = CERT_CHAIN_FIND_ISSUER_PARA {
|
||||
cbSize: std::mem::size_of::<CERT_CHAIN_FIND_ISSUER_PARA>() as u32,
|
||||
pszUsageIdentifier: std::ptr::null(),
|
||||
dwKeySpec: 0,
|
||||
dwAcquirePrivateKeyFlags: 0,
|
||||
cIssuer: 0,
|
||||
rgIssuer: std::ptr::null_mut(),
|
||||
pfnFindCallback: None,
|
||||
pvFindArg: std::ptr::null_mut(),
|
||||
pdwIssuerChainIndex: std::ptr::null_mut(),
|
||||
pdwIssuerElementIndex: std::ptr::null_mut(),
|
||||
};
|
||||
for cert_context in cert_contexts.iter().skip(1) {
|
||||
if let Ok(cert) = Cert::new(*cert_context) {
|
||||
certs.push(cert);
|
||||
let mut cert_chain_context: PCCERT_CHAIN_CONTEXT = std::ptr::null_mut();
|
||||
loop {
|
||||
// CertFindChainInStore finds all certificates with private keys in the store. It also
|
||||
// attempts to build a verified certificate chain to a trust anchor for each certificate.
|
||||
// We gather and hold onto these extra certificates so that gecko can use them when
|
||||
// filtering potential client certificates according to the acceptable CAs list sent by
|
||||
// servers when they request client certificates.
|
||||
cert_chain_context = unsafe {
|
||||
CertFindChainInStore(
|
||||
*store,
|
||||
X509_ASN_ENCODING,
|
||||
CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG
|
||||
| CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG,
|
||||
CERT_CHAIN_FIND_BY_ISSUER,
|
||||
&find_params as *const CERT_CHAIN_FIND_ISSUER_PARA
|
||||
as *const winapi::ctypes::c_void,
|
||||
cert_chain_context,
|
||||
)
|
||||
};
|
||||
if cert_chain_context.is_null() {
|
||||
break;
|
||||
}
|
||||
let cert_contexts = gather_cert_contexts(cert_chain_context);
|
||||
// The 0th CERT_CONTEXT is the end-entity (i.e. the certificate with the private key we're
|
||||
// after).
|
||||
match cert_contexts.get(0) {
|
||||
Some(cert_context) => {
|
||||
let key = match Key::new(*cert_context) {
|
||||
Ok(key) => key,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let cert = match Cert::new(*cert_context) {
|
||||
Ok(cert) => cert,
|
||||
Err(_) => continue,
|
||||
};
|
||||
certs.push(cert);
|
||||
keys.push(key);
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
for cert_context in cert_contexts.iter().skip(1) {
|
||||
if let Ok(cert) = Cert::new(*cert_context) {
|
||||
certs.push(cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((certs, keys))
|
||||
}
|
||||
Ok((certs, keys))
|
||||
}
|
||||
|
||||
@@ -23,10 +23,9 @@ extern crate rsclientcerts;
|
||||
extern crate sha2;
|
||||
#[cfg(all(target_os = "windows", not(target_arch = "aarch64")))]
|
||||
extern crate winapi;
|
||||
extern crate xpcom;
|
||||
|
||||
use pkcs11_bindings::*;
|
||||
use rsclientcerts::manager::Manager;
|
||||
use rsclientcerts::manager::ManagerProxy;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
@@ -45,23 +44,24 @@ use crate::backend_macos::Backend;
|
||||
#[cfg(all(target_os = "windows", not(target_arch = "aarch64")))]
|
||||
use crate::backend_windows::Backend;
|
||||
|
||||
/// 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. Note that the underlying OS
|
||||
/// APIs may not necessarily be thread safe. For platforms where this is the case, the `Backend`
|
||||
/// will synchronously run the relevant code on a background thread.
|
||||
static MANAGER: Mutex<Option<Manager<Backend>>> = Mutex::new(None);
|
||||
/// The singleton `ManagerProxy` 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. However, as
|
||||
/// OS APIs being used are not necessarily thread-safe (e.g. they may be using
|
||||
/// thread-local-storage), the `ManagerProxy` forwards calls from any thread
|
||||
/// to a single thread where the real `Manager` does the actual work.
|
||||
static MANAGER_PROXY: Mutex<Option<ManagerProxy>> = Mutex::new(None);
|
||||
|
||||
// Obtaining a handle on the manager proxy 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 proxy (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 {
|
||||
// let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
// let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
macro_rules! try_to_get_manager_proxy_guard {
|
||||
() => {
|
||||
match MANAGER.lock() {
|
||||
Ok(maybe_manager) => maybe_manager,
|
||||
match MANAGER_PROXY.lock() {
|
||||
Ok(maybe_manager_proxy) => maybe_manager_proxy,
|
||||
Err(poison_error) => {
|
||||
log_with_thread_id!(
|
||||
error,
|
||||
@@ -74,10 +74,10 @@ macro_rules! try_to_get_manager_guard {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! manager_guard_to_manager {
|
||||
($manager_guard:ident) => {
|
||||
match $manager_guard.as_mut() {
|
||||
Some(manager) => manager,
|
||||
macro_rules! manager_proxy_guard_to_manager {
|
||||
($manager_proxy_guard:ident) => {
|
||||
match $manager_proxy_guard.as_mut() {
|
||||
Some(manager_proxy) => manager_proxy,
|
||||
None => {
|
||||
log_with_thread_id!(error, "module state expected to be set, but it is not");
|
||||
return CKR_DEVICE_ERROR;
|
||||
@@ -94,7 +94,7 @@ macro_rules! log_with_thread_id {
|
||||
}
|
||||
|
||||
/// This gets called to initialize the module. For this implementation, this consists of
|
||||
/// instantiating the `Manager`.
|
||||
/// instantiating the `ManagerProxy`.
|
||||
extern "C" fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV {
|
||||
// This will fail if this has already been called, but this isn't a problem because either way,
|
||||
// logging has been initialized.
|
||||
@@ -107,16 +107,16 @@ extern "C" fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV {
|
||||
);
|
||||
}
|
||||
|
||||
let backend = match Backend::new() {
|
||||
Ok(backend) => backend,
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager_proxy = match ManagerProxy::new("osclientcerts", Backend {}) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
log_with_thread_id!(error, "C_Initialize: Backend::new() failed: {}", e);
|
||||
log_with_thread_id!(error, "C_Initialize: ManagerProxy: {}", e);
|
||||
return CKR_DEVICE_ERROR;
|
||||
}
|
||||
};
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
match manager_guard.replace(Manager::new(backend)) {
|
||||
Some(_unexpected_previous_manager) => {
|
||||
match manager_proxy_guard.replace(manager_proxy) {
|
||||
Some(_unexpected_previous_manager_proxy) => {
|
||||
log_with_thread_id!(
|
||||
warn,
|
||||
"C_Initialize: replacing previously set module state (this is expected on macOS but not on Windows)"
|
||||
@@ -129,15 +129,16 @@ extern "C" fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV {
|
||||
}
|
||||
|
||||
extern "C" fn C_Finalize(_pReserved: CK_VOID_PTR) -> CK_RV {
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
match manager_guard.take() {
|
||||
Some(_) => {
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
match manager.stop() {
|
||||
Ok(()) => {
|
||||
log_with_thread_id!(debug, "C_Finalize: CKR_OK");
|
||||
CKR_OK
|
||||
}
|
||||
None => {
|
||||
log_with_thread_id!(debug, "C_Finalize: CKR_CRYPTOKI_NOT_INITIALIZED");
|
||||
CKR_CRYPTOKI_NOT_INITIALIZED
|
||||
Err(e) => {
|
||||
log_with_thread_id!(error, "C_Finalize: CKR_DEVICE_ERROR: {}", e);
|
||||
CKR_DEVICE_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,7 +315,8 @@ extern "C" fn C_SetPIN(
|
||||
CKR_FUNCTION_NOT_SUPPORTED
|
||||
}
|
||||
|
||||
/// This gets called to create a new session. This module defers to the `Manager` to implement this.
|
||||
/// 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,
|
||||
@@ -326,8 +328,8 @@ extern "C" fn C_OpenSession(
|
||||
log_with_thread_id!(error, "C_OpenSession: CKR_ARGUMENTS_BAD");
|
||||
return CKR_ARGUMENTS_BAD;
|
||||
}
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
let manager = manager_guard_to_manager!(manager_guard);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
let session_handle = match manager.open_session() {
|
||||
Ok(session_handle) => session_handle,
|
||||
Err(e) => {
|
||||
@@ -342,10 +344,10 @@ extern "C" fn C_OpenSession(
|
||||
CKR_OK
|
||||
}
|
||||
|
||||
/// This gets called to close a session. This is handled by the `Manager`.
|
||||
/// 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);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
if manager.close_session(hSession).is_err() {
|
||||
log_with_thread_id!(error, "C_CloseSession: CKR_SESSION_HANDLE_INVALID");
|
||||
return CKR_SESSION_HANDLE_INVALID;
|
||||
@@ -354,14 +356,14 @@ extern "C" fn C_CloseSession(hSession: CK_SESSION_HANDLE) -> CK_RV {
|
||||
CKR_OK
|
||||
}
|
||||
|
||||
/// This gets called to close all open sessions at once. This is handled by the `Manager`.
|
||||
/// 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 {
|
||||
log_with_thread_id!(error, "C_CloseAllSessions: CKR_ARGUMENTS_BAD");
|
||||
return CKR_ARGUMENTS_BAD;
|
||||
}
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
let manager = manager_guard_to_manager!(manager_guard);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
match manager.close_all_sessions() {
|
||||
Ok(()) => {
|
||||
log_with_thread_id!(debug, "C_CloseAllSessions: CKR_OK");
|
||||
@@ -457,7 +459,7 @@ extern "C" fn C_GetObjectSize(
|
||||
}
|
||||
|
||||
/// 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 `Manager` find the object
|
||||
/// 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
|
||||
@@ -477,8 +479,8 @@ extern "C" fn C_GetAttributeValue(
|
||||
let attr = unsafe { &*pTemplate.add(i) };
|
||||
attr_types.push(attr.type_);
|
||||
}
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
let manager = manager_guard_to_manager!(manager_guard);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
let values = match manager.get_attributes(hObject, attr_types) {
|
||||
Ok(values) => values,
|
||||
Err(e) => {
|
||||
@@ -570,8 +572,8 @@ const RELEVANT_ATTRIBUTES: &[CK_ATTRIBUTE_TYPE] = &[
|
||||
];
|
||||
|
||||
/// 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 `Manager` to start
|
||||
/// the search.
|
||||
/// 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,
|
||||
@@ -600,8 +602,8 @@ extern "C" fn C_FindObjectsInit(
|
||||
};
|
||||
attrs.push((attr_type, slice.to_owned()));
|
||||
}
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
let manager = manager_guard_to_manager!(manager_guard);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
match manager.start_search(hSession, attrs) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
@@ -614,7 +616,7 @@ extern "C" fn C_FindObjectsInit(
|
||||
}
|
||||
|
||||
/// This gets called after `C_FindObjectsInit` to get the results of a search. This module
|
||||
/// implements this by looking up the search in the `Manager` and copying out the matching
|
||||
/// 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,
|
||||
@@ -626,8 +628,8 @@ extern "C" fn C_FindObjects(
|
||||
log_with_thread_id!(error, "C_FindObjects: CKR_ARGUMENTS_BAD");
|
||||
return CKR_ARGUMENTS_BAD;
|
||||
}
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
let manager = manager_guard_to_manager!(manager_guard);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
let handles = match manager.search(hSession, ulMaxObjectCount as usize) {
|
||||
Ok(handles) => handles,
|
||||
Err(e) => {
|
||||
@@ -655,10 +657,10 @@ extern "C" fn C_FindObjects(
|
||||
}
|
||||
|
||||
/// This gets called after `C_FindObjectsInit` and `C_FindObjects` to finish a search. The module
|
||||
/// tells the `Manager` to clear the search.
|
||||
/// 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);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_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(()) => {
|
||||
@@ -791,7 +793,8 @@ extern "C" fn C_DigestFinal(
|
||||
CKR_FUNCTION_NOT_SUPPORTED
|
||||
}
|
||||
|
||||
/// This gets called to set up a sign operation. The module essentially defers to the `Manager`.
|
||||
/// 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,
|
||||
@@ -818,8 +821,8 @@ extern "C" fn C_SignInit(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
let manager = manager_guard_to_manager!(manager_guard);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
match manager.start_sign(hSession, hKey, mechanism_params) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
@@ -833,7 +836,7 @@ extern "C" fn C_SignInit(
|
||||
|
||||
/// 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
|
||||
/// `Manager` and copies out the resulting signature.
|
||||
/// `ManagerProxy` and copies out the resulting signature.
|
||||
extern "C" fn C_Sign(
|
||||
hSession: CK_SESSION_HANDLE,
|
||||
pData: CK_BYTE_PTR,
|
||||
@@ -847,8 +850,8 @@ extern "C" fn C_Sign(
|
||||
}
|
||||
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);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
match manager.get_signature_length(hSession, data.to_vec()) {
|
||||
Ok(signature_length) => unsafe {
|
||||
*pulSignatureLen = signature_length as CK_ULONG;
|
||||
@@ -859,8 +862,8 @@ extern "C" fn C_Sign(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut manager_guard = try_to_get_manager_guard!();
|
||||
let manager = manager_guard_to_manager!(manager_guard);
|
||||
let mut manager_proxy_guard = try_to_get_manager_proxy_guard!();
|
||||
let manager = manager_proxy_guard_to_manager!(manager_proxy_guard);
|
||||
match manager.sign(hSession, data.to_vec()) {
|
||||
Ok(signature) => {
|
||||
let signature_capacity = unsafe { *pulSignatureLen } as usize;
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
use pkcs11_bindings::*;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::error::{Error, ErrorType};
|
||||
@@ -40,6 +43,281 @@ pub trait ClientCertsBackend {
|
||||
fn find_objects(&self) -> Result<(Vec<Self::Cert>, Vec<Self::Key>), Error>;
|
||||
}
|
||||
|
||||
/// Helper type for sending `ManagerArguments` to the real `Manager`.
|
||||
type ManagerArgumentsSender = Sender<ManagerArguments>;
|
||||
/// Helper type for receiving `ManagerReturnValue`s from the real `Manager`.
|
||||
type ManagerReturnValueReceiver = Receiver<ManagerReturnValue>;
|
||||
|
||||
/// Helper enum that encapsulates arguments to send from the `ManagerProxy` to the real `Manager`.
|
||||
/// `ManagerArguments::Stop` is a special variant that stops the background thread and drops the
|
||||
/// `Manager`.
|
||||
enum ManagerArguments {
|
||||
OpenSession(),
|
||||
CloseSession(CK_SESSION_HANDLE),
|
||||
CloseAllSessions(),
|
||||
StartSearch(CK_SESSION_HANDLE, Vec<(CK_ATTRIBUTE_TYPE, Vec<u8>)>),
|
||||
Search(CK_SESSION_HANDLE, usize),
|
||||
ClearSearch(CK_SESSION_HANDLE),
|
||||
GetAttributes(CK_OBJECT_HANDLE, Vec<CK_ATTRIBUTE_TYPE>),
|
||||
StartSign(
|
||||
CK_SESSION_HANDLE,
|
||||
CK_OBJECT_HANDLE,
|
||||
Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
),
|
||||
GetSignatureLength(CK_SESSION_HANDLE, Vec<u8>),
|
||||
Sign(CK_SESSION_HANDLE, Vec<u8>),
|
||||
Stop,
|
||||
}
|
||||
|
||||
/// Helper enum that encapsulates return values from the real `Manager` that are sent back to the
|
||||
/// `ManagerProxy`. `ManagerReturnValue::Stop` is a special variant that indicates that the
|
||||
/// `Manager` will stop.
|
||||
enum ManagerReturnValue {
|
||||
OpenSession(Result<CK_SESSION_HANDLE, Error>),
|
||||
CloseSession(Result<(), Error>),
|
||||
CloseAllSessions(Result<(), Error>),
|
||||
StartSearch(Result<(), Error>),
|
||||
Search(Result<Vec<CK_OBJECT_HANDLE>, Error>),
|
||||
ClearSearch(Result<(), Error>),
|
||||
GetAttributes(Result<Vec<Option<Vec<u8>>>, Error>),
|
||||
StartSign(Result<(), Error>),
|
||||
GetSignatureLength(Result<usize, Error>),
|
||||
Sign(Result<Vec<u8>, Error>),
|
||||
Stop(Result<(), Error>),
|
||||
}
|
||||
|
||||
/// Helper macro to implement the body of each public `ManagerProxy` function. Takes a
|
||||
/// `ManagerProxy` instance (should always be `self`), a `ManagerArguments` representing the
|
||||
/// `Manager` function to call and the arguments to use, and the qualified type of the expected
|
||||
/// `ManagerReturnValue` that will be received from the `Manager` when it is done.
|
||||
macro_rules! manager_proxy_fn_impl {
|
||||
($manager:ident, $argument_enum:expr, $return_type:path) => {
|
||||
match $manager.proxy_call($argument_enum) {
|
||||
Ok($return_type(result)) => result,
|
||||
Ok(_) => Err(error_here!(ErrorType::LibraryFailure)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// `ManagerProxy` synchronously proxies calls from any thread to the `Manager` that runs on a
|
||||
/// single thread. This is necessary because the underlying OS APIs in use are not guaranteed to be
|
||||
/// thread-safe (e.g. they may use thread-local storage). Using it should be identical to using the
|
||||
/// real `Manager`.
|
||||
pub struct ManagerProxy {
|
||||
sender: ManagerArgumentsSender,
|
||||
receiver: ManagerReturnValueReceiver,
|
||||
thread_handle: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl ManagerProxy {
|
||||
pub fn new<B: ClientCertsBackend + Send + 'static>(
|
||||
name: &'static str,
|
||||
backend: B,
|
||||
) -> Result<ManagerProxy, Error> {
|
||||
let (proxy_sender, manager_receiver) = channel();
|
||||
let (manager_sender, proxy_receiver) = channel();
|
||||
let thread_handle = thread::Builder::new().name(name.into()).spawn(move || {
|
||||
#[cfg(not(test))]
|
||||
gecko_profiler::register_thread(name);
|
||||
|
||||
let mut real_manager = Manager::new(backend);
|
||||
while let Ok(arguments) = manager_receiver.recv() {
|
||||
let results = match arguments {
|
||||
ManagerArguments::OpenSession() => {
|
||||
ManagerReturnValue::OpenSession(real_manager.open_session())
|
||||
}
|
||||
ManagerArguments::CloseSession(session_handle) => {
|
||||
ManagerReturnValue::CloseSession(real_manager.close_session(session_handle))
|
||||
}
|
||||
ManagerArguments::CloseAllSessions() => {
|
||||
ManagerReturnValue::CloseAllSessions(
|
||||
real_manager.close_all_sessions(),
|
||||
)
|
||||
}
|
||||
ManagerArguments::StartSearch(session, attrs) => {
|
||||
ManagerReturnValue::StartSearch(real_manager.start_search(session, attrs))
|
||||
}
|
||||
ManagerArguments::Search(session, max_objects) => {
|
||||
ManagerReturnValue::Search(real_manager.search(session, max_objects))
|
||||
}
|
||||
ManagerArguments::ClearSearch(session) => {
|
||||
ManagerReturnValue::ClearSearch(real_manager.clear_search(session))
|
||||
}
|
||||
ManagerArguments::GetAttributes(object_handle, attr_types) => {
|
||||
ManagerReturnValue::GetAttributes(
|
||||
real_manager.get_attributes(object_handle, attr_types),
|
||||
)
|
||||
}
|
||||
ManagerArguments::StartSign(session, key_handle, params) => {
|
||||
ManagerReturnValue::StartSign(
|
||||
real_manager.start_sign(session, key_handle, params),
|
||||
)
|
||||
}
|
||||
ManagerArguments::GetSignatureLength(session, data) => {
|
||||
ManagerReturnValue::GetSignatureLength(
|
||||
real_manager.get_signature_length(session, data),
|
||||
)
|
||||
}
|
||||
ManagerArguments::Sign(session, data) => {
|
||||
ManagerReturnValue::Sign(real_manager.sign(session, data))
|
||||
}
|
||||
ManagerArguments::Stop => ManagerReturnValue::Stop(Ok(())),
|
||||
};
|
||||
let stop_after_send = matches!(&results, &ManagerReturnValue::Stop(_));
|
||||
match manager_sender.send(results) {
|
||||
Ok(()) => {}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if stop_after_send {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
gecko_profiler::unregister_thread();
|
||||
});
|
||||
match thread_handle {
|
||||
Ok(thread_handle) => Ok(ManagerProxy {
|
||||
sender: proxy_sender,
|
||||
receiver: proxy_receiver,
|
||||
thread_handle: Some(thread_handle),
|
||||
}),
|
||||
Err(_) => Err(error_here!(ErrorType::LibraryFailure)),
|
||||
}
|
||||
}
|
||||
|
||||
fn proxy_call(&self, args: ManagerArguments) -> Result<ManagerReturnValue, Error> {
|
||||
match self.sender.send(args) {
|
||||
Ok(()) => {}
|
||||
Err(_) => {
|
||||
return Err(error_here!(ErrorType::LibraryFailure));
|
||||
}
|
||||
};
|
||||
let result = match self.receiver.recv() {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
return Err(error_here!(ErrorType::LibraryFailure));
|
||||
}
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn open_session(&mut self) -> Result<CK_SESSION_HANDLE, Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::OpenSession(),
|
||||
ManagerReturnValue::OpenSession
|
||||
)
|
||||
}
|
||||
|
||||
pub fn close_session(&mut self, session: CK_SESSION_HANDLE) -> Result<(), Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::CloseSession(session),
|
||||
ManagerReturnValue::CloseSession
|
||||
)
|
||||
}
|
||||
|
||||
pub fn close_all_sessions(&mut self) -> Result<(), Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::CloseAllSessions(),
|
||||
ManagerReturnValue::CloseAllSessions
|
||||
)
|
||||
}
|
||||
|
||||
pub fn start_search(
|
||||
&mut self,
|
||||
session: CK_SESSION_HANDLE,
|
||||
attrs: Vec<(CK_ATTRIBUTE_TYPE, Vec<u8>)>,
|
||||
) -> Result<(), Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::StartSearch(session, attrs),
|
||||
ManagerReturnValue::StartSearch
|
||||
)
|
||||
}
|
||||
|
||||
pub fn search(
|
||||
&mut self,
|
||||
session: CK_SESSION_HANDLE,
|
||||
max_objects: usize,
|
||||
) -> Result<Vec<CK_OBJECT_HANDLE>, Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::Search(session, max_objects),
|
||||
ManagerReturnValue::Search
|
||||
)
|
||||
}
|
||||
|
||||
pub fn clear_search(&mut self, session: CK_SESSION_HANDLE) -> Result<(), Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::ClearSearch(session),
|
||||
ManagerReturnValue::ClearSearch
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_attributes(
|
||||
&self,
|
||||
object_handle: CK_OBJECT_HANDLE,
|
||||
attr_types: Vec<CK_ATTRIBUTE_TYPE>,
|
||||
) -> Result<Vec<Option<Vec<u8>>>, Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::GetAttributes(object_handle, attr_types,),
|
||||
ManagerReturnValue::GetAttributes
|
||||
)
|
||||
}
|
||||
|
||||
pub fn start_sign(
|
||||
&mut self,
|
||||
session: CK_SESSION_HANDLE,
|
||||
key_handle: CK_OBJECT_HANDLE,
|
||||
params: Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<(), Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::StartSign(session, key_handle, params),
|
||||
ManagerReturnValue::StartSign
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_signature_length(
|
||||
&self,
|
||||
session: CK_SESSION_HANDLE,
|
||||
data: Vec<u8>,
|
||||
) -> Result<usize, Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::GetSignatureLength(session, data),
|
||||
ManagerReturnValue::GetSignatureLength
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sign(&mut self, session: CK_SESSION_HANDLE, data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||
manager_proxy_fn_impl!(
|
||||
self,
|
||||
ManagerArguments::Sign(session, data),
|
||||
ManagerReturnValue::Sign
|
||||
)
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) -> Result<(), Error> {
|
||||
manager_proxy_fn_impl!(self, ManagerArguments::Stop, ManagerReturnValue::Stop)?;
|
||||
let thread_handle = match self.thread_handle.take() {
|
||||
Some(thread_handle) => thread_handle,
|
||||
None => return Err(error_here!(ErrorType::LibraryFailure)),
|
||||
};
|
||||
thread_handle
|
||||
.join()
|
||||
.map_err(|_| error_here!(ErrorType::LibraryFailure))
|
||||
}
|
||||
}
|
||||
|
||||
const SUPPORTED_ATTRIBUTES: &[CK_ATTRIBUTE_TYPE] = &[
|
||||
CKA_CLASS,
|
||||
CKA_TOKEN,
|
||||
|
||||
Reference in New Issue
Block a user