use std::fmt::{self, Display};
use crate::jwk::{Algorithm, KeyOperation, KeyType};
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Json(serde_json::Error),
Parse(ParseError),
InvalidUrl(url::ParseError),
InvalidUrlScheme(&'static str),
InvalidKey(InvalidKeyError),
IncompatibleKey(IncompatibleKeyError),
Base64(base64ct::Error),
InvalidInput(&'static str),
#[cfg(feature = "http")]
Http(reqwest::Error),
Fetch(String),
Cache(String),
Other(String),
#[cfg(feature = "web-crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "web-crypto")))]
UnsupportedForWebCrypto {
reason: &'static str,
},
#[cfg(feature = "web-crypto")]
#[cfg_attr(docsrs, doc(cfg(feature = "web-crypto")))]
WebCrypto(String),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Json(e) => write!(f, "JSON error: {}", e),
Error::Parse(e) => write!(f, "parse error: {}", e),
Error::InvalidUrl(err) => write!(f, "invalid URL: {}", err),
Error::InvalidUrlScheme(msg) => write!(f, "invalid URL scheme: {}", msg),
Error::InvalidKey(e) => write!(f, "invalid key: {}", e),
Error::IncompatibleKey(e) => write!(f, "incompatible key: {}", e),
Error::Base64(e) => write!(f, "base64 decoding error: {:?}", e),
Error::InvalidInput(msg) => write!(f, "invalid input: {}", msg),
#[cfg(feature = "http")]
Error::Http(e) => write!(f, "HTTP error: {}", e),
Error::Fetch(msg) => write!(f, "fetch error: {}", msg),
Error::Cache(msg) => write!(f, "cache error: {}", msg),
Error::Other(msg) => write!(f, "{}", msg),
#[cfg(feature = "web-crypto")]
Error::UnsupportedForWebCrypto { reason } => {
write!(f, "unsupported for WebCrypto: {}", reason)
}
#[cfg(feature = "web-crypto")]
Error::WebCrypto(msg) => write!(f, "WebCrypto error: {}", msg),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Json(e) => Some(e),
Error::Parse(e) => Some(e),
Error::InvalidUrl(e) => Some(e),
Error::InvalidKey(e) => Some(e),
Error::IncompatibleKey(e) => Some(e),
Error::Base64(e) => Some(e),
#[cfg(feature = "http")]
Error::Http(e) => Some(e),
_ => None,
}
}
}
impl From<InvalidKeyError> for Error {
fn from(e: InvalidKeyError) -> Self {
Error::InvalidKey(e)
}
}
impl From<IncompatibleKeyError> for Error {
fn from(e: IncompatibleKeyError) -> Self {
Error::IncompatibleKey(e)
}
}
impl From<base64ct::Error> for Error {
fn from(e: base64ct::Error) -> Self {
Error::Base64(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::Json(e)
}
}
impl From<url::ParseError> for Error {
fn from(e: url::ParseError) -> Self {
Error::InvalidUrl(e)
}
}
#[cfg(feature = "http")]
impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Self {
Error::Http(e)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseError {
UnknownKeyType(String),
UnknownCurve(String),
}
impl Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::UnknownKeyType(kty) => write!(f, "unknown key type: {}", kty),
ParseError::UnknownCurve(crv) => write!(f, "unknown curve: {}", crv),
}
}
}
impl std::error::Error for ParseError {}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum InvalidKeyError {
InvalidKeySize {
expected: usize,
actual: usize,
context: &'static str,
},
MissingParameter(&'static str),
InconsistentParameters(String),
InvalidParameter {
name: &'static str,
reason: String,
},
InvalidOtherPrime {
index: usize,
source: Box<InvalidKeyError>,
},
}
impl Display for InvalidKeyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InvalidKeyError::InvalidKeySize {
expected,
actual,
context,
} => {
write!(
f,
"invalid key size for {}: expected {} bytes, got {}",
context, expected, actual
)
}
InvalidKeyError::MissingParameter(param) => {
write!(f, "missing required parameter: {}", param)
}
InvalidKeyError::InconsistentParameters(msg) => {
write!(f, "inconsistent key parameters: {}", msg)
}
InvalidKeyError::InvalidParameter { name, reason } => {
write!(f, "invalid parameter '{}': {}", name, reason)
}
InvalidKeyError::InvalidOtherPrime { index, source } => {
write!(f, "invalid oth[{}]: {}", index, source)
}
}
}
}
impl std::error::Error for InvalidKeyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
InvalidKeyError::InvalidOtherPrime { source, .. } => Some(source.as_ref()),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum IncompatibleKeyError {
AlgorithmMismatch {
requested: Algorithm,
declared: Algorithm,
},
IncompatibleAlgorithm {
algorithm: Algorithm,
key_type: KeyType,
},
InsufficientKeyStrength {
minimum_bits: usize,
actual_bits: usize,
context: &'static str,
},
KeySizeMismatch {
required_bits: usize,
actual_bits: usize,
context: &'static str,
},
OperationNotPermitted {
operations: Vec<KeyOperation>,
reason: String,
},
}
impl Display for IncompatibleKeyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IncompatibleKeyError::AlgorithmMismatch {
requested,
declared,
} => {
let requested_display = match requested {
Algorithm::Unknown(value) => {
format!("unknown({})", sanitize_for_display(value))
}
_ => requested.to_string(),
};
let declared_display = match declared {
Algorithm::Unknown(value) => {
format!("unknown({})", sanitize_for_display(value))
}
_ => declared.to_string(),
};
write!(
f,
"requested algorithm '{}' does not match key's declared alg '{}'",
requested_display, declared_display
)
}
IncompatibleKeyError::IncompatibleAlgorithm {
algorithm,
key_type,
} => {
let algorithm_display = match algorithm {
Algorithm::Unknown(value) => {
format!("unknown({})", sanitize_for_display(value))
}
_ => algorithm.to_string(),
};
write!(
f,
"algorithm '{}' is not compatible with key type '{}'",
algorithm_display, key_type
)
}
IncompatibleKeyError::InsufficientKeyStrength {
minimum_bits,
actual_bits,
context,
} => {
write!(
f,
"insufficient key strength for {}: need {} bits, got {}",
context, minimum_bits, actual_bits
)
}
IncompatibleKeyError::KeySizeMismatch {
required_bits,
actual_bits,
context,
} => {
write!(
f,
"key size mismatch for {}: expected {} bits, got {}",
context, required_bits, actual_bits
)
}
IncompatibleKeyError::OperationNotPermitted { operations, reason } => {
let ops: Vec<String> = operations
.iter()
.map(|op| match op {
KeyOperation::Unknown(value) => {
format!("unknown({})", sanitize_for_display(value))
}
_ => op.to_string(),
})
.collect();
write!(
f,
"operation(s) not permitted ({}): {}",
ops.join(", "),
reason
)
}
}
}
}
impl std::error::Error for IncompatibleKeyError {}
#[cfg(feature = "jwt-simple")]
#[cfg_attr(docsrs, doc(cfg(feature = "jwt-simple")))]
#[derive(Debug)]
#[non_exhaustive]
pub enum JwtSimpleKeyConversionError {
InvalidKey(InvalidKeyError),
IncompatibleKey(IncompatibleKeyError),
KeyTypeMismatch {
expected: &'static str,
actual: String,
},
CurveMismatch {
expected: &'static str,
actual: String,
},
MissingComponent {
field: &'static str,
},
MissingPrivateKey,
Core(Error),
Encoding(String),
Import(String),
}
#[cfg(feature = "jwt-simple")]
impl Display for JwtSimpleKeyConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JwtSimpleKeyConversionError::InvalidKey(e) => write!(f, "invalid key: {}", e),
JwtSimpleKeyConversionError::IncompatibleKey(e) => {
write!(f, "incompatible key: {}", e)
}
JwtSimpleKeyConversionError::KeyTypeMismatch { expected, actual } => {
write!(
f,
"key type mismatch: expected {}, got {}",
expected, actual
)
}
JwtSimpleKeyConversionError::CurveMismatch { expected, actual } => {
write!(f, "curve mismatch: expected {}, got {}", expected, actual)
}
JwtSimpleKeyConversionError::MissingComponent { field } => {
write!(f, "missing required field: {}", field)
}
JwtSimpleKeyConversionError::MissingPrivateKey => {
write!(f, "private key parameters required but not present")
}
JwtSimpleKeyConversionError::Core(err) => {
write!(f, "core error: {}", err)
}
JwtSimpleKeyConversionError::Encoding(msg) => write!(f, "encoding error: {}", msg),
JwtSimpleKeyConversionError::Import(msg) => write!(f, "import error: {}", msg),
}
}
}
#[cfg(feature = "jwt-simple")]
impl std::error::Error for JwtSimpleKeyConversionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
JwtSimpleKeyConversionError::InvalidKey(e) => Some(e),
JwtSimpleKeyConversionError::IncompatibleKey(e) => Some(e),
JwtSimpleKeyConversionError::Core(err) => Some(err),
_ => None,
}
}
}
#[cfg(feature = "jwt-simple")]
impl From<InvalidKeyError> for JwtSimpleKeyConversionError {
fn from(e: InvalidKeyError) -> Self {
JwtSimpleKeyConversionError::InvalidKey(e)
}
}
#[cfg(feature = "jwt-simple")]
impl From<IncompatibleKeyError> for JwtSimpleKeyConversionError {
fn from(e: IncompatibleKeyError) -> Self {
JwtSimpleKeyConversionError::IncompatibleKey(e)
}
}
const MAX_DISPLAY_IDENTIFIER_CHARS: usize = 256;
pub(crate) fn sanitize_for_display(value: &str) -> String {
value
.chars()
.take(MAX_DISPLAY_IDENTIFIER_CHARS)
.map(|ch| if ch.is_control() { ' ' } else { ch })
.collect()
}
pub type Result<T> = std::result::Result<T, Error>;