use std::sync::LazyLock;
use crate::encryption;
use crate::error::Result;
use crate::kasn1::*;
use crate::mechanism::*;
use crate::object::Object;
use crate::pkcs11::*;
use crate::token::Token;
#[cfg(feature = "fips")]
use crate::fips;
#[derive(Debug)]
pub struct SessionSearch {
handles: Vec<CK_OBJECT_HANDLE>,
in_use: bool,
}
impl SearchOperation for SessionSearch {
fn results(&mut self, max: usize) -> Result<Vec<CK_OBJECT_HANDLE>> {
if !self.in_use {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
let mut amount = self.handles.len();
if max < amount {
amount = max;
}
Ok(self.handles.drain(0..amount).collect())
}
fn finalized(&self) -> bool {
false
}
}
#[derive(Debug)]
pub enum OpLoginStatus {
NotInitialized,
NotRequired,
Required,
LoginOk,
}
pub trait ManageOperation {
fn cancel_operation(so: &mut SessionOperations) -> Result<()>;
fn check_op(so: &SessionOperations) -> Result<()>;
fn check_no_op(so: &SessionOperations) -> Result<()>;
fn get_op(so: &mut SessionOperations) -> Result<&mut Self>;
fn set_op(so: &mut SessionOperations, op: Box<Self>);
}
macro_rules! impl_mop {
($optype:ident, $($opname:ident).+) => {
impl ManageOperation for dyn $optype {
fn cancel_operation(so: &mut SessionOperations) -> Result<()> {
so.$($opname).+ = None;
Ok(())
}
fn check_op(so: &SessionOperations) -> Result<()> {
if let Some(ref o) = so.$($opname).+ {
if ! o.finalized() {
return Ok(());
}
}
Err(CKR_OPERATION_NOT_INITIALIZED)?
}
fn check_no_op(so: &SessionOperations) -> Result<()> {
if let Some(ref o) = so.$($opname).+ {
if ! o.finalized() {
return Err(CKR_OPERATION_ACTIVE)?;
}
}
Ok(())
}
fn get_op(so: &mut SessionOperations) -> Result<&mut Self> {
match so.$($opname).+ {
Some(ref mut o) => if o.finalized() {
Err(CKR_OPERATION_NOT_INITIALIZED)?
} else {
Ok(&mut **o)
},
None => Err(CKR_OPERATION_NOT_INITIALIZED)?,
}
}
fn set_op(so: &mut SessionOperations, op: Box<Self>) {
so.$($opname).+ = Some(op);
}
}
};
}
#[derive(Debug)]
pub struct SessionOperations {
msg_encryption: Option<Box<dyn MsgEncryption>>,
msg_decryption: Option<Box<dyn MsgDecryption>>,
search: Option<Box<dyn SearchOperation>>,
encryption: Option<Box<dyn Encryption>>,
decryption: Option<Box<dyn Decryption>>,
digest: Option<Box<dyn Digest>>,
sign: Option<Box<dyn Sign>>,
verify: Option<Box<dyn Verify>>,
verifysig: Option<Box<dyn VerifySignature>>,
}
impl_mop!(MsgEncryption, msg_encryption);
impl_mop!(MsgDecryption, msg_decryption);
impl_mop!(SearchOperation, search);
impl_mop!(Encryption, encryption);
impl_mop!(Decryption, decryption);
impl_mop!(Digest, digest);
impl_mop!(Sign, sign);
impl_mop!(Verify, verify);
impl_mop!(VerifySignature, verifysig);
struct SaveStateData {
mech: CK_MECHANISM_TYPE,
data: Vec<u8>,
}
#[derive(Default)]
struct SaveState {
msgenc: Option<SaveStateData>,
msgdec: Option<SaveStateData>,
enc: Option<SaveStateData>,
dec: Option<SaveStateData>,
dgst: Option<SaveStateData>,
sig: Option<SaveStateData>,
ver: Option<SaveStateData>,
versig: Option<SaveStateData>,
}
#[derive(asn1::Asn1Read, asn1::Asn1Write, Clone)]
struct OperationStateData<'a> {
mech: CK_MECHANISM_TYPE,
data: &'a [u8],
}
#[derive(asn1::Asn1Read, asn1::Asn1Write, Default)]
struct OperationState<'a> {
version: u32,
#[implicit(0)]
msgenc: Option<OperationStateData<'a>>,
#[implicit(1)]
msgdec: Option<OperationStateData<'a>>,
#[implicit(2)]
enc: Option<OperationStateData<'a>>,
#[implicit(3)]
dec: Option<OperationStateData<'a>>,
#[implicit(4)]
dgst: Option<OperationStateData<'a>>,
#[implicit(5)]
sig: Option<OperationStateData<'a>>,
#[implicit(6)]
ver: Option<OperationStateData<'a>>,
#[implicit(7)]
versig: Option<OperationStateData<'a>>,
}
static OPSTATE_DATA_OVERHEAD: LazyLock<usize> = LazyLock::new(|| {
let empty_state = OperationStateData {
mech: CK_UNAVAILABLE_INFORMATION,
data: &[],
};
asn1::write_single(&empty_state).unwrap().len()
});
static OPSTATE_MAX_OVERHEAD: LazyLock<usize> = LazyLock::new(|| {
let empty_data = OperationStateData {
mech: CK_UNAVAILABLE_INFORMATION,
data: &[],
};
let empty_state = OperationState {
version: u32::MAX,
msgenc: Some(empty_data.clone()),
msgdec: Some(empty_data.clone()),
enc: Some(empty_data.clone()),
dec: Some(empty_data.clone()),
dgst: Some(empty_data.clone()),
sig: Some(empty_data.clone()),
ver: Some(empty_data.clone()),
versig: Some(empty_data.clone()),
};
asn1::write_single(&empty_state).unwrap().len()
});
static OPSTATE_ENC_OVERHEAD: LazyLock<usize> = LazyLock::new(|| {
let empty_data = KProtectedData {
algorithm: Box::new(KAlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: KAlgorithmParameters::Aes256Gcm(KGCMParams {
aes_iv: [0u8; encryption::AES_GCM_IV_LEN],
aes_tag: [0u8; encryption::AES_GCM_TAG_LEN],
}),
}),
data: &[],
signature: None,
};
asn1::write_single(&empty_data).unwrap().len()
});
static OPSTATE_AAD: &str = "Kryoptic_State";
static OPSTATE_AES_KEY: LazyLock<Object> =
LazyLock::new(|| encryption::ephemeral_key());
trait ManageOperationState {
fn op_state_size(so: &mut SessionOperations) -> Result<usize>;
fn op_state_save(so: &mut SessionOperations) -> Result<SaveStateData>;
}
macro_rules! impl_mop_state {
($optype:ident, $($opname:ident).+) => {
impl ManageOperationState for dyn $optype {
fn op_state_size(so: &mut SessionOperations) -> Result<usize> {
match so.$($opname).+ {
Some(ref mut o) => {
if o.finalized() {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
o.state_size()
}
None => Err(CKR_OPERATION_NOT_INITIALIZED)?,
}
}
fn op_state_save(
so: &mut SessionOperations
) -> Result<SaveStateData> {
match so.$($opname).+ {
Some(ref mut o) => {
if o.finalized() {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
let mut data = vec![0u8; o.state_size()?];
let dsize = o.state_save(data.as_mut_slice())?;
data.resize(dsize, 0);
Ok(SaveStateData {
mech: o.mechanism()?,
data: data,
})
}
None => Err(CKR_OPERATION_NOT_INITIALIZED)?,
}
}
}
};
}
impl_mop_state!(MsgEncryption, msg_encryption);
impl_mop_state!(MsgDecryption, msg_decryption);
impl_mop_state!(Encryption, encryption);
impl_mop_state!(Decryption, decryption);
impl_mop_state!(Digest, digest);
impl_mop_state!(Sign, sign);
impl_mop_state!(Verify, verify);
impl_mop_state!(VerifySignature, verifysig);
impl SessionOperations {
pub fn new() -> SessionOperations {
SessionOperations {
msg_encryption: None,
msg_decryption: None,
search: None,
encryption: None,
decryption: None,
digest: None,
sign: None,
verify: None,
verifysig: None,
}
}
}
#[derive(Debug)]
pub struct Session {
info: CK_SESSION_INFO,
operations: SessionOperations,
login_status: OpLoginStatus,
fips_indicator: Option<bool>,
}
impl Session {
pub fn new(
slotid: CK_SLOT_ID,
user_type: CK_USER_TYPE,
flags: CK_FLAGS,
) -> Result<Session> {
if flags & CKF_SERIAL_SESSION != CKF_SERIAL_SESSION {
return Err(CKR_ARGUMENTS_BAD)?;
}
let rw = flags & CKF_RW_SESSION == CKF_RW_SESSION;
Ok(Session {
info: CK_SESSION_INFO {
slotID: slotid,
state: match user_type {
CK_UNAVAILABLE_INFORMATION => {
if rw {
CKS_RW_PUBLIC_SESSION
} else {
CKS_RO_PUBLIC_SESSION
}
}
CKU_USER => {
if rw {
CKS_RW_USER_FUNCTIONS
} else {
CKS_RO_USER_FUNCTIONS
}
}
CKU_SO => {
if rw {
CKS_RW_USER_FUNCTIONS
} else {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
}
_ => return Err(CKR_GENERAL_ERROR)?,
},
flags: flags,
ulDeviceError: 0,
},
operations: SessionOperations::new(),
login_status: OpLoginStatus::NotInitialized,
fips_indicator: None,
})
}
pub fn get_session_info(&self) -> &CK_SESSION_INFO {
&self.info
}
#[cfg(feature = "fips")]
pub fn set_fips_indicator(&mut self, flag: bool) {
match self.fips_indicator {
Some(b) => {
if !b {
return;
}
}
None => (),
}
self.fips_indicator = Some(flag)
}
#[cfg(feature = "fips")]
pub fn get_fips_indicator(&self) -> Option<bool> {
self.fips_indicator
}
pub fn get_last_validation_flags(&self) -> CK_FLAGS {
#[cfg(feature = "fips")]
if self.fips_indicator == Some(true) {
return fips::indicators::KRF_FIPS;
}
0
}
#[cfg(feature = "fips")]
pub fn reset_fips_indicator(&mut self) {
self.fips_indicator = None;
}
pub fn get_slot_id(&self) -> CK_SLOT_ID {
self.info.slotID
}
pub fn change_session_state(&mut self, user_type: CK_USER_TYPE) -> CK_RV {
match self.info.state {
CKS_RO_PUBLIC_SESSION => match user_type {
CK_UNAVAILABLE_INFORMATION => CKR_OK,
CKU_USER => {
self.info.state = CKS_RO_USER_FUNCTIONS;
CKR_OK
}
CKU_SO => CKR_OPERATION_NOT_INITIALIZED,
_ => CKR_USER_TYPE_INVALID,
},
CKS_RW_PUBLIC_SESSION => match user_type {
CK_UNAVAILABLE_INFORMATION => CKR_OK,
CKU_USER => {
self.info.state = CKS_RW_USER_FUNCTIONS;
CKR_OK
}
CKU_SO => {
self.info.state = CKS_RW_SO_FUNCTIONS;
CKR_OK
}
_ => CKR_USER_TYPE_INVALID,
},
CKS_RO_USER_FUNCTIONS => match user_type {
CK_UNAVAILABLE_INFORMATION => {
self.info.state = CKS_RO_PUBLIC_SESSION;
CKR_OK
}
CKU_USER => CKR_OK,
CKU_SO => CKR_USER_ANOTHER_ALREADY_LOGGED_IN,
_ => CKR_USER_TYPE_INVALID,
},
CKS_RW_USER_FUNCTIONS => match user_type {
CK_UNAVAILABLE_INFORMATION => {
self.info.state = CKS_RW_PUBLIC_SESSION;
CKR_OK
}
CKU_USER => CKR_OK,
CKU_SO => CKR_USER_ANOTHER_ALREADY_LOGGED_IN,
_ => CKR_USER_TYPE_INVALID,
},
CKS_RW_SO_FUNCTIONS => match user_type {
CK_UNAVAILABLE_INFORMATION => {
self.info.state = CKS_RW_PUBLIC_SESSION;
CKR_OK
}
CKU_USER => CKR_USER_ANOTHER_ALREADY_LOGGED_IN,
CKU_SO => CKR_OK,
_ => CKR_USER_TYPE_INVALID,
},
_ => CKR_GENERAL_ERROR,
}
}
pub fn is_writable(&self) -> bool {
match self.info.state {
CKS_RW_PUBLIC_SESSION => true,
CKS_RW_USER_FUNCTIONS => true,
CKS_RW_SO_FUNCTIONS => true,
_ => false,
}
}
pub fn new_search_operation(
&mut self,
token: &mut Token,
template: &[CK_ATTRIBUTE],
) -> Result<()> {
self.check_no_op::<dyn SearchOperation>()?;
self.set_operation::<dyn SearchOperation>(
Box::new(SessionSearch {
handles: token.search_objects(template)?,
in_use: true,
}),
false,
);
Ok(())
}
pub fn cancel_operation<O: ManageOperation + ?Sized>(
&mut self,
) -> Result<()> {
O::cancel_operation(&mut self.operations)
}
pub fn check_op<O: ManageOperation + ?Sized>(&self) -> Result<()> {
O::check_op(&self.operations)
}
pub fn check_no_op<O: ManageOperation + ?Sized>(&self) -> Result<()> {
O::check_no_op(&self.operations)
}
pub fn check_login_status(&self) -> Result<()> {
match self.login_status {
OpLoginStatus::NotInitialized => {
Err(CKR_OPERATION_NOT_INITIALIZED)?
}
OpLoginStatus::NotRequired => Ok(()),
OpLoginStatus::Required => Err(CKR_USER_NOT_LOGGED_IN)?,
OpLoginStatus::LoginOk => Ok(()),
}
}
pub fn get_operation<O: ManageOperation + ?Sized>(
&mut self,
) -> Result<&mut O> {
self.check_login_status()?;
O::get_op(&mut self.operations)
}
pub fn set_operation<O: ManageOperation + ?Sized>(
&mut self,
op: Box<O>,
needs_login: bool,
) {
self.fips_indicator = None;
self.login_status = if needs_login {
OpLoginStatus::Required
} else {
OpLoginStatus::NotRequired
};
O::set_op(&mut self.operations, op);
}
pub fn set_login_ok(&mut self) {
self.login_status = OpLoginStatus::LoginOk;
}
pub fn state_size(&mut self) -> Result<usize> {
let mut ret = CKR_OPERATION_NOT_INITIALIZED;
let mut total_size = *OPSTATE_MAX_OVERHEAD + *OPSTATE_ENC_OVERHEAD;
macro_rules! op_size {
($optype:ident) => {
match <dyn $optype>::op_state_size(&mut self.operations) {
Ok(s) => {
total_size += (*OPSTATE_DATA_OVERHEAD + s);
ret = CKR_OK;
}
Err(e) => match e.rv() {
CKR_STATE_UNSAVEABLE => {
if ret != CKR_OK {
ret = CKR_STATE_UNSAVEABLE;
}
}
CKR_OPERATION_NOT_INITIALIZED => (),
_ => return Err(e),
},
}
};
}
op_size!(MsgEncryption);
op_size!(MsgDecryption);
op_size!(Encryption);
op_size!(Decryption);
op_size!(Digest);
op_size!(Sign);
op_size!(Verify);
op_size!(VerifySignature);
if ret != CKR_OK {
return Err(ret)?;
}
Ok(total_size)
}
pub fn state_save(
&mut self,
mechanisms: &Mechanisms,
state: &mut [u8],
) -> Result<usize> {
let mut savestate = SaveState::default();
let mut opstate = OperationState {
version: 1,
..Default::default()
};
let mut ret = CKR_OPERATION_NOT_INITIALIZED;
macro_rules! op_save {
($optype:ident, $member:ident) => {
match <dyn $optype>::op_state_save(&mut self.operations) {
Ok(s) => {
savestate.$member = Some(s);
if let Some(ref m) = savestate.$member {
opstate.$member = Some(OperationStateData {
mech: m.mech,
data: m.data.as_slice(),
});
}
ret = CKR_OK;
}
Err(e) => match e.rv() {
CKR_STATE_UNSAVEABLE => {
if ret != CKR_OK {
ret = CKR_STATE_UNSAVEABLE;
}
}
CKR_OPERATION_NOT_INITIALIZED => (),
_ => return Err(e),
},
}
};
}
op_save!(MsgEncryption, msgenc);
op_save!(MsgDecryption, msgdec);
op_save!(Encryption, enc);
op_save!(Decryption, dec);
op_save!(Digest, dgst);
op_save!(Sign, sig);
op_save!(Verify, ver);
op_save!(VerifySignature, versig);
if ret != CKR_OK {
return Err(ret)?;
}
let data = match asn1::write_single(&opstate) {
Ok(d) => d,
Err(_) => return Err(CKR_GENERAL_ERROR)?,
};
let (gcm, edata) = encryption::aes_gcm_encrypt(
mechanisms,
&(*OPSTATE_AES_KEY),
OPSTATE_AAD.as_bytes(),
data.as_slice(),
)?;
let pdata = KProtectedData {
algorithm: Box::new(KAlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: KAlgorithmParameters::Aes256Gcm(gcm),
}),
data: &edata,
signature: None,
};
match asn1::write_single(&pdata) {
Ok(der) => {
if der.len() > state.len() {
Err(CKR_GENERAL_ERROR)?
} else {
state[..der.len()].clone_from_slice(der.as_slice());
Ok(der.len())
}
}
Err(_) => Err(CKR_GENERAL_ERROR)?,
}
}
pub fn state_restore(
&mut self,
mechanisms: &Mechanisms,
state: &[u8],
key: Option<&Object>,
) -> Result<()> {
let pdata = match asn1::parse_single::<KProtectedData>(state) {
Ok(p) => p,
Err(_) => return Err(CKR_SAVED_STATE_INVALID)?,
};
let gcm = match &pdata.algorithm.params {
KAlgorithmParameters::Aes256Gcm(gcm) => gcm,
_ => return Err(CKR_SAVED_STATE_INVALID)?,
};
let data = encryption::aes_gcm_decrypt(
mechanisms,
&(*OPSTATE_AES_KEY),
gcm,
OPSTATE_AAD.as_bytes(),
pdata.data,
)
.map_err(|_| CKR_SAVED_STATE_INVALID)?;
let opstate = match asn1::parse_single::<OperationState>(&data) {
Ok(s) => s,
Err(_) => return Err(CKR_SAVED_STATE_INVALID)?,
};
if opstate.version != 1 {
return Err(CKR_SAVED_STATE_INVALID)?;
}
if let Some(_op) = opstate.msgenc {
return Err(CKR_GENERAL_ERROR)?;
}
if let Some(_op) = opstate.msgdec {
return Err(CKR_GENERAL_ERROR)?;
}
if let Some(_op) = opstate.enc {
return Err(CKR_GENERAL_ERROR)?;
}
if let Some(_op) = opstate.dec {
return Err(CKR_GENERAL_ERROR)?;
}
if let Some(op) = opstate.dgst {
if key.is_some() {
return Err(CKR_KEY_NOT_NEEDED)?;
}
let mech = mechanisms.get(op.mech)?;
let operation = mech.digest_restore(op.mech, op.data)?;
<dyn Digest>::set_op(&mut self.operations, operation);
}
if let Some(op) = opstate.sig {
let op_key = key.ok_or(CKR_KEY_NEEDED)?;
let mech_h = mechanisms.get(op.mech)?;
let operation = mech_h.sign_restore(op.mech, op_key, op.data)?;
<dyn Sign>::set_op(&mut self.operations, operation);
}
if let Some(op) = opstate.ver {
let op_key = key.ok_or(CKR_KEY_NEEDED)?;
let mech_h = mechanisms.get(op.mech)?;
let operation = mech_h.verify_restore(op.mech, op_key, op.data)?;
<dyn Verify>::set_op(&mut self.operations, operation);
}
if let Some(_op) = opstate.versig {
return Err(CKR_SAVED_STATE_INVALID)?;
}
Ok(())
}
}