diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /security/manager/ssl/builtins/src | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/manager/ssl/builtins/src')
-rw-r--r-- | security/manager/ssl/builtins/src/certdata.rs | 43 | ||||
-rw-r--r-- | security/manager/ssl/builtins/src/internal.rs | 344 | ||||
-rw-r--r-- | security/manager/ssl/builtins/src/lib.rs | 9 | ||||
-rw-r--r-- | security/manager/ssl/builtins/src/pkcs11.rs | 1227 | ||||
-rw-r--r-- | security/manager/ssl/builtins/src/version.rs | 6 |
5 files changed, 1629 insertions, 0 deletions
diff --git a/security/manager/ssl/builtins/src/certdata.rs b/security/manager/ssl/builtins/src/certdata.rs new file mode 100644 index 0000000000..c5c587b131 --- /dev/null +++ b/security/manager/ssl/builtins/src/certdata.rs @@ -0,0 +1,43 @@ +/* -*- 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::nss::*; +use pkcs11_bindings::*; + +// We need to expand some PKCS#11 / NSS constants as byte arrays for pattern matching and +// C_GetAttributeValue queries. We use native endianness, because PKCS#11 sits between an +// application and a device driver that are running on the same machine. +pub const CKC_X_509_BYTES: &[u8] = &CKC_X_509.to_ne_bytes(); +pub const CKO_CERTIFICATE_BYTES: &[u8] = &CKO_CERTIFICATE.to_ne_bytes(); +pub const CKO_NSS_BUILTIN_ROOT_LIST_BYTES: &[u8] = &CKO_NSS_BUILTIN_ROOT_LIST.to_ne_bytes(); +pub const CKO_NSS_TRUST_BYTES: &[u8] = &CKO_NSS_TRUST.to_ne_bytes(); +pub const CKT_NSS_MUST_VERIFY_TRUST_BYTES: &[u8] = &CKT_NSS_MUST_VERIFY_TRUST.to_ne_bytes(); +pub const CKT_NSS_NOT_TRUSTED_BYTES: &[u8] = &CKT_NSS_NOT_TRUSTED.to_ne_bytes(); +pub const CKT_NSS_TRUSTED_DELEGATOR_BYTES: &[u8] = &CKT_NSS_TRUSTED_DELEGATOR.to_ne_bytes(); +pub const CK_FALSE_BYTES: &[u8] = &CK_FALSE.to_ne_bytes(); +pub const CK_TRUE_BYTES: &[u8] = &CK_TRUE.to_ne_bytes(); + +#[derive(PartialEq, Eq)] +pub struct Root { + pub label: &'static str, + pub der_name: &'static [u8], + pub der_serial: &'static [u8], + pub der_cert: &'static [u8], + pub mozilla_ca_policy: Option<&'static [u8]>, + pub server_distrust_after: Option<&'static [u8]>, + pub email_distrust_after: Option<&'static [u8]>, + pub sha1: &'static [u8], + pub md5: &'static [u8], + pub trust_server: &'static [u8], + pub trust_email: &'static [u8], +} + +impl PartialOrd for Root { + fn partial_cmp(&self, other: &Root) -> Option<std::cmp::Ordering> { + self.der_name.partial_cmp(other.der_name) + } +} + +include!(concat!(env!("OUT_DIR"), "/builtins.rs")); diff --git a/security/manager/ssl/builtins/src/internal.rs b/security/manager/ssl/builtins/src/internal.rs new file mode 100644 index 0000000000..09de4e5b79 --- /dev/null +++ b/security/manager/ssl/builtins/src/internal.rs @@ -0,0 +1,344 @@ +/* -*- 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::nss::*; +use pkcs11_bindings::*; + +use smallvec::SmallVec; + +use crate::certdata::*; + +// The token stores 2N+1 objects: one NSS root list object, N certificate objects, and N trust +// objects. +// +// Internally, the token identifies each object by its ObjectClass (RootList, Certificate, +// or Trust) and its index in the list of objects of the same class. +// +// The PKCS#11 interface, on the other hand, identifies each object with a unique, non-zero, +// unsigned long. This ulong is referred to as the object's CK_OBJECT_HANDLE. +// +// We're free to choose the mapping between ObjectHandles and CK_OBJECT_HANDLEs. Currently we +// encode the ObjectClass in the low 2 bits of the CK_OBJECT_HANDLE and the index in the higher +// bits. We use the values 1, 2, and 3 for ObjectClass to avoid using 0 as a CK_OBJECT_HANDLE. +// +#[derive(Clone, Copy)] +pub enum ObjectClass { + RootList = 1, + Certificate = 2, + Trust = 3, +} + +#[derive(Clone, Copy)] +pub struct ObjectHandle { + class: ObjectClass, + index: usize, +} + +impl TryFrom<CK_OBJECT_HANDLE> for ObjectHandle { + type Error = (); + fn try_from(handle: CK_OBJECT_HANDLE) -> Result<Self, Self::Error> { + if let Ok(handle) = usize::try_from(handle) { + let index = handle >> 2; + let class = match handle & 3 { + 1 if index == 0 => ObjectClass::RootList, + 2 if index < BUILTINS.len() => ObjectClass::Certificate, + 3 if index < BUILTINS.len() => ObjectClass::Trust, + _ => return Err(()), + }; + Ok(ObjectHandle { class, index }) + } else { + Err(()) + } + } +} + +impl From<ObjectHandle> for CK_OBJECT_HANDLE { + fn from(object_handle: ObjectHandle) -> CK_OBJECT_HANDLE { + match CK_OBJECT_HANDLE::try_from(object_handle.index) { + Ok(index) => (index << 2) | (object_handle.class as CK_OBJECT_HANDLE), + Err(_) => 0, + } + } +} + +pub fn get_attribute(attribute: CK_ATTRIBUTE_TYPE, object: &ObjectHandle) -> Option<&'static [u8]> { + match object.class { + ObjectClass::RootList => get_root_list_attribute(attribute), + ObjectClass::Certificate => get_cert_attribute(attribute, &BUILTINS[object.index]), + ObjectClass::Trust => get_trust_attribute(attribute, &BUILTINS[object.index]), + } +} + +// Every attribute that appears in certdata.txt must have a corresponding match arm in one of the +// get_*_attribute functions. +// +fn get_root_list_attribute(attribute: CK_ATTRIBUTE_TYPE) -> Option<&'static [u8]> { + match attribute { + CKA_CLASS => Some(CKO_NSS_BUILTIN_ROOT_LIST_BYTES), + CKA_TOKEN => Some(CK_TRUE_BYTES), + CKA_PRIVATE => Some(CK_FALSE_BYTES), + CKA_MODIFIABLE => Some(CK_FALSE_BYTES), + CKA_LABEL => Some(ROOT_LIST_LABEL), + _ => None, + } +} + +fn get_cert_attribute(attribute: CK_ATTRIBUTE_TYPE, cert: &Root) -> Option<&'static [u8]> { + match attribute { + CKA_CLASS => Some(CKO_CERTIFICATE_BYTES), + CKA_TOKEN => Some(CK_TRUE_BYTES), + CKA_PRIVATE => Some(CK_FALSE_BYTES), + CKA_MODIFIABLE => Some(CK_FALSE_BYTES), + CKA_LABEL => Some(cert.label.as_bytes()), + CKA_CERTIFICATE_TYPE => Some(CKC_X_509_BYTES), + CKA_SUBJECT => Some(cert.der_name), + CKA_ID => Some(b"0\0"), // null terminated to match C implementation + CKA_ISSUER => Some(cert.der_name), + CKA_SERIAL_NUMBER => Some(cert.der_serial), + CKA_VALUE => Some(cert.der_cert), + CKA_NSS_MOZILLA_CA_POLICY => cert.mozilla_ca_policy, + CKA_NSS_SERVER_DISTRUST_AFTER => cert.server_distrust_after, + CKA_NSS_EMAIL_DISTRUST_AFTER => cert.email_distrust_after, + _ => None, + } +} + +fn get_trust_attribute(attribute: CK_ATTRIBUTE_TYPE, cert: &Root) -> Option<&'static [u8]> { + match attribute { + CKA_CLASS => Some(CKO_NSS_TRUST_BYTES), + CKA_TOKEN => Some(CK_TRUE_BYTES), + CKA_PRIVATE => Some(CK_FALSE_BYTES), + CKA_MODIFIABLE => Some(CK_FALSE_BYTES), + CKA_LABEL => Some(cert.label.as_bytes()), + CKA_CERT_SHA1_HASH => Some(cert.sha1), + CKA_CERT_MD5_HASH => Some(cert.md5), + CKA_ISSUER => Some(cert.der_name), + CKA_SERIAL_NUMBER => Some(cert.der_serial), + CKA_TRUST_STEP_UP_APPROVED => Some(CK_FALSE_BYTES), + CKA_TRUST_SERVER_AUTH => Some(cert.trust_server), + CKA_TRUST_EMAIL_PROTECTION => Some(cert.trust_email), + CKA_TRUST_CODE_SIGNING => Some(CKT_NSS_MUST_VERIFY_TRUST_BYTES), + _ => None, + } +} + +// A query matches an object if each term matches some attribute of the object. A search result is +// a list of object handles. Typical queries yield zero or one results, so we optimize for this +// case. +// +pub type Query<'a> = [(CK_ATTRIBUTE_TYPE, &'a [u8])]; +pub type SearchResult = SmallVec<[ObjectHandle; 1]>; + +pub fn search(query: &Query) -> SearchResult { + // The BUILTINS list is sorted by name. So if the query includes a CKA_SUBJECT or CKA_ISSUER + // field we can binary search. + for &(attr, value) in query { + if attr == CKA_SUBJECT || attr == CKA_ISSUER { + return search_by_name(value, query); + } + } + + let mut results: SearchResult = SearchResult::default(); + + // A query with no name term might match the root list object + if match_root_list(query) { + results.push(ObjectHandle { + class: ObjectClass::RootList, + index: 0, + }); + } + + // A query with a CKA_CLASS term matches exactly one type of object, and we should avoid + // iterating over BUILTINS when CKO_CLASS is neither CKO_CERTIFICATE_BYTES nor + // CKO_NSS_TRUST_BYTES. + let mut maybe_cert = true; + let mut maybe_trust = true; + for &(attr, value) in query { + if attr == CKA_CLASS { + maybe_cert = value.eq(CKO_CERTIFICATE_BYTES); + maybe_trust = value.eq(CKO_NSS_TRUST_BYTES); + break; + } + } + + if !(maybe_cert || maybe_trust) { + return results; // The root list or nothing. + } + + for (index, builtin) in BUILTINS.iter().enumerate() { + if maybe_cert && match_cert(query, builtin) { + results.push(ObjectHandle { + class: ObjectClass::Certificate, + index, + }); + } + if maybe_trust && match_trust(query, builtin) { + results.push(ObjectHandle { + class: ObjectClass::Trust, + index, + }); + } + } + results +} + +fn search_by_name(name: &[u8], query: &Query) -> SearchResult { + let mut results: SearchResult = SearchResult::default(); + + let index = match BUILTINS.binary_search_by_key(&name, |r| r.der_name) { + Ok(index) => index, + _ => return results, + }; + + // binary search returned a matching index, but maybe not the smallest + let mut min = index; + while min > 0 && name.eq(BUILTINS[min - 1].der_name) { + min -= 1; + } + + // ... and maybe not the largest. + let mut max = index; + while max < BUILTINS.len() - 1 && name.eq(BUILTINS[max + 1].der_name) { + max += 1; + } + + for (index, builtin) in BUILTINS.iter().enumerate().take(max + 1).skip(min) { + if match_cert(query, builtin) { + results.push(ObjectHandle { + class: ObjectClass::Certificate, + index, + }); + } + if match_trust(query, builtin) { + results.push(ObjectHandle { + class: ObjectClass::Trust, + index, + }); + } + } + + results +} + +fn match_root_list(query: &Query) -> bool { + for &(typ, x) in query { + match get_root_list_attribute(typ) { + Some(y) if x.eq(y) => (), + _ => return false, + } + } + true +} + +fn match_cert(query: &Query, cert: &Root) -> bool { + for &(typ, x) in query { + match get_cert_attribute(typ, cert) { + Some(y) if x.eq(y) => (), + _ => return false, + } + } + true +} + +fn match_trust(query: &Query, cert: &Root) -> bool { + for &(typ, x) in query { + match get_trust_attribute(typ, cert) { + Some(y) if x.eq(y) => (), + _ => return false, + } + } + true +} + +#[cfg(test)] +mod internal_tests { + use crate::certdata::BUILTINS; + use crate::internal::*; + use pkcs11_bindings::*; + + // commented out to avoid vendoring x509_parser + // fn is_valid_utctime(utctime: &[u8]) -> bool { + // /* TODO: actual validation */ + // utctime.len() == 13 + // } + // #[test] + // fn test_certdata() { + // for root in BUILTINS { + // // the der_cert field is valid DER + // let parsed_cert = X509Certificate::from_der(root.der_cert); + // assert!(parsed_cert.is_ok()); + + // // the der_cert field has no trailing data + // let (trailing, parsed_cert) = parsed_cert.unwrap(); + // assert!(trailing.is_empty()); + + // // the der_serial field matches the encoded serial + // assert!(root.der_serial.len() > 2); + // assert!(root.der_serial[0] == 0x02); // der integer + // assert!(root.der_serial[1] <= 20); // no more than 20 bytes long + // assert!(root.der_serial[1] as usize == root.der_serial.len() - 2); + // assert!(parsed_cert.raw_serial().eq(&root.der_serial[2..])); + + // // the der_name field matches the encoded subject + // assert!(parsed_cert.subject.as_raw().eq(root.der_name)); + + // // the der_name field matches the encoded issuer + // assert!(parsed_cert.issuer.as_raw().eq(root.der_name)); + + // // The server_distrust_after field is None or a valid UTC time + // if let Some(utctime) = root.server_distrust_after { + // assert!(is_valid_utctime(&utctime)); + // } + + // // The email_distrust_after field is None or a valid UTC time + // if let Some(utctime) = root.email_distrust_after { + // assert!(is_valid_utctime(&utctime)); + // } + + // assert!( + // root.trust_server == CKT_NSS_MUST_VERIFY_TRUST_BYTES + // || root.trust_server == CKT_NSS_TRUSTED_DELEGATOR_BYTES + // || root.trust_server == CKT_NSS_NOT_TRUSTED_BYTES + // ); + // assert!( + // root.trust_email == CKT_NSS_MUST_VERIFY_TRUST_BYTES + // || root.trust_email == CKT_NSS_TRUSTED_DELEGATOR_BYTES + // || root.trust_email == CKT_NSS_NOT_TRUSTED_BYTES + // ); + // } + // } + + #[test] + fn test_builtins_sorted() { + for i in 0..(BUILTINS.len() - 1) { + assert!(BUILTINS[i].der_name.le(BUILTINS[i + 1].der_name)); + } + } + + #[test] + fn test_search() { + // search for an element that will not be found + let result = search(&[(CKA_TOKEN, &[CK_FALSE])]); + assert_eq!(result.len(), 0); + + // search for root list + let result = search(&[(CKA_CLASS, CKO_NSS_BUILTIN_ROOT_LIST_BYTES)]); + assert!(result.len() == 1); + + // search by name + let result = search(&[ + (CKA_CLASS, CKO_CERTIFICATE_BYTES), + (CKA_SUBJECT, BUILTINS[0].der_name), + ]); + assert!(result.len() >= 1); + + // search by issuer and serial + let result = search(&[ + (CKA_ISSUER, BUILTINS[0].der_name), + (CKA_SERIAL_NUMBER, BUILTINS[0].der_serial), + ]); + assert!(result.len() >= 1); + } +} diff --git a/security/manager/ssl/builtins/src/lib.rs b/security/manager/ssl/builtins/src/lib.rs new file mode 100644 index 0000000000..17b05b454d --- /dev/null +++ b/security/manager/ssl/builtins/src/lib.rs @@ -0,0 +1,9 @@ +/* -*- 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/. */ + +mod certdata; +mod internal; +mod pkcs11; +mod version; diff --git a/security/manager/ssl/builtins/src/pkcs11.rs b/security/manager/ssl/builtins/src/pkcs11.rs new file mode 100644 index 0000000000..56620f4a68 --- /dev/null +++ b/security/manager/ssl/builtins/src/pkcs11.rs @@ -0,0 +1,1227 @@ +/* -*- 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)] + +use pkcs11_bindings::*; +use std::slice; + +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap, BTreeSet}; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::{Mutex, MutexGuard}; + +use crate::internal::{get_attribute, search}; +use crate::internal::{ObjectHandle, Query, SearchResult}; + +use crate::version::*; + +const BUILTINS_VERSION: CK_VERSION = CK_VERSION { + major: NSS_BUILTINS_LIBRARY_VERSION_MAJOR, + minor: NSS_BUILTINS_LIBRARY_VERSION_MINOR, +}; + +const FIRMWARE_VERSION: CK_VERSION = CK_VERSION { + major: NSS_BUILTINS_FIRMWARE_VERSION_MAJOR, + minor: NSS_BUILTINS_FIRMWARE_VERSION_MINOR, +}; + +const CRYPTOKI_VERSION: CK_VERSION = CK_VERSION { + major: NSS_BUILTINS_CRYPTOKI_VERSION_MAJOR, + minor: NSS_BUILTINS_CRYPTOKI_VERSION_MINOR, +}; +const HARDWARE_VERSION: CK_VERSION = CK_VERSION { + major: NSS_BUILTINS_HARDWARE_VERSION_MAJOR, + minor: NSS_BUILTINS_HARDWARE_VERSION_MINOR, +}; + +const MANUFACTURER_ID_BYTES: &[u8; 32] = b"Mozilla Foundation "; +const LIBRARY_DESCRIPTION_BYTES: &[u8; 32] = b"NSS Builtin Object Cryptoki Modu"; + +const SLOT_COUNT: CK_ULONG = 1; +const SLOT_ID_ROOTS: CK_SLOT_ID = 1; +const SLOT_DESCRIPTION_ROOTS_BYTES: &[u8; 64] = + b"NSS Builtin Objects "; + +const TOKEN_LABEL_ROOTS_BYTES: &[u8; 32] = b"Builtin Object Token "; +const TOKEN_MODEL_BYTES: &[u8; 16] = b"1 "; +const TOKEN_SERIAL_NUMBER_BYTES: &[u8; 16] = b"1 "; +const TOKEN_UTC_TIME: &[u8; 16] = b" "; + +#[derive(Debug)] +struct PK11Error(CK_RV); + +// The token assigns session handles using a counter. It would make sense to use a 64 bit counter, +// as there would then be no risk of exhausting the session handle space. However, +// CK_SESSION_HANDLE is defined as a C unsigned long, which is a u32 on some platforms. +// +// We start the counter at 1 since PKCS#11 reserves 0 to signal an invalid handle +// +type SessionHandle = u32; +static NEXT_HANDLE: AtomicU32 = AtomicU32::new(1); + +// The token needs to keep track of which sessions are open. +// +type SessionSet = BTreeSet<SessionHandle>; +static OPEN_SESSIONS: Mutex<Option<SessionSet>> = Mutex::new(None); + +// Helper functions for accessing OPEN_SESSIONS +// +type SessionSetGuard = MutexGuard<'static, Option<SessionSet>>; + +fn get_open_sessions_guard() -> Result<SessionSetGuard, PK11Error> { + OPEN_SESSIONS + .lock() + .map_err(|_| PK11Error(CKR_DEVICE_ERROR)) +} + +fn get_open_sessions(guard: &mut SessionSetGuard) -> Result<&mut SessionSet, PK11Error> { + let sessions = guard + .as_mut() + .ok_or(PK11Error(CKR_CRYPTOKI_NOT_INITIALIZED))?; + Ok(sessions) +} + +// The token needs to cache search results until the client reads them or closes the session. +// +type SearchCache = BTreeMap<SessionHandle, SearchResult>; +static SEARCHES: Mutex<Option<SearchCache>> = Mutex::new(None); + +// Helper functions for accessing SEARCHES +// +type SearchCacheGuard = MutexGuard<'static, Option<SearchCache>>; + +fn get_search_cache_guard() -> Result<SearchCacheGuard, PK11Error> { + SEARCHES.lock().map_err(|_| PK11Error(CKR_DEVICE_ERROR)) +} + +fn get_search_cache(guard: &mut SearchCacheGuard) -> Result<&mut SearchCache, PK11Error> { + let searches = guard + .as_mut() + .ok_or(PK11Error(CKR_CRYPTOKI_NOT_INITIALIZED))?; + Ok(searches) +} + +fn validate_session(handle: SessionHandle) -> Result<(), PK11Error> { + let mut guard = get_open_sessions_guard()?; + let sessions = get_open_sessions(&mut guard)?; + if sessions.contains(&handle) { + return Ok(()); + } + if handle < NEXT_HANDLE.load(Ordering::SeqCst) { + Err(PK11Error(CKR_SESSION_CLOSED)) + } else { + // Possible that NEXT_HANDLE wrapped and we should return CKR_SESSION_CLOSED. + // But this is best-effort. + Err(PK11Error(CKR_SESSION_HANDLE_INVALID)) + } +} + +// The internal implementation of C_Initialize +fn initialize() -> Result<(), PK11Error> { + { + let mut search_cache_guard = get_search_cache_guard()?; + if (*search_cache_guard).is_some() { + return Err(PK11Error(CKR_CRYPTOKI_ALREADY_INITIALIZED)); + } + *search_cache_guard = Some(SearchCache::default()); + } + + { + let mut session_guard = get_open_sessions_guard()?; + if (*session_guard).is_some() { + return Err(PK11Error(CKR_CRYPTOKI_ALREADY_INITIALIZED)); + } + *session_guard = Some(SessionSet::default()); + } + + Ok(()) +} + +// The internal implementation of C_Finalize +fn finalize() -> Result<(), PK11Error> { + { + let mut guard = get_search_cache_guard()?; + // Try to access the search cache to ensure we're initialized. + // Returns CKR_CRYPTOKI_NOT_INITIALIZED if we're not. + let _ = get_search_cache(&mut guard)?; + *guard = None; + } + + let mut guard = get_open_sessions_guard()?; + let _ = get_open_sessions(&mut guard)?; + *guard = None; + + Ok(()) +} + +// Internal implementation of C_OpenSession +fn open_session() -> Result<SessionHandle, PK11Error> { + let mut handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst); + if handle == 0 { + // skip handle 0 if the addition wraps + handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst); + } + + let mut guard = get_open_sessions_guard()?; + let sessions = get_open_sessions(&mut guard)?; + while !sessions.insert(handle) { + // this only executes if NEXT_HANDLE wraps while sessions with + // small handles are still open. + handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst); + } + + Ok(handle) +} + +// Internal implementation of C_CloseSession +fn close_session(session: SessionHandle) -> Result<(), PK11Error> { + { + let mut guard = get_search_cache_guard()?; + let searches = get_search_cache(&mut guard)?; + searches.remove(&session); + } + + { + let mut guard = get_open_sessions_guard()?; + let sessions = get_open_sessions(&mut guard)?; + if sessions.remove(&session) { + Ok(()) + } else if session < NEXT_HANDLE.load(Ordering::SeqCst) { + Err(PK11Error(CKR_SESSION_CLOSED)) + } else { + Err(PK11Error(CKR_SESSION_HANDLE_INVALID)) + } + } +} + +// Internal implementation of C_CloseAllSessions +fn close_all_sessions() -> Result<(), PK11Error> { + { + let mut guard = get_search_cache_guard()?; + let searches = get_search_cache(&mut guard)?; + searches.clear(); + } + + { + let mut guard = get_open_sessions_guard()?; + let sessions = get_open_sessions(&mut guard)?; + sessions.clear(); + } + + Ok(()) +} + +// Internal implementation of C_FindObjectsInit +fn find_objects_init(session: SessionHandle, query: &Query) -> Result<usize, PK11Error> { + validate_session(session)?; + + let results = search(query); + let count = results.len(); + + let mut guard = get_search_cache_guard()?; + let searches = get_search_cache(&mut guard)?; + match searches.entry(session) { + Entry::Occupied(_) => Err(PK11Error(CKR_OPERATION_ACTIVE)), + Entry::Vacant(v) => { + v.insert(results); + Ok(count) + } + } +} + +// Internal implementation of C_FindObjects +fn find_objects(session: SessionHandle, out: &mut [CK_OBJECT_HANDLE]) -> Result<usize, PK11Error> { + validate_session(session)?; + + let mut guard = get_search_cache_guard()?; + let searches = get_search_cache(&mut guard)?; + if let Some(objects) = searches.get_mut(&session) { + for (i, out_i) in out.iter_mut().enumerate() { + match objects.pop() { + Some(object) => *out_i = object.into(), + None => return Ok(i), + } + } + Ok(out.len()) + } else { + Ok(0) + } +} + +// Internal implementation of C_FindObjectsFinal +fn find_objects_final(session: SessionHandle) -> Result<(), PK11Error> { + validate_session(session)?; + + let mut guard = get_search_cache_guard()?; + let searches = get_search_cache(&mut guard)?; + searches.remove(&session); + Ok(()) +} + +extern "C" fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV { + match initialize() { + Ok(_) => CKR_OK, + Err(PK11Error(e)) => e, + } +} + +extern "C" fn C_Finalize(pReserved: CK_VOID_PTR) -> CK_RV { + if !pReserved.is_null() { + return CKR_ARGUMENTS_BAD; + } + match finalize() { + Ok(_) => CKR_OK, + Err(PK11Error(e)) => e, + } +} + +extern "C" fn C_GetInfo(pInfo: CK_INFO_PTR) -> CK_RV { + if pInfo.is_null() { + return CKR_ARGUMENTS_BAD; + } + unsafe { + *pInfo = CK_INFO { + cryptokiVersion: CRYPTOKI_VERSION, + manufacturerID: *MANUFACTURER_ID_BYTES, + flags: 0, + libraryDescription: *LIBRARY_DESCRIPTION_BYTES, + libraryVersion: BUILTINS_VERSION, + }; + } + CKR_OK +} + +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_ROOTS; + } + } + unsafe { + *pulCount = SLOT_COUNT; + } + CKR_OK +} + +extern "C" fn C_GetSlotInfo(slotID: CK_SLOT_ID, pInfo: CK_SLOT_INFO_PTR) -> CK_RV { + if (slotID != SLOT_ID_ROOTS) || pInfo.is_null() { + return CKR_ARGUMENTS_BAD; + } + unsafe { + *pInfo = CK_SLOT_INFO { + slotDescription: *SLOT_DESCRIPTION_ROOTS_BYTES, + manufacturerID: *MANUFACTURER_ID_BYTES, + flags: CKF_TOKEN_PRESENT, + hardwareVersion: HARDWARE_VERSION, + firmwareVersion: FIRMWARE_VERSION, + }; + } + CKR_OK +} + +extern "C" fn C_GetTokenInfo(slotID: CK_SLOT_ID, pInfo: CK_TOKEN_INFO_PTR) -> CK_RV { + if (slotID != SLOT_ID_ROOTS) || pInfo.is_null() { + return CKR_ARGUMENTS_BAD; + } + unsafe { + *pInfo = CK_TOKEN_INFO { + label: *TOKEN_LABEL_ROOTS_BYTES, + manufacturerID: *MANUFACTURER_ID_BYTES, + model: *TOKEN_MODEL_BYTES, + serialNumber: *TOKEN_SERIAL_NUMBER_BYTES, + flags: CKF_WRITE_PROTECTED, + ulMaxSessionCount: CK_UNAVAILABLE_INFORMATION, + ulSessionCount: 0, + ulMaxRwSessionCount: CK_UNAVAILABLE_INFORMATION, + ulRwSessionCount: 0, + ulMaxPinLen: CK_UNAVAILABLE_INFORMATION, + ulMinPinLen: CK_UNAVAILABLE_INFORMATION, + ulTotalPublicMemory: CK_UNAVAILABLE_INFORMATION, + ulFreePublicMemory: CK_UNAVAILABLE_INFORMATION, + ulTotalPrivateMemory: CK_UNAVAILABLE_INFORMATION, + ulFreePrivateMemory: CK_UNAVAILABLE_INFORMATION, + hardwareVersion: HARDWARE_VERSION, + firmwareVersion: FIRMWARE_VERSION, + utcTime: *TOKEN_UTC_TIME, + }; + } + CKR_OK +} + +extern "C" fn C_GetMechanismList( + slotID: CK_SLOT_ID, + _pMechanismList: CK_MECHANISM_TYPE_PTR, + pulCount: CK_ULONG_PTR, +) -> CK_RV { + if slotID != SLOT_ID_ROOTS || pulCount.is_null() { + return CKR_ARGUMENTS_BAD; + } + unsafe { + *pulCount = 0; + } + 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 +} + +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_ROOTS || phSession.is_null() { + return CKR_ARGUMENTS_BAD; + } + // [pkcs11-base-v3.0, Section 5.6.1] + // For legacy reasons, the CKF_SERIAL_SESSION bit MUST always be set; if a call to + // C_OpenSession does not have this bit set, the call should return unsuccessfully with the + // error code CKR_SESSION_PARALLEL_NOT_SUPPORTED. + if flags & CKF_SERIAL_SESSION == 0 { + return CKR_SESSION_PARALLEL_NOT_SUPPORTED; + } + let session_id = match open_session() { + Ok(session_id) => session_id as CK_SESSION_HANDLE, + Err(PK11Error(e)) => return e, + }; + unsafe { *phSession = session_id }; + CKR_OK +} + +extern "C" fn C_CloseSession(hSession: CK_SESSION_HANDLE) -> CK_RV { + let session: SessionHandle = match hSession.try_into() { + Ok(session) => session, + Err(_) => return CKR_SESSION_HANDLE_INVALID, + }; + match close_session(session) { + Ok(_) => CKR_OK, + Err(PK11Error(e)) => e, + } +} + +extern "C" fn C_CloseAllSessions(slotID: CK_SLOT_ID) -> CK_RV { + if slotID != SLOT_ID_ROOTS { + return CKR_ARGUMENTS_BAD; + } + match close_all_sessions() { + Ok(_) => CKR_OK, + Err(PK11Error(e)) => e, + } +} + +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 +} + +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 +} + +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 count: usize = match ulCount.try_into() { + Ok(count) => count, + Err(_) => return CKR_ARGUMENTS_BAD, + }; + + // C_GetAttributeValue has a session handle parameter because PKCS#11 objects can have + // session-bound lifetimes and access controls. We don't have any session objects, and all of + // our token objects are public. So there's no good reason to validate the session handle. + // + //let session: SessionHandle = match hSession.try_into() { + // Ok(session) => session, + // Err(_) => return CKR_SESSION_HANDLE_INVALID, + //}; + // + //if let Err(PK11Error(e)) = validate_session(session) { + // return e; + //} + + let handle: ObjectHandle = match hObject.try_into() { + Ok(handle) => handle, + Err(_) => return CKR_OBJECT_HANDLE_INVALID, + }; + + let attrs: &mut [CK_ATTRIBUTE] = unsafe { slice::from_raw_parts_mut(pTemplate, count) }; + + let mut rv = CKR_OK; + + // Handle requests with null pValue fields + for attr in attrs.iter_mut().filter(|x| x.pValue.is_null()) { + attr.ulValueLen = match get_attribute(attr.type_, &handle) { + None => { + // [pkcs11-base-v3.0, Section 5.7.5] + // 2. [...] if the specified value for the object is invalid (the object does not possess + // such an attribute), then the ulValueLen field in that triple is modified to hold the + // value CK_UNAVAILABLE_INFORMATION. + rv = CKR_ATTRIBUTE_TYPE_INVALID; + CK_UNAVAILABLE_INFORMATION + } + Some(attr) => { + // [pkcs11-base-v3.0, Section 5.7.5] + // 3. [...] if the pValue field has the value NULL_PTR, then the ulValueLen field is modified + // to hold the exact length of the specified attribute for the object. + attr.len() as CK_ULONG + } + } + } + + // Handle requests with non-null pValue fields + for attr in attrs.iter_mut().filter(|x| !x.pValue.is_null()) { + let dst_len: usize = match attr.ulValueLen.try_into() { + Ok(dst_len) => dst_len, + Err(_) => return CKR_ARGUMENTS_BAD, + }; + attr.ulValueLen = match get_attribute(attr.type_, &handle) { + None => { + // [pkcs11-base-v3.0, Section 5.7.5] + // 2. [...] if the specified value for the object is invalid (the object does not possess + // such an attribute), then the ulValueLen field in that triple is modified to hold the + // value CK_UNAVAILABLE_INFORMATION. + rv = CKR_ATTRIBUTE_TYPE_INVALID; + CK_UNAVAILABLE_INFORMATION + } + Some(src) if dst_len >= src.len() => { + // [pkcs11-base-v3.0, Section 5.7.5] + // 4. [...] if the length specified in ulValueLen is large enough to hold the value + // of the specified attribute for the object, then that attribute is copied into + // the buffer located at pValue, and the ulValueLen field is modified to hold + // the exact length of the attribute. + let dst: &mut [u8] = + unsafe { slice::from_raw_parts_mut(attr.pValue as *mut u8, dst_len) }; + dst[..src.len()].copy_from_slice(src); + src.len() as CK_ULONG + } + _ => { + // [pkcs11-base-v3.0, Section 5.7.5] + // 5. Otherwise, the ulValueLen field is modified to hold the value + // CK_UNAVAILABLE_INFORMATION. + rv = CKR_BUFFER_TOO_SMALL; + CK_UNAVAILABLE_INFORMATION + } + }; + } + + // [pkcs11-base-v3.0, Section 5.7.5] + // If case 2 applies to any of the requested attributes, then the call should return the value + // CKR_ATTRIBUTE_TYPE_INVALID. If case 5 applies to any of the requested attributes, then the + // call should return the value CKR_BUFFER_TOO_SMALL. As usual, if more than one of these + // error codes is applicable, Cryptoki may return any of them. Only if none of them applies to + // any of the requested attributes will CKR_OK be returned. + rv +} + +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 +} + +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 count: usize = match ulCount.try_into() { + Ok(count) => count, + Err(_) => return CKR_ARGUMENTS_BAD, + }; + let session: SessionHandle = match hSession.try_into() { + Ok(session) => session, + Err(_) => return CKR_SESSION_HANDLE_INVALID, + }; + + let raw_attrs: &[CK_ATTRIBUTE] = unsafe { slice::from_raw_parts_mut(pTemplate, count) }; + + let mut query: Vec<(CK_ATTRIBUTE_TYPE, &[u8])> = Vec::with_capacity(raw_attrs.len()); + for attr in raw_attrs { + match usize::try_from(attr.ulValueLen) { + Ok(len) => query.push((attr.type_, unsafe { + slice::from_raw_parts_mut(attr.pValue as *mut u8, len) + })), + Err(_) => return CKR_ARGUMENTS_BAD, + } + } + + match find_objects_init(session, &query) { + Ok(_) => CKR_OK, + Err(PK11Error(e)) => e, + } +} + +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() { + return CKR_ARGUMENTS_BAD; + } + let max_object_count: usize = match ulMaxObjectCount.try_into() { + Ok(max_object_count) => max_object_count, + Err(_) => return CKR_ARGUMENTS_BAD, + }; + let session: SessionHandle = match hSession.try_into() { + Ok(session) => session, + Err(_) => return CKR_SESSION_HANDLE_INVALID, + }; + let out: &mut [CK_OBJECT_HANDLE] = + unsafe { slice::from_raw_parts_mut(phObject, max_object_count) }; + match find_objects(session, out) { + Ok(num_found) => { + unsafe { *pulObjectCount = num_found as CK_ULONG }; + CKR_OK + } + Err(PK11Error(e)) => e, + } +} + +extern "C" fn C_FindObjectsFinal(hSession: CK_SESSION_HANDLE) -> CK_RV { + let session: SessionHandle = match hSession.try_into() { + Ok(session) => session, + Err(_) => return CKR_SESSION_HANDLE_INVALID, + }; + match find_objects_final(session) { + Ok(()) => CKR_OK, + Err(PK11Error(e)) => e, + } +} + +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 +} + +extern "C" fn C_SignInit( + _hSession: CK_SESSION_HANDLE, + _pMechanism: CK_MECHANISM_PTR, + _hKey: CK_OBJECT_HANDLE, +) -> CK_RV { + CKR_FUNCTION_NOT_SUPPORTED +} + +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 { + CKR_FUNCTION_NOT_SUPPORTED +} + +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 +} + +pub static mut FUNCTION_LIST: CK_FUNCTION_LIST = CK_FUNCTION_LIST { + version: CRYPTOKI_VERSION, + 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), +}; + +#[no_mangle] +pub unsafe fn BUILTINSC_GetFunctionList(ppFunctionList: CK_FUNCTION_LIST_PTR_PTR) -> CK_RV { + if ppFunctionList.is_null() { + return CKR_ARGUMENTS_BAD; + } + *ppFunctionList = &mut FUNCTION_LIST; + CKR_OK +} + +#[cfg(test)] +mod pkcs11_tests { + use crate::certdata::*; + use crate::internal::*; + use crate::pkcs11::*; + + #[test] + fn test_main() { + // We need to run tests serially because of C_Initialize / C_Finalize calls. + test_simple(); + test_c_get_function_list(); + test_c_get_attribute(); + } + + fn test_simple() { + let query = &[(CKA_CLASS, CKO_CERTIFICATE_BYTES)]; + initialize().expect("initialize should not fail."); + let hSession = open_session().expect("open_session should not fail."); + let count = find_objects_init(hSession, query).expect("find_objects_init should not fail."); + assert_eq!(count, BUILTINS.len()); + let mut results: [CK_OBJECT_HANDLE; 10] = [0; 10]; + let n_read = + find_objects(hSession, &mut results).expect("find_objects_init should not fail."); + assert_eq!(n_read, 10); + finalize().expect("finalize should not fail."); + } + + fn test_c_get_function_list() { + let c_null = 0 as *mut std::ffi::c_void; + let mut pFunctionList: CK_FUNCTION_LIST_PTR = c_null as CK_FUNCTION_LIST_PTR; + let rv = unsafe { crate::pkcs11::BUILTINSC_GetFunctionList(&mut pFunctionList) }; + assert_eq!(CKR_OK, rv); + if let Some(pC_Initialize) = unsafe { (*pFunctionList).C_Initialize } { + let rv = unsafe { pC_Initialize(c_null) }; + assert_eq!(CKR_OK, rv); + } else { + assert!(false); + } + + if let Some(pC_Finalize) = unsafe { (*pFunctionList).C_Finalize } { + let rv = unsafe { pC_Finalize(c_null) }; + assert_eq!(CKR_OK, rv); + } else { + assert!(false); + } + } + + fn test_c_get_attribute() { + let c_null = 0 as *mut std::ffi::c_void; + let template: &mut [CK_ATTRIBUTE] = &mut [CK_ATTRIBUTE { + type_: CKA_SUBJECT, + pValue: c_null, + ulValueLen: 0, + }]; + let template_ptr = &mut template[0] as CK_ATTRIBUTE_PTR; + let object: CK_OBJECT_HANDLE = 2; + let mut session: CK_SESSION_HANDLE = 0; + assert_eq!(CKR_OK, C_Initialize(c_null)); + assert_eq!( + CKR_OK, + C_OpenSession( + SLOT_ID_ROOTS, + CKF_SERIAL_SESSION, + c_null, + None, + &mut session as *mut CK_SESSION_HANDLE + ) + ); + assert_eq!( + CKR_OK, + C_GetAttributeValue(session, object, template_ptr, 1) + ); + let len = template[0].ulValueLen as usize; + assert_eq!(len, BUILTINS[0].der_name.len()); + + let value: &mut [u8] = &mut vec![0; 1]; + let value_ptr: *mut u8 = &mut value[0] as *mut u8; + template[0].pValue = value_ptr as *mut std::ffi::c_void; + template[0].ulValueLen = 1; + assert_eq!( + CKR_BUFFER_TOO_SMALL, + C_GetAttributeValue(session, object, template_ptr, 1) + ); + assert_eq!(template[0].ulValueLen, CK_UNAVAILABLE_INFORMATION); + + let value: &mut [u8] = &mut vec![0; len]; + let value_ptr: *mut u8 = &mut value[0] as *mut u8; + template[0].pValue = value_ptr as *mut std::ffi::c_void; + template[0].ulValueLen = len as CK_ULONG; + assert_eq!( + CKR_OK, + C_GetAttributeValue(session, object, template_ptr, 1) + ); + assert_eq!(value, BUILTINS[0].der_name); + assert_eq!(CKR_OK, C_Finalize(c_null)); + } +} diff --git a/security/manager/ssl/builtins/src/version.rs b/security/manager/ssl/builtins/src/version.rs new file mode 100644 index 0000000000..f4fc63711d --- /dev/null +++ b/security/manager/ssl/builtins/src/version.rs @@ -0,0 +1,6 @@ +/* -*- 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/. */ + +include!(concat!(env!("OUT_DIR"), "/version.rs")); |