From a11106faffb7ba07c0eb92762a6e382318eb7f85 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Wed, 18 Aug 2021 10:31:52 +0200 Subject: [PATCH] Add types for JWK/JWKS based on biscuit (#195) * Add types for JWK/JWKS based on biscuit * Address comments * Fix issues --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 +- README.md | 2 +- examples/auth0.rs | 38 +++++ src/algorithms.rs | 2 +- src/decoding.rs | 3 +- src/header.rs | 9 +- src/jwk.rs | 357 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/pem/decoder.rs | 2 - 10 files changed, 409 insertions(+), 9 deletions(-) create mode 100644 examples/auth0.rs create mode 100644 src/jwk.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4ef727..085febd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: include: - build: pinned os: ubuntu-18.04 - rust: 1.40.0 + rust: 1.46.0 - build: stable os: ubuntu-18.04 rust: stable diff --git a/Cargo.toml b/Cargo.toml index 59e926c..f93aecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ readme = "README.md" description = "Create and decode JWTs in a strongly typed way." homepage = "https://github.com/Keats/jsonwebtoken" repository = "https://github.com/Keats/jsonwebtoken" -keywords = ["jwt", "web", "api", "token", "jwk"] +keywords = ["jwt", "api", "token", "jwk"] edition = "2018" include = ["src/**/*", "benches/**/*", "tests/**/*", "LICENSE", "README.md", "CHANGELOG.md"] diff --git a/README.md b/README.md index d4d13fe..813d005 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ jsonwebtoken = "7" serde = {version = "1.0", features = ["derive"] } ``` -The minimum required Rust version is 1.40. +The minimum required Rust version is 1.46. ## Algorithms This library currently supports the following: diff --git a/examples/auth0.rs b/examples/auth0.rs new file mode 100644 index 0000000..2873cde --- /dev/null +++ b/examples/auth0.rs @@ -0,0 +1,38 @@ +/// Example for the backend to backend implementation +use std::collections::HashMap; + +use jsonwebtoken::jwk::AlgorithmParameters; +use jsonwebtoken::{decode, decode_header, jwk, DecodingKey, Validation}; + +const TOKEN: &str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjFaNTdkX2k3VEU2S1RZNTdwS3pEeSJ9.eyJpc3MiOiJodHRwczovL2Rldi1kdXp5YXlrNC5ldS5hdXRoMC5jb20vIiwic3ViIjoiNDNxbW44c281R3VFU0U1N0Fkb3BhN09jYTZXeVNidmRAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZGV2LWR1enlheWs0LmV1LmF1dGgwLmNvbS9hcGkvdjIvIiwiaWF0IjoxNjIzNTg1MzAxLCJleHAiOjE2MjM2NzE3MDEsImF6cCI6IjQzcW1uOHNvNUd1RVNFNTdBZG9wYTdPY2E2V3lTYnZkIiwic2NvcGUiOiJyZWFkOnVzZXJzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.0MpewU1GgvRqn4F8fK_-Eu70cUgWA5JJrdbJhkCPCxXP-8WwfI-qx1ZQg2a7nbjXICYAEl-Z6z4opgy-H5fn35wGP0wywDqZpqL35IPqx6d0wRvpPMjJM75zVXuIjk7cEhDr2kaf1LOY9auWUwGzPiDB_wM-R0uvUMeRPMfrHaVN73xhAuQWVjCRBHvNscYS5-i6qBQKDMsql87dwR72DgHzMlaC8NnaGREBC-xiSamesqhKPVyGzSkFSaF3ZKpGrSDapqmHkNW9RDBE3GQ9OHM33vzUdVKOjU1g9Leb9PDt0o1U4p3NQoGJPShQ6zgWSUEaqvUZTfkbpD_DoYDRxA"; +const JWKS_REPLY: &str = r#" +{"keys":[{"alg":"RS256","kty":"RSA","use":"sig","n":"2V31IZF-EY2GxXQPI5OaEE--sezizPamNZDW9AjBE2cCErfufM312nT2jUsCnfjsXnh6Z_b-ncOMr97zIZkq1ofU7avemv8nX7NpKmoPBpVrMPprOax2-e3wt-bSfFLIHyghjFLKpkT0LOL_Fimi7xY-J86R06WHojLo3yGzAgQCswZmD4CFf6NcBWDcb6l6kx5vk_AdzHIkVEZH4aikUL_fn3zq5qbE25oOg6pT7F7Pp4zdHOAEKnIRS8tvP8tvvVRkUCrjBxz_Kx6Ne1YOD-fkIMRk_MgIWeKZZzZOYx4VrC0vqYiM-PcKWbNdt1kNoTHOeL06XZeSE6WPZ3VB1Q","e":"AQAB","kid":"1Z57d_i7TE6KTY57pKzDy","x5t":"1gA-aTE9VglLXZnrqvzwWhHsFdk","x5c":["MIIDDTCCAfWgAwIBAgIJHwhLfcIbNvmkMA0GCSqGSIb3DQEBCwUAMCQxIjAgBgNVBAMTGWRldi1kdXp5YXlrNC5ldS5hdXRoMC5jb20wHhcNMjEwNjEzMDcxMTQ1WhcNMzUwMjIwMDcxMTQ1WjAkMSIwIAYDVQQDExlkZXYtZHV6eWF5azQuZXUuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2V31IZF+EY2GxXQPI5OaEE++sezizPamNZDW9AjBE2cCErfufM312nT2jUsCnfjsXnh6Z/b+ncOMr97zIZkq1ofU7avemv8nX7NpKmoPBpVrMPprOax2+e3wt+bSfFLIHyghjFLKpkT0LOL/Fimi7xY+J86R06WHojLo3yGzAgQCswZmD4CFf6NcBWDcb6l6kx5vk/AdzHIkVEZH4aikUL/fn3zq5qbE25oOg6pT7F7Pp4zdHOAEKnIRS8tvP8tvvVRkUCrjBxz/Kx6Ne1YOD+fkIMRk/MgIWeKZZzZOYx4VrC0vqYiM+PcKWbNdt1kNoTHOeL06XZeSE6WPZ3VB1QIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRPX3shmtgajnR4ly5t9VYB66ufGDAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAHtKpX70WU4uXOMjbFKj0e9HMXyCrdcX6TuYiMFqqlOGWM4yghSM8Bd0HkKcirm4DUoC+1dDMzXMZ+tbntavPt1xG0eRFjeocP+kIYTMQEG2LDM5HQ+Z7bdcwlxnuYOZQfpgKAfYbQ8Cxu38sB6q82I+5NJ0w0VXuG7nUZ1RD+rkXaeMYHNoibAtKBoTWrCaFWGV0E55OM+H0ckcHKUUnNXJOyZ+zEOzPFY5iuYIUmn1LfR1P0SLgIMfiooNC5ZuR/wLdbtyKtor2vzz7niEiewz+aPvfuPnWe/vMtQrfS37/yEhCozFnbIps/+S2Ay78mNBDuOAA9fg5yrnOmjABCU="]},{"alg":"RS256","kty":"RSA","use":"sig","n":"0KDpAuJZyDwPg9CfKi0R3QwDROyH0rvd39lmAoqQNqtYPghDToxFMDLpul0QHttbofHPJMKrPfeEFEOvw7KJgelCHZmckVKaz0e4tfu_2Uvw2kFljCmJGfspUU3mXxLyEea9Ef9JqUru6L8f_0_JIDMT3dceqU5ZqbG8u6-HRgRQ5Jqc_fF29Xyw3gxNP_Q46nsp_0yE68UZE1iPy1om0mpu8mpsY1-Nbvm51C8i4_tFQHdUXbhF4cjAoR0gZFNkzr7FCrL4On0hKeLcvxIHD17SxaBsTuCBGd35g7TmXsA4hSimD9taRHA-SkXh558JG5dr-YV9x80qjeSAvTyjcQ","e":"AQAB","kid":"v2HFn4VqJB-U4vtQRJ3Ql","x5t":"AhUBZjtsFdx7C1PFtWAJ756bo5k","x5c":["MIIDDTCCAfWgAwIBAgIJSSFLkuG8uAM8MA0GCSqGSIb3DQEBCwUAMCQxIjAgBgNVBAMTGWRldi1kdXp5YXlrNC5ldS5hdXRoMC5jb20wHhcNMjEwNjEzMDcxMTQ2WhcNMzUwMjIwMDcxMTQ2WjAkMSIwIAYDVQQDExlkZXYtZHV6eWF5azQuZXUuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0KDpAuJZyDwPg9CfKi0R3QwDROyH0rvd39lmAoqQNqtYPghDToxFMDLpul0QHttbofHPJMKrPfeEFEOvw7KJgelCHZmckVKaz0e4tfu/2Uvw2kFljCmJGfspUU3mXxLyEea9Ef9JqUru6L8f/0/JIDMT3dceqU5ZqbG8u6+HRgRQ5Jqc/fF29Xyw3gxNP/Q46nsp/0yE68UZE1iPy1om0mpu8mpsY1+Nbvm51C8i4/tFQHdUXbhF4cjAoR0gZFNkzr7FCrL4On0hKeLcvxIHD17SxaBsTuCBGd35g7TmXsA4hSimD9taRHA+SkXh558JG5dr+YV9x80qjeSAvTyjcQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSEkRwvkyYzzzY/jPd1n7/1VRQNdzAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAGtdl7QwzpaWZjbmd6UINAIlpuWIo2v4EJD9kGan/tUZTiUdBaJVwFHOkLRsbZHc5PmBB5IryjOcrqsmKvFdo6wUZA92qTuQVZrOTea07msOKSWE6yRUh1/VCXH2+vAiB9A4DFZ23WpZikBR+DmiD8NGwVgAwWw9jM6pe7ODY+qxFXGjQdTCHcDdbqG2160nKEHCBvjR1Sc/F0pzHPv8CBJCyGAPTCXX42sKZI92pPzdKSmNNijCuIEYLsjzKVxaUuwEqIshk3mYeu6im4VmXXFj+MlyMsusVWi2py7fGFadamzyiV/bxZe+4xzzrRG1Kow/WnVEizfTdEzFXO6YikE="]}]} +"#; + +fn main() -> Result<(), Box> { + let jwks: jwk::JwkSet = serde_json::from_str(JWKS_REPLY).unwrap(); + + let header = decode_header(TOKEN)?; + let kid = match header.kid { + Some(k) => k, + None => return Err("Token doesn't have a `kid` header field".into()), + }; + if let Some(j) = jwks.find(&kid) { + match j.algorithm { + AlgorithmParameters::RSA(ref rsa) => { + let decoding_key = DecodingKey::from_rsa_components(&rsa.n, &rsa.e).unwrap(); + let mut validation = Validation::new(j.common.algorithm.unwrap()); + validation.validate_exp = false; + let decoded_token = + decode::>(TOKEN, &decoding_key, &validation) + .unwrap(); + println!("{:?}", decoded_token); + } + _ => unreachable!("this should be a RSA"), + } + } else { + return Err("No matching JWK found for the given kid".into()); + } + + Ok(()) +} diff --git a/src/algorithms.rs b/src/algorithms.rs index 15687e0..97191f8 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -12,7 +12,7 @@ pub(crate) enum AlgorithmFamily { /// The algorithms supported for signing/verifying JWTs #[allow(clippy::upper_case_acronyms)] -#[derive(Debug, PartialEq, Hash, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] pub enum Algorithm { /// HMAC using SHA-256 HS256, diff --git a/src/decoding.rs b/src/decoding.rs index 87dcdfe..cb6b8be 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -79,7 +79,6 @@ impl DecodingKey { } /// If you have (n, e) RSA public key components already decoded, use this. - /// TODO: do we need that? pub fn from_rsa_raw_components(modulus: &[u8], exponent: &[u8]) -> Self { DecodingKey { family: AlgorithmFamily::Rsa, @@ -132,7 +131,7 @@ impl DecodingKey { } pub(crate) fn as_bytes(&self) -> &[u8] { match &self.kind { - DecodingKeyKind::SecretOrDer(b) => &b, + DecodingKeyKind::SecretOrDer(b) => b, DecodingKeyKind::RsaModulusExponent { .. } => unreachable!(), } } diff --git a/src/header.rs b/src/header.rs index ab66165..38e9bed 100644 --- a/src/header.rs +++ b/src/header.rs @@ -4,11 +4,12 @@ use serde::{Deserialize, Serialize}; use crate::algorithms::Algorithm; use crate::errors::Result; +use crate::jwk::Jwk; use crate::serialization::b64_decode; /// A basic JWT header, the alg defaults to HS256 and typ is automatically /// set to `JWT`. All the other fields are optional. -#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Header { /// The type of JWS: it can only be "JWT" here /// @@ -29,6 +30,11 @@ pub struct Header { /// Defined in [RFC7515#4.1.2](https://tools.ietf.org/html/rfc7515#section-4.1.2). #[serde(skip_serializing_if = "Option::is_none")] pub jku: Option, + /// JSON Web Key + /// + /// Defined in [RFC7515#4.1.3](https://tools.ietf.org/html/rfc7515#section-4.1.3). + #[serde(skip_serializing_if = "Option::is_none")] + pub jwk: Option, /// Key ID /// /// Defined in [RFC7515#4.1.4](https://tools.ietf.org/html/rfc7515#section-4.1.4). @@ -59,6 +65,7 @@ impl Header { alg: algorithm, cty: None, jku: None, + jwk: None, kid: None, x5u: None, x5c: None, diff --git a/src/jwk.rs b/src/jwk.rs new file mode 100644 index 0000000..0057387 --- /dev/null +++ b/src/jwk.rs @@ -0,0 +1,357 @@ +#![allow(missing_docs)] +///! This crate contains types only for working JWK and JWK Sets +///! This is only meant to be used to deal with public JWK, not generate ones. +///! Most of the code in this file is taken from https://github.com/lawliet89/biscuit but +/// tweaked to remove the private bits as it's not the goal for this crate currently. +///! +use crate::Algorithm; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; + +/// The intended usage of the public `KeyType`. This enum is serialized `untagged` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum PublicKeyUse { + /// Indicates a public key is meant for signature verification + Signature, + /// Indicates a public key is meant for encryption + Encryption, + /// Other usage + Other(String), +} + +impl Serialize for PublicKeyUse { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let string = match *self { + PublicKeyUse::Signature => "sig", + PublicKeyUse::Encryption => "enc", + PublicKeyUse::Other(ref other) => other, + }; + + serializer.serialize_str(string) + } +} + +impl<'de> Deserialize<'de> for PublicKeyUse { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PublicKeyUseVisitor; + impl<'de> de::Visitor<'de> for PublicKeyUseVisitor { + type Value = PublicKeyUse; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "a string") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(match v { + "sig" => PublicKeyUse::Signature, + "enc" => PublicKeyUse::Encryption, + other => PublicKeyUse::Other(other.to_string()), + }) + } + } + + deserializer.deserialize_string(PublicKeyUseVisitor) + } +} + +/// Operations that the key is intended to be used for. This enum is serialized `untagged` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum KeyOperations { + /// Computer digital signature or MAC + Sign, + /// Verify digital signature or MAC + Verify, + /// Encrypt content + Encrypt, + /// Decrypt content and validate decryption, if applicable + Decrypt, + /// Encrypt key + WrapKey, + /// Decrypt key and validate decryption, if applicable + UnwrapKey, + /// Derive key + DeriveKey, + /// Derive bits not to be used as a key + DeriveBits, + /// Other operation + Other(String), +} + +impl Serialize for KeyOperations { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let string = match *self { + KeyOperations::Sign => "sign", + KeyOperations::Verify => "verify", + KeyOperations::Encrypt => "encrypt", + KeyOperations::Decrypt => "decrypt", + KeyOperations::WrapKey => "wrapKey", + KeyOperations::UnwrapKey => "unwrapKey", + KeyOperations::DeriveKey => "deriveKey", + KeyOperations::DeriveBits => "deriveBits", + KeyOperations::Other(ref other) => other, + }; + + serializer.serialize_str(string) + } +} + +impl<'de> Deserialize<'de> for KeyOperations { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct KeyOperationsVisitor; + impl<'de> de::Visitor<'de> for KeyOperationsVisitor { + type Value = KeyOperations; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "a string") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(match v { + "sign" => KeyOperations::Sign, + "verify" => KeyOperations::Verify, + "encrypt" => KeyOperations::Encrypt, + "decrypt" => KeyOperations::Decrypt, + "wrapKey" => KeyOperations::WrapKey, + "unwrapKey" => KeyOperations::UnwrapKey, + "deriveKey" => KeyOperations::DeriveKey, + "deriveBits" => KeyOperations::DeriveBits, + other => KeyOperations::Other(other.to_string()), + }) + } + } + + deserializer.deserialize_string(KeyOperationsVisitor) + } +} + +/// Common JWK parameters +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)] +pub struct CommonParameters { + /// The intended use of the public key. Should not be specified with `key_operations`. + /// See sections 4.2 and 4.3 of [RFC7517](https://tools.ietf.org/html/rfc7517). + #[serde(rename = "use", skip_serializing_if = "Option::is_none", default)] + pub public_key_use: Option, + + /// The "key_ops" (key operations) parameter identifies the operation(s) + /// for which the key is intended to be used. The "key_ops" parameter is + /// intended for use cases in which public, private, or symmetric keys + /// may be present. + /// Should not be specified with `public_key_use`. + /// See sections 4.2 and 4.3 of [RFC7517](https://tools.ietf.org/html/rfc7517). + #[serde(rename = "key_ops", skip_serializing_if = "Option::is_none", default)] + pub key_operations: Option>, + + /// The algorithm intended for use with the key + #[serde(rename = "alg", skip_serializing_if = "Option::is_none", default)] + pub algorithm: Option, + + /// The case sensitive Key ID for the key + #[serde(rename = "kid", skip_serializing_if = "Option::is_none", default)] + pub key_id: Option, + + /// X.509 Public key cerfificate URL. This is currently not implemented (correctly). + /// Serialized to `x5u`. + #[serde(rename = "x5u", skip_serializing_if = "Option::is_none")] + pub x509_url: Option, + + /// X.509 public key certificate chain. This is currently not implemented (correctly). + /// Serialized to `x5c`. + #[serde(rename = "x5c", skip_serializing_if = "Option::is_none")] + pub x509_chain: Option>, + + /// X.509 Certificate thumbprint. This is currently not implemented (correctly). + /// Also not implemented, is the SHA-256 thumbprint variant of this header. + /// Serialized to `x5t`. + // TODO: How to make sure the headers are mutually exclusive? + #[serde(rename = "x5t", skip_serializing_if = "Option::is_none")] + pub x509_fingerprint: Option, +} + +/// Key type value for an Elliptic Curve Key. +/// This single value enum is a workaround for Rust not supporting associated constants. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum EllipticCurveKeyType { + /// Key type value for an Elliptic Curve Key. + EC, +} + +impl Default for EllipticCurveKeyType { + fn default() -> Self { + EllipticCurveKeyType::EC + } +} + +/// Type of cryptographic curve used by a key. This is defined in +/// [RFC 7518 #7.6](https://tools.ietf.org/html/rfc7518#section-7.6) +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum EllipticCurve { + /// P-256 curve + #[serde(rename = "P-256")] + P256, + /// P-384 curve + #[serde(rename = "P-384")] + P384, + /// P-521 curve -- unsupported by `ring`. + #[serde(rename = "P-521")] + P521, +} + +impl Default for EllipticCurve { + fn default() -> Self { + EllipticCurve::P256 + } +} + +/// Parameters for an Elliptic Curve Key +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +pub struct EllipticCurveKeyParameters { + /// Key type value for an Elliptic Curve Key. + #[serde(rename = "kty")] + pub key_type: EllipticCurveKeyType, + /// The "crv" (curve) parameter identifies the cryptographic curve used + /// with the key. + #[serde(rename = "crv")] + pub curve: EllipticCurve, + /// The "x" (x coordinate) parameter contains the x coordinate for the + /// Elliptic Curve point. + pub x: String, + /// The "y" (y coordinate) parameter contains the y coordinate for the + /// Elliptic Curve point. + pub y: String, +} + +/// Key type value for an RSA Key. +/// This single value enum is a workaround for Rust not supporting associated constants. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum RSAKeyType { + /// Key type value for an RSA Key. + RSA, +} + +impl Default for RSAKeyType { + fn default() -> Self { + RSAKeyType::RSA + } +} + +/// Parameters for a RSA Key +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +pub struct RSAKeyParameters { + /// Key type value for a RSA Key + #[serde(rename = "kty")] + pub key_type: RSAKeyType, + + /// The "n" (modulus) parameter contains the modulus value for the RSA + /// public key. + pub n: String, + + /// The "e" (exponent) parameter contains the exponent value for the RSA + /// public key. + pub e: String, +} + +/// Key type value for an Octet symmetric key. +/// This single value enum is a workaround for Rust not supporting associated constants. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum OctetKeyType { + /// Key type value for an Octet symmetric key. + #[serde(rename = "oct")] + Octet, +} + +impl Default for OctetKeyType { + fn default() -> Self { + OctetKeyType::Octet + } +} + +/// Parameters for an Octet Key +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +pub struct OctetKeyParameters { + /// Key type value for an Octet Key + #[serde(rename = "kty")] + pub key_type: OctetKeyType, + /// The octet key value + pub value: String, +} + +/// Key type value for an Octet Key Pair. +/// This single value enum is a workaround for Rust not supporting associated constants. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum OctetKeyPairType { + /// Key type value for an Octet Key Pair. + #[serde(rename = "OKP")] + OctetKeyPair, +} + +impl Default for OctetKeyPairType { + fn default() -> Self { + OctetKeyPairType::OctetKeyPair + } +} + +/// Parameters for an Octet Key Pair +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +pub struct OctetKeyPairParameters { + /// Key type value for an Octet Key Pair + #[serde(rename = "kty")] + pub key_type: OctetKeyPairType, + /// The "crv" (curve) parameter identifies the cryptographic curve used + /// with the key. + #[serde(rename = "crv")] + pub curve: EllipticCurve, + /// The "x" parameter contains the base64 encoded public key + pub x: String, +} + +/// Algorithm specific parameters +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum AlgorithmParameters { + EllipticCurve(EllipticCurveKeyParameters), + RSA(RSAKeyParameters), + OctetKey(OctetKeyParameters), + OctetKeyPair(OctetKeyPairParameters), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Jwk { + #[serde(flatten)] + pub common: CommonParameters, + /// Key algorithm specific parameters + #[serde(flatten)] + pub algorithm: AlgorithmParameters, +} + +/// A JWK set +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct JwkSet { + pub keys: Vec, +} + +impl JwkSet { + /// Find the key in the set that matches the given key id, if any. + pub fn find(&self, kid: &str) -> Option<&Jwk> { + self.keys + .iter() + .find(|jwk| jwk.common.key_id.is_some() && jwk.common.key_id.as_ref().unwrap() == kid) + } +} diff --git a/src/lib.rs b/src/lib.rs index 12003cf..78ca270 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ mod encoding; /// All the errors that can be encountered while encoding/decoding JWTs pub mod errors; mod header; +pub mod jwk; mod pem; mod serialization; mod validation; diff --git a/src/pem/decoder.rs b/src/pem/decoder.rs index f96beca..8bc98a5 100644 --- a/src/pem/decoder.rs +++ b/src/pem/decoder.rs @@ -1,7 +1,5 @@ use crate::errors::{ErrorKind, Result}; -use simple_asn1::{BigUint, OID}; - /// Supported PEM files for EC and RSA Public and Private Keys #[derive(Debug, PartialEq)] enum PemType {