From fd60bf20feb2d27142a489712f47ca717b9cd34c Mon Sep 17 00:00:00 2001 From: andrew <> Date: Wed, 10 Jan 2024 21:05:15 +0900 Subject: [PATCH 1/6] Changes to support ACME, including JWS --- src/decoding.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ src/encoding.rs | 28 +++++++++++++++++++++++++ src/header.rs | 12 +++++++++++ src/jws.rs | 24 +++++++++++++++++++++ src/lib.rs | 5 +++-- 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/jws.rs diff --git a/src/decoding.rs b/src/decoding.rs index 8d87f03d..ea4a8bc8 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -6,6 +6,7 @@ use crate::crypto::verify; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; use crate::jwk::{AlgorithmParameters, Jwk}; +use crate::jws::Jws; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_decode, DecodedJwtPartClaims}; @@ -286,3 +287,58 @@ pub fn decode_header(token: &str) -> Result
{ let (_, header) = expect_two!(message.rsplitn(2, '.')); Header::from_encoded(header) } + +/// Verify signature of a JWS, and return the header object +/// +/// If the token or its signature is invalid, it will return an error. +fn verify_jws_signature( + jws: &Jws, + key: &DecodingKey, + validation: &Validation, +) -> Result
{ + if validation.validate_signature && validation.algorithms.is_empty() { + return Err(new_error(ErrorKind::MissingAlgorithm)); + } + + if validation.validate_signature { + for alg in &validation.algorithms { + if key.family != alg.family() { + return Err(new_error(ErrorKind::InvalidAlgorithm)); + } + } + } + + let header = Header::from_encoded(&jws.protected)?; + + if validation.validate_signature && !validation.algorithms.contains(&header.alg) { + return Err(new_error(ErrorKind::InvalidAlgorithm)); + } + + let message = [jws.protected.as_str(), jws.payload.as_str()].join("."); + + if validation.validate_signature + && !verify(&jws.signature, message.as_bytes(), key, header.alg)? + { + return Err(new_error(ErrorKind::InvalidSignature)); + } + + Ok(header) +} + +/// Validate a received JWS and decode into the header and claims. +pub fn decode_jws( + jws: &Jws, + key: &DecodingKey, + validation: &Validation, +) -> Result> { + match verify_jws_signature(jws, key, validation) { + Err(e) => Err(e), + Ok(header) => { + let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(&jws.payload)?; + let claims = decoded_claims.deserialize()?; + validate(decoded_claims.deserialize()?, validation)?; + + Ok(TokenData { header, claims }) + } + } +} diff --git a/src/encoding.rs b/src/encoding.rs index 26f5c4c3..d57ec541 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -5,6 +5,7 @@ use crate::algorithms::AlgorithmFamily; use crate::crypto; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; +use crate::jws::Jws; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::b64_encode_part; @@ -129,3 +130,30 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R Ok([message, signature].join(".")) } + +/// Encode the header and claims given and sign the payload using the algorithm from the header and the key. +/// If the algorithm given is RSA or EC, the key needs to be in the PEM format. This produces a JWS instead of +/// a JWT -- usage is similar to `encode`, see that for more details. +pub fn encode_jws( + header: &Header, + claims: Option<&T>, + key: &EncodingKey, +) -> Result> { + if key.family != header.alg.family() { + return Err(new_error(ErrorKind::InvalidAlgorithm)); + } + let encoded_header = b64_encode_part(header)?; + let encoded_claims = match claims { + Some(claims) => b64_encode_part(claims)?, + None => "".to_string(), + }; + let message = [encoded_header.as_str(), encoded_claims.as_str()].join("."); + let signature = crypto::sign(message.as_bytes(), key, header.alg)?; + + Ok(Jws { + protected: encoded_header, + payload: encoded_claims, + signature: signature, + _pd: Default::default(), + }) +} diff --git a/src/header.rs b/src/header.rs index 220f0fa4..6a414434 100644 --- a/src/header.rs +++ b/src/header.rs @@ -64,6 +64,16 @@ pub struct Header { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "x5t#S256")] pub x5t_s256: Option, + /// ACME: The URL to which this JWS object is directed + /// + /// Defined in [RFC8555#6.4](https://datatracker.ietf.org/doc/html/rfc8555#section-6.4). + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, + /// ACME: Random data for preventing replay attacks. + /// + /// Defined in [RFC8555#6.5.2](https://datatracker.ietf.org/doc/html/rfc8555#section-6.5.2). + #[serde(skip_serializing_if = "Option::is_none")] + pub nonce: Option, } impl Header { @@ -80,6 +90,8 @@ impl Header { x5c: None, x5t: None, x5t_s256: None, + url: None, + nonce: None, } } diff --git a/src/jws.rs b/src/jws.rs new file mode 100644 index 00000000..0d1328f2 --- /dev/null +++ b/src/jws.rs @@ -0,0 +1,24 @@ +//! JSON Web Signatures data type. +use std::marker::PhantomData; + +use serde::{Deserialize, Serialize}; + +/// This is a serde-compatible JSON Web Signature structure. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Jws { + /// The base64 encoded header data. + /// + /// Defined in [RFC7515#3.2](https://tools.ietf.org/html/rfc7515#section-3.2). + pub protected: String, + /// The base64 encoded claims data. + /// + /// Defined in [RFC7515#3.2](https://tools.ietf.org/html/rfc7515#section-3.2). + pub payload: String, + /// The signature on the other fields. + /// + /// Defined in [RFC7515#3.2](https://tools.ietf.org/html/rfc7515#section-3.2). + pub signature: String, + /// Unused, for associating type metadata. + #[serde(skip)] + pub _pd: PhantomData, +} diff --git a/src/lib.rs b/src/lib.rs index 0c8664bf..c7195e66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,13 +12,14 @@ mod encoding; pub mod errors; mod header; pub mod jwk; +pub mod jws; #[cfg(feature = "use_pem")] mod pem; mod serialization; mod validation; pub use algorithms::Algorithm; -pub use decoding::{decode, decode_header, DecodingKey, TokenData}; -pub use encoding::{encode, EncodingKey}; +pub use decoding::{decode, decode_header, decode_jws, DecodingKey, TokenData}; +pub use encoding::{encode, encode_jws, EncodingKey}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; From b0072fdf9595c99b3d3493dbcf4edda17a576e85 Mon Sep 17 00:00:00 2001 From: andrew <> Date: Fri, 12 Jan 2024 20:34:11 +0900 Subject: [PATCH 2/6] Crit, zip, enc, readme, refactor --- README.md | 14 ++++++ src/decoding.rs | 55 ++++++++++------------- src/header.rs | 113 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 149 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index ccd4e759..f5c5fffc 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,20 @@ let token = decode::(&token, &DecodingKey::from_rsa_components(jwk["n"], If your key is in PEM format, it is better performance wise to generate the `DecodingKey` once in a `lazy_static` or something similar and reuse it. +### Encoding and decoding JWS + +JWS is handled the same way as JWT, but using `encode_jws` and `decode_jws`: + +```rust +let encoded = encode_jws(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?; +my_claims = decode_jws(&encoded, &DecodingKey::from_secret("secret".as_ref()), &Validation::default())?.claims; +``` + +`encode_jws` returns a `Jws` struct which can be placed in other structs or serialized/deserialized from JSON directly. + +The generic parameter in `Jws` indicates the claims type and prevents accidentally encoding or decoding the wrong claims type +when the Jws is nested in another struct. + ### Convert SEC1 private key to PKCS8 `jsonwebtoken` currently only supports PKCS8 format for private EC keys. If your key has `BEGIN EC PRIVATE KEY` at the top, this is a SEC1 type and can be converted to PKCS8 like so: diff --git a/src/decoding.rs b/src/decoding.rs index ea4a8bc8..9974b42f 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -202,14 +202,13 @@ impl DecodingKey { } } -/// Verify signature of a JWT, and return header object and raw payload -/// -/// If the token or its signature is invalid, it will return an error. -fn verify_signature<'a>( - token: &'a str, +fn verify_signature_body( + header: &Header, + message: &str, + signature: &str, key: &DecodingKey, validation: &Validation, -) -> Result<(Header, &'a str)> { +) -> Result<()> { if validation.validate_signature && validation.algorithms.is_empty() { return Err(new_error(ErrorKind::MissingAlgorithm)); } @@ -222,10 +221,6 @@ fn verify_signature<'a>( } } - let (signature, message) = expect_two!(token.rsplitn(2, '.')); - let (payload, header) = expect_two!(message.rsplitn(2, '.')); - let header = Header::from_encoded(header)?; - if validation.validate_signature && !validation.algorithms.contains(&header.alg) { return Err(new_error(ErrorKind::InvalidAlgorithm)); } @@ -234,6 +229,23 @@ fn verify_signature<'a>( return Err(new_error(ErrorKind::InvalidSignature)); } + return Ok(()); +} + +/// Verify signature of a JWT, and return header object and raw payload +/// +/// If the token or its signature is invalid, it will return an error. +fn verify_signature<'a>( + token: &'a str, + key: &DecodingKey, + validation: &Validation, +) -> Result<(Header, &'a str)> { + let (signature, message) = expect_two!(token.rsplitn(2, '.')); + let (payload, header) = expect_two!(message.rsplitn(2, '.')); + let header = Header::from_encoded(header)?; + + verify_signature_body(&header, message, signature, key, validation)?; + Ok((header, payload)) } @@ -296,31 +308,10 @@ fn verify_jws_signature( key: &DecodingKey, validation: &Validation, ) -> Result
{ - if validation.validate_signature && validation.algorithms.is_empty() { - return Err(new_error(ErrorKind::MissingAlgorithm)); - } - - if validation.validate_signature { - for alg in &validation.algorithms { - if key.family != alg.family() { - return Err(new_error(ErrorKind::InvalidAlgorithm)); - } - } - } - let header = Header::from_encoded(&jws.protected)?; - - if validation.validate_signature && !validation.algorithms.contains(&header.alg) { - return Err(new_error(ErrorKind::InvalidAlgorithm)); - } - let message = [jws.protected.as_str(), jws.payload.as_str()].join("."); - if validation.validate_signature - && !verify(&jws.signature, message.as_bytes(), key, header.alg)? - { - return Err(new_error(ErrorKind::InvalidSignature)); - } + verify_signature_body(&header, &message, &jws.signature, key, validation)?; Ok(header) } diff --git a/src/header.rs b/src/header.rs index 6a414434..4ec2dfd1 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,13 +1,110 @@ use std::result; use base64::{engine::general_purpose::STANDARD, Engine}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::algorithms::Algorithm; use crate::errors::Result; use crate::jwk::Jwk; use crate::serialization::b64_decode; +const ZIP_SERIAL_DEFLATE: &'static str = "DEF"; +const ENC_A128CBC_HS256: &'static str = "A128CBC-HS256"; +const ENC_A192CBC_HS384: &'static str = "A192CBC-HS384"; +const ENC_A256CBC_HS512: &'static str = "A256CBC-HS512"; +const ENC_A128GCM: &'static str = "A128GCM"; +const ENC_A192GCM: &'static str = "A192GCM"; +const ENC_A256GCM: &'static str = "A256GCM"; + +/// Encryption algorithm for encrypted payloads. +/// +/// Defined in [RFC7516#4.1.2](https://datatracker.ietf.org/doc/html/rfc7516#section-4.1.2). +/// +/// Values defined in [RFC7518#5.1](https://datatracker.ietf.org/doc/html/rfc7518#section-5.1). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[allow(clippy::upper_case_acronyms)] +pub enum Enc { + A128CBC_HS256, + A192CBC_HS384, + A256CBC_HS512, + A128GCM, + A192GCM, + A256GCM, + Other(String), +} + +impl Serialize for Enc { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + Enc::A128CBC_HS256 => ENC_A128CBC_HS256, + Enc::A192CBC_HS384 => ENC_A192CBC_HS384, + Enc::A256CBC_HS512 => ENC_A256CBC_HS512, + Enc::A128GCM => ENC_A128GCM, + Enc::A192GCM => ENC_A192GCM, + Enc::A256GCM => ENC_A256GCM, + Enc::Other(v) => v, + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Enc { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + ENC_A128CBC_HS256 => return Ok(Enc::A128CBC_HS256), + ENC_A192CBC_HS384 => return Ok(Enc::A192CBC_HS384), + ENC_A256CBC_HS512 => return Ok(Enc::A256CBC_HS512), + ENC_A128GCM => return Ok(Enc::A128GCM), + ENC_A192GCM => return Ok(Enc::A192GCM), + ENC_A256GCM => return Ok(Enc::A256GCM), + _ => (), + } + Ok(Enc::Other(s)) + } +} +/// Compression applied to plaintext. +/// +/// Defined in [RFC7516#4.1.3](https://datatracker.ietf.org/doc/html/rfc7516#section-4.1.3). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Zip { + Deflate, + Other(String), +} + +impl Serialize for Zip { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + Zip::Deflate => ZIP_SERIAL_DEFLATE, + Zip::Other(v) => v, + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Zip { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + ZIP_SERIAL_DEFLATE => return Ok(Zip::Deflate), + _ => (), + } + Ok(Zip::Other(s)) + } +} + /// 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, Eq, Serialize, Deserialize, Hash)] @@ -64,6 +161,17 @@ pub struct Header { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "x5t#S256")] pub x5t_s256: Option, + /// Critical - indicates header fields that must be understood by the receiver. + /// + /// Defined in [RFC7515#4.1.6](https://tools.ietf.org/html/rfc7515#section-4.1.6). + #[serde(skip_serializing_if = "Option::is_none")] + pub crit: Option>, + /// See `Enc` for description. + #[serde(skip_serializing_if = "Option::is_none")] + pub enc: Option, + /// See `Zip` for description. + #[serde(skip_serializing_if = "Option::is_none")] + pub zip: Option, /// ACME: The URL to which this JWS object is directed /// /// Defined in [RFC8555#6.4](https://datatracker.ietf.org/doc/html/rfc8555#section-6.4). @@ -90,6 +198,9 @@ impl Header { x5c: None, x5t: None, x5t_s256: None, + crit: None, + enc: None, + zip: None, url: None, nonce: None, } From 5a3b07f380bd9310854dd3aa11190ac1d81ccb92 Mon Sep 17 00:00:00 2001 From: andrew <> Date: Sun, 14 Jan 2024 19:58:17 +0900 Subject: [PATCH 3/6] Add support for JWK thumbprints --- README.md | 8 +++++++ src/jwk.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 4 ++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ccd4e759..043ef059 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,14 @@ let token = decode::(&token, &DecodingKey::from_rsa_components(jwk["n"], If your key is in PEM format, it is better performance wise to generate the `DecodingKey` once in a `lazy_static` or something similar and reuse it. +### JWK Thumbprints + +If you have a JWK object, you can generate a thumbprint like + +``` +let tp = my_jwk.thumbprint(&jsonwebtoken::DIGEST_SHA256); +``` + ### Convert SEC1 private key to PKCS8 `jsonwebtoken` currently only supports PKCS8 format for private EC keys. If your key has `BEGIN EC PRIVATE KEY` at the top, this is a SEC1 type and can be converted to PKCS8 like so: diff --git a/src/jwk.rs b/src/jwk.rs index 49c58003..eeeb1c00 100644 --- a/src/jwk.rs +++ b/src/jwk.rs @@ -6,9 +6,11 @@ use crate::{ errors::{self, Error, ErrorKind}, + serialization::b64_encode, Algorithm, }; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use serde_json::json; use std::{fmt, str::FromStr}; /// The intended usage of the public `KeyType`. This enum is serialized `untagged` @@ -416,6 +418,55 @@ impl Jwk { pub fn is_supported(&self) -> bool { self.common.key_algorithm.unwrap().to_algorithm().is_ok() } + + /// Compute the thumbprint of the JWK. + /// + /// Per (RFC-7638)[https://datatracker.ietf.org/doc/html/rfc7638] + pub fn thumbprint(&self, hash_function: &'static ring::digest::Algorithm) -> String { + let pre = match &self.algorithm { + AlgorithmParameters::EllipticCurve(a) => match a.curve { + EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 => { + format!( + r#"{{"crv":{},"kty":{},"x":"{}","y":"{}"}}"#, + serde_json::to_string(&a.curve).unwrap(), + serde_json::to_string(&a.key_type).unwrap(), + a.x, + a.y, + ) + } + EllipticCurve::Ed25519 => panic!("EllipticCurve can't contain this curve type"), + }, + AlgorithmParameters::RSA(a) => { + format!( + r#"{{"e":"{}","kty":{},"n":"{}"}}"#, + a.e, + serde_json::to_string(&a.key_type).unwrap(), + a.n, + ) + } + AlgorithmParameters::OctetKey(a) => { + format!( + r#"{{"k":"{}","kty":{}}}"#, + a.value, + serde_json::to_string(&a.key_type).unwrap() + ) + } + AlgorithmParameters::OctetKeyPair(a) => match a.curve { + EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 => { + panic!("OctetKeyPair can't contain this curve type") + } + EllipticCurve::Ed25519 => { + format!( + r#"{{crv:{},"kty":{},"x":"{}"}}"#, + serde_json::to_string(&a.curve).unwrap(), + serde_json::to_string(&a.key_type).unwrap(), + a.x, + ) + } + }, + }; + return b64_encode(ring::digest::digest(hash_function, &pre.as_bytes())); + } } /// A JWK set @@ -435,7 +486,7 @@ impl JwkSet { #[cfg(test)] mod tests { - use crate::jwk::{AlgorithmParameters, JwkSet, OctetKeyType}; + use crate::jwk::{AlgorithmParameters, Jwk, JwkSet, OctetKeyType, RSAKeyParameters}; use crate::serialization::b64_encode; use crate::Algorithm; use serde_json::json; @@ -471,4 +522,19 @@ mod tests { _ => panic!("Unexpected key algorithm"), } } + + #[test] + #[wasm_bindgen_test] + fn check_thumbprint() { + let tp = Jwk { + common: crate::jwk::CommonParameters { key_id: Some("2011-04-29".to_string()), ..Default::default() }, + algorithm: AlgorithmParameters::RSA(RSAKeyParameters { + key_type: crate::jwk::RSAKeyType::RSA, + n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw".to_string(), + e: "AQAB".to_string(), + }), + } + .thumbprint(&ring::digest::SHA256); + assert_eq!(tp.as_str(), "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"); + } } diff --git a/src/lib.rs b/src/lib.rs index 0c8664bf..d736b39a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,3 +22,7 @@ pub use decoding::{decode, decode_header, DecodingKey, TokenData}; pub use encoding::{encode, EncodingKey}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; + +pub use ring::digest::SHA256 as DIGEST_SHA256; +pub use ring::digest::SHA384 as DIGEST_SHA384; +pub use ring::digest::SHA512 as DIGEST_SHA512; From c64cbe21e00e60690c005637af511828c5ae20cb Mon Sep 17 00:00:00 2001 From: andrew <> Date: Mon, 15 Jan 2024 20:10:20 +0900 Subject: [PATCH 4/6] Add thumbprint hash enum, clippy fixes --- src/jwk.rs | 21 ++++++++++++++++----- src/lib.rs | 4 ---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/jwk.rs b/src/jwk.rs index eeeb1c00..0b8ce9df 100644 --- a/src/jwk.rs +++ b/src/jwk.rs @@ -10,7 +10,6 @@ use crate::{ Algorithm, }; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use serde_json::json; use std::{fmt, str::FromStr}; /// The intended usage of the public `KeyType`. This enum is serialized `untagged` @@ -404,6 +403,13 @@ pub enum AlgorithmParameters { OctetKeyPair(OctetKeyPairParameters), } +/// The function to use to hash the intermediate thumbprint data. +pub enum ThumbprintHash { + SHA256, + SHA384, + SHA512 +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub struct Jwk { #[serde(flatten)] @@ -422,7 +428,12 @@ impl Jwk { /// Compute the thumbprint of the JWK. /// /// Per (RFC-7638)[https://datatracker.ietf.org/doc/html/rfc7638] - pub fn thumbprint(&self, hash_function: &'static ring::digest::Algorithm) -> String { + pub fn thumbprint(&self, hash_function: ThumbprintHash) -> String { + let hash_function = match hash_function { + ThumbprintHash::SHA256 => &ring::digest::SHA256, + ThumbprintHash::SHA384 => &ring::digest::SHA384, + ThumbprintHash::SHA512 => &ring::digest::SHA512, + }; let pre = match &self.algorithm { AlgorithmParameters::EllipticCurve(a) => match a.curve { EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 => { @@ -465,7 +476,7 @@ impl Jwk { } }, }; - return b64_encode(ring::digest::digest(hash_function, &pre.as_bytes())); + return b64_encode(ring::digest::digest(hash_function, pre.as_bytes())); } } @@ -486,7 +497,7 @@ impl JwkSet { #[cfg(test)] mod tests { - use crate::jwk::{AlgorithmParameters, Jwk, JwkSet, OctetKeyType, RSAKeyParameters}; + use crate::jwk::{AlgorithmParameters, Jwk, JwkSet, OctetKeyType, RSAKeyParameters, ThumbprintHash}; use crate::serialization::b64_encode; use crate::Algorithm; use serde_json::json; @@ -534,7 +545,7 @@ mod tests { e: "AQAB".to_string(), }), } - .thumbprint(&ring::digest::SHA256); + .thumbprint(ThumbprintHash::SHA256); assert_eq!(tp.as_str(), "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"); } } diff --git a/src/lib.rs b/src/lib.rs index d736b39a..0c8664bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,3 @@ pub use decoding::{decode, decode_header, DecodingKey, TokenData}; pub use encoding::{encode, EncodingKey}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; - -pub use ring::digest::SHA256 as DIGEST_SHA256; -pub use ring::digest::SHA384 as DIGEST_SHA384; -pub use ring::digest::SHA512 as DIGEST_SHA512; From dbc9dbded4ce512ac21751ffd7ad77af7089ba39 Mon Sep 17 00:00:00 2001 From: andrew <> Date: Mon, 15 Jan 2024 20:14:13 +0900 Subject: [PATCH 5/6] Clippy --- src/decoding.rs | 2 +- src/encoding.rs | 2 +- src/header.rs | 21 ++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/decoding.rs b/src/decoding.rs index 9974b42f..6f87fe61 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -229,7 +229,7 @@ fn verify_signature_body( return Err(new_error(ErrorKind::InvalidSignature)); } - return Ok(()); + Ok(()) } /// Verify signature of a JWT, and return header object and raw payload diff --git a/src/encoding.rs b/src/encoding.rs index d57ec541..dda26c4c 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -153,7 +153,7 @@ pub fn encode_jws( Ok(Jws { protected: encoded_header, payload: encoded_claims, - signature: signature, + signature, _pd: Default::default(), }) } diff --git a/src/header.rs b/src/header.rs index 4ec2dfd1..16e7bde3 100644 --- a/src/header.rs +++ b/src/header.rs @@ -8,13 +8,13 @@ use crate::errors::Result; use crate::jwk::Jwk; use crate::serialization::b64_decode; -const ZIP_SERIAL_DEFLATE: &'static str = "DEF"; -const ENC_A128CBC_HS256: &'static str = "A128CBC-HS256"; -const ENC_A192CBC_HS384: &'static str = "A192CBC-HS384"; -const ENC_A256CBC_HS512: &'static str = "A256CBC-HS512"; -const ENC_A128GCM: &'static str = "A128GCM"; -const ENC_A192GCM: &'static str = "A192GCM"; -const ENC_A256GCM: &'static str = "A256GCM"; +const ZIP_SERIAL_DEFLATE: &str = "DEF"; +const ENC_A128CBC_HS256: &str = "A128CBC-HS256"; +const ENC_A192CBC_HS384: &str = "A192CBC-HS384"; +const ENC_A256CBC_HS512: &str = "A256CBC-HS512"; +const ENC_A128GCM: &str = "A128GCM"; +const ENC_A192GCM: &str = "A192GCM"; +const ENC_A256GCM: &str = "A256GCM"; /// Encryption algorithm for encrypted payloads. /// @@ -22,7 +22,7 @@ const ENC_A256GCM: &'static str = "A256GCM"; /// /// Values defined in [RFC7518#5.1](https://datatracker.ietf.org/doc/html/rfc7518#section-5.1). #[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[allow(clippy::upper_case_acronyms)] +#[allow(clippy::upper_case_acronyms, non_camel_case_types)] pub enum Enc { A128CBC_HS256, A192CBC_HS384, @@ -98,10 +98,9 @@ impl<'de> Deserialize<'de> for Zip { { let s = String::deserialize(deserializer)?; match s.as_str() { - ZIP_SERIAL_DEFLATE => return Ok(Zip::Deflate), - _ => (), + ZIP_SERIAL_DEFLATE => Ok(Zip::Deflate), + _ => Ok(Zip::Other(s)), } - Ok(Zip::Other(s)) } } From a1cf7b7fb98be62b28b73904394eb6150f43a52a Mon Sep 17 00:00:00 2001 From: andrew <> Date: Mon, 15 Jan 2024 21:14:26 +0900 Subject: [PATCH 6/6] Format fixes --- src/jwk.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jwk.rs b/src/jwk.rs index 0b8ce9df..9a656bb9 100644 --- a/src/jwk.rs +++ b/src/jwk.rs @@ -407,7 +407,7 @@ pub enum AlgorithmParameters { pub enum ThumbprintHash { SHA256, SHA384, - SHA512 + SHA512, } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] @@ -497,7 +497,9 @@ impl JwkSet { #[cfg(test)] mod tests { - use crate::jwk::{AlgorithmParameters, Jwk, JwkSet, OctetKeyType, RSAKeyParameters, ThumbprintHash}; + use crate::jwk::{ + AlgorithmParameters, Jwk, JwkSet, OctetKeyType, RSAKeyParameters, ThumbprintHash, + }; use crate::serialization::b64_encode; use crate::Algorithm; use serde_json::json;