@@ -14,13 +14,19 @@ use {
1414/// Maximum allowed value for a Unix timestamp.
1515pub const MAX_SECS : u64 = i64:: MAX as u64 ;
1616
17+ /// Sentinel value meaning "no expiry" per OpenSSH PROTOCOL.certkeys.
18+ /// When `valid_before` is set to this value, the certificate never expires.
19+ pub const FOREVER_SECS : u64 = u64:: MAX ;
20+
1721/// Unix timestamps as used in OpenSSH certificates.
1822#[ derive( Copy , Clone , Eq , PartialEq , PartialOrd , Ord ) ]
1923pub ( super ) struct UnixTime {
20- /// Number of seconds since the Unix epoch
24+ /// Number of seconds since the Unix epoch.
25+ /// `u64::MAX` (FOREVER_SECS) is preserved as-is for round-trip encoding.
2126 secs : u64 ,
2227
23- /// System time corresponding to this Unix timestamp
28+ /// System time corresponding to this Unix timestamp.
29+ /// For FOREVER_SECS, this is capped at MAX_SECS to keep a valid SystemTime.
2430 #[ cfg( feature = "std" ) ]
2531 time : SystemTime ,
2632}
@@ -29,10 +35,11 @@ impl UnixTime {
2935 /// Create a new Unix timestamp.
3036 ///
3137 /// `secs` is the number of seconds since the Unix epoch and must be less
32- /// than or equal to `i64::MAX`.
38+ /// than or equal to `i64::MAX`, or `u64::MAX` (the OpenSSH "no expiry"
39+ /// sentinel defined in PROTOCOL.certkeys).
3340 #[ cfg( not( feature = "std" ) ) ]
3441 pub fn new ( secs : u64 ) -> Result < Self > {
35- if secs <= MAX_SECS {
42+ if secs == FOREVER_SECS || secs <= MAX_SECS {
3643 Ok ( Self { secs } )
3744 } else {
3845 Err ( Error :: Time )
@@ -43,14 +50,22 @@ impl UnixTime {
4350 ///
4451 /// This version requires `std` and ensures there's a valid `SystemTime`
4552 /// representation with an infallible conversion (which also improves the
46- /// `Debug` output)
53+ /// `Debug` output).
54+ ///
55+ /// `u64::MAX` is the OpenSSH "no expiry" sentinel (PROTOCOL.certkeys) and
56+ /// is accepted; its `SystemTime` representation is capped at `MAX_SECS`.
4757 #[ cfg( feature = "std" ) ]
4858 pub fn new ( secs : u64 ) -> Result < Self > {
49- if secs > MAX_SECS {
59+ // u64::MAX is OpenSSH's sentinel for "certificate never expires".
60+ // Cap the SystemTime representation at MAX_SECS so it remains valid,
61+ // but preserve the original secs value for encoding round-trips.
62+ let time_secs = if secs == FOREVER_SECS { MAX_SECS } else { secs } ;
63+
64+ if time_secs > MAX_SECS {
5065 return Err ( Error :: Time ) ;
5166 }
5267
53- match UNIX_EPOCH . checked_add ( Duration :: from_secs ( secs ) ) {
68+ match UNIX_EPOCH . checked_add ( Duration :: from_secs ( time_secs ) ) {
5469 Some ( time) => Ok ( Self { secs, time } ) ,
5570 None => Err ( Error :: Time ) ,
5671 }
@@ -120,7 +135,7 @@ impl fmt::Debug for UnixTime {
120135
121136#[ cfg( test) ]
122137mod tests {
123- use super :: { MAX_SECS , UnixTime } ;
138+ use super :: { FOREVER_SECS , MAX_SECS , UnixTime } ;
124139 use crate :: Error ;
125140
126141 #[ test]
@@ -132,4 +147,23 @@ mod tests {
132147 fn new_over_max_secs_returns_error ( ) {
133148 assert_eq ! ( UnixTime :: new( MAX_SECS + 1 ) , Err ( Error :: Time ) ) ;
134149 }
150+
151+ #[ test]
152+ fn new_with_forever_secs_is_ok ( ) {
153+ // u64::MAX is the OpenSSH "no expiry" sentinel and must be accepted
154+ assert ! ( UnixTime :: new( FOREVER_SECS ) . is_ok( ) ) ;
155+ }
156+
157+ #[ test]
158+ fn forever_secs_preserves_raw_value ( ) {
159+ let t = UnixTime :: new ( FOREVER_SECS ) . unwrap ( ) ;
160+ assert_eq ! ( u64 :: from( t) , FOREVER_SECS ) ;
161+ }
162+
163+ #[ test]
164+ fn forever_secs_greater_than_any_normal_timestamp ( ) {
165+ let forever = UnixTime :: new ( FOREVER_SECS ) . unwrap ( ) ;
166+ let now = UnixTime :: new ( MAX_SECS ) . unwrap ( ) ;
167+ assert ! ( forever > now) ;
168+ }
135169}
0 commit comments