Skip to content

Commit

Permalink
ssh-key: use u64 type for certificate timestamps
Browse files Browse the repository at this point in the history
Closes #174

Previously certificates only supported the `i64` range to allow for
infallible conversions to/from `SystemTime`.

Unfortunately OpenSSH defaults to using `u64::MAX` as the `valid_before`
time in order to represent certificate that's valid "forever". The
previous restriction meant that `ssh-key` was incapible of parsing such
certificates.

This commit switches to using a raw `u64` everywhere, and changing
conversions to `SystemTime` to return an `Option<SystemTime>` which is
`None` if the `u64` value overflows an `i64`.
  • Loading branch information
tarcieri committed Mar 29, 2024
1 parent bce9963 commit 623425e
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 26 deletions.
2 changes: 1 addition & 1 deletion ssh-key/src/authorized_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const COMMENT_DELIMITER: char = '#';
/// the key).
pub struct AuthorizedKeys<'a> {
/// Lines of the file being iterated over
lines: core::str::Lines<'a>,
lines: str::Lines<'a>,
}

impl<'a> AuthorizedKeys<'a> {
Expand Down
38 changes: 22 additions & 16 deletions ssh-key/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ pub struct Certificate {
valid_principals: Vec<String>,

/// Valid after.
valid_after: UnixTime,
valid_after: u64,

/// Valid before.
valid_before: UnixTime,
valid_before: u64,

/// Critical options.
critical_options: OptionsMap,
Expand Down Expand Up @@ -281,26 +281,34 @@ impl Certificate {
&self.valid_principals
}

/// Valid after (Unix time).
/// Valid after (Unix time), i.e. certificate issuance time.
pub fn valid_after(&self) -> u64 {
self.valid_after.into()
self.valid_after
}

/// Valid before (Unix time).
/// Valid before (Unix time), i.e. certificate expiration time.
pub fn valid_before(&self) -> u64 {
self.valid_before.into()
self.valid_before
}

/// Valid after (system time).
/// Valid after (system time), i.e. certificate issuance time.
///
/// # Returns
/// - `Some` if the `u64` value is a valid `SystemTime`
/// - `None` if it is not (i.e. overflows `i64`)
#[cfg(feature = "std")]
pub fn valid_after_time(&self) -> SystemTime {
self.valid_after.into()
pub fn valid_after_time(&self) -> Option<SystemTime> {
UnixTime::try_from(self.valid_after).ok().map(Into::into)
}

/// Valid before (system time).
/// Valid before (system time), i.e. certificate expiration time.
///
/// # Returns
/// - `Some` if the `u64` value is a valid `SystemTime`
/// - `None` if it is not (i.e. overflows `i64`, effectively never-expiring)
#[cfg(feature = "std")]
pub fn valid_before_time(&self) -> SystemTime {
self.valid_before.into()
pub fn valid_before_time(&self) -> Option<SystemTime> {
UnixTime::try_from(self.valid_before).ok().map(Into::into)
}

/// The critical options section of the certificate specifies zero or more
Expand Down Expand Up @@ -391,8 +399,6 @@ impl Certificate {
return Err(Error::CertificateValidation);
}

let unix_timestamp = UnixTime::new(unix_timestamp)?;

// From PROTOCOL.certkeys:
//
// "valid after" and "valid before" specify a validity period for the
Expand Down Expand Up @@ -462,8 +468,8 @@ impl Decode for Certificate {
cert_type: CertType::decode(reader)?,
key_id: String::decode(reader)?,
valid_principals: Vec::decode(reader)?,
valid_after: UnixTime::decode(reader)?,
valid_before: UnixTime::decode(reader)?,
valid_after: u64::decode(reader)?,
valid_before: u64::decode(reader)?,
critical_options: OptionsMap::decode(reader)?,
extensions: OptionsMap::decode(reader)?,
reserved: Vec::decode(reader)?,
Expand Down
10 changes: 2 additions & 8 deletions ssh-key/src/certificate/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ pub struct Builder {
cert_type: Option<CertType>,
key_id: Option<String>,
valid_principals: Option<Vec<String>>,
valid_after: UnixTime,
valid_before: UnixTime,
valid_after: u64,
valid_before: u64,
critical_options: OptionsMap,
extensions: OptionsMap,
comment: Option<String>,
Expand All @@ -105,12 +105,6 @@ impl Builder {
valid_after: u64,
valid_before: u64,
) -> Result<Self> {
let valid_after =
UnixTime::new(valid_after).map_err(|_| Field::ValidAfter.invalid_error())?;

let valid_before =
UnixTime::new(valid_before).map_err(|_| Field::ValidBefore.invalid_error())?;

if valid_before < valid_after {
return Err(Field::ValidBefore.invalid_error());
}
Expand Down
2 changes: 1 addition & 1 deletion ssh-key/src/known_hosts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const MAGIC_HASH_PREFIX: &str = "|1|";
/// the key).
pub struct KnownHosts<'a> {
/// Lines of the file being iterated over
lines: core::str::Lines<'a>,
lines: str::Lines<'a>,
}

impl<'a> KnownHosts<'a> {
Expand Down

0 comments on commit 623425e

Please sign in to comment.