summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/builtins/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /security/manager/ssl/builtins/src
parentInitial commit. (diff)
downloadfirefox-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 '')
-rw-r--r--security/manager/ssl/builtins/src/certdata.rs43
-rw-r--r--security/manager/ssl/builtins/src/internal.rs344
-rw-r--r--security/manager/ssl/builtins/src/lib.rs9
-rw-r--r--security/manager/ssl/builtins/src/pkcs11.rs1227
-rw-r--r--security/manager/ssl/builtins/src/version.rs6
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"));