diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2019-12-16 14:38:53 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-12-16 14:50:51 +0100 |
commit | 4b7457928f2d57bdb881a70c762db7d4359d541f (patch) | |
tree | 930b88392029f7a719e751d44016fc0678e9529d | |
parent | 94babb06f0210666fcb70415f37de5566d3a7834 (diff) |
sqv: Support more variants of ISO 6801 timestamps.
- Fixes #403.
-rw-r--r-- | sqv/src/sqv-usage.rs | 15 | ||||
-rw-r--r-- | sqv/src/sqv.rs | 78 | ||||
-rw-r--r-- | sqv/src/sqv_cli.rs | 19 |
3 files changed, 97 insertions, 15 deletions
diff --git a/sqv/src/sqv-usage.rs b/sqv/src/sqv-usage.rs index 534fc409..83a0828e 100644 --- a/sqv/src/sqv-usage.rs +++ b/sqv/src/sqv-usage.rs @@ -14,14 +14,21 @@ //! -V, --version Prints version information //! //! OPTIONS: -//! --keyring <FILE>... A keyring. Can be given multiple times. -//! --not-after <YYYY-MM-DD> Consider signatures created after YYYY-MM-DD as invalid. Default: now -//! --not-before <YYYY-MM-DD> Consider signatures created before YYYY-MM-DD as invalid. Default: no constraint -//! -n, --signatures <N> The number of valid signatures to return success. Default: 1 +//! --keyring <FILE>... A keyring. Can be given multiple times. +//! --not-after <TIMESTAMP> Consider signatures created after TIMESTAMP as invalid. If a date is given, +//! 23:59:59 is used for the time. +//! [default: now] +//! --not-before <TIMESTAMP> Consider signatures created before TIMESTAMP as invalid. If a date is given, +//! 00:00:00 is used for the time. +//! [default: no constraint] +//! -n, --signatures <N> The number of valid signatures to return success. Default: 1 //! //! ARGS: //! <SIG-FILE> File containing the detached signature. //! <FILE> File to verify. +//! +//! TIMESTAMPs must be given in ISO 9801 format (e.g. '2017-03-04T13:25:35Z', '2017-03-04T13:25', '20170304T1325+0830', +//! '2017-03-04', '2017031', ...). If no timezone is specified, UTC is assumed. //! ``` include!("sqv.rs"); diff --git a/sqv/src/sqv.rs b/sqv/src/sqv.rs index 373bbd38..5543efb0 100644 --- a/sqv/src/sqv.rs +++ b/sqv/src/sqv.rs @@ -3,6 +3,7 @@ /// See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=872271 for /// the motivation. +use chrono::{DateTime, offset::Utc}; extern crate clap; extern crate failure; use failure::ResultExt; @@ -50,11 +51,9 @@ fn real_main() -> Result<(), failure::Error> { exit(2); } - use chrono::{DateTime, offset::Utc, NaiveDate}; let not_before: Option<std::time::SystemTime> = if let Some(t) = matches.value_of("not-before") { - Some(NaiveDate::parse_from_str(t, "%Y-%m-%d") - .map(|n| DateTime::<Utc>::from_utc(n.and_hms(0, 0, 0), Utc)) + Some(parse_iso8601(t, chrono::NaiveTime::from_hms(0, 0, 0)) .context(format!("Bad value passed to --not-before: {:?}", t))? .into()) } else { @@ -62,8 +61,7 @@ fn real_main() -> Result<(), failure::Error> { }; let not_after: std::time::SystemTime = if let Some(t) = matches.value_of("not-after") { - Some(NaiveDate::parse_from_str(t, "%Y-%m-%d") - .map(|n| DateTime::<Utc>::from_utc(n.and_hms(23, 59, 59), Utc)) + Some(parse_iso8601(t, chrono::NaiveTime::from_hms(23, 59, 59)) .context(format!("Bad value passed to --not-after: {:?}", t))? .into()) } else { @@ -376,3 +374,73 @@ fn main() { exit(2); } } + +/// Parses the given string depicting a ISO 8601 timestamp. +fn parse_iso8601(s: &str, pad_date_with: chrono::NaiveTime) + -> failure::Fallible<DateTime<Utc>> +{ + for f in &[ + "%Y-%m-%dT%H:%M:%S%#z", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%dT%H:%M%#z", + "%Y-%m-%dT%H:%M", + "%Y-%m-%dT%H%#z", + "%Y-%m-%dT%H", + "%Y%m%dT%H%M%S%#z", + "%Y%m%dT%H%M%S", + "%Y%m%dT%H%M%#z", + "%Y%m%dT%H%M", + "%Y%m%dT%H%#z", + "%Y%m%dT%H", + ] { + if f.ends_with("%#z") { + if let Ok(d) = DateTime::parse_from_str(s, *f) { + return Ok(d.into()); + } + } else { + if let Ok(d) = chrono::NaiveDateTime::parse_from_str(s, *f) { + return Ok(DateTime::from_utc(d, Utc)); + } + } + } + for f in &[ + "%Y-%m-%d", + "%Y-%m", + "%Y-%j", + "%Y%m%d", + "%Y%m", + "%Y%j", + "%Y", + ] { + if let Ok(d) = chrono::NaiveDate::parse_from_str(s, *f) { + return Ok(DateTime::from_utc(d.and_time(pad_date_with), Utc)); + } + } + Err(failure::format_err!("Malformed ISO8601 timestamp: {}", s)) +} + +#[test] +fn test_parse_iso8601() { + let z = chrono::NaiveTime::from_hms(0, 0, 0); + parse_iso8601("2017-03-04T13:25:35Z", z).unwrap(); + parse_iso8601("2017-03-04T13:25:35+08:30", z).unwrap(); + parse_iso8601("2017-03-04T13:25:35", z).unwrap(); + parse_iso8601("2017-03-04T13:25Z", z).unwrap(); + parse_iso8601("2017-03-04T13:25", z).unwrap(); + // parse_iso8601("2017-03-04T13Z", z).unwrap(); // XXX: chrono doesn't like + // parse_iso8601("2017-03-04T13", z).unwrap(); // ditto + parse_iso8601("2017-03-04", z).unwrap(); + // parse_iso8601("2017-03", z).unwrap(); // ditto + parse_iso8601("2017-031", z).unwrap(); + parse_iso8601("20170304T132535Z", z).unwrap(); + parse_iso8601("20170304T132535+0830", z).unwrap(); + parse_iso8601("20170304T132535", z).unwrap(); + parse_iso8601("20170304T1325Z", z).unwrap(); + parse_iso8601("20170304T1325", z).unwrap(); + // parse_iso8601("20170304T13Z", z).unwrap(); // ditto + // parse_iso8601("20170304T13", z).unwrap(); // ditto + parse_iso8601("20170304", z).unwrap(); + // parse_iso8601("201703", z).unwrap(); // ditto + parse_iso8601("2017031", z).unwrap(); + // parse_iso8601("2017", z).unwrap(); // ditto +} diff --git a/sqv/src/sqv_cli.rs b/sqv/src/sqv_cli.rs index f0826948..7efde976 100644 --- a/sqv/src/sqv_cli.rs +++ b/sqv/src/sqv_cli.rs @@ -24,14 +24,16 @@ pub fn build() -> App<'static, 'static> { .long("signatures") .short("n") .takes_value(true)) - .arg(Arg::with_name("not-before").value_name("YYYY-MM-DD") - .help("Consider signatures created before YYYY-MM-DD as invalid. \ - Default: no constraint") + .arg(Arg::with_name("not-before").value_name("TIMESTAMP") + .help("Consider signatures created before TIMESTAMP as invalid. \ + If a date is given, 00:00:00 is used for the time. \ + \n[default: no constraint]") .long("not-before") .takes_value(true)) - .arg(Arg::with_name("not-after").value_name("YYYY-MM-DD") - .help("Consider signatures created after YYYY-MM-DD as invalid. \ - Default: now") + .arg(Arg::with_name("not-after").value_name("TIMESTAMP") + .help("Consider signatures created after TIMESTAMP as invalid. \ + If a date is given, 23:59:59 is used for the time. \ + \n[default: now]") .long("not-after") .takes_value(true)) .arg(Arg::with_name("sig-file").value_name("SIG-FILE") @@ -43,4 +45,9 @@ pub fn build() -> App<'static, 'static> { .arg(Arg::with_name("trace") .help("Trace execution.") .long("trace")) + .after_help( + "TIMESTAMPs must be given in ISO 9801 format \ + (e.g. '2017-03-04T13:25:35Z', '2017-03-04T13:25', \ + '20170304T1325+0830', '2017-03-04', '2017031', ...). \ + If no timezone is specified, UTC is assumed.") } |