Replace usage of chrono with time crate
The time crate is the underlying crate which chrono uses for its various operations. Unfortunately, chrono is unmaintained and older versions of the time crate have security vulnerabilites[0] which are unfixed in chrono[1]. The medallion code does not use any chrono-specific features and all uses of it could be trivially replaced with the underlying time structs. Note that this change adds calls to `expect`. Where these calls are made, the previous chrono functions also panicked internally if out-of-range values were passed. We noticed this issue while doing a similar refactoring in a program that also uses medallion[2]. [0]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html [1]: https://rustsec.org/advisories/RUSTSEC-2020-0159.html [2]: https://cl.tvl.fyi/c/depot/+/5311
This commit is contained in:
parent
970f33d596
commit
025b143d88
3 changed files with 21 additions and 25 deletions
|
@ -21,4 +21,4 @@ openssl = "~0.10.15"
|
||||||
serde = { version = "^1.0.114", features = [ "derive" ] }
|
serde = { version = "^1.0.114", features = [ "derive" ] }
|
||||||
serde_json = "^1.0.55"
|
serde_json = "^1.0.55"
|
||||||
anyhow = "^1.0.31"
|
anyhow = "^1.0.31"
|
||||||
chrono = "~0.4.11"
|
time = "~0.3"
|
||||||
|
|
18
src/lib.rs
18
src/lib.rs
|
@ -95,8 +95,8 @@ mod tests {
|
||||||
use super::Algorithm::{HS256, RS512};
|
use super::Algorithm::{HS256, RS512};
|
||||||
use crate::{DefaultPayload, DefaultToken, Header, Payload, Token};
|
use crate::{DefaultPayload, DefaultToken, Header, Payload, Token};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::{prelude::*, Duration};
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn raw_data() {
|
pub fn raw_data() {
|
||||||
|
@ -111,11 +111,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn roundtrip_hmac() {
|
pub fn roundtrip_hmac() {
|
||||||
let now = Utc::now();
|
let now = OffsetDateTime::now_utc();
|
||||||
let header: Header<()> = Header::default();
|
let header: Header<()> = Header::default();
|
||||||
let payload = DefaultPayload {
|
let payload = DefaultPayload {
|
||||||
nbf: Some(now.timestamp().try_into().unwrap()),
|
nbf: Some(now.unix_timestamp().try_into().unwrap()),
|
||||||
exp: Some((now + Duration::minutes(5)).timestamp().try_into().unwrap()),
|
exp: Some((now + Duration::minutes(5)).unix_timestamp().try_into().unwrap()),
|
||||||
..DefaultPayload::default()
|
..DefaultPayload::default()
|
||||||
};
|
};
|
||||||
let token = Token::new(header, payload);
|
let token = Token::new(header, payload);
|
||||||
|
@ -129,7 +129,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn roundtrip_expired() -> Result<()> {
|
pub fn roundtrip_expired() -> Result<()> {
|
||||||
let now = Utc::now();
|
let now = OffsetDateTime::now_utc();
|
||||||
let token = create_for_range(now, now + Duration::minutes(-5))?;
|
let token = create_for_range(now, now + Duration::minutes(-5))?;
|
||||||
let key = b"secret";
|
let key = b"secret";
|
||||||
let raw = token.sign(key)?;
|
let raw = token.sign(key)?;
|
||||||
|
@ -142,7 +142,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn roundtrip_not_yet_valid() -> Result<()> {
|
pub fn roundtrip_not_yet_valid() -> Result<()> {
|
||||||
let now = Utc::now();
|
let now = OffsetDateTime::now_utc();
|
||||||
let token = create_for_range(now + Duration::minutes(5), now + Duration::minutes(10))?;
|
let token = create_for_range(now + Duration::minutes(5), now + Duration::minutes(10))?;
|
||||||
let key = b"secret";
|
let key = b"secret";
|
||||||
let raw = token.sign(key)?;
|
let raw = token.sign(key)?;
|
||||||
|
@ -175,11 +175,11 @@ mod tests {
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_for_range(nbf: DateTime<Utc>, exp: DateTime<Utc>) -> Result<Token> {
|
fn create_for_range(nbf: OffsetDateTime, exp: OffsetDateTime) -> Result<Token> {
|
||||||
let header: Header = Header::default();
|
let header: Header = Header::default();
|
||||||
let payload = Payload {
|
let payload = Payload {
|
||||||
nbf: Some(nbf.timestamp().try_into()?),
|
nbf: Some(nbf.unix_timestamp().try_into()?),
|
||||||
exp: Some(exp.timestamp().try_into()?),
|
exp: Some(exp.unix_timestamp().try_into()?),
|
||||||
..Payload::default()
|
..Payload::default()
|
||||||
};
|
};
|
||||||
Ok(Token::new(header, payload))
|
Ok(Token::new(header, payload))
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use anyhow::format_err;
|
use anyhow::format_err;
|
||||||
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
|
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
|
||||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use serde_json::value::Value;
|
use serde_json::value::Value;
|
||||||
|
use time::{OffsetDateTime};
|
||||||
|
|
||||||
/// A default claim set, including the standard, or registered, claims and the ability to specify
|
/// A default claim set, including the standard, or registered, claims and the ability to specify
|
||||||
/// your own as custom claims.
|
/// your own as custom claims.
|
||||||
|
@ -81,23 +81,19 @@ impl<T: Serialize + DeserializeOwned> Payload<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self) -> bool {
|
pub fn verify(&self) -> bool {
|
||||||
let now = Utc::now();
|
let now = OffsetDateTime::now_utc();
|
||||||
let nbf_verified = match self.nbf {
|
let nbf_verified = match self.nbf {
|
||||||
Some(nbf_sec) => {
|
Some(nbf_sec) => {
|
||||||
let nbf = DateTime::<Utc>::from_utc(
|
let nbf = OffsetDateTime::from_unix_timestamp(nbf_sec as i64)
|
||||||
NaiveDateTime::from_timestamp(nbf_sec as i64, 0),
|
.expect("nbf timestamp out of range");
|
||||||
Utc,
|
|
||||||
);
|
|
||||||
nbf < now
|
nbf < now
|
||||||
}
|
}
|
||||||
None => true,
|
None => true,
|
||||||
};
|
};
|
||||||
let exp_verified = match self.exp {
|
let exp_verified = match self.exp {
|
||||||
Some(exp_sec) => {
|
Some(exp_sec) => {
|
||||||
let exp = DateTime::<Utc>::from_utc(
|
let exp = OffsetDateTime::from_unix_timestamp(exp_sec as i64)
|
||||||
NaiveDateTime::from_timestamp(exp_sec as i64, 0),
|
.expect("exp timestamp out of range");
|
||||||
Utc,
|
|
||||||
);
|
|
||||||
now < exp
|
now < exp
|
||||||
}
|
}
|
||||||
None => true,
|
None => true,
|
||||||
|
@ -109,9 +105,9 @@ impl<T: Serialize + DeserializeOwned> Payload<T> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{DefaultPayload, Payload};
|
use super::{DefaultPayload, Payload};
|
||||||
use chrono::{prelude::*, Duration};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
struct CustomClaims {
|
struct CustomClaims {
|
||||||
|
@ -211,7 +207,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_with_nbf(offset: i64) -> DefaultPayload {
|
fn create_with_nbf(offset: i64) -> DefaultPayload {
|
||||||
let nbf = (Utc::now() - Duration::minutes(offset)).timestamp();
|
let nbf = (OffsetDateTime::now_utc() - Duration::minutes(offset)).unix_timestamp();
|
||||||
DefaultPayload {
|
DefaultPayload {
|
||||||
nbf: Some(nbf as u64),
|
nbf: Some(nbf as u64),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -219,7 +215,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_with_exp(offset: i64) -> DefaultPayload {
|
fn create_with_exp(offset: i64) -> DefaultPayload {
|
||||||
let exp = (Utc::now() + Duration::minutes(offset)).timestamp();
|
let exp = (OffsetDateTime::now_utc() + Duration::minutes(offset)).unix_timestamp();
|
||||||
DefaultPayload {
|
DefaultPayload {
|
||||||
exp: Some(exp as u64),
|
exp: Some(exp as u64),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -227,8 +223,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_with_nbf_exp(nbf_offset: i64, exp_offset: i64) -> DefaultPayload {
|
fn create_with_nbf_exp(nbf_offset: i64, exp_offset: i64) -> DefaultPayload {
|
||||||
let nbf = (Utc::now() - Duration::minutes(nbf_offset)).timestamp();
|
let nbf = (OffsetDateTime::now_utc() - Duration::minutes(nbf_offset)).unix_timestamp();
|
||||||
let exp = (Utc::now() + Duration::minutes(exp_offset)).timestamp();
|
let exp = (OffsetDateTime::now_utc() + Duration::minutes(exp_offset)).unix_timestamp();
|
||||||
DefaultPayload {
|
DefaultPayload {
|
||||||
nbf: Some(nbf as u64),
|
nbf: Some(nbf as u64),
|
||||||
exp: Some(exp as u64),
|
exp: Some(exp as u64),
|
||||||
|
|
Loading…
Reference in a new issue