use alloc::string::{String, ToString};
use core::fmt::Display;
use hashbrown::HashSet;
use thiserror::Error;
use super::{CryptographicOperation, Policy, PolicyError};
use crate::{
jwa::{JsonWebAlgorithm, JsonWebEncryptionAlgorithm, JsonWebSigningAlgorithm},
jwk::{KeyOperation, KeyUsage},
};
#[derive(Debug, Error)]
pub enum StandardPolicyFail {
#[error("this key may not perform this cryptographic operation")]
OperationNotAllowed,
#[error("`none` algorithm is not allowed")]
NoneAlgorithm,
#[error("`use` contained custom usage which can't be checked")]
OtherKeyUsage,
#[error("`key_ops` contained custom operation which can't be checked")]
OtherKeyOperation,
#[error("`alg` header contains an unknown value")]
OtherAlgorithm,
#[error("{0}")]
Custom(String),
}
impl PolicyError for StandardPolicyFail {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
Self::Custom(msg.to_string())
}
}
#[non_exhaustive]
#[derive(Debug, Default, Clone)]
pub struct StandardPolicy;
impl StandardPolicy {
pub const fn new() -> Self {
Self
}
}
impl Policy for StandardPolicy {
type Error = StandardPolicyFail;
fn algorithm(&self, alg: &JsonWebAlgorithm) -> Result<(), Self::Error> {
match alg {
JsonWebAlgorithm::Encryption(JsonWebEncryptionAlgorithm::Other(_))
| JsonWebAlgorithm::Signing(JsonWebSigningAlgorithm::Other(_)) => {
Err(StandardPolicyFail::OtherAlgorithm)
}
JsonWebAlgorithm::Signing(JsonWebSigningAlgorithm::None) => {
Err(StandardPolicyFail::NoneAlgorithm)
}
_ => Ok(()),
}
}
fn compare_key_ops_and_use(
&self,
key_use: &KeyUsage,
key_ops: &HashSet<KeyOperation>,
) -> Result<(), Self::Error> {
if matches!(key_use, KeyUsage::Other(..)) {
return Err(StandardPolicyFail::OtherKeyUsage);
}
if key_ops.iter().any(|o| matches!(o, KeyOperation::Other(..))) {
return Err(StandardPolicyFail::OtherKeyOperation);
}
Ok(())
}
fn may_perform_operation_key_ops(
&self,
operation: CryptographicOperation,
key_ops: &HashSet<KeyOperation>,
) -> Result<(), Self::Error> {
use CryptographicOperation::*;
match operation {
Encrypt if key_ops.contains(&KeyOperation::Encrypt) => Ok(()),
Decrypt if key_ops.contains(&KeyOperation::Encrypt) => Ok(()),
Sign if key_ops.contains(&KeyOperation::Sign) => Ok(()),
Verify if key_ops.contains(&KeyOperation::Verify) => Ok(()),
_ => Err(StandardPolicyFail::OperationNotAllowed),
}
}
fn may_perform_operation_key_use(
&self,
operation: CryptographicOperation,
key_use: &KeyUsage,
) -> Result<(), Self::Error> {
use CryptographicOperation::*;
match operation {
Decrypt | Encrypt if key_use == &KeyUsage::Encryption => Ok(()),
Sign | Verify if key_use == &KeyUsage::Signing => Ok(()),
_ => Err(StandardPolicyFail::OperationNotAllowed),
}
}
}