Change Time to wrap an OpenPGP timestamp.

- `cli::types::Time` wraps a `chrono::DateTime`, which has more
    resolution, and a larger range than an OpenPGP timestamp.

  - Change it to hold an `openpgp::types::Timestamp` instead.

  - This will catch out of range errors at parsing time rather than
    time of use, and prevents us forgetting to do the conversion.

  - Fixes #153.
This commit is contained in:
Neal H. Walfield 2023-11-22 13:26:57 +01:00
parent 186de775ae
commit f13b7d1320
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
2 changed files with 52 additions and 21 deletions

View File

@ -12,7 +12,6 @@ use std::sync::atomic::Ordering;
use std::time::Duration;
use std::time::SystemTime;
use anyhow::anyhow;
use anyhow::Context;
use anyhow::Result;
@ -23,12 +22,14 @@ use chrono::{offset::Utc, DateTime};
/// Common types for arguments of sq.
use clap::ValueEnum;
use sequoia_openpgp as openpgp;
use openpgp::armor;
use openpgp::fmt::hex;
use openpgp::serialize::stream::Armorer;
use openpgp::serialize::stream::Message;
use openpgp::types::SymmetricAlgorithm;
use sequoia_openpgp as openpgp;
use openpgp::types::Timestamp;
use terminal_size::terminal_size;
use crate::cli::SECONDS_IN_DAY;
@ -700,9 +701,14 @@ impl<'a> std::fmt::Display for SessionKeyDisplay<'a> {
}
}
/// A thin wrapper around `openpgp::types::Timestamp`.
///
/// Recall: an OpenPGP timestamp has a whole second resolution, and
/// uses a 32-bit quantity to represent the number of seconds since
/// the UNIX epoch.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Time {
pub time: DateTime<Utc>,
pub time: Timestamp,
}
impl std::str::FromStr for Time {
@ -712,16 +718,45 @@ impl std::str::FromStr for Time {
let time =
Time::parse_iso8601(s, chrono::NaiveTime::from_hms_opt(0, 0, 0)
.unwrap())?;
Ok(Time { time })
Ok(Time::try_from(SystemTime::from(time))?)
}
}
impl From<Time> for SystemTime {
fn from(t: Time) -> SystemTime {
t.time.into()
}
}
impl TryFrom<SystemTime> for Time {
type Error = anyhow::Error;
fn try_from(time: SystemTime) -> Result<Self> {
Ok(Self {
time: Timestamp::try_from(time)?,
})
}
}
impl TryFrom<DateTime<Utc>> for Time {
type Error = anyhow::Error;
fn try_from(time: DateTime<Utc>) -> Result<Self> {
Self::try_from(SystemTime::try_from(time)?)
}
}
impl Time {
/// Returns the current time.
pub fn now() -> Self {
Self {
time: Timestamp::now()
}
}
/// Returns the time as openpgp::types::Timestamp.
pub fn timestamp(&self) -> Result<openpgp::types::Timestamp> {
let seconds = u32::try_from(self.time.naive_utc().timestamp())
.map_err(|_| anyhow!("Time {} not representable", self.time))?;
Ok(seconds.try_into()?)
pub fn timestamp(&self) -> openpgp::types::Timestamp {
self.time.clone()
}
/// Parses the given string depicting a ISO 8601 timestamp.
@ -838,12 +873,10 @@ mod test {
);
let expiry = Expiry::Timestamp(
Time{
time: DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(2, 0).unwrap(),
Utc,
)}
);
Time::try_from(DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(2, 0).unwrap(),
Utc
)).expect("valid"));
assert_eq!(
expiry.as_duration(reference).unwrap(),
Some(Duration::new(1, 0)),
@ -866,12 +899,10 @@ mod test {
Utc,
);
let expiry = Expiry::Timestamp(
Time{
time: DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(1, 0).unwrap(),
Utc,
)}
);
Time::try_from(DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(1, 0).unwrap(),
Utc
)).expect("valid"));
assert!(expiry.as_duration(reference).is_err());
}
}

View File

@ -149,7 +149,7 @@ pub fn adopt(config: Config, command: cli::key::AdoptCommand) -> Result<()>
// Set key expiration.
if let Some(e) = &command.expire {
builder = builder.set_key_expiration_time(&key, e.timestamp()?)?;
builder = builder.set_key_expiration_time(&key, e.timestamp())?;
}
// If there is a valid backsig, recreate it.