From 0d890ca6d750a09335d8472ddea3c3af4b53dd11 Mon Sep 17 00:00:00 2001 From: Nick Hynes Date: Mon, 25 Jan 2021 21:06:26 +0300 Subject: [PATCH] Add x509 registered claims --- src/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++++++---------- src/tests.rs | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index df96dc0..7b05270 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,42 @@ pub struct JsonWebKey { #[serde(default, rename = "alg", skip_serializing_if = "Option::is_none")] pub algorithm: Option, + + #[serde(default, flatten, skip_serializing_if = "X509Params::is_empty")] + pub x5: X509Params, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct X509Params { + /// x5u: The URL of the X.509 cert corresponding to this key. + #[serde(default, rename = "x5u", skip_serializing_if = "Option::is_none")] + url: Option, + + /// x5c: The certificate chain used to verify this key. + #[serde(default, rename = "x5c", skip_serializing_if = "Option::is_none")] + cert_chain: Option, + + /// x5t: The SHA-1 thumbprint of the DER-encoded X.509 version of the public key. + #[serde(default, rename = "x5t", skip_serializing_if = "Option::is_none")] + thumbprint: Option, + + /// x5t#S256: The same data as the thumbprint, but digested using SHA-256 + #[serde(default, rename = "x5t#S256", skip_serializing_if = "Option::is_none")] + thumbprint_sha256: Option, +} + +impl X509Params { + fn is_empty(&self) -> bool { + matches!( + self, + X509Params { + url: None, + cert_chain: None, + thumbprint: None, + thumbprint_sha256: None, + } + ) + } } impl JsonWebKey { @@ -103,6 +139,7 @@ impl JsonWebKey { key_ops: KeyOps::empty(), key_id: None, algorithm: None, + x5: Default::default(), } } @@ -184,17 +221,15 @@ impl Key { /// Returns true iff this key only contains private components (i.e. a private asymmetric /// key or a symmetric key). pub fn is_private(&self) -> bool { - match self { - Self::Symmetric { .. } - | Self::EC { - curve: Curve::P256 { d: Some(_), .. }, - .. - } - | Self::RSA { - private: Some(_), .. - } => true, - _ => false, + matches!(self, Self::Symmetric { .. } + | Self::EC { + curve: Curve::P256 { d: Some(_), .. }, + .. } + | Self::RSA { + private: Some(_), .. + } + ) } /// Returns the public part of this key (symmetric keys have no public parts). diff --git a/src/tests.rs b/src/tests.rs index ca2a2cf..232a5ad 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -64,6 +64,7 @@ fn deserialize_es256() { key_id: Some("a key".into()), key_ops: KeyOps::empty(), key_use: Some(KeyUse::Encryption), + x5: Default::default(), } ); } @@ -82,6 +83,7 @@ fn serialize_es256() { algorithm: None, key_ops: KeyOps::empty(), key_use: None, + x5: Default::default(), }; assert_eq!( jwk.to_string(), @@ -135,6 +137,7 @@ fn deserialize_hs256() { key_id: None, key_ops: KeyOps::SIGN | KeyOps::VERIFY, key_use: None, + x5: Default::default(), } ); } @@ -149,6 +152,7 @@ fn serialize_hs256() { algorithm: None, key_ops: KeyOps::empty(), key_use: None, + x5: Default::default(), }; assert_eq!( jwk.to_string(), @@ -225,6 +229,7 @@ fn deserialize_rs256() { key_id: None, key_ops: KeyOps::WRAP_KEY, key_use: Some(KeyUse::Encryption), + x5: Default::default(), } ); } @@ -250,6 +255,7 @@ fn serialize_rs256() { algorithm: None, key_ops: KeyOps::empty(), key_use: None, + x5: Default::default(), }; assert_eq!( jwk.to_string(), @@ -413,3 +419,31 @@ fn rsa_is_private() { assert!(!public_jwk.key.is_private()); assert!(!public_jwk.key.to_public().unwrap().is_private()); } + +#[test] +fn x509_params() { + let private_jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); + assert!(private_jwk.key.is_private()); + assert!(!private_jwk.key.to_public().unwrap().is_private()); + + static X509_JWK_FIXTURE: &str = r#"{ + "kty": "oct", + "k": "TdSBZdXL5n39JXlQc7QL3w", + "x5u": "https://example.com/testing.crt", + "x5c": "---BEGIN CERTIFICATE---...", + "x5t": "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", + "x5t#S256": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" + }"#; + + let jwk = JsonWebKey::from_str(X509_JWK_FIXTURE).unwrap(); + assert_eq!(jwk.x5.url.unwrap(), "https://example.com/testing.crt"); + assert_eq!(jwk.x5.cert_chain.unwrap(), "---BEGIN CERTIFICATE---..."); + assert_eq!( + jwk.x5.thumbprint.unwrap(), + "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" + ); + assert_eq!( + jwk.x5.thumbprint_sha256.unwrap(), + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" + ); +}