use parsec_interface::operations::psa_algorithm::{
Aead, AeadWithDefaultLengthTag, Algorithm, AsymmetricSignature, Cipher, FullLengthMac, Hash,
KeyAgreement, Mac, RawKeyAgreement, SignHash,
};
use parsec_interface::operations::psa_key_attributes::{Attributes, EccFamily, Type};
use parsec_interface::requests::{Opcode, ResponseStatus};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum KeySlotStatus {
Free,
Busy,
Locked,
}
#[derive(Copy, Clone, Debug)]
pub struct AteccKeySlot {
pub ref_count: u8,
pub status: KeySlotStatus,
pub config: rust_cryptoauthlib::SlotConfig,
}
impl Default for AteccKeySlot {
fn default() -> Self {
AteccKeySlot {
ref_count: 0u8,
status: KeySlotStatus::Free,
config: rust_cryptoauthlib::SlotConfig::default(),
}
}
}
impl AteccKeySlot {
pub fn key_attr_vs_config(
&self,
slot: u8,
key_attr: &Attributes,
op: Option<Opcode>,
) -> Result<(), ResponseStatus> {
if !self.is_key_type_ok(slot, key_attr) {
return Err(ResponseStatus::PsaErrorNotSupported);
}
if !self.is_usage_flags_ok(key_attr) {
return Err(ResponseStatus::PsaErrorNotSupported);
}
if !self.is_permitted_algorithms_ok(key_attr, op) {
return Err(ResponseStatus::PsaErrorNotSupported);
}
Ok(())
}
pub fn set_slot_status(&mut self, status: KeySlotStatus) -> Result<(), ResponseStatus> {
if self.status == KeySlotStatus::Locked {
return Err(ResponseStatus::PsaErrorNotPermitted);
}
self.status = match status {
KeySlotStatus::Locked => return Err(ResponseStatus::PsaErrorNotPermitted),
_ => status,
};
Ok(())
}
fn is_key_type_ok(&self, slot: u8, key_attr: &Attributes) -> bool {
match key_attr.key_type {
Type::RawData => self.config.key_type == rust_cryptoauthlib::KeyType::ShaOrText,
Type::Hmac => !self.config.no_mac,
Type::Aes => self.config.key_type == rust_cryptoauthlib::KeyType::Aes,
Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
} => {
(key_attr.bits == 0 || key_attr.bits == 256)
&& self.config.key_type == rust_cryptoauthlib::KeyType::P256EccKey
&& self.config.ecc_key_attr.is_private
&& self.config.is_secret
}
Type::EccPublicKey {
curve_family: EccFamily::SecpR1,
} => {
(key_attr.bits == 0 || key_attr.bits == 256)
&& self.config.key_type == rust_cryptoauthlib::KeyType::P256EccKey
&& slot >= rust_cryptoauthlib::ATCA_ATECC_MIN_SLOT_IDX_FOR_PUB_KEY
}
Type::Derive | Type::DhKeyPair { .. } | Type::DhPublicKey { .. } => {
false
}
_ => false,
}
}
fn is_usage_flags_ok(&self, key_attr: &Attributes) -> bool {
let mut result = true;
if key_attr.policy.usage_flags.export() || key_attr.policy.usage_flags.copy() {
result &= match key_attr.key_type {
Type::EccKeyPair { .. } => {
self.config.key_type == rust_cryptoauthlib::KeyType::P256EccKey
&& if self.config.ecc_key_attr.is_private {
self.config.pub_info
} else {
true
}
}
_ => true,
}
}
if !result {
return result;
}
if key_attr.policy.usage_flags.sign_hash() || key_attr.policy.usage_flags.sign_message() {
result &= self.config.key_type == rust_cryptoauthlib::KeyType::P256EccKey;
result &= self.config.ecc_key_attr.is_private;
result &= self.config.ecc_key_attr.ext_sign; result &= matches!(key_attr.key_type, Type::EccKeyPair { .. });
}
if !result {
return result;
}
if key_attr.policy.usage_flags.verify_hash() || key_attr.policy.usage_flags.verify_message()
{
result &= self.config.key_type == rust_cryptoauthlib::KeyType::P256EccKey;
result &= match key_attr.key_type {
Type::EccKeyPair { .. }
if self.config.ecc_key_attr.is_private => self.config.pub_info,
_ => true,
};
}
result
}
fn is_permitted_algorithms_ok(&self, key_attr: &Attributes, op: Option<Opcode>) -> bool {
match key_attr.policy.permitted_algorithms {
Algorithm::Hash(Hash::Sha256) => true,
Algorithm::Mac(Mac::Truncated {
mac_alg:
FullLengthMac::Hmac {
hash_alg: Hash::Sha256,
},
..
})
| Algorithm::Mac(Mac::FullLength(FullLengthMac::Hmac {
hash_alg: Hash::Sha256,
})) => {
!self.config.no_mac
&& self.config.key_type != rust_cryptoauthlib::KeyType::P256EccKey
&& !self.config.ecc_key_attr.is_private
}
Algorithm::Mac(Mac::Truncated {
mac_alg: FullLengthMac::CbcMac,
..
})
| Algorithm::Mac(Mac::FullLength(FullLengthMac::CbcMac))
| Algorithm::Mac(Mac::Truncated {
mac_alg: FullLengthMac::Cmac,
..
})
| Algorithm::Mac(Mac::FullLength(FullLengthMac::Cmac)) => {
!self.config.no_mac && self.config.key_type == rust_cryptoauthlib::KeyType::Aes
}
Algorithm::Cipher(Cipher::CbcPkcs7)
| Algorithm::Cipher(Cipher::CbcNoPadding)
| Algorithm::Cipher(Cipher::EcbNoPadding)
| Algorithm::Cipher(Cipher::Ctr)
| Algorithm::Cipher(Cipher::Cfb)
| Algorithm::Cipher(Cipher::Ofb) => {
self.config.key_type == rust_cryptoauthlib::KeyType::Aes
}
Algorithm::Aead(Aead::AeadWithDefaultLengthTag(AeadWithDefaultLengthTag::Ccm))
| Algorithm::Aead(Aead::AeadWithDefaultLengthTag(AeadWithDefaultLengthTag::Gcm))
| Algorithm::Aead(Aead::AeadWithShortenedTag {
aead_alg: AeadWithDefaultLengthTag::Ccm,
..
})
| Algorithm::Aead(Aead::AeadWithShortenedTag {
aead_alg: AeadWithDefaultLengthTag::Gcm,
..
}) => self.config.key_type == rust_cryptoauthlib::KeyType::Aes,
Algorithm::AsymmetricSignature(AsymmetricSignature::Ecdsa {
hash_alg: SignHash::Specific(Hash::Sha256),
}) => {
self.config.key_type == rust_cryptoauthlib::KeyType::P256EccKey
&& match key_attr.key_type {
Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
} => {
self.config.ecc_key_attr.is_private
&& self.config.ecc_key_attr.ext_sign
&& match op {
Some(opcode) => match opcode {
Opcode::PsaImportKey => {
self.config.write_config
== rust_cryptoauthlib::WriteConfig::Encrypt
}
Opcode::PsaGenerateKey => {
matches!(
self.config.write_config,
rust_cryptoauthlib::WriteConfig::Encrypt
| rust_cryptoauthlib::WriteConfig::Never
)
}
_ => false,
},
None => true,
}
}
Type::EccPublicKey {
curve_family: EccFamily::SecpR1,
} => {
matches!(
self.config.write_config,
rust_cryptoauthlib::WriteConfig::Encrypt
| rust_cryptoauthlib::WriteConfig::Never
| rust_cryptoauthlib::WriteConfig::Always
)
}
_ => false,
}
}
Algorithm::AsymmetricSignature(AsymmetricSignature::DeterministicEcdsa {
hash_alg: SignHash::Specific(Hash::Sha256),
}) => {
false
}
Algorithm::AsymmetricEncryption(..) => {
false
}
Algorithm::KeyAgreement(KeyAgreement::Raw(RawKeyAgreement::Ecdh))
| Algorithm::KeyAgreement(KeyAgreement::WithKeyDerivation {
ka_alg: RawKeyAgreement::Ecdh,
..
}) => self.config.key_type == rust_cryptoauthlib::KeyType::P256EccKey,
_ => false,
}
}
pub fn reference_check_and_set(&mut self) -> Result<(), ()> {
if 0 < self.ref_count {
Err(())
} else {
self.ref_count = 1;
Ok(())
}
}
pub fn is_free(&self) -> bool {
matches!(self.status, KeySlotStatus::Free)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parsec_interface::operations::psa_key_attributes::{
Attributes, Lifetime, Policy, Type, UsageFlags,
};
use rust_cryptoauthlib::{EccKeyAttr, ReadKey, SlotConfig};
#[test]
fn test_is_key_type_ok() {
let slot_config = SlotConfig {
write_config: rust_cryptoauthlib::WriteConfig::Always,
key_type: rust_cryptoauthlib::KeyType::P256EccKey,
read_key: ReadKey {
encrypt_read: false,
slot_number: 0,
},
ecc_key_attr: EccKeyAttr {
is_private: false,
ext_sign: false,
int_sign: false,
ecdh_operation: false,
ecdh_secret_out: false,
},
x509id: 0,
auth_key: 0,
write_key: 0,
is_secret: false,
limited_use: false,
no_mac: true,
persistent_disable: false,
req_auth: false,
req_random: false,
lockable: false,
pub_info: false,
};
let mut key_slot = AteccKeySlot {
ref_count: 1,
status: KeySlotStatus::Busy,
config: slot_config,
};
let mut attributes = Attributes {
lifetime: Lifetime::Persistent,
key_type: Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
},
bits: 256,
policy: Policy {
usage_flags: {
let mut flags = UsageFlags::default();
let _ = flags.set_sign_hash().set_verify_hash().set_sign_message();
flags
},
permitted_algorithms: AsymmetricSignature::DeterministicEcdsa {
hash_alg: Hash::Sha256.into(),
}
.into(),
},
};
assert!(!key_slot.is_key_type_ok(0, &attributes));
key_slot.config.key_type = rust_cryptoauthlib::KeyType::P256EccKey;
key_slot.config.write_config = rust_cryptoauthlib::WriteConfig::Encrypt;
key_slot.config.is_secret = true;
key_slot.config.ecc_key_attr.is_private = true;
assert!(key_slot.is_key_type_ok(0, &attributes));
attributes.key_type = Type::Aes;
assert!(!key_slot.is_key_type_ok(0, &attributes));
attributes.key_type = Type::RawData;
assert!(!key_slot.is_key_type_ok(0, &attributes));
key_slot.config.key_type = rust_cryptoauthlib::KeyType::Aes;
attributes.key_type = Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
};
assert!(!key_slot.is_key_type_ok(0, &attributes));
attributes.key_type = Type::Aes;
assert!(key_slot.is_key_type_ok(0, &attributes));
attributes.key_type = Type::RawData;
assert!(!key_slot.is_key_type_ok(0, &attributes));
key_slot.config.key_type = rust_cryptoauthlib::KeyType::ShaOrText;
attributes.key_type = Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
};
assert!(!key_slot.is_key_type_ok(0, &attributes));
attributes.key_type = Type::Aes;
assert!(!key_slot.is_key_type_ok(0, &attributes));
}
#[test]
fn test_is_usage_flags_ok() {
let slot_config = SlotConfig {
write_config: rust_cryptoauthlib::WriteConfig::Always,
key_type: rust_cryptoauthlib::KeyType::P256EccKey,
read_key: ReadKey {
encrypt_read: false,
slot_number: 0,
},
ecc_key_attr: EccKeyAttr {
is_private: true,
ext_sign: true,
int_sign: false,
ecdh_operation: false,
ecdh_secret_out: false,
},
x509id: 0,
auth_key: 0,
write_key: 0,
is_secret: false,
limited_use: false,
no_mac: true,
persistent_disable: false,
req_auth: false,
req_random: false,
lockable: false,
pub_info: true,
};
let mut key_slot = AteccKeySlot {
ref_count: 1,
status: KeySlotStatus::Busy,
config: slot_config,
};
let mut attributes = Attributes {
lifetime: Lifetime::Persistent,
key_type: Type::EccKeyPair {
curve_family: EccFamily::SecpR1,
},
bits: 256,
policy: Policy {
usage_flags: {
let mut flags = UsageFlags::default();
let _ = flags.set_verify_hash().set_export().set_copy();
flags
},
permitted_algorithms: AsymmetricSignature::DeterministicEcdsa {
hash_alg: Hash::Sha256.into(),
}
.into(),
},
};
assert!(key_slot.is_usage_flags_ok(&attributes));
key_slot.config.pub_info = false;
assert!(!key_slot.is_usage_flags_ok(&attributes));
key_slot.config.pub_info = false;
assert!(!key_slot.is_usage_flags_ok(&attributes));
key_slot.config.ecc_key_attr.is_private = false;
assert!(key_slot.is_usage_flags_ok(&attributes));
let mut flags = UsageFlags::default();
let _ = flags.set_verify_hash();
attributes.policy.usage_flags = flags;
assert!(key_slot.is_usage_flags_ok(&attributes));
let mut flags = UsageFlags::default();
let _ = flags.set_verify_hash().set_export().set_copy();
attributes.policy.usage_flags = flags;
key_slot.config.key_type = rust_cryptoauthlib::KeyType::Aes;
assert!(!key_slot.is_usage_flags_ok(&attributes));
attributes.policy.usage_flags = UsageFlags::default();
assert!(key_slot.is_usage_flags_ok(&attributes));
}
#[test]
fn test_is_permitted_algorithms_ok() {
let slot_config = SlotConfig {
write_config: rust_cryptoauthlib::WriteConfig::Encrypt,
key_type: rust_cryptoauthlib::KeyType::P256EccKey,
read_key: ReadKey {
encrypt_read: false,
slot_number: 0,
},
ecc_key_attr: EccKeyAttr {
is_private: true,
ext_sign: true,
int_sign: false,
ecdh_operation: false,
ecdh_secret_out: false,
},
x509id: 0,
auth_key: 0,
write_key: 0,
is_secret: true,
limited_use: false,
no_mac: false,
persistent_disable: false,
req_auth: false,
req_random: false,
lockable: false,
pub_info: true,
};
let mut key_slot = AteccKeySlot {
ref_count: 1,
status: KeySlotStatus::Busy,
config: slot_config,
};
let mut attributes = Attributes {
lifetime: Lifetime::Persistent,
key_type: Type::EccPublicKey {
curve_family: EccFamily::SecpR1,
},
bits: 256,
policy: Policy {
usage_flags: {
let mut flags = UsageFlags::default();
let _ = flags
.set_sign_hash()
.set_verify_hash()
.set_sign_message()
.set_export()
.set_copy();
flags
},
permitted_algorithms: AsymmetricSignature::Ecdsa {
hash_alg: Hash::Sha256.into(),
}
.into(),
},
};
assert!(key_slot.is_permitted_algorithms_ok(&attributes, None));
attributes.policy.permitted_algorithms = Mac::FullLength(FullLengthMac::Hmac {
hash_alg: Hash::Sha256,
})
.into();
assert!(!key_slot.is_permitted_algorithms_ok(&attributes, None));
attributes.policy.permitted_algorithms = AsymmetricSignature::DeterministicEcdsa {
hash_alg: Hash::Sha256.into(),
}
.into();
assert!(!key_slot.is_permitted_algorithms_ok(&attributes, None));
attributes.policy.permitted_algorithms = KeyAgreement::Raw(RawKeyAgreement::Ecdh).into();
assert!(key_slot.is_permitted_algorithms_ok(&attributes, None));
attributes.policy.permitted_algorithms =
Aead::AeadWithDefaultLengthTag(AeadWithDefaultLengthTag::Ccm).into();
key_slot.config.key_type = rust_cryptoauthlib::KeyType::Aes;
assert!(key_slot.is_permitted_algorithms_ok(&attributes, None));
attributes.policy.permitted_algorithms = Algorithm::Cipher(Cipher::CbcPkcs7);
assert!(key_slot.is_permitted_algorithms_ok(&attributes, None));
}
}