use core::fmt;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sha2::{Digest as _, Sha256};
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::JwkError;
use super::base64url;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum KeyType {
Ed25519,
P256,
Secp256k1,
Rsa2048,
Rsa4096,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Jwk {
kty: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
crv: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
x: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
y: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
n: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
e: Option<String>,
#[serde(flatten)]
pub(crate) private: PrivateParams,
#[serde(rename = "use", default, skip_serializing_if = "Option::is_none")]
key_use: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
key_ops: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
alg: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
kid: Option<String>,
#[serde(flatten)]
additional: IndexMap<String, Value>,
}
#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub(crate) struct PrivateParams {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) d: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) p: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) q: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) dp: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) dq: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) qi: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) oth: Option<Vec<OtherPrime>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) k: Option<String>,
}
impl PrivateParams {
fn is_empty(&self) -> bool {
*self == Self::default()
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Zeroize)]
pub(crate) struct OtherPrime {
pub(crate) r: String,
pub(crate) d: String,
pub(crate) t: String,
}
fn decode_member(name: &'static str, value: Option<&str>) -> Result<Vec<u8>, JwkError> {
let value = value.ok_or(JwkError::MissingMember(name))?;
base64url::decode(value).map_err(|_| JwkError::InvalidBase64Url(name))
}
impl Jwk {
fn with_kty(kty: &str) -> Self {
Self {
kty: kty.to_owned(),
crv: None,
x: None,
y: None,
n: None,
e: None,
key_use: None,
key_ops: None,
alg: None,
kid: None,
private: PrivateParams::default(),
additional: IndexMap::new(),
}
}
pub fn from_ed25519_public(public: &[u8]) -> Result<Self, JwkError> {
let bytes: &[u8; 32] = public.try_into().map_err(|_| JwkError::InvalidKey {
key: "Ed25519",
reason: format!("expected 32 bytes, found {}", public.len()),
})?;
ed25519_dalek::VerifyingKey::from_bytes(bytes).map_err(|e| JwkError::InvalidKey {
key: "Ed25519",
reason: e.to_string(),
})?;
let mut jwk = Self::with_kty("OKP");
jwk.crv = Some("Ed25519".to_owned());
jwk.x = Some(base64url::encode(bytes));
Ok(jwk)
}
pub fn from_x25519_public(public: &[u8]) -> Result<Self, JwkError> {
if public.len() != 32 {
return Err(JwkError::InvalidKeyLength {
key: "X25519",
expected: 32,
actual: public.len(),
});
}
let mut jwk = Self::with_kty("OKP");
jwk.crv = Some("X25519".to_owned());
jwk.x = Some(base64url::encode(public));
Ok(jwk)
}
pub fn from_p256_public(sec1: &[u8]) -> Result<Self, JwkError> {
let key = p256::PublicKey::from_sec1_bytes(sec1).map_err(|e| JwkError::InvalidKey {
key: "P-256",
reason: e.to_string(),
})?;
Ok(Self::from_p256_key(&key))
}
fn from_p256_key(key: &p256::PublicKey) -> Self {
use p256::elliptic_curve::sec1::ToSec1Point as _;
let point = key.to_sec1_point(false);
let mut jwk = Self::with_kty("EC");
jwk.crv = Some("P-256".to_owned());
jwk.x = point.x().map(|x| base64url::encode(x));
jwk.y = point.y().map(|y| base64url::encode(y));
jwk
}
#[cfg(feature = "secp256k1")]
#[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
pub fn from_secp256k1_public(sec1: &[u8]) -> Result<Self, JwkError> {
let key = k256::PublicKey::from_sec1_bytes(sec1).map_err(|e| JwkError::InvalidKey {
key: "secp256k1",
reason: e.to_string(),
})?;
Ok(Self::from_secp256k1_key(&key))
}
#[cfg(feature = "secp256k1")]
fn from_secp256k1_key(key: &k256::PublicKey) -> Self {
use k256::elliptic_curve::sec1::ToSec1Point as _;
let point = key.to_sec1_point(false);
let mut jwk = Self::with_kty("EC");
jwk.crv = Some("secp256k1".to_owned());
jwk.x = point.x().map(|x| base64url::encode(x));
jwk.y = point.y().map(|y| base64url::encode(y));
jwk
}
pub fn from_rsa_public(n: &[u8], e: &[u8]) -> Result<Self, JwkError> {
if n.is_empty() || e.is_empty() {
return Err(JwkError::InvalidKey {
key: "RSA",
reason: "modulus and exponent must not be empty".to_owned(),
});
}
let mut jwk = Self::with_kty("RSA");
jwk.n = Some(base64url::encode_uint(n));
jwk.e = Some(base64url::encode_uint(e));
Ok(jwk)
}
pub fn from_spki_der(hint: KeyType, der: &[u8]) -> Result<Self, JwkError> {
match hint {
KeyType::Ed25519 => {
use ed25519_dalek::pkcs8::DecodePublicKey as _;
let key = ed25519_dalek::VerifyingKey::from_public_key_der(der).map_err(|e| {
JwkError::InvalidKey {
key: "Ed25519",
reason: e.to_string(),
}
})?;
Self::from_ed25519_public(&key.to_bytes())
}
KeyType::P256 => {
use p256::pkcs8::DecodePublicKey as _;
let key = p256::PublicKey::from_public_key_der(der).map_err(|e| {
JwkError::InvalidKey {
key: "P-256",
reason: e.to_string(),
}
})?;
Ok(Self::from_p256_key(&key))
}
#[cfg(feature = "secp256k1")]
KeyType::Secp256k1 => {
use k256::pkcs8::DecodePublicKey as _;
let key = k256::PublicKey::from_public_key_der(der).map_err(|e| {
JwkError::InvalidKey {
key: "secp256k1",
reason: e.to_string(),
}
})?;
Ok(Self::from_secp256k1_key(&key))
}
#[cfg(not(feature = "secp256k1"))]
KeyType::Secp256k1 => Err(JwkError::FeatureDisabled {
key: "secp256k1",
feature: "secp256k1",
}),
#[cfg(feature = "rsa")]
KeyType::Rsa2048 | KeyType::Rsa4096 => {
use rsa::pkcs8::DecodePublicKey as _;
use rsa::traits::PublicKeyParts as _;
let key = rsa::RsaPublicKey::from_public_key_der(der).map_err(|e| {
JwkError::InvalidKey {
key: "RSA",
reason: e.to_string(),
}
})?;
let expected_bits = match hint {
KeyType::Rsa4096 => 4096,
_ => 2048,
};
if key.n().bits_vartime() != expected_bits {
return Err(JwkError::KeyMismatch { expected: hint });
}
Self::from_rsa_public(
&key.n().to_be_bytes_trimmed_vartime(),
&key.e().to_be_bytes_trimmed_vartime(),
)
}
#[cfg(not(feature = "rsa"))]
KeyType::Rsa2048 | KeyType::Rsa4096 => Err(JwkError::FeatureDisabled {
key: "RSA",
feature: "rsa",
}),
}
}
pub fn kty(&self) -> &str {
&self.kty
}
pub fn crv(&self) -> Option<&str> {
self.crv.as_deref()
}
pub fn x(&self) -> Option<&str> {
self.x.as_deref()
}
pub fn y(&self) -> Option<&str> {
self.y.as_deref()
}
pub fn n(&self) -> Option<&str> {
self.n.as_deref()
}
pub fn e(&self) -> Option<&str> {
self.e.as_deref()
}
pub fn key_use(&self) -> Option<&str> {
self.key_use.as_deref()
}
pub fn key_ops(&self) -> Option<&[String]> {
self.key_ops.as_deref()
}
pub fn alg(&self) -> Option<&str> {
self.alg.as_deref()
}
pub fn kid(&self) -> Option<&str> {
self.kid.as_deref()
}
pub fn additional(&self) -> &IndexMap<String, Value> {
&self.additional
}
pub fn additional_mut(&mut self) -> &mut IndexMap<String, Value> {
&mut self.additional
}
pub fn is_private(&self) -> bool {
!self.private.is_empty()
}
#[must_use]
pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
self.kid = Some(kid.into());
self
}
#[must_use]
pub fn with_alg(mut self, alg: impl Into<String>) -> Self {
self.alg = Some(alg.into());
self
}
#[must_use]
pub fn with_key_use(mut self, key_use: impl Into<String>) -> Self {
self.key_use = Some(key_use.into());
self
}
#[must_use]
pub fn with_key_ops<I, S>(mut self, key_ops: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.key_ops = Some(key_ops.into_iter().map(Into::into).collect());
self
}
#[must_use]
pub fn public(&self) -> Self {
let mut public = self.clone();
public.private = PrivateParams::default();
public
}
pub fn thumbprint_sha256(&self) -> Result<String, JwkError> {
let canonical = self.thumbprint_input()?;
Ok(base64url::encode(&Sha256::digest(canonical.as_bytes())))
}
pub(crate) fn thumbprint_input(&self) -> Result<String, JwkError> {
fn member(name: &'static str, value: Option<&str>) -> Result<String, JwkError> {
let value = value.ok_or(JwkError::MissingMember(name))?;
Ok(serde_json::to_string(value).unwrap_or_default())
}
let kty = serde_json::to_string(&self.kty).unwrap_or_default();
Ok(match self.kty.as_str() {
"EC" => format!(
r#"{{"crv":{},"kty":{kty},"x":{},"y":{}}}"#,
member("crv", self.crv())?,
member("x", self.x())?,
member("y", self.y())?
),
"OKP" => format!(
r#"{{"crv":{},"kty":{kty},"x":{}}}"#,
member("crv", self.crv())?,
member("x", self.x())?
),
"RSA" => format!(
r#"{{"e":{},"kty":{kty},"n":{}}}"#,
member("e", self.e())?,
member("n", self.n())?
),
"oct" => format!(
r#"{{"k":{},"kty":{kty}}}"#,
member("k", self.private.k.as_deref())?
),
_ => return Err(self.unsupported()),
})
}
pub fn ed25519_public_bytes(&self) -> Result<[u8; 32], JwkError> {
Ok(self.ed25519_verifying_key()?.to_bytes())
}
pub(crate) fn ed25519_verifying_key(&self) -> Result<ed25519_dalek::VerifyingKey, JwkError> {
self.expect("Ed25519", "OKP", Some("Ed25519"))?;
let x = decode_member("x", self.x())?;
let bytes: &[u8; 32] = x.as_slice().try_into().map_err(|_| JwkError::InvalidKey {
key: "Ed25519",
reason: format!("expected 32 bytes, found {}", x.len()),
})?;
ed25519_dalek::VerifyingKey::from_bytes(bytes).map_err(|e| JwkError::InvalidKey {
key: "Ed25519",
reason: e.to_string(),
})
}
pub fn x25519_public_bytes(&self) -> Result<[u8; 32], JwkError> {
self.expect("X25519", "OKP", Some("X25519"))?;
let x = decode_member("x", self.x())?;
x.as_slice()
.try_into()
.map_err(|_| JwkError::InvalidKeyLength {
key: "X25519",
expected: 32,
actual: x.len(),
})
}
pub fn p256_public_bytes(&self) -> Result<[u8; 65], JwkError> {
use p256::elliptic_curve::sec1::ToSec1Point as _;
let point = self.p256_public_key()?.to_sec1_point(false);
point
.as_bytes()
.try_into()
.map_err(|_| JwkError::InvalidKey {
key: "P-256",
reason: "point is not 65 bytes".to_owned(),
})
}
pub(crate) fn p256_public_key(&self) -> Result<p256::PublicKey, JwkError> {
self.expect("P-256", "EC", Some("P-256"))?;
let sec1 = self.ec_uncompressed_point(32, "P-256")?;
p256::PublicKey::from_sec1_bytes(&sec1).map_err(|e| JwkError::InvalidKey {
key: "P-256",
reason: e.to_string(),
})
}
#[cfg(feature = "secp256k1")]
#[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
pub fn secp256k1_public_bytes(&self) -> Result<[u8; 65], JwkError> {
use k256::elliptic_curve::sec1::ToSec1Point as _;
let point = self.secp256k1_public_key()?.to_sec1_point(false);
point
.as_bytes()
.try_into()
.map_err(|_| JwkError::InvalidKey {
key: "secp256k1",
reason: "point is not 65 bytes".to_owned(),
})
}
#[cfg(feature = "secp256k1")]
pub(crate) fn secp256k1_public_key(&self) -> Result<k256::PublicKey, JwkError> {
self.expect("secp256k1", "EC", Some("secp256k1"))?;
let sec1 = self.ec_uncompressed_point(32, "secp256k1")?;
k256::PublicKey::from_sec1_bytes(&sec1).map_err(|e| JwkError::InvalidKey {
key: "secp256k1",
reason: e.to_string(),
})
}
fn ec_uncompressed_point(&self, len: usize, key: &'static str) -> Result<Vec<u8>, JwkError> {
let x = decode_member("x", self.x())?;
let y = decode_member("y", self.y())?;
if x.len() != len || y.len() != len {
return Err(JwkError::InvalidKey {
key,
reason: format!(
"expected {len}-byte coordinates, found {} and {}",
x.len(),
y.len()
),
});
}
let mut sec1 = Vec::with_capacity(1 + 2 * len);
sec1.push(0x04);
sec1.extend_from_slice(&x);
sec1.extend_from_slice(&y);
Ok(sec1)
}
pub fn rsa_public_components(&self) -> Result<(Vec<u8>, Vec<u8>), JwkError> {
self.expect("RSA", "RSA", None)?;
Ok((decode_member("n", self.n())?, decode_member("e", self.e())?))
}
#[cfg(feature = "rsa")]
pub(crate) fn rsa_public_key(&self) -> Result<rsa::RsaPublicKey, JwkError> {
let (n, e) = self.rsa_public_components()?;
if n.is_empty() || e.is_empty() {
return Err(JwkError::InvalidKey {
key: "RSA",
reason: "modulus and exponent must not be empty".to_owned(),
});
}
rsa::RsaPublicKey::new(
rsa::BoxedUint::from_be_slice_vartime(&n),
rsa::BoxedUint::from_be_slice_vartime(&e),
)
.map_err(|e| JwkError::InvalidKey {
key: "RSA",
reason: e.to_string(),
})
}
pub fn to_decoding_key(&self) -> Result<jsonwebtoken::DecodingKey, JwkError> {
use jsonwebtoken::DecodingKey;
let result = match (self.kty.as_str(), self.crv()) {
("OKP", Some("Ed25519")) => {
DecodingKey::from_ed_components(self.x().ok_or(JwkError::MissingMember("x"))?)
}
("EC", Some("P-256" | "P-384")) => DecodingKey::from_ec_components(
self.x().ok_or(JwkError::MissingMember("x"))?,
self.y().ok_or(JwkError::MissingMember("y"))?,
),
("RSA", _) => DecodingKey::from_rsa_components(
self.n().ok_or(JwkError::MissingMember("n"))?,
self.e().ok_or(JwkError::MissingMember("e"))?,
),
_ => return Err(self.unsupported()),
};
result.map_err(|e| JwkError::DecodingKey(e.to_string()))
}
pub(super) fn expect(
&self,
expected: &'static str,
kty: &str,
crv: Option<&str>,
) -> Result<(), JwkError> {
if self.kty == kty && (crv.is_none() || self.crv() == crv) {
Ok(())
} else {
Err(JwkError::UnexpectedKeyType {
expected,
kty: self.kty.clone(),
crv: self.crv.clone(),
})
}
}
pub(super) fn unsupported(&self) -> JwkError {
JwkError::UnsupportedKeyType {
kty: self.kty.clone(),
crv: self.crv.clone(),
}
}
}
impl fmt::Debug for Jwk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Jwk");
s.field("kty", &self.kty);
for (name, value) in [
("crv", &self.crv),
("x", &self.x),
("y", &self.y),
("n", &self.n),
("e", &self.e),
("use", &self.key_use),
("alg", &self.alg),
("kid", &self.kid),
] {
if let Some(value) = value {
s.field(name, value);
}
}
if let Some(key_ops) = &self.key_ops {
s.field("key_ops", key_ops);
}
if !self.additional.is_empty() {
s.field("additional", &self.additional);
}
s.field("private", &self.is_private());
s.finish()
}
}
impl fmt::Display for Jwk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(&self.public()).map_err(|_| fmt::Error)?;
f.write_str(&json)
}
}