use alloc::{boxed::Box, string::String, vec, vec::Vec};
use core::{
fmt::Debug,
ops::{ControlFlow, Deref},
};
use hashbrown::HashSet;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{
crypto::hmac::{self, Variant as _},
jwa::{
AesGcm, AesKw, EcDSA, Hmac, JsonWebAlgorithm, JsonWebEncryptionAlgorithm,
JsonWebSigningAlgorithm, Pbes2,
},
policy::{Checkable, Checked, CryptographicOperation, Policy},
sealed::Sealed,
uri::BorrowedUri,
UntypedAdditionalProperties, Uri,
};
pub mod symmetric;
mod asymmetric;
mod builder;
mod key_ops;
mod key_use;
mod private;
mod public;
pub(crate) mod serde_impl;
mod signer;
pub(crate) mod thumbprint;
mod verifier;
use self::serde_impl::Base64DerCertificate;
#[doc(inline)]
pub use self::{
asymmetric::AsymmetricJsonWebKey,
builder::{JsonWebKeyBuildError, JsonWebKeyBuilder},
key_ops::KeyOperation,
key_use::KeyUsage,
private::{EcPrivate, OkpPrivate, Private},
public::{EcPublic, OkpPublic, Public},
signer::{FromJwkError, JwkSigner},
symmetric::SymmetricJsonWebKey,
thumbprint::Thumbprint,
verifier::JwkVerifier,
};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct JsonWebKey<A = ()> {
#[serde(flatten)]
additional: A,
#[serde(flatten)]
key_type: JsonWebKeyType,
#[serde(rename = "use", skip_serializing_if = "Option::is_none")]
key_use: Option<KeyUsage>,
#[serde(
deserialize_with = "serde_impl::deserialize_ensure_set",
rename = "key_ops",
skip_serializing_if = "Option::is_none",
// default needed because else serde will error if the `key_ops` parameter is not present
default
)]
key_operations: Option<HashSet<KeyOperation>>,
#[serde(rename = "alg", skip_serializing_if = "Option::is_none")]
algorithm: Option<JsonWebAlgorithm>,
#[serde(skip_serializing_if = "Option::is_none")]
kid: Option<String>,
#[serde(rename = "x5u", skip_serializing_if = "Option::is_none")]
x509_url: Option<Uri>,
#[serde(rename = "x5c", skip_serializing_if = "Vec::is_empty", default)]
x509_certificate_chain: Vec<Base64DerCertificate>,
#[serde(
serialize_with = "serde_impl::serialize_ga_sha1",
deserialize_with = "serde_impl::deserialize_ga_sha1",
rename = "x5t",
default,
skip_serializing_if = "Option::is_none"
)]
x509_certificate_sha1_thumbprint: Option<[u8; 20]>,
#[serde(
serialize_with = "serde_impl::serialize_ga_sha256",
deserialize_with = "serde_impl::deserialize_ga_sha256",
rename = "x5t#S256",
default,
skip_serializing_if = "Option::is_none"
)]
x509_certificate_sha256_thumbprint: Option<[u8; 32]>,
}
impl JsonWebKey<()> {
pub(crate) fn new_with_algorithm(
key_type: JsonWebKeyType,
alg: Option<JsonWebAlgorithm>,
) -> Self {
Self {
key_type,
key_use: None,
key_operations: None,
algorithm: alg,
kid: None,
x509_url: None,
x509_certificate_chain: vec![],
x509_certificate_sha1_thumbprint: None,
x509_certificate_sha256_thumbprint: None,
additional: (),
}
}
}
impl JsonWebKey<()> {
pub fn builder(key_type: impl Into<JsonWebKeyType>) -> JsonWebKeyBuilder<()> {
JsonWebKeyBuilder::new(key_type)
}
}
impl<T> JsonWebKey<T> {
pub fn into_builder(self) -> JsonWebKeyBuilder<T> {
JsonWebKeyBuilder {
key_type: self.key_type,
key_use: self.key_use,
key_operations: self.key_operations,
algorithm: self.algorithm,
kid: self.kid,
x509_url: self.x509_url,
x509_certificate_chain: self.x509_certificate_chain,
x509_certificate_sha1_thumbprint: self.x509_certificate_sha1_thumbprint,
x509_certificate_sha256_thumbprint: self.x509_certificate_sha256_thumbprint,
additional: self.additional,
}
}
pub fn key_type(&self) -> &JsonWebKeyType {
&self.key_type
}
pub fn key_usage(&self) -> Option<&KeyUsage> {
self.key_use.as_ref()
}
pub fn key_operations(&self) -> Option<&HashSet<KeyOperation>> {
self.key_operations.as_ref()
}
pub fn algorithm(&self) -> Option<&JsonWebAlgorithm> {
self.algorithm.as_ref()
}
pub fn key_id(&self) -> Option<&str> {
self.kid.as_deref()
}
pub fn x509_url(&self) -> Option<BorrowedUri<'_>> {
self.x509_url.as_ref().map(|x| x.borrow())
}
pub fn x509_certificate_chain(&self) -> impl ExactSizeIterator<Item = &[u8]> {
self.x509_certificate_chain.iter().map(Deref::deref)
}
pub fn x509_certificate_sha1_thumbprint(&self) -> Option<&[u8; 20]> {
self.x509_certificate_sha1_thumbprint.as_ref()
}
pub fn x509_certificate_sha256_thumbprint(&self) -> Option<&[u8; 32]> {
self.x509_certificate_sha256_thumbprint.as_ref()
}
pub fn additional(&self) -> &T {
&self.additional
}
#[inline]
pub fn is_symmetric(&self) -> bool {
matches!(self.key_type, JsonWebKeyType::Symmetric(_))
}
#[inline]
pub fn is_asymmetric(&self) -> bool {
matches!(self.key_type, JsonWebKeyType::Asymmetric(_))
}
#[inline]
pub fn is_signing_key(&self) -> bool {
match self.key_type() {
JsonWebKeyType::Symmetric(_) => true,
JsonWebKeyType::Asymmetric(ref key) => match &**key {
AsymmetricJsonWebKey::Private(_) => true,
AsymmetricJsonWebKey::Public(_) => false,
},
}
}
#[doc(alias = "strip_private_material")]
pub fn strip_secret_material(mut self) -> Option<Self> {
let key = match self.key_type {
JsonWebKeyType::Symmetric(_) => return None,
JsonWebKeyType::Asymmetric(ref key) => key,
};
match &**key {
AsymmetricJsonWebKey::Public(_) => Some(self),
AsymmetricJsonWebKey::Private(Private::Okp(okp)) => {
let key = match okp {
OkpPrivate::Ed25519(key) => OkpPublic::Ed25519(key.to_public_key()),
OkpPrivate::Ed448(key) => OkpPublic::Ed448(key.to_public_key()),
};
self.key_type = JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(
Public::Okp(key),
)));
Some(self)
}
AsymmetricJsonWebKey::Private(Private::Ec(ec)) => {
let pub_key = match ec {
EcPrivate::P256(key) => EcPublic::P256(key.to_public_key()),
EcPrivate::P384(key) => EcPublic::P384(key.to_public_key()),
EcPrivate::P521(key) => EcPublic::P521(key.to_public_key()),
EcPrivate::Secp256k1(key) => EcPublic::Secp256k1(key.to_public_key()),
};
self.key_type = JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(
Public::Ec(pub_key),
)));
Some(self)
}
AsymmetricJsonWebKey::Private(Private::Rsa(rsa)) => {
let pub_key = Public::Rsa(rsa.to_public_key());
self.key_type =
JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(pub_key)));
Some(self)
}
}
}
#[doc(alias = "into_public_key")]
pub fn into_verifying_key(mut self) -> Self {
match self.key_type {
JsonWebKeyType::Symmetric(_) => self,
JsonWebKeyType::Asymmetric(ref key) => match &**key {
AsymmetricJsonWebKey::Public(_) => self,
AsymmetricJsonWebKey::Private(Private::Okp(okp)) => {
let key = match okp {
OkpPrivate::Ed25519(key) => OkpPublic::Ed25519(key.to_public_key()),
OkpPrivate::Ed448(key) => OkpPublic::Ed448(key.to_public_key()),
};
self.key_type = JsonWebKeyType::Asymmetric(Box::new(
AsymmetricJsonWebKey::Public(Public::Okp(key)),
));
self
}
AsymmetricJsonWebKey::Private(Private::Ec(ec)) => {
let pub_key = match ec {
EcPrivate::P256(key) => EcPublic::P256(key.to_public_key()),
EcPrivate::P384(key) => EcPublic::P384(key.to_public_key()),
EcPrivate::P521(key) => EcPublic::P521(key.to_public_key()),
EcPrivate::Secp256k1(key) => EcPublic::Secp256k1(key.to_public_key()),
};
self.key_type = JsonWebKeyType::Asymmetric(Box::new(
AsymmetricJsonWebKey::Public(Public::Ec(pub_key)),
));
self
}
AsymmetricJsonWebKey::Private(Private::Rsa(rsa)) => {
let pub_key = Public::Rsa(rsa.to_public_key());
self.key_type =
JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(pub_key)));
self
}
},
}
}
}
impl<T: Serialize> JsonWebKey<T> {
pub fn into_untyped_additional(
self,
) -> Result<JsonWebKey<UntypedAdditionalProperties>, serde_json::Error> {
let value = serde_json::to_value(&self.additional)?;
let additional: UntypedAdditionalProperties = serde_json::from_value(value)?;
Ok(JsonWebKey {
additional,
key_type: self.key_type,
key_use: self.key_use,
key_operations: self.key_operations,
algorithm: self.algorithm,
kid: self.kid,
x509_url: self.x509_url,
x509_certificate_chain: self.x509_certificate_chain,
x509_certificate_sha1_thumbprint: self.x509_certificate_sha1_thumbprint,
x509_certificate_sha256_thumbprint: self.x509_certificate_sha256_thumbprint,
})
}
}
impl JsonWebKey<UntypedAdditionalProperties> {
pub fn deserialize_additional<T: DeserializeOwned>(
self,
) -> Result<JsonWebKey<T>, serde_json::Error> {
let additional = serde_json::from_value(self.additional.into())?;
Ok(JsonWebKey {
additional,
key_type: self.key_type,
key_use: self.key_use,
key_operations: self.key_operations,
algorithm: self.algorithm,
kid: self.kid,
x509_url: self.x509_url,
x509_certificate_chain: self.x509_certificate_chain,
x509_certificate_sha1_thumbprint: self.x509_certificate_sha1_thumbprint,
x509_certificate_sha256_thumbprint: self.x509_certificate_sha256_thumbprint,
})
}
}
impl<T> Checkable for JsonWebKey<T>
where
T: Checkable,
{
fn check<P: Policy>(self, policy: P) -> Result<Checked<Self, P>, (Self, P::Error)> {
if let Some(alg) = self.algorithm() {
if let Err(e) = policy.algorithm(alg) {
return Err((self, e));
}
let operations = match alg {
JsonWebAlgorithm::Encryption(..) => [
CryptographicOperation::Encrypt,
CryptographicOperation::Decrypt,
],
JsonWebAlgorithm::Signing(..) => {
[CryptographicOperation::Sign, CryptographicOperation::Verify]
}
};
debug_assert!(!operations.is_empty());
if let Some(key_use) = self.key_usage() {
match operations.iter().try_fold(None, |_, operation| {
match policy.may_perform_operation_key_use(*operation, key_use) {
Ok(_) => ControlFlow::Break(()),
Err(err) => ControlFlow::Continue(Some(err)),
}
}) {
ControlFlow::Break(_) => (),
ControlFlow::Continue(err) => {
return Err((
self,
err.expect("at least one call has been made to the Policy"),
));
}
}
}
if let Some(key_ops) = self.key_operations() {
match operations.iter().try_fold(None, |_, operation| {
match policy.may_perform_operation_key_ops(*operation, key_ops) {
Ok(_) => ControlFlow::Break(()),
Err(err) => ControlFlow::Continue(Some(err)),
}
}) {
ControlFlow::Break(_) => (),
ControlFlow::Continue(err) => {
return Err((
self,
err.expect("at leat one call has been made to the Policy"),
));
}
}
}
}
if let (Some(key_use), Some(key_ops)) = (self.key_usage(), self.key_operations()) {
if let Err(e) = policy.compare_key_ops_and_use(key_use, key_ops) {
return Err((self, e));
}
}
match self.additional.check(policy) {
Err(e) => Err((
Self {
additional: e.0,
..self
},
e.1,
)),
Ok(o) => {
let (additional, p) = o.into_inner();
Ok(Checked::new(Self { additional, ..self }, p))
}
}
}
}
impl Sealed for JsonWebKey {}
impl Thumbprint for JsonWebKey {
fn thumbprint_prehashed(&self) -> String {
self.key_type().thumbprint_prehashed()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash)]
#[serde(untagged)]
pub enum JsonWebKeyType {
Symmetric(SymmetricJsonWebKey),
Asymmetric(Box<AsymmetricJsonWebKey>),
}
impl Sealed for JsonWebKeyType {}
impl Thumbprint for JsonWebKeyType {
fn thumbprint_prehashed(&self) -> String {
match self {
JsonWebKeyType::Symmetric(key) => key.thumbprint_prehashed(),
JsonWebKeyType::Asymmetric(key) => key.thumbprint_prehashed(),
}
}
}
impl JsonWebKeyType {
pub(self) fn compatible_with(&self, alg: &JsonWebAlgorithm) -> bool {
use JsonWebAlgorithm::*;
use JsonWebKeyType::*;
#[allow(clippy::match_like_matches_macro)]
match (self, alg) {
(
Symmetric(SymmetricJsonWebKey::OctetSequence(key)),
Signing(JsonWebSigningAlgorithm::Hmac(hmac)),
) => match (hmac, key.len()) {
(Hmac::Hs256, hmac::Hs256::OUTPUT_SIZE_BYTES..)
| (Hmac::Hs384, hmac::Hs384::OUTPUT_SIZE_BYTES..)
| (Hmac::Hs512, hmac::Hs512::OUTPUT_SIZE_BYTES..) => true,
_ => false,
},
(
Symmetric(SymmetricJsonWebKey::OctetSequence(key)),
Encryption(JsonWebEncryptionAlgorithm::AesKw(aes)),
) => matches!(
(aes, key.len()),
(AesKw::Aes128, 16) | (AesKw::Aes192, 24) | (AesKw::Aes256, 32)
),
(
Symmetric(SymmetricJsonWebKey::OctetSequence(key)),
Encryption(JsonWebEncryptionAlgorithm::AesGcmKw(aes)),
) => matches!(
(aes, key.len()),
(AesGcm::Aes128, 16) | (AesGcm::Aes192, 24) | (AesGcm::Aes256, 32)
),
(
Symmetric(SymmetricJsonWebKey::OctetSequence(key)),
Encryption(JsonWebEncryptionAlgorithm::Pbes2(pbes2)),
) => matches!(
(pbes2, key.len()),
(Pbes2::Hs256Aes128, 16) | (Pbes2::Hs384Aes192, 24) | (Pbes2::Hs512Aes256, 32)
),
(
Symmetric(SymmetricJsonWebKey::OctetSequence(..)),
Encryption(JsonWebEncryptionAlgorithm::Direct),
) => true,
(Asymmetric(key), alg) => match (&**key, alg) {
(
AsymmetricJsonWebKey::Public(Public::Ec(..))
| AsymmetricJsonWebKey::Private(Private::Ec(..)),
Encryption(JsonWebEncryptionAlgorithm::EcDhES(..)),
)
| (
AsymmetricJsonWebKey::Public(Public::Ec(EcPublic::P256(..)))
| AsymmetricJsonWebKey::Private(Private::Ec(EcPrivate::P256(..))),
Signing(JsonWebSigningAlgorithm::EcDSA(EcDSA::Es256)),
)
| (
AsymmetricJsonWebKey::Public(Public::Ec(EcPublic::P384(..)))
| AsymmetricJsonWebKey::Private(Private::Ec(EcPrivate::P384(..))),
Signing(JsonWebSigningAlgorithm::EcDSA(EcDSA::Es384)),
)
| (
AsymmetricJsonWebKey::Public(Public::Ec(EcPublic::Secp256k1(..)))
| AsymmetricJsonWebKey::Private(Private::Ec(EcPrivate::Secp256k1(..))),
Signing(JsonWebSigningAlgorithm::EcDSA(EcDSA::Es256K)),
)
| (
AsymmetricJsonWebKey::Public(Public::Okp(
OkpPublic::Ed25519(..) | OkpPublic::Ed448(..),
))
| AsymmetricJsonWebKey::Private(Private::Okp(
OkpPrivate::Ed25519(..) | OkpPrivate::Ed448(..),
)),
Signing(JsonWebSigningAlgorithm::EdDSA),
)
| (
AsymmetricJsonWebKey::Public(Public::Rsa(..))
| AsymmetricJsonWebKey::Private(Private::Rsa(..)),
Signing(JsonWebSigningAlgorithm::Rsa(..))
| Encryption(JsonWebEncryptionAlgorithm::Rsa1_5)
| Encryption(JsonWebEncryptionAlgorithm::RsaesOaep(..)),
) => true,
_ => false,
},
_ => false,
}
}
}
pub trait FromKey<K>: Sized {
type Error;
fn from_key(value: K, alg: JsonWebAlgorithm) -> Result<Self, Self::Error>;
}
pub trait IntoJsonWebKey: Sealed {
type Algorithm;
type Error;
fn into_jwk(self, alg: Option<impl Into<Self::Algorithm>>) -> Result<JsonWebKey, Self::Error>;
}
mod hash_impl {
use core::hash::{Hash, Hasher};
use super::{symmetric::OctetSequence, JsonWebKey};
use crate::crypto::{ec, okp, rsa};
impl_thumbprint_hash_trait!(ec::P256PublicKey, ec::P256PrivateKey);
impl_thumbprint_hash_trait!(ec::P384PublicKey, ec::P384PrivateKey);
impl_thumbprint_hash_trait!(ec::P521PublicKey, ec::P521PrivateKey);
impl_thumbprint_hash_trait!(ec::Secp256k1PublicKey, ec::Secp256k1PrivateKey);
impl_thumbprint_hash_trait!(okp::Ed25519PublicKey, okp::Ed25519PrivateKey);
impl_thumbprint_hash_trait!(okp::Ed448PublicKey, okp::Ed448PrivateKey);
impl_thumbprint_hash_trait!(rsa::PublicKey, rsa::PrivateKey);
impl_thumbprint_hash_trait!(OctetSequence);
impl Hash for JsonWebKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.key_type.hash(state);
}
}
#[test]
fn smoke() {
use crate::{jwk::Thumbprint, JsonWebKey};
#[allow(unused_extern_crates)]
extern crate std;
let serialized_private_key = r#"
{
"crv": "P-256",
"kty": "EC",
"x": "1uiXGPoQ3eLR3VOsCfnx1YzIJZGUQLbVfbl1CpCHcs0",
"y": "danaoyQqKi48vlB2jnCoFmq3PdIbYwIRJyNKWiindZM",
"d": "eLGzm5zd242okyN9SQBvmaC_4EPvASCgMhFgwtBvf3k",
"alg": "ES256"
}
"#;
let private_key: JsonWebKey =
serde_json::from_str(serialized_private_key).expect("valid key");
let expected_prehash = r#"{"crv":"P-256","kty":"EC","x":"1uiXGPoQ3eLR3VOsCfnx1YzIJZGUQLbVfbl1CpCHcs0","y":"danaoyQqKi48vlB2jnCoFmq3PdIbYwIRJyNKWiindZM"}"#;
assert_eq!(private_key.thumbprint_prehashed(), expected_prehash);
let mut hasher = std::hash::DefaultHasher::default();
private_key.hash(&mut hasher);
let hash_private = hasher.finish();
let serialized_public_key = r#"
{
"crv": "P-256",
"kty": "EC",
"x": "1uiXGPoQ3eLR3VOsCfnx1YzIJZGUQLbVfbl1CpCHcs0",
"y": "danaoyQqKi48vlB2jnCoFmq3PdIbYwIRJyNKWiindZM",
"alg": "ES256"
}
"#;
let public_key: JsonWebKey = serde_json::from_str(serialized_public_key).unwrap();
let mut hasher = std::hash::DefaultHasher::default();
public_key.hash(&mut hasher);
let hash_public = hasher.finish();
assert_ne!(hash_private, hash_public);
}
}