2024-07-05 00:21:45 +03:00
use sequoia_openpgp as openpgp ;
2024-11-21 12:49:41 +03:00
use openpgp ::Cert ;
2024-07-05 00:21:45 +03:00
use openpgp ::KeyHandle ;
use openpgp ::KeyID ;
use openpgp ::Result ;
2024-11-21 12:49:41 +03:00
use openpgp ::cert ::amalgamation ::ValidAmalgamation ;
2024-07-05 00:21:45 +03:00
use openpgp ::packet ::Key ;
2024-11-21 12:49:41 +03:00
use openpgp ::parse ::Parse ;
use openpgp ::types ::RevocationStatus ;
2024-07-05 00:21:45 +03:00
2024-11-21 12:49:41 +03:00
use super ::common ::FileOrKeyHandle ;
2024-08-15 14:38:43 +03:00
use super ::common ::power_set ;
use super ::common ::Sq ;
2024-11-21 12:49:41 +03:00
use super ::common ::STANDARD_POLICY ;
fn check < ' a , H > (
sq : & Sq ,
cert_handle : H ,
to_delete : & [ KeyHandle ] ,
success : bool )
where H : Into < FileOrKeyHandle > ,
{
let cert_handle = cert_handle . into ( ) ;
eprintln! ( " Deleting keys from {:?} : " , cert_handle ) ;
for k in to_delete . iter ( ) {
eprintln! ( " - {} " , k ) ;
}
// Delete the selection.
let result = sq . try_key_subkey_delete ( & cert_handle , to_delete , None ) ;
let got = match ( success , result ) {
( true , Ok ( cert ) ) = > cert ,
( true , Err ( err ) ) = > {
panic! ( " Failed, but should have succeeded: {} " , err )
}
( false , Ok ( _ ) ) = > {
panic! ( " Succeded, but should have failed " )
}
( false , Err ( _ ) ) = > return ,
} ;
// Make sure we got exactly what we asked for; no
// more, no less.
eprintln! ( " Result: " ) ;
let mut deletions = 0 ;
for got in got . keys ( ) {
eprintln! ( " {} {} secret key material " ,
got . fingerprint ( ) ,
if got . has_secret ( ) {
" has "
} else {
" doesn't have "
} ) ;
let should_have_deleted
= to_delete . iter ( ) . find ( | kh | kh . aliases ( got . key_handle ( ) ) ) . is_some ( ) ;
if should_have_deleted {
assert! (
! got . has_secret ( ) ,
" got secret key material \
for a key we should have deleted ( { } ) " ,
got . fingerprint ( ) ) ;
deletions + = 1 ;
} else {
assert! (
got . has_secret ( ) ,
" didn't get secret key material \
for a key we didn ' t delete ( { } ) " ,
got . fingerprint ( ) ) ;
}
}
assert_eq! ( deletions , to_delete . len ( ) ) ;
}
2024-07-05 00:21:45 +03:00
2024-08-15 15:05:24 +03:00
#[ test ]
fn sq_key_subkey_delete ( ) -> Result < ( ) >
{
let sq = Sq ::new ( ) ;
2024-07-05 00:21:45 +03:00
2024-08-15 15:05:24 +03:00
// Generate a key in a file.
let ( cert , cert_file , _rev_file ) = sq . key_generate ( & [ ] , & [ " alice " ] ) ;
assert! ( cert . is_tsk ( ) ) ;
// Delete each non-empty subset of keys.
eprintln! ( " Certificate: " ) ;
for k in cert . keys ( ) {
eprintln! ( " {} " , k . fingerprint ( ) ) ;
}
2024-07-05 00:21:45 +03:00
2024-08-15 15:05:24 +03:00
let keys : Vec < Key < _ , _ > > = cert . keys ( )
. map ( | k | {
k . key ( ) . clone ( )
} )
. collect ( ) ;
2024-07-05 00:21:45 +03:00
2024-08-15 15:05:24 +03:00
let key_ids = keys . iter ( ) . map ( | k | k . fingerprint ( ) ) . collect ::< Vec < _ > > ( ) ;
2024-07-05 00:21:45 +03:00
2024-08-15 15:05:24 +03:00
for ( ( ( i , to_delete ) , keystore ) , by_fpr ) in power_set ( & key_ids ) . into_iter ( )
. enumerate ( )
. flat_map ( | x | [ ( x . clone ( ) , false ) , ( x . clone ( ) , true ) ] )
. flat_map ( | x | [ ( x . clone ( ) , false ) , ( x . clone ( ) , true ) ] )
{
eprintln! ( " Test # {} , by {} , from {} : " ,
i + 1 ,
if by_fpr { " fingerprint " } else { " key ID " } ,
if keystore {
" the key store " . to_string ( )
} else {
cert_file . display ( ) . to_string ( )
} ) ;
2024-07-05 00:21:45 +03:00
2024-11-21 12:49:41 +03:00
let to_delete : Vec < KeyHandle > = if by_fpr {
2024-08-15 15:05:24 +03:00
to_delete . iter ( )
. map ( | fpr | KeyHandle ::from ( fpr ) )
. collect ( )
} else {
to_delete . iter ( )
. map ( | fpr | KeyHandle ::from ( KeyID ::from ( fpr ) ) )
. collect ( )
} ;
2024-11-21 12:49:41 +03:00
if keystore {
2024-08-15 15:05:24 +03:00
// Import it into the key store.
sq . key_import ( & cert_file ) ;
2024-11-21 12:49:41 +03:00
check ( & sq , cert . key_handle ( ) , & to_delete , true ) ;
2024-08-15 15:05:24 +03:00
} else {
2024-11-21 12:49:41 +03:00
check ( & sq , & cert_file , & to_delete , true ) ;
2024-07-05 00:21:45 +03:00
}
}
2024-08-15 15:05:24 +03:00
Ok ( ( ) )
2024-07-05 00:21:45 +03:00
}
2024-11-21 12:49:41 +03:00
#[ test ]
fn unbound_subkey ( ) {
// Make sure we can't delete 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 " ) ;
check ( & sq , & cert_path , & [ unbound ] , false ) ;
}
#[ test ]
fn soft_revoked_subkey ( ) {
// Make sure we can delete 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 . key_handle ( ) ) ;
}
}
let revoked = if let Some ( revoked ) = revoked {
revoked
} else {
panic! ( " Expected a revoked subkey, but didn't fine one " ) ;
} ;
check ( & sq , & cert_path , & [ revoked ] , true ) ;
}
#[ test ]
fn hard_revoked_subkey ( ) {
// Make sure we can delete 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 . key_handle ( ) ) ;
}
}
let revoked = if let Some ( revoked ) = revoked {
revoked
} else {
panic! ( " Expected a revoked subkey, but didn't fine one " ) ;
} ;
check ( & sq , & cert_path , & [ revoked ] , true ) ;
}
#[ test ]
fn sha1_subkey ( ) {
// Make sure we can delete a subkey that is bound using SHA-1.
let sq = Sq ::new ( ) ;
let cert_path = sq . test_data ( )
. join ( " keys " )
. join ( " sha1-subkey-priv.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 subkey key is there and really uses SHA-1.
let valid_subkeys : Vec < _ > = vc . keys ( ) . subkeys ( )
. map ( | ka | ka . fingerprint ( ) )
. collect ( ) ;
let all_subkeys : Vec < _ > = cert . keys ( ) . subkeys ( )
. map ( | ka | ka . fingerprint ( ) )
. collect ( ) ;
assert_eq! ( valid_subkeys . len ( ) , 0 ) ;
assert_eq! ( all_subkeys . len ( ) , 1 ) ;
let subkey = all_subkeys [ 0 ] . clone ( ) ;
check ( & sq , & cert_path , & [ KeyHandle ::from ( subkey ) ] , true ) ;
}