summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2019-12-16 14:38:53 +0100
committerJustus Winter <justus@sequoia-pgp.org>2019-12-16 14:50:51 +0100
commit4b7457928f2d57bdb881a70c762db7d4359d541f (patch)
tree930b88392029f7a719e751d44016fc0678e9529d
parent94babb06f0210666fcb70415f37de5566d3a7834 (diff)
sqv: Support more variants of ISO 6801 timestamps.
- Fixes #403.
-rw-r--r--sqv/src/sqv-usage.rs15
-rw-r--r--sqv/src/sqv.rs78
-rw-r--r--sqv/src/sqv_cli.rs19
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.")
}