2024-05-30 21:47:52 +03:00
use std ::time ::Duration ;
2024-10-07 14:27:16 +03:00
use sequoia_openpgp as openpgp ;
2024-05-30 21:47:52 +03:00
use openpgp ::parse ::Parse ;
use openpgp ::Cert ;
2024-10-07 14:27:16 +03:00
use openpgp ::cert ::amalgamation ::ValidAmalgamation ;
2024-05-30 21:47:52 +03:00
use openpgp ::Result ;
2024-10-07 14:27:16 +03:00
use openpgp ::types ::RevocationStatus ;
2024-05-30 21:47:52 +03:00
2024-08-15 14:38:43 +03:00
use super ::common ::STANDARD_POLICY ;
use super ::common ::Sq ;
use super ::common ::time_as_string ;
2024-11-13 11:20:18 +03:00
use super ::common ::UserIDArg ;
2024-05-30 21:47:52 +03:00
#[ test ]
fn sq_key_expire ( ) -> Result < ( ) > {
2024-05-31 14:22:27 +03:00
for keystore in [ false , true ] {
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> " ] ) ;
let fpr = cert . fingerprint ( ) . to_string ( ) ;
2024-05-31 14:22:27 +03:00
// Two days go by.
sq . tick ( 2 * 24 * 60 * 60 ) ;
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-31 14:22:27 +03:00
if keystore {
sq . key_import ( & cert_path ) ;
}
// Change the key to expire in one day.
let mut cmd = sq . command ( ) ;
2024-10-29 10:57:18 +03:00
cmd . args ( [ " key " , " expire " , " --expiration " , " 1d " ] ) ;
2024-05-31 14:22:27 +03:00
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-11-18 16:57:09 +03:00
. arg ( " --cert-file " ) . arg ( & cert_path )
2024-07-20 23:05:03 +03:00
. arg ( " --output " ) . arg ( & updated_path ) ;
2024-05-31 14:22:27 +03:00
}
sq . run ( cmd , true ) ;
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 " ) ;
assert! ( matches! ( vc . alive ( ) , 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 " ) ;
assert! ( matches! ( vc . alive ( ) , 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 " ) ;
assert! ( matches! ( vc . alive ( ) , Err ( _ ) ) ) ;
// 12 hours go by. Clear the expiration time.
sq . tick ( 12 * 60 * 60 ) ;
let mut cmd = sq . command ( ) ;
2024-10-29 10:57:18 +03:00
cmd . args ( [ " key " , " expire " , " --expiration " , " never " ] ) ;
2024-05-31 14:22:27 +03:00
if keystore {
cmd . args ( [ " --cert " , & fpr ] ) ;
} else {
cmd . args ( [
2024-11-18 16:57:09 +03:00
" --cert-file " , & updated_path . to_string_lossy ( ) ,
2024-05-31 14:22:27 +03:00
" --output " , & updated2_path . to_string_lossy ( ) ,
] ) ;
}
sq . run ( cmd , true ) ;
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 ( & 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 , None )
. expect ( " valid " ) ;
assert! ( matches! ( vc . alive ( ) , Ok ( ( ) ) ) ) ;
// It should be alive in 1 day minus 1 second.
let vc = updated . with_policy (
STANDARD_POLICY ,
sq . now ( ) + Duration ::new ( 24 * 60 * 60 - 1 , 0 ) )
. expect ( " valid " ) ;
assert! ( matches! ( vc . alive ( ) , Ok ( ( ) ) ) ) ;
// And in exactly one day...
let vc = updated . with_policy (
STANDARD_POLICY ,
sq . now ( ) + Duration ::new ( 24 * 60 * 60 , 0 ) )
. expect ( " valid " ) ;
assert! ( matches! ( vc . alive ( ) , Ok ( ( ) ) ) ) ;
}
2024-05-30 21:47:52 +03:00
Ok ( ( ) )
}
2024-10-02 17:34:17 +03:00
/// Tests changing the expiration time of a key without direct key
/// signature.
///
/// These kind of keys are generated by GnuPG.
#[ test ]
fn sq_key_expire_no_direct_key_sig ( ) -> Result < ( ) > {
use openpgp ::{
packet ::{ Any , Signature } ,
serialize ::Serialize ,
types ::SignatureType ,
} ;
let mut sq = Sq ::new ( ) ;
let ( cert , _cert_path , _rev_path )
2024-11-13 11:20:18 +03:00
= sq . key_generate ( & [ ] , & [ UserIDArg ::Email ( " alice@example.org " ) ] ) ;
2024-10-02 17:34:17 +03:00
let fipr = cert . fingerprint ( ) . to_string ( ) ;
// Strip the direct key signature.
let mut p = cert . as_tsk ( ) . into_packets ( ) . collect ::< Vec < _ > > ( ) ;
p . retain ( | p | Any ::< Signature > ::downcast_ref ( p )
. map ( | sig | sig . typ ( ) ! = SignatureType ::DirectKey )
. unwrap_or ( true ) ) ;
let cert = Cert ::from_packets ( p . into_iter ( ) ) ? ;
// Two days go by.
sq . tick ( 2 * 24 * 60 * 60 ) ;
let cert_path = sq . scratch_file ( " updated.pgp " ) ;
cert . as_tsk ( ) . serialize ( & mut std ::fs ::File ::create ( & cert_path ) ? ) ? ;
sq . key_import ( & cert_path ) ;
// Change the key to expire in one day.
let mut cmd = sq . command ( ) ;
2024-10-29 10:57:18 +03:00
cmd . args ( [ " key " , " expire " , " --expiration " , " 1d " , " --cert " , & fipr ] ) ;
2024-10-02 17:34:17 +03:00
sq . run ( cmd , true ) ;
eprintln! ( " Updated certificate to expire in one day: \n {} " ,
sq . inspect ( cert . key_handle ( ) ) ) ;
let updated = sq . cert_export ( cert . key_handle ( ) ) ;
// It should be alive now.
let vc = updated . with_policy ( STANDARD_POLICY , sq . now ( ) ) . expect ( " valid " ) ;
assert! ( matches! ( vc . alive ( ) , 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 " ) ;
assert! ( matches! ( vc . alive ( ) , 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 " ) ;
assert! ( matches! ( vc . alive ( ) , Err ( _ ) ) ) ;
// 12 hours go by. Clear the expiration time.
sq . tick ( 12 * 60 * 60 ) ;
let mut cmd = sq . command ( ) ;
2024-10-29 10:57:18 +03:00
cmd . args ( [ " key " , " expire " , " --expiration " , " never " , " --cert " , & fipr ] ) ;
2024-10-02 17:34:17 +03:00
sq . run ( cmd , true ) ;
eprintln! ( " Updated certificate to expire in one day: \n {} " ,
sq . inspect ( cert . key_handle ( ) ) ) ;
let updated = sq . cert_export ( cert . key_handle ( ) ) ;
// It should be alive now.
let vc = updated . with_policy ( STANDARD_POLICY , None )
. expect ( " valid " ) ;
assert! ( matches! ( vc . alive ( ) , Ok ( ( ) ) ) ) ;
// It should be alive in 1 day minus 1 second.
let vc = updated . with_policy (
STANDARD_POLICY ,
sq . now ( ) + Duration ::new ( 24 * 60 * 60 - 1 , 0 ) )
. expect ( " valid " ) ;
assert! ( matches! ( vc . alive ( ) , Ok ( ( ) ) ) ) ;
// And in exactly one day...
let vc = updated . with_policy (
STANDARD_POLICY ,
sq . now ( ) + Duration ::new ( 24 * 60 * 60 , 0 ) )
. expect ( " valid " ) ;
assert! ( matches! ( vc . alive ( ) , Ok ( ( ) ) ) ) ;
Ok ( ( ) )
}
2024-10-07 12:50:36 +03:00
#[ test ]
fn unbound_userid ( ) {
// Make sure we can extend the expiration time of a certificate
// that includes an unbound user ID (i.e., a user ID without a
// self signature).
let sq = Sq ::new ( ) ;
let cert_path = sq . test_data ( )
. join ( " keys " )
. join ( " unbound-userid.pgp " ) ;
let cert = Cert ::from_file ( & cert_path ) . expect ( " can read " ) ;
let vc = cert . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
// It shouldn't be expired yet.
assert! ( vc . primary_key ( ) . key_expiration_time ( ) . is_none ( ) ) ;
// Set it to expire in a day.
let updated_path = sq . scratch_file ( " updated " ) ;
let updated = sq . key_expire ( cert_path ,
" 1d " ,
None ,
updated_path . as_path ( ) ,
true )
2024-10-07 14:27:16 +03:00
. expect ( " sq key expire should succeed " ) ;
let vc = updated . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
let expiration = vc . primary_key ( ) . key_expiration_time ( ) ;
assert_eq! ( expiration ,
Some ( sq . now ( ) + std ::time ::Duration ::new ( 24 * 60 * 60 , 0 ) ) ) ;
}
#[ test ]
fn revoked_userid ( ) {
// Make sure we can extend the expiration time of a certificate
// that includes a revoked user ID (i.e., a user ID without a self
// signature), and make sure we DON'T make the user ID valid.
let sq = Sq ::new ( ) ;
let cert_path = sq . test_data ( )
. join ( " keys " )
. join ( " retired-userid.pgp " ) ;
let cert = Cert ::from_file ( & cert_path ) . expect ( " can read " ) ;
let vc = cert . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
// It shouldn't be expired yet.
assert! ( vc . primary_key ( ) . key_expiration_time ( ) . is_none ( ) ) ;
let ua = vc . userids ( ) . next ( ) . expect ( " have a user ID " ) ;
if let RevocationStatus ::Revoked ( _ ) = ua . revocation_status ( ) {
} else {
panic! ( " User ID should be revoked, but isn't. " ) ;
} ;
// Set it to expire in a day.
let updated_path = sq . scratch_file ( " updated " ) ;
let updated = sq . key_expire ( cert_path ,
" 1d " ,
None ,
updated_path . as_path ( ) ,
true )
. expect ( " sq key expire should succeed " ) ;
2024-10-07 12:50:36 +03:00
let vc = updated . with_policy ( STANDARD_POLICY , sq . now ( ) )
. expect ( " valid cert " ) ;
let expiration = vc . primary_key ( ) . key_expiration_time ( ) ;
assert_eq! ( expiration ,
Some ( sq . now ( ) + std ::time ::Duration ::new ( 24 * 60 * 60 , 0 ) ) ) ;
2024-10-07 14:27:16 +03:00
let ua = vc . userids ( ) . next ( ) . expect ( " have a user ID " ) ;
if let RevocationStatus ::Revoked ( _ ) = ua . revocation_status ( ) {
} else {
panic! ( " User ID should be revoked, but isn't. " ) ;
} ;
2024-10-07 12:50:36 +03:00
}