use crate::operations::psa_algorithm::{Algorithm, Cipher};
use crate::requests::{ResponseStatus, Result};
use log::error;
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct KeyAttributes {
pub key_type: KeyType,
pub key_bits: u32,
pub key_policy: KeyPolicy,
}
impl KeyAttributes {
pub fn is_exportable(self) -> bool {
self.key_policy.key_usage_flags.export
}
pub fn can_export(self) -> Result<()> {
if self.is_exportable() {
Ok(())
} else {
error!("Key attributes do not permit exporting key.");
Err(ResponseStatus::PsaErrorNotPermitted)
}
}
pub fn is_hash_signable(self) -> bool {
self.key_policy.key_usage_flags.sign_hash
}
pub fn can_sign_hash(self) -> Result<()> {
if self.is_hash_signable() {
Ok(())
} else {
error!("Key attributes do not permit signing hashes.");
Err(ResponseStatus::PsaErrorNotPermitted)
}
}
pub fn is_hash_verifiable(self) -> bool {
self.key_policy.key_usage_flags.verify_hash
}
pub fn can_verify_hash(self) -> Result<()> {
if self.is_hash_verifiable() {
Ok(())
} else {
error!("Key attributes do not permit verifying hashes.");
Err(ResponseStatus::PsaErrorNotPermitted)
}
}
pub fn is_alg_permitted(self, alg: Algorithm) -> bool {
match self.key_policy.key_algorithm {
Algorithm::None => false,
Algorithm::Hash(hash_policy) => {
if let Algorithm::Hash(hash_alg) = alg {
hash_policy.is_alg_permitted(hash_alg)
} else {
false
}
}
Algorithm::Mac(mac_policy) => {
if let Algorithm::Mac(mac_alg) = alg {
mac_policy.is_alg_permitted(mac_alg)
} else {
false
}
}
Algorithm::AsymmetricSignature(asymmetric_signature_alg_policy) => {
if let Algorithm::AsymmetricSignature(asymmetric_signature_alg) = alg {
asymmetric_signature_alg_policy.is_alg_permitted(asymmetric_signature_alg)
} else {
false
}
}
Algorithm::AsymmetricEncryption(asymmetric_encryption_alg_policy) => {
if let Algorithm::AsymmetricEncryption(asymmetric_encryption_alg) = alg {
asymmetric_encryption_alg_policy.is_alg_permitted(asymmetric_encryption_alg)
} else {
false
}
}
Algorithm::KeyDerivation(key_derivation_alg_policy) => {
if let Algorithm::KeyDerivation(key_derivation_alg) = alg {
key_derivation_alg_policy.is_alg_permitted(key_derivation_alg)
} else {
false
}
}
Algorithm::KeyAgreement(key_agreement_alg_policy) => {
if let Algorithm::KeyAgreement(key_agreement_alg) = alg {
key_agreement_alg_policy.is_alg_permitted(key_agreement_alg)
} else {
false
}
}
Algorithm::Cipher(_) | Algorithm::Aead(_) => self.key_policy.key_algorithm == alg,
}
}
pub fn permits_alg(self, alg: Algorithm) -> Result<()> {
if self.is_alg_permitted(alg) {
Ok(())
} else {
error!("Key attributes do not permit specified algorithm.");
Err(ResponseStatus::PsaErrorNotPermitted)
}
}
pub fn is_compatible_with_alg(self, alg: Algorithm) -> bool {
match self.key_type {
KeyType::RawData => false,
KeyType::Hmac => alg.is_hmac(),
KeyType::Derive => {
if let Algorithm::KeyDerivation(_) = alg {
true
} else {
false
}
}
KeyType::Aes | KeyType::Camellia => {
if let Algorithm::Mac(mac_alg) = alg {
mac_alg.is_block_cipher_needed()
} else if let Algorithm::Cipher(cipher_alg) = alg {
cipher_alg.is_block_cipher_mode()
} else if let Algorithm::Aead(aead_alg) = alg {
aead_alg.is_block_cipher_needed()
} else {
false
}
}
KeyType::Des => {
if let Algorithm::Mac(mac_alg) = alg {
mac_alg.is_block_cipher_needed()
} else if let Algorithm::Cipher(cipher_alg) = alg {
cipher_alg.is_block_cipher_mode()
} else {
false
}
}
KeyType::Arc4 => alg == Algorithm::Cipher(Cipher::StreamCipher),
KeyType::Chacha20 => {
if alg == Algorithm::Cipher(Cipher::StreamCipher) {
true
} else if let Algorithm::Aead(aead_alg) = alg {
aead_alg.is_chacha20_poly1305_alg()
} else {
false
}
}
KeyType::RsaPublicKey | KeyType::RsaKeyPair => {
if let Algorithm::AsymmetricSignature(sign_alg) = alg {
sign_alg.is_rsa_alg()
} else if let Algorithm::AsymmetricEncryption(_) = alg {
true
} else {
false
}
}
KeyType::EccKeyPair { .. } | KeyType::EccPublicKey { .. } => {
if let Algorithm::AsymmetricSignature(sign_alg) = alg {
sign_alg.is_ecc_alg()
} else {
false
}
}
KeyType::DhKeyPair { .. } | KeyType::DhPublicKey { .. } => {
if let Algorithm::KeyAgreement(_) = alg {
true
} else {
false
}
}
}
}
pub fn compatible_with_alg(self, alg: Algorithm) -> Result<()> {
if self.is_compatible_with_alg(alg) {
Ok(())
} else {
error!("Key attributes are not compatible with specified algorithm.");
Err(ResponseStatus::PsaErrorNotPermitted)
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum KeyType {
RawData,
Hmac,
Derive,
Aes,
Des,
Camellia,
Arc4,
Chacha20,
RsaPublicKey,
RsaKeyPair,
EccKeyPair {
curve_family: EccFamily,
},
EccPublicKey {
curve_family: EccFamily,
},
DhKeyPair {
group_family: DhFamily,
},
DhPublicKey {
group_family: DhFamily,
},
}
impl KeyType {
pub fn is_ecc_key_pair(self) -> bool {
match self {
KeyType::EccKeyPair { .. } => true,
_ => false,
}
}
pub fn is_ecc_public_key(self) -> bool {
match self {
KeyType::EccPublicKey { .. } => true,
_ => false,
}
}
pub fn is_dh_public_key(self) -> bool {
match self {
KeyType::DhPublicKey { .. } => true,
_ => false,
}
}
pub fn is_dh_key_pair(self) -> bool {
match self {
KeyType::DhKeyPair { .. } => true,
_ => false,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum EccFamily {
SecpK1,
SecpR1,
#[deprecated = "This family of curve is weak and deprecated."]
SecpR2,
SectK1,
SectR1,
#[deprecated = "This family of curve is weak and deprecated."]
SectR2,
BrainpoolPR1,
Frp,
Montgomery,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum DhFamily {
Rfc7919,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct KeyPolicy {
pub key_usage_flags: UsageFlags,
pub key_algorithm: Algorithm,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UsageFlags {
pub export: bool,
pub copy: bool,
pub cache: bool,
pub encrypt: bool,
pub decrypt: bool,
pub sign_message: bool,
pub verify_message: bool,
pub sign_hash: bool,
pub verify_hash: bool,
pub derive: bool,
}
#[cfg(test)]
mod tests {
use super::{KeyAttributes, KeyPolicy, KeyType, UsageFlags};
use crate::operations::psa_algorithm::{
Aead, AeadWithDefaultLengthTag, Algorithm, AsymmetricSignature, Cipher, FullLengthMac,
Hash, Mac,
};
#[test]
fn usage_flags() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let mut attributes = KeyAttributes {
key_type: KeyType::RsaKeyPair,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: false,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(!attributes.is_exportable());
assert!(!attributes.is_hash_signable());
assert!(!attributes.is_hash_verifiable());
attributes.key_policy.key_usage_flags.export = true;
assert!(attributes.is_exportable());
assert!(!attributes.is_hash_signable());
assert!(!attributes.is_hash_verifiable());
attributes.key_policy.key_usage_flags.sign_hash = true;
assert!(attributes.is_exportable());
assert!(attributes.is_hash_signable());
assert!(!attributes.is_hash_verifiable());
attributes.key_policy.key_usage_flags.verify_hash = true;
assert!(attributes.is_exportable());
assert!(attributes.is_hash_signable());
assert!(attributes.is_hash_verifiable());
}
#[test]
fn permits_good_alg() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let key_attributes = KeyAttributes {
key_type: KeyType::Hmac,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: true,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(key_attributes.is_alg_permitted(alg));
}
#[test]
fn permits_bad_alg() {
let permitted_alg = Algorithm::Mac(Mac::FullLength(FullLengthMac::Hmac {
hash_alg: Hash::Sha1,
}));
let alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha1,
});
let key_attributes = KeyAttributes {
key_type: KeyType::Hmac,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: true,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(!key_attributes.is_alg_permitted(alg));
}
#[test]
fn permits_wildcard_alg() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Any,
});
let alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha1,
});
let key_attributes = KeyAttributes {
key_type: KeyType::Hmac,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: true,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(key_attributes.is_alg_permitted(alg));
}
#[test]
fn permits_bad_wildcard_alg() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Any,
});
let key_attributes = KeyAttributes {
key_type: KeyType::Hmac,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: true,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(!key_attributes.is_alg_permitted(alg));
}
#[test]
fn compat_rsa() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let mut key_attributes = KeyAttributes {
key_type: KeyType::RsaKeyPair,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: false,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(key_attributes.is_compatible_with_alg(alg));
key_attributes.key_type = KeyType::RsaPublicKey;
assert!(key_attributes.is_compatible_with_alg(alg));
}
#[test]
fn compat_raw_data() {
let permitted_alg = Algorithm::None;
let alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let key_attributes = KeyAttributes {
key_type: KeyType::RawData,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: false,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(!key_attributes.is_compatible_with_alg(alg));
}
#[test]
fn compat_block_cipher() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let mut alg = Algorithm::Cipher(Cipher::Ofb);
let mut key_attributes = KeyAttributes {
key_type: KeyType::Aes,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: false,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(key_attributes.is_compatible_with_alg(alg));
key_attributes.key_type = KeyType::Des;
assert!(key_attributes.is_compatible_with_alg(alg));
key_attributes.key_type = KeyType::Camellia;
assert!(key_attributes.is_compatible_with_alg(alg));
alg = Algorithm::Aead(Aead::AeadWithDefaultLengthTag(
AeadWithDefaultLengthTag::Ccm,
));
assert!(key_attributes.is_compatible_with_alg(alg));
key_attributes.key_type = KeyType::Des;
assert!(!key_attributes.is_compatible_with_alg(alg));
}
#[test]
fn compat_chacha() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let alg = Algorithm::Aead(Aead::AeadWithDefaultLengthTag(
AeadWithDefaultLengthTag::Chacha20Poly1305,
));
let key_attributes = KeyAttributes {
key_type: KeyType::Chacha20,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: false,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(key_attributes.is_compatible_with_alg(alg));
}
#[test]
fn bad_compat() {
let permitted_alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let alg = Algorithm::AsymmetricSignature(AsymmetricSignature::RsaPkcs1v15Sign {
hash_alg: Hash::Sha256,
});
let key_attributes = KeyAttributes {
key_type: KeyType::Hmac,
key_bits: 1024,
key_policy: KeyPolicy {
key_usage_flags: UsageFlags {
export: false,
copy: false,
cache: false,
encrypt: false,
decrypt: false,
sign_message: false,
verify_message: false,
sign_hash: false,
verify_hash: false,
derive: false,
},
key_algorithm: permitted_alg,
},
};
assert!(!key_attributes.is_compatible_with_alg(alg));
}
}