2024-05-30 11:07:53 +03:00
use std ::time ::Duration ;
2024-10-08 12:03:15 +03:00
use sequoia_openpgp as openpgp ;
2024-05-30 11:07:53 +03:00
use openpgp ::Cert ;
2024-10-08 12:22:01 +03:00
use openpgp ::cert ::amalgamation ::ValidAmalgamation ;
2024-10-08 12:03:15 +03:00
use openpgp ::KeyHandle ;
2024-05-30 11:07:53 +03:00
use openpgp ::Result ;
2024-10-08 12:03:15 +03:00
use openpgp ::parse ::Parse ;
2024-10-08 12:22:01 +03:00
use openpgp ::types ::RevocationStatus ;
2024-05-30 11:07:53 +03:00
2024-08-15 14:38:43 +03:00
use super ::common ::STANDARD_POLICY ;
use super ::common ::Sq ;
use super ::common ::power_set ;
use super ::common ::time_as_string ;
2024-05-30 11:07:53 +03:00
#[ test ]
fn sq_key_subkey_expire ( ) -> Result < ( ) > {
2024-07-20 23:05:03 +03:00
let mut sq = Sq ::new ( ) ;
let ( cert , cert_path , _rev_path )
= sq . key_generate ( & [ ] , & [ " alice <alice@example.org> " ] ) ;
2024-05-31 14:22:27 +03:00
let fpr = cert . fingerprint ( ) . to_string ( ) ;
2024-05-30 11:07:53 +03:00
2024-07-20 23:05:03 +03:00
let updated_path = sq . scratch_file ( " updated.pgp " ) ;
let updated2_path = sq . scratch_file ( " updated2.pgp " ) ;
2024-05-30 11:07:53 +03:00
let keys = cert . keys ( ) . map ( | k | k . fingerprint ( ) ) . collect ::< Vec < _ > > ( ) ;
2024-07-20 23:05:03 +03:00
// Two days go by.
sq . tick ( 2 * 24 * 60 * 60 ) ;
2024-05-30 11:07:53 +03:00
for expiring in power_set ( & keys ) {
2024-05-31 14:22:27 +03:00
for keystore in [ false , true ] {
let cert_expiring = expiring . contains ( & cert . fingerprint ( ) ) ;
for ( i , fpr ) in keys . iter ( ) . enumerate ( ) {
eprintln! ( " {} . {} : {} expiring " ,
i , fpr ,
if expiring . contains ( & fpr ) {
" "
} else {
" NOT "
} ) ;
}
if keystore {
sq . key_import ( & cert_path ) ;
}
// Change the key to expire in one day.
let mut cmd = sq . command ( ) ;
cmd . args ( [
" key " , " subkey " , " expire " , " 1d " ,
] ) ;
if keystore {
cmd . args ( [ " --cert " , & fpr ] ) ;
} else {
2024-07-20 23:05:03 +03:00
cmd
2024-10-02 19:42:31 +03:00
. arg ( " --overwrite " )
2024-07-20 23:05:03 +03:00
. arg ( " --cert-file " ) . arg ( & cert_path )
. arg ( " --output " ) . arg ( & updated_path ) ;
2024-05-31 14:22:27 +03:00
}
for k in expiring . iter ( ) {
cmd . args ( [ " --key " , & k . to_string ( ) ] ) ;
}
sq . run ( cmd , true ) ;
eprintln! ( " Updated keys at {} to expire in one day: \n {} " ,
sq . now_as_string ( ) ,
2024-07-20 23:05:03 +03:00
sq . inspect ( & updated_path ) ) ;
2024-05-31 14:22:27 +03:00
let updated = if keystore {
eprintln! ( " Updated certificate to expire in one day: \n {} " ,
sq . inspect ( cert . key_handle ( ) ) ) ;
sq . cert_export ( cert . key_handle ( ) )
} else {
eprintln! ( " Updated certificate to expire in one day: \n {} " ,
2024-07-20 23:05:03 +03:00
sq . inspect ( & updated_path ) ) ;
2024-05-31 14:22:27 +03:00
Cert ::from_file ( & updated_path ) . expect ( " valid cert " )
} ;
// It should be alive now.
let vc = updated . with_policy ( STANDARD_POLICY , sq . now ( ) ) . expect ( " valid " ) ;
for k in vc . keys ( ) {
assert! ( k . alive ( ) . is_ok ( ) ) ;
}
// It should be alive in 1 day minus 1 second.
let t = sq . now ( ) + Duration ::new ( 24 * 60 * 60 - 1 , 0 ) ;
eprintln! ( " Checking expiration status at {} " , time_as_string ( t . into ( ) ) ) ;
let vc = updated . with_policy ( STANDARD_POLICY , t ) . expect ( " valid " ) ;
for k in vc . keys ( ) {
assert! ( k . alive ( ) . is_ok ( ) ) ;
}
// But in exactly one day, it should be expired.
let t = sq . now ( ) + Duration ::new ( 24 * 60 * 60 , 0 ) ;
eprintln! ( " Checking expiration status at {} " , time_as_string ( t . into ( ) ) ) ;
let vc = updated . with_policy ( STANDARD_POLICY , t ) . expect ( " valid " ) ;
for k in vc . keys ( ) {
assert_eq! (
cert_expiring | | expiring . contains ( & k . fingerprint ( ) ) ,
k . alive ( ) . is_err ( ) ,
" {} is {}alive " ,
k . fingerprint ( ) ,
if k . alive ( ) . is_ok ( ) { " " } else { " NOT " } ) ;
}
// 12 hours go by. Clear the expiration time.
sq . tick ( 12 * 60 * 60 ) ;
let mut cmd = sq . command ( ) ;
cmd . args ( [ " key " , " subkey " , " expire " , " never " ] ) ;
if keystore {
cmd . args ( [ " --cert " , & fpr ] ) ;
} else {
2024-07-20 23:05:03 +03:00
cmd
2024-10-02 19:42:31 +03:00
. arg ( " --overwrite " )
2024-07-20 23:05:03 +03:00
. arg ( " --cert-file " ) . arg ( & updated_path )
. arg ( " --output " ) . arg ( & updated2_path ) ;
2024-05-31 14:22:27 +03:00
}
for k in expiring . iter ( ) {
cmd . args ( [ " --key " , & k . to_string ( ) ] ) ;
}
sq . run ( cmd , true ) ;
let updated = if keystore {
eprintln! ( " Updated certificate at {} to never expire: \n {} " ,
sq . now_as_string ( ) ,
sq . inspect ( cert . key_handle ( ) ) ) ;
sq . cert_export ( cert . key_handle ( ) )
} else {
eprintln! ( " Updated certificate at {} to never expire: \n {} " ,
sq . now_as_string ( ) ,
2024-07-20 23:05:03 +03:00
sq . inspect ( & updated2_path ) ) ;
2024-05-31 14:22:27 +03:00
Cert ::from_file ( & updated2_path ) . expect ( " valid cert " )
} ;
// It should be alive now.
let vc = updated . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid " ) ;
for k in vc . keys ( ) {
assert! ( k . alive ( ) . is_ok ( ) ) ;
}
// It should be alive in 1 day minus 1 second.
let t = sq . now ( ) + Duration ::new ( 24 * 60 * 60 - 1 , 0 ) ;
eprintln! ( " Checking expiration status at {} " , time_as_string ( t . into ( ) ) ) ;
let vc = updated . with_policy ( STANDARD_POLICY , t ) . expect ( " valid " ) ;
for k in vc . keys ( ) {
2024-09-16 16:34:48 +03:00
eprintln! ( " {} expires at {} " ,
k . fingerprint ( ) ,
if let Some ( t ) = k . key_expiration_time ( ) {
time_as_string ( t . into ( ) )
} else {
" never " . to_string ( )
} ) ;
if let Err ( err ) = k . alive ( ) {
panic! ( " {} should be alive, but it's not: {} " ,
k . fingerprint ( ) , err ) ;
}
2024-05-31 14:22:27 +03:00
}
// And in exactly one day...
let t = sq . now ( ) + Duration ::new ( 24 * 60 * 60 , 0 ) ;
eprintln! ( " Checking expiration status at {} " , time_as_string ( t . into ( ) ) ) ;
let vc = updated . with_policy ( STANDARD_POLICY , t ) . expect ( " valid " ) ;
for k in vc . keys ( ) {
2024-09-16 16:34:48 +03:00
eprintln! ( " {} expires at {} " ,
k . fingerprint ( ) ,
if let Some ( t ) = k . key_expiration_time ( ) {
time_as_string ( t . into ( ) )
} else {
" never " . to_string ( )
} ) ;
if let Err ( err ) = k . alive ( ) {
panic! ( " {} should be alive, but it's not: {} " ,
k . fingerprint ( ) , err ) ;
}
2024-05-31 14:22:27 +03:00
}
2024-05-30 11:07:53 +03:00
}
}
Ok ( ( ) )
}
2024-10-08 12:03:15 +03:00
#[ test ]
fn unbound_subkey ( ) {
// Make sure we can't extend the expiration time of an unbound
// subkey.
let sq = Sq ::new ( ) ;
let cert_path = sq . test_data ( )
. join ( " keys " )
. join ( " unbound-subkey.pgp " ) ;
let cert = Cert ::from_file ( & cert_path ) . expect ( " can read " ) ;
let vc = cert . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
// One subkey should be considered invalid.
assert! ( vc . keys ( ) . count ( ) < cert . keys ( ) . count ( ) ) ;
let unbound = " E992BF8BA7A27BB4FBB71D973857E47B14874045 "
. parse ::< KeyHandle > ( ) . expect ( " valid " ) ;
// Set it to expire in a day.
let updated_path = sq . scratch_file ( " updated " ) ;
let updated = sq . key_subkey_expire ( cert_path ,
& [ unbound ] ,
" 1d " ,
None ,
updated_path . as_path ( ) ,
false ) ;
assert! ( updated . is_err ( ) ) ;
}
2024-10-08 12:22:01 +03:00
#[ test ]
fn soft_revoked_subkey ( ) {
// Make sure we can't extend the expiration time of a soft revoked
// subkey.
let sq = Sq ::new ( ) ;
let cert_path = sq . test_data ( )
. join ( " keys " )
. join ( " soft-revoked-subkey.pgp " ) ;
let cert = Cert ::from_file ( & cert_path ) . expect ( " can read " ) ;
let vc = cert . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
// Make sure the revoked key is there and is really revoked.
let mut revoked = None ;
for k in vc . keys ( ) . subkeys ( ) {
if let RevocationStatus ::Revoked ( _ ) = k . revocation_status ( ) {
assert! ( revoked . is_none ( ) ,
" Only expected a single revoked subkey " ) ;
revoked = Some ( k . fingerprint ( ) ) ;
}
}
let revoked = if let Some ( revoked ) = revoked {
revoked
} else {
panic! ( " Expected a revoked subkey, but didn't fine one " ) ;
} ;
// Set it to expire in a day.
let updated_path = sq . scratch_file ( " updated " ) ;
let updated = sq . key_subkey_expire ( cert_path ,
& [ revoked . clone ( ) . into ( ) ] ,
" 1d " ,
None ,
updated_path . as_path ( ) ,
true )
. expect ( " sq key expire should succeed " ) ;
let vc = updated . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
let mut good = false ;
for k in vc . keys ( ) {
if k . fingerprint ( ) = = revoked {
if let RevocationStatus ::Revoked ( _ ) = k . revocation_status ( ) {
panic! ( " {} shouldn't be revoked, but is. " ,
revoked ) ;
}
let expiration = k . key_expiration_time ( ) ;
assert_eq! ( expiration ,
Some ( sq . now ( )
+ std ::time ::Duration ::new ( 24 * 60 * 60 , 0 ) ) ) ;
good = true ;
break ;
}
}
assert! ( good ) ;
}
2024-10-08 12:38:53 +03:00
#[ test ]
fn hard_revoked_subkey ( ) {
// Make sure we can't extend the expiration time of a hard revoked
// subkey.
let sq = Sq ::new ( ) ;
let cert_path = sq . test_data ( )
. join ( " keys " )
. join ( " hard-revoked-subkey.pgp " ) ;
let cert = Cert ::from_file ( & cert_path ) . expect ( " can read " ) ;
let vc = cert . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
// Make sure the revoked key is there and is really revoked.
let mut revoked = None ;
for k in vc . keys ( ) . subkeys ( ) {
if let RevocationStatus ::Revoked ( _ ) = k . revocation_status ( ) {
assert! ( revoked . is_none ( ) ,
" Only expected a single revoked subkey " ) ;
revoked = Some ( k . fingerprint ( ) ) ;
}
}
let revoked = if let Some ( revoked ) = revoked {
revoked
} else {
panic! ( " Expected a revoked subkey, but didn't fine one " ) ;
} ;
// Set it to expire in a day.
let updated_path = sq . scratch_file ( " updated " ) ;
let result = sq . key_subkey_expire ( cert_path ,
& [ revoked . clone ( ) . into ( ) ] ,
" 1d " ,
None ,
updated_path . as_path ( ) ,
false ) ;
if result . is_ok ( ) {
panic! ( " Updated expiration of hard revoked subkey, but shouldn't have. " ) ;
}
}