From 3241c1b8e782d78615f596c1e7b9312a9b8ff5a5 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 11 Nov 2021 11:21:48 -0500 Subject: [PATCH 01/36] Add a missing flag value used by TokenInfo This flag indicates a failure in a token self-test. See: PKCS#11 2.40, Table 6 Signed-off-by: Keith Koskie --- cryptoki-sys/pkcs11.h | 1 + cryptoki-sys/src/lib.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/cryptoki-sys/pkcs11.h b/cryptoki-sys/pkcs11.h index 1cdfb1e5..b1700cb6 100644 --- a/cryptoki-sys/pkcs11.h +++ b/cryptoki-sys/pkcs11.h @@ -293,6 +293,7 @@ struct ck_token_info #define CKF_SO_PIN_FINAL_TRY (1UL << 21) #define CKF_SO_PIN_LOCKED (1UL << 22) #define CKF_SO_PIN_TO_BE_CHANGED (1UL << 23) +#define CKF_ERROR_STATE (1UL << 24) #define CK_UNAVAILABLE_INFORMATION ((unsigned long)-1L) #define CK_EFFECTIVELY_INFINITE (0UL) diff --git a/cryptoki-sys/src/lib.rs b/cryptoki-sys/src/lib.rs index 59921890..5f7933f9 100644 --- a/cryptoki-sys/src/lib.rs +++ b/cryptoki-sys/src/lib.rs @@ -124,6 +124,7 @@ pub const CKF_SO_PIN_COUNT_LOW: CK_FLAGS = 0x00100000; pub const CKF_SO_PIN_FINAL_TRY: CK_FLAGS = 0x00200000; pub const CKF_SO_PIN_LOCKED: CK_FLAGS = 0x00400000; pub const CKF_SO_PIN_TO_BE_CHANGED: CK_FLAGS = 0x00800000; +pub const CKF_ERROR_STATE: CK_FLAGS = 0x01000000; pub const CK_UNAVAILABLE_INFORMATION: CK_ULONG = !0; pub const CK_EFFECTIVELY_INFINITE: CK_ULONG = 0; pub const CK_INVALID_HANDLE: CK_ULONG = 0; From 5951c483be48f50f11d53bdb8aa9d351806aa2d7 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 11 Nov 2021 15:38:26 -0500 Subject: [PATCH 02/36] Add two missing MechanismInfo flags Signed-off-by: Keith Koskie --- cryptoki-sys/pkcs11.h | 2 ++ cryptoki-sys/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cryptoki-sys/pkcs11.h b/cryptoki-sys/pkcs11.h index b1700cb6..5623df18 100644 --- a/cryptoki-sys/pkcs11.h +++ b/cryptoki-sys/pkcs11.h @@ -1045,6 +1045,8 @@ struct ck_aes_cbc_encrypt_data_params { #define CKF_EXTENSION ((unsigned long) (1UL << 31)) #define CKF_EC_F_P (1UL << 20) +#define CKF_EC_F_2M (1UL << 21) +#define CKF_EC_ECPARAMETERS (1UL << 22) #define CKF_EC_NAMEDCURVE (1UL << 23) #define CKF_EC_UNCOMPRESS (1UL << 24) #define CKF_EC_COMPRESS (1UL << 25) diff --git a/cryptoki-sys/src/lib.rs b/cryptoki-sys/src/lib.rs index 5f7933f9..869ddc97 100644 --- a/cryptoki-sys/src/lib.rs +++ b/cryptoki-sys/src/lib.rs @@ -690,6 +690,8 @@ pub const CKF_UNWRAP: CK_FLAGS = 0x00040000; pub const CKF_DERIVE: CK_FLAGS = 0x00080000; pub const CKF_EXTENSION: CK_FLAGS = 0x80000000; pub const CKF_EC_F_P: CK_FLAGS = 0x00100000; +pub const CKF_EC_F_2M: CK_FLAGS = 0x00200000; +pub const CKF_EC_ECPARAMETERS: CK_FLAGS = 0x00400000; pub const CKF_EC_NAMEDCURVE: CK_FLAGS = 0x00800000; pub const CKF_EC_UNCOMPRESS: CK_FLAGS = 0x01000000; pub const CKF_EC_COMPRESS: CK_FLAGS = 0x02000000; From 59fc60dcc05551ccd953b1c95a2c6a267ae06829 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Sat, 6 Nov 2021 10:12:45 -0400 Subject: [PATCH 03/36] Remove unused SlotInfo traits Signed-off-by: Keith Koskie --- cryptoki/src/slot/mod.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index edc50c87..31502a38 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -124,20 +124,6 @@ impl SlotInfo { } } -impl Deref for SlotInfo { - type Target = CK_SLOT_INFO; - - fn deref(&self) -> &Self::Target { - &self.val - } -} - -impl From for CK_SLOT_INFO { - fn from(token_info: SlotInfo) -> Self { - *token_info - } -} - /// Contains information about a token #[derive(Debug, Clone, Copy, Default)] pub struct TokenInfo { From 0fcd6110cd714920073d5e07a51f8d080a4edec1 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Sat, 6 Nov 2021 10:14:38 -0400 Subject: [PATCH 04/36] Convert SlotInfo contents once Give the SlotInfo struct its own member definitions instead of being a wrapper around CK_SLOT_INFO. This causes the conversion for each memmber to happen once on construction. Signed-off-by: Keith Koskie --- cryptoki/src/slot/mod.rs | 41 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 31502a38..3b7a332b 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -88,39 +88,48 @@ impl std::fmt::Display for Slot { } /// Contains information about the slot -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone)] pub struct SlotInfo { - val: CK_SLOT_INFO, + slot_description: String, + manufacturer_id: String, + flags: SlotFlags, + hardware_version: Version, + firmware_version: Version, } impl SlotInfo { pub(crate) fn new(val: CK_SLOT_INFO) -> Self { - Self { val } + Self { + slot_description: string_from_blank_padded(&val.slotDescription), + manufacturer_id: string_from_blank_padded(&val.manufacturerID), + flags: val.flags.into(), + hardware_version: val.hardwareVersion.into(), + firmware_version: val.firmwareVersion.into(), + } + } + /// Returns the slot description + pub fn slot_description(&self) -> &str { + &self.slot_description } - /// Returns the firmware version - pub fn firmware_version(&self) -> Version { - self.val.firmwareVersion.into() + /// Returns the manufacturer ID + pub fn manufacturer_id(&self) -> &str { + &self.manufacturer_id } /// Returns the flags of the slot pub fn flags(&self) -> SlotFlags { - self.val.flags.into() + self.flags } /// Returns the hardware version pub fn hardware_version(&self) -> Version { - self.val.hardwareVersion.into() + self.hardware_version } - /// Returns the manufacturer ID - pub fn manufacturer_id(&self) -> String { - string_from_blank_padded(&self.val.manufacturerID) - } - - /// Returns the slot description - pub fn slot_description(&self) -> String { - string_from_blank_padded(&self.val.slotDescription) + /// Returns the firmware version + pub fn firmware_version(&self) -> Version { + self.firmware_version } } From 8e5db2619901b10e2729feee89e6ba7933ac3894 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Sat, 6 Nov 2021 10:28:39 -0400 Subject: [PATCH 05/36] Replace SlotInfo::new() with From impl Using the From trait here is both more flexible and also a better semantic match for how SlotInfo is constructed. Signed-off-by: Keith Koskie --- cryptoki/src/context/slot_token_management.rs | 2 +- cryptoki/src/slot/mod.rs | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cryptoki/src/context/slot_token_management.rs b/cryptoki/src/context/slot_token_management.rs index 7257b0cf..e5525aa2 100644 --- a/cryptoki/src/context/slot_token_management.rs +++ b/cryptoki/src/context/slot_token_management.rs @@ -121,7 +121,7 @@ pub(super) fn get_slot_info(ctx: &Pkcs11, slot: Slot) -> Result { &mut slot_info, )) .into_result()?; - Ok(SlotInfo::new(slot_info)) + Ok(SlotInfo::from(slot_info)) } } diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 3b7a332b..d8c586a1 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -98,15 +98,6 @@ pub struct SlotInfo { } impl SlotInfo { - pub(crate) fn new(val: CK_SLOT_INFO) -> Self { - Self { - slot_description: string_from_blank_padded(&val.slotDescription), - manufacturer_id: string_from_blank_padded(&val.manufacturerID), - flags: val.flags.into(), - hardware_version: val.hardwareVersion.into(), - firmware_version: val.firmwareVersion.into(), - } - } /// Returns the slot description pub fn slot_description(&self) -> &str { &self.slot_description @@ -133,6 +124,18 @@ impl SlotInfo { } } +impl From for SlotInfo { + fn from(val: CK_SLOT_INFO) -> Self { + Self { + slot_description: string_from_blank_padded(&val.slotDescription), + manufacturer_id: string_from_blank_padded(&val.manufacturerID), + flags: val.flags.into(), + hardware_version: val.hardwareVersion.into(), + firmware_version: val.firmwareVersion.into(), + } + } +} + /// Contains information about a token #[derive(Debug, Clone, Copy, Default)] pub struct TokenInfo { From 937d9048e13f5f7f8a2766f51d4c10e3a89522b3 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Sat, 6 Nov 2021 11:08:58 -0400 Subject: [PATCH 06/36] Merge get_all_slots and get_all_slots_with_token These functions differ only in a boolean. Them being distinct is even less necessary since they've been accessed through parent module stubs. Signed-off-by: Keith Koskie --- cryptoki/src/context/mod.rs | 5 ++- cryptoki/src/context/slot_token_management.rs | 41 ++----------------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/cryptoki/src/context/mod.rs b/cryptoki/src/context/mod.rs index d0f6c66b..ccc9f29e 100644 --- a/cryptoki/src/context/mod.rs +++ b/cryptoki/src/context/mod.rs @@ -20,6 +20,7 @@ mod locking; mod session_management; mod slot_token_management; +use cryptoki_sys::{CK_TRUE, CK_FALSE}; pub use flags::*; pub use info::*; pub use locking::*; @@ -117,7 +118,7 @@ impl Pkcs11 { /// Get all slots available with a token pub fn get_slots_with_token(&self) -> Result> { - slot_token_management::get_slots_with_token(self) + slot_token_management::get_slots(self, CK_TRUE) } /// Get all slots available with a token @@ -127,7 +128,7 @@ impl Pkcs11 { /// Get all slots pub fn get_all_slots(&self) -> Result> { - slot_token_management::get_all_slots(self) + slot_token_management::get_slots(self, CK_FALSE) } /// Initialize a token diff --git a/cryptoki/src/context/slot_token_management.rs b/cryptoki/src/context/slot_token_management.rs index e5525aa2..3812fc3b 100644 --- a/cryptoki/src/context/slot_token_management.rs +++ b/cryptoki/src/context/slot_token_management.rs @@ -7,17 +7,17 @@ use crate::error::{Result, Rv}; use crate::label_from_str; use crate::mechanism::{MechanismInfo, MechanismType}; use crate::slot::{Slot, SlotInfo, TokenInfo}; -use cryptoki_sys::{CK_MECHANISM_INFO, CK_SLOT_INFO, CK_TOKEN_INFO}; +use cryptoki_sys::{CK_BBOOL, CK_MECHANISM_INFO, CK_SLOT_INFO, CK_TOKEN_INFO}; use std::convert::TryInto; // See public docs on stub in parent mod.rs #[inline(always)] -pub(super) fn get_slots_with_token(ctx: &Pkcs11) -> Result> { +pub(super) fn get_slots(ctx: &Pkcs11, with_token: CK_BBOOL) -> Result> { let mut slot_count = 0; unsafe { Rv::from(get_pkcs11!(ctx, C_GetSlotList)( - cryptoki_sys::CK_TRUE, + with_token, std::ptr::null_mut(), &mut slot_count, )) @@ -28,7 +28,7 @@ pub(super) fn get_slots_with_token(ctx: &Pkcs11) -> Result> { unsafe { Rv::from(get_pkcs11!(ctx, C_GetSlotList)( - cryptoki_sys::CK_TRUE, + with_token, slots.as_mut_ptr(), &mut slot_count, )) @@ -63,39 +63,6 @@ pub(super) fn get_slots_with_initialized_token(ctx: &Pkcs11) -> Result .collect() } -// See public docs on stub in parent mod.rs -#[inline(always)] -pub(super) fn get_all_slots(ctx: &Pkcs11) -> Result> { - let mut slot_count = 0; - - unsafe { - Rv::from(get_pkcs11!(ctx, C_GetSlotList)( - cryptoki_sys::CK_FALSE, - std::ptr::null_mut(), - &mut slot_count, - )) - .into_result()?; - } - - let mut slots = vec![0; slot_count.try_into()?]; - - unsafe { - Rv::from(get_pkcs11!(ctx, C_GetSlotList)( - cryptoki_sys::CK_FALSE, - slots.as_mut_ptr(), - &mut slot_count, - )) - .into_result()?; - } - - let mut slots: Vec = slots.into_iter().map(Slot::new).collect(); - - // This should always truncate slots. - slots.resize(slot_count.try_into()?, Slot::new(0)); - - Ok(slots) -} - // See public docs on stub in parent mod.rs #[inline(always)] pub(super) fn init_token(ctx: &Pkcs11, slot: Slot, pin: &str, label: &str) -> Result<()> { From e4436e2945597d3294053d7da7b4e3b49555e140 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Sat, 6 Nov 2021 12:00:19 -0400 Subject: [PATCH 07/36] Fix race condition in get_slots Fixes #34 with respect to slots (still open for mechanisms) Signed-off-by: Keith Koskie --- cryptoki/src/context/mod.rs | 2 +- cryptoki/src/context/slot_token_management.rs | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/cryptoki/src/context/mod.rs b/cryptoki/src/context/mod.rs index ccc9f29e..88268423 100644 --- a/cryptoki/src/context/mod.rs +++ b/cryptoki/src/context/mod.rs @@ -20,7 +20,7 @@ mod locking; mod session_management; mod slot_token_management; -use cryptoki_sys::{CK_TRUE, CK_FALSE}; +use cryptoki_sys::{CK_FALSE, CK_TRUE}; pub use flags::*; pub use info::*; pub use locking::*; diff --git a/cryptoki/src/context/slot_token_management.rs b/cryptoki/src/context/slot_token_management.rs index 3812fc3b..9c0e8cda 100644 --- a/cryptoki/src/context/slot_token_management.rs +++ b/cryptoki/src/context/slot_token_management.rs @@ -10,37 +10,37 @@ use crate::slot::{Slot, SlotInfo, TokenInfo}; use cryptoki_sys::{CK_BBOOL, CK_MECHANISM_INFO, CK_SLOT_INFO, CK_TOKEN_INFO}; use std::convert::TryInto; +use crate::error::RvError::BufferTooSmall; + // See public docs on stub in parent mod.rs #[inline(always)] pub(super) fn get_slots(ctx: &Pkcs11, with_token: CK_BBOOL) -> Result> { let mut slot_count = 0; - - unsafe { - Rv::from(get_pkcs11!(ctx, C_GetSlotList)( - with_token, - std::ptr::null_mut(), - &mut slot_count, - )) - .into_result()?; + let rval = unsafe { + get_pkcs11!(ctx, C_GetSlotList)(with_token, std::ptr::null_mut(), &mut slot_count) + }; + Rv::from(rval).into_result()?; + + let mut slots; + loop { + slots = vec![0; slot_count as usize]; + let rval = unsafe { + get_pkcs11!(ctx, C_GetSlotList)(with_token, slots.as_mut_ptr(), &mut slot_count) + }; + // Account for a race condition between the call to get the + // slot_count and the last call in which the number of slots grew. + // In this case, slot_count will have been updated to the larger amount + // and we want to loop again with a resized buffer. + if !matches!(Rv::from(rval), Rv::Error(BufferTooSmall)) { + // Account for other possible error types + Rv::from(rval).into_result()?; + // Otherwise, we have a valid list to process + break; + } } - - let mut slots = vec![0; slot_count.try_into()?]; - - unsafe { - Rv::from(get_pkcs11!(ctx, C_GetSlotList)( - with_token, - slots.as_mut_ptr(), - &mut slot_count, - )) - .into_result()?; - } - - let mut slots: Vec = slots.into_iter().map(Slot::new).collect(); - - // This should always truncate slots. - slots.resize(slot_count.try_into()?, Slot::new(0)); - - Ok(slots) + // Account for the same race condition, but with a shrinking slot_count + slots.truncate(slot_count as usize); + Ok(slots.into_iter().map(Slot::new).collect()) } // See public docs on stub in parent mod.rs From 10928a1b47042cc8833f0434a4912bd8b1fe413c Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Sat, 6 Nov 2021 12:26:11 -0400 Subject: [PATCH 08/36] Expose SlotInfo flags individually Flags being accumulated into an integer is an implementation detail that has no significant meaning to its consumers. Instead, expose the information from the flags as booleans. Signed-off-by: Keith Koskie --- cryptoki/src/slot/mod.rs | 16 +++++++++++++--- cryptoki/tests/basic.rs | 6 +++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index d8c586a1..9119734b 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -108,9 +108,19 @@ impl SlotInfo { &self.manufacturer_id } - /// Returns the flags of the slot - pub fn flags(&self) -> SlotFlags { - self.flags + /// Gets value of [`CKF_TOKEN_PRESENT`] + pub fn token_present(&self) -> bool { + self.flags.token_present() + } + + /// Gets value of [`CKF_REMOVABLE_DEVICE`] + pub fn removable_device(&self) -> bool { + self.flags.removable_device() + } + + /// Gets value of [`CKF_HW_SLOT`] + pub fn hardware_slot(&self) -> bool { + self.flags.hardware_slot() } /// Returns the hardware version diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index aa6f4e0f..3fe22f01 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -450,9 +450,9 @@ fn get_info_test() -> Result<()> { fn get_slot_info_test() -> Result<()> { let (pkcs11, slot) = init_pins(); let slot_info = pkcs11.get_slot_info(slot)?; - assert!(slot_info.flags().token_present()); - assert!(!slot_info.flags().hardware_slot()); - assert!(!slot_info.flags().removable_device()); + assert!(slot_info.token_present()); + assert!(!slot_info.hardware_slot()); + assert!(!slot_info.removable_device()); assert_eq!(slot_info.manufacturer_id(), String::from("SoftHSM project")); Ok(()) } From 26925ab589294a2294729cc3c5e2942ab97af7b9 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Sat, 6 Nov 2021 12:36:54 -0400 Subject: [PATCH 09/36] Privatize SlotFlags; remove usused methods Information from SlotFlags is now available through SlotInfo, the only type that uses it. It's also a read-only value. The PCKS#11 provider sets these flags to be read, but setting the flags on the client side at best has no effect, and at worst is misleading about how the slot can be properly interacted with. Signed-off-by: Keith Koskie --- cryptoki/src/slot/flags.rs | 48 +------------------------------------- cryptoki/src/slot/mod.rs | 12 ++++++---- 2 files changed, 8 insertions(+), 52 deletions(-) diff --git a/cryptoki/src/slot/flags.rs b/cryptoki/src/slot/flags.rs index 182c712f..2c648e65 100644 --- a/cryptoki/src/slot/flags.rs +++ b/cryptoki/src/slot/flags.rs @@ -8,7 +8,7 @@ use std::fmt::Formatter; #[derive(Debug, Default, Clone, Copy)] /// Collection of flags defined for [`CK_SLOT_INFO`] -pub struct SlotFlags { +pub(crate) struct SlotFlags { flags: CK_FLAGS, } @@ -41,46 +41,6 @@ impl Flags for SlotFlags { } } -impl SlotFlags { - /// Creates a new instance of `SlotFlags` with no flags set - pub fn new() -> Self { - SlotFlags::default() - } - - /// Gets value of [`CKF_TOKEN_PRESENT`] - pub fn token_present(&self) -> bool { - self.flag(CKF_TOKEN_PRESENT) - } - - /// Sets value of [`CKF_TOKEN_PRESENT`] - pub fn set_token_present(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_TOKEN_PRESENT, b); - self - } - - /// Gets value of [`CKF_REMOVABLE_DEVICE`] - pub fn removable_device(&self) -> bool { - self.flag(CKF_REMOVABLE_DEVICE) - } - - /// Sets value of [`CKF_REMOVABLE_DEVICE`] - pub fn set_removable_device(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_REMOVABLE_DEVICE, b); - self - } - - /// Gets value of [`CKF_HW_SLOT`] - pub fn hardware_slot(&self) -> bool { - self.flag(CKF_HW_SLOT) - } - - /// Sets value of [`CKF_HW_SLOT`] - pub fn set_hardware_slot(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_HW_SLOT, b); - self - } -} - impl std::fmt::Display for SlotFlags { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let flags = vec![CKF_TOKEN_PRESENT, CKF_REMOVABLE_DEVICE, CKF_HW_SLOT]; @@ -88,12 +48,6 @@ impl std::fmt::Display for SlotFlags { } } -impl From for CK_FLAGS { - fn from(flags: SlotFlags) -> Self { - flags.flags - } -} - impl From for SlotFlags { fn from(flags: CK_FLAGS) -> Self { Self { flags } diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 9119734b..784929a2 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -9,8 +9,10 @@ mod flags; use crate::error::{Error, Result}; use crate::string_from_blank_padded; -use crate::types::{Ulong, Version}; -use cryptoki_sys::{CK_SLOT_ID, CK_SLOT_INFO, CK_TOKEN_INFO}; +use crate::types::{Flags, Ulong, Version}; +use cryptoki_sys::{ + CKF_HW_SLOT, CKF_REMOVABLE_DEVICE, CKF_TOKEN_PRESENT, CK_SLOT_ID, CK_SLOT_INFO, CK_TOKEN_INFO, +}; pub use flags::*; use std::convert::{TryFrom, TryInto}; use std::fmt::Formatter; @@ -110,17 +112,17 @@ impl SlotInfo { /// Gets value of [`CKF_TOKEN_PRESENT`] pub fn token_present(&self) -> bool { - self.flags.token_present() + self.flags.flag(CKF_TOKEN_PRESENT) } /// Gets value of [`CKF_REMOVABLE_DEVICE`] pub fn removable_device(&self) -> bool { - self.flags.removable_device() + self.flags.flag(CKF_REMOVABLE_DEVICE) } /// Gets value of [`CKF_HW_SLOT`] pub fn hardware_slot(&self) -> bool { - self.flags.hardware_slot() + self.flags.flag(CKF_HW_SLOT) } /// Returns the hardware version From 6e6a2e90f79964b5cd3860551eb5c5f216d9998a Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Wed, 10 Nov 2021 16:34:43 -0500 Subject: [PATCH 10/36] Reimplement generic flags, then use in SlotInfo This commit is the first of several with the end goal of removing Flags types from the public interface. It makes several design changes. * Type state information encoded into an integer as flags is now exposed publically as booleans only. * The boolean values are read and/or written through the structure that contains the flag integer value. They do not exist as distinct entities, but as features of their outer type. * Flags are now type-bound to their container. Even though the CK_FLAGS type still backs them all, flag sets associated with one structure cannot interact with flags from a different structure. * Operations on the generic flag type are restricted to those required to set, unset, toggle, and test the values as needed by their outer types. These operations are limited to avoid common errors while still having the terseness of binary oprations. * Debug implementation for flags that display the full contents of the flag set as through it were a 'Flags' struct of named booleans. * Display implementation for flags that display only the relevant attributes as a set of string labels. Signed-off-by: Keith Koskie --- cryptoki/src/flag.rs | 319 +++++++++++++++++++++++++++++++++++++ cryptoki/src/lib.rs | 1 + cryptoki/src/slot/flags.rs | 64 +++----- cryptoki/src/slot/mod.rs | 17 +- 4 files changed, 353 insertions(+), 48 deletions(-) create mode 100644 cryptoki/src/flag.rs diff --git a/cryptoki/src/flag.rs b/cryptoki/src/flag.rs new file mode 100644 index 00000000..bb37ac63 --- /dev/null +++ b/cryptoki/src/flag.rs @@ -0,0 +1,319 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +//! Generic abstraction of CK_FLAGS for specialization in other modules +//! +//! Generate a bit flag set bound to a type definition, T. T has no +//! function beyond providing a compile-time bounds check and is only +//! present as phantom data. +//! +//! These definitions also divide the roles of a flag accumulator +//! (`BitFlags`) from a single, constant bit that contributes to it +//! ('FlagBit`) or that bit's inverse (`FlagMask`) +//! +//! The following fundamental operations are defined. +//! +//! * Flags traits: From CK_FLAGS, Deref and DerefMut as CK_FLAGS +//! * !Bit -> Mask (ones compliment) +//! * Flags |= Bit +//! * Flags &= Mask +//! * Flags ^= Bit +//! * Bit | Bit +//! * Mask & Mask +//! * Flags.contains(Bit) +//! +//! Imporantly, Bit types cannot* be assigned to, but can be composed +//! for assignment to the associated accumulator type. Likewise, two +//! accumulators can't be composed with each other. Only the accumulator +//! type can be converted to/from CK_FLAGS for use in FFI operations. +//! +//! *Technically false. The binary operations resolve to a bit type, not +//! an accumulator. If assigned to an itermediate variable, a multi-bit +//! type can be constructed. There is also no enforcement of the assumption +//! that FlagBit only has a single set bit. The lack of assign-op operators +//! is intended to discourage this, though, and the type isn't pub to be +//! abused anyway. +//! +//! The following are intended use patterns. +//! +//! * Set: Flags |= Bit +//! * Set multiple: Flags |= Bit | Bit | ... +//! * Unset: Flags &= !Bit +//! * Unset multiple: Flags &= !(Bit | Bit | ...) +//! * Toggle: Flags ^= Bit +//! * Toggle mutliple: Flags ^= Bit | Bit | ... +//! * Test: Flags.contains(Bit) -> bool +//! * Test (ALL): Flags.contains(Bit | Bit | ...) -> bool +use cryptoki_sys::CK_FLAGS; +use std::marker::PhantomData; +use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXorAssign, Deref, DerefMut, Not}; + +/// A type representing a 1-bit flag constant or some composition thereof +#[repr(transparent)] +pub(crate) struct FlagBit { + value: CK_FLAGS, + _type: PhantomData, +} + +/// A type representing the one's compliment of a FlagBit +#[repr(transparent)] +pub(crate) struct FlagMask { + value: CK_FLAGS, + _type: PhantomData, +} + +/// An accumulator type representing the flag state of a T instance +#[derive(Default)] +#[repr(transparent)] +pub(crate) struct CkFlags { + value: CK_FLAGS, + _type: PhantomData, +} + +// Must hand write Clone and Copy traits because the derived +// versions assume T to be Copy and constrain the impl accordingly. +// In general, we'll have non-Copy Ts and want to avoid this. +impl Copy for FlagBit {} +impl Clone for FlagBit { + fn clone(&self) -> Self { + Self { + value: self.value, + _type: PhantomData, + } + } +} +impl Copy for FlagMask {} +impl Clone for FlagMask { + fn clone(&self) -> Self { + Self { + value: self.value, + _type: PhantomData, + } + } +} +impl Copy for CkFlags {} +impl Clone for CkFlags { + fn clone(&self) -> Self { + Self { + value: self.value, + _type: PhantomData, + } + } +} + +/// Const constructor for defined CKF_* constants +impl FlagBit { + pub(crate) const fn new(value: CK_FLAGS) -> Self { + Self { + value, + _type: PhantomData, + } + } +} + +/// Bit | Bit -> Bit +impl BitOr for FlagBit { + type Output = FlagBit; + fn bitor(self, rhs: Self) -> Self::Output { + Self { + value: self.value | rhs.value, + ..self + } + } +} + +/// !Bit -> Mask +impl Not for FlagBit { + type Output = FlagMask; + fn not(self) -> Self::Output { + FlagMask { + value: !self.value, + _type: PhantomData, + } + } +} + +/// Mask & Mask -> Mask +impl BitAnd for FlagMask { + type Output = FlagMask; + fn bitand(self, rhs: Self) -> Self::Output { + Self { + value: self.value & rhs.value, + ..self + } + } +} + +/// Flags = CK_FLAGS.into() +impl From for CkFlags { + fn from(value: CK_FLAGS) -> Self { + Self { + value, + _type: PhantomData, + } + } +} + +/// CK_FLAGS = *Flags +impl Deref for CkFlags { + type Target = CK_FLAGS; + fn deref(&self) -> &Self::Target { + &self.value + } +} + +/// *Flags = CK_FLAGS +impl DerefMut for CkFlags { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.value + } +} + +/// Flags |= Bit +impl BitOrAssign> for CkFlags { + fn bitor_assign(&mut self, rhs: FlagBit) { + **self |= rhs.value + } +} + +/// Flags &= Mask +impl BitAndAssign> for CkFlags { + fn bitand_assign(&mut self, rhs: FlagMask) { + **self &= rhs.value + } +} + +/// Flags ^= Bit +impl BitXorAssign> for CkFlags { + fn bitxor_assign(&mut self, rhs: FlagBit) { + **self ^= rhs.value + } +} + +impl CkFlags { + pub(crate) fn contains(&self, bits: FlagBit) -> bool { + (self.value & bits.value) == bits.value + } +} + +#[cfg(test)] +mod test { + use super::*; + const ONE: FlagBit<()> = FlagBit::new(1); + const TWO: FlagBit<()> = FlagBit::new(2); + const FOUR: FlagBit<()> = FlagBit::new(4); + fn is_copy(_: T) -> bool { + true + } + + #[test] + fn c_type_is_copy() { + // Probably overkill, but if this is ever false + // the hand implementation of Copy may hide it. + let n: CK_FLAGS = 0; + assert!(is_copy(n)); + } + + #[test] + fn default_to_zero() { + let flags: CkFlags<()> = CkFlags::default(); + assert_eq!(flags.value, 0); + } + + #[test] + fn set_one() { + let mut flags = CkFlags::default(); + flags |= ONE; + assert_eq!(flags.value, 1); + flags |= ONE; + assert_eq!(flags.value, 1); + } + + #[test] + fn set_multi() { + let mut flags = CkFlags::default(); + flags |= ONE | TWO | FOUR; + assert_eq!(flags.value, 7); + flags |= ONE | TWO | FOUR; + assert_eq!(flags.value, 7); + } + + #[test] + fn unset_one() { + let mut flags = CkFlags::from(7); + flags &= !TWO; + assert_eq!(flags.value, 5); + flags &= !TWO; + assert_eq!(flags.value, 5); + } + + #[test] + fn unset_multi() { + let mut flags = CkFlags::from(7); + flags &= !(ONE | FOUR); + assert_eq!(flags.value, 2); + flags &= !(ONE | FOUR); + assert_eq!(flags.value, 2); + } + + #[test] + fn toggle_one() { + let mut flags = CkFlags::from(7); + flags ^= TWO; + assert_eq!(flags.value, 5); + flags ^= TWO; + assert_eq!(flags.value, 7); + } + + #[test] + fn toggle_multi() { + let mut flags = CkFlags::from(7); + flags ^= ONE | FOUR; + assert_eq!(flags.value, 2); + flags ^= ONE | FOUR; + assert_eq!(flags.value, 7); + } + + #[test] + fn check_contains() { + let flags = CkFlags::default(); + assert!(!flags.contains(ONE)); + assert!(!flags.contains(TWO)); + assert!(!flags.contains(ONE | TWO)); + assert!(!flags.contains(FOUR)); + assert!(!flags.contains(ONE | FOUR)); + assert!(!flags.contains(TWO | FOUR)); + assert!(!flags.contains(ONE | TWO | FOUR)); + assert_eq!(flags.value, 0); + + let flags = CkFlags::from(2); + assert!(!flags.contains(ONE)); + assert!(flags.contains(TWO)); + assert!(!flags.contains(ONE | TWO)); + assert!(!flags.contains(FOUR)); + assert!(!flags.contains(ONE | FOUR)); + assert!(!flags.contains(TWO | FOUR)); + assert!(!flags.contains(ONE | TWO | FOUR)); + assert_eq!(flags.value, 2); + + let flags = CkFlags::from(5); + assert!(flags.contains(ONE)); // counterintuitive + assert!(!flags.contains(TWO)); + assert!(!flags.contains(ONE | TWO)); + assert!(flags.contains(FOUR)); // counterintuitive + assert!(flags.contains(ONE | FOUR)); + assert!(!flags.contains(TWO | FOUR)); + assert!(!flags.contains(ONE | TWO | FOUR)); + assert_eq!(flags.value, 5); + + let flags = CkFlags::from(7); + assert!(flags.contains(ONE)); + assert!(flags.contains(TWO)); + assert!(flags.contains(ONE | TWO)); + assert!(flags.contains(FOUR)); + assert!(flags.contains(ONE | FOUR)); + assert!(flags.contains(TWO | FOUR)); + assert!(flags.contains(ONE | TWO | FOUR)); + assert_eq!(flags.value, 7); + } +} diff --git a/cryptoki/src/lib.rs b/cryptoki/src/lib.rs index 77252f14..8769654e 100644 --- a/cryptoki/src/lib.rs +++ b/cryptoki/src/lib.rs @@ -44,6 +44,7 @@ pub mod context; pub mod error; +pub(crate) mod flag; pub mod mechanism; pub mod object; pub mod session; diff --git a/cryptoki/src/slot/flags.rs b/cryptoki/src/slot/flags.rs index 2c648e65..9a6a6ddc 100644 --- a/cryptoki/src/slot/flags.rs +++ b/cryptoki/src/slot/flags.rs @@ -2,55 +2,41 @@ // SPDX-License-Identifier: Apache-2.0 //! PKCS11 General Data Types +use super::SlotInfo; use crate::types::Flags; use cryptoki_sys::*; use std::fmt::Formatter; -#[derive(Debug, Default, Clone, Copy)] -/// Collection of flags defined for [`CK_SLOT_INFO`] -pub(crate) struct SlotFlags { - flags: CK_FLAGS, -} - -impl Flags for SlotFlags { - type FlagType = CK_FLAGS; - - fn flag_value(&self) -> Self::FlagType { - self.flags - } - - fn flag(&self, flag: Self::FlagType) -> bool { - self.flag_value() & flag == flag - } +use crate::flag::*; - fn set_flag(&mut self, flag: Self::FlagType, b: bool) { - if b { - self.flags |= flag; - } else { - self.flags &= !flag; - } - } - - fn stringify_flag(flag: Self::FlagType) -> &'static str { - match flag { - CKF_TOKEN_PRESENT => std::stringify!(CKF_TOKEN_PRESENT), - CKF_REMOVABLE_DEVICE => std::stringify!(CKF_REMOVABLE_DEVICE), - CKF_HW_SLOT => std::stringify!(CKF_HW_SLOT), - _ => "Unknown CK_SLOT_INFO flag", - } - } -} +/// Collection of flags defined for [`CK_SLOT_INFO`] +pub(crate) const TOKEN_PRESENT: FlagBit = FlagBit::new(CKF_TOKEN_PRESENT); +pub(crate) const REMOVABLE_DEVICE: FlagBit = FlagBit::new(CKF_REMOVABLE_DEVICE); +pub(crate) const HW_SLOT: FlagBit = FlagBit::new(CKF_HW_SLOT); -impl std::fmt::Display for SlotFlags { +impl core::fmt::Debug for CkFlags { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let flags = vec![CKF_TOKEN_PRESENT, CKF_REMOVABLE_DEVICE, CKF_HW_SLOT]; - self.stringify_fmt(f, flags) + f.debug_struct("Flags") + .field("token_present", &(self.contains(TOKEN_PRESENT))) + .field("removable_device", &(self.contains(REMOVABLE_DEVICE))) + .field("hw_slot", &(self.contains(HW_SLOT))) + .finish() } } -impl From for SlotFlags { - fn from(flags: CK_FLAGS) -> Self { - Self { flags } +impl core::fmt::Display for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut set = f.debug_set(); + if self.contains(TOKEN_PRESENT) { + let _ = set.entry(&"Token Present"); + } + if self.contains(REMOVABLE_DEVICE) { + let _ = set.entry(&"Removable Device"); + } + if self.contains(HW_SLOT) { + let _ = set.entry(&"Hardware Slot"); + } + set.finish() } } diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 784929a2..edc0eafd 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -8,12 +8,11 @@ mod flags; use crate::error::{Error, Result}; +use crate::flag::CkFlags; use crate::string_from_blank_padded; -use crate::types::{Flags, Ulong, Version}; -use cryptoki_sys::{ - CKF_HW_SLOT, CKF_REMOVABLE_DEVICE, CKF_TOKEN_PRESENT, CK_SLOT_ID, CK_SLOT_INFO, CK_TOKEN_INFO, -}; -pub use flags::*; +use crate::types::{Ulong, Version}; +use cryptoki_sys::{CK_SLOT_ID, CK_SLOT_INFO, CK_TOKEN_INFO}; +use flags::*; use std::convert::{TryFrom, TryInto}; use std::fmt::Formatter; use std::ops::Deref; @@ -94,7 +93,7 @@ impl std::fmt::Display for Slot { pub struct SlotInfo { slot_description: String, manufacturer_id: String, - flags: SlotFlags, + flags: CkFlags, hardware_version: Version, firmware_version: Version, } @@ -112,17 +111,17 @@ impl SlotInfo { /// Gets value of [`CKF_TOKEN_PRESENT`] pub fn token_present(&self) -> bool { - self.flags.flag(CKF_TOKEN_PRESENT) + self.flags.contains(TOKEN_PRESENT) } /// Gets value of [`CKF_REMOVABLE_DEVICE`] pub fn removable_device(&self) -> bool { - self.flags.flag(CKF_REMOVABLE_DEVICE) + self.flags.contains(REMOVABLE_DEVICE) } /// Gets value of [`CKF_HW_SLOT`] pub fn hardware_slot(&self) -> bool { - self.flags.flag(CKF_HW_SLOT) + self.flags.contains(HW_SLOT) } /// Returns the hardware version From 995618779605c146bd37025b6e701bf4177fd30c Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Wed, 10 Nov 2021 17:47:53 -0500 Subject: [PATCH 11/36] Update SlotInfo docs This replaces the prior documentation of SlotInfo members with wording from the standard itself. It also adds a crate-level Conformance Notes to provide context about documentation that would otherise have the appearance of a guarantee issued by this crate. Signed-off-by: Keith Koskie --- cryptoki/src/lib.rs | 10 ++++++++++ cryptoki/src/slot/mod.rs | 29 ++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/cryptoki/src/lib.rs b/cryptoki/src/lib.rs index 8769654e..f4f563d8 100644 --- a/cryptoki/src/lib.rs +++ b/cryptoki/src/lib.rs @@ -7,6 +7,16 @@ //! that is implemented is safe. //! //! The modules under `new` follow the structure of the PKCS11 document version 2.40 available [here](http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html). +//! +//! # Conformance Notes +//! +//! Throughout this crate, many functions and other items include additional +//! "**Conformance**" notes. These notes may provide guarantees about behavior or +//! additional, contextual information. In all cases, such items pertain +//! to information from the PKCS#11 standard and are contingent on the provider +//! being accessed through this crate conforming to that standard. That is, this +//! crate is permitted to *assume* these guarantees, and is does not necessarily +//! check for or enforce them itself. // This list comes from // https://github.com/rust-unofficial/patterns/blob/master/anti_patterns/deny-warnings.md diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index edc0eafd..7ad5deff 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -99,37 +99,52 @@ pub struct SlotInfo { } impl SlotInfo { - /// Returns the slot description + /// String description of the slot + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 64 bytes (*not* chars) as UTF-8 pub fn slot_description(&self) -> &str { &self.slot_description } - /// Returns the manufacturer ID + /// ID of the slot manufacturer + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 pub fn manufacturer_id(&self) -> &str { &self.manufacturer_id } - /// Gets value of [`CKF_TOKEN_PRESENT`] + /// True if a token is in the slot (e.g., a device is in the reader). + /// + /// **[Conformance](crate#conformance-notes):** + /// If this slot does not represent a removable device, a token is *always* + /// considered to be present. That is, `slot.removable device() == false` + /// implies `slot.token_present() == true`. pub fn token_present(&self) -> bool { self.flags.contains(TOKEN_PRESENT) } - /// Gets value of [`CKF_REMOVABLE_DEVICE`] + /// True if the reader supports removable devices. + /// + /// **[Conformance](crate#conformance-notes):** + /// For a given slot, this flag *never* changes pub fn removable_device(&self) -> bool { self.flags.contains(REMOVABLE_DEVICE) } - /// Gets value of [`CKF_HW_SLOT`] + /// True if the slot is a hardware slot, as opposed to a software slot + /// implementing a "soft token" pub fn hardware_slot(&self) -> bool { self.flags.contains(HW_SLOT) } - /// Returns the hardware version + /// Version number of the slot's hardware pub fn hardware_version(&self) -> Version { self.hardware_version } - /// Returns the firmware version + /// Version number of the slot's firmware pub fn firmware_version(&self) -> Version { self.firmware_version } From a573088a2404eaca12c9c15a9b638ce463182b75 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 11 Nov 2021 11:17:18 -0500 Subject: [PATCH 12/36] Refactor TokenInfo Flags Signed-off-by: Keith Koskie --- cryptoki/src/context/slot_token_management.rs | 4 +- cryptoki/src/slot/flags.rs | 428 ++++++------------ cryptoki/src/slot/mod.rs | 380 +++++++++++++--- cryptoki/src/types.rs | 38 ++ 4 files changed, 482 insertions(+), 368 deletions(-) diff --git a/cryptoki/src/context/slot_token_management.rs b/cryptoki/src/context/slot_token_management.rs index 9c0e8cda..23e6c8ba 100644 --- a/cryptoki/src/context/slot_token_management.rs +++ b/cryptoki/src/context/slot_token_management.rs @@ -52,7 +52,7 @@ pub(super) fn get_slots_with_initialized_token(ctx: &Pkcs11) -> Result .into_iter() .filter_map(|slot| match ctx.get_token_info(slot) { Ok(token_info) => { - if token_info.flags().token_initialized() { + if token_info.token_initialized() { Some(Ok(slot)) } else { None @@ -102,7 +102,7 @@ pub(super) fn get_token_info(ctx: &Pkcs11, slot: Slot) -> Result { &mut token_info, )) .into_result()?; - Ok(TokenInfo::new(token_info)) + Ok(TokenInfo::from(token_info)) } } diff --git a/cryptoki/src/slot/flags.rs b/cryptoki/src/slot/flags.rs index 9a6a6ddc..873d112b 100644 --- a/cryptoki/src/slot/flags.rs +++ b/cryptoki/src/slot/flags.rs @@ -2,10 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 //! PKCS11 General Data Types -use super::SlotInfo; -use crate::types::Flags; +use super::{SlotInfo, TokenInfo}; use cryptoki_sys::*; -use std::fmt::Formatter; +use std::fmt::{self, Debug, Display, Formatter}; use crate::flag::*; @@ -14,8 +13,8 @@ pub(crate) const TOKEN_PRESENT: FlagBit = FlagBit::new(CKF_TOKEN_PRESE pub(crate) const REMOVABLE_DEVICE: FlagBit = FlagBit::new(CKF_REMOVABLE_DEVICE); pub(crate) const HW_SLOT: FlagBit = FlagBit::new(CKF_HW_SLOT); -impl core::fmt::Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +impl Debug for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Flags") .field("token_present", &(self.contains(TOKEN_PRESENT))) .field("removable_device", &(self.contains(REMOVABLE_DEVICE))) @@ -24,8 +23,8 @@ impl core::fmt::Debug for CkFlags { } } -impl core::fmt::Display for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +impl Display for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut set = f.debug_set(); if self.contains(TOKEN_PRESENT) { let _ = set.entry(&"Token Present"); @@ -40,295 +39,138 @@ impl core::fmt::Display for CkFlags { } } -#[derive(Debug, Default, Clone, Copy)] -/// Collection of flags defined for [`CK_TOKEN_INFO`] -pub struct TokenFlags { - flags: CK_FLAGS, -} - -impl Flags for TokenFlags { - type FlagType = CK_FLAGS; - - fn flag_value(&self) -> Self::FlagType { - self.flags - } - - fn flag(&self, flag: Self::FlagType) -> bool { - self.flag_value() & flag == flag - } - - fn set_flag(&mut self, flag: Self::FlagType, b: bool) { - if b { - self.flags |= flag; - } else { - self.flags &= !flag; - } - } - - fn stringify_flag(flag: Self::FlagType) -> &'static str { - match flag { - CKF_RNG => std::stringify!(CKF_RNG), - CKF_WRITE_PROTECTED => std::stringify!(CKF_WRITE_PROTECTED), - CKF_LOGIN_REQUIRED => std::stringify!(CKF_LOGIN_REQUIRED), - CKF_USER_PIN_INITIALIZED => std::stringify!(CKF_USER_PIN_INITIALIZED), - CKF_RESTORE_KEY_NOT_NEEDED => std::stringify!(CKF_RESTORE_KEY_NOT_NEEDED), - CKF_CLOCK_ON_TOKEN => std::stringify!(CKF_CLOCK_ON_TOKEN), - CKF_PROTECTED_AUTHENTICATION_PATH => std::stringify!(CKF_PROTECTED_AUTHENTICATION_PATH), - CKF_DUAL_CRYPTO_OPERATIONS => std::stringify!(CKF_DUAL_CRYPTO_OPERATIONS), - CKF_TOKEN_INITIALIZED => std::stringify!(CKF_TOKEN_INITIALIZED), - CKF_SECONDARY_AUTHENTICATION => std::stringify!(CKF_SECONDARY_AUTHENTICATION), - CKF_USER_PIN_COUNT_LOW => std::stringify!(CKF_USER_PIN_COUNT_LOW), - CKF_USER_PIN_FINAL_TRY => std::stringify!(CKF_USER_PIN_FINAL_TRY), - CKF_USER_PIN_LOCKED => std::stringify!(CKF_USER_PIN_LOCKED), - CKF_USER_PIN_TO_BE_CHANGED => std::stringify!(CKF_USER_PIN_TO_BE_CHANGED), - CKF_SO_PIN_COUNT_LOW => std::stringify!(CKF_SO_PIN_COUNT_LOW), - CKF_SO_PIN_FINAL_TRY => std::stringify!(CKF_SO_PIN_FINAL_TRY), - CKF_SO_PIN_LOCKED => std::stringify!(CKF_SO_PIN_LOCKED), - CKF_SO_PIN_TO_BE_CHANGED => std::stringify!(CKF_SO_PIN_TO_BE_CHANGED), - _ => "Unknown CK_TOKEN_INFO flag", - } - } -} - -impl TokenFlags { - /// Creates a new instance of `TokenFlags` with no flags set - pub fn new() -> Self { - TokenFlags::default() - } - - /// Gets value of [`CKF_RNG`] - pub fn random_number_generator(&self) -> bool { - self.flag(CKF_RNG) - } - - /// Sets value of [`CKF_RNG`] - pub fn set_random_number_generator(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_RNG, b); - self - } - - /// Gets value of [`CKF_WRITE_PROTECTED`] - pub fn write_protected(&self) -> bool { - self.flag(CKF_WRITE_PROTECTED) - } - - /// Sets value of [`CKF_WRITE_PROTECTED`] - pub fn set_write_protected(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_WRITE_PROTECTED, b); - self - } - - /// Gets value of [`CKF_LOGIN_REQUIRED`] - pub fn login_required(&self) -> bool { - self.flag(CKF_LOGIN_REQUIRED) - } - - /// Sets value of [`CKF_LOGIN_REQUIRED`] - pub fn set_login_required(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_LOGIN_REQUIRED, b); - self - } - - /// Gets value of [`CKF_USER_PIN_INITIALIZED`] - pub fn user_pin_initialized(&self) -> bool { - self.flag(CKF_USER_PIN_INITIALIZED) - } - - /// Sets value of [`CKF_USER_PIN_INITIALIZED`] - pub fn set_user_pin_initialized(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_USER_PIN_INITIALIZED, b); - self - } - - /// Gets value of [`CKF_RESTORE_KEY_NOT_NEEDED`] - pub fn restore_key_not_needed(&self) -> bool { - self.flag(CKF_RESTORE_KEY_NOT_NEEDED) - } - - /// Sets value of [`CKF_RESTORE_KEY_NOT_NEEDED`] - pub fn set_restore_key_not_needed(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_RESTORE_KEY_NOT_NEEDED, b); - self - } - - /// Gets value of [`CKF_CLOCK_ON_TOKEN`] - pub fn clock_on_token(&self) -> bool { - self.flag(CKF_CLOCK_ON_TOKEN) - } - - /// Sets value of [`CKF_CLOCK_ON_TOKEN`] - pub fn set_clock_on_token(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_CLOCK_ON_TOKEN, b); - self - } - - /// Gets value of [`CKF_PROTECTED_AUTHENTICATION_PATH`] - pub fn protected_authentication_path(&self) -> bool { - self.flag(CKF_PROTECTED_AUTHENTICATION_PATH) - } - - /// Sets value of [`CKF_PROTECTED_AUTHENTICATION_PATH`] - pub fn set_protected_authentication_path(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_PROTECTED_AUTHENTICATION_PATH, b); - self - } - - /// Gets value of [`CKF_DUAL_CRYPTO_OPERATIONS`] - pub fn dual_crypto_operations(&self) -> bool { - self.flag(CKF_DUAL_CRYPTO_OPERATIONS) - } - - /// Sets value of [`CKF_DUAL_CRYPTO_OPERATIONS`] - pub fn set_dual_crypto_operations(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_DUAL_CRYPTO_OPERATIONS, b); - self - } - - /// Gets value of [`CKF_TOKEN_INITIALIZED`] - pub fn token_initialized(&self) -> bool { - self.flag(CKF_TOKEN_INITIALIZED) - } - - /// Sets value of [`CKF_TOKEN_INITIALIZED`] - pub fn set_token_initialized(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_TOKEN_INITIALIZED, b); - self - } - - /// Gets value of [`CKF_SECONDARY_AUTHENTICATION`] - pub fn secondary_authentication(&self) -> bool { - self.flag(CKF_SECONDARY_AUTHENTICATION) - } - - /// Sets value of [`CKF_SECONDARY_AUTHENTICATION`] - pub fn set_secondary_authentication(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SECONDARY_AUTHENTICATION, b); - self - } - - /// Gets value of [`CKF_USER_PIN_COUNT_LOW`] - pub fn user_pin_count_low(&self) -> bool { - self.flag(CKF_USER_PIN_COUNT_LOW) - } - - /// Sets value of [`CKF_USER_PIN_COUNT_LOW`] - pub fn set_user_pin_count_low(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_USER_PIN_COUNT_LOW, b); - self - } - - /// Gets value of [`CKF_USER_PIN_FINAL_TRY`] - pub fn user_pin_final_try(&self) -> bool { - self.flag(CKF_USER_PIN_FINAL_TRY) - } - - /// Sets value of [`CKF_USER_PIN_FINAL_TRY`] - pub fn set_user_pin_final_try(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_USER_PIN_FINAL_TRY, b); - self - } - - /// Gets value of [`CKF_USER_PIN_LOCKED`] - pub fn user_pin_locked(&self) -> bool { - self.flag(CKF_USER_PIN_LOCKED) - } - - /// Sets value of [`CKF_USER_PIN_LOCKED`] - pub fn set_user_pin_locked(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_USER_PIN_LOCKED, b); - self - } - - /// Gets value of [`CKF_USER_PIN_TO_BE_CHANGED`] - pub fn user_pin_to_be_changed(&self) -> bool { - self.flag(CKF_USER_PIN_TO_BE_CHANGED) - } - - /// Sets value of [`CKF_USER_PIN_TO_BE_CHANGED`] - pub fn set_user_pin_to_be_changed(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_USER_PIN_TO_BE_CHANGED, b); - self - } - - /// Gets value of [`CKF_SO_PIN_COUNT_LOW`] - pub fn so_pin_count_low(&self) -> bool { - self.flag(CKF_SO_PIN_COUNT_LOW) - } - - /// Sets value of [`CKF_SO_PIN_COUNT_LOW`] - pub fn set_so_pin_count_low(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SO_PIN_COUNT_LOW, b); - self - } - - /// Gets value of [`CKF_SO_PIN_FINAL_TRY`] - pub fn so_pin_final_try(&self) -> bool { - self.flag(CKF_SO_PIN_FINAL_TRY) - } - - /// Sets value of [`CKF_SO_PIN_FINAL_TRY`] - pub fn set_so_pin_final_try(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SO_PIN_FINAL_TRY, b); - self - } - - /// Gets value of [`CKF_SO_PIN_LOCKED`] - pub fn so_pin_locked(&self) -> bool { - self.flag(CKF_SO_PIN_LOCKED) - } - - /// Sets value of [`CKF_SO_PIN_LOCKED`] - pub fn set_so_pin_locked(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SO_PIN_LOCKED, b); - self - } - - /// Gets value of [`CKF_SO_PIN_TO_BE_CHANGED`] - pub fn so_pin_to_be_changed(&self) -> bool { - self.flag(CKF_SO_PIN_TO_BE_CHANGED) - } - - /// Sets value of [`CKF_SO_PIN_TO_BE_CHANGED`] - pub fn set_so_pin_to_be_changed(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SO_PIN_TO_BE_CHANGED, b); - self - } -} - -impl std::fmt::Display for TokenFlags { +pub(crate) const RNG: FlagBit = FlagBit::new(CKF_RNG); +pub(crate) const WRITE_PROTECTED: FlagBit = FlagBit::new(CKF_WRITE_PROTECTED); +pub(crate) const LOGIN_REQUIRED: FlagBit = FlagBit::new(CKF_LOGIN_REQUIRED); +pub(crate) const USER_PIN_INITIALIZED: FlagBit = FlagBit::new(CKF_USER_PIN_INITIALIZED); +pub(crate) const RESTORE_KEY_NOT_NEEDED: FlagBit = + FlagBit::new(CKF_RESTORE_KEY_NOT_NEEDED); +pub(crate) const CLOCK_ON_TOKEN: FlagBit = FlagBit::new(CKF_CLOCK_ON_TOKEN); +pub(crate) const PROTECTED_AUTHENTICATION_PATH: FlagBit = + FlagBit::new(CKF_PROTECTED_AUTHENTICATION_PATH); +pub(crate) const DUAL_CRYPTO_OPERATIONS: FlagBit = + FlagBit::new(CKF_DUAL_CRYPTO_OPERATIONS); +pub(crate) const TOKEN_INITIALIZED: FlagBit = FlagBit::new(CKF_TOKEN_INITIALIZED); +pub(crate) const SECONDARY_AUTHENTICATION: FlagBit = + FlagBit::new(CKF_SECONDARY_AUTHENTICATION); +pub(crate) const USER_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_USER_PIN_COUNT_LOW); +pub(crate) const USER_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_USER_PIN_FINAL_TRY); +pub(crate) const USER_PIN_LOCKED: FlagBit = FlagBit::new(CKF_USER_PIN_LOCKED); +pub(crate) const USER_PIN_TO_BE_CHANGED: FlagBit = + FlagBit::new(CKF_USER_PIN_TO_BE_CHANGED); +pub(crate) const SO_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_SO_PIN_COUNT_LOW); +pub(crate) const SO_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_SO_PIN_FINAL_TRY); +pub(crate) const SO_PIN_LOCKED: FlagBit = FlagBit::new(CKF_SO_PIN_LOCKED); +pub(crate) const SO_PIN_TO_BE_CHANGED: FlagBit = FlagBit::new(CKF_SO_PIN_TO_BE_CHANGED); +pub(crate) const ERROR_STATE: FlagBit = FlagBit::new(CKF_ERROR_STATE); + +impl Debug for CkFlags { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let flags = vec![ - CKF_RNG, - CKF_WRITE_PROTECTED, - CKF_LOGIN_REQUIRED, - CKF_USER_PIN_INITIALIZED, - CKF_RESTORE_KEY_NOT_NEEDED, - CKF_CLOCK_ON_TOKEN, - CKF_PROTECTED_AUTHENTICATION_PATH, - CKF_DUAL_CRYPTO_OPERATIONS, - CKF_TOKEN_INITIALIZED, - CKF_SECONDARY_AUTHENTICATION, - CKF_USER_PIN_COUNT_LOW, - CKF_USER_PIN_FINAL_TRY, - CKF_USER_PIN_LOCKED, - CKF_USER_PIN_TO_BE_CHANGED, - CKF_SO_PIN_COUNT_LOW, - CKF_SO_PIN_FINAL_TRY, - CKF_SO_PIN_LOCKED, - CKF_SO_PIN_TO_BE_CHANGED, - ]; - self.stringify_fmt(f, flags) - } -} - -impl From for CK_FLAGS { - fn from(flags: TokenFlags) -> Self { - flags.flags + f.debug_struct("Flags") + .field("rng", &(self.contains(RNG))) + .field("write_protected", &(self.contains(WRITE_PROTECTED))) + .field("login_required", &(self.contains(LOGIN_REQUIRED))) + .field( + "user_pin_initialized", + &(self.contains(USER_PIN_INITIALIZED)), + ) + .field( + "restore_key_not_needed", + &(self.contains(RESTORE_KEY_NOT_NEEDED)), + ) + .field("clock_on token", &(self.contains(CLOCK_ON_TOKEN))) + .field( + "protected_authentication_path", + &(self.contains(PROTECTED_AUTHENTICATION_PATH)), + ) + .field( + "dual_crypto_operations", + &(self.contains(DUAL_CRYPTO_OPERATIONS)), + ) + .field("token_initialized", &(self.contains(TOKEN_INITIALIZED))) + .field( + "secondary_authentication", + &(self.contains(SECONDARY_AUTHENTICATION)), + ) + .field("user_pin_count_low", &(self.contains(USER_PIN_COUNT_LOW))) + .field("user_pin_final_try", &(self.contains(USER_PIN_FINAL_TRY))) + .field("user_pin_locked", &(self.contains(USER_PIN_LOCKED))) + .field( + "user_pin_to_be_changed", + &(self.contains(USER_PIN_TO_BE_CHANGED)), + ) + .field("so_pin_count_low", &(self.contains(SO_PIN_COUNT_LOW))) + .field("so_pin_final_try", &(self.contains(SO_PIN_FINAL_TRY))) + .field("so_pin_locked", &(self.contains(SO_PIN_LOCKED))) + .field( + "so_pin_to_be_changed", + &(self.contains(SO_PIN_TO_BE_CHANGED)), + ) + .field("error_state", &(self.contains(ERROR_STATE))) + .finish() } } -impl From for TokenFlags { - fn from(flags: CK_FLAGS) -> Self { - Self { flags } +impl Display for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut set = f.debug_set(); + if self.contains(RNG) { + let _ = set.entry(&"Random Number Generator"); + } + if self.contains(WRITE_PROTECTED) { + let _ = set.entry(&"Write-Protected"); + } + if self.contains(LOGIN_REQUIRED) { + let _ = set.entry(&"Login Required"); + } + if self.contains(USER_PIN_INITIALIZED) { + let _ = set.entry(&"User Pin Initialized"); + } + if self.contains(RESTORE_KEY_NOT_NEEDED) { + let _ = set.entry(&"Restore Key Not Needed"); + } + if self.contains(CLOCK_ON_TOKEN) { + let _ = set.entry(&"Clock on Token"); + } + if self.contains(PROTECTED_AUTHENTICATION_PATH) { + let _ = set.entry(&"Protected Authentication Path"); + } + if self.contains(DUAL_CRYPTO_OPERATIONS) { + let _ = set.entry(&"Dual Crypto Operations"); + } + if self.contains(TOKEN_INITIALIZED) { + let _ = set.entry(&"Token Initialized"); + } + if self.contains(SECONDARY_AUTHENTICATION) { + let _ = set.entry(&"Secondary Authentication"); + } + if self.contains(USER_PIN_COUNT_LOW) { + let _ = set.entry(&"User PIN Count Low"); + } + if self.contains(USER_PIN_FINAL_TRY) { + let _ = set.entry(&"User PIN Final Try"); + } + if self.contains(USER_PIN_LOCKED) { + let _ = set.entry(&"User PIN Locked"); + } + if self.contains(USER_PIN_TO_BE_CHANGED) { + let _ = set.entry(&"User PIN to be Changed"); + } + if self.contains(SO_PIN_COUNT_LOW) { + let _ = set.entry(&"Security Officer PIN Count Low"); + } + if self.contains(SO_PIN_FINAL_TRY) { + let _ = set.entry(&"Security Officer PIN Final Try"); + } + if self.contains(SO_PIN_LOCKED) { + let _ = set.entry(&"Security Officer PIN Locked"); + } + if self.contains(SO_PIN_TO_BE_CHANGED) { + let _ = set.entry(&"Security Officer PIN to be Changed"); + } + if self.contains(ERROR_STATE) { + let _ = set.entry(&"Error State"); + } + set.finish() } } diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 7ad5deff..8fd8f8ba 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -10,12 +10,11 @@ mod flags; use crate::error::{Error, Result}; use crate::flag::CkFlags; use crate::string_from_blank_padded; -use crate::types::{Ulong, Version}; +use crate::types::{maybe_unlimited, MaybeUnavailable, Version}; use cryptoki_sys::{CK_SLOT_ID, CK_SLOT_INFO, CK_TOKEN_INFO}; use flags::*; use std::convert::{TryFrom, TryInto}; use std::fmt::Formatter; -use std::ops::Deref; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] /// Type identifying a slot @@ -163,121 +162,356 @@ impl From for SlotInfo { } /// Contains information about a token -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone)] pub struct TokenInfo { - val: CK_TOKEN_INFO, + label: String, // 32 + manufacturer_id: String, // 32 + model: String, // 16 + serial_number: String, // 16 + flags: CkFlags, + max_session_count: Option>, + session_count: Option, + max_rw_session_count: Option>, + rw_session_count: Option, + max_pin_len: usize, + min_pin_len: usize, + total_public_memory: Option, + free_public_memory: Option, + total_private_memory: Option, + free_private_memory: Option, + hardware_version: Version, + firmware_version: Version, + utc_time: String, +} +impl From for TokenInfo { + fn from(val: CK_TOKEN_INFO) -> Self { + Self { + label: string_from_blank_padded(&val.label), + manufacturer_id: string_from_blank_padded(&val.manufacturerID), + model: string_from_blank_padded(&val.model), + serial_number: string_from_blank_padded(&val.serialNumber), + flags: CkFlags::from(val.flags), + max_session_count: maybe_unlimited(val.ulMaxSessionCount), + session_count: u64::maybe_unavailable(val.ulSessionCount), + max_rw_session_count: maybe_unlimited(val.ulMaxRwSessionCount), + rw_session_count: u64::maybe_unavailable(val.ulRwSessionCount), + max_pin_len: val.ulMaxPinLen as usize, + min_pin_len: val.ulMinPinLen as usize, + total_public_memory: usize::maybe_unavailable(val.ulTotalPublicMemory), + free_public_memory: usize::maybe_unavailable(val.ulFreePublicMemory), + total_private_memory: usize::maybe_unavailable(val.ulTotalPrivateMemory), + free_private_memory: usize::maybe_unavailable(val.ulFreePrivateMemory), + hardware_version: val.hardwareVersion.into(), + firmware_version: val.firmwareVersion.into(), + // UTC time is not blank padded as it has the format YYYYMMDDhhmmssxx where + // x is the '0' character + utc_time: String::from_utf8_lossy(&val.utcTime).trim_end().to_string(), + } + } } impl TokenInfo { - pub(crate) fn new(val: CK_TOKEN_INFO) -> Self { - Self { val } + /// An application-defined label, assigned during token initialization + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 + pub fn label(&self) -> &str { + &self.label } - /// Returns the firmware version - pub fn firmware_version(&self) -> Version { - self.val.firmwareVersion.into() + /// The ID of the device manufacturer + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 + pub fn manufacturer_id(&self) -> &str { + &self.manufacturer_id } - /// Returns the free private memory - pub fn free_private_memory(&self) -> Ulong { - self.val.ulFreePrivateMemory.into() + /// The model of the device + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 16 bytes (*not* chars) as UTF-8 + pub fn model(&self) -> &str { + &self.model } - /// Returns the free public memory - pub fn free_public_memory(&self) -> Ulong { - self.val.ulFreePublicMemory.into() + /// The character-string serial number of the device + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 16 bytes (*not* chars) as UTF-8 + pub fn serial_number(&self) -> &str { + &self.serial_number } - /// Returns the hardware version - pub fn hardware_version(&self) -> Version { - self.val.hardwareVersion.into() + /// True if the token has its own random number generator + pub fn rng(&self) -> bool { + self.flags.contains(RNG) } - /// Returns the label of the token - pub fn label(&self) -> String { - string_from_blank_padded(&self.val.label) + /// True if the token is write-protected + /// + /// **[Conformance](crate#conformance-notes):** + /// Exaclty what this value means is determined by the application. An + /// application may be unable to perform certain actions on a write- + /// protected token. These actions can include any of the following (non- + /// exhaustive): + /// * Creating/modifying/deleting any object on the token + /// * Creating/modifying/deleting a token object on the token + /// * Changing the Security Officer's PIN + /// * Changing the normal user's PIN + /// + /// The token may change its write-protected status depending on the + /// session state to implement its object management policy. For instance, + /// the token may report write-protection unless the session state is R/W + /// SO or R/W User to implement a policy that does not allow any objects, + /// public or private, to be created, modified, or deleted unless the user + /// has successfully called [`Session::login`](crate::session::Session::login). + pub fn write_protected(&self) -> bool { + self.flags.contains(WRITE_PROTECTED) } - /// Returns the ID of the device manufacturer - pub fn manufacturer_id(&self) -> String { - string_from_blank_padded(&self.val.manufacturerID) + /// True if there are some cryptographic functions that a user *must* be + /// logged in to perform + pub fn login_required(&self) -> bool { + self.flags.contains(LOGIN_REQUIRED) } - /// Returns the max PIN length - pub fn max_pin_length(&self) -> Ulong { - self.val.ulMaxPinLen.into() + /// True of the normal user's PIN has been initialized + pub fn user_pin_initialized(&self) -> bool { + self.flags.contains(USER_PIN_INITIALIZED) } - /// Returns the max session count - pub fn max_session_count(&self) -> Ulong { - self.val.ulMaxSessionCount.into() + /// True if a successful save of a session's cryptographic operations state + /// *always* contains all keys needed to restore the state of the session. + pub fn restore_key_not_needed(&self) -> bool { + self.flags.contains(RESTORE_KEY_NOT_NEEDED) } - /// Returns the max r/w session count - pub fn max_rw_session_count(&self) -> Ulong { - self.val.ulMaxRwSessionCount.into() + /// True if the token has its own hardware clock + pub fn clock_on_token(&self) -> bool { + self.flags.contains(CLOCK_ON_TOKEN) } - /// Returns the min PIN length - pub fn min_pin_length(&self) -> Ulong { - self.val.ulMinPinLen.into() + /// True if the token has a "protected authentication path" whereby a user + /// can log into the token without passing a PIN + pub fn protected_authentication_path(&self) -> bool { + self.flags.contains(PROTECTED_AUTHENTICATION_PATH) } - /// Returns the model of the token - pub fn model(&self) -> String { - string_from_blank_padded(&self.val.model) + /// True if a single session with the token can perform dual cryptographic + /// operations + // TODO: Requires Session callbacks:to access + // * digest_encrypt_update + // * decrypt_digest_update + // * sign_encrypt_update + // * decrypt_verify_update + pub fn dual_crypto_operations(&self) -> bool { + self.flags.contains(DUAL_CRYPTO_OPERATIONS) } - /// Returns the r/w session count - pub fn rw_session_count(&self) -> Ulong { - self.val.ulRwSessionCount.into() + /// True if the token has been initialized with + /// [`Pkcs11::init_token](crate::context::Pkcs11::init_token) or an + /// equivalent mechanism outside the scope of the PKCS#11 standard + /// + /// **[Conformance](crate#conformance-notes):** + /// Calling [`Pkcs11::init_token`](crate::context::Pkcs11::init_token) when + /// this flag is set will cause the token to be reinitialized. + pub fn token_initialized(&self) -> bool { + self.flags.contains(TOKEN_INITIALIZED) } - /// Returns the character-string serial number of the device - pub fn serial_number(&self) -> String { - string_from_blank_padded(&self.val.serialNumber) + /// True if the token supports secondary authentication for private key + /// objects + /// **[Conformance](crate#conformance-notes):** + /// This field is deprecated and new providers *must not* set it. I.e., this function must always return `false`. + pub fn secondary_authentication(&self) -> bool { + self.flags.contains(SECONDARY_AUTHENTICATION) } - /// Returns current session count - pub fn session_count(&self) -> Ulong { - self.val.ulSessionCount.into() + /// True if an incorrect user login PIN has been entered at least once + /// since the last successful authentication + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn user_pin_count_low(&self) -> bool { + self.flags.contains(USER_PIN_COUNT_LOW) } - /// Returns the total private memory - pub fn total_private_memory(&self) -> Ulong { - self.val.ulTotalPrivateMemory.into() + /// True if supplying an incorrect user PIN will cause it to become locked + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn user_pin_final_try(&self) -> bool { + self.flags.contains(USER_PIN_FINAL_TRY) } - /// Returns the total public memory - pub fn total_public_memory(&self) -> Ulong { - self.val.ulTotalPublicMemory.into() + /// True if the user PIN has been locked; user login to the token is not + /// possible + pub fn user_pin_locked(&self) -> bool { + self.flags.contains(USER_PIN_LOCKED) } - /// Returns the UTC Time of the token - pub fn utc_time(&self) -> String { - // UTC time is not blank padded as it has the format YYYYMMDDhhmmssxx where - // x is the '0' character - String::from_utf8_lossy(&self.val.utcTime) - .trim_end() - .to_string() + /// True if the user PIN value is the default value set by the token + /// initialization or manufacturing, or the PIN has been expired by the + /// card + /// + /// **[Conformance](crate#conformance-notes):** + /// This may be always false if the token either does not support the + /// functionality or will not reveal the information because of its + /// security policy. + /// + /// If a PIN is set to the default value or has expired, this function + /// returns `true`. When true, logging in with a PIN will succeed, but only + /// the [`Session::set_pin`][crate::session::Session::set_pin] function can + /// be called. Calling any other function that required the user to be + /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired] + /// to be returned until + /// [`Session::set_pin`][crate::session::Session::set_pin] is called + /// successfully. + pub fn user_pin_to_be_changed(&self) -> bool { + self.flags.contains(USER_PIN_TO_BE_CHANGED) + } + + /// True if an incorrect Security Officer login PIN has been entered at least once since + /// the last successful authentication + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn so_pin_count_low(&self) -> bool { + self.flags.contains(SO_PIN_COUNT_LOW) } - /// Returns the Token's flags - pub fn flags(&self) -> TokenFlags { - self.val.flags.into() + /// True if supplying an incorrect Security Officer PIN will cause it to become locked + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn so_pin_final_try(&self) -> bool { + self.flags.contains(SO_PIN_FINAL_TRY) + } + + /// True if the Security Officer PIN has been locked; Security Officer login to the token is not + /// possible + pub fn so_pin_locked(&self) -> bool { + self.flags.contains(SO_PIN_LOCKED) } -} -impl Deref for TokenInfo { - type Target = CK_TOKEN_INFO; + /// True if the Security Officer PIN value is the default value set by the token + /// initialization or manufacturing, or the PIN has been expired by the card + /// + /// **[Conformance](crate#conformance-notes):** + /// This may be always false if the token either does not support the + /// functionality or will not reveal the information because of its security + /// policy. + /// + /// If a PIN is set to the default value or has expired, this function + /// returns `true`. When true, logging in with a PIN will succeed, but only + /// the [`Session::set_pin`][crate::session::Session::set_pin] function can + /// be called. Calling any other function that required the user to be + /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired] + /// to be returned until + /// [`Session::set_pin`][crate::session::Session::set_pin] is called + /// successfully. + pub fn so_pin_to_be_changed(&self) -> bool { + self.flags.contains(SO_PIN_TO_BE_CHANGED) + } - fn deref(&self) -> &Self::Target { - &self.val + /// True if the token failed a FIPS 140-2 self-test and entered an error state + pub fn error_state(&self) -> bool { + self.flags.contains(ERROR_STATE) + } + + /// The maximum number of sessions that can be opened with the token at one + /// time by a single application + /// If `None`, this information was unavailable. + /// If `Some(None)` there is no maximum, meaning the value is effectively infinite + /// If `Some(Some(n))` the maximum number of sessions is `n` + pub fn max_session_count(&self) -> Option> { + self.max_session_count + } + + /// The number of sessions this application currently has open with the + /// token + pub fn session_count(&self) -> Option { + self.session_count + } + + /// The maximum number of read/write sessions that can be opened with the + /// token at one time by a single application + /// If `None`, this information was unavailable. + /// If `Some(None)` there is no maximum, meaning the value is effectively infinite + /// If `Some(Some(n))` the maximum number of read/write sessions is `n` + pub fn max_rw_session_count(&self) -> Option> { + self.max_rw_session_count + } + + /// The number of read/write sessions this application currently has open + /// with the token + pub fn rw_session_count(&self) -> Option { + self.rw_session_count + } + + /// The maximum length in bytes of the PIN + pub fn max_pin_length(&self) -> usize { + self.max_pin_len } -} -impl From for CK_TOKEN_INFO { - fn from(token_info: TokenInfo) -> Self { - *token_info + /// The minimum length in bytes of the PIN + pub fn min_pin_length(&self) -> usize { + self.min_pin_len + } + + /// The total amount of memory on the token (in bytes) in which public + /// objects may be stored + /// Returns `None` if this information is unavailable + pub fn total_public_memory(&self) -> Option { + self.total_public_memory + } + + /// The amount of free (unused) emmeory on the token (in bytes) for public + /// objects + /// Returns `None` if this information is unavailable + pub fn free_public_memory(&self) -> Option { + self.free_public_memory + } + + /// The total amount of memory on the token (in bytes) in which private + /// objects may be stored + /// Returns `None` if this information is unavailable + pub fn total_private_memory(&self) -> Option { + self.total_private_memory + } + + /// The amount of free (unused) emmeory on the token (in bytes) for private + /// objects + /// Returns `None` if this information is unavailable + pub fn free_private_memory(&self) -> Option { + self.free_private_memory + } + + /// The version number of the hardware + pub fn hardware_version(&self) -> Version { + self.hardware_version + } + + /// The version number of the firmware + pub fn firmware_version(&self) -> Version { + self.firmware_version + } + + /// The current datetime with resolution in seconds + /// + /// Returns None if the token is not equipped with a clock (i.e., + /// `self.clock_on_token() == false`) + pub fn utc_time(&self) -> Option<&str> { + // TODO + Some(&self.utc_time) } } diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index cae0a905..97e8a211 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -166,6 +166,44 @@ impl std::fmt::Display for Ulong { } } +pub(crate) trait MaybeUnavailable: Sized { + fn maybe_unavailable(value: CK_ULONG) -> Option; +} + +impl MaybeUnavailable for usize { + fn maybe_unavailable(value: CK_ULONG) -> Option { + if value == CK_UNAVAILABLE_INFORMATION { + None + } else { + Some(value as usize) + } + } +} + +impl MaybeUnavailable for u64 { + fn maybe_unavailable(value: CK_ULONG) -> Option { + if value == CK_UNAVAILABLE_INFORMATION { + None + } else { + // Must have cast for when ulong is 32 bits + // Must have lint suppression when ulong is 64 bits + #[allow(trivial_numeric_casts)] + Some(value as u64) + } + } +} + +pub(crate) fn maybe_unlimited(value: CK_ULONG) -> Option> { + match value { + CK_UNAVAILABLE_INFORMATION => None, + CK_EFFECTIVELY_INFINITE => Some(None), + // Must have cast for when ulong is 32 bits + // Must have lint suppression when ulong is 64 bits + #[allow(trivial_numeric_casts)] + _ => Some(Some(value as u64)), + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] /// Represents a version pub struct Version { From 54cb98c0b98db6a14cb5c2546d1a8689f6322193 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 11 Nov 2021 11:19:17 -0500 Subject: [PATCH 13/36] Hide conversions from sys crate in public docs Signed-off-by: Keith Koskie --- cryptoki/src/slot/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 8fd8f8ba..751af1cb 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -149,6 +149,7 @@ impl SlotInfo { } } +#[doc(hidden)] impl From for SlotInfo { fn from(val: CK_SLOT_INFO) -> Self { Self { @@ -183,6 +184,8 @@ pub struct TokenInfo { firmware_version: Version, utc_time: String, } + +#[doc(hidden)] impl From for TokenInfo { fn from(val: CK_TOKEN_INFO) -> Self { Self { From bee02b9b0057001f51431dcf2e7c39a3528037fe Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 11 Nov 2021 19:23:57 -0500 Subject: [PATCH 14/36] Refactor MechanismInfo Flags Signed-off-by: Keith Koskie --- cryptoki/src/context/slot_token_management.rs | 2 +- cryptoki/src/mechanism/flags.rs | 398 +++++------------- cryptoki/src/mechanism/mod.rs | 188 ++++++++- 3 files changed, 279 insertions(+), 309 deletions(-) diff --git a/cryptoki/src/context/slot_token_management.rs b/cryptoki/src/context/slot_token_management.rs index 23e6c8ba..a9f9a7b7 100644 --- a/cryptoki/src/context/slot_token_management.rs +++ b/cryptoki/src/context/slot_token_management.rs @@ -155,6 +155,6 @@ pub(super) fn get_mechanism_info( &mut mechanism_info, )) .into_result()?; - Ok(MechanismInfo::new(mechanism_info)) + Ok(MechanismInfo::from(mechanism_info)) } } diff --git a/cryptoki/src/mechanism/flags.rs b/cryptoki/src/mechanism/flags.rs index 507f90ea..369b6891 100644 --- a/cryptoki/src/mechanism/flags.rs +++ b/cryptoki/src/mechanism/flags.rs @@ -2,299 +2,127 @@ // SPDX-License-Identifier: Apache-2.0 //! PKCS11 General Data Types -use crate::types::Flags; use cryptoki_sys::*; use std::fmt::Formatter; -#[derive(Debug, Default, Clone, Copy)] -/// Collection of flags defined for [`CK_MECHANISM_INFO`] -pub struct MechanismFlags { - flags: CK_FLAGS, -} - -impl Flags for MechanismFlags { - type FlagType = CK_FLAGS; - - fn flag_value(&self) -> Self::FlagType { - self.flags - } - - fn flag(&self, flag: Self::FlagType) -> bool { - self.flag_value() & flag == flag +use super::MechanismInfo; +use crate::flag::{CkFlags, FlagBit}; +use std::fmt::{self, Debug, Display}; + +pub(crate) const HW: FlagBit = FlagBit::new(CKF_HW); +pub(crate) const ENCRYPT: FlagBit = FlagBit::new(CKF_ENCRYPT); +pub(crate) const DECRYPT: FlagBit = FlagBit::new(CKF_DECRYPT); +pub(crate) const DIGEST: FlagBit = FlagBit::new(CKF_DIGEST); +pub(crate) const SIGN: FlagBit = FlagBit::new(CKF_SIGN); +pub(crate) const SIGN_RECOVER: FlagBit = FlagBit::new(CKF_SIGN_RECOVER); +pub(crate) const VERIFY: FlagBit = FlagBit::new(CKF_VERIFY); +pub(crate) const VERIFY_RECOVER: FlagBit = FlagBit::new(CKF_VERIFY_RECOVER); +pub(crate) const GENERATE: FlagBit = FlagBit::new(CKF_GENERATE); +pub(crate) const GENERATE_KEY_PAIR: FlagBit = FlagBit::new(CKF_GENERATE_KEY_PAIR); +pub(crate) const WRAP: FlagBit = FlagBit::new(CKF_WRAP); +pub(crate) const UNWRAP: FlagBit = FlagBit::new(CKF_UNWRAP); +pub(crate) const DERIVE: FlagBit = FlagBit::new(CKF_DERIVE); +pub(crate) const EXTENSION: FlagBit = FlagBit::new(CKF_EXTENSION); +pub(crate) const EC_F_P: FlagBit = FlagBit::new(CKF_EC_F_P); +pub(crate) const EC_F_2M: FlagBit = FlagBit::new(CKF_EC_F_2M); +pub(crate) const EC_ECPARAMETERS: FlagBit = FlagBit::new(CKF_EC_ECPARAMETERS); +pub(crate) const EC_NAMEDCURVE: FlagBit = FlagBit::new(CKF_EC_NAMEDCURVE); +pub(crate) const EC_UNCOMPRESS: FlagBit = FlagBit::new(CKF_EC_UNCOMPRESS); +pub(crate) const EC_COMPRESS: FlagBit = FlagBit::new(CKF_EC_COMPRESS); + +impl Debug for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Flags") + .field("hw", &(self.contains(HW))) + .field("encrypt", &(self.contains(ENCRYPT))) + .field("decrypt", &(self.contains(DECRYPT))) + .field("digest", &(self.contains(DIGEST))) + .field("sign", &(self.contains(SIGN))) + .field("sign_recover", &(self.contains(SIGN_RECOVER))) + .field("verify", &(self.contains(VERIFY))) + .field("verify_recover", &(self.contains(VERIFY_RECOVER))) + .field("generate", &(self.contains(GENERATE))) + .field("generate_key_pair", &(self.contains(GENERATE_KEY_PAIR))) + .field("wrap", &(self.contains(WRAP))) + .field("unwrap", &(self.contains(UNWRAP))) + .field("derive", &(self.contains(DERIVE))) + .field("extension", &(self.contains(EXTENSION))) + .field("ec_f_p", &(self.contains(EC_F_P))) + .field("ec_f_2m", &(self.contains(EC_F_2M))) + .field("ec_ecparameters", &(self.contains(EC_ECPARAMETERS))) + .field("ec_namedcurve", &(self.contains(EC_NAMEDCURVE))) + .field("ec_uncompress", &(self.contains(EC_UNCOMPRESS))) + .field("ec_compress", &(self.contains(EC_COMPRESS))) + .finish() } +} - fn set_flag(&mut self, flag: Self::FlagType, b: bool) { - if b { - self.flags |= flag; +impl Display for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut set = f.debug_set(); + if self.contains(HW) { + let _ = set.entry(&"Hardware"); } else { - self.flags &= !flag; + let _ = set.entry(&"Software"); } - } - fn stringify_flag(flag: CK_FLAGS) -> &'static str { - match flag { - CKF_HW => std::stringify!(CKF_HW), - CKF_ENCRYPT => std::stringify!(CKF_ENCRYPT), - CKF_DECRYPT => std::stringify!(CKF_DECRYPT), - CKF_DIGEST => std::stringify!(CKF_DIGEST), - CKF_SIGN => std::stringify!(CKF_SIGN), - CKF_SIGN_RECOVER => std::stringify!(CKF_SIGN_RECOVER), - CKF_VERIFY => std::stringify!(CKF_VERIFY), - CKF_VERIFY_RECOVER => std::stringify!(CKF_VERIFY_RECOVER), - CKF_GENERATE => std::stringify!(CKF_GENERATE), - CKF_GENERATE_KEY_PAIR => std::stringify!(CKF_GENERATE_KEY_PAIR), - CKF_WRAP => std::stringify!(CKF_WRAP), - CKF_UNWRAP => std::stringify!(CKF_UNWRAP), - CKF_DERIVE => std::stringify!(CKF_DERIVE), - CKF_EXTENSION => std::stringify!(CKF_EXTENSION), - CKF_EC_F_P => std::stringify!(CKF_EC_F_P), - CKF_EC_NAMEDCURVE => std::stringify!(CKF_EC_NAMEDCURVE), - CKF_EC_UNCOMPRESS => std::stringify!(CKF_EC_UNCOMPRESS), - CKF_EC_COMPRESS => std::stringify!(CKF_EC_COMPRESS), - _ => "Unknown CK_MECHANISM_INFO flag", + if self.contains(ENCRYPT) { + let _ = set.entry(&"Encrypts"); } - } -} - -impl MechanismFlags { - /// Creates a new instance of `MechanismFlags` with no flags set - pub fn new() -> Self { - MechanismFlags::default() - } - - /// Gets value of [`CKF_HW`] - pub fn hardware(&self) -> bool { - self.flag(CKF_HW) - } - - /// Sets value of [`CKF_HW`] - pub fn set_hardware(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_HW, b); - self - } - - /// Gets value of [`CKF_ENCRYPT`] - pub fn encrypt(&self) -> bool { - self.flag(CKF_ENCRYPT) - } - - /// Sets value of [`CKF_ENCRYPT`] - pub fn set_encrypt(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_ENCRYPT, b); - self - } - - /// Gets value of [`CKF_DECRYPT`] - pub fn decrypt(&self) -> bool { - self.flag(CKF_DECRYPT) - } - - /// Sets value of [`CKF_DECRYPT`] - pub fn set_decrypt(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_DECRYPT, b); - self - } - - /// Gets value of [`CKF_DIGEST`] - pub fn digest(&self) -> bool { - self.flag(CKF_DIGEST) - } - - /// Sets value of [`CKF_DIGEST`] - pub fn set_digest(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_DIGEST, b); - self - } - - /// Gets value of [`CKF_SIGN`] - pub fn sign(&self) -> bool { - self.flag(CKF_SIGN) - } - - /// Sets value of [`CKF_SIGN`] - pub fn set_sign(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SIGN, b); - self - } - - /// Gets value of [`CKF_SIGN_RECOVER`] - pub fn sign_recover(&self) -> bool { - self.flag(CKF_SIGN_RECOVER) - } - - /// Sets value of [`CKF_SIGN_RECOVER`] - pub fn set_sign_recover(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SIGN_RECOVER, b); - self - } - - /// Gets value of [`CKF_VERIFY`] - pub fn verify(&self) -> bool { - self.flag(CKF_VERIFY) - } - - /// Sets value of [`CKF_VERIFY`] - pub fn set_verify(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_VERIFY, b); - self - } - - /// Gets value of [`CKF_VERIFY_RECOVER`] - pub fn verify_recover(&self) -> bool { - self.flag(CKF_VERIFY_RECOVER) - } - - /// Sets value of [`CKF_VERIFY_RECOVER`] - pub fn set_verify_recover(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_VERIFY_RECOVER, b); - self - } - - /// Gets value of [`CKF_GENERATE`] - pub fn generate(&self) -> bool { - self.flag(CKF_GENERATE) - } - - /// Sets value of [`CKF_GENERATE`] - pub fn set_generate(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_GENERATE, b); - self - } - - /// Gets value of [`CKF_GENERATE_KEY_PAIR`] - pub fn generate_key_pair(&self) -> bool { - self.flag(CKF_GENERATE_KEY_PAIR) - } - - /// Sets value of [`CKF_GENERATE_KEY_PAIR`] - pub fn set_generate_key_pair(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_GENERATE_KEY_PAIR, b); - self - } - - /// Gets value of [`CKF_WRAP`] - pub fn wrap(&self) -> bool { - self.flag(CKF_WRAP) - } - - /// Sets value of [`CKF_WRAP`] - pub fn set_wrap(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_WRAP, b); - self - } - - /// Gets value of [`CKF_UNWRAP`] - pub fn unwrap(&self) -> bool { - self.flag(CKF_UNWRAP) - } - - /// Sets value of [`CKF_UNWRAP`] - pub fn set_unwrap(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_UNWRAP, b); - self - } - - /// Gets value of [`CKF_DERIVE`] - pub fn derive(&self) -> bool { - self.flag(CKF_DERIVE) - } - - /// Sets value of [`CKF_DERIVE`] - pub fn set_derive(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_DERIVE, b); - self - } - - /// Gets value of [`CKF_EXTENSION`] - pub fn extension(&self) -> bool { - self.flag(CKF_EXTENSION) - } - - /// Sets value of [`CKF_EXTENSION`] - pub fn set_extension(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_EXTENSION, b); - self - } - - /// Gets value of [`CKF_EC_F_P`] - pub fn ec_f_p(&self) -> bool { - self.flag(CKF_EC_F_P) - } - - /// Sets value of [`CKF_EC_F_P`] - pub fn set_ec_f_p(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_EC_F_P, b); - self - } - - /// Gets value of [`CKF_EC_NAMEDCURVE`] - pub fn ec_namedcurve(&self) -> bool { - self.flag(CKF_EC_NAMEDCURVE) - } - - /// Sets value of [`CKF_EC_NAMEDCURVE`] - pub fn set_ec_namedcurve(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_EC_NAMEDCURVE, b); - self - } - - /// Gets value of [`CKF_EC_UNCOMPRESS`] - pub fn ec_uncompress(&self) -> bool { - self.flag(CKF_EC_UNCOMPRESS) - } - - /// Sets value of [`CKF_EC_UNCOMPRESS`] - pub fn set_ec_uncompress(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_EC_UNCOMPRESS, b); - self - } - - /// Gets value of [`CKF_EC_COMPRESS`] - pub fn ec_compress(&self) -> bool { - self.flag(CKF_EC_COMPRESS) - } - - /// Sets value of [`CKF_EC_COMPRESS`] - pub fn set_ec_compress(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_EC_COMPRESS, b); - self - } -} - -impl std::fmt::Display for MechanismFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let flags = vec![ - CKF_HW, - CKF_ENCRYPT, - CKF_DECRYPT, - CKF_DIGEST, - CKF_SIGN, - CKF_SIGN_RECOVER, - CKF_VERIFY, - CKF_VERIFY_RECOVER, - CKF_GENERATE, - CKF_GENERATE_KEY_PAIR, - CKF_WRAP, - CKF_UNWRAP, - CKF_DERIVE, - CKF_EXTENSION, - CKF_EC_F_P, - CKF_EC_NAMEDCURVE, - CKF_EC_UNCOMPRESS, - CKF_EC_COMPRESS, - ]; - self.stringify_fmt(f, flags) - } -} - -impl From for CK_FLAGS { - fn from(flags: MechanismFlags) -> Self { - flags.flags - } -} - -impl From for MechanismFlags { - fn from(flags: CK_FLAGS) -> Self { - Self { flags } + if self.contains(DECRYPT) { + let _ = set.entry(&"Decrypts"); + } + if self.contains(DIGEST) { + let _ = set.entry(&"Digests"); + } + if self.contains(SIGN) { + let _ = set.entry(&"Signs"); + } + if self.contains(SIGN_RECOVER) { + let _ = set.entry(&"Signs into Data"); + } + if self.contains(VERIFY) { + let _ = set.entry(&"Verifies"); + } + if self.contains(VERIFY_RECOVER) { + let _ = set.entry(&"Verifies from Data"); + } + if self.contains(GENERATE) { + let _ = set.entry(&"Generates Keys"); + } + if self.contains(GENERATE_KEY_PAIR) { + let _ = set.entry(&"Generates Key Pairs"); + } + if self.contains(WRAP) { + let _ = set.entry(&"Wraps Keys"); + } + if self.contains(UNWRAP) { + let _ = set.entry(&"Unwraps Keys"); + } + if self.contains(DERIVE) { + let _ = set.entry(&"Derives Keyes"); + } + if self.contains(EXTENSION) { + let _ = set.entry(&"Flag Extensions"); + } + if self.contains(EC_F_P) { + let _ = set.entry(&"Supports ECs Over F_p"); + } + if self.contains(EC_F_2M) { + let _ = set.entry(&"Supports ECs Over F_2^m"); + } + if self.contains(EC_ECPARAMETERS) { + let _ = set.entry(&"Accepts EC as Parameters"); + } + if self.contains(EC_NAMEDCURVE) { + let _ = set.entry(&"Accepts EC by Name"); + } + if self.contains(EC_UNCOMPRESS) { + let _ = set.entry(&"Accepts Uncompressed EC Points"); + } + if self.contains(EC_COMPRESS) { + let _ = set.entry(&"Accepts Compressed EC Points"); + } + set.finish() } } diff --git a/cryptoki/src/mechanism/mod.rs b/cryptoki/src/mechanism/mod.rs index d7115c0d..8dce8cb8 100644 --- a/cryptoki/src/mechanism/mod.rs +++ b/cryptoki/src/mechanism/mod.rs @@ -5,10 +5,9 @@ pub mod elliptic_curve; mod flags; pub mod rsa; -pub use flags::*; use crate::error::Error; -use crate::types::Ulong; +use crate::flag::CkFlags; use cryptoki_sys::*; use log::error; use std::convert::{TryFrom, TryInto}; @@ -17,6 +16,8 @@ use std::fmt::Formatter; use std::ops::Deref; use std::ptr::null_mut; +use flags::*; + #[derive(Copy, Debug, Clone, PartialEq, Eq)] // transparent so that a vector of MechanismType should have the same layout than a vector of // CK_MECHANISM_TYPE. @@ -708,40 +709,181 @@ impl TryFrom for Mechanism { /// Contains information about a mechanism #[derive(Debug, Clone, Copy, Default)] pub struct MechanismInfo { - val: CK_MECHANISM_INFO, + min_key_size: usize, + max_key_size: usize, + flags: CkFlags, +} + +#[doc(hidden)] +impl From for MechanismInfo { + fn from(val: CK_MECHANISM_INFO) -> Self { + Self { + min_key_size: val.ulMinKeySize as usize, + max_key_size: val.ulMaxKeySize as usize, + flags: CkFlags::from(val.flags), + } + } } impl MechanismInfo { - pub(crate) fn new(val: CK_MECHANISM_INFO) -> Self { - Self { val } + /// The minimum size of the key for the mechanism + /// + /// **[Conformance](crate#conformance-notes):** + /// Whether this is measured in bits or in bytes is mechanism-dependent + pub fn min_key_size(&self) -> usize { + self.min_key_size } - /// Returns the minimum key size for this mechanism. - pub fn min_key_size(&self) -> Ulong { - self.val.ulMinKeySize.into() + /// The maximum size of the key for the mechanism + /// + /// **[Conformance](crate#conformance-notes):** + /// Whether this is measured in bits or in bytes is mechanism-dependent + pub fn max_key_size(&self) -> usize { + self.max_key_size } - /// Returns the maximum key size for this mechanism. - pub fn max_key_size(&self) -> Ulong { - self.val.ulMaxKeySize.into() + /// True if the mechanism is performed by the device; false if the mechanism is performed in software + pub fn hardware(&self) -> bool { + self.flags.contains(HW) } - /// Returns the flags for this mechanism. - pub fn flags(&self) -> MechanismFlags { - self.val.flags.into() + /// True if the mechanism can be used to encrypt data + /// + /// See [`Session::encrypt`](crate::session::Session::encrypt) + pub fn encrypt(&self) -> bool { + self.flags.contains(ENCRYPT) } -} -impl Deref for MechanismInfo { - type Target = CK_MECHANISM_INFO; + /// True if the mechanism can be used to decrypt encrypted data + /// + /// See [`Session::decrypt`](crate::session::Session::decrypt) + pub fn decrypt(&self) -> bool { + self.flags.contains(DECRYPT) + } - fn deref(&self) -> &Self::Target { - &self.val + /// True if the mechanism can be used to digest a message + /// + // TODO See [`Session::digest`](crate::session::Session::digest) + pub fn digest(&self) -> bool { + self.flags.contains(DIGEST) + } + + /// True if the mechanism can be used to digitally sign data + /// + /// See [`Session::sign`](crate::session::Session::sign) + pub fn sign(&self) -> bool { + self.flags.contains(SIGN) + } + + /// True if the mechanism can be used to digitally data which can be recovered from the signature + /// + // TODO See [`Session::sign_recover`](crate::session::Session::sign_recover) + pub fn sign_recover(&self) -> bool { + self.flags.contains(SIGN_RECOVER) + } + + /// True if the mechanism can be used to verify a digital signature + /// + /// See [`Session::verify`](crate::session::Session::verify) + pub fn verify(&self) -> bool { + self.flags.contains(VERIFY) + } + + /// True if the mechanism can be used to [`verify`](crate::session::Session::verify) a digital signature and recover the signed data + /// + // TODO See [`Session::verify_recover`](crate::session::Session::verify_recover) + pub fn verify_recover(&self) -> bool { + self.flags.contains(VERIFY_RECOVER) + } + + /// True if the mechanism can be used to generate a secret key + /// + // TODO See [`Session::generate`](crate::session::Session::generate) + pub fn generate(&self) -> bool { + self.flags.contains(GENERATE) + } + + /// True if the mechanism can be used to generate a public/private key pair + /// + /// See [`Session::generate_key_pair`](crate::session::Session::generate_key_pair)) + pub fn generate_key_pair(&self) -> bool { + self.flags.contains(GENERATE_KEY_PAIR) + } + + /// True if the mechanism can be used to wrap (encrypt) a key + /// + // TODO See [`Session::wrap`](crate::session::Session::wrap)) + pub fn wrap(&self) -> bool { + self.flags.contains(WRAP) + } + + /// True if the mechanism can be used to unwrap (decrypt) a key + /// + // TODO See [`Session::unwrap`](crate::session::Session::unwrap)) + pub fn unwrap(&self) -> bool { + self.flags.contains(UNWRAP) + } + + /// True if the mechanism can be used to derive a key from a base key + /// + // TODO See [`Session::derive`](crate::session::Session::derive)) + pub fn derive(&self) -> bool { + self.flags.contains(DERIVE) + } + + /// True if there is an extension to the flags; false if no extensions + /// + /// **[Conformance](crate#conformance-notes):** + /// This *must* be false for PKCS#11 v2.40 + pub fn extension(&self) -> bool { + self.flags.contains(EXTENSION) + } + + /// True if the mechanism can be used to with elliptic curve domain parameters over ***Fp*** + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` + pub fn ec_f_p(&self) -> bool { + self.flags.contains(EC_F_P) + } + + /// True if the mechanism can be used with elliptic curve domain parameters over ***F2m*** + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` + pub fn ec_f_2m(&self) -> bool { + self.flags.contains(EC_F_2M) + } + + /// True if the mechanism supports specifying elliptic curve domain parameters explicitly + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` + pub fn ec_from_parameters(&self) -> bool { + self.flags.contains(EC_ECPARAMETERS) + } + + /// True if the mechanism supports specifying elliptic curve domain parameters with a named curve + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` + pub fn ec_from_named_curve(&self) -> bool { + self.flags.contains(EC_NAMEDCURVE) + } + + /// True if the mechanism can be used with elliptic curve points in uncompressed form + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` + pub fn ec_uncompressed(&self) -> bool { + self.flags.contains(EC_UNCOMPRESS) } -} -impl From for CK_MECHANISM_INFO { - fn from(mechanism_info: MechanismInfo) -> Self { - *mechanism_info + /// True if the mechanism can be used with elliptic curve points in compressed form + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` + pub fn ec_compressed(&self) -> bool { + self.flags.contains(EC_COMPRESS) } } From 0ae3adb1ccacb4125adad6ece555546538ee2f55 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 11 Nov 2021 20:32:36 -0500 Subject: [PATCH 15/36] Move info types This take the three previously refactored info+flag types and moves them together. Before, the flag types had to reach into their parent module to bring their associated info type into scope, while the info type had to do the same in the other direction. While this is legal, the tighter coupling made it natural to place them together. This has a side effect that all FlagBit constants can be made private within the new files. slot/{mod.rs:SlotInfo,flag.rs:CkFlag} -> slot/slot_info.rs slot/{mod.rs:TokenInfo,flag.rs:CkFlag} -> slot/token_info.rs mechanism/{mod.rs:MechanismInfo,flags.rs} -> mechanism/mechanism_info.rs Signed-off-by: Keith Koskie --- cryptoki/src/mechanism/flags.rs | 128 ------ cryptoki/src/mechanism/mechanism_info.rs | 309 ++++++++++++++ cryptoki/src/mechanism/mod.rs | 187 +-------- cryptoki/src/slot/flags.rs | 176 -------- cryptoki/src/slot/mod.rs | 445 +------------------- cryptoki/src/slot/slot_info.rs | 114 ++++++ cryptoki/src/slot/token_info.rs | 499 +++++++++++++++++++++++ 7 files changed, 931 insertions(+), 927 deletions(-) delete mode 100644 cryptoki/src/mechanism/flags.rs create mode 100644 cryptoki/src/mechanism/mechanism_info.rs delete mode 100644 cryptoki/src/slot/flags.rs create mode 100644 cryptoki/src/slot/slot_info.rs create mode 100644 cryptoki/src/slot/token_info.rs diff --git a/cryptoki/src/mechanism/flags.rs b/cryptoki/src/mechanism/flags.rs deleted file mode 100644 index 369b6891..00000000 --- a/cryptoki/src/mechanism/flags.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2021 Contributors to the Parsec project. -// SPDX-License-Identifier: Apache-2.0 -//! PKCS11 General Data Types - -use cryptoki_sys::*; -use std::fmt::Formatter; - -use super::MechanismInfo; -use crate::flag::{CkFlags, FlagBit}; -use std::fmt::{self, Debug, Display}; - -pub(crate) const HW: FlagBit = FlagBit::new(CKF_HW); -pub(crate) const ENCRYPT: FlagBit = FlagBit::new(CKF_ENCRYPT); -pub(crate) const DECRYPT: FlagBit = FlagBit::new(CKF_DECRYPT); -pub(crate) const DIGEST: FlagBit = FlagBit::new(CKF_DIGEST); -pub(crate) const SIGN: FlagBit = FlagBit::new(CKF_SIGN); -pub(crate) const SIGN_RECOVER: FlagBit = FlagBit::new(CKF_SIGN_RECOVER); -pub(crate) const VERIFY: FlagBit = FlagBit::new(CKF_VERIFY); -pub(crate) const VERIFY_RECOVER: FlagBit = FlagBit::new(CKF_VERIFY_RECOVER); -pub(crate) const GENERATE: FlagBit = FlagBit::new(CKF_GENERATE); -pub(crate) const GENERATE_KEY_PAIR: FlagBit = FlagBit::new(CKF_GENERATE_KEY_PAIR); -pub(crate) const WRAP: FlagBit = FlagBit::new(CKF_WRAP); -pub(crate) const UNWRAP: FlagBit = FlagBit::new(CKF_UNWRAP); -pub(crate) const DERIVE: FlagBit = FlagBit::new(CKF_DERIVE); -pub(crate) const EXTENSION: FlagBit = FlagBit::new(CKF_EXTENSION); -pub(crate) const EC_F_P: FlagBit = FlagBit::new(CKF_EC_F_P); -pub(crate) const EC_F_2M: FlagBit = FlagBit::new(CKF_EC_F_2M); -pub(crate) const EC_ECPARAMETERS: FlagBit = FlagBit::new(CKF_EC_ECPARAMETERS); -pub(crate) const EC_NAMEDCURVE: FlagBit = FlagBit::new(CKF_EC_NAMEDCURVE); -pub(crate) const EC_UNCOMPRESS: FlagBit = FlagBit::new(CKF_EC_UNCOMPRESS); -pub(crate) const EC_COMPRESS: FlagBit = FlagBit::new(CKF_EC_COMPRESS); - -impl Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Flags") - .field("hw", &(self.contains(HW))) - .field("encrypt", &(self.contains(ENCRYPT))) - .field("decrypt", &(self.contains(DECRYPT))) - .field("digest", &(self.contains(DIGEST))) - .field("sign", &(self.contains(SIGN))) - .field("sign_recover", &(self.contains(SIGN_RECOVER))) - .field("verify", &(self.contains(VERIFY))) - .field("verify_recover", &(self.contains(VERIFY_RECOVER))) - .field("generate", &(self.contains(GENERATE))) - .field("generate_key_pair", &(self.contains(GENERATE_KEY_PAIR))) - .field("wrap", &(self.contains(WRAP))) - .field("unwrap", &(self.contains(UNWRAP))) - .field("derive", &(self.contains(DERIVE))) - .field("extension", &(self.contains(EXTENSION))) - .field("ec_f_p", &(self.contains(EC_F_P))) - .field("ec_f_2m", &(self.contains(EC_F_2M))) - .field("ec_ecparameters", &(self.contains(EC_ECPARAMETERS))) - .field("ec_namedcurve", &(self.contains(EC_NAMEDCURVE))) - .field("ec_uncompress", &(self.contains(EC_UNCOMPRESS))) - .field("ec_compress", &(self.contains(EC_COMPRESS))) - .finish() - } -} - -impl Display for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut set = f.debug_set(); - if self.contains(HW) { - let _ = set.entry(&"Hardware"); - } else { - let _ = set.entry(&"Software"); - } - - if self.contains(ENCRYPT) { - let _ = set.entry(&"Encrypts"); - } - if self.contains(DECRYPT) { - let _ = set.entry(&"Decrypts"); - } - if self.contains(DIGEST) { - let _ = set.entry(&"Digests"); - } - if self.contains(SIGN) { - let _ = set.entry(&"Signs"); - } - if self.contains(SIGN_RECOVER) { - let _ = set.entry(&"Signs into Data"); - } - if self.contains(VERIFY) { - let _ = set.entry(&"Verifies"); - } - if self.contains(VERIFY_RECOVER) { - let _ = set.entry(&"Verifies from Data"); - } - if self.contains(GENERATE) { - let _ = set.entry(&"Generates Keys"); - } - if self.contains(GENERATE_KEY_PAIR) { - let _ = set.entry(&"Generates Key Pairs"); - } - if self.contains(WRAP) { - let _ = set.entry(&"Wraps Keys"); - } - if self.contains(UNWRAP) { - let _ = set.entry(&"Unwraps Keys"); - } - if self.contains(DERIVE) { - let _ = set.entry(&"Derives Keyes"); - } - if self.contains(EXTENSION) { - let _ = set.entry(&"Flag Extensions"); - } - if self.contains(EC_F_P) { - let _ = set.entry(&"Supports ECs Over F_p"); - } - if self.contains(EC_F_2M) { - let _ = set.entry(&"Supports ECs Over F_2^m"); - } - if self.contains(EC_ECPARAMETERS) { - let _ = set.entry(&"Accepts EC as Parameters"); - } - if self.contains(EC_NAMEDCURVE) { - let _ = set.entry(&"Accepts EC by Name"); - } - if self.contains(EC_UNCOMPRESS) { - let _ = set.entry(&"Accepts Uncompressed EC Points"); - } - if self.contains(EC_COMPRESS) { - let _ = set.entry(&"Accepts Compressed EC Points"); - } - set.finish() - } -} diff --git a/cryptoki/src/mechanism/mechanism_info.rs b/cryptoki/src/mechanism/mechanism_info.rs new file mode 100644 index 00000000..0aef1446 --- /dev/null +++ b/cryptoki/src/mechanism/mechanism_info.rs @@ -0,0 +1,309 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +//! Mechanism info and associated flags + +use cryptoki_sys::*; +use std::fmt::Formatter; + +use crate::flag::{CkFlags, FlagBit}; +use std::fmt::{self, Debug, Display}; + +const HW: FlagBit = FlagBit::new(CKF_HW); +const ENCRYPT: FlagBit = FlagBit::new(CKF_ENCRYPT); +const DECRYPT: FlagBit = FlagBit::new(CKF_DECRYPT); +const DIGEST: FlagBit = FlagBit::new(CKF_DIGEST); +const SIGN: FlagBit = FlagBit::new(CKF_SIGN); +const SIGN_RECOVER: FlagBit = FlagBit::new(CKF_SIGN_RECOVER); +const VERIFY: FlagBit = FlagBit::new(CKF_VERIFY); +const VERIFY_RECOVER: FlagBit = FlagBit::new(CKF_VERIFY_RECOVER); +const GENERATE: FlagBit = FlagBit::new(CKF_GENERATE); +const GENERATE_KEY_PAIR: FlagBit = FlagBit::new(CKF_GENERATE_KEY_PAIR); +const WRAP: FlagBit = FlagBit::new(CKF_WRAP); +const UNWRAP: FlagBit = FlagBit::new(CKF_UNWRAP); +const DERIVE: FlagBit = FlagBit::new(CKF_DERIVE); +const EXTENSION: FlagBit = FlagBit::new(CKF_EXTENSION); +const EC_F_P: FlagBit = FlagBit::new(CKF_EC_F_P); +const EC_F_2M: FlagBit = FlagBit::new(CKF_EC_F_2M); +const EC_ECPARAMETERS: FlagBit = FlagBit::new(CKF_EC_ECPARAMETERS); +const EC_NAMEDCURVE: FlagBit = FlagBit::new(CKF_EC_NAMEDCURVE); +const EC_UNCOMPRESS: FlagBit = FlagBit::new(CKF_EC_UNCOMPRESS); +const EC_COMPRESS: FlagBit = FlagBit::new(CKF_EC_COMPRESS); + +impl Debug for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Flags") + .field("hw", &(self.contains(HW))) + .field("encrypt", &(self.contains(ENCRYPT))) + .field("decrypt", &(self.contains(DECRYPT))) + .field("digest", &(self.contains(DIGEST))) + .field("sign", &(self.contains(SIGN))) + .field("sign_recover", &(self.contains(SIGN_RECOVER))) + .field("verify", &(self.contains(VERIFY))) + .field("verify_recover", &(self.contains(VERIFY_RECOVER))) + .field("generate", &(self.contains(GENERATE))) + .field("generate_key_pair", &(self.contains(GENERATE_KEY_PAIR))) + .field("wrap", &(self.contains(WRAP))) + .field("unwrap", &(self.contains(UNWRAP))) + .field("derive", &(self.contains(DERIVE))) + .field("extension", &(self.contains(EXTENSION))) + .field("ec_f_p", &(self.contains(EC_F_P))) + .field("ec_f_2m", &(self.contains(EC_F_2M))) + .field("ec_ecparameters", &(self.contains(EC_ECPARAMETERS))) + .field("ec_namedcurve", &(self.contains(EC_NAMEDCURVE))) + .field("ec_uncompress", &(self.contains(EC_UNCOMPRESS))) + .field("ec_compress", &(self.contains(EC_COMPRESS))) + .finish() + } +} + +impl Display for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut set = f.debug_set(); + if self.contains(HW) { + let _ = set.entry(&"Hardware"); + } else { + let _ = set.entry(&"Software"); + } + + if self.contains(ENCRYPT) { + let _ = set.entry(&"Encrypts"); + } + if self.contains(DECRYPT) { + let _ = set.entry(&"Decrypts"); + } + if self.contains(DIGEST) { + let _ = set.entry(&"Digests"); + } + if self.contains(SIGN) { + let _ = set.entry(&"Signs"); + } + if self.contains(SIGN_RECOVER) { + let _ = set.entry(&"Signs into Data"); + } + if self.contains(VERIFY) { + let _ = set.entry(&"Verifies"); + } + if self.contains(VERIFY_RECOVER) { + let _ = set.entry(&"Verifies from Data"); + } + if self.contains(GENERATE) { + let _ = set.entry(&"Generates Keys"); + } + if self.contains(GENERATE_KEY_PAIR) { + let _ = set.entry(&"Generates Key Pairs"); + } + if self.contains(WRAP) { + let _ = set.entry(&"Wraps Keys"); + } + if self.contains(UNWRAP) { + let _ = set.entry(&"Unwraps Keys"); + } + if self.contains(DERIVE) { + let _ = set.entry(&"Derives Keyes"); + } + if self.contains(EXTENSION) { + let _ = set.entry(&"Flag Extensions"); + } + if self.contains(EC_F_P) { + let _ = set.entry(&"Supports ECs Over F_p"); + } + if self.contains(EC_F_2M) { + let _ = set.entry(&"Supports ECs Over F_2^m"); + } + if self.contains(EC_ECPARAMETERS) { + let _ = set.entry(&"Accepts EC as Parameters"); + } + if self.contains(EC_NAMEDCURVE) { + let _ = set.entry(&"Accepts EC by Name"); + } + if self.contains(EC_UNCOMPRESS) { + let _ = set.entry(&"Accepts Uncompressed EC Points"); + } + if self.contains(EC_COMPRESS) { + let _ = set.entry(&"Accepts Compressed EC Points"); + } + set.finish() + } +} + +/// Contains information about a mechanism +#[derive(Debug, Clone, Copy, Default)] +pub struct MechanismInfo { + min_key_size: usize, + max_key_size: usize, + flags: CkFlags, +} + +#[doc(hidden)] +impl From for MechanismInfo { + fn from(val: CK_MECHANISM_INFO) -> Self { + Self { + min_key_size: val.ulMinKeySize as usize, + max_key_size: val.ulMaxKeySize as usize, + flags: CkFlags::from(val.flags), + } + } +} + +impl MechanismInfo { + /// The minimum size of the key for the mechanism + /// + /// **[Conformance](crate#conformance-notes):** + /// Whether this is measured in bits or in bytes is mechanism-dependent + pub fn min_key_size(&self) -> usize { + self.min_key_size + } + + /// The maximum size of the key for the mechanism + /// + /// **[Conformance](crate#conformance-notes):** + /// Whether this is measured in bits or in bytes is mechanism-dependent + pub fn max_key_size(&self) -> usize { + self.max_key_size + } + + /// True if the mechanism is performed by the device; false if the mechanism is performed in software + pub fn hardware(&self) -> bool { + self.flags.contains(HW) + } + + /// True if the mechanism can be used to encrypt data + /// + /// See [`Session::encrypt`](crate::session::Session::encrypt) + pub fn encrypt(&self) -> bool { + self.flags.contains(ENCRYPT) + } + + /// True if the mechanism can be used to decrypt encrypted data + /// + /// See [`Session::decrypt`](crate::session::Session::decrypt) + pub fn decrypt(&self) -> bool { + self.flags.contains(DECRYPT) + } + + /// True if the mechanism can be used to digest a message + /// + // TODO See [`Session::digest`](crate::session::Session::digest) + pub fn digest(&self) -> bool { + self.flags.contains(DIGEST) + } + + /// True if the mechanism can be used to digitally sign data + /// + /// See [`Session::sign`](crate::session::Session::sign) + pub fn sign(&self) -> bool { + self.flags.contains(SIGN) + } + + /// True if the mechanism can be used to digitally data which can be recovered from the signature + /// + // TODO See [`Session::sign_recover`](crate::session::Session::sign_recover) + pub fn sign_recover(&self) -> bool { + self.flags.contains(SIGN_RECOVER) + } + + /// True if the mechanism can be used to verify a digital signature + /// + /// See [`Session::verify`](crate::session::Session::verify) + pub fn verify(&self) -> bool { + self.flags.contains(VERIFY) + } + + /// True if the mechanism can be used to [`verify`](crate::session::Session::verify) a digital signature and recover the signed data + /// + // TODO See [`Session::verify_recover`](crate::session::Session::verify_recover) + pub fn verify_recover(&self) -> bool { + self.flags.contains(VERIFY_RECOVER) + } + + /// True if the mechanism can be used to generate a secret key + /// + // TODO See [`Session::generate`](crate::session::Session::generate) + pub fn generate(&self) -> bool { + self.flags.contains(GENERATE) + } + + /// True if the mechanism can be used to generate a public/private key pair + /// + /// See [`Session::generate_key_pair`](crate::session::Session::generate_key_pair)) + pub fn generate_key_pair(&self) -> bool { + self.flags.contains(GENERATE_KEY_PAIR) + } + + /// True if the mechanism can be used to wrap (encrypt) a key + /// + // TODO See [`Session::wrap`](crate::session::Session::wrap)) + pub fn wrap(&self) -> bool { + self.flags.contains(WRAP) + } + + /// True if the mechanism can be used to unwrap (decrypt) a key + /// + // TODO See [`Session::unwrap`](crate::session::Session::unwrap)) + pub fn unwrap(&self) -> bool { + self.flags.contains(UNWRAP) + } + + /// True if the mechanism can be used to derive a key from a base key + /// + // TODO See [`Session::derive`](crate::session::Session::derive)) + pub fn derive(&self) -> bool { + self.flags.contains(DERIVE) + } + + /// True if there is an extension to the flags; false if no extensions + /// + /// **[Conformance](crate#conformance-notes):** + /// This *must* be false for PKCS#11 v2.40 + pub fn extension(&self) -> bool { + self.flags.contains(EXTENSION) + } + + /// True if the mechanism can be used to with elliptic curve domain parameters over ***Fp*** + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` + pub fn ec_f_p(&self) -> bool { + self.flags.contains(EC_F_P) + } + + /// True if the mechanism can be used with elliptic curve domain parameters over ***F2m*** + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` + pub fn ec_f_2m(&self) -> bool { + self.flags.contains(EC_F_2M) + } + + /// True if the mechanism supports specifying elliptic curve domain parameters explicitly + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` + pub fn ec_from_parameters(&self) -> bool { + self.flags.contains(EC_ECPARAMETERS) + } + + /// True if the mechanism supports specifying elliptic curve domain parameters with a named curve + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` + pub fn ec_from_named_curve(&self) -> bool { + self.flags.contains(EC_NAMEDCURVE) + } + + /// True if the mechanism can be used with elliptic curve points in uncompressed form + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` + pub fn ec_uncompressed(&self) -> bool { + self.flags.contains(EC_UNCOMPRESS) + } + + /// True if the mechanism can be used with elliptic curve points in compressed form + /// + /// **[Conformance](crate#conformance-notes):** + /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` + pub fn ec_compressed(&self) -> bool { + self.flags.contains(EC_COMPRESS) + } +} diff --git a/cryptoki/src/mechanism/mod.rs b/cryptoki/src/mechanism/mod.rs index 8dce8cb8..c5792661 100644 --- a/cryptoki/src/mechanism/mod.rs +++ b/cryptoki/src/mechanism/mod.rs @@ -3,11 +3,10 @@ //! Data types for mechanisms pub mod elliptic_curve; -mod flags; +mod mechanism_info; pub mod rsa; use crate::error::Error; -use crate::flag::CkFlags; use cryptoki_sys::*; use log::error; use std::convert::{TryFrom, TryInto}; @@ -16,7 +15,7 @@ use std::fmt::Formatter; use std::ops::Deref; use std::ptr::null_mut; -use flags::*; +pub use mechanism_info::MechanismInfo; #[derive(Copy, Debug, Clone, PartialEq, Eq)] // transparent so that a vector of MechanismType should have the same layout than a vector of @@ -705,185 +704,3 @@ impl TryFrom for Mechanism { } } } - -/// Contains information about a mechanism -#[derive(Debug, Clone, Copy, Default)] -pub struct MechanismInfo { - min_key_size: usize, - max_key_size: usize, - flags: CkFlags, -} - -#[doc(hidden)] -impl From for MechanismInfo { - fn from(val: CK_MECHANISM_INFO) -> Self { - Self { - min_key_size: val.ulMinKeySize as usize, - max_key_size: val.ulMaxKeySize as usize, - flags: CkFlags::from(val.flags), - } - } -} - -impl MechanismInfo { - /// The minimum size of the key for the mechanism - /// - /// **[Conformance](crate#conformance-notes):** - /// Whether this is measured in bits or in bytes is mechanism-dependent - pub fn min_key_size(&self) -> usize { - self.min_key_size - } - - /// The maximum size of the key for the mechanism - /// - /// **[Conformance](crate#conformance-notes):** - /// Whether this is measured in bits or in bytes is mechanism-dependent - pub fn max_key_size(&self) -> usize { - self.max_key_size - } - - /// True if the mechanism is performed by the device; false if the mechanism is performed in software - pub fn hardware(&self) -> bool { - self.flags.contains(HW) - } - - /// True if the mechanism can be used to encrypt data - /// - /// See [`Session::encrypt`](crate::session::Session::encrypt) - pub fn encrypt(&self) -> bool { - self.flags.contains(ENCRYPT) - } - - /// True if the mechanism can be used to decrypt encrypted data - /// - /// See [`Session::decrypt`](crate::session::Session::decrypt) - pub fn decrypt(&self) -> bool { - self.flags.contains(DECRYPT) - } - - /// True if the mechanism can be used to digest a message - /// - // TODO See [`Session::digest`](crate::session::Session::digest) - pub fn digest(&self) -> bool { - self.flags.contains(DIGEST) - } - - /// True if the mechanism can be used to digitally sign data - /// - /// See [`Session::sign`](crate::session::Session::sign) - pub fn sign(&self) -> bool { - self.flags.contains(SIGN) - } - - /// True if the mechanism can be used to digitally data which can be recovered from the signature - /// - // TODO See [`Session::sign_recover`](crate::session::Session::sign_recover) - pub fn sign_recover(&self) -> bool { - self.flags.contains(SIGN_RECOVER) - } - - /// True if the mechanism can be used to verify a digital signature - /// - /// See [`Session::verify`](crate::session::Session::verify) - pub fn verify(&self) -> bool { - self.flags.contains(VERIFY) - } - - /// True if the mechanism can be used to [`verify`](crate::session::Session::verify) a digital signature and recover the signed data - /// - // TODO See [`Session::verify_recover`](crate::session::Session::verify_recover) - pub fn verify_recover(&self) -> bool { - self.flags.contains(VERIFY_RECOVER) - } - - /// True if the mechanism can be used to generate a secret key - /// - // TODO See [`Session::generate`](crate::session::Session::generate) - pub fn generate(&self) -> bool { - self.flags.contains(GENERATE) - } - - /// True if the mechanism can be used to generate a public/private key pair - /// - /// See [`Session::generate_key_pair`](crate::session::Session::generate_key_pair)) - pub fn generate_key_pair(&self) -> bool { - self.flags.contains(GENERATE_KEY_PAIR) - } - - /// True if the mechanism can be used to wrap (encrypt) a key - /// - // TODO See [`Session::wrap`](crate::session::Session::wrap)) - pub fn wrap(&self) -> bool { - self.flags.contains(WRAP) - } - - /// True if the mechanism can be used to unwrap (decrypt) a key - /// - // TODO See [`Session::unwrap`](crate::session::Session::unwrap)) - pub fn unwrap(&self) -> bool { - self.flags.contains(UNWRAP) - } - - /// True if the mechanism can be used to derive a key from a base key - /// - // TODO See [`Session::derive`](crate::session::Session::derive)) - pub fn derive(&self) -> bool { - self.flags.contains(DERIVE) - } - - /// True if there is an extension to the flags; false if no extensions - /// - /// **[Conformance](crate#conformance-notes):** - /// This *must* be false for PKCS#11 v2.40 - pub fn extension(&self) -> bool { - self.flags.contains(EXTENSION) - } - - /// True if the mechanism can be used to with elliptic curve domain parameters over ***Fp*** - /// - /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` - pub fn ec_f_p(&self) -> bool { - self.flags.contains(EC_F_P) - } - - /// True if the mechanism can be used with elliptic curve domain parameters over ***F2m*** - /// - /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` - pub fn ec_f_2m(&self) -> bool { - self.flags.contains(EC_F_2M) - } - - /// True if the mechanism supports specifying elliptic curve domain parameters explicitly - /// - /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` - pub fn ec_from_parameters(&self) -> bool { - self.flags.contains(EC_ECPARAMETERS) - } - - /// True if the mechanism supports specifying elliptic curve domain parameters with a named curve - /// - /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` - pub fn ec_from_named_curve(&self) -> bool { - self.flags.contains(EC_NAMEDCURVE) - } - - /// True if the mechanism can be used with elliptic curve points in uncompressed form - /// - /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` - pub fn ec_uncompressed(&self) -> bool { - self.flags.contains(EC_UNCOMPRESS) - } - - /// True if the mechanism can be used with elliptic curve points in compressed form - /// - /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` - pub fn ec_compressed(&self) -> bool { - self.flags.contains(EC_COMPRESS) - } -} diff --git a/cryptoki/src/slot/flags.rs b/cryptoki/src/slot/flags.rs deleted file mode 100644 index 873d112b..00000000 --- a/cryptoki/src/slot/flags.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2021 Contributors to the Parsec project. -// SPDX-License-Identifier: Apache-2.0 -//! PKCS11 General Data Types - -use super::{SlotInfo, TokenInfo}; -use cryptoki_sys::*; -use std::fmt::{self, Debug, Display, Formatter}; - -use crate::flag::*; - -/// Collection of flags defined for [`CK_SLOT_INFO`] -pub(crate) const TOKEN_PRESENT: FlagBit = FlagBit::new(CKF_TOKEN_PRESENT); -pub(crate) const REMOVABLE_DEVICE: FlagBit = FlagBit::new(CKF_REMOVABLE_DEVICE); -pub(crate) const HW_SLOT: FlagBit = FlagBit::new(CKF_HW_SLOT); - -impl Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Flags") - .field("token_present", &(self.contains(TOKEN_PRESENT))) - .field("removable_device", &(self.contains(REMOVABLE_DEVICE))) - .field("hw_slot", &(self.contains(HW_SLOT))) - .finish() - } -} - -impl Display for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut set = f.debug_set(); - if self.contains(TOKEN_PRESENT) { - let _ = set.entry(&"Token Present"); - } - if self.contains(REMOVABLE_DEVICE) { - let _ = set.entry(&"Removable Device"); - } - if self.contains(HW_SLOT) { - let _ = set.entry(&"Hardware Slot"); - } - set.finish() - } -} - -pub(crate) const RNG: FlagBit = FlagBit::new(CKF_RNG); -pub(crate) const WRITE_PROTECTED: FlagBit = FlagBit::new(CKF_WRITE_PROTECTED); -pub(crate) const LOGIN_REQUIRED: FlagBit = FlagBit::new(CKF_LOGIN_REQUIRED); -pub(crate) const USER_PIN_INITIALIZED: FlagBit = FlagBit::new(CKF_USER_PIN_INITIALIZED); -pub(crate) const RESTORE_KEY_NOT_NEEDED: FlagBit = - FlagBit::new(CKF_RESTORE_KEY_NOT_NEEDED); -pub(crate) const CLOCK_ON_TOKEN: FlagBit = FlagBit::new(CKF_CLOCK_ON_TOKEN); -pub(crate) const PROTECTED_AUTHENTICATION_PATH: FlagBit = - FlagBit::new(CKF_PROTECTED_AUTHENTICATION_PATH); -pub(crate) const DUAL_CRYPTO_OPERATIONS: FlagBit = - FlagBit::new(CKF_DUAL_CRYPTO_OPERATIONS); -pub(crate) const TOKEN_INITIALIZED: FlagBit = FlagBit::new(CKF_TOKEN_INITIALIZED); -pub(crate) const SECONDARY_AUTHENTICATION: FlagBit = - FlagBit::new(CKF_SECONDARY_AUTHENTICATION); -pub(crate) const USER_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_USER_PIN_COUNT_LOW); -pub(crate) const USER_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_USER_PIN_FINAL_TRY); -pub(crate) const USER_PIN_LOCKED: FlagBit = FlagBit::new(CKF_USER_PIN_LOCKED); -pub(crate) const USER_PIN_TO_BE_CHANGED: FlagBit = - FlagBit::new(CKF_USER_PIN_TO_BE_CHANGED); -pub(crate) const SO_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_SO_PIN_COUNT_LOW); -pub(crate) const SO_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_SO_PIN_FINAL_TRY); -pub(crate) const SO_PIN_LOCKED: FlagBit = FlagBit::new(CKF_SO_PIN_LOCKED); -pub(crate) const SO_PIN_TO_BE_CHANGED: FlagBit = FlagBit::new(CKF_SO_PIN_TO_BE_CHANGED); -pub(crate) const ERROR_STATE: FlagBit = FlagBit::new(CKF_ERROR_STATE); - -impl Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Flags") - .field("rng", &(self.contains(RNG))) - .field("write_protected", &(self.contains(WRITE_PROTECTED))) - .field("login_required", &(self.contains(LOGIN_REQUIRED))) - .field( - "user_pin_initialized", - &(self.contains(USER_PIN_INITIALIZED)), - ) - .field( - "restore_key_not_needed", - &(self.contains(RESTORE_KEY_NOT_NEEDED)), - ) - .field("clock_on token", &(self.contains(CLOCK_ON_TOKEN))) - .field( - "protected_authentication_path", - &(self.contains(PROTECTED_AUTHENTICATION_PATH)), - ) - .field( - "dual_crypto_operations", - &(self.contains(DUAL_CRYPTO_OPERATIONS)), - ) - .field("token_initialized", &(self.contains(TOKEN_INITIALIZED))) - .field( - "secondary_authentication", - &(self.contains(SECONDARY_AUTHENTICATION)), - ) - .field("user_pin_count_low", &(self.contains(USER_PIN_COUNT_LOW))) - .field("user_pin_final_try", &(self.contains(USER_PIN_FINAL_TRY))) - .field("user_pin_locked", &(self.contains(USER_PIN_LOCKED))) - .field( - "user_pin_to_be_changed", - &(self.contains(USER_PIN_TO_BE_CHANGED)), - ) - .field("so_pin_count_low", &(self.contains(SO_PIN_COUNT_LOW))) - .field("so_pin_final_try", &(self.contains(SO_PIN_FINAL_TRY))) - .field("so_pin_locked", &(self.contains(SO_PIN_LOCKED))) - .field( - "so_pin_to_be_changed", - &(self.contains(SO_PIN_TO_BE_CHANGED)), - ) - .field("error_state", &(self.contains(ERROR_STATE))) - .finish() - } -} - -impl Display for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut set = f.debug_set(); - if self.contains(RNG) { - let _ = set.entry(&"Random Number Generator"); - } - if self.contains(WRITE_PROTECTED) { - let _ = set.entry(&"Write-Protected"); - } - if self.contains(LOGIN_REQUIRED) { - let _ = set.entry(&"Login Required"); - } - if self.contains(USER_PIN_INITIALIZED) { - let _ = set.entry(&"User Pin Initialized"); - } - if self.contains(RESTORE_KEY_NOT_NEEDED) { - let _ = set.entry(&"Restore Key Not Needed"); - } - if self.contains(CLOCK_ON_TOKEN) { - let _ = set.entry(&"Clock on Token"); - } - if self.contains(PROTECTED_AUTHENTICATION_PATH) { - let _ = set.entry(&"Protected Authentication Path"); - } - if self.contains(DUAL_CRYPTO_OPERATIONS) { - let _ = set.entry(&"Dual Crypto Operations"); - } - if self.contains(TOKEN_INITIALIZED) { - let _ = set.entry(&"Token Initialized"); - } - if self.contains(SECONDARY_AUTHENTICATION) { - let _ = set.entry(&"Secondary Authentication"); - } - if self.contains(USER_PIN_COUNT_LOW) { - let _ = set.entry(&"User PIN Count Low"); - } - if self.contains(USER_PIN_FINAL_TRY) { - let _ = set.entry(&"User PIN Final Try"); - } - if self.contains(USER_PIN_LOCKED) { - let _ = set.entry(&"User PIN Locked"); - } - if self.contains(USER_PIN_TO_BE_CHANGED) { - let _ = set.entry(&"User PIN to be Changed"); - } - if self.contains(SO_PIN_COUNT_LOW) { - let _ = set.entry(&"Security Officer PIN Count Low"); - } - if self.contains(SO_PIN_FINAL_TRY) { - let _ = set.entry(&"Security Officer PIN Final Try"); - } - if self.contains(SO_PIN_LOCKED) { - let _ = set.entry(&"Security Officer PIN Locked"); - } - if self.contains(SO_PIN_TO_BE_CHANGED) { - let _ = set.entry(&"Security Officer PIN to be Changed"); - } - if self.contains(ERROR_STATE) { - let _ = set.entry(&"Error State"); - } - set.finish() - } -} diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 751af1cb..3f8c6934 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -6,13 +6,14 @@ //! Slot and token types -mod flags; +mod slot_info; +mod token_info; + +pub use slot_info::SlotInfo; +pub use token_info::TokenInfo; + use crate::error::{Error, Result}; -use crate::flag::CkFlags; -use crate::string_from_blank_padded; -use crate::types::{maybe_unlimited, MaybeUnavailable, Version}; -use cryptoki_sys::{CK_SLOT_ID, CK_SLOT_INFO, CK_TOKEN_INFO}; -use flags::*; +use cryptoki_sys::CK_SLOT_ID; use std::convert::{TryFrom, TryInto}; use std::fmt::Formatter; @@ -86,435 +87,3 @@ impl std::fmt::Display for Slot { write!(f, "{}", self.slot_id) } } - -/// Contains information about the slot -#[derive(Debug, Clone)] -pub struct SlotInfo { - slot_description: String, - manufacturer_id: String, - flags: CkFlags, - hardware_version: Version, - firmware_version: Version, -} - -impl SlotInfo { - /// String description of the slot - /// - /// **[Conformance](crate#conformance-notes):** - /// This string is maximally 64 bytes (*not* chars) as UTF-8 - pub fn slot_description(&self) -> &str { - &self.slot_description - } - - /// ID of the slot manufacturer - /// - /// **[Conformance](crate#conformance-notes):** - /// This string is maximally 32 bytes (*not* chars) as UTF-8 - pub fn manufacturer_id(&self) -> &str { - &self.manufacturer_id - } - - /// True if a token is in the slot (e.g., a device is in the reader). - /// - /// **[Conformance](crate#conformance-notes):** - /// If this slot does not represent a removable device, a token is *always* - /// considered to be present. That is, `slot.removable device() == false` - /// implies `slot.token_present() == true`. - pub fn token_present(&self) -> bool { - self.flags.contains(TOKEN_PRESENT) - } - - /// True if the reader supports removable devices. - /// - /// **[Conformance](crate#conformance-notes):** - /// For a given slot, this flag *never* changes - pub fn removable_device(&self) -> bool { - self.flags.contains(REMOVABLE_DEVICE) - } - - /// True if the slot is a hardware slot, as opposed to a software slot - /// implementing a "soft token" - pub fn hardware_slot(&self) -> bool { - self.flags.contains(HW_SLOT) - } - - /// Version number of the slot's hardware - pub fn hardware_version(&self) -> Version { - self.hardware_version - } - - /// Version number of the slot's firmware - pub fn firmware_version(&self) -> Version { - self.firmware_version - } -} - -#[doc(hidden)] -impl From for SlotInfo { - fn from(val: CK_SLOT_INFO) -> Self { - Self { - slot_description: string_from_blank_padded(&val.slotDescription), - manufacturer_id: string_from_blank_padded(&val.manufacturerID), - flags: val.flags.into(), - hardware_version: val.hardwareVersion.into(), - firmware_version: val.firmwareVersion.into(), - } - } -} - -/// Contains information about a token -#[derive(Debug, Clone)] -pub struct TokenInfo { - label: String, // 32 - manufacturer_id: String, // 32 - model: String, // 16 - serial_number: String, // 16 - flags: CkFlags, - max_session_count: Option>, - session_count: Option, - max_rw_session_count: Option>, - rw_session_count: Option, - max_pin_len: usize, - min_pin_len: usize, - total_public_memory: Option, - free_public_memory: Option, - total_private_memory: Option, - free_private_memory: Option, - hardware_version: Version, - firmware_version: Version, - utc_time: String, -} - -#[doc(hidden)] -impl From for TokenInfo { - fn from(val: CK_TOKEN_INFO) -> Self { - Self { - label: string_from_blank_padded(&val.label), - manufacturer_id: string_from_blank_padded(&val.manufacturerID), - model: string_from_blank_padded(&val.model), - serial_number: string_from_blank_padded(&val.serialNumber), - flags: CkFlags::from(val.flags), - max_session_count: maybe_unlimited(val.ulMaxSessionCount), - session_count: u64::maybe_unavailable(val.ulSessionCount), - max_rw_session_count: maybe_unlimited(val.ulMaxRwSessionCount), - rw_session_count: u64::maybe_unavailable(val.ulRwSessionCount), - max_pin_len: val.ulMaxPinLen as usize, - min_pin_len: val.ulMinPinLen as usize, - total_public_memory: usize::maybe_unavailable(val.ulTotalPublicMemory), - free_public_memory: usize::maybe_unavailable(val.ulFreePublicMemory), - total_private_memory: usize::maybe_unavailable(val.ulTotalPrivateMemory), - free_private_memory: usize::maybe_unavailable(val.ulFreePrivateMemory), - hardware_version: val.hardwareVersion.into(), - firmware_version: val.firmwareVersion.into(), - // UTC time is not blank padded as it has the format YYYYMMDDhhmmssxx where - // x is the '0' character - utc_time: String::from_utf8_lossy(&val.utcTime).trim_end().to_string(), - } - } -} - -impl TokenInfo { - /// An application-defined label, assigned during token initialization - /// - /// **[Conformance](crate#conformance-notes):** - /// This string is maximally 32 bytes (*not* chars) as UTF-8 - pub fn label(&self) -> &str { - &self.label - } - - /// The ID of the device manufacturer - /// - /// **[Conformance](crate#conformance-notes):** - /// This string is maximally 32 bytes (*not* chars) as UTF-8 - pub fn manufacturer_id(&self) -> &str { - &self.manufacturer_id - } - - /// The model of the device - /// - /// **[Conformance](crate#conformance-notes):** - /// This string is maximally 16 bytes (*not* chars) as UTF-8 - pub fn model(&self) -> &str { - &self.model - } - - /// The character-string serial number of the device - /// - /// **[Conformance](crate#conformance-notes):** - /// This string is maximally 16 bytes (*not* chars) as UTF-8 - pub fn serial_number(&self) -> &str { - &self.serial_number - } - - /// True if the token has its own random number generator - pub fn rng(&self) -> bool { - self.flags.contains(RNG) - } - - /// True if the token is write-protected - /// - /// **[Conformance](crate#conformance-notes):** - /// Exaclty what this value means is determined by the application. An - /// application may be unable to perform certain actions on a write- - /// protected token. These actions can include any of the following (non- - /// exhaustive): - /// * Creating/modifying/deleting any object on the token - /// * Creating/modifying/deleting a token object on the token - /// * Changing the Security Officer's PIN - /// * Changing the normal user's PIN - /// - /// The token may change its write-protected status depending on the - /// session state to implement its object management policy. For instance, - /// the token may report write-protection unless the session state is R/W - /// SO or R/W User to implement a policy that does not allow any objects, - /// public or private, to be created, modified, or deleted unless the user - /// has successfully called [`Session::login`](crate::session::Session::login). - pub fn write_protected(&self) -> bool { - self.flags.contains(WRITE_PROTECTED) - } - - /// True if there are some cryptographic functions that a user *must* be - /// logged in to perform - pub fn login_required(&self) -> bool { - self.flags.contains(LOGIN_REQUIRED) - } - - /// True of the normal user's PIN has been initialized - pub fn user_pin_initialized(&self) -> bool { - self.flags.contains(USER_PIN_INITIALIZED) - } - - /// True if a successful save of a session's cryptographic operations state - /// *always* contains all keys needed to restore the state of the session. - pub fn restore_key_not_needed(&self) -> bool { - self.flags.contains(RESTORE_KEY_NOT_NEEDED) - } - - /// True if the token has its own hardware clock - pub fn clock_on_token(&self) -> bool { - self.flags.contains(CLOCK_ON_TOKEN) - } - - /// True if the token has a "protected authentication path" whereby a user - /// can log into the token without passing a PIN - pub fn protected_authentication_path(&self) -> bool { - self.flags.contains(PROTECTED_AUTHENTICATION_PATH) - } - - /// True if a single session with the token can perform dual cryptographic - /// operations - // TODO: Requires Session callbacks:to access - // * digest_encrypt_update - // * decrypt_digest_update - // * sign_encrypt_update - // * decrypt_verify_update - pub fn dual_crypto_operations(&self) -> bool { - self.flags.contains(DUAL_CRYPTO_OPERATIONS) - } - - /// True if the token has been initialized with - /// [`Pkcs11::init_token](crate::context::Pkcs11::init_token) or an - /// equivalent mechanism outside the scope of the PKCS#11 standard - /// - /// **[Conformance](crate#conformance-notes):** - /// Calling [`Pkcs11::init_token`](crate::context::Pkcs11::init_token) when - /// this flag is set will cause the token to be reinitialized. - pub fn token_initialized(&self) -> bool { - self.flags.contains(TOKEN_INITIALIZED) - } - - /// True if the token supports secondary authentication for private key - /// objects - /// **[Conformance](crate#conformance-notes):** - /// This field is deprecated and new providers *must not* set it. I.e., this function must always return `false`. - pub fn secondary_authentication(&self) -> bool { - self.flags.contains(SECONDARY_AUTHENTICATION) - } - - /// True if an incorrect user login PIN has been entered at least once - /// since the last successful authentication - /// - /// **[Conformance](crate#conformance-notes):** - /// This value may be set to always be false if the token either does not - /// support the functionality or will not reveal the information because of - /// its security policy. - pub fn user_pin_count_low(&self) -> bool { - self.flags.contains(USER_PIN_COUNT_LOW) - } - - /// True if supplying an incorrect user PIN will cause it to become locked - /// - /// **[Conformance](crate#conformance-notes):** - /// This value may be set to always be false if the token either does not - /// support the functionality or will not reveal the information because of - /// its security policy. - pub fn user_pin_final_try(&self) -> bool { - self.flags.contains(USER_PIN_FINAL_TRY) - } - - /// True if the user PIN has been locked; user login to the token is not - /// possible - pub fn user_pin_locked(&self) -> bool { - self.flags.contains(USER_PIN_LOCKED) - } - - /// True if the user PIN value is the default value set by the token - /// initialization or manufacturing, or the PIN has been expired by the - /// card - /// - /// **[Conformance](crate#conformance-notes):** - /// This may be always false if the token either does not support the - /// functionality or will not reveal the information because of its - /// security policy. - /// - /// If a PIN is set to the default value or has expired, this function - /// returns `true`. When true, logging in with a PIN will succeed, but only - /// the [`Session::set_pin`][crate::session::Session::set_pin] function can - /// be called. Calling any other function that required the user to be - /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired] - /// to be returned until - /// [`Session::set_pin`][crate::session::Session::set_pin] is called - /// successfully. - pub fn user_pin_to_be_changed(&self) -> bool { - self.flags.contains(USER_PIN_TO_BE_CHANGED) - } - - /// True if an incorrect Security Officer login PIN has been entered at least once since - /// the last successful authentication - /// - /// **[Conformance](crate#conformance-notes):** - /// This value may be set to always be false if the token either does not - /// support the functionality or will not reveal the information because of - /// its security policy. - pub fn so_pin_count_low(&self) -> bool { - self.flags.contains(SO_PIN_COUNT_LOW) - } - - /// True if supplying an incorrect Security Officer PIN will cause it to become locked - /// - /// **[Conformance](crate#conformance-notes):** - /// This value may be set to always be false if the token either does not - /// support the functionality or will not reveal the information because of - /// its security policy. - pub fn so_pin_final_try(&self) -> bool { - self.flags.contains(SO_PIN_FINAL_TRY) - } - - /// True if the Security Officer PIN has been locked; Security Officer login to the token is not - /// possible - pub fn so_pin_locked(&self) -> bool { - self.flags.contains(SO_PIN_LOCKED) - } - - /// True if the Security Officer PIN value is the default value set by the token - /// initialization or manufacturing, or the PIN has been expired by the card - /// - /// **[Conformance](crate#conformance-notes):** - /// This may be always false if the token either does not support the - /// functionality or will not reveal the information because of its security - /// policy. - /// - /// If a PIN is set to the default value or has expired, this function - /// returns `true`. When true, logging in with a PIN will succeed, but only - /// the [`Session::set_pin`][crate::session::Session::set_pin] function can - /// be called. Calling any other function that required the user to be - /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired] - /// to be returned until - /// [`Session::set_pin`][crate::session::Session::set_pin] is called - /// successfully. - pub fn so_pin_to_be_changed(&self) -> bool { - self.flags.contains(SO_PIN_TO_BE_CHANGED) - } - - /// True if the token failed a FIPS 140-2 self-test and entered an error state - pub fn error_state(&self) -> bool { - self.flags.contains(ERROR_STATE) - } - - /// The maximum number of sessions that can be opened with the token at one - /// time by a single application - /// If `None`, this information was unavailable. - /// If `Some(None)` there is no maximum, meaning the value is effectively infinite - /// If `Some(Some(n))` the maximum number of sessions is `n` - pub fn max_session_count(&self) -> Option> { - self.max_session_count - } - - /// The number of sessions this application currently has open with the - /// token - pub fn session_count(&self) -> Option { - self.session_count - } - - /// The maximum number of read/write sessions that can be opened with the - /// token at one time by a single application - /// If `None`, this information was unavailable. - /// If `Some(None)` there is no maximum, meaning the value is effectively infinite - /// If `Some(Some(n))` the maximum number of read/write sessions is `n` - pub fn max_rw_session_count(&self) -> Option> { - self.max_rw_session_count - } - - /// The number of read/write sessions this application currently has open - /// with the token - pub fn rw_session_count(&self) -> Option { - self.rw_session_count - } - - /// The maximum length in bytes of the PIN - pub fn max_pin_length(&self) -> usize { - self.max_pin_len - } - - /// The minimum length in bytes of the PIN - pub fn min_pin_length(&self) -> usize { - self.min_pin_len - } - - /// The total amount of memory on the token (in bytes) in which public - /// objects may be stored - /// Returns `None` if this information is unavailable - pub fn total_public_memory(&self) -> Option { - self.total_public_memory - } - - /// The amount of free (unused) emmeory on the token (in bytes) for public - /// objects - /// Returns `None` if this information is unavailable - pub fn free_public_memory(&self) -> Option { - self.free_public_memory - } - - /// The total amount of memory on the token (in bytes) in which private - /// objects may be stored - /// Returns `None` if this information is unavailable - pub fn total_private_memory(&self) -> Option { - self.total_private_memory - } - - /// The amount of free (unused) emmeory on the token (in bytes) for private - /// objects - /// Returns `None` if this information is unavailable - pub fn free_private_memory(&self) -> Option { - self.free_private_memory - } - - /// The version number of the hardware - pub fn hardware_version(&self) -> Version { - self.hardware_version - } - - /// The version number of the firmware - pub fn firmware_version(&self) -> Version { - self.firmware_version - } - - /// The current datetime with resolution in seconds - /// - /// Returns None if the token is not equipped with a clock (i.e., - /// `self.clock_on_token() == false`) - pub fn utc_time(&self) -> Option<&str> { - // TODO - Some(&self.utc_time) - } -} diff --git a/cryptoki/src/slot/slot_info.rs b/cryptoki/src/slot/slot_info.rs new file mode 100644 index 00000000..160cf001 --- /dev/null +++ b/cryptoki/src/slot/slot_info.rs @@ -0,0 +1,114 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +//! PKCS11 Slot info and associated flags + +use crate::flag::{CkFlags, FlagBit}; +use crate::{string_from_blank_padded, types::Version}; +use cryptoki_sys::*; +use std::fmt::{self, Debug, Display, Formatter}; + +/// Collection of flags defined for [`CK_SLOT_INFO`] +const TOKEN_PRESENT: FlagBit = FlagBit::new(CKF_TOKEN_PRESENT); +const REMOVABLE_DEVICE: FlagBit = FlagBit::new(CKF_REMOVABLE_DEVICE); +const HW_SLOT: FlagBit = FlagBit::new(CKF_HW_SLOT); + +impl Debug for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Flags") + .field("token_present", &(self.contains(TOKEN_PRESENT))) + .field("removable_device", &(self.contains(REMOVABLE_DEVICE))) + .field("hw_slot", &(self.contains(HW_SLOT))) + .finish() + } +} + +impl Display for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut set = f.debug_set(); + if self.contains(TOKEN_PRESENT) { + let _ = set.entry(&"Token Present"); + } + if self.contains(REMOVABLE_DEVICE) { + let _ = set.entry(&"Removable Device"); + } + if self.contains(HW_SLOT) { + let _ = set.entry(&"Hardware Slot"); + } + set.finish() + } +} + +/// Contains information about the slot +#[derive(Debug, Clone)] +pub struct SlotInfo { + slot_description: String, + manufacturer_id: String, + flags: CkFlags, + hardware_version: Version, + firmware_version: Version, +} + +impl SlotInfo { + /// String description of the slot + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 64 bytes (*not* chars) as UTF-8 + pub fn slot_description(&self) -> &str { + &self.slot_description + } + + /// ID of the slot manufacturer + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 + pub fn manufacturer_id(&self) -> &str { + &self.manufacturer_id + } + + /// True if a token is in the slot (e.g., a device is in the reader). + /// + /// **[Conformance](crate#conformance-notes):** + /// If this slot does not represent a removable device, a token is *always* + /// considered to be present. That is, `slot.removable device() == false` + /// implies `slot.token_present() == true`. + pub fn token_present(&self) -> bool { + self.flags.contains(TOKEN_PRESENT) + } + + /// True if the reader supports removable devices. + /// + /// **[Conformance](crate#conformance-notes):** + /// For a given slot, this flag *never* changes + pub fn removable_device(&self) -> bool { + self.flags.contains(REMOVABLE_DEVICE) + } + + /// True if the slot is a hardware slot, as opposed to a software slot + /// implementing a "soft token" + pub fn hardware_slot(&self) -> bool { + self.flags.contains(HW_SLOT) + } + + /// Version number of the slot's hardware + pub fn hardware_version(&self) -> Version { + self.hardware_version + } + + /// Version number of the slot's firmware + pub fn firmware_version(&self) -> Version { + self.firmware_version + } +} + +#[doc(hidden)] +impl From for SlotInfo { + fn from(val: CK_SLOT_INFO) -> Self { + Self { + slot_description: string_from_blank_padded(&val.slotDescription), + manufacturer_id: string_from_blank_padded(&val.manufacturerID), + flags: val.flags.into(), + hardware_version: val.hardwareVersion.into(), + firmware_version: val.firmwareVersion.into(), + } + } +} diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs new file mode 100644 index 00000000..16988478 --- /dev/null +++ b/cryptoki/src/slot/token_info.rs @@ -0,0 +1,499 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +//! PKCS11 Token info and associated flags + +use cryptoki_sys::*; +use std::fmt::{self, Debug, Display, Formatter}; + +use crate::flag::{CkFlags, FlagBit}; +use crate::string_from_blank_padded; +use crate::types::{maybe_unlimited, MaybeUnavailable, Version}; + +const RNG: FlagBit = FlagBit::new(CKF_RNG); +const WRITE_PROTECTED: FlagBit = FlagBit::new(CKF_WRITE_PROTECTED); +const LOGIN_REQUIRED: FlagBit = FlagBit::new(CKF_LOGIN_REQUIRED); +const USER_PIN_INITIALIZED: FlagBit = FlagBit::new(CKF_USER_PIN_INITIALIZED); +const RESTORE_KEY_NOT_NEEDED: FlagBit = FlagBit::new(CKF_RESTORE_KEY_NOT_NEEDED); +const CLOCK_ON_TOKEN: FlagBit = FlagBit::new(CKF_CLOCK_ON_TOKEN); +const PROTECTED_AUTHENTICATION_PATH: FlagBit = + FlagBit::new(CKF_PROTECTED_AUTHENTICATION_PATH); +const DUAL_CRYPTO_OPERATIONS: FlagBit = FlagBit::new(CKF_DUAL_CRYPTO_OPERATIONS); +const TOKEN_INITIALIZED: FlagBit = FlagBit::new(CKF_TOKEN_INITIALIZED); +const SECONDARY_AUTHENTICATION: FlagBit = FlagBit::new(CKF_SECONDARY_AUTHENTICATION); +const USER_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_USER_PIN_COUNT_LOW); +const USER_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_USER_PIN_FINAL_TRY); +const USER_PIN_LOCKED: FlagBit = FlagBit::new(CKF_USER_PIN_LOCKED); +const USER_PIN_TO_BE_CHANGED: FlagBit = FlagBit::new(CKF_USER_PIN_TO_BE_CHANGED); +const SO_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_SO_PIN_COUNT_LOW); +const SO_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_SO_PIN_FINAL_TRY); +const SO_PIN_LOCKED: FlagBit = FlagBit::new(CKF_SO_PIN_LOCKED); +const SO_PIN_TO_BE_CHANGED: FlagBit = FlagBit::new(CKF_SO_PIN_TO_BE_CHANGED); +const ERROR_STATE: FlagBit = FlagBit::new(CKF_ERROR_STATE); + +impl Debug for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Flags") + .field("rng", &(self.contains(RNG))) + .field("write_protected", &(self.contains(WRITE_PROTECTED))) + .field("login_required", &(self.contains(LOGIN_REQUIRED))) + .field( + "user_pin_initialized", + &(self.contains(USER_PIN_INITIALIZED)), + ) + .field( + "restore_key_not_needed", + &(self.contains(RESTORE_KEY_NOT_NEEDED)), + ) + .field("clock_on token", &(self.contains(CLOCK_ON_TOKEN))) + .field( + "protected_authentication_path", + &(self.contains(PROTECTED_AUTHENTICATION_PATH)), + ) + .field( + "dual_crypto_operations", + &(self.contains(DUAL_CRYPTO_OPERATIONS)), + ) + .field("token_initialized", &(self.contains(TOKEN_INITIALIZED))) + .field( + "secondary_authentication", + &(self.contains(SECONDARY_AUTHENTICATION)), + ) + .field("user_pin_count_low", &(self.contains(USER_PIN_COUNT_LOW))) + .field("user_pin_final_try", &(self.contains(USER_PIN_FINAL_TRY))) + .field("user_pin_locked", &(self.contains(USER_PIN_LOCKED))) + .field( + "user_pin_to_be_changed", + &(self.contains(USER_PIN_TO_BE_CHANGED)), + ) + .field("so_pin_count_low", &(self.contains(SO_PIN_COUNT_LOW))) + .field("so_pin_final_try", &(self.contains(SO_PIN_FINAL_TRY))) + .field("so_pin_locked", &(self.contains(SO_PIN_LOCKED))) + .field( + "so_pin_to_be_changed", + &(self.contains(SO_PIN_TO_BE_CHANGED)), + ) + .field("error_state", &(self.contains(ERROR_STATE))) + .finish() + } +} + +impl Display for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut set = f.debug_set(); + if self.contains(RNG) { + let _ = set.entry(&"Random Number Generator"); + } + if self.contains(WRITE_PROTECTED) { + let _ = set.entry(&"Write-Protected"); + } + if self.contains(LOGIN_REQUIRED) { + let _ = set.entry(&"Login Required"); + } + if self.contains(USER_PIN_INITIALIZED) { + let _ = set.entry(&"User Pin Initialized"); + } + if self.contains(RESTORE_KEY_NOT_NEEDED) { + let _ = set.entry(&"Restore Key Not Needed"); + } + if self.contains(CLOCK_ON_TOKEN) { + let _ = set.entry(&"Clock on Token"); + } + if self.contains(PROTECTED_AUTHENTICATION_PATH) { + let _ = set.entry(&"Protected Authentication Path"); + } + if self.contains(DUAL_CRYPTO_OPERATIONS) { + let _ = set.entry(&"Dual Crypto Operations"); + } + if self.contains(TOKEN_INITIALIZED) { + let _ = set.entry(&"Token Initialized"); + } + if self.contains(SECONDARY_AUTHENTICATION) { + let _ = set.entry(&"Secondary Authentication"); + } + if self.contains(USER_PIN_COUNT_LOW) { + let _ = set.entry(&"User PIN Count Low"); + } + if self.contains(USER_PIN_FINAL_TRY) { + let _ = set.entry(&"User PIN Final Try"); + } + if self.contains(USER_PIN_LOCKED) { + let _ = set.entry(&"User PIN Locked"); + } + if self.contains(USER_PIN_TO_BE_CHANGED) { + let _ = set.entry(&"User PIN to be Changed"); + } + if self.contains(SO_PIN_COUNT_LOW) { + let _ = set.entry(&"Security Officer PIN Count Low"); + } + if self.contains(SO_PIN_FINAL_TRY) { + let _ = set.entry(&"Security Officer PIN Final Try"); + } + if self.contains(SO_PIN_LOCKED) { + let _ = set.entry(&"Security Officer PIN Locked"); + } + if self.contains(SO_PIN_TO_BE_CHANGED) { + let _ = set.entry(&"Security Officer PIN to be Changed"); + } + if self.contains(ERROR_STATE) { + let _ = set.entry(&"Error State"); + } + set.finish() + } +} + +/// Contains information about a token +#[derive(Debug, Clone)] +pub struct TokenInfo { + label: String, // 32 + manufacturer_id: String, // 32 + model: String, // 16 + serial_number: String, // 16 + flags: CkFlags, + max_session_count: Option>, + session_count: Option, + max_rw_session_count: Option>, + rw_session_count: Option, + max_pin_len: usize, + min_pin_len: usize, + total_public_memory: Option, + free_public_memory: Option, + total_private_memory: Option, + free_private_memory: Option, + hardware_version: Version, + firmware_version: Version, + utc_time: String, +} + +#[doc(hidden)] +impl From for TokenInfo { + fn from(val: CK_TOKEN_INFO) -> Self { + Self { + label: string_from_blank_padded(&val.label), + manufacturer_id: string_from_blank_padded(&val.manufacturerID), + model: string_from_blank_padded(&val.model), + serial_number: string_from_blank_padded(&val.serialNumber), + flags: CkFlags::from(val.flags), + max_session_count: maybe_unlimited(val.ulMaxSessionCount), + session_count: u64::maybe_unavailable(val.ulSessionCount), + max_rw_session_count: maybe_unlimited(val.ulMaxRwSessionCount), + rw_session_count: u64::maybe_unavailable(val.ulRwSessionCount), + max_pin_len: val.ulMaxPinLen as usize, + min_pin_len: val.ulMinPinLen as usize, + total_public_memory: usize::maybe_unavailable(val.ulTotalPublicMemory), + free_public_memory: usize::maybe_unavailable(val.ulFreePublicMemory), + total_private_memory: usize::maybe_unavailable(val.ulTotalPrivateMemory), + free_private_memory: usize::maybe_unavailable(val.ulFreePrivateMemory), + hardware_version: val.hardwareVersion.into(), + firmware_version: val.firmwareVersion.into(), + // UTC time is not blank padded as it has the format YYYYMMDDhhmmssxx where + // x is the '0' character + utc_time: String::from_utf8_lossy(&val.utcTime).trim_end().to_string(), + } + } +} + +impl TokenInfo { + /// An application-defined label, assigned during token initialization + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 + pub fn label(&self) -> &str { + &self.label + } + + /// The ID of the device manufacturer + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 + pub fn manufacturer_id(&self) -> &str { + &self.manufacturer_id + } + + /// The model of the device + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 16 bytes (*not* chars) as UTF-8 + pub fn model(&self) -> &str { + &self.model + } + + /// The character-string serial number of the device + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 16 bytes (*not* chars) as UTF-8 + pub fn serial_number(&self) -> &str { + &self.serial_number + } + + /// True if the token has its own random number generator + pub fn rng(&self) -> bool { + self.flags.contains(RNG) + } + + /// True if the token is write-protected + /// + /// **[Conformance](crate#conformance-notes):** + /// Exaclty what this value means is determined by the application. An + /// application may be unable to perform certain actions on a write- + /// protected token. These actions can include any of the following (non- + /// exhaustive): + /// * Creating/modifying/deleting any object on the token + /// * Creating/modifying/deleting a token object on the token + /// * Changing the Security Officer's PIN + /// * Changing the normal user's PIN + /// + /// The token may change its write-protected status depending on the + /// session state to implement its object management policy. For instance, + /// the token may report write-protection unless the session state is R/W + /// SO or R/W User to implement a policy that does not allow any objects, + /// public or private, to be created, modified, or deleted unless the user + /// has successfully called [`Session::login`](crate::session::Session::login). + pub fn write_protected(&self) -> bool { + self.flags.contains(WRITE_PROTECTED) + } + + /// True if there are some cryptographic functions that a user *must* be + /// logged in to perform + pub fn login_required(&self) -> bool { + self.flags.contains(LOGIN_REQUIRED) + } + + /// True of the normal user's PIN has been initialized + pub fn user_pin_initialized(&self) -> bool { + self.flags.contains(USER_PIN_INITIALIZED) + } + + /// True if a successful save of a session's cryptographic operations state + /// *always* contains all keys needed to restore the state of the session. + pub fn restore_key_not_needed(&self) -> bool { + self.flags.contains(RESTORE_KEY_NOT_NEEDED) + } + + /// True if the token has its own hardware clock + pub fn clock_on_token(&self) -> bool { + self.flags.contains(CLOCK_ON_TOKEN) + } + + /// True if the token has a "protected authentication path" whereby a user + /// can log into the token without passing a PIN + pub fn protected_authentication_path(&self) -> bool { + self.flags.contains(PROTECTED_AUTHENTICATION_PATH) + } + + /// True if a single session with the token can perform dual cryptographic + /// operations + // TODO: Requires Session callbacks:to access + // * digest_encrypt_update + // * decrypt_digest_update + // * sign_encrypt_update + // * decrypt_verify_update + pub fn dual_crypto_operations(&self) -> bool { + self.flags.contains(DUAL_CRYPTO_OPERATIONS) + } + + /// True if the token has been initialized with + /// [`Pkcs11::init_token](crate::context::Pkcs11::init_token) or an + /// equivalent mechanism outside the scope of the PKCS#11 standard + /// + /// **[Conformance](crate#conformance-notes):** + /// Calling [`Pkcs11::init_token`](crate::context::Pkcs11::init_token) when + /// this flag is set will cause the token to be reinitialized. + pub fn token_initialized(&self) -> bool { + self.flags.contains(TOKEN_INITIALIZED) + } + + /// True if the token supports secondary authentication for private key + /// objects + /// **[Conformance](crate#conformance-notes):** + /// This field is deprecated and new providers *must not* set it. I.e., this function must always return `false`. + pub fn secondary_authentication(&self) -> bool { + self.flags.contains(SECONDARY_AUTHENTICATION) + } + + /// True if an incorrect user login PIN has been entered at least once + /// since the last successful authentication + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn user_pin_count_low(&self) -> bool { + self.flags.contains(USER_PIN_COUNT_LOW) + } + + /// True if supplying an incorrect user PIN will cause it to become locked + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn user_pin_final_try(&self) -> bool { + self.flags.contains(USER_PIN_FINAL_TRY) + } + + /// True if the user PIN has been locked; user login to the token is not + /// possible + pub fn user_pin_locked(&self) -> bool { + self.flags.contains(USER_PIN_LOCKED) + } + + /// True if the user PIN value is the default value set by the token + /// initialization or manufacturing, or the PIN has been expired by the + /// card + /// + /// **[Conformance](crate#conformance-notes):** + /// This may be always false if the token either does not support the + /// functionality or will not reveal the information because of its + /// security policy. + /// + /// If a PIN is set to the default value or has expired, this function + /// returns `true`. When true, logging in with a PIN will succeed, but only + /// the [`Session::set_pin`][crate::session::Session::set_pin] function can + /// be called. Calling any other function that required the user to be + /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired] + /// to be returned until + /// [`Session::set_pin`][crate::session::Session::set_pin] is called + /// successfully. + pub fn user_pin_to_be_changed(&self) -> bool { + self.flags.contains(USER_PIN_TO_BE_CHANGED) + } + + /// True if an incorrect Security Officer login PIN has been entered at least once since + /// the last successful authentication + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn so_pin_count_low(&self) -> bool { + self.flags.contains(SO_PIN_COUNT_LOW) + } + + /// True if supplying an incorrect Security Officer PIN will cause it to become locked + /// + /// **[Conformance](crate#conformance-notes):** + /// This value may be set to always be false if the token either does not + /// support the functionality or will not reveal the information because of + /// its security policy. + pub fn so_pin_final_try(&self) -> bool { + self.flags.contains(SO_PIN_FINAL_TRY) + } + + /// True if the Security Officer PIN has been locked; Security Officer login to the token is not + /// possible + pub fn so_pin_locked(&self) -> bool { + self.flags.contains(SO_PIN_LOCKED) + } + + /// True if the Security Officer PIN value is the default value set by the token + /// initialization or manufacturing, or the PIN has been expired by the card + /// + /// **[Conformance](crate#conformance-notes):** + /// This may be always false if the token either does not support the + /// functionality or will not reveal the information because of its security + /// policy. + /// + /// If a PIN is set to the default value or has expired, this function + /// returns `true`. When true, logging in with a PIN will succeed, but only + /// the [`Session::set_pin`][crate::session::Session::set_pin] function can + /// be called. Calling any other function that required the user to be + /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired] + /// to be returned until + /// [`Session::set_pin`][crate::session::Session::set_pin] is called + /// successfully. + pub fn so_pin_to_be_changed(&self) -> bool { + self.flags.contains(SO_PIN_TO_BE_CHANGED) + } + + /// True if the token failed a FIPS 140-2 self-test and entered an error state + pub fn error_state(&self) -> bool { + self.flags.contains(ERROR_STATE) + } + + /// The maximum number of sessions that can be opened with the token at one + /// time by a single application + /// If `None`, this information was unavailable. + /// If `Some(None)` there is no maximum, meaning the value is effectively infinite + /// If `Some(Some(n))` the maximum number of sessions is `n` + pub fn max_session_count(&self) -> Option> { + self.max_session_count + } + + /// The number of sessions this application currently has open with the + /// token + pub fn session_count(&self) -> Option { + self.session_count + } + + /// The maximum number of read/write sessions that can be opened with the + /// token at one time by a single application + /// If `None`, this information was unavailable. + /// If `Some(None)` there is no maximum, meaning the value is effectively infinite + /// If `Some(Some(n))` the maximum number of read/write sessions is `n` + pub fn max_rw_session_count(&self) -> Option> { + self.max_rw_session_count + } + + /// The number of read/write sessions this application currently has open + /// with the token + pub fn rw_session_count(&self) -> Option { + self.rw_session_count + } + + /// The maximum length in bytes of the PIN + pub fn max_pin_length(&self) -> usize { + self.max_pin_len + } + + /// The minimum length in bytes of the PIN + pub fn min_pin_length(&self) -> usize { + self.min_pin_len + } + + /// The total amount of memory on the token (in bytes) in which public + /// objects may be stored + /// Returns `None` if this information is unavailable + pub fn total_public_memory(&self) -> Option { + self.total_public_memory + } + + /// The amount of free (unused) emmeory on the token (in bytes) for public + /// objects + /// Returns `None` if this information is unavailable + pub fn free_public_memory(&self) -> Option { + self.free_public_memory + } + + /// The total amount of memory on the token (in bytes) in which private + /// objects may be stored + /// Returns `None` if this information is unavailable + pub fn total_private_memory(&self) -> Option { + self.total_private_memory + } + + /// The amount of free (unused) emmeory on the token (in bytes) for private + /// objects + /// Returns `None` if this information is unavailable + pub fn free_private_memory(&self) -> Option { + self.free_private_memory + } + + /// The version number of the hardware + pub fn hardware_version(&self) -> Version { + self.hardware_version + } + + /// The version number of the firmware + pub fn firmware_version(&self) -> Version { + self.firmware_version + } + + /// The current datetime with resolution in seconds + /// + /// Returns None if the token is not equipped with a clock (i.e., + /// `self.clock_on_token() == false`) + pub fn utc_time(&self) -> Option<&str> { + // TODO + Some(&self.utc_time) + } +} From aab33b7af1b232260019743627c258d76a483c79 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 11 Nov 2021 20:56:01 -0500 Subject: [PATCH 16/36] Tidy info/flag docs Signed-off-by: Keith Koskie --- cryptoki/src/mechanism/mechanism_info.rs | 78 +++++++++++++++--------- cryptoki/src/slot/slot_info.rs | 2 +- cryptoki/src/slot/token_info.rs | 64 +++++++++---------- 3 files changed, 81 insertions(+), 63 deletions(-) diff --git a/cryptoki/src/mechanism/mechanism_info.rs b/cryptoki/src/mechanism/mechanism_info.rs index 0aef1446..6906b8a9 100644 --- a/cryptoki/src/mechanism/mechanism_info.rs +++ b/cryptoki/src/mechanism/mechanism_info.rs @@ -56,6 +56,7 @@ impl Debug for CkFlags { } } +// Uses active voice to indicate descriptors are actions impl Display for CkFlags { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut set = f.debug_set(); @@ -108,7 +109,7 @@ impl Display for CkFlags { let _ = set.entry(&"Supports ECs Over F_p"); } if self.contains(EC_F_2M) { - let _ = set.entry(&"Supports ECs Over F_2^m"); + let _ = set.entry(&"Supports ECs Over F_2**m"); } if self.contains(EC_ECPARAMETERS) { let _ = set.entry(&"Accepts EC as Parameters"); @@ -126,30 +127,20 @@ impl Display for CkFlags { } } -/// Contains information about a mechanism -#[derive(Debug, Clone, Copy, Default)] +/// Information about a particular mechanism +#[derive(Debug, Clone, Copy)] pub struct MechanismInfo { min_key_size: usize, max_key_size: usize, flags: CkFlags, } -#[doc(hidden)] -impl From for MechanismInfo { - fn from(val: CK_MECHANISM_INFO) -> Self { - Self { - min_key_size: val.ulMinKeySize as usize, - max_key_size: val.ulMaxKeySize as usize, - flags: CkFlags::from(val.flags), - } - } -} - impl MechanismInfo { /// The minimum size of the key for the mechanism /// /// **[Conformance](crate#conformance-notes):** - /// Whether this is measured in bits or in bytes is mechanism-dependent + /// Whether this is measured in bits or in bytes is mechanism-dependent. + /// For some mechanisms, this field may be meaningless and take any value. pub fn min_key_size(&self) -> usize { self.min_key_size } @@ -158,11 +149,13 @@ impl MechanismInfo { /// /// **[Conformance](crate#conformance-notes):** /// Whether this is measured in bits or in bytes is mechanism-dependent + /// For some mechanisms, this field may be meaningless and take any value. pub fn max_key_size(&self) -> usize { self.max_key_size } - /// True if the mechanism is performed by the device; false if the mechanism is performed in software + /// True if the mechanism is performed by the device; false if the + /// mechanism is performed in software pub fn hardware(&self) -> bool { self.flags.contains(HW) } @@ -195,7 +188,8 @@ impl MechanismInfo { self.flags.contains(SIGN) } - /// True if the mechanism can be used to digitally data which can be recovered from the signature + /// True if the mechanism can be used to digitally data which can be + /// recovered from the signature /// // TODO See [`Session::sign_recover`](crate::session::Session::sign_recover) pub fn sign_recover(&self) -> bool { @@ -209,7 +203,8 @@ impl MechanismInfo { self.flags.contains(VERIFY) } - /// True if the mechanism can be used to [`verify`](crate::session::Session::verify) a digital signature and recover the signed data + /// True if the mechanism can be used to verify a digital signature and + /// recover the signed data /// // TODO See [`Session::verify_recover`](crate::session::Session::verify_recover) pub fn verify_recover(&self) -> bool { @@ -259,51 +254,74 @@ impl MechanismInfo { self.flags.contains(EXTENSION) } - /// True if the mechanism can be used to with elliptic curve domain parameters over ***Fp*** + /// True if the mechanism can be used to with elliptic curve domain + /// parameters over ***Fp*** /// /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` + /// *At least* one of [`ec_f_p`](Self::ec_f_p) and + /// [`ec_f_2m`](Self::ec_f_2m) must be `true` pub fn ec_f_p(&self) -> bool { self.flags.contains(EC_F_P) } - /// True if the mechanism can be used with elliptic curve domain parameters over ***F2m*** + /// True if the mechanism can be used with elliptic curve domain parameters + /// over ***F2m*** /// /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_f_p`](Self::ec_f_p) and [`ec_f_2m`](Self::ec_f_2m) must be `true` + /// *At least* one of [`ec_f_p`](Self::ec_f_p) and + /// [`ec_f_2m`](Self::ec_f_2m) must be `true` pub fn ec_f_2m(&self) -> bool { self.flags.contains(EC_F_2M) } - /// True if the mechanism supports specifying elliptic curve domain parameters explicitly + /// True if the mechanism supports specifying elliptic curve domain + /// parameters explicitly /// /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` + /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and + /// [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` pub fn ec_from_parameters(&self) -> bool { self.flags.contains(EC_ECPARAMETERS) } - /// True if the mechanism supports specifying elliptic curve domain parameters with a named curve + /// True if the mechanism supports specifying elliptic curve domain + /// parameters with a named curve /// /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` + /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and + /// [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` pub fn ec_from_named_curve(&self) -> bool { self.flags.contains(EC_NAMEDCURVE) } - /// True if the mechanism can be used with elliptic curve points in uncompressed form + /// True if the mechanism can be used with elliptic curve points in + /// uncompressed form /// /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` + /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and + /// [`ec_compressed`](Self::ec_compressed) must be `true` pub fn ec_uncompressed(&self) -> bool { self.flags.contains(EC_UNCOMPRESS) } - /// True if the mechanism can be used with elliptic curve points in compressed form + /// True if the mechanism can be used with elliptic curve points in + /// compressed form /// /// **[Conformance](crate#conformance-notes):** - /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and [`ec_compressed`](Self::ec_compressed) must be `true` + /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and + /// [`ec_compressed`](Self::ec_compressed) must be `true` pub fn ec_compressed(&self) -> bool { self.flags.contains(EC_COMPRESS) } } + +#[doc(hidden)] +impl From for MechanismInfo { + fn from(val: CK_MECHANISM_INFO) -> Self { + Self { + min_key_size: val.ulMinKeySize as usize, + max_key_size: val.ulMaxKeySize as usize, + flags: CkFlags::from(val.flags), + } + } +} diff --git a/cryptoki/src/slot/slot_info.rs b/cryptoki/src/slot/slot_info.rs index 160cf001..b398badd 100644 --- a/cryptoki/src/slot/slot_info.rs +++ b/cryptoki/src/slot/slot_info.rs @@ -38,7 +38,7 @@ impl Display for CkFlags { } } -/// Contains information about the slot +/// Information about a slot #[derive(Debug, Clone)] pub struct SlotInfo { slot_description: String, diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index 16988478..1daadcda 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -96,19 +96,19 @@ impl Display for CkFlags { let _ = set.entry(&"Restore Key Not Needed"); } if self.contains(CLOCK_ON_TOKEN) { - let _ = set.entry(&"Clock on Token"); + let _ = set.entry(&"Hardware Clock"); } if self.contains(PROTECTED_AUTHENTICATION_PATH) { let _ = set.entry(&"Protected Authentication Path"); } if self.contains(DUAL_CRYPTO_OPERATIONS) { - let _ = set.entry(&"Dual Crypto Operations"); + let _ = set.entry(&"Supports Dual Crypto Operations"); } if self.contains(TOKEN_INITIALIZED) { let _ = set.entry(&"Token Initialized"); } if self.contains(SECONDARY_AUTHENTICATION) { - let _ = set.entry(&"Secondary Authentication"); + let _ = set.entry(&"Supports Secondary Authentication"); } if self.contains(USER_PIN_COUNT_LOW) { let _ = set.entry(&"User PIN Count Low"); @@ -141,7 +141,7 @@ impl Display for CkFlags { } } -/// Contains information about a token +/// Information about a token #[derive(Debug, Clone)] pub struct TokenInfo { label: String, // 32 @@ -164,34 +164,6 @@ pub struct TokenInfo { utc_time: String, } -#[doc(hidden)] -impl From for TokenInfo { - fn from(val: CK_TOKEN_INFO) -> Self { - Self { - label: string_from_blank_padded(&val.label), - manufacturer_id: string_from_blank_padded(&val.manufacturerID), - model: string_from_blank_padded(&val.model), - serial_number: string_from_blank_padded(&val.serialNumber), - flags: CkFlags::from(val.flags), - max_session_count: maybe_unlimited(val.ulMaxSessionCount), - session_count: u64::maybe_unavailable(val.ulSessionCount), - max_rw_session_count: maybe_unlimited(val.ulMaxRwSessionCount), - rw_session_count: u64::maybe_unavailable(val.ulRwSessionCount), - max_pin_len: val.ulMaxPinLen as usize, - min_pin_len: val.ulMinPinLen as usize, - total_public_memory: usize::maybe_unavailable(val.ulTotalPublicMemory), - free_public_memory: usize::maybe_unavailable(val.ulFreePublicMemory), - total_private_memory: usize::maybe_unavailable(val.ulTotalPrivateMemory), - free_private_memory: usize::maybe_unavailable(val.ulFreePrivateMemory), - hardware_version: val.hardwareVersion.into(), - firmware_version: val.firmwareVersion.into(), - // UTC time is not blank padded as it has the format YYYYMMDDhhmmssxx where - // x is the '0' character - utc_time: String::from_utf8_lossy(&val.utcTime).trim_end().to_string(), - } - } -} - impl TokenInfo { /// An application-defined label, assigned during token initialization /// @@ -497,3 +469,31 @@ impl TokenInfo { Some(&self.utc_time) } } + +#[doc(hidden)] +impl From for TokenInfo { + fn from(val: CK_TOKEN_INFO) -> Self { + Self { + label: string_from_blank_padded(&val.label), + manufacturer_id: string_from_blank_padded(&val.manufacturerID), + model: string_from_blank_padded(&val.model), + serial_number: string_from_blank_padded(&val.serialNumber), + flags: CkFlags::from(val.flags), + max_session_count: maybe_unlimited(val.ulMaxSessionCount), + session_count: u64::maybe_unavailable(val.ulSessionCount), + max_rw_session_count: maybe_unlimited(val.ulMaxRwSessionCount), + rw_session_count: u64::maybe_unavailable(val.ulRwSessionCount), + max_pin_len: val.ulMaxPinLen as usize, + min_pin_len: val.ulMinPinLen as usize, + total_public_memory: usize::maybe_unavailable(val.ulTotalPublicMemory), + free_public_memory: usize::maybe_unavailable(val.ulFreePublicMemory), + total_private_memory: usize::maybe_unavailable(val.ulTotalPrivateMemory), + free_private_memory: usize::maybe_unavailable(val.ulFreePrivateMemory), + hardware_version: val.hardwareVersion.into(), + firmware_version: val.firmwareVersion.into(), + // UTC time is not blank padded as it has the format YYYYMMDDhhmmssxx where + // x is the '0' character + utc_time: String::from_utf8_lossy(&val.utcTime).trim_end().to_string(), + } + } +} From 5bb40c6ad61746a127a72fde04e83e68cbe28a56 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 12 Nov 2021 09:41:14 -0500 Subject: [PATCH 17/36] Add Version constructor Function added to support unit testing Signed-off-by: Keith Koskie --- cryptoki/src/types.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index 97e8a211..2a928a13 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -218,6 +218,11 @@ impl std::fmt::Display for Version { } impl Version { + /// Construct a new version + pub fn new(major: u8, minor: u8) -> Self { + Self { major, minor } + } + /// Returns the major version pub fn major(&self) -> CK_BYTE { self.major From 88134f093fe111f229158d4b1555a7eedcf9379d Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 12 Nov 2021 09:42:29 -0500 Subject: [PATCH 18/36] Add unit tests for info+flags debug content Signed-off-by: Keith Koskie --- cryptoki/src/mechanism/mechanism_info.rs | 103 ++++++++++++++++ cryptoki/src/slot/slot_info.rs | 61 ++++++++++ cryptoki/src/slot/token_info.rs | 147 +++++++++++++++++++++++ 3 files changed, 311 insertions(+) diff --git a/cryptoki/src/mechanism/mechanism_info.rs b/cryptoki/src/mechanism/mechanism_info.rs index 6906b8a9..a5e63f04 100644 --- a/cryptoki/src/mechanism/mechanism_info.rs +++ b/cryptoki/src/mechanism/mechanism_info.rs @@ -325,3 +325,106 @@ impl From for MechanismInfo { } } } + +#[cfg(test)] +mod test { + use super::MechanismInfo; + use crate::flag::CkFlags; + use cryptoki_sys::CK_FLAGS; + + #[test] + fn debug_flags_all() { + let expected = r"Flags { + hw: true, + encrypt: true, + decrypt: true, + digest: true, + sign: true, + sign_recover: true, + verify: true, + verify_recover: true, + generate: true, + generate_key_pair: true, + wrap: true, + unwrap: true, + derive: true, + extension: true, + ec_f_p: true, + ec_f_2m: true, + ec_ecparameters: true, + ec_namedcurve: true, + ec_uncompress: true, + ec_compress: true, +}"; + let all: CkFlags = CkFlags::from(CK_FLAGS::MAX); + let observed = format!("{:#?}", all); + println!("{}", observed); + assert_eq!(observed, expected); + } + + #[test] + fn debug_flags_none() { + let expected = r"Flags { + hw: false, + encrypt: false, + decrypt: false, + digest: false, + sign: false, + sign_recover: false, + verify: false, + verify_recover: false, + generate: false, + generate_key_pair: false, + wrap: false, + unwrap: false, + derive: false, + extension: false, + ec_f_p: false, + ec_f_2m: false, + ec_ecparameters: false, + ec_namedcurve: false, + ec_uncompress: false, + ec_compress: false, +}"; + let none: CkFlags = CkFlags::from(0); + let observed = format!("{:#?}", none); + assert_eq!(observed, expected); + } + + #[test] + fn debug_info() { + let info = MechanismInfo { + min_key_size: 16, + max_key_size: 4096, + flags: CkFlags::from(0), + }; + let expected = r#"MechanismInfo { + min_key_size: 16, + max_key_size: 4096, + flags: Flags { + hw: false, + encrypt: false, + decrypt: false, + digest: false, + sign: false, + sign_recover: false, + verify: false, + verify_recover: false, + generate: false, + generate_key_pair: false, + wrap: false, + unwrap: false, + derive: false, + extension: false, + ec_f_p: false, + ec_f_2m: false, + ec_ecparameters: false, + ec_namedcurve: false, + ec_uncompress: false, + ec_compress: false, + }, +}"#; + let observed = format!("{:#?}", info); + assert_eq!(observed, expected); + } +} diff --git a/cryptoki/src/slot/slot_info.rs b/cryptoki/src/slot/slot_info.rs index b398badd..068c656e 100644 --- a/cryptoki/src/slot/slot_info.rs +++ b/cryptoki/src/slot/slot_info.rs @@ -112,3 +112,64 @@ impl From for SlotInfo { } } } + +#[cfg(test)] +mod test { + use super::SlotInfo; + use crate::{flag::CkFlags, types::Version}; + use cryptoki_sys::CK_FLAGS; + + #[test] + fn debug_flags_all() { + let expected = r"Flags { + token_present: true, + removable_device: true, + hw_slot: true, +}"; + let all: CkFlags = CkFlags::from(CK_FLAGS::MAX); + let observed = format!("{:#?}", all); + assert_eq!(observed, expected); + } + + #[test] + fn debug_none_set() { + let expected = r"Flags { + token_present: false, + removable_device: false, + hw_slot: false, +}"; + let none: CkFlags = CkFlags::from(0); + let observed = format!("{:#?}", none); + assert_eq!(observed, expected); + } + + #[test] + fn debug_info() { + let info = SlotInfo { + slot_description: String::from("Slot Description"), + manufacturer_id: String::from("Manufacturer ID"), + flags: CkFlags::from(0), + hardware_version: Version::new(0, 255), + firmware_version: Version::new(255, 0), + }; + let expected = r#"SlotInfo { + slot_description: "Slot Description", + manufacturer_id: "Manufacturer ID", + flags: Flags { + token_present: false, + removable_device: false, + hw_slot: false, + }, + hardware_version: Version { + major: 0, + minor: 255, + }, + firmware_version: Version { + major: 255, + minor: 0, + }, +}"#; + let observed = format!("{:#?}", info); + assert_eq!(observed, expected); + } +} diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index 1daadcda..d3e97a81 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -497,3 +497,150 @@ impl From for TokenInfo { } } } + +#[cfg(test)] +mod test { + use super::TokenInfo; + use crate::{flag::CkFlags, types::Version}; + use cryptoki_sys::CK_FLAGS; + + #[test] + fn debug_flags_all() { + let expected = r"Flags { + rng: true, + write_protected: true, + login_required: true, + user_pin_initialized: true, + restore_key_not_needed: true, + clock_on token: true, + protected_authentication_path: true, + dual_crypto_operations: true, + token_initialized: true, + secondary_authentication: true, + user_pin_count_low: true, + user_pin_final_try: true, + user_pin_locked: true, + user_pin_to_be_changed: true, + so_pin_count_low: true, + so_pin_final_try: true, + so_pin_locked: true, + so_pin_to_be_changed: true, + error_state: true, +}"; + let all: CkFlags = CkFlags::from(CK_FLAGS::MAX); + let observed = format!("{:#?}", all); + assert_eq!(observed, expected); + } + + #[test] + fn debug_flags_none() { + let expected = r"Flags { + rng: false, + write_protected: false, + login_required: false, + user_pin_initialized: false, + restore_key_not_needed: false, + clock_on token: false, + protected_authentication_path: false, + dual_crypto_operations: false, + token_initialized: false, + secondary_authentication: false, + user_pin_count_low: false, + user_pin_final_try: false, + user_pin_locked: false, + user_pin_to_be_changed: false, + so_pin_count_low: false, + so_pin_final_try: false, + so_pin_locked: false, + so_pin_to_be_changed: false, + error_state: false, +}"; + let none: CkFlags = CkFlags::from(0); + let observed = format!("{:#?}", none); + assert_eq!(observed, expected); + } + + #[test] + fn debug_info() { + let info = TokenInfo { + label: String::from("Token Label"), + manufacturer_id: String::from("Manufacturer ID"), + model: String::from("Token Model"), + serial_number: String::from("Serial Number"), + flags: CkFlags::from(0), + max_session_count: Some(Some(100)), // max == 100 + session_count: None, // unavailable + max_rw_session_count: Some(None), // max == infinite + rw_session_count: Some(1), + max_pin_len: 16, + min_pin_len: 4, + total_public_memory: Some(32 << 30), // 32GiB + free_public_memory: Some(1234567890), + total_private_memory: None, // unavailable + free_private_memory: None, // unavailable + hardware_version: Version::new(0, 255), + firmware_version: Version::new(255, 0), + utc_time: String::from("YYYYMMDDHHMMSS--"), + }; + let expected = r#"TokenInfo { + label: "Token Label", + manufacturer_id: "Manufacturer ID", + model: "Token Model", + serial_number: "Serial Number", + flags: Flags { + rng: false, + write_protected: false, + login_required: false, + user_pin_initialized: false, + restore_key_not_needed: false, + clock_on token: false, + protected_authentication_path: false, + dual_crypto_operations: false, + token_initialized: false, + secondary_authentication: false, + user_pin_count_low: false, + user_pin_final_try: false, + user_pin_locked: false, + user_pin_to_be_changed: false, + so_pin_count_low: false, + so_pin_final_try: false, + so_pin_locked: false, + so_pin_to_be_changed: false, + error_state: false, + }, + max_session_count: Some( + Some( + 100, + ), + ), + session_count: None, + max_rw_session_count: Some( + None, + ), + rw_session_count: Some( + 1, + ), + max_pin_len: 16, + min_pin_len: 4, + total_public_memory: Some( + 34359738368, + ), + free_public_memory: Some( + 1234567890, + ), + total_private_memory: None, + free_private_memory: None, + hardware_version: Version { + major: 0, + minor: 255, + }, + firmware_version: Version { + major: 255, + minor: 0, + }, + utc_time: "YYYYMMDDHHMMSS--", +}"#; + let observed = format!("{:#?}", info); + assert_eq!(observed, expected); + } +} From de46362a20f8f71e4a6988f77417089921d1779e Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 12 Nov 2021 09:45:22 -0500 Subject: [PATCH 19/36] Remove Display trait from Flag types Having a human-readable format for the flag types seemed like a good idea initially. They were more compact than the Debug equivalents and had the flexibility of rephrasing for clarity. However, the format for these outputs was somewhat arbitrary which makes them potentially unstable. But most importantly, they were for a private type and served no purpose within the crate. In order for them to be usable in client code, the Display trait would need to be implemented on the containing info type as well. Again, the arbitrariness of the output presents concerns about fragility in client code, and there's no obvious use case that Debug can't also serve. This commit will be left behind rather than squashed out in case it becomes useful to revert. Signed-off-by: Keith Koskie --- cryptoki/src/mechanism/mechanism_info.rs | 73 +----------------------- cryptoki/src/slot/slot_info.rs | 18 +----- cryptoki/src/slot/token_info.rs | 66 +-------------------- 3 files changed, 3 insertions(+), 154 deletions(-) diff --git a/cryptoki/src/mechanism/mechanism_info.rs b/cryptoki/src/mechanism/mechanism_info.rs index a5e63f04..164ebc3b 100644 --- a/cryptoki/src/mechanism/mechanism_info.rs +++ b/cryptoki/src/mechanism/mechanism_info.rs @@ -6,7 +6,7 @@ use cryptoki_sys::*; use std::fmt::Formatter; use crate::flag::{CkFlags, FlagBit}; -use std::fmt::{self, Debug, Display}; +use std::fmt::{self, Debug}; const HW: FlagBit = FlagBit::new(CKF_HW); const ENCRYPT: FlagBit = FlagBit::new(CKF_ENCRYPT); @@ -56,77 +56,6 @@ impl Debug for CkFlags { } } -// Uses active voice to indicate descriptors are actions -impl Display for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut set = f.debug_set(); - if self.contains(HW) { - let _ = set.entry(&"Hardware"); - } else { - let _ = set.entry(&"Software"); - } - - if self.contains(ENCRYPT) { - let _ = set.entry(&"Encrypts"); - } - if self.contains(DECRYPT) { - let _ = set.entry(&"Decrypts"); - } - if self.contains(DIGEST) { - let _ = set.entry(&"Digests"); - } - if self.contains(SIGN) { - let _ = set.entry(&"Signs"); - } - if self.contains(SIGN_RECOVER) { - let _ = set.entry(&"Signs into Data"); - } - if self.contains(VERIFY) { - let _ = set.entry(&"Verifies"); - } - if self.contains(VERIFY_RECOVER) { - let _ = set.entry(&"Verifies from Data"); - } - if self.contains(GENERATE) { - let _ = set.entry(&"Generates Keys"); - } - if self.contains(GENERATE_KEY_PAIR) { - let _ = set.entry(&"Generates Key Pairs"); - } - if self.contains(WRAP) { - let _ = set.entry(&"Wraps Keys"); - } - if self.contains(UNWRAP) { - let _ = set.entry(&"Unwraps Keys"); - } - if self.contains(DERIVE) { - let _ = set.entry(&"Derives Keyes"); - } - if self.contains(EXTENSION) { - let _ = set.entry(&"Flag Extensions"); - } - if self.contains(EC_F_P) { - let _ = set.entry(&"Supports ECs Over F_p"); - } - if self.contains(EC_F_2M) { - let _ = set.entry(&"Supports ECs Over F_2**m"); - } - if self.contains(EC_ECPARAMETERS) { - let _ = set.entry(&"Accepts EC as Parameters"); - } - if self.contains(EC_NAMEDCURVE) { - let _ = set.entry(&"Accepts EC by Name"); - } - if self.contains(EC_UNCOMPRESS) { - let _ = set.entry(&"Accepts Uncompressed EC Points"); - } - if self.contains(EC_COMPRESS) { - let _ = set.entry(&"Accepts Compressed EC Points"); - } - set.finish() - } -} - /// Information about a particular mechanism #[derive(Debug, Clone, Copy)] pub struct MechanismInfo { diff --git a/cryptoki/src/slot/slot_info.rs b/cryptoki/src/slot/slot_info.rs index 068c656e..2a6e54d9 100644 --- a/cryptoki/src/slot/slot_info.rs +++ b/cryptoki/src/slot/slot_info.rs @@ -5,7 +5,7 @@ use crate::flag::{CkFlags, FlagBit}; use crate::{string_from_blank_padded, types::Version}; use cryptoki_sys::*; -use std::fmt::{self, Debug, Display, Formatter}; +use std::fmt::{self, Debug, Formatter}; /// Collection of flags defined for [`CK_SLOT_INFO`] const TOKEN_PRESENT: FlagBit = FlagBit::new(CKF_TOKEN_PRESENT); @@ -22,22 +22,6 @@ impl Debug for CkFlags { } } -impl Display for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut set = f.debug_set(); - if self.contains(TOKEN_PRESENT) { - let _ = set.entry(&"Token Present"); - } - if self.contains(REMOVABLE_DEVICE) { - let _ = set.entry(&"Removable Device"); - } - if self.contains(HW_SLOT) { - let _ = set.entry(&"Hardware Slot"); - } - set.finish() - } -} - /// Information about a slot #[derive(Debug, Clone)] pub struct SlotInfo { diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index d3e97a81..9ae4fd62 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -3,7 +3,7 @@ //! PKCS11 Token info and associated flags use cryptoki_sys::*; -use std::fmt::{self, Debug, Display, Formatter}; +use std::fmt::{self, Debug, Formatter}; use crate::flag::{CkFlags, FlagBit}; use crate::string_from_blank_padded; @@ -77,70 +77,6 @@ impl Debug for CkFlags { } } -impl Display for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut set = f.debug_set(); - if self.contains(RNG) { - let _ = set.entry(&"Random Number Generator"); - } - if self.contains(WRITE_PROTECTED) { - let _ = set.entry(&"Write-Protected"); - } - if self.contains(LOGIN_REQUIRED) { - let _ = set.entry(&"Login Required"); - } - if self.contains(USER_PIN_INITIALIZED) { - let _ = set.entry(&"User Pin Initialized"); - } - if self.contains(RESTORE_KEY_NOT_NEEDED) { - let _ = set.entry(&"Restore Key Not Needed"); - } - if self.contains(CLOCK_ON_TOKEN) { - let _ = set.entry(&"Hardware Clock"); - } - if self.contains(PROTECTED_AUTHENTICATION_PATH) { - let _ = set.entry(&"Protected Authentication Path"); - } - if self.contains(DUAL_CRYPTO_OPERATIONS) { - let _ = set.entry(&"Supports Dual Crypto Operations"); - } - if self.contains(TOKEN_INITIALIZED) { - let _ = set.entry(&"Token Initialized"); - } - if self.contains(SECONDARY_AUTHENTICATION) { - let _ = set.entry(&"Supports Secondary Authentication"); - } - if self.contains(USER_PIN_COUNT_LOW) { - let _ = set.entry(&"User PIN Count Low"); - } - if self.contains(USER_PIN_FINAL_TRY) { - let _ = set.entry(&"User PIN Final Try"); - } - if self.contains(USER_PIN_LOCKED) { - let _ = set.entry(&"User PIN Locked"); - } - if self.contains(USER_PIN_TO_BE_CHANGED) { - let _ = set.entry(&"User PIN to be Changed"); - } - if self.contains(SO_PIN_COUNT_LOW) { - let _ = set.entry(&"Security Officer PIN Count Low"); - } - if self.contains(SO_PIN_FINAL_TRY) { - let _ = set.entry(&"Security Officer PIN Final Try"); - } - if self.contains(SO_PIN_LOCKED) { - let _ = set.entry(&"Security Officer PIN Locked"); - } - if self.contains(SO_PIN_TO_BE_CHANGED) { - let _ = set.entry(&"Security Officer PIN to be Changed"); - } - if self.contains(ERROR_STATE) { - let _ = set.entry(&"Error State"); - } - set.finish() - } -} - /// Information about a token #[derive(Debug, Clone)] pub struct TokenInfo { From 1b7f5202ed1e20551459fea88c6988f90ed2b98b Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 12 Nov 2021 10:00:01 -0500 Subject: [PATCH 20/36] Tidy imports Signed-off-by: Keith Koskie --- cryptoki/src/mechanism/mechanism_info.rs | 6 ++---- cryptoki/src/slot/token_info.rs | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/cryptoki/src/mechanism/mechanism_info.rs b/cryptoki/src/mechanism/mechanism_info.rs index 164ebc3b..9511fdc2 100644 --- a/cryptoki/src/mechanism/mechanism_info.rs +++ b/cryptoki/src/mechanism/mechanism_info.rs @@ -2,11 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 //! Mechanism info and associated flags -use cryptoki_sys::*; -use std::fmt::Formatter; - use crate::flag::{CkFlags, FlagBit}; -use std::fmt::{self, Debug}; +use cryptoki_sys::*; +use std::fmt::{self, Debug, Formatter}; const HW: FlagBit = FlagBit::new(CKF_HW); const ENCRYPT: FlagBit = FlagBit::new(CKF_ENCRYPT); diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index 9ae4fd62..37c8532a 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -2,12 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 //! PKCS11 Token info and associated flags -use cryptoki_sys::*; -use std::fmt::{self, Debug, Formatter}; - use crate::flag::{CkFlags, FlagBit}; use crate::string_from_blank_padded; use crate::types::{maybe_unlimited, MaybeUnavailable, Version}; +use cryptoki_sys::*; +use std::fmt::{self, Debug, Formatter}; const RNG: FlagBit = FlagBit::new(CKF_RNG); const WRITE_PROTECTED: FlagBit = FlagBit::new(CKF_WRITE_PROTECTED); From 0650deb911e8bd6f89e336cc2084da44ec8f9ee3 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Wed, 17 Nov 2021 14:41:45 -0500 Subject: [PATCH 21/36] Remove some stray comment errors Signed-off-by: Keith Koskie --- cryptoki/src/slot/token_info.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index 37c8532a..fbab9145 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -189,7 +189,7 @@ impl TokenInfo { /// True if a single session with the token can perform dual cryptographic /// operations - // TODO: Requires Session callbacks:to access + // TODO: Requires Session callbacks to access // * digest_encrypt_update // * decrypt_digest_update // * sign_encrypt_update @@ -400,7 +400,6 @@ impl TokenInfo { /// Returns None if the token is not equipped with a clock (i.e., /// `self.clock_on_token() == false`) pub fn utc_time(&self) -> Option<&str> { - // TODO Some(&self.utc_time) } } From 5dfddf8034889ad34e9384b3c4e61cfb738f508d Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 18 Nov 2021 17:48:09 -0500 Subject: [PATCH 22/36] Add UtcTime implementation Designs considered and rejected for TokenInfo::utc_time() return type * Option of 14 original non-pad characters. This option is simplest but is an annoyance to anyone who wants to do anything except print. It might even be annoying to those people too. * Option using ISO 8601 formatting. A step up in usability since it's common enough that most external tools already know how to parse it. However, it also implies additional constraints and guarantees from the ISO standard that PKCS#11 doesn't. Even with doc warnings it felt like a bad idea knowing how little validation is happening behind the scenes. * Option<(u16,u8,...)> tuple with the six parsed numerical fields. Serves the goal of not having custom types beyond necessity, but it's just too big to get away with unnamed std types only. * An Option or similar. Like ISO, appears to provide guarantees that PCKS#11 doesn't. Also, it would require assuming a specific notion of valid value ranges (e.g., January is month 0 or 1) beyond the specification that could report conformant strings as invalid. The final choice was a simple new type with minimal integral members. The members are made public so there's no need to implement getters. This is fine because mutating the struct is meaningless; it's not used as an input anywhere. The ISO formatting remains minimally present in the Display trait implementation, which provides much of the convenience of that format while weakening association with the ISO standard by not being the type's primary representation in code. Signed-off-by: Keith Koskie --- cryptoki/src/slot/token_info.rs | 52 +++++++++++---- cryptoki/src/types.rs | 115 ++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 13 deletions(-) diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index fbab9145..917a6069 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -4,7 +4,8 @@ use crate::flag::{CkFlags, FlagBit}; use crate::string_from_blank_padded; -use crate::types::{maybe_unlimited, MaybeUnavailable, Version}; +use crate::types::{convert_utc_time, maybe_unlimited}; +use crate::types::{MaybeUnavailable, UtcTime, Version}; use cryptoki_sys::*; use std::fmt::{self, Debug, Formatter}; @@ -96,7 +97,7 @@ pub struct TokenInfo { free_private_memory: Option, hardware_version: Version, firmware_version: Version, - utc_time: String, + utc_time: Option, } impl TokenInfo { @@ -395,24 +396,35 @@ impl TokenInfo { self.firmware_version } - /// The current datetime with resolution in seconds + /// The current UTC datetime reported by the token /// - /// Returns None if the token is not equipped with a clock (i.e., + /// Returns `None` if the token is not equipped with a clock (i.e., /// `self.clock_on_token() == false`) - pub fn utc_time(&self) -> Option<&str> { - Some(&self.utc_time) + /// + /// **[Conformance](crate#conformance-notes):** + /// The string representation of the datetime from the token is only + /// required to be parsable as a string of ASCII digits. No additional + /// structure (e.g., months numbered from 0 or from 1) is defined. + pub fn utc_time(&self) -> Option { + self.utc_time } } #[doc(hidden)] impl From for TokenInfo { fn from(val: CK_TOKEN_INFO) -> Self { + let flags = CkFlags::from(val.flags); + let utc_time = if flags.contains(CLOCK_ON_TOKEN) { + convert_utc_time(val.utcTime) + } else { + None + }; Self { label: string_from_blank_padded(&val.label), manufacturer_id: string_from_blank_padded(&val.manufacturerID), model: string_from_blank_padded(&val.model), serial_number: string_from_blank_padded(&val.serialNumber), - flags: CkFlags::from(val.flags), + flags, max_session_count: maybe_unlimited(val.ulMaxSessionCount), session_count: u64::maybe_unavailable(val.ulSessionCount), max_rw_session_count: maybe_unlimited(val.ulMaxRwSessionCount), @@ -425,9 +437,7 @@ impl From for TokenInfo { free_private_memory: usize::maybe_unavailable(val.ulFreePrivateMemory), hardware_version: val.hardwareVersion.into(), firmware_version: val.firmwareVersion.into(), - // UTC time is not blank padded as it has the format YYYYMMDDhhmmssxx where - // x is the '0' character - utc_time: String::from_utf8_lossy(&val.utcTime).trim_end().to_string(), + utc_time, } } } @@ -435,7 +445,7 @@ impl From for TokenInfo { #[cfg(test)] mod test { use super::TokenInfo; - use crate::{flag::CkFlags, types::Version}; + use crate::{flag::CkFlags, types::UtcTime, types::Version}; use cryptoki_sys::CK_FLAGS; #[test] @@ -514,7 +524,14 @@ mod test { free_private_memory: None, // unavailable hardware_version: Version::new(0, 255), firmware_version: Version::new(255, 0), - utc_time: String::from("YYYYMMDDHHMMSS--"), + utc_time: Some(UtcTime { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + }), }; let expected = r#"TokenInfo { label: "Token Label", @@ -572,7 +589,16 @@ mod test { major: 255, minor: 0, }, - utc_time: "YYYYMMDDHHMMSS--", + utc_time: Some( + UtcTime { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + }, + ), }"#; let observed = format!("{:#?}", info); assert_eq!(observed, expected); diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index 2a928a13..52c80069 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -6,6 +6,7 @@ use crate::error::{Error, Result}; use cryptoki_sys::*; use std::convert::TryFrom; use std::convert::TryInto; +use std::fmt; use std::fmt::Formatter; use std::ops::Deref; @@ -251,3 +252,117 @@ impl From for Version { } } } + +/// A UTC datetime returned by a token's clock if present. +#[derive(Copy, Clone, Debug)] +pub struct UtcTime { + /// **[Conformance](crate#conformance-notes):** + /// Guaranteed to be in range 0..=9999 + pub year: u16, + /// **[Conformance](crate#conformance-notes):** + /// Guaranteed to be in range 0..=99 + pub month: u8, + /// **[Conformance](crate#conformance-notes):** + /// Guaranteed to be in range 0..=99 + pub day: u8, + /// **[Conformance](crate#conformance-notes):** + /// Guaranteed to be in range 0..=99 + pub hour: u8, + /// **[Conformance](crate#conformance-notes):** + /// Guaranteed to be in range 0..=99 + pub minute: u8, + /// **[Conformance](crate#conformance-notes):** + /// Guaranteed to be in range 0..=99 + pub second: u8, +} + +/// Uses ISO 8601 format for convenience, but does not guarantee +/// or enforce any part of that standard. +impl fmt::Display for UtcTime { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", + self.year, self.month, self.day, self.hour, self.minute, self.second + ) + } +} + +// UTC time has the format YYYYMMDDhhmmss00 as ASCII digits +pub(crate) fn convert_utc_time(orig: [u8; 16]) -> Option { + // skip check of reserved padding chars + for c in orig[0..14].iter() { + if !c.is_ascii_digit() { + return None; + } + } + // Note: No validaiton of these values beyond being ASCII digits + // because PKCS#11 doesn't impose any such restrictions. + // Unwraps are safe here because of the digit check above. + Some(UtcTime { + year: std::str::from_utf8(&orig[0..4]).unwrap().parse().unwrap(), + month: std::str::from_utf8(&orig[4..6]).unwrap().parse().unwrap(), + day: std::str::from_utf8(&orig[6..8]).unwrap().parse().unwrap(), + hour: std::str::from_utf8(&orig[8..10]).unwrap().parse().unwrap(), + minute: std::str::from_utf8(&orig[10..12]).unwrap().parse().unwrap(), + second: std::str::from_utf8(&orig[12..14]).unwrap().parse().unwrap(), + }) +} + +#[cfg(test)] +mod test { + + use super::*; + const UTC_TIME: UtcTime = UtcTime { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + }; + + #[test] + fn utc_time_convert_good() { + let valid: [u8; 16] = [ + 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, + ]; + let valid = convert_utc_time(valid).unwrap(); + assert_eq!(valid.year, UTC_TIME.year); + assert_eq!(valid.month, UTC_TIME.month); + assert_eq!(valid.day, UTC_TIME.day); + assert_eq!(valid.hour, UTC_TIME.hour); + assert_eq!(valid.minute, UTC_TIME.minute); + assert_eq!(valid.second, UTC_TIME.second); + } + + #[test] + fn utc_time_convert_bad() { + let invalid: [u8; 16] = [ + 0x41, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, + ]; + let invalid = convert_utc_time(invalid); + assert!(invalid.is_none()); + } + + #[test] + fn utc_time_debug_fmt() { + let expected = r#"UtcTime { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, +}"#; + let observed = format!("{:#?}", UTC_TIME); + assert_eq!(observed, expected); + } + #[test] + fn utc_time_display_fmt() { + let display = format!("{}", UTC_TIME); + assert_eq!(&display, "1970-01-01T00:00:00Z"); + } +} From 46a26916ad9845a666034c3c39608a5106187d9a Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 18 Nov 2021 20:00:49 -0500 Subject: [PATCH 23/36] Make UtcTime conversion fallible In the initial implementation, any parsing error was silently converted to None, which indicates the token doesn't have a clock. Signed-off-by: Keith Koskie --- cryptoki/src/context/slot_token_management.rs | 4 +-- cryptoki/src/error/mod.rs | 22 ++++++++++++++ cryptoki/src/slot/token_info.rs | 13 +++++---- cryptoki/src/types.rs | 29 +++++++------------ 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/cryptoki/src/context/slot_token_management.rs b/cryptoki/src/context/slot_token_management.rs index a9f9a7b7..77df0b50 100644 --- a/cryptoki/src/context/slot_token_management.rs +++ b/cryptoki/src/context/slot_token_management.rs @@ -8,7 +8,7 @@ use crate::label_from_str; use crate::mechanism::{MechanismInfo, MechanismType}; use crate::slot::{Slot, SlotInfo, TokenInfo}; use cryptoki_sys::{CK_BBOOL, CK_MECHANISM_INFO, CK_SLOT_INFO, CK_TOKEN_INFO}; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use crate::error::RvError::BufferTooSmall; @@ -102,7 +102,7 @@ pub(super) fn get_token_info(ctx: &Pkcs11, slot: Slot) -> Result { &mut token_info, )) .into_result()?; - Ok(TokenInfo::from(token_info)) + TokenInfo::try_from(token_info) } } diff --git a/cryptoki/src/error/mod.rs b/cryptoki/src/error/mod.rs index a5f484d6..8d4652c9 100644 --- a/cryptoki/src/error/mod.rs +++ b/cryptoki/src/error/mod.rs @@ -29,6 +29,12 @@ pub enum Error { /// Error when converting a slice to an array TryFromSlice(std::array::TryFromSliceError), + /// Error when converting a numerical str to an integral value + ParseInt(core::num::ParseIntError), + + /// Error converting into a type assuming valid UTF-8 + Utf8(std::str::Utf8Error), + /// Error with nul characters in Strings NulError(std::ffi::NulError), @@ -50,6 +56,8 @@ impl fmt::Display for Error { Error::NotSupported => write!(f, "Feature not supported"), Error::TryFromInt(e) => write!(f, "Conversion between integers failed ({})", e), Error::TryFromSlice(e) => write!(f, "Error converting slice to array ({})", e), + Error::ParseInt(e) => write!(f, "Error parsing string as integer ({})", e), + Error::Utf8(e) => write!(f, "Invalid UTF-8 ({})", e), Error::NulError(e) => write!(f, "An interior nul byte was found ({})", e), Error::NullFunctionPointer => write!(f, "Calling a NULL function pointer"), Error::InvalidValue => write!(f, "The value is not one of the expected options"), @@ -64,6 +72,8 @@ impl std::error::Error for Error { Error::LibraryLoading(e) => Some(e), Error::TryFromInt(e) => Some(e), Error::TryFromSlice(e) => Some(e), + Error::ParseInt(e) => Some(e), + Error::Utf8(e) => Some(e), Error::NulError(e) => Some(e), Error::Pkcs11(_) | Error::NotSupported @@ -92,6 +102,18 @@ impl From for Error { } } +impl From for Error { + fn from(err: core::num::ParseIntError) -> Error { + Error::ParseInt(err) + } +} + +impl From for Error { + fn from(err: std::str::Utf8Error) -> Error { + Error::Utf8(err) + } +} + impl From for Error { fn from(err: std::ffi::NulError) -> Error { Error::NulError(err) diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index 917a6069..7705bf58 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 //! PKCS11 Token info and associated flags +use crate::error::{Error, Result}; use crate::flag::{CkFlags, FlagBit}; use crate::string_from_blank_padded; use crate::types::{convert_utc_time, maybe_unlimited}; use crate::types::{MaybeUnavailable, UtcTime, Version}; use cryptoki_sys::*; +use std::convert::TryFrom; use std::fmt::{self, Debug, Formatter}; const RNG: FlagBit = FlagBit::new(CKF_RNG); @@ -411,15 +413,16 @@ impl TokenInfo { } #[doc(hidden)] -impl From for TokenInfo { - fn from(val: CK_TOKEN_INFO) -> Self { +impl TryFrom for TokenInfo { + type Error = Error; + fn try_from(val: CK_TOKEN_INFO) -> Result { let flags = CkFlags::from(val.flags); let utc_time = if flags.contains(CLOCK_ON_TOKEN) { - convert_utc_time(val.utcTime) + convert_utc_time(val.utcTime)? } else { None }; - Self { + Ok(Self { label: string_from_blank_padded(&val.label), manufacturer_id: string_from_blank_padded(&val.manufacturerID), model: string_from_blank_padded(&val.model), @@ -438,7 +441,7 @@ impl From for TokenInfo { hardware_version: val.hardwareVersion.into(), firmware_version: val.firmwareVersion.into(), utc_time, - } + }) } } diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index 52c80069..e1a3814f 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -289,24 +289,17 @@ impl fmt::Display for UtcTime { } // UTC time has the format YYYYMMDDhhmmss00 as ASCII digits -pub(crate) fn convert_utc_time(orig: [u8; 16]) -> Option { - // skip check of reserved padding chars - for c in orig[0..14].iter() { - if !c.is_ascii_digit() { - return None; - } - } +pub(crate) fn convert_utc_time(orig: [u8; 16]) -> Result> { // Note: No validaiton of these values beyond being ASCII digits // because PKCS#11 doesn't impose any such restrictions. - // Unwraps are safe here because of the digit check above. - Some(UtcTime { - year: std::str::from_utf8(&orig[0..4]).unwrap().parse().unwrap(), - month: std::str::from_utf8(&orig[4..6]).unwrap().parse().unwrap(), - day: std::str::from_utf8(&orig[6..8]).unwrap().parse().unwrap(), - hour: std::str::from_utf8(&orig[8..10]).unwrap().parse().unwrap(), - minute: std::str::from_utf8(&orig[10..12]).unwrap().parse().unwrap(), - second: std::str::from_utf8(&orig[12..14]).unwrap().parse().unwrap(), - }) + Ok(Some(UtcTime { + year: std::str::from_utf8(&orig[0..4])?.parse()?, + month: std::str::from_utf8(&orig[4..6])?.parse()?, + day: std::str::from_utf8(&orig[6..8])?.parse()?, + hour: std::str::from_utf8(&orig[8..10])?.parse()?, + minute: std::str::from_utf8(&orig[10..12])?.parse()?, + second: std::str::from_utf8(&orig[12..14])?.parse()?, + })) } #[cfg(test)] @@ -328,7 +321,7 @@ mod test { 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, ]; - let valid = convert_utc_time(valid).unwrap(); + let valid = convert_utc_time(valid).unwrap().unwrap(); assert_eq!(valid.year, UTC_TIME.year); assert_eq!(valid.month, UTC_TIME.month); assert_eq!(valid.day, UTC_TIME.day); @@ -344,7 +337,7 @@ mod test { 0x30, 0x30, ]; let invalid = convert_utc_time(invalid); - assert!(invalid.is_none()); + assert!(invalid.is_err()); } #[test] From 261d934095e823aa07c7b1c2a3fabd53845fa087 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 17:54:37 -0500 Subject: [PATCH 24/36] Refactor SessionInfo Signed-off-by: Keith Koskie --- cryptoki/src/session/mod.rs | 40 +----------- cryptoki/src/session/session_info.rs | 71 ++++++++++++++++++++++ cryptoki/src/session/session_management.rs | 2 +- cryptoki/tests/basic.rs | 10 +-- 4 files changed, 80 insertions(+), 43 deletions(-) create mode 100644 cryptoki/src/session/session_info.rs diff --git a/cryptoki/src/session/mod.rs b/cryptoki/src/session/mod.rs index 3aad2718..97423120 100644 --- a/cryptoki/src/session/mod.rs +++ b/cryptoki/src/session/mod.rs @@ -6,13 +6,10 @@ use crate::context::Pkcs11; use crate::error::Result; use crate::mechanism::Mechanism; use crate::object::{Attribute, AttributeInfo, AttributeType, ObjectHandle}; -use crate::slot::Slot; -use crate::types::Ulong; use cryptoki_sys::*; use log::error; use std::collections::HashMap; -use std::convert::TryInto; use std::fmt::Formatter; use std::marker::PhantomData; use std::ops::Deref; @@ -23,10 +20,13 @@ mod flags; mod key_management; mod object_management; mod random; +mod session_info; mod session_management; mod signing_macing; mod slot_token_management; +pub use session_info::SessionInfo; + pub use flags::*; /// Type that identifies a session @@ -443,37 +443,3 @@ impl std::fmt::Display for SessionState { write!(f, "{}", SessionState::stringify(self.val)) } } - -/// Type identifying the session information -#[derive(Copy, Clone, Debug)] -pub struct SessionInfo { - val: CK_SESSION_INFO, -} - -impl SessionInfo { - pub(crate) fn new(val: CK_SESSION_INFO) -> Self { - Self { val } - } - - /// Returns an error code defined by the cryptographic device - pub fn device_error(&self) -> Ulong { - self.val.ulDeviceError.into() - } - - /// Returns the flags for this session - pub fn flags(&self) -> SessionFlags { - self.val.flags.into() - } - - /// Returns the state of the session - pub fn session_state(&self) -> SessionState { - self.val.state.into() - } - - /// Returns the slot the session is on - pub fn slot_id(&self) -> Slot { - // The unwrap should not fail as `slotID` is a `CK_SLOT_ID ` which is the same type as - // `slot_id` within the `Slot` structure - self.val.slotID.try_into().unwrap() - } -} diff --git a/cryptoki/src/session/session_info.rs b/cryptoki/src/session/session_info.rs new file mode 100644 index 00000000..ede9d062 --- /dev/null +++ b/cryptoki/src/session/session_info.rs @@ -0,0 +1,71 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +//! Session info + +use crate::{ + flag::{CkFlags, FlagBit}, + slot::Slot, +}; +use cryptoki_sys::*; +use std::fmt::{self, Debug, Formatter}; + +use super::SessionState; + +const RW_SESSION: FlagBit = FlagBit::new(CKF_RW_SESSION); +const SERIAL_SESSION: FlagBit = FlagBit::new(CKF_SERIAL_SESSION); + +impl Debug for CkFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Flags") + .field("rw_session", &(self.contains(RW_SESSION))) + .field("serial_session", &(self.contains(SERIAL_SESSION))) + .finish() + } +} + +/// Provides information about a session +#[derive(Copy, Clone, Debug)] +pub struct SessionInfo { + slot_id: Slot, + state: SessionState, + flags: CkFlags, + device_error: u64, +} + +impl SessionInfo { + /// ID of the slot that interfaces the token + pub fn slot_id(&self) -> Slot { + self.slot_id + } + + /// The state of the session + pub fn session_state(&self) -> SessionState { + self.state + } + + /// True if the session has R/W access to token objects, and false if access + /// is read-only + pub fn read_write(&self) -> bool { + self.flags.contains(RW_SESSION) + } + + /// An error code defined by the cryptographic device (used for errors not + /// covered by PKCS#11) + pub fn device_error(&self) -> u64 { + self.device_error + } +} + +#[doc(hidden)] +impl From for SessionInfo { + fn from(val: CK_SESSION_INFO) -> Self { + #[allow(trivial_numeric_casts)] + let device_error = val.ulDeviceError as u64; + Self { + slot_id: Slot::new(val.slotID), + state: val.state.into(), + flags: CkFlags::from(val.flags), + device_error, + } + } +} diff --git a/cryptoki/src/session/session_management.rs b/cryptoki/src/session/session_management.rs index 41f11612..c23b93c1 100644 --- a/cryptoki/src/session/session_management.rs +++ b/cryptoki/src/session/session_management.rs @@ -52,6 +52,6 @@ pub(super) fn get_session_info(session: &Session) -> Result { &mut session_info, )) .into_result()?; - Ok(SessionInfo::new(session_info)) + Ok(SessionInfo::from(session_info)) } } diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 3fe22f01..c097db16 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -477,7 +477,7 @@ fn get_session_info_test() -> Result<()> { { let session = pkcs11.open_session_no_callback(slot, flags)?; let session_info = session.get_session_info()?; - assert_eq!(session_info.flags(), flags); + assert!(!session_info.read_write()); assert_eq!(session_info.slot_id(), slot); assert_eq!( session_info.session_state(), @@ -486,7 +486,7 @@ fn get_session_info_test() -> Result<()> { session.login(UserType::User, Some(USER_PIN))?; let session_info = session.get_session_info()?; - assert_eq!(session_info.flags(), flags); + assert!(!session_info.read_write()); assert_eq!(session_info.slot_id(), slot); assert_eq!( session_info.session_state(), @@ -506,7 +506,7 @@ fn get_session_info_test() -> Result<()> { let session = pkcs11.open_session_no_callback(slot, flags)?; let session_info = session.get_session_info()?; - assert_eq!(session_info.flags(), flags); + assert!(session_info.read_write()); assert_eq!(session_info.slot_id(), slot); assert_eq!( session_info.session_state(), @@ -515,7 +515,7 @@ fn get_session_info_test() -> Result<()> { session.login(UserType::User, Some(USER_PIN))?; let session_info = session.get_session_info()?; - assert_eq!(session_info.flags(), flags); + assert!(session_info.read_write()); assert_eq!(session_info.slot_id(), slot); assert_eq!( session_info.session_state(), @@ -524,7 +524,7 @@ fn get_session_info_test() -> Result<()> { session.logout()?; session.login(UserType::So, Some(SO_PIN))?; let session_info = session.get_session_info()?; - assert_eq!(session_info.flags(), flags); + assert!(session_info.read_write()); assert_eq!(session_info.slot_id(), slot); assert_eq!(session_info.session_state(), SessionState::RW_SO_FUNCTIONS); From 67e758928631c68f3ed4db601df8e58dd95ad50a Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 18:59:59 -0500 Subject: [PATCH 25/36] Replace SessionFlags with bool to open session There are only two flags supported as arguments when opening a session. One of them must always be true, but perversely defaults to false. This forces client code to construct a trivial value to pass. This commit now hides this flag, setting it to its only valid value always. This also removes a single test which checked for failure when the flag was set to false. With only one flag (read/write) remaining, the open session call now accepts a boolean for the option and conversion to a wider integer type is handled internally. Signed-off-by: Keith Koskie --- cryptoki/src/context/mod.rs | 6 ++-- cryptoki/src/context/session_management.rs | 14 +++++++--- cryptoki/src/session/mod.rs | 5 +--- cryptoki/tests/basic.rs | 32 ++++++++-------------- cryptoki/tests/common.rs | 2 +- 5 files changed, 26 insertions(+), 33 deletions(-) diff --git a/cryptoki/src/context/mod.rs b/cryptoki/src/context/mod.rs index 88268423..e112ee14 100644 --- a/cryptoki/src/context/mod.rs +++ b/cryptoki/src/context/mod.rs @@ -27,7 +27,7 @@ pub use locking::*; use crate::error::{Error, Result, Rv}; use crate::mechanism::{MechanismInfo, MechanismType}; -use crate::session::{Session, SessionFlags}; +use crate::session::Session; use crate::slot::{Slot, SlotInfo, TokenInfo}; use derivative::Derivative; @@ -159,7 +159,7 @@ impl Pkcs11 { } /// Open a new session with no callback set - pub fn open_session_no_callback(&self, slot_id: Slot, flags: SessionFlags) -> Result { - session_management::open_session_no_callback(self, slot_id, flags) + pub fn open_session_no_callback(&self, slot_id: Slot, read_write: bool) -> Result { + session_management::open_session_no_callback(self, slot_id, read_write) } } diff --git a/cryptoki/src/context/session_management.rs b/cryptoki/src/context/session_management.rs index fba0b1d0..bf628312 100644 --- a/cryptoki/src/context/session_management.rs +++ b/cryptoki/src/context/session_management.rs @@ -2,25 +2,31 @@ // SPDX-License-Identifier: Apache-2.0 //! Session management functions +use cryptoki_sys::{CKF_RW_SESSION, CKF_SERIAL_SESSION}; + use crate::context::Pkcs11; use crate::error::{Result, Rv}; -use crate::session::{Session, SessionFlags}; +use crate::session::Session; use crate::slot::Slot; use std::convert::TryInto; - // See public docs on stub in parent mod.rs #[inline(always)] pub(super) fn open_session_no_callback( ctx: &Pkcs11, slot_id: Slot, - flags: SessionFlags, + read_write: bool, ) -> Result { let mut session_handle = 0; + let flags = if read_write { + CKF_SERIAL_SESSION | CKF_RW_SESSION + } else { + CKF_SERIAL_SESSION + }; unsafe { Rv::from(get_pkcs11!(ctx, C_OpenSession)( slot_id.try_into()?, - flags.into(), + flags, // TODO: abstract those types or create new functions for callbacks std::ptr::null_mut(), None, diff --git a/cryptoki/src/session/mod.rs b/cryptoki/src/session/mod.rs index 97423120..c33c1355 100644 --- a/cryptoki/src/session/mod.rs +++ b/cryptoki/src/session/mod.rs @@ -151,7 +151,6 @@ impl Session { /// use cryptoki::context::CInitializeArgs; /// use cryptoki::object::AttributeType; /// use cryptoki::session::UserType; - /// use cryptoki::session::SessionFlags; /// use std::collections::HashMap; /// use std::env; /// @@ -163,10 +162,8 @@ impl Session { /// /// pkcs11.initialize(CInitializeArgs::OsThreads).unwrap(); /// let slot = pkcs11.get_slots_with_token().unwrap().remove(0); - /// let mut flags = SessionFlags::new(); - /// let _ = flags.set_rw_session(true).set_serial_session(true); /// - /// let session = pkcs11.open_session_no_callback(slot, flags).unwrap(); + /// let session = pkcs11.open_session_no_callback(slot, true).unwrap(); /// session.login(UserType::User, Some("fedcba")); /// /// let empty_attrib= vec![]; diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index c097db16..b7b26b89 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -33,7 +33,7 @@ fn sign_verify() -> Result<()> { let _ = flags.set_rw_session(true).set_serial_session(true); // open a session - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, true)?; // log in the session session.login(UserType::User, Some(USER_PIN))?; @@ -85,7 +85,7 @@ fn encrypt_decrypt() -> Result<()> { let _ = flags.set_rw_session(true).set_serial_session(true); // open a session - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, true)?; // log in the session session.login(UserType::User, Some(USER_PIN))?; @@ -141,7 +141,7 @@ fn derive_key() -> Result<()> { let _ = flags.set_rw_session(true).set_serial_session(true); // open a session - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, true)?; // log in the session session.login(UserType::User, Some(USER_PIN))?; @@ -236,7 +236,7 @@ fn import_export() -> Result<()> { let _ = flags.set_rw_session(true).set_serial_session(true); // open a session - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, true)?; // log in the session session.login(UserType::User, Some(USER_PIN))?; @@ -306,7 +306,7 @@ fn wrap_and_unwrap_key() { let _ = flags.set_rw_session(true).set_serial_session(true); // open a session - let session = pkcs11.open_session_no_callback(slot, flags).unwrap(); + let session = pkcs11.open_session_no_callback(slot, true).unwrap(); // log in the session session.login(UserType::User, Some(USER_PIN)).unwrap(); @@ -400,7 +400,7 @@ fn login_feast() { for _ in 0..SESSIONS { let pkcs11 = pkcs11.clone(); threads.push(thread::spawn(move || { - let session = pkcs11.open_session_no_callback(slot, flags).unwrap(); + let session = pkcs11.open_session_no_callback(slot, true).unwrap(); match session.login(UserType::User, Some(USER_PIN)) { Ok(_) | Err(Error::Pkcs11(RvError::UserAlreadyLoggedIn)) => {} Err(e) => panic!("Bad error response: {}", e), @@ -463,19 +463,9 @@ fn get_session_info_test() -> Result<()> { let (pkcs11, slot) = init_pins(); let mut flags = SessionFlags::new(); - - // Check that OpenSession errors when CKF_SERIAL_SESSION is not set - if let Err(cryptoki::error::Error::Pkcs11(rv_error)) = - pkcs11.open_session_no_callback(slot, flags) - { - assert_eq!(rv_error, RvError::SessionParallelNotSupported); - } else { - panic!("Should error when CKF_SERIAL_SESSION is not set"); - } - let _ = flags.set_serial_session(true); { - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, false)?; let session_info = session.get_session_info()?; assert!(!session_info.read_write()); assert_eq!(session_info.slot_id(), slot); @@ -504,7 +494,7 @@ fn get_session_info_test() -> Result<()> { let _ = flags.set_rw_session(true); - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, true)?; let session_info = session.get_session_info()?; assert!(session_info.read_write()); assert_eq!(session_info.slot_id(), slot); @@ -539,7 +529,7 @@ fn generate_random_test() -> Result<()> { let mut flags = SessionFlags::new(); let _ = flags.set_serial_session(true); - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, false)?; let poor_seed: [u8; 32] = [0; 32]; session.seed_random(&poor_seed)?; @@ -566,7 +556,7 @@ fn set_pin_test() -> Result<()> { let mut flags = SessionFlags::new(); let _ = flags.set_serial_session(true).set_rw_session(true); - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, true)?; session.login(UserType::User, Some(USER_PIN))?; session.set_pin(USER_PIN, new_user_pin)?; @@ -585,7 +575,7 @@ fn get_attribute_info_test() -> Result<()> { let _ = flags.set_rw_session(true).set_serial_session(true); // open a session - let session = pkcs11.open_session_no_callback(slot, flags)?; + let session = pkcs11.open_session_no_callback(slot, true)?; // log in the session session.login(UserType::User, Some(USER_PIN))?; diff --git a/cryptoki/tests/common.rs b/cryptoki/tests/common.rs index 20e83047..e03420a6 100644 --- a/cryptoki/tests/common.rs +++ b/cryptoki/tests/common.rs @@ -32,7 +32,7 @@ pub fn init_pins() -> (Pkcs11, Slot) { { // open a session - let session = pkcs11.open_session_no_callback(slot, flags).unwrap(); + let session = pkcs11.open_session_no_callback(slot, true).unwrap(); // log in the session session.login(UserType::So, Some(SO_PIN)).unwrap(); session.init_pin(USER_PIN).unwrap(); From e221cf667f3c6452afe0f7d7cdc96edd1cd2e763 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 19:23:37 -0500 Subject: [PATCH 26/36] Remove unused SessionFlags type This type is no longer used after the previous two commits. Signed-off-by: Keith Koskie --- cryptoki/src/session/flags.rs | 89 ----------------------------------- cryptoki/src/session/mod.rs | 3 -- cryptoki/tests/basic.rs | 41 +--------------- cryptoki/tests/common.rs | 5 -- 4 files changed, 1 insertion(+), 137 deletions(-) delete mode 100644 cryptoki/src/session/flags.rs diff --git a/cryptoki/src/session/flags.rs b/cryptoki/src/session/flags.rs deleted file mode 100644 index 27f5af9d..00000000 --- a/cryptoki/src/session/flags.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2021 Contributors to the Parsec project. -// SPDX-License-Identifier: Apache-2.0 -//! PKCS11 General Data Types - -use crate::types::Flags; -use cryptoki_sys::*; -use std::fmt::Formatter; - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -/// Collection of flags defined for [`CK_SESSION_INFO`] -pub struct SessionFlags { - flags: CK_FLAGS, -} - -impl Flags for SessionFlags { - type FlagType = CK_FLAGS; - - fn flag_value(&self) -> Self::FlagType { - self.flags - } - - fn flag(&self, flag: Self::FlagType) -> bool { - self.flag_value() & flag == flag - } - - fn set_flag(&mut self, flag: Self::FlagType, b: bool) { - if b { - self.flags |= flag; - } else { - self.flags &= !flag; - } - } - - fn stringify_flag(flag: Self::FlagType) -> &'static str { - match flag { - CKF_RW_SESSION => std::stringify!(CKF_RW_SESSION), - CKF_SERIAL_SESSION => std::stringify!(CKF_SERIAL_SESSION), - _ => "Unknown session flag", - } - } -} - -impl SessionFlags { - /// Creates a new instance of `SessionFlags` with no flags set - pub fn new() -> Self { - SessionFlags::default() - } - - /// Gets value of [`CKF_RW_SESSION`] - pub fn rw_session(&self) -> bool { - self.flag(CKF_RW_SESSION) - } - - /// Sets value of [`CKF_RW_SESSION`] - pub fn set_rw_session(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_RW_SESSION, b); - self - } - - /// Gets value of [`CKF_SERIAL_SESSION`] - pub fn serial_session(&self) -> bool { - self.flag(CKF_SERIAL_SESSION) - } - - /// Sets value of [`CKF_SERIAL_SESSION`] - pub fn set_serial_session(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_SERIAL_SESSION, b); - self - } -} - -impl std::fmt::Display for SessionFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let flags = vec![CKF_RW_SESSION, CKF_SERIAL_SESSION]; - self.stringify_fmt(f, flags) - } -} - -impl From for CK_FLAGS { - fn from(flags: SessionFlags) -> Self { - flags.flags - } -} - -impl From for SessionFlags { - fn from(flags: CK_FLAGS) -> Self { - Self { flags } - } -} diff --git a/cryptoki/src/session/mod.rs b/cryptoki/src/session/mod.rs index c33c1355..2ed711ea 100644 --- a/cryptoki/src/session/mod.rs +++ b/cryptoki/src/session/mod.rs @@ -16,7 +16,6 @@ use std::ops::Deref; mod decryption; mod encryption; -mod flags; mod key_management; mod object_management; mod random; @@ -27,8 +26,6 @@ mod slot_token_management; pub use session_info::SessionInfo; -pub use flags::*; - /// Type that identifies a session /// /// It will automatically get closed (and logout) on drop. diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index b7b26b89..4d11b56c 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -7,7 +7,7 @@ use common::init_pins; use cryptoki::error::{Error, RvError}; use cryptoki::mechanism::Mechanism; use cryptoki::object::{Attribute, AttributeInfo, AttributeType, KeyType, ObjectClass}; -use cryptoki::session::{SessionFlags, SessionState, UserType}; +use cryptoki::session::{SessionState, UserType}; use serial_test::serial; use std::collections::HashMap; use std::thread; @@ -28,10 +28,6 @@ type Result = std::result::Result; fn sign_verify() -> Result<()> { let (pkcs11, slot) = init_pins(); - // set flags - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - // open a session let session = pkcs11.open_session_no_callback(slot, true)?; @@ -80,10 +76,6 @@ fn sign_verify() -> Result<()> { fn encrypt_decrypt() -> Result<()> { let (pkcs11, slot) = init_pins(); - // set flags - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - // open a session let session = pkcs11.open_session_no_callback(slot, true)?; @@ -136,10 +128,6 @@ fn encrypt_decrypt() -> Result<()> { fn derive_key() -> Result<()> { let (pkcs11, slot) = init_pins(); - // set flags - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - // open a session let session = pkcs11.open_session_no_callback(slot, true)?; @@ -231,10 +219,6 @@ fn derive_key() -> Result<()> { fn import_export() -> Result<()> { let (pkcs11, slot) = init_pins(); - // set flags - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - // open a session let session = pkcs11.open_session_no_callback(slot, true)?; @@ -301,10 +285,6 @@ fn get_token_info() -> Result<()> { #[serial] fn wrap_and_unwrap_key() { let (pkcs11, slot) = init_pins(); - // set flags - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - // open a session let session = pkcs11.open_session_no_callback(slot, true).unwrap(); @@ -390,11 +370,6 @@ fn login_feast() { const SESSIONS: usize = 100; let (pkcs11, slot) = init_pins(); - - // set flags - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - let mut threads = Vec::new(); for _ in 0..SESSIONS { @@ -461,9 +436,6 @@ fn get_slot_info_test() -> Result<()> { #[serial] fn get_session_info_test() -> Result<()> { let (pkcs11, slot) = init_pins(); - - let mut flags = SessionFlags::new(); - let _ = flags.set_serial_session(true); { let session = pkcs11.open_session_no_callback(slot, false)?; let session_info = session.get_session_info()?; @@ -492,8 +464,6 @@ fn get_session_info_test() -> Result<()> { } } - let _ = flags.set_rw_session(true); - let session = pkcs11.open_session_no_callback(slot, true)?; let session_info = session.get_session_info()?; assert!(session_info.read_write()); @@ -526,9 +496,6 @@ fn get_session_info_test() -> Result<()> { fn generate_random_test() -> Result<()> { let (pkcs11, slot) = init_pins(); - let mut flags = SessionFlags::new(); - - let _ = flags.set_serial_session(true); let session = pkcs11.open_session_no_callback(slot, false)?; let poor_seed: [u8; 32] = [0; 32]; @@ -553,9 +520,6 @@ fn set_pin_test() -> Result<()> { let new_user_pin = "123456"; let (pkcs11, slot) = init_pins(); - let mut flags = SessionFlags::new(); - - let _ = flags.set_serial_session(true).set_rw_session(true); let session = pkcs11.open_session_no_callback(slot, true)?; session.login(UserType::User, Some(USER_PIN))?; @@ -571,9 +535,6 @@ fn set_pin_test() -> Result<()> { fn get_attribute_info_test() -> Result<()> { let (pkcs11, slot) = init_pins(); - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - // open a session let session = pkcs11.open_session_no_callback(slot, true)?; diff --git a/cryptoki/tests/common.rs b/cryptoki/tests/common.rs index e03420a6..2b3f334f 100644 --- a/cryptoki/tests/common.rs +++ b/cryptoki/tests/common.rs @@ -1,7 +1,6 @@ // Copyright 2021 Contributors to the Parsec project. // SPDX-License-Identifier: Apache-2.0 use cryptoki::context::{CInitializeArgs, Pkcs11}; -use cryptoki::session::SessionFlags; use cryptoki::session::UserType; use cryptoki::slot::Slot; use std::env; @@ -26,10 +25,6 @@ pub fn init_pins() -> (Pkcs11, Slot) { pkcs11.init_token(slot, SO_PIN, "Test Token").unwrap(); - // set flags - let mut flags = SessionFlags::new(); - let _ = flags.set_rw_session(true).set_serial_session(true); - { // open a session let session = pkcs11.open_session_no_callback(slot, true).unwrap(); From 992bfbff5a65009934e9172f452b221f0ce863cd Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 20:39:50 -0500 Subject: [PATCH 27/36] Refactor SessionState This type is now an rust enum which better matches the type's intended behavior. Signed-off-by: Keith Koskie --- cryptoki/src/session/mod.rs | 74 +--------------------- cryptoki/src/session/session_info.rs | 56 +++++++++++++--- cryptoki/src/session/session_management.rs | 4 +- cryptoki/tests/basic.rs | 27 ++++---- 4 files changed, 61 insertions(+), 100 deletions(-) diff --git a/cryptoki/src/session/mod.rs b/cryptoki/src/session/mod.rs index 2ed711ea..11fba825 100644 --- a/cryptoki/src/session/mod.rs +++ b/cryptoki/src/session/mod.rs @@ -12,7 +12,6 @@ use log::error; use std::collections::HashMap; use std::fmt::Formatter; use std::marker::PhantomData; -use std::ops::Deref; mod decryption; mod encryption; @@ -24,7 +23,7 @@ mod session_management; mod signing_macing; mod slot_token_management; -pub use session_info::SessionInfo; +pub use session_info::{SessionInfo, SessionState}; /// Type that identifies a session /// @@ -366,74 +365,3 @@ impl From for CK_USER_TYPE { } } } - -/// Represents the state of a session -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SessionState { - val: CK_STATE, -} - -impl SessionState { - /// Read-only public session - pub const RO_PUBLIC_SESSION: SessionState = SessionState { - val: CKS_RO_PUBLIC_SESSION, - }; - - /// Read-only user access - pub const RO_USER_FUNCTIONS: SessionState = SessionState { - val: CKS_RO_USER_FUNCTIONS, - }; - - /// Read/write public session - pub const RW_PUBLIC_SESSION: SessionState = SessionState { - val: CKS_RW_PUBLIC_SESSION, - }; - - /// Read/write user access - pub const RW_USER_FUNCTIONS: SessionState = SessionState { - val: CKS_RW_USER_FUNCTIONS, - }; - - /// Read/write SO access - pub const RW_SO_FUNCTIONS: SessionState = SessionState { - val: CKS_RW_SO_FUNCTIONS, - }; - - /// Stringifies the value of a [CK_STATE] - pub(crate) fn stringify(state: CK_STATE) -> &'static str { - match state { - CKS_RO_PUBLIC_SESSION => stringify!(CKS_RO_PUBLIC_SESSION), - CKS_RO_USER_FUNCTIONS => stringify!(CKS_RO_USER_FUNCTIONS), - CKS_RW_PUBLIC_SESSION => stringify!(CKS_RW_PUBLIC_SESSION), - CKS_RW_USER_FUNCTIONS => stringify!(CKS_RW_USER_FUNCTIONS), - CKS_RW_SO_FUNCTIONS => stringify!(CKS_RW_SO_FUNCTIONS), - _ => "Unknown state value", - } - } -} - -impl Deref for SessionState { - type Target = CK_STATE; - - fn deref(&self) -> &Self::Target { - &self.val - } -} - -impl From for CK_STATE { - fn from(session_state: SessionState) -> Self { - *session_state - } -} - -impl From for SessionState { - fn from(val: CK_STATE) -> Self { - Self { val } - } -} - -impl std::fmt::Display for SessionState { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", SessionState::stringify(self.val)) - } -} diff --git a/cryptoki/src/session/session_info.rs b/cryptoki/src/session/session_info.rs index ede9d062..a2fc9b40 100644 --- a/cryptoki/src/session/session_info.rs +++ b/cryptoki/src/session/session_info.rs @@ -2,15 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 //! Session info -use crate::{ - flag::{CkFlags, FlagBit}, - slot::Slot, -}; +use crate::error::{Error, Result}; +use crate::flag::{CkFlags, FlagBit}; +use crate::slot::Slot; use cryptoki_sys::*; +use std::convert::{TryFrom, TryInto}; use std::fmt::{self, Debug, Formatter}; -use super::SessionState; - const RW_SESSION: FlagBit = FlagBit::new(CKF_RW_SESSION); const SERIAL_SESSION: FlagBit = FlagBit::new(CKF_SERIAL_SESSION); @@ -57,15 +55,53 @@ impl SessionInfo { } #[doc(hidden)] -impl From for SessionInfo { - fn from(val: CK_SESSION_INFO) -> Self { +impl TryFrom for SessionInfo { + type Error = Error; + fn try_from(val: CK_SESSION_INFO) -> Result { #[allow(trivial_numeric_casts)] let device_error = val.ulDeviceError as u64; - Self { + Ok(Self { slot_id: Slot::new(val.slotID), - state: val.state.into(), + state: val.state.try_into()?, flags: CkFlags::from(val.flags), device_error, + }) + } +} + +/// The current state of the session which describes access to token and session +/// obects based on user type and login status +#[derive(Debug, Copy, Clone)] +pub enum SessionState { + /// The session has read-only access to public token objects and R/W access + /// to to public session objects + RoPublic, + /// A normal user has been authenticated to the token. + /// The session has read-only access to all public and private token objects + /// The session has R/W access to all public and private session objects + RoUser, + /// The session has read/write access to all public objects + RwPublic, + /// A normal user has been authenticated to the token. + /// The session has read/write access to all objects + RwUser, + /// A security officer (SO) user has been authenticated to the token. + /// The session has R/W access only to public token objects. The SO + /// can set the normal user's PIN. + RwSecurityOfficer, +} + +#[doc(hidden)] +impl TryFrom for SessionState { + type Error = Error; + fn try_from(value: CK_STATE) -> Result { + match value { + CKS_RO_PUBLIC_SESSION => Ok(Self::RoPublic), + CKS_RO_USER_FUNCTIONS => Ok(Self::RoUser), + CKS_RW_PUBLIC_SESSION => Ok(Self::RwPublic), + CKS_RW_USER_FUNCTIONS => Ok(Self::RwUser), + CKS_RW_SO_FUNCTIONS => Ok(Self::RwSecurityOfficer), + _ => Err(Error::InvalidValue), } } } diff --git a/cryptoki/src/session/session_management.rs b/cryptoki/src/session/session_management.rs index c23b93c1..ee4d8720 100644 --- a/cryptoki/src/session/session_management.rs +++ b/cryptoki/src/session/session_management.rs @@ -5,7 +5,7 @@ use crate::error::{Result, Rv}; use crate::session::{Session, SessionInfo, UserType}; use cryptoki_sys::CK_SESSION_INFO; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; // See public docs on close() in parent mod.rs #[inline(always)] @@ -52,6 +52,6 @@ pub(super) fn get_session_info(session: &Session) -> Result { &mut session_info, )) .into_result()?; - Ok(SessionInfo::from(session_info)) + SessionInfo::try_from(session_info) } } diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 4d11b56c..e0f9155c 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -441,19 +441,16 @@ fn get_session_info_test() -> Result<()> { let session_info = session.get_session_info()?; assert!(!session_info.read_write()); assert_eq!(session_info.slot_id(), slot); - assert_eq!( + assert!(matches!( session_info.session_state(), - SessionState::RO_PUBLIC_SESSION - ); + SessionState::RoPublic + )); session.login(UserType::User, Some(USER_PIN))?; let session_info = session.get_session_info()?; assert!(!session_info.read_write()); assert_eq!(session_info.slot_id(), slot); - assert_eq!( - session_info.session_state(), - SessionState::RO_USER_FUNCTIONS - ); + assert!(matches!(session_info.session_state(), SessionState::RoUser)); session.logout()?; if let Err(cryptoki::error::Error::Pkcs11(rv_error)) = session.login(UserType::So, Some(SO_PIN)) @@ -468,25 +465,25 @@ fn get_session_info_test() -> Result<()> { let session_info = session.get_session_info()?; assert!(session_info.read_write()); assert_eq!(session_info.slot_id(), slot); - assert_eq!( + assert!(matches!( session_info.session_state(), - SessionState::RW_PUBLIC_SESSION - ); + SessionState::RwPublic + )); session.login(UserType::User, Some(USER_PIN))?; let session_info = session.get_session_info()?; assert!(session_info.read_write()); assert_eq!(session_info.slot_id(), slot); - assert_eq!( - session_info.session_state(), - SessionState::RW_USER_FUNCTIONS - ); + assert!(matches!(session_info.session_state(), SessionState::RwUser,)); session.logout()?; session.login(UserType::So, Some(SO_PIN))?; let session_info = session.get_session_info()?; assert!(session_info.read_write()); assert_eq!(session_info.slot_id(), slot); - assert_eq!(session_info.session_state(), SessionState::RW_SO_FUNCTIONS); + assert!(matches!( + session_info.session_state(), + SessionState::RwSecurityOfficer + )); Ok(()) } From d4917a8c20a80623c9ab72ebacbcb44e601d783f Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 21:09:09 -0500 Subject: [PATCH 28/36] Remove InitializeFlags This is only two flags, one of which isn't supported yet, and the structure itself was effectively private despite having pub visibility. Because the initialization APIs need considerable attention outside the scope of this branch, the change avoids any elaborate redesign and just falls back to using raw CK_FLAGS. Because this is the last use of the Flags trait, it is also removed. Signed-off-by: Keith Koskie --- cryptoki/src/context/flags.rs | 99 --------------------------------- cryptoki/src/context/locking.rs | 9 +-- cryptoki/src/context/mod.rs | 2 - cryptoki/src/types.rs | 26 --------- 4 files changed, 5 insertions(+), 131 deletions(-) delete mode 100644 cryptoki/src/context/flags.rs diff --git a/cryptoki/src/context/flags.rs b/cryptoki/src/context/flags.rs deleted file mode 100644 index e1ed47a8..00000000 --- a/cryptoki/src/context/flags.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2021 Contributors to the Parsec project. -// SPDX-License-Identifier: Apache-2.0 -//! PKCS11 flags for use with CInitializeArgs - -use crate::error::{Error, Result}; -use crate::types::Flags; -use cryptoki_sys::*; -use std::convert::TryFrom; -use std::fmt::Formatter; - -#[derive(Debug, Default, Clone, Copy)] -/// Collection of flags defined for [`CK_C_INITIALIZE_ARGS`] -pub struct InitializeFlags { - flags: CK_FLAGS, -} - -impl Flags for InitializeFlags { - type FlagType = CK_FLAGS; - - fn flag_value(&self) -> Self::FlagType { - self.flags - } - - fn flag(&self, flag: Self::FlagType) -> bool { - self.flag_value() & flag == flag - } - - fn set_flag(&mut self, flag: Self::FlagType, b: bool) { - if b { - self.flags |= flag; - } else { - self.flags &= !flag; - } - } - - fn stringify_flag(flag: CK_FLAGS) -> &'static str { - match flag { - CKF_LIBRARY_CANT_CREATE_OS_THREADS => { - std::stringify!(CKF_LIBRARY_CANT_CREATE_OS_THREADS) - } - CKF_OS_LOCKING_OK => std::stringify!(CKF_OS_LOCKING_OK), - _ => "Unknown CK_C_INITIALIZE_ARGS flag", - } - } -} - -impl InitializeFlags { - /// Creates a new instance of `InitializeFlags` with no flags set - pub fn new() -> Self { - InitializeFlags::default() - } - - /// Gets value of [`CKF_LIBRARY_CANT_CREATE_OS_THREADS`] - pub fn library_cant_create_os_threads(&self) -> bool { - self.flag(CKF_LIBRARY_CANT_CREATE_OS_THREADS) - } - - /// Sets value of [`CKF_LIBRARY_CANT_CREATE_OS_THREADS`] - pub fn set_library_cant_create_os_threads(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_LIBRARY_CANT_CREATE_OS_THREADS, b); - self - } - - /// Gets value of [`CKF_OS_LOCKING_OK`] - pub fn os_locking_ok(&self) -> bool { - self.flag(CKF_OS_LOCKING_OK) - } - - /// Sets value of [`CKF_OS_LOCKING_OK`] - pub fn set_os_locking_ok(&mut self, b: bool) -> &mut Self { - self.set_flag(CKF_OS_LOCKING_OK, b); - self - } -} - -impl std::fmt::Display for InitializeFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let flags = vec![CKF_LIBRARY_CANT_CREATE_OS_THREADS, CKF_OS_LOCKING_OK]; - self.stringify_fmt(f, flags) - } -} - -impl From for CK_FLAGS { - fn from(flags: InitializeFlags) -> Self { - flags.flags - } -} - -impl TryFrom for InitializeFlags { - type Error = Error; - - fn try_from(flags: CK_FLAGS) -> Result { - if flags & !(CKF_OS_LOCKING_OK | CKF_LIBRARY_CANT_CREATE_OS_THREADS) != 0 { - Err(Error::InvalidValue) - } else { - Ok(Self { flags }) - } - } -} diff --git a/cryptoki/src/context/locking.rs b/cryptoki/src/context/locking.rs index 2c0093ad..4f9d0cca 100644 --- a/cryptoki/src/context/locking.rs +++ b/cryptoki/src/context/locking.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 //! Locking related type -use super::InitializeFlags; +use cryptoki_sys::{CKF_OS_LOCKING_OK, CK_FLAGS}; + use std::ptr; /// Argument for the initialize function @@ -16,12 +17,12 @@ pub enum CInitializeArgs { impl From for cryptoki_sys::CK_C_INITIALIZE_ARGS { fn from(c_initialize_args: CInitializeArgs) -> Self { - let mut flags = InitializeFlags::default(); + let mut flags = CK_FLAGS::default(); match c_initialize_args { CInitializeArgs::OsThreads => { - let _ = flags.set_os_locking_ok(true); + flags |= CKF_OS_LOCKING_OK; Self { - flags: flags.into(), + flags, CreateMutex: None, DestroyMutex: None, LockMutex: None, diff --git a/cryptoki/src/context/mod.rs b/cryptoki/src/context/mod.rs index e112ee14..62e1ba1d 100644 --- a/cryptoki/src/context/mod.rs +++ b/cryptoki/src/context/mod.rs @@ -13,7 +13,6 @@ macro_rules! get_pkcs11 { }; } -mod flags; mod general_purpose; mod info; mod locking; @@ -21,7 +20,6 @@ mod session_management; mod slot_token_management; use cryptoki_sys::{CK_FALSE, CK_TRUE}; -pub use flags::*; pub use info::*; pub use locking::*; diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index e1a3814f..551d9ba7 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -10,32 +10,6 @@ use std::fmt; use std::fmt::Formatter; use std::ops::Deref; -pub(crate) trait Flags: std::fmt::Display { - type FlagType: Copy; - - fn flag_value(&self) -> Self::FlagType; - - fn flag(&self, flag: Self::FlagType) -> bool; - - fn set_flag(&mut self, flag: Self::FlagType, b: bool); - - fn stringify_flag(flag: Self::FlagType) -> &'static str; - - fn stringify_fmt(&self, f: &mut Formatter<'_>, flags: Vec) -> std::fmt::Result { - let mut first_done = false; - for flag in flags.iter() { - if self.flag(*flag) { - if first_done { - write!(f, ", ")?; - } - write!(f, "{}", Self::stringify_flag(*flag))?; - first_done = true; - } - } - Ok(()) - } -} - #[derive(Debug, Copy, Clone)] #[repr(transparent)] /// Value that represents a date From 1cbad0528ada0b80a8a06c194bc9b49577efef17 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 22:01:06 -0500 Subject: [PATCH 29/36] Replace Display for UtcTime with named function Suggested during review to both improve discoverability and make the non-association with ISO more explicit. Signed-off-by: Keith Koskie --- cryptoki/src/types.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index 551d9ba7..6e2365e1 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -6,7 +6,6 @@ use crate::error::{Error, Result}; use cryptoki_sys::*; use std::convert::TryFrom; use std::convert::TryInto; -use std::fmt; use std::fmt::Formatter; use std::ops::Deref; @@ -250,12 +249,17 @@ pub struct UtcTime { pub second: u8, } -/// Uses ISO 8601 format for convenience, but does not guarantee -/// or enforce any part of that standard. -impl fmt::Display for UtcTime { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, +impl UtcTime { + /// Stringify the structure in ISO 8601 format. + /// + /// PKCS#11 and ISO are unrelated standards, and this funciton is provided + /// only for convenience. ISO format is more widely recognized and parsable + /// by various date/time utilities, while PKCS#11's internal representation + /// of this type is is not used elsewhere. + /// Other than formatting, this crate does not guarantee or enforce any part + /// of the ISO standard. + pub fn as_iso8601_string(&self) -> String { + format!( "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", self.year, self.month, self.day, self.hour, self.minute, self.second ) @@ -329,7 +333,7 @@ mod test { } #[test] fn utc_time_display_fmt() { - let display = format!("{}", UTC_TIME); - assert_eq!(&display, "1970-01-01T00:00:00Z"); + let iso_format = UTC_TIME.as_iso8601_string(); + assert_eq!(&iso_format, "1970-01-01T00:00:00Z"); } } From 7ffa71111582ebcbd4ef807b9707944cfc7bdf7e Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 23:25:36 -0500 Subject: [PATCH 30/36] Replace custom CkFlags impl with bitflags crate The CkFlags type was written under the assumption that it would be part of the public API and would benefit from being bound (via generics) to the struct that contained each flag group. But the final API ended up keeping flags types private. With full control against incorrect usage being internal to the crate, CkFlags offers no benefit over bitflags, which is well established for this use case. bitflags auto-implements the Debug trait for its structures, which led to the corresponding tests being updated or deleted. Signed-off-by: Keith Koskie --- cryptoki/Cargo.toml | 1 + cryptoki/src/flag.rs | 319 ----------------------- cryptoki/src/lib.rs | 1 - cryptoki/src/mechanism/mechanism_info.rs | 202 ++++---------- cryptoki/src/session/session_info.rs | 58 ++++- cryptoki/src/slot/slot_info.rs | 67 ++--- cryptoki/src/slot/token_info.rs | 226 +++++----------- 7 files changed, 181 insertions(+), 693 deletions(-) delete mode 100644 cryptoki/src/flag.rs diff --git a/cryptoki/Cargo.toml b/cryptoki/Cargo.toml index 74ed658a..2bb286f8 100644 --- a/cryptoki/Cargo.toml +++ b/cryptoki/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/parallaxsecond/rust-cryptoki" documentation = "https://docs.rs/crate/cryptoki" [dependencies] +bitflags = "1.3" libloading = "0.7.0" log = "0.4.14" derivative = "2.2.0" diff --git a/cryptoki/src/flag.rs b/cryptoki/src/flag.rs deleted file mode 100644 index bb37ac63..00000000 --- a/cryptoki/src/flag.rs +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2021 Contributors to the Parsec project. -// SPDX-License-Identifier: Apache-2.0 - -//! Generic abstraction of CK_FLAGS for specialization in other modules -//! -//! Generate a bit flag set bound to a type definition, T. T has no -//! function beyond providing a compile-time bounds check and is only -//! present as phantom data. -//! -//! These definitions also divide the roles of a flag accumulator -//! (`BitFlags`) from a single, constant bit that contributes to it -//! ('FlagBit`) or that bit's inverse (`FlagMask`) -//! -//! The following fundamental operations are defined. -//! -//! * Flags traits: From CK_FLAGS, Deref and DerefMut as CK_FLAGS -//! * !Bit -> Mask (ones compliment) -//! * Flags |= Bit -//! * Flags &= Mask -//! * Flags ^= Bit -//! * Bit | Bit -//! * Mask & Mask -//! * Flags.contains(Bit) -//! -//! Imporantly, Bit types cannot* be assigned to, but can be composed -//! for assignment to the associated accumulator type. Likewise, two -//! accumulators can't be composed with each other. Only the accumulator -//! type can be converted to/from CK_FLAGS for use in FFI operations. -//! -//! *Technically false. The binary operations resolve to a bit type, not -//! an accumulator. If assigned to an itermediate variable, a multi-bit -//! type can be constructed. There is also no enforcement of the assumption -//! that FlagBit only has a single set bit. The lack of assign-op operators -//! is intended to discourage this, though, and the type isn't pub to be -//! abused anyway. -//! -//! The following are intended use patterns. -//! -//! * Set: Flags |= Bit -//! * Set multiple: Flags |= Bit | Bit | ... -//! * Unset: Flags &= !Bit -//! * Unset multiple: Flags &= !(Bit | Bit | ...) -//! * Toggle: Flags ^= Bit -//! * Toggle mutliple: Flags ^= Bit | Bit | ... -//! * Test: Flags.contains(Bit) -> bool -//! * Test (ALL): Flags.contains(Bit | Bit | ...) -> bool -use cryptoki_sys::CK_FLAGS; -use std::marker::PhantomData; -use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXorAssign, Deref, DerefMut, Not}; - -/// A type representing a 1-bit flag constant or some composition thereof -#[repr(transparent)] -pub(crate) struct FlagBit { - value: CK_FLAGS, - _type: PhantomData, -} - -/// A type representing the one's compliment of a FlagBit -#[repr(transparent)] -pub(crate) struct FlagMask { - value: CK_FLAGS, - _type: PhantomData, -} - -/// An accumulator type representing the flag state of a T instance -#[derive(Default)] -#[repr(transparent)] -pub(crate) struct CkFlags { - value: CK_FLAGS, - _type: PhantomData, -} - -// Must hand write Clone and Copy traits because the derived -// versions assume T to be Copy and constrain the impl accordingly. -// In general, we'll have non-Copy Ts and want to avoid this. -impl Copy for FlagBit {} -impl Clone for FlagBit { - fn clone(&self) -> Self { - Self { - value: self.value, - _type: PhantomData, - } - } -} -impl Copy for FlagMask {} -impl Clone for FlagMask { - fn clone(&self) -> Self { - Self { - value: self.value, - _type: PhantomData, - } - } -} -impl Copy for CkFlags {} -impl Clone for CkFlags { - fn clone(&self) -> Self { - Self { - value: self.value, - _type: PhantomData, - } - } -} - -/// Const constructor for defined CKF_* constants -impl FlagBit { - pub(crate) const fn new(value: CK_FLAGS) -> Self { - Self { - value, - _type: PhantomData, - } - } -} - -/// Bit | Bit -> Bit -impl BitOr for FlagBit { - type Output = FlagBit; - fn bitor(self, rhs: Self) -> Self::Output { - Self { - value: self.value | rhs.value, - ..self - } - } -} - -/// !Bit -> Mask -impl Not for FlagBit { - type Output = FlagMask; - fn not(self) -> Self::Output { - FlagMask { - value: !self.value, - _type: PhantomData, - } - } -} - -/// Mask & Mask -> Mask -impl BitAnd for FlagMask { - type Output = FlagMask; - fn bitand(self, rhs: Self) -> Self::Output { - Self { - value: self.value & rhs.value, - ..self - } - } -} - -/// Flags = CK_FLAGS.into() -impl From for CkFlags { - fn from(value: CK_FLAGS) -> Self { - Self { - value, - _type: PhantomData, - } - } -} - -/// CK_FLAGS = *Flags -impl Deref for CkFlags { - type Target = CK_FLAGS; - fn deref(&self) -> &Self::Target { - &self.value - } -} - -/// *Flags = CK_FLAGS -impl DerefMut for CkFlags { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.value - } -} - -/// Flags |= Bit -impl BitOrAssign> for CkFlags { - fn bitor_assign(&mut self, rhs: FlagBit) { - **self |= rhs.value - } -} - -/// Flags &= Mask -impl BitAndAssign> for CkFlags { - fn bitand_assign(&mut self, rhs: FlagMask) { - **self &= rhs.value - } -} - -/// Flags ^= Bit -impl BitXorAssign> for CkFlags { - fn bitxor_assign(&mut self, rhs: FlagBit) { - **self ^= rhs.value - } -} - -impl CkFlags { - pub(crate) fn contains(&self, bits: FlagBit) -> bool { - (self.value & bits.value) == bits.value - } -} - -#[cfg(test)] -mod test { - use super::*; - const ONE: FlagBit<()> = FlagBit::new(1); - const TWO: FlagBit<()> = FlagBit::new(2); - const FOUR: FlagBit<()> = FlagBit::new(4); - fn is_copy(_: T) -> bool { - true - } - - #[test] - fn c_type_is_copy() { - // Probably overkill, but if this is ever false - // the hand implementation of Copy may hide it. - let n: CK_FLAGS = 0; - assert!(is_copy(n)); - } - - #[test] - fn default_to_zero() { - let flags: CkFlags<()> = CkFlags::default(); - assert_eq!(flags.value, 0); - } - - #[test] - fn set_one() { - let mut flags = CkFlags::default(); - flags |= ONE; - assert_eq!(flags.value, 1); - flags |= ONE; - assert_eq!(flags.value, 1); - } - - #[test] - fn set_multi() { - let mut flags = CkFlags::default(); - flags |= ONE | TWO | FOUR; - assert_eq!(flags.value, 7); - flags |= ONE | TWO | FOUR; - assert_eq!(flags.value, 7); - } - - #[test] - fn unset_one() { - let mut flags = CkFlags::from(7); - flags &= !TWO; - assert_eq!(flags.value, 5); - flags &= !TWO; - assert_eq!(flags.value, 5); - } - - #[test] - fn unset_multi() { - let mut flags = CkFlags::from(7); - flags &= !(ONE | FOUR); - assert_eq!(flags.value, 2); - flags &= !(ONE | FOUR); - assert_eq!(flags.value, 2); - } - - #[test] - fn toggle_one() { - let mut flags = CkFlags::from(7); - flags ^= TWO; - assert_eq!(flags.value, 5); - flags ^= TWO; - assert_eq!(flags.value, 7); - } - - #[test] - fn toggle_multi() { - let mut flags = CkFlags::from(7); - flags ^= ONE | FOUR; - assert_eq!(flags.value, 2); - flags ^= ONE | FOUR; - assert_eq!(flags.value, 7); - } - - #[test] - fn check_contains() { - let flags = CkFlags::default(); - assert!(!flags.contains(ONE)); - assert!(!flags.contains(TWO)); - assert!(!flags.contains(ONE | TWO)); - assert!(!flags.contains(FOUR)); - assert!(!flags.contains(ONE | FOUR)); - assert!(!flags.contains(TWO | FOUR)); - assert!(!flags.contains(ONE | TWO | FOUR)); - assert_eq!(flags.value, 0); - - let flags = CkFlags::from(2); - assert!(!flags.contains(ONE)); - assert!(flags.contains(TWO)); - assert!(!flags.contains(ONE | TWO)); - assert!(!flags.contains(FOUR)); - assert!(!flags.contains(ONE | FOUR)); - assert!(!flags.contains(TWO | FOUR)); - assert!(!flags.contains(ONE | TWO | FOUR)); - assert_eq!(flags.value, 2); - - let flags = CkFlags::from(5); - assert!(flags.contains(ONE)); // counterintuitive - assert!(!flags.contains(TWO)); - assert!(!flags.contains(ONE | TWO)); - assert!(flags.contains(FOUR)); // counterintuitive - assert!(flags.contains(ONE | FOUR)); - assert!(!flags.contains(TWO | FOUR)); - assert!(!flags.contains(ONE | TWO | FOUR)); - assert_eq!(flags.value, 5); - - let flags = CkFlags::from(7); - assert!(flags.contains(ONE)); - assert!(flags.contains(TWO)); - assert!(flags.contains(ONE | TWO)); - assert!(flags.contains(FOUR)); - assert!(flags.contains(ONE | FOUR)); - assert!(flags.contains(TWO | FOUR)); - assert!(flags.contains(ONE | TWO | FOUR)); - assert_eq!(flags.value, 7); - } -} diff --git a/cryptoki/src/lib.rs b/cryptoki/src/lib.rs index f4f563d8..7718602d 100644 --- a/cryptoki/src/lib.rs +++ b/cryptoki/src/lib.rs @@ -54,7 +54,6 @@ pub mod context; pub mod error; -pub(crate) mod flag; pub mod mechanism; pub mod object; pub mod session; diff --git a/cryptoki/src/mechanism/mechanism_info.rs b/cryptoki/src/mechanism/mechanism_info.rs index 9511fdc2..e9e9b2f1 100644 --- a/cryptoki/src/mechanism/mechanism_info.rs +++ b/cryptoki/src/mechanism/mechanism_info.rs @@ -2,55 +2,32 @@ // SPDX-License-Identifier: Apache-2.0 //! Mechanism info and associated flags -use crate::flag::{CkFlags, FlagBit}; +use bitflags::bitflags; use cryptoki_sys::*; -use std::fmt::{self, Debug, Formatter}; +use std::fmt::Debug; -const HW: FlagBit = FlagBit::new(CKF_HW); -const ENCRYPT: FlagBit = FlagBit::new(CKF_ENCRYPT); -const DECRYPT: FlagBit = FlagBit::new(CKF_DECRYPT); -const DIGEST: FlagBit = FlagBit::new(CKF_DIGEST); -const SIGN: FlagBit = FlagBit::new(CKF_SIGN); -const SIGN_RECOVER: FlagBit = FlagBit::new(CKF_SIGN_RECOVER); -const VERIFY: FlagBit = FlagBit::new(CKF_VERIFY); -const VERIFY_RECOVER: FlagBit = FlagBit::new(CKF_VERIFY_RECOVER); -const GENERATE: FlagBit = FlagBit::new(CKF_GENERATE); -const GENERATE_KEY_PAIR: FlagBit = FlagBit::new(CKF_GENERATE_KEY_PAIR); -const WRAP: FlagBit = FlagBit::new(CKF_WRAP); -const UNWRAP: FlagBit = FlagBit::new(CKF_UNWRAP); -const DERIVE: FlagBit = FlagBit::new(CKF_DERIVE); -const EXTENSION: FlagBit = FlagBit::new(CKF_EXTENSION); -const EC_F_P: FlagBit = FlagBit::new(CKF_EC_F_P); -const EC_F_2M: FlagBit = FlagBit::new(CKF_EC_F_2M); -const EC_ECPARAMETERS: FlagBit = FlagBit::new(CKF_EC_ECPARAMETERS); -const EC_NAMEDCURVE: FlagBit = FlagBit::new(CKF_EC_NAMEDCURVE); -const EC_UNCOMPRESS: FlagBit = FlagBit::new(CKF_EC_UNCOMPRESS); -const EC_COMPRESS: FlagBit = FlagBit::new(CKF_EC_COMPRESS); - -impl Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Flags") - .field("hw", &(self.contains(HW))) - .field("encrypt", &(self.contains(ENCRYPT))) - .field("decrypt", &(self.contains(DECRYPT))) - .field("digest", &(self.contains(DIGEST))) - .field("sign", &(self.contains(SIGN))) - .field("sign_recover", &(self.contains(SIGN_RECOVER))) - .field("verify", &(self.contains(VERIFY))) - .field("verify_recover", &(self.contains(VERIFY_RECOVER))) - .field("generate", &(self.contains(GENERATE))) - .field("generate_key_pair", &(self.contains(GENERATE_KEY_PAIR))) - .field("wrap", &(self.contains(WRAP))) - .field("unwrap", &(self.contains(UNWRAP))) - .field("derive", &(self.contains(DERIVE))) - .field("extension", &(self.contains(EXTENSION))) - .field("ec_f_p", &(self.contains(EC_F_P))) - .field("ec_f_2m", &(self.contains(EC_F_2M))) - .field("ec_ecparameters", &(self.contains(EC_ECPARAMETERS))) - .field("ec_namedcurve", &(self.contains(EC_NAMEDCURVE))) - .field("ec_uncompress", &(self.contains(EC_UNCOMPRESS))) - .field("ec_compress", &(self.contains(EC_COMPRESS))) - .finish() +bitflags! { + struct MechanismInfoFlags: CK_FLAGS { + const HW = CKF_HW; + const ENCRYPT = CKF_ENCRYPT; + const DECRYPT = CKF_DECRYPT; + const DIGEST = CKF_DIGEST; + const SIGN = CKF_SIGN; + const SIGN_RECOVER = CKF_SIGN_RECOVER; + const VERIFY = CKF_VERIFY; + const VERIFY_RECOVER = CKF_VERIFY_RECOVER; + const GENERATE = CKF_GENERATE; + const GENERATE_KEY_PAIR = CKF_GENERATE_KEY_PAIR; + const WRAP = CKF_WRAP; + const UNWRAP = CKF_UNWRAP; + const DERIVE = CKF_DERIVE; + const EXTENSION = CKF_EXTENSION; + const EC_F_P = CKF_EC_F_P; + const EC_F_2M = CKF_EC_F_2M; + const EC_ECPARAMETERS = CKF_EC_ECPARAMETERS; + const EC_NAMEDCURVE = CKF_EC_NAMEDCURVE; + const EC_UNCOMPRESS = CKF_EC_UNCOMPRESS; + const EC_COMPRESS = CKF_EC_COMPRESS; } } @@ -59,7 +36,7 @@ impl Debug for CkFlags { pub struct MechanismInfo { min_key_size: usize, max_key_size: usize, - flags: CkFlags, + flags: MechanismInfoFlags, } impl MechanismInfo { @@ -84,35 +61,35 @@ impl MechanismInfo { /// True if the mechanism is performed by the device; false if the /// mechanism is performed in software pub fn hardware(&self) -> bool { - self.flags.contains(HW) + self.flags.contains(MechanismInfoFlags::HW) } /// True if the mechanism can be used to encrypt data /// /// See [`Session::encrypt`](crate::session::Session::encrypt) pub fn encrypt(&self) -> bool { - self.flags.contains(ENCRYPT) + self.flags.contains(MechanismInfoFlags::ENCRYPT) } /// True if the mechanism can be used to decrypt encrypted data /// /// See [`Session::decrypt`](crate::session::Session::decrypt) pub fn decrypt(&self) -> bool { - self.flags.contains(DECRYPT) + self.flags.contains(MechanismInfoFlags::DECRYPT) } /// True if the mechanism can be used to digest a message /// // TODO See [`Session::digest`](crate::session::Session::digest) pub fn digest(&self) -> bool { - self.flags.contains(DIGEST) + self.flags.contains(MechanismInfoFlags::DIGEST) } /// True if the mechanism can be used to digitally sign data /// /// See [`Session::sign`](crate::session::Session::sign) pub fn sign(&self) -> bool { - self.flags.contains(SIGN) + self.flags.contains(MechanismInfoFlags::SIGN) } /// True if the mechanism can be used to digitally data which can be @@ -120,14 +97,14 @@ impl MechanismInfo { /// // TODO See [`Session::sign_recover`](crate::session::Session::sign_recover) pub fn sign_recover(&self) -> bool { - self.flags.contains(SIGN_RECOVER) + self.flags.contains(MechanismInfoFlags::SIGN_RECOVER) } /// True if the mechanism can be used to verify a digital signature /// /// See [`Session::verify`](crate::session::Session::verify) pub fn verify(&self) -> bool { - self.flags.contains(VERIFY) + self.flags.contains(MechanismInfoFlags::VERIFY) } /// True if the mechanism can be used to verify a digital signature and @@ -135,42 +112,42 @@ impl MechanismInfo { /// // TODO See [`Session::verify_recover`](crate::session::Session::verify_recover) pub fn verify_recover(&self) -> bool { - self.flags.contains(VERIFY_RECOVER) + self.flags.contains(MechanismInfoFlags::VERIFY_RECOVER) } /// True if the mechanism can be used to generate a secret key /// // TODO See [`Session::generate`](crate::session::Session::generate) pub fn generate(&self) -> bool { - self.flags.contains(GENERATE) + self.flags.contains(MechanismInfoFlags::GENERATE) } /// True if the mechanism can be used to generate a public/private key pair /// /// See [`Session::generate_key_pair`](crate::session::Session::generate_key_pair)) pub fn generate_key_pair(&self) -> bool { - self.flags.contains(GENERATE_KEY_PAIR) + self.flags.contains(MechanismInfoFlags::GENERATE_KEY_PAIR) } /// True if the mechanism can be used to wrap (encrypt) a key /// // TODO See [`Session::wrap`](crate::session::Session::wrap)) pub fn wrap(&self) -> bool { - self.flags.contains(WRAP) + self.flags.contains(MechanismInfoFlags::WRAP) } /// True if the mechanism can be used to unwrap (decrypt) a key /// // TODO See [`Session::unwrap`](crate::session::Session::unwrap)) pub fn unwrap(&self) -> bool { - self.flags.contains(UNWRAP) + self.flags.contains(MechanismInfoFlags::UNWRAP) } /// True if the mechanism can be used to derive a key from a base key /// // TODO See [`Session::derive`](crate::session::Session::derive)) pub fn derive(&self) -> bool { - self.flags.contains(DERIVE) + self.flags.contains(MechanismInfoFlags::DERIVE) } /// True if there is an extension to the flags; false if no extensions @@ -178,7 +155,7 @@ impl MechanismInfo { /// **[Conformance](crate#conformance-notes):** /// This *must* be false for PKCS#11 v2.40 pub fn extension(&self) -> bool { - self.flags.contains(EXTENSION) + self.flags.contains(MechanismInfoFlags::EXTENSION) } /// True if the mechanism can be used to with elliptic curve domain @@ -188,7 +165,7 @@ impl MechanismInfo { /// *At least* one of [`ec_f_p`](Self::ec_f_p) and /// [`ec_f_2m`](Self::ec_f_2m) must be `true` pub fn ec_f_p(&self) -> bool { - self.flags.contains(EC_F_P) + self.flags.contains(MechanismInfoFlags::EC_F_P) } /// True if the mechanism can be used with elliptic curve domain parameters @@ -198,7 +175,7 @@ impl MechanismInfo { /// *At least* one of [`ec_f_p`](Self::ec_f_p) and /// [`ec_f_2m`](Self::ec_f_2m) must be `true` pub fn ec_f_2m(&self) -> bool { - self.flags.contains(EC_F_2M) + self.flags.contains(MechanismInfoFlags::EC_F_2M) } /// True if the mechanism supports specifying elliptic curve domain @@ -208,7 +185,7 @@ impl MechanismInfo { /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and /// [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` pub fn ec_from_parameters(&self) -> bool { - self.flags.contains(EC_ECPARAMETERS) + self.flags.contains(MechanismInfoFlags::EC_ECPARAMETERS) } /// True if the mechanism supports specifying elliptic curve domain @@ -218,7 +195,7 @@ impl MechanismInfo { /// *At least* one of [`ec_from_parameters`](Self::ec_from_parameters) and /// [`ec_from_named_curve`](Self::ec_from_named_curve) must be `true` pub fn ec_from_named_curve(&self) -> bool { - self.flags.contains(EC_NAMEDCURVE) + self.flags.contains(MechanismInfoFlags::EC_NAMEDCURVE) } /// True if the mechanism can be used with elliptic curve points in @@ -228,7 +205,7 @@ impl MechanismInfo { /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and /// [`ec_compressed`](Self::ec_compressed) must be `true` pub fn ec_uncompressed(&self) -> bool { - self.flags.contains(EC_UNCOMPRESS) + self.flags.contains(MechanismInfoFlags::EC_UNCOMPRESS) } /// True if the mechanism can be used with elliptic curve points in @@ -238,7 +215,7 @@ impl MechanismInfo { /// *At least* one of [`ec_uncompressed`](Self::ec_uncompressed) and /// [`ec_compressed`](Self::ec_compressed) must be `true` pub fn ec_compressed(&self) -> bool { - self.flags.contains(EC_COMPRESS) + self.flags.contains(MechanismInfoFlags::EC_COMPRESS) } } @@ -248,108 +225,39 @@ impl From for MechanismInfo { Self { min_key_size: val.ulMinKeySize as usize, max_key_size: val.ulMaxKeySize as usize, - flags: CkFlags::from(val.flags), + flags: MechanismInfoFlags::from_bits_truncate(val.flags), } } } #[cfg(test)] mod test { - use super::MechanismInfo; - use crate::flag::CkFlags; - use cryptoki_sys::CK_FLAGS; + use super::{MechanismInfo, MechanismInfoFlags}; #[test] fn debug_flags_all() { - let expected = r"Flags { - hw: true, - encrypt: true, - decrypt: true, - digest: true, - sign: true, - sign_recover: true, - verify: true, - verify_recover: true, - generate: true, - generate_key_pair: true, - wrap: true, - unwrap: true, - derive: true, - extension: true, - ec_f_p: true, - ec_f_2m: true, - ec_ecparameters: true, - ec_namedcurve: true, - ec_uncompress: true, - ec_compress: true, -}"; - let all: CkFlags = CkFlags::from(CK_FLAGS::MAX); + let expected = "\ +HW | ENCRYPT | DECRYPT | DIGEST | SIGN | SIGN_RECOVER | VERIFY | \ +VERIFY_RECOVER | GENERATE | GENERATE_KEY_PAIR | WRAP | UNWRAP | DERIVE | \ +EXTENSION | EC_F_P | EC_F_2M | EC_ECPARAMETERS | EC_NAMEDCURVE | \ +EC_UNCOMPRESS | EC_COMPRESS"; + let all = MechanismInfoFlags::all(); let observed = format!("{:#?}", all); println!("{}", observed); assert_eq!(observed, expected); } - #[test] - fn debug_flags_none() { - let expected = r"Flags { - hw: false, - encrypt: false, - decrypt: false, - digest: false, - sign: false, - sign_recover: false, - verify: false, - verify_recover: false, - generate: false, - generate_key_pair: false, - wrap: false, - unwrap: false, - derive: false, - extension: false, - ec_f_p: false, - ec_f_2m: false, - ec_ecparameters: false, - ec_namedcurve: false, - ec_uncompress: false, - ec_compress: false, -}"; - let none: CkFlags = CkFlags::from(0); - let observed = format!("{:#?}", none); - assert_eq!(observed, expected); - } - #[test] fn debug_info() { let info = MechanismInfo { min_key_size: 16, max_key_size: 4096, - flags: CkFlags::from(0), + flags: MechanismInfoFlags::empty(), }; let expected = r#"MechanismInfo { min_key_size: 16, max_key_size: 4096, - flags: Flags { - hw: false, - encrypt: false, - decrypt: false, - digest: false, - sign: false, - sign_recover: false, - verify: false, - verify_recover: false, - generate: false, - generate_key_pair: false, - wrap: false, - unwrap: false, - derive: false, - extension: false, - ec_f_p: false, - ec_f_2m: false, - ec_ecparameters: false, - ec_namedcurve: false, - ec_uncompress: false, - ec_compress: false, - }, + flags: (empty), }"#; let observed = format!("{:#?}", info); assert_eq!(observed, expected); diff --git a/cryptoki/src/session/session_info.rs b/cryptoki/src/session/session_info.rs index a2fc9b40..16ef1430 100644 --- a/cryptoki/src/session/session_info.rs +++ b/cryptoki/src/session/session_info.rs @@ -3,21 +3,17 @@ //! Session info use crate::error::{Error, Result}; -use crate::flag::{CkFlags, FlagBit}; use crate::slot::Slot; +use bitflags::bitflags; use cryptoki_sys::*; use std::convert::{TryFrom, TryInto}; -use std::fmt::{self, Debug, Formatter}; +use std::fmt::Debug; -const RW_SESSION: FlagBit = FlagBit::new(CKF_RW_SESSION); -const SERIAL_SESSION: FlagBit = FlagBit::new(CKF_SERIAL_SESSION); - -impl Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Flags") - .field("rw_session", &(self.contains(RW_SESSION))) - .field("serial_session", &(self.contains(SERIAL_SESSION))) - .finish() +bitflags! { + /// Collection of flags defined for [`CK_SESSION_INFO`] + struct SessionInfoFlags: CK_FLAGS { + const RW_SESSION = CKF_RW_SESSION; + const SERIAL_SESSION = CKF_SERIAL_SESSION; } } @@ -26,7 +22,7 @@ impl Debug for CkFlags { pub struct SessionInfo { slot_id: Slot, state: SessionState, - flags: CkFlags, + flags: SessionInfoFlags, device_error: u64, } @@ -44,7 +40,7 @@ impl SessionInfo { /// True if the session has R/W access to token objects, and false if access /// is read-only pub fn read_write(&self) -> bool { - self.flags.contains(RW_SESSION) + self.flags.contains(SessionInfoFlags::RW_SESSION) } /// An error code defined by the cryptographic device (used for errors not @@ -63,7 +59,7 @@ impl TryFrom for SessionInfo { Ok(Self { slot_id: Slot::new(val.slotID), state: val.state.try_into()?, - flags: CkFlags::from(val.flags), + flags: SessionInfoFlags::from_bits_truncate(val.flags), device_error, }) } @@ -105,3 +101,37 @@ impl TryFrom for SessionState { } } } + +#[cfg(test)] +mod test { + use super::{SessionInfo, SessionInfoFlags, SessionState}; + use crate::slot::Slot; + + #[test] + fn debug_flags_all() { + let expected = "RW_SESSION | SERIAL_SESSION"; + let all = SessionInfoFlags::all(); + let observed = format!("{:#?}", all); + assert_eq!(observed, expected); + } + + #[test] + fn debug_info() { + let info = SessionInfo { + slot_id: Slot::new(100), + state: SessionState::RoPublic, + flags: SessionInfoFlags::empty(), + device_error: 0, + }; + let expected = r#"SessionInfo { + slot_id: Slot { + slot_id: 100, + }, + state: RoPublic, + flags: (empty), + device_error: 0, +}"#; + let observed = format!("{:#?}", info); + assert_eq!(observed, expected); + } +} diff --git a/cryptoki/src/slot/slot_info.rs b/cryptoki/src/slot/slot_info.rs index 2a6e54d9..d0cfa90b 100644 --- a/cryptoki/src/slot/slot_info.rs +++ b/cryptoki/src/slot/slot_info.rs @@ -2,23 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 //! PKCS11 Slot info and associated flags -use crate::flag::{CkFlags, FlagBit}; use crate::{string_from_blank_padded, types::Version}; +use bitflags::bitflags; use cryptoki_sys::*; -use std::fmt::{self, Debug, Formatter}; - -/// Collection of flags defined for [`CK_SLOT_INFO`] -const TOKEN_PRESENT: FlagBit = FlagBit::new(CKF_TOKEN_PRESENT); -const REMOVABLE_DEVICE: FlagBit = FlagBit::new(CKF_REMOVABLE_DEVICE); -const HW_SLOT: FlagBit = FlagBit::new(CKF_HW_SLOT); - -impl Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Flags") - .field("token_present", &(self.contains(TOKEN_PRESENT))) - .field("removable_device", &(self.contains(REMOVABLE_DEVICE))) - .field("hw_slot", &(self.contains(HW_SLOT))) - .finish() +use std::fmt::Debug; + +bitflags! { + /// Collection of flags defined for [`CK_SLOT_INFO`] + struct SlotInfoFlags: CK_FLAGS { + const TOKEN_PRESENT=CKF_TOKEN_PRESENT; + const REMOVABLE_DEVICE=CKF_REMOVABLE_DEVICE; + const HW_SLOT = CKF_HW_SLOT; } } @@ -27,7 +21,7 @@ impl Debug for CkFlags { pub struct SlotInfo { slot_description: String, manufacturer_id: String, - flags: CkFlags, + flags: SlotInfoFlags, hardware_version: Version, firmware_version: Version, } @@ -56,7 +50,7 @@ impl SlotInfo { /// considered to be present. That is, `slot.removable device() == false` /// implies `slot.token_present() == true`. pub fn token_present(&self) -> bool { - self.flags.contains(TOKEN_PRESENT) + self.flags.contains(SlotInfoFlags::TOKEN_PRESENT) } /// True if the reader supports removable devices. @@ -64,13 +58,13 @@ impl SlotInfo { /// **[Conformance](crate#conformance-notes):** /// For a given slot, this flag *never* changes pub fn removable_device(&self) -> bool { - self.flags.contains(REMOVABLE_DEVICE) + self.flags.contains(SlotInfoFlags::REMOVABLE_DEVICE) } /// True if the slot is a hardware slot, as opposed to a software slot /// implementing a "soft token" pub fn hardware_slot(&self) -> bool { - self.flags.contains(HW_SLOT) + self.flags.contains(SlotInfoFlags::HW_SLOT) } /// Version number of the slot's hardware @@ -90,7 +84,7 @@ impl From for SlotInfo { Self { slot_description: string_from_blank_padded(&val.slotDescription), manufacturer_id: string_from_blank_padded(&val.manufacturerID), - flags: val.flags.into(), + flags: SlotInfoFlags::from_bits_truncate(val.flags), hardware_version: val.hardwareVersion.into(), firmware_version: val.firmwareVersion.into(), } @@ -99,51 +93,30 @@ impl From for SlotInfo { #[cfg(test)] mod test { - use super::SlotInfo; - use crate::{flag::CkFlags, types::Version}; - use cryptoki_sys::CK_FLAGS; + use super::{SlotInfo, SlotInfoFlags}; + use crate::types::Version; #[test] fn debug_flags_all() { - let expected = r"Flags { - token_present: true, - removable_device: true, - hw_slot: true, -}"; - let all: CkFlags = CkFlags::from(CK_FLAGS::MAX); + let expected = "TOKEN_PRESENT | REMOVABLE_DEVICE | HW_SLOT"; + let all = SlotInfoFlags::all(); let observed = format!("{:#?}", all); assert_eq!(observed, expected); } - #[test] - fn debug_none_set() { - let expected = r"Flags { - token_present: false, - removable_device: false, - hw_slot: false, -}"; - let none: CkFlags = CkFlags::from(0); - let observed = format!("{:#?}", none); - assert_eq!(observed, expected); - } - #[test] fn debug_info() { let info = SlotInfo { slot_description: String::from("Slot Description"), manufacturer_id: String::from("Manufacturer ID"), - flags: CkFlags::from(0), + flags: SlotInfoFlags::empty(), hardware_version: Version::new(0, 255), firmware_version: Version::new(255, 0), }; let expected = r#"SlotInfo { slot_description: "Slot Description", manufacturer_id: "Manufacturer ID", - flags: Flags { - token_present: false, - removable_device: false, - hw_slot: false, - }, + flags: (empty), hardware_version: Version { major: 0, minor: 255, diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index 7705bf58..38dc084f 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -3,79 +3,36 @@ //! PKCS11 Token info and associated flags use crate::error::{Error, Result}; -use crate::flag::{CkFlags, FlagBit}; use crate::string_from_blank_padded; use crate::types::{convert_utc_time, maybe_unlimited}; use crate::types::{MaybeUnavailable, UtcTime, Version}; +use bitflags::bitflags; use cryptoki_sys::*; use std::convert::TryFrom; -use std::fmt::{self, Debug, Formatter}; - -const RNG: FlagBit = FlagBit::new(CKF_RNG); -const WRITE_PROTECTED: FlagBit = FlagBit::new(CKF_WRITE_PROTECTED); -const LOGIN_REQUIRED: FlagBit = FlagBit::new(CKF_LOGIN_REQUIRED); -const USER_PIN_INITIALIZED: FlagBit = FlagBit::new(CKF_USER_PIN_INITIALIZED); -const RESTORE_KEY_NOT_NEEDED: FlagBit = FlagBit::new(CKF_RESTORE_KEY_NOT_NEEDED); -const CLOCK_ON_TOKEN: FlagBit = FlagBit::new(CKF_CLOCK_ON_TOKEN); -const PROTECTED_AUTHENTICATION_PATH: FlagBit = - FlagBit::new(CKF_PROTECTED_AUTHENTICATION_PATH); -const DUAL_CRYPTO_OPERATIONS: FlagBit = FlagBit::new(CKF_DUAL_CRYPTO_OPERATIONS); -const TOKEN_INITIALIZED: FlagBit = FlagBit::new(CKF_TOKEN_INITIALIZED); -const SECONDARY_AUTHENTICATION: FlagBit = FlagBit::new(CKF_SECONDARY_AUTHENTICATION); -const USER_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_USER_PIN_COUNT_LOW); -const USER_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_USER_PIN_FINAL_TRY); -const USER_PIN_LOCKED: FlagBit = FlagBit::new(CKF_USER_PIN_LOCKED); -const USER_PIN_TO_BE_CHANGED: FlagBit = FlagBit::new(CKF_USER_PIN_TO_BE_CHANGED); -const SO_PIN_COUNT_LOW: FlagBit = FlagBit::new(CKF_SO_PIN_COUNT_LOW); -const SO_PIN_FINAL_TRY: FlagBit = FlagBit::new(CKF_SO_PIN_FINAL_TRY); -const SO_PIN_LOCKED: FlagBit = FlagBit::new(CKF_SO_PIN_LOCKED); -const SO_PIN_TO_BE_CHANGED: FlagBit = FlagBit::new(CKF_SO_PIN_TO_BE_CHANGED); -const ERROR_STATE: FlagBit = FlagBit::new(CKF_ERROR_STATE); - -impl Debug for CkFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Flags") - .field("rng", &(self.contains(RNG))) - .field("write_protected", &(self.contains(WRITE_PROTECTED))) - .field("login_required", &(self.contains(LOGIN_REQUIRED))) - .field( - "user_pin_initialized", - &(self.contains(USER_PIN_INITIALIZED)), - ) - .field( - "restore_key_not_needed", - &(self.contains(RESTORE_KEY_NOT_NEEDED)), - ) - .field("clock_on token", &(self.contains(CLOCK_ON_TOKEN))) - .field( - "protected_authentication_path", - &(self.contains(PROTECTED_AUTHENTICATION_PATH)), - ) - .field( - "dual_crypto_operations", - &(self.contains(DUAL_CRYPTO_OPERATIONS)), - ) - .field("token_initialized", &(self.contains(TOKEN_INITIALIZED))) - .field( - "secondary_authentication", - &(self.contains(SECONDARY_AUTHENTICATION)), - ) - .field("user_pin_count_low", &(self.contains(USER_PIN_COUNT_LOW))) - .field("user_pin_final_try", &(self.contains(USER_PIN_FINAL_TRY))) - .field("user_pin_locked", &(self.contains(USER_PIN_LOCKED))) - .field( - "user_pin_to_be_changed", - &(self.contains(USER_PIN_TO_BE_CHANGED)), - ) - .field("so_pin_count_low", &(self.contains(SO_PIN_COUNT_LOW))) - .field("so_pin_final_try", &(self.contains(SO_PIN_FINAL_TRY))) - .field("so_pin_locked", &(self.contains(SO_PIN_LOCKED))) - .field( - "so_pin_to_be_changed", - &(self.contains(SO_PIN_TO_BE_CHANGED)), - ) - .field("error_state", &(self.contains(ERROR_STATE))) - .finish() +use std::fmt::Debug; + +bitflags! { + /// Collection of flags defined for [`CK_TOKEN_INFO`] + struct TokenInfoFlags: CK_FLAGS { + const RNG = CKF_RNG; + const WRITE_PROTECTED = CKF_WRITE_PROTECTED; + const LOGIN_REQUIRED = CKF_LOGIN_REQUIRED; + const USER_PIN_INITIALIZED = CKF_USER_PIN_INITIALIZED; + const RESTORE_KEY_NOT_NEEDED = CKF_RESTORE_KEY_NOT_NEEDED; + const CLOCK_ON_TOKEN = CKF_CLOCK_ON_TOKEN; + const PROTECTED_AUTHENTICATION_PATH = CKF_PROTECTED_AUTHENTICATION_PATH; + const DUAL_CRYPTO_OPERATIONS = CKF_DUAL_CRYPTO_OPERATIONS; + const TOKEN_INITIALIZED = CKF_TOKEN_INITIALIZED; + const SECONDARY_AUTHENTICATION = CKF_SECONDARY_AUTHENTICATION; + const USER_PIN_COUNT_LOW = CKF_USER_PIN_COUNT_LOW; + const USER_PIN_FINAL_TRY = CKF_USER_PIN_FINAL_TRY; + const USER_PIN_LOCKED = CKF_USER_PIN_LOCKED; + const USER_PIN_TO_BE_CHANGED = CKF_USER_PIN_TO_BE_CHANGED; + const SO_PIN_COUNT_LOW = CKF_SO_PIN_COUNT_LOW; + const SO_PIN_FINAL_TRY = CKF_SO_PIN_FINAL_TRY; + const SO_PIN_LOCKED = CKF_SO_PIN_LOCKED; + const SO_PIN_TO_BE_CHANGED = CKF_SO_PIN_TO_BE_CHANGED; + const ERROR_STATE = CKF_ERROR_STATE; } } @@ -86,7 +43,7 @@ pub struct TokenInfo { manufacturer_id: String, // 32 model: String, // 16 serial_number: String, // 16 - flags: CkFlags, + flags: TokenInfoFlags, max_session_count: Option>, session_count: Option, max_rw_session_count: Option>, @@ -137,7 +94,7 @@ impl TokenInfo { /// True if the token has its own random number generator pub fn rng(&self) -> bool { - self.flags.contains(RNG) + self.flags.contains(TokenInfoFlags::RNG) } /// True if the token is write-protected @@ -159,35 +116,36 @@ impl TokenInfo { /// public or private, to be created, modified, or deleted unless the user /// has successfully called [`Session::login`](crate::session::Session::login). pub fn write_protected(&self) -> bool { - self.flags.contains(WRITE_PROTECTED) + self.flags.contains(TokenInfoFlags::WRITE_PROTECTED) } /// True if there are some cryptographic functions that a user *must* be /// logged in to perform pub fn login_required(&self) -> bool { - self.flags.contains(LOGIN_REQUIRED) + self.flags.contains(TokenInfoFlags::LOGIN_REQUIRED) } /// True of the normal user's PIN has been initialized pub fn user_pin_initialized(&self) -> bool { - self.flags.contains(USER_PIN_INITIALIZED) + self.flags.contains(TokenInfoFlags::USER_PIN_INITIALIZED) } /// True if a successful save of a session's cryptographic operations state /// *always* contains all keys needed to restore the state of the session. pub fn restore_key_not_needed(&self) -> bool { - self.flags.contains(RESTORE_KEY_NOT_NEEDED) + self.flags.contains(TokenInfoFlags::RESTORE_KEY_NOT_NEEDED) } /// True if the token has its own hardware clock pub fn clock_on_token(&self) -> bool { - self.flags.contains(CLOCK_ON_TOKEN) + self.flags.contains(TokenInfoFlags::CLOCK_ON_TOKEN) } /// True if the token has a "protected authentication path" whereby a user /// can log into the token without passing a PIN pub fn protected_authentication_path(&self) -> bool { - self.flags.contains(PROTECTED_AUTHENTICATION_PATH) + self.flags + .contains(TokenInfoFlags::PROTECTED_AUTHENTICATION_PATH) } /// True if a single session with the token can perform dual cryptographic @@ -198,7 +156,7 @@ impl TokenInfo { // * sign_encrypt_update // * decrypt_verify_update pub fn dual_crypto_operations(&self) -> bool { - self.flags.contains(DUAL_CRYPTO_OPERATIONS) + self.flags.contains(TokenInfoFlags::DUAL_CRYPTO_OPERATIONS) } /// True if the token has been initialized with @@ -209,7 +167,7 @@ impl TokenInfo { /// Calling [`Pkcs11::init_token`](crate::context::Pkcs11::init_token) when /// this flag is set will cause the token to be reinitialized. pub fn token_initialized(&self) -> bool { - self.flags.contains(TOKEN_INITIALIZED) + self.flags.contains(TokenInfoFlags::TOKEN_INITIALIZED) } /// True if the token supports secondary authentication for private key @@ -217,7 +175,8 @@ impl TokenInfo { /// **[Conformance](crate#conformance-notes):** /// This field is deprecated and new providers *must not* set it. I.e., this function must always return `false`. pub fn secondary_authentication(&self) -> bool { - self.flags.contains(SECONDARY_AUTHENTICATION) + self.flags + .contains(TokenInfoFlags::SECONDARY_AUTHENTICATION) } /// True if an incorrect user login PIN has been entered at least once @@ -228,7 +187,7 @@ impl TokenInfo { /// support the functionality or will not reveal the information because of /// its security policy. pub fn user_pin_count_low(&self) -> bool { - self.flags.contains(USER_PIN_COUNT_LOW) + self.flags.contains(TokenInfoFlags::USER_PIN_COUNT_LOW) } /// True if supplying an incorrect user PIN will cause it to become locked @@ -238,13 +197,13 @@ impl TokenInfo { /// support the functionality or will not reveal the information because of /// its security policy. pub fn user_pin_final_try(&self) -> bool { - self.flags.contains(USER_PIN_FINAL_TRY) + self.flags.contains(TokenInfoFlags::USER_PIN_FINAL_TRY) } /// True if the user PIN has been locked; user login to the token is not /// possible pub fn user_pin_locked(&self) -> bool { - self.flags.contains(USER_PIN_LOCKED) + self.flags.contains(TokenInfoFlags::USER_PIN_LOCKED) } /// True if the user PIN value is the default value set by the token @@ -265,7 +224,7 @@ impl TokenInfo { /// [`Session::set_pin`][crate::session::Session::set_pin] is called /// successfully. pub fn user_pin_to_be_changed(&self) -> bool { - self.flags.contains(USER_PIN_TO_BE_CHANGED) + self.flags.contains(TokenInfoFlags::USER_PIN_TO_BE_CHANGED) } /// True if an incorrect Security Officer login PIN has been entered at least once since @@ -276,7 +235,7 @@ impl TokenInfo { /// support the functionality or will not reveal the information because of /// its security policy. pub fn so_pin_count_low(&self) -> bool { - self.flags.contains(SO_PIN_COUNT_LOW) + self.flags.contains(TokenInfoFlags::SO_PIN_COUNT_LOW) } /// True if supplying an incorrect Security Officer PIN will cause it to become locked @@ -286,13 +245,13 @@ impl TokenInfo { /// support the functionality or will not reveal the information because of /// its security policy. pub fn so_pin_final_try(&self) -> bool { - self.flags.contains(SO_PIN_FINAL_TRY) + self.flags.contains(TokenInfoFlags::SO_PIN_FINAL_TRY) } /// True if the Security Officer PIN has been locked; Security Officer login to the token is not /// possible pub fn so_pin_locked(&self) -> bool { - self.flags.contains(SO_PIN_LOCKED) + self.flags.contains(TokenInfoFlags::SO_PIN_LOCKED) } /// True if the Security Officer PIN value is the default value set by the token @@ -312,12 +271,12 @@ impl TokenInfo { /// [`Session::set_pin`][crate::session::Session::set_pin] is called /// successfully. pub fn so_pin_to_be_changed(&self) -> bool { - self.flags.contains(SO_PIN_TO_BE_CHANGED) + self.flags.contains(TokenInfoFlags::SO_PIN_TO_BE_CHANGED) } /// True if the token failed a FIPS 140-2 self-test and entered an error state pub fn error_state(&self) -> bool { - self.flags.contains(ERROR_STATE) + self.flags.contains(TokenInfoFlags::ERROR_STATE) } /// The maximum number of sessions that can be opened with the token at one @@ -416,8 +375,8 @@ impl TokenInfo { impl TryFrom for TokenInfo { type Error = Error; fn try_from(val: CK_TOKEN_INFO) -> Result { - let flags = CkFlags::from(val.flags); - let utc_time = if flags.contains(CLOCK_ON_TOKEN) { + let flags = TokenInfoFlags::from_bits_truncate(val.flags); + let utc_time = if flags.contains(TokenInfoFlags::CLOCK_ON_TOKEN) { convert_utc_time(val.utcTime)? } else { None @@ -447,66 +406,23 @@ impl TryFrom for TokenInfo { #[cfg(test)] mod test { - use super::TokenInfo; - use crate::{flag::CkFlags, types::UtcTime, types::Version}; - use cryptoki_sys::CK_FLAGS; + use super::{TokenInfo, TokenInfoFlags}; + use crate::types::{UtcTime, Version}; #[test] fn debug_flags_all() { - let expected = r"Flags { - rng: true, - write_protected: true, - login_required: true, - user_pin_initialized: true, - restore_key_not_needed: true, - clock_on token: true, - protected_authentication_path: true, - dual_crypto_operations: true, - token_initialized: true, - secondary_authentication: true, - user_pin_count_low: true, - user_pin_final_try: true, - user_pin_locked: true, - user_pin_to_be_changed: true, - so_pin_count_low: true, - so_pin_final_try: true, - so_pin_locked: true, - so_pin_to_be_changed: true, - error_state: true, -}"; - let all: CkFlags = CkFlags::from(CK_FLAGS::MAX); + let expected = "\ +RNG | WRITE_PROTECTED | LOGIN_REQUIRED | USER_PIN_INITIALIZED | \ +RESTORE_KEY_NOT_NEEDED | CLOCK_ON_TOKEN | PROTECTED_AUTHENTICATION_PATH | \ +DUAL_CRYPTO_OPERATIONS | TOKEN_INITIALIZED | SECONDARY_AUTHENTICATION | \ +USER_PIN_COUNT_LOW | USER_PIN_FINAL_TRY | USER_PIN_LOCKED | \ +USER_PIN_TO_BE_CHANGED | SO_PIN_COUNT_LOW | SO_PIN_FINAL_TRY | SO_PIN_LOCKED | \ +SO_PIN_TO_BE_CHANGED | ERROR_STATE"; + let all = TokenInfoFlags::all(); let observed = format!("{:#?}", all); assert_eq!(observed, expected); } - #[test] - fn debug_flags_none() { - let expected = r"Flags { - rng: false, - write_protected: false, - login_required: false, - user_pin_initialized: false, - restore_key_not_needed: false, - clock_on token: false, - protected_authentication_path: false, - dual_crypto_operations: false, - token_initialized: false, - secondary_authentication: false, - user_pin_count_low: false, - user_pin_final_try: false, - user_pin_locked: false, - user_pin_to_be_changed: false, - so_pin_count_low: false, - so_pin_final_try: false, - so_pin_locked: false, - so_pin_to_be_changed: false, - error_state: false, -}"; - let none: CkFlags = CkFlags::from(0); - let observed = format!("{:#?}", none); - assert_eq!(observed, expected); - } - #[test] fn debug_info() { let info = TokenInfo { @@ -514,7 +430,7 @@ mod test { manufacturer_id: String::from("Manufacturer ID"), model: String::from("Token Model"), serial_number: String::from("Serial Number"), - flags: CkFlags::from(0), + flags: TokenInfoFlags::empty(), max_session_count: Some(Some(100)), // max == 100 session_count: None, // unavailable max_rw_session_count: Some(None), // max == infinite @@ -541,27 +457,7 @@ mod test { manufacturer_id: "Manufacturer ID", model: "Token Model", serial_number: "Serial Number", - flags: Flags { - rng: false, - write_protected: false, - login_required: false, - user_pin_initialized: false, - restore_key_not_needed: false, - clock_on token: false, - protected_authentication_path: false, - dual_crypto_operations: false, - token_initialized: false, - secondary_authentication: false, - user_pin_count_low: false, - user_pin_final_try: false, - user_pin_locked: false, - user_pin_to_be_changed: false, - so_pin_count_low: false, - so_pin_final_try: false, - so_pin_locked: false, - so_pin_to_be_changed: false, - error_state: false, - }, + flags: (empty), max_session_count: Some( Some( 100, From 6dacb6212f862833ce03ba951a168870ee9af440 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 3 Dec 2021 23:54:09 -0500 Subject: [PATCH 31/36] Refactor general Info struct In particular, it * removes a lot of unnecessary interaction with ffi types, * removes the flags from the public API, * flattens the contents of CK_INFO into the struct, * moves individual field conversions out of public getters into a one-time, struct-level conversion, and * makes conversion from CK_INFO fallible. Signed-off-by: Keith Koskie --- cryptoki/src/context/general_purpose.rs | 3 +- cryptoki/src/context/info.rs | 73 +++++++++++++------------ 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/cryptoki/src/context/general_purpose.rs b/cryptoki/src/context/general_purpose.rs index fa16adb2..2495f78e 100644 --- a/cryptoki/src/context/general_purpose.rs +++ b/cryptoki/src/context/general_purpose.rs @@ -5,6 +5,7 @@ use crate::context::{CInitializeArgs, Info, Pkcs11}; use crate::error::{Result, Rv}; use cryptoki_sys::{CK_C_INITIALIZE_ARGS, CK_INFO}; +use std::convert::TryFrom; // See public docs on stub in parent mod.rs #[inline(always)] @@ -26,6 +27,6 @@ pub(super) fn get_library_info(ctx: &Pkcs11) -> Result { let mut info = CK_INFO::default(); unsafe { Rv::from(get_pkcs11!(ctx, C_GetInfo)(&mut info)).into_result()?; - Ok(Info::new(info)) + Info::try_from(info) } } diff --git a/cryptoki/src/context/info.rs b/cryptoki/src/context/info.rs index e2fed6ba..48e1ab2b 100644 --- a/cryptoki/src/context/info.rs +++ b/cryptoki/src/context/info.rs @@ -2,58 +2,63 @@ // SPDX-License-Identifier: Apache-2.0 //! PKCS11 library information +use crate::error::{Error, Result}; use crate::string_from_blank_padded; use crate::types::Version; use cryptoki_sys::*; -use std::ops::Deref; +use std::convert::TryFrom; -#[derive(Debug, Clone, Copy)] -/// Type identifying the PKCS#11 library information +#[derive(Debug, Clone)] +/// General information about the Cryptoki (PKCS#11 library) pub struct Info { - val: CK_INFO, + cryptoki_version: Version, + manufacturer_id: String, + // flags + library_description: String, + library_version: Version, } impl Info { - pub(crate) fn new(val: CK_INFO) -> Self { - Self { val } - } - - /// Returns the version of Cryptoki that the library is compatible with + /// Returns the version of Cryptoki interface for compatibility with future + /// revisions pub fn cryptoki_version(&self) -> Version { - self.val.cryptokiVersion.into() + self.cryptoki_version } - /// Returns the flags of the library (should be zero!) - pub fn flags(&self) -> CK_FLAGS { - self.val.flags + /// ID of the Cryptoki library manufacturer + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 + pub fn manufacturer_id(&self) -> &str { + &self.manufacturer_id } - /// Returns the description of the library - pub fn library_description(&self) -> String { - string_from_blank_padded(&self.val.libraryDescription) + /// Description of the library + /// + /// **[Conformance](crate#conformance-notes):** + /// This string is maximally 32 bytes (*not* chars) as UTF-8 + pub fn library_description(&self) -> &str { + &self.library_description } - /// Returns the version of the library + /// Cryptoki library version number pub fn library_version(&self) -> Version { - self.val.libraryVersion.into() - } - - /// Returns the manufacturer of the library - pub fn manufacturer_id(&self) -> String { - string_from_blank_padded(&self.val.manufacturerID) - } -} - -impl Deref for Info { - type Target = CK_INFO; - - fn deref(&self) -> &Self::Target { - &self.val + self.library_version } } -impl From for CK_INFO { - fn from(info: Info) -> Self { - *info +#[doc(hidden)] +impl TryFrom for Info { + type Error = Error; + fn try_from(val: CK_INFO) -> Result { + if val.flags != 0 { + return Err(Error::InvalidValue); + } + Ok(Self { + cryptoki_version: val.cryptokiVersion.into(), + manufacturer_id: string_from_blank_padded(&val.manufacturerID), + library_description: string_from_blank_padded(&val.libraryDescription), + library_version: val.libraryVersion.into(), + }) } } From f18c73ee6b6974d75584ce2f0f3801aee3b5ab0a Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 13 Jan 2022 16:02:48 -0500 Subject: [PATCH 32/36] Replace nested Options with Limit newtype Signed-off-by: Keith Koskie --- cryptoki/src/slot/mod.rs | 2 +- cryptoki/src/slot/token_info.rs | 98 ++++++++++++++++++++++++--------- cryptoki/src/types.rs | 38 ------------- 3 files changed, 74 insertions(+), 64 deletions(-) diff --git a/cryptoki/src/slot/mod.rs b/cryptoki/src/slot/mod.rs index 3f8c6934..5b3f3d39 100644 --- a/cryptoki/src/slot/mod.rs +++ b/cryptoki/src/slot/mod.rs @@ -10,7 +10,7 @@ mod slot_info; mod token_info; pub use slot_info::SlotInfo; -pub use token_info::TokenInfo; +pub use token_info::{Limit, TokenInfo}; use crate::error::{Error, Result}; use cryptoki_sys::CK_SLOT_ID; diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index 38dc084f..c940066a 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -4,8 +4,8 @@ use crate::error::{Error, Result}; use crate::string_from_blank_padded; -use crate::types::{convert_utc_time, maybe_unlimited}; -use crate::types::{MaybeUnavailable, UtcTime, Version}; +use crate::types::convert_utc_time; +use crate::types::{UtcTime, Version}; use bitflags::bitflags; use cryptoki_sys::*; use std::convert::TryFrom; @@ -36,6 +36,17 @@ bitflags! { } } +#[derive(Debug, Clone, Copy)] +/// A limiting value for the token that may or may not take an explicit value +pub enum Limit { + /// There is an explict value for this limit + Max(u64), + /// The token does not provide information about this limit + Unavailable, + /// The limit is "effectively infinite" and may be treated as such + Infinite, +} + /// Information about a token #[derive(Debug, Clone)] pub struct TokenInfo { @@ -44,9 +55,9 @@ pub struct TokenInfo { model: String, // 16 serial_number: String, // 16 flags: TokenInfoFlags, - max_session_count: Option>, + max_session_count: Limit, session_count: Option, - max_rw_session_count: Option>, + max_rw_session_count: Limit, rw_session_count: Option, max_pin_len: usize, min_pin_len: usize, @@ -58,6 +69,53 @@ pub struct TokenInfo { firmware_version: Version, utc_time: Option, } +trait MaybeUnavailable: Sized { + fn maybe_unavailable(value: CK_ULONG) -> Option; +} + +impl MaybeUnavailable for usize { + fn maybe_unavailable(value: CK_ULONG) -> Option { + if value == CK_UNAVAILABLE_INFORMATION { + None + } else { + Some(value as usize) + } + } +} + +impl MaybeUnavailable for u64 { + fn maybe_unavailable(value: CK_ULONG) -> Option { + if value == CK_UNAVAILABLE_INFORMATION { + None + } else { + // Must have cast for when ulong is 32 bits + // Must have lint suppression when ulong is 64 bits + #[allow(trivial_numeric_casts)] + Some(value as u64) + } + } +} + +/// Flattens both `Infinite` and `Unavailable` to `None`, +impl From for Option { + fn from(limit: Limit) -> Self { + match limit { + Limit::Unavailable | Limit::Infinite => None, + Limit::Max(n) => Some(n), + } + } +} + +fn maybe_unlimited(value: CK_ULONG) -> Limit { + match value { + CK_UNAVAILABLE_INFORMATION => Limit::Unavailable, + CK_EFFECTIVELY_INFINITE => Limit::Infinite, + // Must have cast for when ulong is 32 bits + // Must have lint suppression when ulong is 64 bits + #[allow(trivial_numeric_casts)] + _ => Limit::Max(value as u64), + } +} impl TokenInfo { /// An application-defined label, assigned during token initialization @@ -280,11 +338,8 @@ impl TokenInfo { } /// The maximum number of sessions that can be opened with the token at one - /// time by a single application - /// If `None`, this information was unavailable. - /// If `Some(None)` there is no maximum, meaning the value is effectively infinite - /// If `Some(Some(n))` the maximum number of sessions is `n` - pub fn max_session_count(&self) -> Option> { + /// time by a single application. + pub fn max_session_count(&self) -> Limit { self.max_session_count } @@ -295,11 +350,8 @@ impl TokenInfo { } /// The maximum number of read/write sessions that can be opened with the - /// token at one time by a single application - /// If `None`, this information was unavailable. - /// If `Some(None)` there is no maximum, meaning the value is effectively infinite - /// If `Some(Some(n))` the maximum number of read/write sessions is `n` - pub fn max_rw_session_count(&self) -> Option> { + /// token at one time by a single application. + pub fn max_rw_session_count(&self) -> Limit { self.max_rw_session_count } @@ -406,7 +458,7 @@ impl TryFrom for TokenInfo { #[cfg(test)] mod test { - use super::{TokenInfo, TokenInfoFlags}; + use super::{Limit, TokenInfo, TokenInfoFlags}; use crate::types::{UtcTime, Version}; #[test] @@ -431,9 +483,9 @@ SO_PIN_TO_BE_CHANGED | ERROR_STATE"; model: String::from("Token Model"), serial_number: String::from("Serial Number"), flags: TokenInfoFlags::empty(), - max_session_count: Some(Some(100)), // max == 100 - session_count: None, // unavailable - max_rw_session_count: Some(None), // max == infinite + max_session_count: Limit::Max(100), // max == 100 + session_count: None, // unavailable + max_rw_session_count: Limit::Infinite, // max == infinite rw_session_count: Some(1), max_pin_len: 16, min_pin_len: 4, @@ -458,15 +510,11 @@ SO_PIN_TO_BE_CHANGED | ERROR_STATE"; model: "Token Model", serial_number: "Serial Number", flags: (empty), - max_session_count: Some( - Some( - 100, - ), + max_session_count: Max( + 100, ), session_count: None, - max_rw_session_count: Some( - None, - ), + max_rw_session_count: Infinite, rw_session_count: Some( 1, ), diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index 6e2365e1..7236a78d 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -140,44 +140,6 @@ impl std::fmt::Display for Ulong { } } -pub(crate) trait MaybeUnavailable: Sized { - fn maybe_unavailable(value: CK_ULONG) -> Option; -} - -impl MaybeUnavailable for usize { - fn maybe_unavailable(value: CK_ULONG) -> Option { - if value == CK_UNAVAILABLE_INFORMATION { - None - } else { - Some(value as usize) - } - } -} - -impl MaybeUnavailable for u64 { - fn maybe_unavailable(value: CK_ULONG) -> Option { - if value == CK_UNAVAILABLE_INFORMATION { - None - } else { - // Must have cast for when ulong is 32 bits - // Must have lint suppression when ulong is 64 bits - #[allow(trivial_numeric_casts)] - Some(value as u64) - } - } -} - -pub(crate) fn maybe_unlimited(value: CK_ULONG) -> Option> { - match value { - CK_UNAVAILABLE_INFORMATION => None, - CK_EFFECTIVELY_INFINITE => Some(None), - // Must have cast for when ulong is 32 bits - // Must have lint suppression when ulong is 64 bits - #[allow(trivial_numeric_casts)] - _ => Some(Some(value as u64)), - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] /// Represents a version pub struct Version { From 40810eef84425ac3c7850c0d439d0d2531951ded Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Thu, 13 Jan 2022 16:04:13 -0500 Subject: [PATCH 33/36] Add comment to explain test condition Signed-off-by: Keith Koskie --- cryptoki/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index 7236a78d..36ef37f8 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -272,6 +272,7 @@ mod test { #[test] fn utc_time_convert_bad() { + // Year starts with a non-numeric value ('A') let invalid: [u8; 16] = [ 0x41, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, From 7a0ad301730e93b8818ec5ba3cfc29e3bdb4285b Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 14 Jan 2022 11:41:16 -0500 Subject: [PATCH 34/36] Move Option variants to single scope A silly mistake on my part. The token may not have a clock, so returning an Option is appropriate. But that designation was improperly split between the conversion function and the getter for the token. This removes the option from the conversion into the getter. Signed-off-by: Keith Koskie --- cryptoki/src/slot/token_info.rs | 2 +- cryptoki/src/types.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index c940066a..bb7e4d4b 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -429,7 +429,7 @@ impl TryFrom for TokenInfo { fn try_from(val: CK_TOKEN_INFO) -> Result { let flags = TokenInfoFlags::from_bits_truncate(val.flags); let utc_time = if flags.contains(TokenInfoFlags::CLOCK_ON_TOKEN) { - convert_utc_time(val.utcTime)? + Some(convert_utc_time(val.utcTime)?) } else { None }; diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index 36ef37f8..f55525ac 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -229,17 +229,17 @@ impl UtcTime { } // UTC time has the format YYYYMMDDhhmmss00 as ASCII digits -pub(crate) fn convert_utc_time(orig: [u8; 16]) -> Result> { +pub(crate) fn convert_utc_time(orig: [u8; 16]) -> Result { // Note: No validaiton of these values beyond being ASCII digits // because PKCS#11 doesn't impose any such restrictions. - Ok(Some(UtcTime { + Ok(UtcTime { year: std::str::from_utf8(&orig[0..4])?.parse()?, month: std::str::from_utf8(&orig[4..6])?.parse()?, day: std::str::from_utf8(&orig[6..8])?.parse()?, hour: std::str::from_utf8(&orig[8..10])?.parse()?, minute: std::str::from_utf8(&orig[10..12])?.parse()?, second: std::str::from_utf8(&orig[12..14])?.parse()?, - })) + }) } #[cfg(test)] @@ -261,7 +261,7 @@ mod test { 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, ]; - let valid = convert_utc_time(valid).unwrap().unwrap(); + let valid = convert_utc_time(valid).unwrap(); assert_eq!(valid.year, UTC_TIME.year); assert_eq!(valid.month, UTC_TIME.month); assert_eq!(valid.day, UTC_TIME.day); From aae8de047159450eb61bc4f016909891e1f7656e Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Fri, 14 Jan 2022 12:26:43 -0500 Subject: [PATCH 35/36] Reduce visibility of Version constructor This function was added in 5bb40c6 to support unit testing but does not need to be public. Because it's only used during testing, it triggers a dead code lint unless limited to cfg(test). Signed-off-by: Keith Koskie --- cryptoki/src/types.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cryptoki/src/types.rs b/cryptoki/src/types.rs index f55525ac..fd3c7b75 100644 --- a/cryptoki/src/types.rs +++ b/cryptoki/src/types.rs @@ -155,7 +155,8 @@ impl std::fmt::Display for Version { impl Version { /// Construct a new version - pub fn new(major: u8, minor: u8) -> Self { + #[cfg(test)] + pub(crate) fn new(major: u8, minor: u8) -> Self { Self { major, minor } } From b15eecde967cc70d60bf03964de6243804b76fd5 Mon Sep 17 00:00:00 2001 From: Keith Koskie Date: Tue, 25 Jan 2022 09:58:07 -0500 Subject: [PATCH 36/36] Minor fix/enhance of a few comments Signed-off-by: Keith Koskie --- cryptoki/src/slot/slot_info.rs | 2 +- cryptoki/src/slot/token_info.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cryptoki/src/slot/slot_info.rs b/cryptoki/src/slot/slot_info.rs index d0cfa90b..2dff9d01 100644 --- a/cryptoki/src/slot/slot_info.rs +++ b/cryptoki/src/slot/slot_info.rs @@ -47,7 +47,7 @@ impl SlotInfo { /// /// **[Conformance](crate#conformance-notes):** /// If this slot does not represent a removable device, a token is *always* - /// considered to be present. That is, `slot.removable device() == false` + /// considered to be present. That is, `slot.removable_device() == false` /// implies `slot.token_present() == true`. pub fn token_present(&self) -> bool { self.flags.contains(SlotInfoFlags::TOKEN_PRESENT) diff --git a/cryptoki/src/slot/token_info.rs b/cryptoki/src/slot/token_info.rs index bb7e4d4b..ebb91a04 100644 --- a/cryptoki/src/slot/token_info.rs +++ b/cryptoki/src/slot/token_info.rs @@ -50,10 +50,14 @@ pub enum Limit { /// Information about a token #[derive(Debug, Clone)] pub struct TokenInfo { - label: String, // 32 - manufacturer_id: String, // 32 - model: String, // 16 - serial_number: String, // 16 + // The following four strings are limited in size based on + // the orignating struct definition. Sizes are in *bytes* + // but UTF-8 data may represent fewer characters. + // Original buffers were space (0x20) padded. + label: String, // len <= 32 bytes + manufacturer_id: String, // len <= 32 bytes + model: String, // len <= 16 bytes + serial_number: String, // len <= 16 bytes flags: TokenInfoFlags, max_session_count: Limit, session_count: Option,