2023-03-14 19:09:03 +03:00
use assert_cmd ::Command ;
use tempfile ::TempDir ;
use sequoia_openpgp as openpgp ;
use openpgp ::KeyHandle ;
use openpgp ::Result ;
use openpgp ::cert ::prelude ::* ;
use openpgp ::parse ::Parse ;
mod integration {
use super ::* ;
#[ test ]
fn sq_encrypt_using_cert_store ( ) -> Result < ( ) >
{
let dir = TempDir ::new ( ) ? ;
let certd = dir . path ( ) . join ( " cert.d " ) . display ( ) . to_string ( ) ;
std ::fs ::create_dir ( & certd ) . expect ( " mkdir works " ) ;
let key_pgp = dir . path ( ) . join ( " key.pgp " ) . display ( ) . to_string ( ) ;
// Generate a key.
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
" key " , " generate " ,
2023-05-08 17:27:33 +03:00
" --expiry " , " never " ,
2023-03-14 19:09:03 +03:00
" --userid " , " <alice@example.org> " ,
2023-06-07 17:03:33 +03:00
" --output " , & key_pgp ] ) ;
2023-03-14 19:09:03 +03:00
cmd . assert ( ) . success ( ) ;
let cert = Cert ::from_file ( & key_pgp ) ? ;
// Try to encrypt a message. This should fail, because we
// haven't imported the key.
for kh in cert . keys ( ) . map ( | ka | KeyHandle ::from ( ka . fingerprint ( ) ) )
. chain ( cert . keys ( ) . map ( | ka | KeyHandle ::from ( ka . keyid ( ) ) ) )
{
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
" encrypt " ,
" --recipient-cert " ,
& kh . to_string ( ) ] )
. write_stdin ( " a secret message " )
. assert ( ) . failure ( ) ;
}
// Import the key.
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
2024-01-19 13:24:21 +03:00
" cert " , " import " , & key_pgp ] ) ;
2023-03-14 19:09:03 +03:00
cmd . assert ( ) . success ( ) ;
const MESSAGE : & str = " \n a secret message \n \n or two \n " ;
// Now we should be able to encrypt a message to it, and
// decrypt it.
for kh in cert . keys ( ) . map ( | ka | KeyHandle ::from ( ka . fingerprint ( ) ) )
. chain ( cert . keys ( ) . map ( | ka | KeyHandle ::from ( ka . keyid ( ) ) ) )
{
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
" encrypt " ,
" --recipient-cert " ,
& kh . to_string ( ) ] )
. write_stdin ( MESSAGE ) ;
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
assert! ( output . status . success ( ) ,
" encryption succeeds for {} \n stdout: \n {} \n stderr: \n {} " ,
kh , stdout , stderr ) ;
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " decrypt " ,
" --recipient-file " ,
& key_pgp ] )
. write_stdin ( stdout . as_bytes ( ) ) ;
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
assert! ( output . status . success ( ) ,
" decryption succeeds for {} \n stdout: \n {} \n stderr: \n {} " ,
kh , stdout , stderr ) ;
}
Ok ( ( ) )
}
2023-03-18 23:03:16 +03:00
#[ test ]
fn sq_encrypt_recipient_userid ( ) -> Result < ( ) >
{
let dir = TempDir ::new ( ) ? ;
let certd = dir . path ( ) . join ( " cert.d " ) . display ( ) . to_string ( ) ;
std ::fs ::create_dir ( & certd ) . expect ( " mkdir works " ) ;
let alice_pgp = dir . path ( ) . join ( " alice.pgp " ) . display ( ) . to_string ( ) ;
let bob_pgp = dir . path ( ) . join ( " bob.pgp " ) . display ( ) . to_string ( ) ;
// Generate the keys.
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
" key " , " generate " ,
2023-05-08 17:27:33 +03:00
" --expiry " , " never " ,
2023-03-18 23:03:16 +03:00
" --userid " , " <alice@example.org> " ,
2023-06-07 17:03:33 +03:00
" --output " , & alice_pgp ] ) ;
2023-03-18 23:03:16 +03:00
cmd . assert ( ) . success ( ) ;
let alice = Cert ::from_file ( & alice_pgp ) ? ;
let bob_userids = & [
" <bob@some.org> " ,
" Bob <bob@other.org> " ,
" <bob@other.org> " ,
] ;
let bob_emails = & [
" bob@some.org " ,
" bob@other.org " ,
] ;
let bob_certified_userids = & [
" Bob <bob@other.org> " ,
] ;
let bob_certified_emails = & [
" bob@other.org " ,
] ;
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
" key " , " generate " ,
2023-05-08 17:27:33 +03:00
" --expiry " , " never " ,
2023-06-07 17:03:33 +03:00
" --output " , & bob_pgp ] ) ;
2023-03-18 23:03:16 +03:00
for userid in bob_userids . iter ( ) {
cmd . args ( [ " --userid " , userid ] ) ;
}
cmd . assert ( ) . success ( ) ;
let bob = Cert ::from_file ( & bob_pgp ) ? ;
// Import the certificates.
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
2024-01-19 13:24:21 +03:00
" cert " , " import " , & alice_pgp ] ) ;
2023-03-18 23:03:16 +03:00
cmd . assert ( ) . success ( ) ;
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
2024-01-19 13:24:21 +03:00
" cert " , " import " , & bob_pgp ] ) ;
2023-03-18 23:03:16 +03:00
cmd . assert ( ) . success ( ) ;
const MESSAGE : & [ u8 ] = & [ 0x42 ; 24 * 1024 + 23 ] ;
let encrypt = | trust_roots : & [ & str ] ,
recipients : & [ ( & str , & str ) ] ,
decryption_keys : & [ & str ] |
{
let mut cmd = Command ::cargo_bin ( " sq " ) . unwrap ( ) ;
cmd . args ( [ " --cert-store " , & certd ] ) ;
for trust_root in trust_roots {
cmd . args ( [ " --trust-root " , trust_root ] ) ;
}
cmd . arg ( " encrypt " ) ;
// Make a string for debugging.
let mut cmd_display = " sq encrypt " . to_string ( ) ;
for ( option , recipient ) in recipients . iter ( ) {
cmd . args ( [ option , recipient ] ) ;
cmd_display . push_str ( " " ) ;
cmd_display . push_str ( option ) ;
cmd_display . push_str ( " " ) ;
cmd_display . push_str ( recipient ) ;
}
cmd . write_stdin ( MESSAGE ) ;
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
if decryption_keys . is_empty ( ) {
assert! ( ! output . status . success ( ) ,
" '{}' should have failed \n stdout: \n {} \n stderr: \n {} " ,
cmd_display , stdout , stderr ) ;
} else {
assert! ( output . status . success ( ) ,
" '{}' should have succeeded \n stdout: \n {} \n stderr: \n {} " ,
cmd_display , stdout , stderr ) ;
for key in decryption_keys . iter ( ) {
let mut cmd = Command ::cargo_bin ( " sq " ) . unwrap ( ) ;
cmd . args ( [ " --no-cert-store " ,
2024-01-18 20:09:59 +03:00
" --no-key-store " ,
2023-03-18 23:03:16 +03:00
" decrypt " ,
" --recipient-file " ,
& key ] )
. write_stdin ( stdout . as_bytes ( ) ) ;
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
assert! ( output . status . success ( ) ,
" '{}' decryption should succeed \n stdout: \n {} \n stderr: \n {} " ,
cmd_display , stdout , stderr ) ;
}
}
} ;
// Encryption by fingerprint should work.
encrypt ( & [ ] ,
& [ ( " --recipient-cert " , & bob . fingerprint ( ) . to_string ( ) ) ] ,
& [ & bob_pgp ] ) ;
// Encryption by email address and user id should fail if the
// binding can't be authenticated.
for email in bob_emails . iter ( ) {
encrypt ( & [ ] ,
& [ ( " --recipient-email " , email ) ] ,
& [ ] ) ;
}
for userid in bob_userids . iter ( ) {
encrypt ( & [ ] ,
& [ ( " --recipient-userid " , userid ) ] ,
& [ ] ) ;
}
// Alice certifies Bob's certificate.
for userid in bob_certified_userids {
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
2024-01-16 18:40:53 +03:00
" pki " , " certify " , & alice_pgp , & bob_pgp , userid ] ) ;
2023-03-18 23:03:16 +03:00
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
assert! ( output . status . success ( ) ,
2024-01-16 18:40:53 +03:00
" 'sq pki certify {} ...' should have succeeded \
2023-03-18 23:03:16 +03:00
\ nstdout :\ n { } \ nstderr :\ n { } " ,
userid , stdout , stderr ) ;
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
2024-01-19 13:24:21 +03:00
" cert " , " import " ] )
2023-03-18 23:03:16 +03:00
. write_stdin ( stdout . as_bytes ( ) ) ;
cmd . assert ( ) . success ( ) ;
}
// Still don't use a trust root. This should still fail.
for email in bob_emails . iter ( ) {
encrypt ( & [ ] ,
& [ ( " --recipient-email " , email ) ] ,
& [ ] ) ;
}
for userid in bob_userids . iter ( ) {
encrypt ( & [ ] ,
& [ ( " --recipient-userid " , userid ) ] ,
& [ ] ) ;
}
// Make Alice the trust root. This should succeed.
for email in bob_emails . iter ( ) {
if bob_certified_emails . contains ( email ) {
encrypt ( & [ & alice . fingerprint ( ) . to_string ( ) ] ,
& [ ( " --recipient-email " , email ) ] ,
& [ & bob_pgp ] ) ;
} else {
encrypt ( & [ & alice . fingerprint ( ) . to_string ( ) ] ,
& [ ( " --recipient-email " , email ) ] ,
& [ ] ) ;
}
}
for userid in bob_userids . iter ( ) {
if bob_certified_userids . contains ( userid ) {
encrypt ( & [ & alice . fingerprint ( ) . to_string ( ) ] ,
& [ ( " --recipient-userid " , userid ) ] ,
& [ & bob_pgp ] ) ;
} else {
encrypt ( & [ & alice . fingerprint ( ) . to_string ( ) ] ,
& [ ( " --recipient-userid " , userid ) ] ,
& [ ] ) ;
}
}
// Make Bob a trust root. This should succeed for all
// self-signed user ids.
for email in bob_emails . iter ( ) {
encrypt ( & [ & bob . fingerprint ( ) . to_string ( ) ] ,
& [ ( " --recipient-email " , email ) ] ,
& [ & bob_pgp ] ) ;
}
for userid in bob_userids . iter ( ) {
encrypt ( & [ & bob . fingerprint ( ) . to_string ( ) ] ,
& [ ( " --recipient-userid " , userid ) ] ,
& [ & bob_pgp ] ) ;
}
2023-03-27 17:23:29 +03:00
Ok ( ( ) )
}
// Encrypt a message to two recipients: one whose certificate is
// in the certificate store, and one whose certificated is in a
// keyring.
#[ test ]
fn sq_encrypt_keyring ( ) -> Result < ( ) >
{
let dir = TempDir ::new ( ) ? ;
let certd = dir . path ( ) . join ( " cert.d " ) . display ( ) . to_string ( ) ;
std ::fs ::create_dir ( & certd ) . expect ( " mkdir works " ) ;
let alice_pgp = dir . path ( ) . join ( " alice.pgp " ) . display ( ) . to_string ( ) ;
let bob_pgp = dir . path ( ) . join ( " bob.pgp " ) . display ( ) . to_string ( ) ;
// Generate the keys.
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
" key " , " generate " ,
2023-05-08 17:27:33 +03:00
" --expiry " , " never " ,
2023-03-27 17:23:29 +03:00
" --userid " , " <alice@example.org> " ,
2023-06-07 17:03:33 +03:00
" --output " , & alice_pgp ] ) ;
2023-03-27 17:23:29 +03:00
cmd . assert ( ) . success ( ) ;
let alice = Cert ::from_file ( & alice_pgp ) ? ;
let alice_fpr = alice . fingerprint ( ) . to_string ( ) ;
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
" key " , " generate " ,
2023-05-08 17:27:33 +03:00
" --expiry " , " never " ,
2023-03-27 17:23:29 +03:00
" --userid " , " <bob@example.org> " ,
2023-06-07 17:03:33 +03:00
" --output " , & bob_pgp ] ) ;
2023-03-27 17:23:29 +03:00
cmd . assert ( ) . success ( ) ;
let bob = Cert ::from_file ( & bob_pgp ) ? ;
let bob_fpr = bob . keyid ( ) . to_string ( ) ;
const MESSAGE : & [ u8 ] = & [ 0x42 ; 24 * 1024 + 23 ] ;
let encrypt = | keyrings : & [ & str ] ,
recipients : & [ & str ] ,
decryption_keys : & [ & str ] |
{
let mut cmd = Command ::cargo_bin ( " sq " ) . unwrap ( ) ;
cmd . args ( [ " --cert-store " , & certd ] ) ;
// Make a string for debugging.
let mut cmd_display = " sq " . to_string ( ) ;
for keyring in keyrings . iter ( ) {
cmd . args ( [ " --keyring " , keyring ] ) ;
cmd_display . push_str ( " --keyring " ) ;
cmd_display . push_str ( keyring ) ;
}
cmd_display . push_str ( " encrypt " ) ;
cmd . arg ( " encrypt " ) ;
for recipient in recipients . iter ( ) {
cmd . args ( [ " --recipient-cert " , recipient ] ) ;
cmd_display . push_str ( " --recipient-cert " ) ;
cmd_display . push_str ( recipient ) ;
}
cmd . write_stdin ( MESSAGE ) ;
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
if decryption_keys . is_empty ( ) {
assert! ( ! output . status . success ( ) ,
" '{}' should have failed \n stdout: \n {} \n stderr: \n {} " ,
cmd_display , stdout , stderr ) ;
} else {
assert! ( output . status . success ( ) ,
" '{}' should have succeeded \n stdout: \n {} \n stderr: \n {} " ,
cmd_display , stdout , stderr ) ;
for key in decryption_keys . iter ( ) {
let mut cmd = Command ::cargo_bin ( " sq " ) . unwrap ( ) ;
cmd . args ( [ " --no-cert-store " ,
2024-01-18 20:09:59 +03:00
" --no-key-store " ,
2023-03-27 17:23:29 +03:00
" decrypt " ,
" --recipient-file " ,
& key ] )
. write_stdin ( stdout . as_bytes ( ) ) ;
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
assert! ( output . status . success ( ) ,
" '{}' decryption should succeed \n stdout: \n {} \n stderr: \n {} " ,
cmd_display , stdout , stderr ) ;
}
}
} ;
encrypt ( & [ & alice_pgp , & bob_pgp ] ,
& [ & alice_fpr , & bob_fpr ] ,
& [ & alice_pgp , & bob_pgp ] ) ;
// Import Alice's certificate.
let mut cmd = Command ::cargo_bin ( " sq " ) ? ;
cmd . args ( [ " --cert-store " , & certd ,
2024-01-19 13:24:21 +03:00
" cert " , " import " , & alice_pgp ] ) ;
2023-03-27 17:23:29 +03:00
let output = cmd . output ( ) . expect ( " success " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) ;
assert! ( output . status . success ( ) ,
" sq import should succeed \n stdout: \n {} \n stderr: \n {} " ,
stdout , stderr ) ;
encrypt ( & [ & alice_pgp , & bob_pgp ] ,
& [ & alice_fpr , & bob_fpr ] ,
& [ & alice_pgp , & bob_pgp ] ) ;
encrypt ( & [ & bob_pgp ] ,
& [ & alice_fpr , & bob_fpr ] ,
& [ & alice_pgp , & bob_pgp ] ) ;
2023-03-18 23:03:16 +03:00
Ok ( ( ) )
}
2023-03-14 19:09:03 +03:00
}