use alloc::string::String;
use secrecy::{ExposeSecret, SecretBox};
use serde::{de::Error, Deserialize, Deserializer, Serialize};
use subtle::ConstantTimeEq;
use zeroize::Zeroize;
use super::thumbprint::{self, Thumbprint};
use crate::{base64_url::Base64UrlBytes, jws::InvalidSigningAlgorithmError};
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash)]
#[serde(untagged)]
pub enum SymmetricJsonWebKey {
OctetSequence(OctetSequence),
}
impl crate::sealed::Sealed for SymmetricJsonWebKey {}
impl Thumbprint for SymmetricJsonWebKey {
fn thumbprint_prehashed(&self) -> String {
match self {
SymmetricJsonWebKey::OctetSequence(key) => key.thumbprint_prehashed(),
}
}
}
#[derive(Debug, Clone, Zeroize)]
pub struct OctetSequence(SecretBox<[u8]>);
impl OctetSequence {
pub(crate) fn new(x: SecretBox<[u8]>) -> Self {
Self(x)
}
pub(crate) fn bytes(&self) -> &SecretBox<[u8]> {
&self.0
}
pub(crate) fn into_bytes(self) -> SecretBox<[u8]> {
self.0
}
#[inline]
pub fn len(&self) -> usize {
self.0.expose_secret().len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl PartialEq for OctetSequence {
fn eq(&self, other: &Self) -> bool {
bool::from(self.0.expose_secret().ct_eq(other.0.expose_secret()))
}
}
impl Eq for OctetSequence {}
impl crate::sealed::Sealed for OctetSequence {}
impl Thumbprint for OctetSequence {
fn thumbprint_prehashed(&self) -> String {
thumbprint::serialize_key_thumbprint(self)
}
}
impl From<SymmetricJsonWebKey> for super::JsonWebKeyType {
fn from(x: SymmetricJsonWebKey) -> Self {
super::JsonWebKeyType::Symmetric(x)
}
}
impl From<OctetSequence> for super::JsonWebKeyType {
fn from(x: OctetSequence) -> Self {
super::JsonWebKeyType::Symmetric(SymmetricJsonWebKey::OctetSequence(x))
}
}
impl<'de> Deserialize<'de> for OctetSequence {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Repr {
kty: String,
k: Base64UrlBytes,
}
let repr = Repr::deserialize(deserializer)?;
if repr.kty != "oct" {
return Err(D::Error::custom("`kty` field is required to be `oct`"));
}
Ok(Self(SecretBox::new(repr.k.0.into_boxed_slice())))
}
}
impl Serialize for OctetSequence {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
struct Repr<'a> {
kty: &'static str,
k: &'a Base64UrlBytes,
}
Repr {
kty: "oct",
k: &Base64UrlBytes(self.0.expose_secret().to_vec()),
}
.serialize(serializer)
}
}
#[derive(Debug, thiserror::Error)]
pub enum FromOctetSequenceError {
#[error(transparent)]
InvalidSigningAlgorithm(#[from] InvalidSigningAlgorithmError),
#[error("the length of the is invalid")]
InvalidLength,
#[error("the crypto backend failed")]
Crypto(
#[from]
#[source]
crate::crypto::Error,
),
}