Skip to content

Commit

Permalink
feat: C_WaitForSlotEvent()
Browse files Browse the repository at this point in the history
  • Loading branch information
nponsard committed Aug 30, 2023
1 parent 607fdd3 commit 1338b85
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 45 deletions.
11 changes: 10 additions & 1 deletion pkcs11/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ pub mod token;
pub mod verify;

use crate::{
data::{self, DEVICE},
backend::events::{fetch_slots_state, EventsManager},
data::{self, DEVICE, EVENTS_MANAGER, TOKENS_STATE},
defs, padded_str,
};
use cryptoki_sys::{CK_INFO, CK_INFO_PTR, CK_RV, CK_VOID_PTR};
Expand Down Expand Up @@ -72,6 +73,12 @@ pub extern "C" fn C_Initialize(pInitArgs: CK_VOID_PTR) -> CK_RV {
}
}

// Initialize the events manager
*EVENTS_MANAGER.write().unwrap() = EventsManager::new();
*TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new();

fetch_slots_state();

cryptoki_sys::CKR_OK
}

Expand All @@ -80,6 +87,8 @@ pub extern "C" fn C_Finalize(pReserved: CK_VOID_PTR) -> CK_RV {
if !pReserved.is_null() {
return cryptoki_sys::CKR_ARGUMENTS_BAD;
}
EVENTS_MANAGER.write().unwrap().finalized = true;

cryptoki_sys::CKR_OK
}

Expand Down
171 changes: 149 additions & 22 deletions pkcs11/src/api/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ use cryptoki_sys::{
CK_TOKEN_INFO, CK_ULONG,
};
use log::{debug, error, trace, warn};
use nethsm_sdk_rs::{apis::default_api, models::SystemState};
use nethsm_sdk_rs::{
apis::default_api,
models::{HealthStateData, InfoData, SystemState},
};

use crate::{
backend::{
events::fetch_slots_state,
login::{LoginCtx, UserMode},
slot::get_slot,
},
data::DEVICE,
data::{DEVICE, EVENTS_MANAGER},
defs::{DEFAULT_FIRMWARE_VERSION, DEFAULT_HARDWARE_VERSION, MECHANISM_LIST},
lock_mutex, lock_session, padded_str, version_struct_from_str,
};
Expand Down Expand Up @@ -89,13 +93,16 @@ pub extern "C" fn C_GetSlotInfo(
crate::backend::login::UserMode::Guest,
);

// // fetch info from the device
// fetch info from the device

let info = match result {
Ok(info) => info,
Ok(info) => info.entity,
Err(e) => {
error!("Error getting info: {:?}", e);
return cryptoki_sys::CKR_FUNCTION_FAILED;
trace!("Error getting info: {:?}", e);
InfoData {
product: "unknown".to_string(),
vendor: "unknown".to_string(),
}
}
};

Expand All @@ -107,20 +114,22 @@ pub extern "C" fn C_GetSlotInfo(
// fetch the sysem state

let system_state = match result {
Ok(info) => info,
Ok(info) => info.entity,
Err(e) => {
error!("Error getting system state: {:?}", e);
return cryptoki_sys::CKR_FUNCTION_FAILED;
trace!("Error getting system state: {:?}", e);
HealthStateData {
state: SystemState::Unprovisioned,
}
}
};

if system_state.entity.state == SystemState::Operational {
if system_state.state == SystemState::Operational {
flags |= cryptoki_sys::CKF_TOKEN_PRESENT;
}

let info: CK_SLOT_INFO = CK_SLOT_INFO {
slotDescription: padded_str!("info.entity.product", 64),
manufacturerID: padded_str!("info.entity.vendor", 32),
slotDescription: padded_str!(info.product, 64),
manufacturerID: padded_str!(info.vendor, 32),
flags,
hardwareVersion: DEFAULT_HARDWARE_VERSION,
firmwareVersion: DEFAULT_FIRMWARE_VERSION,
Expand Down Expand Up @@ -351,17 +360,141 @@ pub extern "C" fn C_WaitForSlotEvent(
pReserved: cryptoki_sys::CK_VOID_PTR,
) -> cryptoki_sys::CK_RV {
trace!("C_WaitForSlotEvent() called");
cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED

if pSlot.is_null() {
return cryptoki_sys::CKR_ARGUMENTS_BAD;
}

fetch_slots_state();

loop {
// check if there is an event in the queue

let slot = EVENTS_MANAGER.write().unwrap().events.pop();
if let Some(slot) = slot {
unsafe {
std::ptr::write(pSlot, slot);
}
return cryptoki_sys::CKR_OK;
}

// if the dont block flag is set, return no event
if flags & cryptoki_sys::CKF_DONT_BLOCK == 1 {
return cryptoki_sys::CKR_NO_EVENT;
} else {
// Otherwise, wait for an event

// If C_Finalize() has been called, return an error
if EVENTS_MANAGER.read().unwrap().finalized {
return cryptoki_sys::CKR_CRYPTOKI_NOT_INITIALIZED;
}

// sleep for 1 second
std::thread::sleep(std::time::Duration::from_secs(1));

// fetch the slots state so we get the latest events in the next iteration
fetch_slots_state();
}
}
}

#[cfg(test)]
mod tests {
use cryptoki_sys::{CKU_USER, CK_MECHANISM_INFO};

use crate::{backend::slot::set_test_config_env, data::SESSION_MANAGER};
use cryptoki_sys::{CKF_DONT_BLOCK, CKU_USER, CK_MECHANISM_INFO};

use crate::{
api::C_Finalize,
backend::{
events::{update_slot_state, EventsManager},
slot::set_test_config_env,
},
data::{SESSION_MANAGER, TOKENS_STATE},
};

use super::*;

#[test]
fn test_wait_for_slot_event_no_event() {
set_test_config_env();
*EVENTS_MANAGER.write().unwrap() = EventsManager::new();
*TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new();

let mut slot = 0;
let result = C_WaitForSlotEvent(CKF_DONT_BLOCK, &mut slot, std::ptr::null_mut());
assert_eq!(result, cryptoki_sys::CKR_NO_EVENT);
}

#[test]
fn test_wait_for_slot_event_one_event() {
set_test_config_env();
*EVENTS_MANAGER.write().unwrap() = EventsManager::new();
*TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new();

update_slot_state(0, false);
update_slot_state(0, true);

println!("Events: {:?}", EVENTS_MANAGER.read().unwrap().events);

let mut slot = 15;
let result = C_WaitForSlotEvent(CKF_DONT_BLOCK, &mut slot, std::ptr::null_mut());
assert_eq!(result, cryptoki_sys::CKR_OK);
assert_eq!(slot, 0);
}

// we ignore this test because it requires cargo test -- --test-threads=1
#[test]
#[ignore]
fn test_wait_for_slot_event_blocking_one_event() {
set_test_config_env();
*EVENTS_MANAGER.write().unwrap() = EventsManager::new();
*TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new();

// update the slot state in a separate thread

let handle = std::thread::spawn(|| {
std::thread::sleep(std::time::Duration::from_millis(100));
update_slot_state(0, false);
update_slot_state(0, true);
});

let mut slot = 15;
let result = C_WaitForSlotEvent(0, &mut slot, std::ptr::null_mut());
handle.join().unwrap();
assert_eq!(result, cryptoki_sys::CKR_OK);
assert_eq!(slot, 0);
}

// we ignore this test because it requires cargo test -- --test-threads=1
#[test]
#[ignore]
fn test_wait_for_slot_event_blocking_finalize() {
set_test_config_env();
*EVENTS_MANAGER.write().unwrap() = EventsManager::new();
*TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new();

// update the slot state in a separate thread

let handle = std::thread::spawn(|| {
std::thread::sleep(std::time::Duration::from_millis(100));

C_Finalize(std::ptr::null_mut());
});

let mut slot = 15;
let result = C_WaitForSlotEvent(0, &mut slot, std::ptr::null_mut());
handle.join().unwrap();
println!("slot: {}", slot);
assert_eq!(result, cryptoki_sys::CKR_CRYPTOKI_NOT_INITIALIZED);
}

#[test]
fn test_wait_for_slot_event_null_slot_ptr() {
set_test_config_env();

let result = C_WaitForSlotEvent(CKF_DONT_BLOCK, std::ptr::null_mut(), std::ptr::null_mut());
assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD);
}

#[test]
fn test_get_slot_list_null_count() {
let result = C_GetSlotList(0, std::ptr::null_mut(), std::ptr::null_mut());
Expand Down Expand Up @@ -537,10 +670,4 @@ mod tests {
let result = C_InitToken(0, std::ptr::null_mut(), 0, std::ptr::null_mut());
assert_eq!(result, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED);
}

#[test]
fn test_wait_for_slot_event() {
let result = C_WaitForSlotEvent(0, std::ptr::null_mut(), std::ptr::null_mut());
assert_eq!(result, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED);
}
}
4 changes: 2 additions & 2 deletions pkcs11/src/backend/encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ fn encrypt_data(
login::UserMode::Operator,
)
.map_err(|err| {
if let Error::Api(ApiError::ResponseError(ref resp)) = err {
if let ApiError::ResponseError(ref resp) = err {
if resp.status == 400 {
if resp.content.contains("argument length") {
return Error::InvalidDataLength;
}
return Error::InvalidData;
}
}
err
err.into()
})?;

Ok(Base64::decode_vec(&output.entity.encrypted)?)
Expand Down
50 changes: 50 additions & 0 deletions pkcs11/src/backend/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use cryptoki_sys::CK_SLOT_ID;
use nethsm_sdk_rs::{apis::default_api, models::SystemState};

use crate::data::{DEVICE, EVENTS_MANAGER, TOKENS_STATE};

use super::login::LoginCtx;

pub struct EventsManager {
pub events: Vec<CK_SLOT_ID>, // list of slots that changed

// Used when CKF_DONT_BLOCK is clear and C_Finalize is called, then every blocking call to C_WaitForSlotEvent should return CKR_CRYPTOKI_NOT_INITIALIZED
pub finalized: bool,
}

impl EventsManager {
pub fn new() -> Self {
EventsManager {
events: Vec::new(),
finalized: false,
}
}
}

pub fn update_slot_state(slot_id: CK_SLOT_ID, present: bool) {
let mut tokens_state = TOKENS_STATE.lock().unwrap();
if let Some(prev) = tokens_state.get(&slot_id) {
if *prev == present {
return;
} else {
// new event
EVENTS_MANAGER.write().unwrap().events.push(slot_id);
}
}
tokens_state.insert(slot_id, present);
}

pub fn fetch_slots_state() {
for (index, slot) in DEVICE.slots.iter().enumerate() {
let mut login_ctx = LoginCtx::new(None, None, slot.instances.clone());
let status = login_ctx
.try_(
|conf| default_api::health_state_get(&conf),
super::login::UserMode::Guest,
)
.map(|state| state.entity.state == SystemState::Operational)
.unwrap_or(false);

update_slot_state(index as CK_SLOT_ID, status);
}
}
7 changes: 2 additions & 5 deletions pkcs11/src/backend/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,14 +489,11 @@ pub fn fetch_key(
debug!("Failed to fetch key {}: {:?}", key_id, err);
if matches!(
err,
Error::Api(ApiError::ResponseError(backend::ResponseContent {
status: 404,
..
}))
ApiError::ResponseError(backend::ResponseContent { status: 404, .. })
) {
return Ok(vec![]);
}
return Err(err);
return Err(err.into());
}
};

Expand Down
11 changes: 3 additions & 8 deletions pkcs11/src/backend/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::fmt::Debug;

use crate::config::config_file::UserConfig;

use super::Error;
use super::{ApiError, Error};

#[derive(Debug, Clone)]
pub struct LoginCtx {
Expand Down Expand Up @@ -65,11 +65,6 @@ impl LoginCtx {
administrator: Option<UserConfig>,
instances: Vec<Configuration>,
) -> Self {
trace!(
"Creating login context with administrator: {:?}",
administrator
);

let mut ck_state = CKS_RO_PUBLIC_SESSION;

let firt_instance = instances.first();
Expand Down Expand Up @@ -193,7 +188,7 @@ impl LoginCtx {
}

// Try to run the api call on each instance until one succeeds
pub fn try_<F, T, R>(&mut self, api_call: F, user_mode: UserMode) -> Result<R, Error>
pub fn try_<F, T, R>(&mut self, api_call: F, user_mode: UserMode) -> Result<R, ApiError>
where
F: FnOnce(Configuration) -> Result<R, apis::Error<T>> + Clone,
{
Expand All @@ -219,7 +214,7 @@ impl LoginCtx {
Err(err) => return Err(err.into()),
}
}
Err(Error::NoInstance)
Err(ApiError::NoInstance)
}

pub fn ck_state(&self) -> CK_STATE {
Expand Down
Loading

0 comments on commit 1338b85

Please sign in to comment.