2023-02-21 14:43:43 +03:00
use std ::fs ::{ self , File } ;
use std ::io ;
use tempfile ::TempDir ;
use assert_cmd ::Command ;
use sequoia_openpgp as openpgp ;
use openpgp ::Fingerprint ;
use openpgp ::KeyHandle ;
use openpgp ::Result ;
use openpgp ::{ Packet , PacketPile , Cert } ;
use openpgp ::cert ::CertBuilder ;
use openpgp ::crypto ::KeyPair ;
use openpgp ::packet ::key ::SecretKeyMaterial ;
use openpgp ::packet ::signature ::subpacket ::NotationData ;
use openpgp ::packet ::signature ::subpacket ::NotationDataFlags ;
use openpgp ::types ::{ CompressionAlgorithm , SignatureType } ;
use openpgp ::parse ::Parse ;
use openpgp ::policy ::StandardPolicy ;
use openpgp ::serialize ::stream ::{ Message , Signer , Compressor , LiteralWriter } ;
use openpgp ::serialize ::Serialize ;
const P : & StandardPolicy = & StandardPolicy ::new ( ) ;
fn artifact ( filename : & str ) -> String {
format! ( " tests/data/ {} " , filename )
}
#[ test ]
fn sq_sign ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Sign message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton-private.pgp " ) ] )
. args ( [ " --output " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 3 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 1 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 2 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify signed message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. arg ( & * sig . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
}
#[ test ]
fn sq_sign_with_notations ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Sign message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton-private.pgp " ) ] )
. args ( [ " --output " , & sig . to_string_lossy ( ) ] )
. args ( [ " --notation " , " foo " , " bar " ] )
. args ( [ " --notation " , " !foo " , " xyzzy " ] )
. args ( [ " --notation " , " hello@example.org " , " 1234567890 " ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 3 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 1 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 2 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
eprintln! ( " {:?} " , sig ) ;
let hr = NotationDataFlags ::empty ( ) . set_human_readable ( ) ;
let notations = & mut [
( NotationData ::new ( " foo " , " bar " , hr . clone ( ) ) , false ) ,
( NotationData ::new ( " foo " , " xyzzy " , hr . clone ( ) ) , false ) ,
( NotationData ::new ( " hello@example.org " , " 1234567890 " , hr ) , false )
] ;
for n in sig . notation_data ( ) {
if n . name ( ) = = " salt@notations.sequoia-pgp.org " {
continue ;
}
for ( m , found ) in notations . iter_mut ( ) {
if n = = m {
assert! ( ! * found ) ;
* found = true ;
}
}
}
for ( n , found ) in notations . iter ( ) {
assert! ( found , " Missing: {:?} " , n ) ;
}
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify signed message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. args ( [ " --known-notation " , " foo " ] )
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. arg ( & * sig . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
}
#[ test ]
fn sq_sign_append ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig0 = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Sign message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton-private.pgp " ) ] )
. args ( [ " --output " , & sig0 . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig0 ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 3 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 1 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 2 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig0 ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify signed message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
// Now add a second signature with --append.
let sig1 = tmp_dir . path ( ) . join ( " sig1 " ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --append " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp " ) ] )
. arg ( " --output " )
. arg ( & * sig1 . to_string_lossy ( ) )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig1 ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 5 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ! ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::OnePassSig ( ref ops ) = packets [ 1 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 2 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 3 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 0 ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 4 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 0 ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig1 ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify both signatures of the signed message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. arg ( & * sig1 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256.pgp " ) ] )
. arg ( & * sig1 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
}
#[ test ]
#[ allow(unreachable_code) ]
fn sq_sign_append_on_compress_then_sign ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig0 = tmp_dir . path ( ) . join ( " sig0 " ) ;
// This is quite an odd scheme, so we need to create such a
// message by foot.
let tsk = Cert ::from_file ( & artifact ( " keys/dennis-simon-anton-private.pgp " ) )
. unwrap ( ) ;
let key = tsk . keys ( ) . with_policy ( P , None ) . for_signing ( ) . next ( ) . unwrap ( ) . key ( ) ;
let sec = match key . optional_secret ( ) {
Some ( SecretKeyMaterial ::Unencrypted ( ref u ) ) = > u . clone ( ) ,
_ = > unreachable! ( ) ,
} ;
let keypair = KeyPair ::new ( key . clone ( ) , sec ) . unwrap ( ) ;
let signer = Signer ::new ( Message ::new ( File ::create ( & sig0 ) . unwrap ( ) ) ,
keypair ) . build ( ) . unwrap ( ) ;
let compressor = Compressor ::new ( signer )
. algo ( CompressionAlgorithm ::Uncompressed )
. build ( ) . unwrap ( ) ;
let mut literal = LiteralWriter ::new ( compressor ) . build ( )
. unwrap ( ) ;
io ::copy (
& mut File ::open ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) ) . unwrap ( ) ,
& mut literal )
. unwrap ( ) ;
literal . finalize ( )
. unwrap ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig0 ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 3 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::CompressedData ( _ ) = packets [ 1 ] {
// Do nothing.
} else {
panic! ( " expected compressed data " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 2 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
// Verify signed message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
// Now add a second signature with --append.
let sig1 = tmp_dir . path ( ) . join ( " sig1 " ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --append " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp " ) ] )
. arg ( " --output " )
. arg ( & * sig1 . to_string_lossy ( ) )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. failure ( ) ; // XXX: Currently, this is not implemented.
// XXX: Currently, this is not implemented in sq.
return ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig1 ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 5 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ! ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::OnePassSig ( ref ops ) = packets [ 1 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::CompressedData ( _ ) = packets [ 2 ] {
// Do nothing.
} else {
panic! ( " expected compressed data " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 3 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 0 ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 4 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 0 ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig1 ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify both signatures of the signed message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
}
#[ test ]
fn sq_sign_detached ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Sign detached.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --detached " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton-private.pgp " ) ] )
. args ( [ " --output " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 1 ) ;
if let Packet ::Signature ( ref sig ) = packets [ 0 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP SIGNATURE----- \n \n " ) ) ;
// Verify detached.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. args ( [ " --detached " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
}
#[ test ]
fn sq_sign_detached_append ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Sign detached.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --detached " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton-private.pgp " ) ] )
. args ( [ " --output " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 1 ) ;
if let Packet ::Signature ( ref sig ) = packets [ 0 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP SIGNATURE----- \n \n " ) ) ;
// Verify detached.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. args ( [ " --detached " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that we don't blindly overwrite signatures.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --detached " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp " ) ] )
. args ( [ " --output " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. failure ( ) ;
// Now add a second signature with --append.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --detached " )
. arg ( " --append " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp " ) ] )
. args ( [ " --output " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 2 ) ;
if let Packet ::Signature ( ref sig ) = packets [ 0 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 1 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP SIGNATURE----- \n \n " ) ) ;
// Verify both detached signatures.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/dennis-simon-anton.pgp " ) ] )
. args ( [ " --detached " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256.pgp " ) ] )
. args ( [ " --detached " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Finally, check that we don't truncate the file if something
// goes wrong.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --detached " )
. arg ( " --append " )
. arg ( " --signer-file " ) // Not a private key => signing will fail.
. arg ( & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp521.pgp " ) )
. args ( [ " --output " , & sig . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. failure ( ) ;
// Check that the content is still sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 2 ) ;
if let Packet ::Signature ( ref sig ) = packets [ 0 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 1 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected signature " ) ;
}
}
// Notarizations ahead.
2023-11-24 14:39:48 +03:00
#[ ignore ]
2023-02-21 14:43:43 +03:00
#[ test ]
fn sq_sign_append_a_notarization ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig0 = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Now add a third signature with --append to a notarized message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --append " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp " ) ] )
. args ( [ " --output " , & sig0 . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/signed-1-notarized-by-ed25519.pgp " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig0 ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 7 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ! ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::OnePassSig ( ref ops ) = packets [ 1 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::OnePassSig ( ref ops ) = packets [ 2 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 3 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 4 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 0 ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 5 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 1 ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 6 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 1 ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig0 ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify both notarizations and the signature.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/neal.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/emmelie-dorothea-dina-samantha-awina-ed25519.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
}
2023-11-24 14:39:48 +03:00
#[ ignore ]
2023-02-21 14:43:43 +03:00
#[ test ]
fn sq_sign_notarize ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig0 = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Now add a third signature with --append to a notarized message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --notarize " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp " ) ] )
. args ( [ " --output " , & sig0 . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/signed-1.gpg " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig0 ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 5 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::OnePassSig ( ref ops ) = packets [ 1 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 2 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 3 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 0 ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 4 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 1 ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig0 ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify both notarizations and the signature.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/neal.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
}
2023-11-24 14:39:48 +03:00
#[ ignore ]
2023-02-21 14:43:43 +03:00
#[ test ]
fn sq_sign_notarize_a_notarization ( ) {
let tmp_dir = TempDir ::new ( ) . unwrap ( ) ;
let sig0 = tmp_dir . path ( ) . join ( " sig0 " ) ;
// Now add a third signature with --append to a notarized message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " sign " )
. arg ( " --notarize " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256-private.pgp " ) ] )
. args ( [ " --output " , & sig0 . to_string_lossy ( ) ] )
. arg ( & artifact ( " messages/signed-1-notarized-by-ed25519.pgp " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & sig0 ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 7 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::OnePassSig ( ref ops ) = packets [ 1 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::OnePassSig ( ref ops ) = packets [ 2 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 3 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 4 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 0 ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 5 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 1 ) ;
} else {
panic! ( " expected signature " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 6 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
assert_eq! ( sig . level ( ) , 2 ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & sig0 ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify both notarizations and the signature.
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/neal.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/emmelie-dorothea-dina-samantha-awina-ed25519.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
Command ::cargo_bin ( " sq " )
. unwrap ( )
2023-03-14 19:09:03 +03:00
. arg ( " --no-cert-store " )
2023-02-21 14:43:43 +03:00
. arg ( " verify " )
. args ( [ " --signer-file " , & artifact ( " keys/erika-corinna-daniela-simone-antonia-nistp256.pgp " ) ] )
. arg ( & * sig0 . to_string_lossy ( ) )
. assert ( )
. success ( ) ;
}
#[ test ]
fn sq_multiple_signers ( ) -> Result < ( ) > {
let tmp = TempDir ::new ( ) ? ;
let gen = | userid : & str | {
CertBuilder ::new ( )
. add_signing_subkey ( )
. add_userid ( userid )
. generate ( ) . map ( | ( key , _rev ) | key )
} ;
let alice = gen ( " <alice@some.org> " ) ? ;
let alice_pgp = tmp . path ( ) . join ( " alice.pgp " ) ;
let mut file = File ::create ( & alice_pgp ) ? ;
alice . as_tsk ( ) . serialize ( & mut file ) ? ;
let bob = gen ( " <bob@some.org> " ) ? ;
let bob_pgp = tmp . path ( ) . join ( " bob.pgp " ) ;
let mut file = File ::create ( & bob_pgp ) ? ;
bob . as_tsk ( ) . serialize ( & mut file ) ? ;
// Sign message.
let assertion = Command ::cargo_bin ( " sq " ) ?
. args ( [
2023-03-14 19:09:03 +03:00
" --no-cert-store " ,
2023-02-21 14:43:43 +03:00
" sign " ,
" --signer-file " , alice_pgp . to_str ( ) . unwrap ( ) ,
" --signer-file " , & bob_pgp . to_str ( ) . unwrap ( ) ,
" --detached " ,
] )
. write_stdin ( & b " foo " [ .. ] )
. assert ( ) . try_success ( ) ? ;
let stdout = String ::from_utf8_lossy ( & assertion . get_output ( ) . stdout ) ;
let pp = PacketPile ::from_bytes ( & * stdout ) ? ;
assert_eq! ( pp . children ( ) . count ( ) , 2 ,
" expected two packets " ) ;
let mut sigs : Vec < Fingerprint > = pp . children ( ) . map ( | p | {
if let & Packet ::Signature ( ref sig ) = p {
if let Some ( KeyHandle ::Fingerprint ( fpr ) )
= sig . get_issuers ( ) . into_iter ( ) . next ( )
{
fpr
} else {
panic! ( " No issuer fingerprint subpacket! " ) ;
}
} else {
panic! ( " Expected a signature, got: {:?} " , pp ) ;
}
} ) . collect ( ) ;
sigs . sort ( ) ;
let alice_sig_fpr = alice . with_policy ( P , None ) ?
. keys ( ) . for_signing ( ) . next ( ) . unwrap ( ) . fingerprint ( ) ;
let bob_sig_fpr = bob . with_policy ( P , None ) ?
. keys ( ) . for_signing ( ) . next ( ) . unwrap ( ) . fingerprint ( ) ;
let mut expected = vec! [
alice_sig_fpr ,
bob_sig_fpr ,
] ;
expected . sort ( ) ;
assert_eq! ( sigs , expected ) ;
Ok ( ( ) )
}
2023-03-14 19:09:03 +03:00
#[ test ]
fn sq_sign_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 alice_pgp = dir . path ( ) . join ( " alice.pgp " ) . display ( ) . to_string ( ) ;
let msg_pgp = dir . path ( ) . join ( " msg.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 " , & alice_pgp ] ) ;
2023-03-14 19:09:03 +03:00
cmd . assert ( ) . success ( ) ;
let alice = Cert ::from_file ( & alice_pgp ) ? ;
// Import it.
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-14 19:09:03 +03:00
cmd . assert ( ) . success ( ) ;
// Sign a message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
. arg ( " --no-cert-store " )
. arg ( " sign " )
. args ( [ " --signer-file " , & alice_pgp ] )
. args ( [ " --output " , & msg_pgp ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// Check that the content is sane.
let packets : Vec < Packet > =
PacketPile ::from_file ( & msg_pgp ) . unwrap ( ) . into_children ( ) . collect ( ) ;
assert_eq! ( packets . len ( ) , 3 ) ;
if let Packet ::OnePassSig ( ref ops ) = packets [ 0 ] {
assert! ( ops . last ( ) ) ;
assert_eq! ( ops . typ ( ) , SignatureType ::Binary ) ;
} else {
panic! ( " expected one pass signature " ) ;
}
if let Packet ::Literal ( _ ) = packets [ 1 ] {
// Do nothing.
} else {
panic! ( " expected literal " ) ;
}
if let Packet ::Signature ( ref sig ) = packets [ 2 ] {
assert_eq! ( sig . typ ( ) , SignatureType ::Binary ) ;
let alice_signer = alice . with_policy ( P , None ) ?
. keys ( ) . for_signing ( ) . next ( ) . expect ( " have one " ) ;
assert_eq! ( sig . get_issuers ( ) . into_iter ( ) . next ( ) ,
Some ( KeyHandle ::from ( alice_signer . fingerprint ( ) ) ) ) ;
} else {
panic! ( " expected signature " ) ;
}
let content = fs ::read ( & msg_pgp ) . unwrap ( ) ;
assert! ( & content [ .. ] . starts_with ( b " -----BEGIN PGP MESSAGE----- \n \n " ) ) ;
// Verify the signed message. First, we specify the certificate
// explicitly.
Command ::cargo_bin ( " sq " )
. unwrap ( )
. arg ( " --no-cert-store " )
. arg ( " verify " )
. args ( [ " --signer-file " , & alice_pgp ] )
. arg ( & msg_pgp )
. assert ( )
. success ( ) ;
// Verify the signed message. Now, we don't specify the
// certificate or use a certificate store.
let mut cmd = Command ::cargo_bin ( " sq " ) . unwrap ( ) ;
cmd . arg ( " --no-cert-store " )
. arg ( " verify " )
. arg ( & msg_pgp ) ;
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 ( ) ,
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
assert! ( stderr . contains ( " No key to check checksum from " ) ,
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
assert! ( stderr . contains ( " Error: Verification failed " ) ,
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
// Now we use the certificate store.
let mut cmd = Command ::cargo_bin ( " sq " ) . unwrap ( ) ;
cmd . arg ( " --cert-store " ) . arg ( & certd )
. arg ( " verify " )
. arg ( & msg_pgp ) ;
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 ( ) ,
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
// The default trust model says that certificates from the
// certificate store are not authenticated.
2023-03-21 22:14:10 +03:00
assert! ( stderr . contains ( " Unauthenticated checksum from " ) ,
2023-03-14 19:09:03 +03:00
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
assert! ( stderr . contains ( " Error: Verification failed " ) ,
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
// Now we use the certificate store *and* specify the certificate.
let mut cmd = Command ::cargo_bin ( " sq " ) . unwrap ( ) ;
cmd . arg ( " --cert-store " ) . arg ( & certd )
. arg ( " verify " )
. arg ( " --signer-cert " ) . arg ( & alice . fingerprint ( ) . to_string ( ) )
. arg ( & msg_pgp ) ;
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 ( ) ,
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
// The default trust model says that certificates from the
// certificate store are not authenticated.
assert! ( stderr . contains ( " Good signature from " ) ,
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
2023-11-30 14:01:53 +03:00
assert! ( stderr . contains ( " 1 good signature. " ) ,
2023-03-14 19:09:03 +03:00
" stdout: \n {} \n stderr: {} " , stdout , stderr ) ;
Ok ( ( ) )
}
2023-03-21 22:14:10 +03:00
// Verify signatures using the web of trust to authenticate the
// signers.
#[ test ]
fn sq_verify_wot ( ) -> 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 ( ) ;
let carol_pgp = dir . path ( ) . join ( " carol.pgp " ) . display ( ) . to_string ( ) ;
let dave_pgp = dir . path ( ) . join ( " dave.pgp " ) . display ( ) . to_string ( ) ;
let msg_pgp = dir . path ( ) . join ( " msg.pgp " ) . display ( ) . to_string ( ) ;
// Imports a certificate.
let sq_import = | cert_store : & str , files : & [ & str ] , stdin : Option < & str > | {
let mut cmd = Command ::cargo_bin ( " sq " ) . expect ( " have sq " ) ;
2024-01-19 13:24:21 +03:00
cmd . args ( [ " --cert-store " , cert_store , " cert " , " import " ] ) ;
2023-03-21 22:14:10 +03:00
for file in files {
cmd . arg ( file ) ;
}
if let Some ( stdin ) = stdin {
cmd . write_stdin ( stdin ) ;
}
cmd . assert ( ) . success ( ) ;
} ;
// Generates a key.
//
// If cert_store is not `None`, then the resulting certificate is also
// imported.
let sq_gen_key = | cert_store : Option < & str > , userids : & [ & str ] , file : & str |
-> Cert
{
let mut cmd = Command ::cargo_bin ( " sq " ) . expect ( " have sq " ) ;
cmd . args ( [ " --no-cert-store " ,
" key " , " generate " ,
2023-05-08 17:27:33 +03:00
" --expiry " , " never " ,
2023-06-07 17:03:33 +03:00
" --output " , file ] ) ;
2023-03-21 22:14:10 +03:00
for userid in userids . iter ( ) {
cmd . args ( [ " --userid " , userid ] ) ;
}
cmd . assert ( ) . success ( ) ;
if let Some ( cert_store ) = cert_store {
sq_import ( cert_store , & [ file ] , None ) ;
}
Cert ::from_file ( file ) . expect ( " valid certificate " )
} ;
// Verifies a signed message.
let sq_verify = | cert_store : Option < & str > ,
trust_roots : & [ & str ] ,
signer_files : & [ & str ] ,
msg_pgp : & str |
{
let mut cmd = Command ::cargo_bin ( " sq " ) . expect ( " have sq " ) ;
if let Some ( cert_store ) = cert_store {
cmd . args ( & [ " --cert-store " , cert_store ] ) ;
} else {
cmd . arg ( " --no-cert-store " ) ;
}
for trust_root in trust_roots {
cmd . args ( & [ " --trust-root " , trust_root ] ) ;
}
cmd . arg ( " verify " ) ;
for signer_file in signer_files {
cmd . args ( & [ " --signer-file " , signer_file ] ) ;
}
cmd . arg ( msg_pgp ) ;
let output = cmd . output ( ) . expect ( " can run " ) ;
( output . status . clone ( ) ,
String ::from_utf8_lossy ( & output . stdout ) . to_string ( ) ,
String ::from_utf8_lossy ( & output . stderr ) . to_string ( ) )
} ;
// Certifies a binding.
//
// The certification is imported into the cert store.
let sq_certify = | cert_store : & str ,
key : & str , cert : & str , userid : & str ,
trust_amount : Option < usize > |
{
let mut cmd = Command ::cargo_bin ( " sq " ) . expect ( " have sq " ) ;
cmd . args ( & [ " --cert-store " , cert_store ] ) ;
2024-01-16 18:40:53 +03:00
cmd . args ( & [ " pki " , " certify " , key , cert , userid ] ) ;
2023-03-21 22:14:10 +03:00
if let Some ( trust_amount ) = trust_amount {
cmd . args ( & [ " --amount " , & trust_amount . to_string ( ) [ .. ] ] ) ;
}
let output = cmd . output ( ) . expect ( " can run " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) . to_string ( ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) . to_string ( ) ;
assert! ( output . status . success ( ) ,
2024-01-16 18:40:53 +03:00
" sq pki certify \n stdout: \n {} \n stderr: \n {} " ,
2023-03-21 22:14:10 +03:00
stdout , stderr ) ;
// Import the certification.
sq_import ( cert_store , & [ ] , Some ( & stdout ) ) ;
let output = cmd . output ( ) . expect ( " can run " ) ;
let stdout = String ::from_utf8_lossy ( & output . stdout ) . to_string ( ) ;
let stderr = String ::from_utf8_lossy ( & output . stderr ) . to_string ( ) ;
assert! ( output . status . success ( ) ,
2024-01-16 18:40:53 +03:00
" sq pki certify | sq import \n stdout: \n {} \n stderr: \n {} " ,
2023-03-21 22:14:10 +03:00
stdout , stderr ) ;
} ;
let alice = sq_gen_key ( Some ( & certd ) , & [ " <alice@example.org> " ] , & alice_pgp ) ;
let bob = sq_gen_key ( Some ( & certd ) , & [ " <bob@example.org> " ] , & bob_pgp ) ;
let carol = sq_gen_key ( Some ( & certd ) , & [ " <carol@example.org> " ] , & carol_pgp ) ;
let dave = sq_gen_key ( Some ( & certd ) , & [ " <dave@example.org> " ] , & dave_pgp ) ;
let alice_fpr = alice . fingerprint ( ) . to_string ( ) ;
let bob_fpr = bob . fingerprint ( ) . to_string ( ) ;
let carol_fpr = carol . fingerprint ( ) . to_string ( ) ;
let dave_fpr = dave . fingerprint ( ) . to_string ( ) ;
// Sign a message.
Command ::cargo_bin ( " sq " )
. unwrap ( )
. arg ( " --no-cert-store " )
. arg ( " sign " )
. args ( [ " --signer-file " , & bob_pgp ] )
. args ( [ " --signer-file " , & carol_pgp ] )
. args ( [ " --signer-file " , & dave_pgp ] )
. args ( [ " --output " , & msg_pgp ] )
. arg ( & artifact ( " messages/a-cypherpunks-manifesto.txt " ) )
. assert ( )
. success ( ) ;
// When designating the signers using a file, the signers are
// fully trusted.
{
let output = sq_verify ( Some ( & certd ) , & [ ] , & [ & bob_pgp ] , & msg_pgp ) ;
assert! ( output . 0. success ( ) ) ;
let output = sq_verify ( Some ( & certd ) , & [ ] , & [ & carol_pgp ] , & msg_pgp ) ;
assert! ( output . 0. success ( ) ) ;
let output = sq_verify ( Some ( & certd ) , & [ ] , & [ & dave_pgp ] , & msg_pgp ) ;
assert! ( output . 0. success ( ) ) ;
// Alice did not sign it so this should fail.
let output = sq_verify ( Some ( & certd ) , & [ ] , & [ & alice_pgp ] , & msg_pgp ) ;
assert! ( ! output . 0. success ( ) ) ;
// But, one good signature is enough.
let output = sq_verify ( Some ( & certd ) , & [ ] , & [ & alice_pgp , & bob_pgp ] , & msg_pgp ) ;
assert! ( output . 0. success ( ) ) ;
}
// When the signers' certificates are found in the cert store, and
// they can't be authenticated with the web of trust, the
// verification will fail.
{
let output = sq_verify ( Some ( & certd ) , & [ ] , & [ ] , & msg_pgp ) ;
assert! ( ! output . 0. success ( ) ,
" stdout: \n {} \n stderr: \n {} " , output . 1 , output . 2 ) ;
assert! ( output . 2. contains ( " Unauthenticated checksum from " ) ,
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
// Specifying a trust root won't help if there is no path to a
// signer.
let output = sq_verify ( Some ( & certd ) , & [ & alice_fpr ] , & [ ] , & msg_pgp ) ;
assert! ( ! output . 0. success ( ) ,
" stdout: \n {} \n stderr: \n {} " , output . 1 , output . 2 ) ;
assert! ( output . 2. contains ( " Unauthenticated checksum from " ) ,
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
}
// A trust root can certify itself
{
let output = sq_verify ( Some ( & certd ) , & [ & bob_fpr ] , & [ ] , & msg_pgp ) ;
assert! ( output . 0. success ( ) ,
" stdout: \n {} \n stderr: \n {} " , output . 1 , output . 2 ) ;
assert! ( output . 2. contains ( " Good signature from " ) ,
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
let output = sq_verify (
Some ( & certd ) , & [ & alice_fpr , & bob_fpr ] , & [ ] , & msg_pgp ) ;
assert! ( output . 0. success ( ) ,
" stdout: \n {} \n stderr: \n {} " , output . 1 , output . 2 ) ;
assert! ( output . 2. contains ( " Good signature from " ) ,
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
}
// Have Alice partially certify Bob, and make Alice the trust
// root. The signature should still be bad.
{
sq_certify ( & certd , & alice_pgp ,
& bob . fingerprint ( ) . to_string ( ) , " <bob@example.org> " ,
Some ( 90 ) ) ;
let output = sq_verify ( Some ( & certd ) , & [ & alice_fpr ] , & [ ] , & msg_pgp ) ;
assert! ( ! output . 0. success ( ) ,
" stdout: \n {} \n stderr: \n {} " , output . 1 , output . 2 ) ;
assert! ( output . 2. contains ( " Unauthenticated checksum from " ) ,
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
}
// Have Alice also partially certify Carol, and make Alice the
// trust root. Bob and Carol combined don't (currently) make the
// signature good.
{
sq_certify ( & certd , & alice_pgp ,
& carol_fpr , " <carol@example.org> " ,
Some ( 60 ) ) ;
let output = sq_verify ( Some ( & certd ) , & [ & alice_fpr ] , & [ ] , & msg_pgp ) ;
assert! ( ! output . 0. success ( ) ,
" stdout: \n {} \n stderr: \n {} " , output . 1 , output . 2 ) ;
assert! ( output . 2. contains ( " Unauthenticated checksum from " ) ,
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
2023-11-30 14:01:53 +03:00
assert! ( output . 2. contains ( " 3 unauthenticated checksums " ) ,
2023-03-21 22:14:10 +03:00
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
}
// Have Alice fully certify Dave, and make Alice the trust root.
// Now the signature will be considered verified.
{
sq_certify ( & certd , & alice_pgp ,
& dave_fpr , " <dave@example.org> " ,
None ) ;
let output = sq_verify ( Some ( & certd ) , & [ & alice_fpr ] , & [ ] , & msg_pgp ) ;
assert! ( output . 0. success ( ) ,
" stdout: \n {} \n stderr: \n {} " , output . 1 , output . 2 ) ;
assert! ( output . 2. contains ( " Good signature from " ) ,
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
2023-11-30 14:01:53 +03:00
assert! ( output . 2. contains ( " 1 good signature, 2 unauthenticated checksums " ) ,
2023-03-21 22:14:10 +03:00
" stdout: \n {} \n stderr: \n {} " ,
output . 1 , output . 2 ) ;
}
Ok ( ( ) )
}