@@ -598,8 +598,12 @@ fn build_validity(
598598 max_lifetime_secs : u64 ,
599599) -> std:: result:: Result < Validity , String > {
600600 let now = Duration :: from_millis ( now_millis) ;
601+ // notAfter is inclusive, so the endpoint span is one second less than the lifetime.
602+ let validity_span_secs = max_lifetime_secs
603+ . checked_sub ( 1 )
604+ . ok_or_else ( || "maximum certificate lifetime must be at least one second" . to_string ( ) ) ?;
601605 let not_before = UtcTime :: from_unix_duration ( now) . map_err ( |e| e. to_string ( ) ) ?;
602- let not_after = UtcTime :: from_unix_duration ( now + Duration :: from_secs ( max_lifetime_secs ) )
606+ let not_after = UtcTime :: from_unix_duration ( now + Duration :: from_secs ( validity_span_secs ) )
603607 . map_err ( |e| e. to_string ( ) ) ?;
604608
605609 Ok ( Validity :: new (
@@ -704,20 +708,21 @@ mod tests {
704708 }
705709
706710 #[ test]
707- fn test_build_validity ( ) {
708- let now_ms = 1_700_000_000_000_u64 ; // Nov 2023
709- let lifetime_secs = 86400_u64 ; // 1 day
711+ fn test_build_validity_respects_inclusive_lifetime ( ) {
712+ let now_ms = 1_700_000_000_500_u64 ;
713+ let lifetime_secs = 7 * 24 * 60 * 60 ;
710714
711715 let validity = build_validity ( now_ms, lifetime_secs) . unwrap ( ) ;
716+ let not_before = validity. not_before . to_unix_duration ( ) . as_secs ( ) ;
717+ let not_after = validity. not_after . to_unix_duration ( ) . as_secs ( ) ;
712718
713- assert_eq ! (
714- validity. not_before. to_unix_duration( ) . as_secs( ) ,
715- now_ms / 1000
716- ) ;
717- assert_eq ! (
718- validity. not_after. to_unix_duration( ) . as_secs( ) ,
719- now_ms / 1000 + lifetime_secs
720- ) ;
719+ assert_eq ! ( not_before, now_ms / 1000 ) ;
720+ assert_eq ! ( not_after - not_before + 1 , lifetime_secs) ;
721+ }
722+
723+ #[ test]
724+ fn test_build_validity_rejects_zero_lifetime ( ) {
725+ assert ! ( build_validity( 1_700_000_000_000 , 0 ) . is_err( ) ) ;
721726 }
722727
723728 #[ test]
0 commit comments