use std::fmt::Debug;
use crate::attribute::CkAttrs;
use crate::error::Result;
use crate::mechanism::*;
use crate::misc::{bytes_to_vec, cast_params};
use crate::object::{default_key_attributes, Object, ObjectFactories};
use crate::pkcs11::*;
#[cfg(feature = "fips")]
use ossl::fips::FipsApproval;
#[cfg(feature = "fips")]
use crate::fips::indicators::is_key_approved;
#[derive(Debug)]
struct KeyInfo {
sensitive: bool,
extractable: bool,
always_sensitive: bool,
never_extractable: bool,
value: Vec<u8>,
}
impl Drop for KeyInfo {
fn drop(&mut self) {
ossl::zeromem(self.value.as_mut_slice());
}
}
impl KeyInfo {
pub fn new_from_object(obj: &Object) -> Result<KeyInfo> {
Ok(KeyInfo {
sensitive: obj.is_sensitive(),
extractable: obj.is_extractable(),
always_sensitive: obj.is_always_sensitive(),
never_extractable: obj.is_never_extractable(),
value: obj.get_attr_as_bytes(CKA_VALUE)?.clone(),
})
}
}
#[derive(Debug)]
pub struct SimpleKDFOperation {
finalized: bool,
mech: CK_MECHANISM_TYPE,
key_handle: Option<[CK_OBJECT_HANDLE; 1]>,
key_info: Option<KeyInfo>,
data: Option<Vec<u8>>,
bit_offset: Option<CK_ULONG>,
#[cfg(feature = "fips")]
fips_approval: FipsApproval,
}
unsafe impl Send for SimpleKDFOperation {}
unsafe impl Sync for SimpleKDFOperation {}
impl SimpleKDFOperation {
pub fn new(mech: &CK_MECHANISM) -> Result<SimpleKDFOperation> {
match mech.mechanism {
CKM_CONCATENATE_BASE_AND_KEY => {
let object_handle = cast_params!(mech, CK_OBJECT_HANDLE);
Ok(SimpleKDFOperation {
finalized: false,
mech: mech.mechanism,
key_handle: Some([object_handle]),
key_info: None,
data: None,
bit_offset: None,
#[cfg(feature = "fips")]
fips_approval: FipsApproval::init(),
})
}
CKM_CONCATENATE_BASE_AND_DATA
| CKM_CONCATENATE_DATA_AND_BASE
| CKM_XOR_BASE_AND_DATA => {
let params = cast_params!(mech, CK_KEY_DERIVATION_STRING_DATA);
let data = bytes_to_vec!(params.pData, params.ulLen);
if data.len() < 1 {
return Err(CKR_MECHANISM_PARAM_INVALID)?;
}
Ok(SimpleKDFOperation {
finalized: false,
mech: mech.mechanism,
key_handle: None,
key_info: None,
data: Some(data),
bit_offset: None,
#[cfg(feature = "fips")]
fips_approval: FipsApproval::init(),
})
}
CKM_EXTRACT_KEY_FROM_KEY => {
let params = cast_params!(mech, CK_EXTRACT_PARAMS);
Ok(SimpleKDFOperation {
finalized: false,
mech: mech.mechanism,
key_handle: None,
key_info: None,
data: None,
bit_offset: Some(params),
#[cfg(feature = "fips")]
fips_approval: FipsApproval::init(),
})
}
_ => return Err(CKR_MECHANISM_INVALID)?,
}
}
}
impl MechOperation for SimpleKDFOperation {
fn mechanism(&self) -> Result<CK_MECHANISM_TYPE> {
Ok(self.mech)
}
fn finalized(&self) -> bool {
self.finalized
}
#[cfg(feature = "fips")]
fn fips_approved(&self) -> Option<bool> {
self.fips_approval.approval()
}
fn requires_objects(&self) -> Result<&[CK_OBJECT_HANDLE]> {
if let Some(h) = &self.key_handle {
return Ok(h);
}
Err(CKR_OK)?
}
fn receives_objects(&mut self, objs: &[&Object]) -> Result<()> {
if objs.len() != 1 {
return Err(CKR_GENERAL_ERROR)?;
}
self.key_info = Some(KeyInfo::new_from_object(objs[0])?);
#[cfg(feature = "fips")]
self.fips_approval.set(is_key_approved(objs[0], CKF_DERIVE));
Ok(())
}
}
impl Derive for SimpleKDFOperation {
fn derive(
&mut self,
key: &Object,
template: &[CK_ATTRIBUTE],
_mechanisms: &Mechanisms,
objfactories: &ObjectFactories,
) -> Result<Vec<Object>> {
if self.finalized {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
self.finalized = true;
key.check_key_ops(
CKO_SECRET_KEY,
CK_UNAVAILABLE_INFORMATION,
CKA_DERIVE,
)?;
let base_key_info = KeyInfo::new_from_object(key)?;
let mut tmpl = CkAttrs::from(template);
tmpl.add_missing_ulong(CKA_CLASS, &CKO_SECRET_KEY);
tmpl.add_missing_ulong(CKA_KEY_TYPE, &CKK_GENERIC_SECRET);
let other_data_len = match self.mech {
CKM_CONCATENATE_BASE_AND_KEY => {
let another_key_info = match &self.key_info {
Some(k) => k,
None => return Err(CKR_MECHANISM_PARAM_INVALID)?,
};
if base_key_info.sensitive || another_key_info.sensitive {
tmpl.add_bool(CKA_SENSITIVE, &CK_TRUE);
}
if !base_key_info.extractable || !another_key_info.extractable {
tmpl.add_bool(CKA_EXTRACTABLE, &CK_FALSE);
}
if base_key_info.always_sensitive
&& another_key_info.always_sensitive
{
tmpl.add_bool(CKA_ALWAYS_SENSITIVE, &CK_TRUE);
}
if base_key_info.never_extractable
&& another_key_info.never_extractable
{
tmpl.add_bool(CKA_NEVER_EXTRACTABLE, &CK_TRUE);
}
another_key_info.value.len()
}
CKM_CONCATENATE_BASE_AND_DATA
| CKM_CONCATENATE_DATA_AND_BASE
| CKM_XOR_BASE_AND_DATA => {
if base_key_info.sensitive {
tmpl.add_bool(CKA_SENSITIVE, &CK_TRUE);
}
if !base_key_info.extractable {
tmpl.add_bool(CKA_EXTRACTABLE, &CK_FALSE);
}
if base_key_info.always_sensitive {
tmpl.add_bool(CKA_ALWAYS_SENSITIVE, &CK_TRUE);
}
if base_key_info.never_extractable {
tmpl.add_bool(CKA_NEVER_EXTRACTABLE, &CK_TRUE);
}
match &self.data {
Some(d) => d.len(),
None => return Err(CKR_MECHANISM_PARAM_INVALID)?,
}
}
CKM_EXTRACT_KEY_FROM_KEY => {
if base_key_info.sensitive {
tmpl.add_bool(CKA_SENSITIVE, &CK_TRUE);
}
if !base_key_info.extractable {
tmpl.add_bool(CKA_EXTRACTABLE, &CK_FALSE);
}
if base_key_info.always_sensitive {
tmpl.add_bool(CKA_ALWAYS_SENSITIVE, &CK_TRUE);
}
if base_key_info.never_extractable {
tmpl.add_bool(CKA_NEVER_EXTRACTABLE, &CK_TRUE);
}
0
}
_ => return Err(CKR_MECHANISM_INVALID)?,
};
let outlen = match self.mech {
CKM_XOR_BASE_AND_DATA => {
std::cmp::min(base_key_info.value.len(), other_data_len)
}
CKM_EXTRACT_KEY_FROM_KEY => base_key_info.value.len(),
_ => base_key_info.value.len() + other_data_len,
};
let factory =
objfactories.get_obj_factory_from_key_template(tmpl.as_slice())?;
let keylen = match tmpl.find_attr(CKA_VALUE_LEN) {
Some(a) => usize::try_from(a.to_ulong()?)?,
None => {
match factory
.as_secret_key_factory()?
.recommend_key_size(outlen)
{
Ok(len) => len,
Err(_) => return Err(CKR_TEMPLATE_INCONSISTENT)?,
}
}
};
if self.mech == CKM_EXTRACT_KEY_FROM_KEY {
let bit_offset =
self.bit_offset.ok_or(CKR_MECHANISM_PARAM_INVALID)?;
let required_bits = keylen * 8;
let available_bits = base_key_info.value.len() * 8;
if (required_bits > available_bits)
|| (bit_offset + 7) / 8 > available_bits as CK_ULONG
{
return Err(CKR_TEMPLATE_INCONSISTENT)?;
}
} else if keylen > outlen {
return Err(CKR_TEMPLATE_INCONSISTENT)?;
}
let mut secret = Vec::with_capacity(outlen);
match self.mech {
CKM_CONCATENATE_BASE_AND_KEY => {
secret.extend_from_slice(&base_key_info.value);
if let Some(k) = &self.key_info {
secret.extend_from_slice(&k.value);
} else {
return Err(CKR_MECHANISM_PARAM_INVALID)?;
}
secret.resize(keylen, 0);
}
CKM_CONCATENATE_BASE_AND_DATA => {
secret.extend_from_slice(&base_key_info.value);
if let Some(d) = &self.data {
secret.extend_from_slice(&d);
} else {
return Err(CKR_MECHANISM_PARAM_INVALID)?;
}
secret.resize(keylen, 0);
}
CKM_CONCATENATE_DATA_AND_BASE => {
if let Some(d) = &self.data {
secret.extend_from_slice(&d);
} else {
return Err(CKR_MECHANISM_PARAM_INVALID)?;
}
secret.extend_from_slice(&base_key_info.value);
secret.resize(keylen, 0);
}
CKM_XOR_BASE_AND_DATA => {
if let Some(d) = &self.data {
secret.extend_from_slice(&d);
} else {
return Err(CKR_MECHANISM_PARAM_INVALID)?;
}
secret
.iter_mut()
.zip(base_key_info.value.iter())
.for_each(|(k, b)| *k ^= *b);
secret.resize(keylen, 0);
}
CKM_EXTRACT_KEY_FROM_KEY => {
let bit_offset =
self.bit_offset.ok_or(CKR_MECHANISM_PARAM_INVALID)?
as usize;
secret.resize(keylen, 0);
let bit_shift = bit_offset % 8;
let base_byte = (bit_offset - bit_shift) / 8;
for i in 0..keylen {
let first_half = base_key_info.value
[(base_byte + i) % outlen]
<< bit_shift;
let second_half = base_key_info.value
[(base_byte + i + 1) % outlen]
>> (8 - bit_shift);
secret[i] = first_half | second_half;
}
}
_ => return Err(CKR_MECHANISM_INVALID)?,
}
let mut tmpl = CkAttrs::from(template);
tmpl.zeroize = true;
tmpl.add_vec(CKA_VALUE, secret)?;
let mut obj = factory.create(tmpl.as_slice())?;
default_key_attributes(&mut obj, self.mech)?;
#[cfg(feature = "fips")]
self.fips_approval.finalize();
Ok(vec![obj])
}
}