From 369d1090bb1a332ba2f89e657456e7a777f8c0e3 Mon Sep 17 00:00:00 2001 From: Adam Gleave Date: Mon, 1 Nov 2021 13:26:13 +0000 Subject: [PATCH] (`next`) Remove chrono (#213) * Remove chrono from all dependencies * Fix tests * Fix tests and comments --- Cargo.toml | 6 +- examples/{custom_chrono.rs => custom_time.rs} | 64 +++++++++++-------- tests/ecdsa/mod.rs | 6 +- tests/eddsa/mod.rs | 4 +- tests/hmac.rs | 8 +-- tests/rsa/mod.rs | 8 +-- 6 files changed, 52 insertions(+), 44 deletions(-) rename examples/{custom_chrono.rs => custom_time.rs} (63%) diff --git a/Cargo.toml b/Cargo.toml index 2af0e53..f8e786c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,11 +18,11 @@ ring = { version = "0.16.5", features = ["std"] } base64 = "0.13" # For PEM decoding pem = "1" -simple_asn1 = "0.5" +simple_asn1 = "0.6" [dev-dependencies] -# For the custom chrono example -chrono = "0.4" +# For the custom time example +time = "0.3" criterion = "0.3" [[bench]] diff --git a/examples/custom_chrono.rs b/examples/custom_time.rs similarity index 63% rename from examples/custom_chrono.rs rename to examples/custom_time.rs index 987b859..d502a0f 100644 --- a/examples/custom_chrono.rs +++ b/examples/custom_time.rs @@ -1,6 +1,6 @@ -use chrono::prelude::*; use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation}; use serde::{Deserialize, Serialize}; +use time::{Duration, OffsetDateTime}; const SECRET: &str = "some-secret"; @@ -8,63 +8,71 @@ const SECRET: &str = "some-secret"; struct Claims { sub: String, #[serde(with = "jwt_numeric_date")] - iat: DateTime, + iat: OffsetDateTime, #[serde(with = "jwt_numeric_date")] - exp: DateTime, + exp: OffsetDateTime, } impl Claims { /// If a token should always be equal to its representation after serializing and deserializing - /// again, this function must be used for construction. `DateTime` contains a microsecond field - /// but JWT timestamps are defined as UNIX timestamps (seconds). This function normalizes the - /// timestamps. - pub fn new(sub: String, iat: DateTime, exp: DateTime) -> Self { + /// again, this function must be used for construction. `OffsetDateTime` contains a microsecond + /// field but JWT timestamps are defined as UNIX timestamps (seconds). This function normalizes + /// the timestamps. + pub fn new(sub: String, iat: OffsetDateTime, exp: OffsetDateTime) -> Self { // normalize the timestamps by stripping of microseconds - let iat = iat.date().and_hms_milli(iat.hour(), iat.minute(), iat.second(), 0); - let exp = exp.date().and_hms_milli(exp.hour(), exp.minute(), exp.second(), 0); + let iat = iat + .date() + .with_hms_milli(iat.hour(), iat.minute(), iat.second(), 0) + .unwrap() + .assume_utc(); + let exp = exp + .date() + .with_hms_milli(exp.hour(), exp.minute(), exp.second(), 0) + .unwrap() + .assume_utc(); + Self { sub, iat, exp } } } mod jwt_numeric_date { - //! Custom serialization of DateTime to conform with the JWT spec (RFC 7519 section 2, "Numeric Date") - use chrono::{DateTime, TimeZone, Utc}; + //! Custom serialization of OffsetDateTime to conform with the JWT spec (RFC 7519 section 2, "Numeric Date") use serde::{self, Deserialize, Deserializer, Serializer}; + use time::OffsetDateTime; - /// Serializes a DateTime to a Unix timestamp (milliseconds since 1970/1/1T00:00:00T) - pub fn serialize(date: &DateTime, serializer: S) -> Result + /// Serializes an OffsetDateTime to a Unix timestamp (milliseconds since 1970/1/1T00:00:00T) + pub fn serialize(date: &OffsetDateTime, serializer: S) -> Result where S: Serializer, { - let timestamp = date.timestamp(); + let timestamp = date.unix_timestamp(); serializer.serialize_i64(timestamp) } /// Attempts to deserialize an i64 and use as a Unix timestamp - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - Utc.timestamp_opt(i64::deserialize(deserializer)?, 0) - .single() // If there are multiple or no valid DateTimes from timestamp, return None - .ok_or_else(|| serde::de::Error::custom("invalid Unix timestamp value")) + OffsetDateTime::from_unix_timestamp(i64::deserialize(deserializer)?) + .map_err(|_| serde::de::Error::custom("invalid Unix timestamp value")) } #[cfg(test)] mod tests { - const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gRGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.RTgha0S53MjPC2pMA4e2oMzaBxSY3DMjiYR2qFfV55A"; + const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gT2Zmc2V0RGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.BcPipupP9oIV6uFRI6Acn7FMLws_wA3oo6CrfeFF3Gg"; use super::super::{Claims, SECRET}; - use chrono::{Duration, TimeZone, Utc}; use jsonwebtoken::{ decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; + use time::{Duration, OffsetDateTime}; #[test] fn round_trip() { - let sub = "Custom DateTime ser/de".to_string(); - let iat = Utc.timestamp(0, 0); - let exp = Utc.timestamp(32503680000, 0); + let sub = "Custom OffsetDateTime ser/de".to_string(); + let iat = OffsetDateTime::from_unix_timestamp(0).unwrap(); + let exp = OffsetDateTime::from_unix_timestamp(32503680000).unwrap(); let claims = Claims::new(sub.clone(), iat, exp); @@ -100,9 +108,9 @@ mod jwt_numeric_date { #[test] fn to_token_and_parse_equals_identity() { - let iat = Utc::now(); + let iat = OffsetDateTime::now_utc(); let exp = iat + Duration::days(1); - let sub = "Custom DateTime ser/de".to_string(); + let sub = "Custom OffsetDateTime ser/de".to_string(); let claims = Claims::new(sub.clone(), iat, exp); @@ -124,9 +132,9 @@ mod jwt_numeric_date { } fn main() -> Result<(), Box> { - let sub = "Custom DateTime ser/de".to_string(); - let iat = Utc::now(); - let exp = iat + chrono::Duration::days(1); + let sub = "Custom OffsetDateTime ser/de".to_string(); + let iat = OffsetDateTime::now_utc(); + let exp = iat + Duration::days(1); let claims = Claims::new(sub, iat, exp); diff --git a/tests/ecdsa/mod.rs b/tests/ecdsa/mod.rs index 7637d90..a54a5c4 100644 --- a/tests/ecdsa/mod.rs +++ b/tests/ecdsa/mod.rs @@ -1,9 +1,9 @@ -use chrono::Utc; use jsonwebtoken::{ crypto::{sign, verify}, decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Claims { @@ -49,7 +49,7 @@ fn round_trip_claim() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode( &Header::new(Algorithm::ES256), @@ -75,7 +75,7 @@ fn roundtrip_with_jwtio_example() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode( &Header::new(Algorithm::ES384), diff --git a/tests/eddsa/mod.rs b/tests/eddsa/mod.rs index 819de0d..a3ca291 100644 --- a/tests/eddsa/mod.rs +++ b/tests/eddsa/mod.rs @@ -1,9 +1,9 @@ -use chrono::Utc; use jsonwebtoken::{ crypto::{sign, verify}, decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Claims { @@ -49,7 +49,7 @@ fn round_trip_claim() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode( &Header::new(Algorithm::EdDSA), diff --git a/tests/hmac.rs b/tests/hmac.rs index 8968dfa..ace5d10 100644 --- a/tests/hmac.rs +++ b/tests/hmac.rs @@ -1,10 +1,10 @@ -use chrono::Utc; use jsonwebtoken::errors::ErrorKind; use jsonwebtoken::{ crypto::{sign, verify}, decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Claims { @@ -34,7 +34,7 @@ fn encode_with_custom_header() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let header = Header { kid: Some("kid".to_string()), ..Default::default() }; let token = encode(&header, &my_claims, &EncodingKey::from_secret(b"secret")).unwrap(); @@ -53,7 +53,7 @@ fn round_trip_claim() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret(b"secret")).unwrap(); @@ -122,7 +122,7 @@ fn encode_wrong_alg_family() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let claims = encode(&Header::default(), &my_claims, &EncodingKey::from_rsa_der(b"secret")); claims.unwrap(); diff --git a/tests/rsa/mod.rs b/tests/rsa/mod.rs index a05e7b4..78d8b51 100644 --- a/tests/rsa/mod.rs +++ b/tests/rsa/mod.rs @@ -1,9 +1,9 @@ -use chrono::Utc; use jsonwebtoken::{ crypto::{sign, verify}, decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; const RSA_ALGORITHMS: &[Algorithm] = &[ Algorithm::RS256, @@ -78,7 +78,7 @@ fn round_trip_claim() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem"); let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); @@ -104,7 +104,7 @@ fn rsa_modulus_exponent() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let n = "yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ"; let e = "AQAB"; @@ -132,7 +132,7 @@ fn roundtrip_with_jwtio_example_jey() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), - exp: Utc::now().timestamp() + 10000, + exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; for &alg in RSA_ALGORITHMS {