2024-05-30 20:47:52 +02:00
#![ allow(unused) ]
2024-08-29 10:59:20 +02:00
use std ::borrow ::Borrow ;
2024-06-05 15:23:21 +02:00
use std ::ffi ::OsStr ;
use std ::ffi ::OsString ;
2024-08-14 11:22:07 +02:00
use std ::fs ::File ;
2024-05-30 20:47:52 +02:00
use std ::path ::Path ;
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
use std ::path ::PathBuf ;
2024-05-30 20:47:52 +02:00
use std ::process ::Output ;
2024-06-05 12:48:12 +02:00
use std ::sync ::atomic ::AtomicUsize ;
use std ::sync ::atomic ::Ordering ;
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
use anyhow ::anyhow ;
2024-06-05 15:23:21 +02:00
use anyhow ::Context ;
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
use assert_cmd ::Command ;
use chrono ::DateTime ;
use chrono ::Duration ;
use chrono ::Utc ;
use openpgp ::packet ::Signature ;
use openpgp ::parse ::Parse ;
2024-06-12 16:29:13 +02:00
use openpgp ::policy ::NullPolicy ;
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
use openpgp ::policy ::StandardPolicy ;
use openpgp ::Cert ;
2024-06-10 15:22:41 +02:00
use openpgp ::cert ::CertParser ;
2024-06-09 13:33:14 +02:00
use openpgp ::Fingerprint ;
2024-05-30 20:47:52 +02:00
use openpgp ::KeyHandle ;
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
use openpgp ::Result ;
2024-08-14 11:22:07 +02:00
use openpgp ::serialize ::Serialize ;
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
use sequoia_openpgp as openpgp ;
use tempfile ::TempDir ;
pub const STANDARD_POLICY : & StandardPolicy = & StandardPolicy ::new ( ) ;
2024-06-12 16:29:13 +02:00
pub const NULL_POLICY : & NullPolicy = & NullPolicy ::new ( ) ;
pub fn artifact ( filename : & str ) -> PathBuf {
PathBuf ::from ( " tests/data " ) . join ( filename )
}
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
2024-05-30 20:47:52 +02:00
// Returns the power set excluding the empty set.
pub fn power_set < T : Clone > ( set : & [ T ] ) -> Vec < Vec < T > > {
let mut power_set : Vec < Vec < T > > = Vec ::new ( ) ;
for element in set . iter ( ) {
power_set . extend (
power_set . clone ( ) . into_iter ( ) . map ( | mut v : Vec < T > | {
v . push ( element . clone ( ) ) ;
v
} ) ) ;
power_set . push ( vec! [ element . clone ( ) ] ) ;
}
power_set
}
/// Returns the time formatted as an ISO 8106 string.
pub fn time_as_string ( t : DateTime < Utc > ) -> String {
t . format ( " %Y-%m-%dT%H:%M:%SZ " ) . to_string ( )
}
2024-05-31 13:22:27 +02:00
/// Designates a certificate by path, or by key handle.
2024-06-05 15:23:21 +02:00
#[ derive(Clone, Debug) ]
2024-05-31 13:22:27 +02:00
pub enum FileOrKeyHandle {
FileOrStdin ( PathBuf ) ,
2024-06-05 15:23:21 +02:00
KeyHandle ( ( KeyHandle , OsString ) ) ,
}
impl From < & str > for FileOrKeyHandle {
fn from ( path : & str ) -> Self {
PathBuf ::from ( path ) . into ( )
}
}
impl From < String > for FileOrKeyHandle {
fn from ( path : String ) -> Self {
PathBuf ::from ( path ) . into ( )
}
2024-05-31 13:22:27 +02:00
}
impl From < & Path > for FileOrKeyHandle {
fn from ( path : & Path ) -> Self {
path . to_path_buf ( ) . into ( )
}
}
impl From < & PathBuf > for FileOrKeyHandle {
fn from ( path : & PathBuf ) -> Self {
path . clone ( ) . into ( )
}
}
impl From < PathBuf > for FileOrKeyHandle {
fn from ( path : PathBuf ) -> Self {
FileOrKeyHandle ::FileOrStdin ( path . into ( ) )
}
}
impl From < & KeyHandle > for FileOrKeyHandle {
fn from ( kh : & KeyHandle ) -> Self {
2024-06-05 15:23:21 +02:00
FileOrKeyHandle ::KeyHandle ( ( kh . clone ( ) , kh . to_string ( ) . into ( ) ) )
2024-05-31 13:22:27 +02:00
}
}
impl From < KeyHandle > for FileOrKeyHandle {
fn from ( kh : KeyHandle ) -> Self {
2024-06-05 15:23:21 +02:00
let s = kh . to_string ( ) . into ( ) ;
FileOrKeyHandle ::KeyHandle ( ( kh , s ) )
}
}
2024-06-09 13:33:14 +02:00
impl From < & Fingerprint > for FileOrKeyHandle {
fn from ( fpr : & Fingerprint ) -> Self {
KeyHandle ::from ( fpr ) . into ( )
}
}
impl From < Fingerprint > for FileOrKeyHandle {
fn from ( fpr : Fingerprint ) -> Self {
KeyHandle ::from ( fpr ) . into ( )
}
}
2024-06-05 15:23:21 +02:00
impl From < & FileOrKeyHandle > for FileOrKeyHandle {
fn from ( h : & FileOrKeyHandle ) -> Self {
h . clone ( )
}
}
impl AsRef < OsStr > for FileOrKeyHandle {
fn as_ref ( & self ) -> & OsStr {
match self {
FileOrKeyHandle ::FileOrStdin ( file ) = > file . as_os_str ( ) ,
FileOrKeyHandle ::KeyHandle ( ( kh , s ) ) = > s . as_os_str ( ) ,
}
2024-05-31 13:22:27 +02:00
}
}
impl FileOrKeyHandle {
/// Returns whether this contains a `FileOrStdin`.
pub fn is_file ( & self ) -> bool {
match self {
FileOrKeyHandle ::FileOrStdin ( _ ) = > true ,
FileOrKeyHandle ::KeyHandle ( _ ) = > false ,
}
}
/// Returns whether this contains a `KeyHandle`.
pub fn is_key_handle ( & self ) -> bool {
match self {
FileOrKeyHandle ::FileOrStdin ( _ ) = > false ,
FileOrKeyHandle ::KeyHandle ( _ ) = > true ,
}
}
}
2024-05-30 20:47:52 +02:00
pub struct Sq {
base : TempDir ,
home : PathBuf ,
2024-08-29 10:59:20 +02:00
certd : PathBuf ,
2024-05-30 20:47:52 +02:00
now : std ::time ::SystemTime ,
2024-06-05 12:48:12 +02:00
scratch : AtomicUsize ,
2024-05-30 20:47:52 +02:00
}
impl Sq {
/// Creates a new Sq context in a new, emphemeral home directory.
/// The clock is set to the specified time.
pub fn at ( now : std ::time ::SystemTime ) -> Self {
let base = TempDir ::new ( )
. expect ( " can create a temporary directory " ) ;
let home = base . path ( ) . join ( " home " ) ;
2024-08-29 10:59:20 +02:00
let certd = home . join ( " data " ) . join ( " pgp.cert.d " ) ;
2024-05-30 20:47:52 +02:00
Sq {
base ,
home ,
2024-08-29 10:59:20 +02:00
certd ,
2024-05-30 20:47:52 +02:00
now ,
2024-06-05 12:48:12 +02:00
scratch : 0. into ( ) ,
2024-05-30 20:47:52 +02:00
}
}
/// Creates a new Sq context in a new, emphemeral home directory.
/// The clock is set to the current time.
pub fn new ( ) -> Self {
// The current time.
let mut now = std ::time ::SystemTime ::now ( ) ;
let since_epoch = now . duration_since ( std ::time ::UNIX_EPOCH ) . unwrap ( ) ;
now = now - std ::time ::Duration ::new ( 0 , since_epoch . subsec_nanos ( ) ) ;
Self ::at ( now )
}
/// Returns the base directory.
///
/// The sequoia home directory is under the `home` subdirectory.
/// The rest can be used as scratch space.
pub fn base ( & self ) -> & Path {
self . base . path ( )
}
/// Returns the home directory.
pub fn home ( & self ) -> & Path {
& self . home
}
2024-08-29 10:59:20 +02:00
/// Returns the path to the cert.d.
pub fn certd ( & self ) -> & Path {
& self . certd
}
2024-06-05 12:48:12 +02:00
/// Returns the scratch directory.
pub fn scratch_dir ( & self ) -> PathBuf {
let dir = self . home . join ( " scratch " ) ;
std ::fs ::create_dir_all ( & dir )
. expect ( " can create scratch directory " ) ;
dir
}
/// Returns a new scratch file.
///
/// The file is guaranteed to not exist, but it isn't actually
/// created.
pub fn scratch_file < ' a , S > ( & self , name : S ) -> PathBuf
where S : Into < Option < & ' a str > >
{
let name = name . into ( ) ;
let name_ ;
let name = if let Some ( name ) = name {
name_ = name . chars ( )
. map ( | c | {
if c . is_ascii_alphanumeric ( ) {
c
} else {
'-'
}
} )
. collect ::< String > ( ) ;
& name_
} else {
name . unwrap_or ( " scratch-file " )
} ;
let dir = self . scratch_dir ( ) ;
loop {
let i = self . scratch . fetch_add ( 1 , Ordering ::Relaxed ) ;
let file = dir . join ( format! ( " {} - {} " , i , name ) ) ;
if ! file . exists ( ) {
return file ;
}
}
}
2024-05-30 20:47:52 +02:00
/// Returns the current time.
pub fn now ( & self ) -> std ::time ::SystemTime {
self . now
}
/// Returns the current time formatted as an ISO 8106 string.
pub fn now_as_string ( & self ) -> String {
time_as_string ( self . now . into ( ) )
}
/// Advances the clock by `sec` number of seconds.
pub fn tick ( & mut self , secs : u64 )
{
self . now + = std ::time ::Duration ::new ( secs , 0 ) ;
}
/// Returns a command that is set to run `sq`. The home directory
/// and time are already set.
pub fn command ( & self ) -> Command {
let mut cmd = Command ::cargo_bin ( " sq " )
. expect ( " can run sq " ) ;
2024-08-29 11:14:48 +02:00
cmd . arg ( " --batch " ) ;
2024-05-30 20:47:52 +02:00
cmd . arg ( " --home " ) . arg ( self . home ( ) ) ;
cmd . arg ( " --time " ) . arg ( & self . now_as_string ( ) ) ;
cmd
}
/// Runs the command. If `expect` is `Some`, asserts that the
/// command succeeds or fails as per the boolean.
pub fn run < E > ( & self , mut cmd : Command , expect : E ) -> Output
where E : Into < Option < bool > >
{
eprintln! ( " Running: {:?} " , cmd ) ;
let output = cmd . output ( ) . expect ( " can run command " ) ;
2024-08-14 13:51:55 +02:00
let expect = expect . into ( ) ;
match ( output . status . success ( ) , expect ) {
( true , Some ( true ) ) | ( false , Some ( false ) ) | ( _ , None ) = > {
eprintln! ( " Exit status: " ) ;
let dump = | id , stream | {
let limit = 70 ;
let data = String ::from_utf8_lossy ( stream )
. chars ( )
. collect ::< Vec < _ > > ( ) ;
if data . is_empty ( ) {
eprintln! ( " {} : empty " , id ) ;
} else {
eprintln! ( " {} : {} {} " ,
id ,
data . iter ( ) . take ( limit ) . collect ::< String > ( ) ,
if data . len ( ) > limit {
format! ( " ... {} more bytes " ,
data . len ( ) - limit )
} else {
" " . to_string ( )
} ) ;
}
} ;
dump ( " stdout " , & output . stdout ) ;
dump ( " stderr " , & output . stderr ) ;
}
( got , expected ) = > {
let expected = expect . unwrap ( ) ;
panic! (
" Running {:?}: {}, but should have {}: \n \
stdout : { } \ n \
stderr : { } " ,
cmd ,
if got { " succeeded " } else { " failed " } ,
if expected { " succeeded " } else { " failed " } ,
& String ::from_utf8_lossy ( & output . stdout ) ,
& String ::from_utf8_lossy ( & output . stderr ) ) ;
2024-05-30 20:47:52 +02:00
}
}
output
}
2024-06-05 12:48:12 +02:00
/// Generates a new key.
///
/// The certificate is not imported into the cert store or key
/// store, but saved in a file.
///
/// Returns the certificate, the certificate's filename, and the
/// revocation certificate's filename.
pub fn key_generate ( & self ,
extra_args : & [ & str ] ,
userids : & [ & str ] )
-> ( Cert , PathBuf , PathBuf )
{
let mut cmd = self . command ( ) ;
2024-09-02 10:03:50 +02:00
cmd . args ( [ " key " , " generate " ] ) ;
if ! extra_args . contains ( & " --new-password-file " ) {
cmd . arg ( " --without-password " ) ;
}
2024-06-05 12:48:12 +02:00
for arg in extra_args {
cmd . arg ( arg ) ;
}
2024-08-14 10:47:25 +02:00
let any_userids = ! userids . is_empty ( )
| | extra_args . iter ( ) . any ( | a | a . starts_with ( " --name " )
| | a . starts_with ( " --email " ) ) ;
if ! any_userids {
2024-06-05 12:48:12 +02:00
cmd . arg ( " --no-userids " ) ;
} else {
for userid in userids {
cmd . arg ( " --userid " ) . arg ( userid ) ;
}
}
let cert_filename = self . scratch_file (
userids . get ( 0 ) . map ( | u | format! ( " {} -cert " , u ) ) . as_deref ( ) ) ;
cmd . arg ( " --output " ) . arg ( & cert_filename ) ;
let rev_filename = self . scratch_file (
userids . get ( 0 ) . map ( | u | format! ( " {} -rev " , u ) ) . as_deref ( ) ) ;
cmd . arg ( " --rev-cert " ) . arg ( & rev_filename ) ;
let output = self . run ( cmd , Some ( true ) ) ;
let cert = Cert ::from_file ( & cert_filename )
. expect ( " can parse certificate " ) ;
assert! ( cert . is_tsk ( ) ) ;
( cert , cert_filename , rev_filename )
}
/// Run `sq inspect` and return stdout.
2024-05-31 13:22:27 +02:00
pub fn inspect < H > ( & self , handle : H ) -> String
where H : Into < FileOrKeyHandle >
2024-05-30 20:47:52 +02:00
{
let mut cmd = self . command ( ) ;
2024-05-31 13:22:27 +02:00
cmd . arg ( " inspect " ) ;
match handle . into ( ) {
FileOrKeyHandle ::FileOrStdin ( path ) = > {
cmd . arg ( path ) ;
}
2024-06-05 15:23:21 +02:00
FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) = > {
cmd . arg ( " --cert " ) . arg ( & s ) ;
2024-05-31 13:22:27 +02:00
}
} ;
2024-05-30 20:47:52 +02:00
let output = self . run ( cmd , Some ( true ) ) ;
String ::from_utf8_lossy ( & output . stdout ) . to_string ( )
}
2024-05-31 13:22:27 +02:00
2024-07-04 23:06:44 +02:00
/// Delete the specified key.
pub fn key_delete < ' a , H , Q > ( & self ,
cert_handle : H ,
output_file : Q )
-> Cert
where H : Into < FileOrKeyHandle > ,
Q : Into < Option < & ' a Path > > ,
{
let cert_handle = cert_handle . into ( ) ;
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . arg ( " key " ) . arg ( " delete " ) ;
match & cert_handle {
FileOrKeyHandle ::FileOrStdin ( path ) = > {
cmd . arg ( " --cert-file " ) . arg ( path ) ;
}
FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) = > {
cmd . arg ( " --cert " ) . arg ( & s ) ;
}
} ;
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
let output = self . run ( cmd , Some ( true ) ) ;
assert! ( output . status . success ( ) ) ;
if let Some ( output_file ) = output_file {
if output_file ! = & PathBuf ::from ( " - " ) {
return Cert ::from_file ( output_file )
. expect ( " can parse certificate " ) ;
}
} else if output_file . is_none ( ) {
if let FileOrKeyHandle ::KeyHandle ( ( kh , _s ) ) = cert_handle {
return self . cert_export ( kh ) ;
}
}
Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
}
2024-06-12 14:53:26 +02:00
/// Run `sq key revoked` and return the revocation certificate.
pub fn key_revoke < ' a , H , I , Q > ( & self ,
cert_handle : H ,
revoker_handle : I ,
reason : & str ,
message : & str ,
revocation_time : Option < DateTime < Utc > > ,
notations : & [ ( & str , & str ) ] ,
output_file : Q )
-> Cert
where H : Into < FileOrKeyHandle > ,
I : Into < Option < FileOrKeyHandle > > ,
Q : Into < Option < & ' a Path > > ,
{
let cert_handle = cert_handle . into ( ) ;
let revoker_handle = revoker_handle . into ( ) ;
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . arg ( " key " ) . arg ( " revoke " )
. arg ( reason )
. arg ( message ) ;
match & cert_handle {
FileOrKeyHandle ::FileOrStdin ( path ) = > {
cmd . arg ( " --cert-file " ) . arg ( path ) ;
}
FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) = > {
cmd . arg ( " --cert " ) . arg ( & s ) ;
}
} ;
match revoker_handle . as_ref ( ) {
Some ( FileOrKeyHandle ::FileOrStdin ( path ) ) = > {
cmd . arg ( " --revoker-file " ) . arg ( path ) ;
}
Some ( FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) ) = > {
cmd . arg ( " --revoker " ) . arg ( & s ) ;
}
None = > ( ) ,
} ;
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
for ( k , v ) in notations {
cmd . args ( [ " --notation " , k , v ] ) ;
}
if let Some ( time ) = revocation_time {
cmd . args ( [
" --time " ,
& time . format ( " %Y-%m-%dT%H:%M:%SZ " ) . to_string ( ) ,
] ) ;
}
let output = self . run ( cmd , Some ( true ) ) ;
assert! ( output . status . success ( ) ) ;
if let Some ( output_file ) = output_file {
if output_file ! = & PathBuf ::from ( " - " ) {
return Cert ::from_file ( output_file )
. expect ( " can parse certificate " ) ;
}
} else if output_file . is_none ( ) {
if let FileOrKeyHandle ::KeyHandle ( ( kh , _s ) ) = cert_handle {
return self . cert_export ( kh ) ;
}
}
Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
}
2024-05-31 13:22:27 +02:00
/// Imports the specified key into the keystore.
pub fn key_import < P > ( & self , path : P )
where P : AsRef < Path >
{
let mut cmd = self . command ( ) ;
cmd . arg ( " key " ) . arg ( " import " ) . arg ( path . as_ref ( ) ) ;
self . run ( cmd , Some ( true ) ) ;
}
2024-07-04 23:06:44 +02:00
/// Exports the specified key.
pub fn key_export ( & self , kh : KeyHandle ) -> Cert {
self . key_export_maybe ( kh )
. expect ( " can export key " )
}
/// Exports the specified key from the key store.
///
/// Returns an error if `sq key export` fails. This happens if
/// the certificate is known, but the key store doesn't manage any
/// of its secret key material.
pub fn key_export_maybe ( & self , kh : KeyHandle ) -> Result < Cert > {
let mut cmd = self . command ( ) ;
cmd . args ( [ " key " , " export " , " --cert " , & kh . to_string ( ) ] ) ;
let output = self . run ( cmd , None ) ;
if output . status . success ( ) {
Ok ( Cert ::from_bytes ( & output . stdout ) . expect ( " can parse certificate " ) )
} else {
Err ( anyhow ::anyhow! ( " sq key export returned an error " ) )
}
}
2024-07-05 22:02:35 +02:00
/// Change the key's password.
pub fn key_password < ' a , H , Q > ( & self ,
cert_handle : H ,
old_password_file : Option < & ' a Path > ,
new_password_file : Option < & ' a Path > ,
output_file : Q ,
success : bool )
-> Result < Cert >
where
H : Into < FileOrKeyHandle > ,
Q : Into < Option < & ' a Path > > ,
{
let cert_handle = cert_handle . into ( ) ;
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . arg ( " key " ) . arg ( " password " ) ;
if cert_handle . is_file ( ) {
cmd . arg ( " --cert-file " ) . arg ( & cert_handle ) ;
} else {
cmd . arg ( " --cert " ) . arg ( & cert_handle ) ;
} ;
if let Some ( p ) = old_password_file {
cmd . arg ( " --password-file " ) . arg ( p ) ;
}
if let Some ( p ) = new_password_file {
cmd . arg ( " --new-password-file " ) . arg ( p ) ;
} else {
cmd . arg ( " --clear-password " ) ;
}
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
let output = self . run ( cmd , Some ( success ) ) ;
if output . status . success ( ) {
if let Some ( output_file ) = output_file {
if output_file ! = & PathBuf ::from ( " - " ) {
return Ok ( Cert ::from_file ( output_file )
. expect ( " can parse certificate " ) ) ;
}
} else if output_file . is_none ( ) {
if let FileOrKeyHandle ::KeyHandle ( ( kh , _s ) ) = cert_handle {
return Ok ( self . key_export ( kh ) ) ;
}
}
Ok ( Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " ) )
} else {
Err ( anyhow ::anyhow! ( format! (
" Failed (expected): \n {} " ,
String ::from_utf8_lossy ( & output . stderr ) ) ) )
}
}
2024-06-08 17:59:28 +02:00
/// Target is a certificate.
///
/// `keys` is the set of keys to adopt.
pub fn key_adopt < P , T , K , Q > ( & self ,
keyrings : Vec < P > ,
target : T ,
keys : Vec < K > ,
expire : Option < DateTime < Utc > > ,
allow_broken_crypto : bool ,
output_file : Q ,
success : bool )
-> Result < Cert >
where
P : AsRef < Path > ,
T : Into < FileOrKeyHandle > ,
K : Into < KeyHandle > ,
Q : AsRef < Path > ,
{
let target = target . into ( ) ;
let output_file = output_file . as_ref ( ) ;
let mut cmd = self . command ( ) ;
cmd . arg ( " key " ) . arg ( " adopt " ) ;
for k in keyrings . into_iter ( ) {
cmd . arg ( " --keyring " ) . arg ( k . as_ref ( ) ) ;
}
2024-06-09 13:33:14 +02:00
if target . is_file ( ) {
cmd . arg ( " --cert-file " ) . arg ( target ) ;
} else {
cmd . arg ( " --cert " ) . arg ( target ) ;
} ;
2024-06-08 17:59:28 +02:00
assert! ( ! keys . is_empty ( ) ) ;
for k in keys . into_iter ( ) {
let k : KeyHandle = k . into ( ) ;
cmd . arg ( " --key " ) . arg ( k . to_string ( ) ) ;
}
if let Some ( expire ) = expire {
cmd . arg ( " --expire " ) . arg ( time_as_string ( expire . into ( ) ) ) ;
}
if allow_broken_crypto {
cmd . arg ( " --allow-broken-crypto " ) ;
}
cmd . arg ( " --output " ) . arg ( & output_file ) ;
let output = self . run ( cmd , Some ( success ) ) ;
if output . status . success ( ) {
let cert = if output_file = = PathBuf ::from ( " - " ) {
Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
} else {
Cert ::from_file ( output_file )
. expect ( " can parse certificate " )
} ;
Ok ( cert )
} else {
Err ( anyhow ::anyhow! ( format! (
" Failed (expected): \n {} " ,
String ::from_utf8_lossy ( & output . stderr ) ) ) )
}
}
2024-08-12 15:48:22 +02:00
pub fn key_approvals_update < ' a , H , Q > ( & self ,
cert : H ,
2024-08-20 15:06:49 +02:00
args : & [ & str ] ,
2024-08-12 15:48:22 +02:00
output_file : Q )
2024-06-10 15:22:41 +02:00
-> Cert
2024-06-10 17:04:46 +02:00
where H : Into < FileOrKeyHandle > ,
2024-06-10 15:22:41 +02:00
Q : Into < Option < & ' a Path > > ,
{
2024-06-10 17:04:46 +02:00
let cert = cert . into ( ) ;
2024-06-10 15:22:41 +02:00
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
2024-08-12 15:48:22 +02:00
cmd . arg ( " key " ) . arg ( " approvals " ) . arg ( " update " ) ;
2024-06-10 17:04:46 +02:00
match & cert {
FileOrKeyHandle ::FileOrStdin ( file ) = > {
cmd . arg ( " --cert-file " ) . arg ( file ) ;
}
FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) = > {
cmd . arg ( " --cert " ) . arg ( s ) ;
}
}
2024-08-20 15:06:49 +02:00
cmd . args ( args ) ;
2024-06-10 15:22:41 +02:00
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
let output = self . run ( cmd , Some ( true ) ) ;
if let Some ( output_file ) = output_file {
if output_file ! = & PathBuf ::from ( " - " ) {
return Cert ::from_file ( output_file )
. expect ( " can parse certificate " ) ;
}
2024-06-10 17:04:46 +02:00
} else if output_file . is_none ( ) {
if let FileOrKeyHandle ::KeyHandle ( ( kh , _s ) ) = cert {
return self . cert_export ( kh ) ;
}
2024-06-10 15:22:41 +02:00
}
Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
}
2024-08-14 13:58:15 +02:00
/// Exports the specified keys.
pub fn key_subkey_export < H > ( & self , khs : Vec < H > ) -> Vec < Cert >
where H : Into < KeyHandle >
{
self . key_subkey_export_maybe ( khs )
. expect ( " can export key " )
}
/// Exports the specified keys from the key store.
///
/// Returns an error if `sq key subkey export` fails. This
/// happens if the key is known, but the key store doesn't manage
/// any of its secret key material.
pub fn key_subkey_export_maybe < H > ( & self , khs : Vec < H > ) -> Result < Vec < Cert > >
where H : Into < KeyHandle >
{
let mut cmd = self . command ( ) ;
2024-08-14 14:52:28 +02:00
cmd . args ( [ " key " , " subkey " , " export " ] ) ;
2024-08-14 13:58:15 +02:00
for kh in khs . into_iter ( ) {
let kh : KeyHandle = kh . into ( ) ;
cmd . arg ( " --key " ) . arg ( kh . to_string ( ) ) ;
}
let output = self . run ( cmd , None ) ;
if output . status . success ( ) {
let parser = CertParser ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " ) ;
Ok ( parser . collect ::< Result < Vec < Cert > > > ( ) ? )
} else {
Err ( anyhow ::anyhow! ( " sq key export returned an error " ) )
}
}
2024-07-04 23:21:45 +02:00
/// Delete the specified key.
pub fn key_subkey_delete < ' a , H , Q > ( & self ,
cert_handle : H ,
key_handles : & [ KeyHandle ] ,
output_file : Q )
-> Cert
where H : Into < FileOrKeyHandle > ,
Q : Into < Option < & ' a Path > > ,
{
let cert_handle = cert_handle . into ( ) ;
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . arg ( " key " ) . arg ( " subkey " ) . arg ( " delete " ) ;
match & cert_handle {
FileOrKeyHandle ::FileOrStdin ( path ) = > {
cmd . arg ( " --cert-file " ) . arg ( path ) ;
}
FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) = > {
cmd . arg ( " --cert " ) . arg ( & s ) ;
}
} ;
for kh in key_handles {
cmd . arg ( " --key " ) . arg ( kh . to_string ( ) ) ;
}
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
let output = self . run ( cmd , Some ( true ) ) ;
assert! ( output . status . success ( ) ) ;
if let Some ( output_file ) = output_file {
if output_file ! = & PathBuf ::from ( " - " ) {
return Cert ::from_file ( output_file )
. expect ( " can parse certificate " ) ;
}
} else if output_file . is_none ( ) {
if let FileOrKeyHandle ::KeyHandle ( ( kh , _s ) ) = cert_handle {
// This will fail if the key no longer has any secret
// key material. If it fails, fall back to `sq cert
// export`.
if let Ok ( cert ) = self . key_export_maybe ( kh . clone ( ) ) {
return cert ;
} else {
return self . cert_export ( kh . clone ( ) ) ;
}
}
}
Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
}
2024-07-05 22:58:37 +02:00
/// Change the key's password.
pub fn key_subkey_password < ' a , H , Q > ( & self ,
cert_handle : H ,
keys : & [ KeyHandle ] ,
old_password_file : Option < & ' a Path > ,
new_password_file : Option < & ' a Path > ,
output_file : Q ,
success : bool )
-> Result < Cert >
where
H : Into < FileOrKeyHandle > ,
Q : Into < Option < & ' a Path > > ,
{
let cert_handle = cert_handle . into ( ) ;
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . arg ( " key " ) . arg ( " subkey " ) . arg ( " password " ) ;
if cert_handle . is_file ( ) {
cmd . arg ( " --cert-file " ) . arg ( & cert_handle ) ;
} else {
cmd . arg ( " --cert " ) . arg ( & cert_handle ) ;
} ;
for key in keys . iter ( ) {
cmd . arg ( " --key " ) . arg ( key . to_string ( ) ) ;
}
if let Some ( p ) = old_password_file {
cmd . arg ( " --password-file " ) . arg ( p ) ;
}
if let Some ( p ) = new_password_file {
cmd . arg ( " --new-password-file " ) . arg ( p ) ;
} else {
cmd . arg ( " --clear-password " ) ;
}
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
let output = self . run ( cmd , Some ( success ) ) ;
if output . status . success ( ) {
if let Some ( output_file ) = output_file {
if output_file ! = & PathBuf ::from ( " - " ) {
return Ok ( Cert ::from_file ( output_file )
. expect ( " can parse certificate " ) ) ;
}
} else if output_file . is_none ( ) {
if let FileOrKeyHandle ::KeyHandle ( ( kh , _s ) ) = cert_handle {
return Ok ( self . key_export ( kh ) ) ;
}
}
Ok ( Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " ) )
} else {
Err ( anyhow ::anyhow! ( format! (
" Failed (expected): \n {} " ,
String ::from_utf8_lossy ( & output . stderr ) ) ) )
}
}
2024-08-14 11:22:07 +02:00
/// Adds user IDs to the given key.
pub fn key_userid_add ( & self , key : Cert , args : & [ & str ] ) -> Result < Cert > {
let mut cmd = self . command ( ) ;
cmd . args ( [ " key " , " userid " , " add " ] ) ;
for arg in args {
cmd . arg ( arg ) ;
}
let in_filename = self . scratch_file ( None ) ;
key . as_tsk ( ) . serialize ( & mut File ::create ( & in_filename ) ? ) ? ;
cmd . arg ( " --cert-file " ) . arg ( & in_filename ) ;
let out_filename = self . scratch_file ( None ) ;
cmd . arg ( " --output " ) . arg ( & out_filename ) ;
let output = self . run ( cmd , Some ( true ) ) ;
let out_key = Cert ::from_file ( & out_filename ) ? ;
assert! ( out_key . is_tsk ( ) ) ;
Ok ( out_key )
}
2024-08-15 12:29:39 +02:00
/// Strips user IDs to the given key.
pub fn key_userid_strip ( & self , key : Cert , args : & [ & str ] ) -> Result < Cert > {
let mut cmd = self . command ( ) ;
cmd . args ( [ " key " , " userid " , " strip " ] ) ;
for arg in args {
cmd . arg ( arg ) ;
}
let in_filename = self . scratch_file ( None ) ;
key . as_tsk ( ) . serialize ( & mut File ::create ( & in_filename ) ? ) ? ;
2024-08-15 12:31:31 +02:00
cmd . arg ( " --cert-file " ) . arg ( & in_filename ) ;
2024-08-15 12:29:39 +02:00
let out_filename = self . scratch_file ( None ) ;
cmd . arg ( " --output " ) . arg ( & out_filename ) ;
let output = self . run ( cmd , Some ( true ) ) ;
let out_key = Cert ::from_file ( & out_filename ) ? ;
assert! ( out_key . is_tsk ( ) ) ;
Ok ( out_key )
}
2024-06-05 12:48:12 +02:00
/// Imports the specified certificate into the keystore.
pub fn cert_import < P > ( & self , path : P )
where P : AsRef < Path >
{
let mut cmd = self . command ( ) ;
cmd . arg ( " cert " ) . arg ( " import " ) . arg ( path . as_ref ( ) ) ;
self . run ( cmd , Some ( true ) ) ;
}
2024-05-31 13:22:27 +02:00
/// Exports the specified certificate.
2024-08-29 10:59:20 +02:00
pub fn cert_export < H > ( & self , kh : H ) -> Cert
where
H : Borrow < KeyHandle > ,
{
2024-05-31 13:22:27 +02:00
let mut cmd = self . command ( ) ;
2024-08-29 10:59:20 +02:00
cmd . args ( [ " cert " , " export " , " --cert " , & kh . borrow ( ) . to_string ( ) ] ) ;
2024-05-31 13:22:27 +02:00
let output = self . run ( cmd , Some ( true ) ) ;
Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
}
2024-06-05 12:48:12 +02:00
/// Try to certify the user ID binding.
2024-06-05 15:23:21 +02:00
///
/// If `output_file` is `Some`, then the output is written to that
/// file. Otherwise, the default behavior is followed.
pub fn pki_certify_p < ' a , H , C , Q > ( & self , extra_args : & [ & str ] ,
certifier : H ,
cert : C ,
userid : & str ,
output_file : Q ,
success : bool )
2024-06-05 12:48:12 +02:00
-> Result < Cert >
2024-06-05 15:23:21 +02:00
where H : Into < FileOrKeyHandle > ,
C : Into < FileOrKeyHandle > ,
Q : Into < Option < & ' a Path > > ,
2024-06-05 12:48:12 +02:00
{
2024-06-05 15:23:21 +02:00
let certifier = certifier . into ( ) ;
let cert = cert . into ( ) ;
let output_file = output_file . into ( ) ;
2024-06-05 12:48:12 +02:00
let mut cmd = self . command ( ) ;
cmd . args ( [ " pki " , " certify " ] ) ;
for arg in extra_args {
cmd . arg ( arg ) ;
}
2024-06-05 15:23:21 +02:00
match & certifier {
FileOrKeyHandle ::FileOrStdin ( file ) = > {
cmd . arg ( " --certifier-file " ) . arg ( file ) ;
}
FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) = > {
cmd . arg ( " --certifier " ) . arg ( s ) ;
}
}
cmd . arg ( & cert ) . arg ( userid ) ;
if let Some ( output_file ) = output_file {
cmd . arg ( " --force " ) . arg ( " --output " ) . arg ( output_file ) ;
}
2024-06-05 12:48:12 +02:00
let output = self . run ( cmd , Some ( success ) ) ;
if output . status . success ( ) {
2024-06-05 15:23:21 +02:00
if let Some ( output_file ) = output_file {
// The output was explicitly written to a file.
if output_file = = & PathBuf ::from ( " - " ) {
Ok ( Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " ) )
} else {
Ok ( Cert ::from_file ( & output_file )
. expect ( " can parse certificate " ) )
}
} else {
match cert {
FileOrKeyHandle ::FileOrStdin ( _ ) = > {
// When the cert is from a file, the output is
// written to stdout by default.
Ok ( Cert ::from_bytes ( & output . stdout )
. with_context ( | | {
format! ( " Importing result from the file {:?} " ,
cert )
} )
. expect ( " can parse certificate " ) )
}
FileOrKeyHandle ::KeyHandle ( ( kh , _s ) ) = > {
// When the cert is from the cert store, the
// output is written to the cert store by
// default.
Ok ( self . cert_export ( kh . clone ( ) ) )
}
}
}
2024-06-05 12:48:12 +02:00
} else {
Err ( anyhow ::anyhow! ( format! (
" Failed (expected): \n {} " ,
String ::from_utf8_lossy ( & output . stderr ) ) ) )
}
}
/// Certify the user ID binding.
2024-06-05 15:23:21 +02:00
pub fn pki_certify < ' a , H , C , Q > ( & self , extra_args : & [ & str ] ,
certifier : H ,
cert : C ,
userid : & str ,
output_file : Q )
2024-06-05 12:48:12 +02:00
-> Cert
2024-06-05 15:23:21 +02:00
where H : Into < FileOrKeyHandle > ,
C : Into < FileOrKeyHandle > ,
Q : Into < Option < & ' a Path > > ,
2024-06-05 12:48:12 +02:00
{
2024-06-05 15:23:21 +02:00
self . pki_certify_p (
extra_args , certifier , cert , userid , output_file , true )
2024-06-05 12:48:12 +02:00
. expect ( " success " )
}
2024-06-09 01:23:14 +02:00
2024-07-05 22:02:35 +02:00
pub fn sign < ' a , H , Q > ( & self ,
signer : H ,
password_file : Option < & Path > ,
input_file : & Path ,
output_file : Q )
-> Vec < u8 >
where H : Into < FileOrKeyHandle > ,
Q : Into < Option < & ' a Path > > ,
{
let signer = signer . into ( ) ;
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . arg ( " sign " ) ;
match & signer {
FileOrKeyHandle ::FileOrStdin ( path ) = > {
cmd . arg ( " --signer-file " ) . arg ( path ) ;
}
FileOrKeyHandle ::KeyHandle ( ( _kh , s ) ) = > {
cmd . arg ( " --signer-key " ) . arg ( & s ) ;
}
} ;
if let Some ( password_file ) = password_file {
cmd . arg ( " --password-file " ) . arg ( password_file ) ;
}
cmd . arg ( input_file ) ;
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
} ;
let output = self . run ( cmd , Some ( true ) ) ;
assert! ( output . status . success ( ) ) ;
if let Some ( output_file ) = output_file {
std ::fs ::read ( output_file ) . expect ( " can read file " )
} else {
output . stdout
}
}
2024-06-09 01:23:14 +02:00
// Strips the secret key material from input. Writes it to
// `output_file`, if `Some`.
pub fn toolbox_extract_cert < ' a , P , Q > ( & self , input : P ,
output_file : Q )
-> Cert
where P : AsRef < Path > ,
Q : Into < Option < & ' a Path > > ,
{
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . args ( [ " toolbox " , " extract-cert " ] ) ;
cmd . arg ( input . as_ref ( ) ) ;
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
let output = self . run ( cmd , Some ( true ) ) ;
if let Some ( output_file ) = output_file {
if output_file ! = & PathBuf ::from ( " - " ) {
return Cert ::from_file ( & output_file )
. expect ( " can parse certificate " ) ;
}
}
// Read from stdout.
Cert ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
}
2024-06-10 15:22:41 +02:00
// Merges the certificates.
pub fn toolbox_keyring_merge < ' a , P , Q > ( & self ,
input_files : Vec < P > ,
input_bytes : Option < & [ u8 ] > ,
output_file : Q )
-> Vec < Cert >
where P : AsRef < Path > ,
Q : Into < Option < & ' a Path > > ,
{
let output_file = output_file . into ( ) ;
let mut cmd = self . command ( ) ;
cmd . args ( [ " toolbox " , " keyring " , " merge " ] ) ;
for input_file in input_files . into_iter ( ) {
cmd . arg ( input_file . as_ref ( ) ) ;
}
if let Some ( input_bytes ) = input_bytes {
cmd . arg ( " - " ) ;
cmd . write_stdin ( input_bytes ) ;
}
if let Some ( output_file ) = output_file {
cmd . arg ( " --output " ) . arg ( output_file ) ;
}
let output = self . run ( cmd , Some ( true ) ) ;
let parser = None ;
if let Some ( output_file ) = output_file {
if PathBuf ::from ( " - " ) . as_path ( ) ! = output_file {
CertParser ::from_file ( & output_file )
. expect ( " can parse certificate " ) ;
}
} ;
let parser = if let Some ( parser ) = parser {
parser
} else {
// Read from stdout.
CertParser ::from_bytes ( & output . stdout )
. expect ( " can parse certificate " )
} ;
parser . collect ::< Result < Vec < _ > > > ( )
. expect ( " valid certificates " )
}
2024-05-30 20:47:52 +02:00
}
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
/// Ensure notations can be found in a Signature
///
/// ## Errors
///
/// Returns an error if a notation can not be found in the Signature
pub fn compare_notations (
signature : & Signature ,
2024-06-12 14:12:57 +02:00
notations : & [ ( & str , & str ) ] ,
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
) -> Result < ( ) > {
2024-06-12 14:12:57 +02:00
if ! notations . is_empty ( ) {
Consolidate `sq revoke` commands as `sq key` subcommands
- Move the `sq revoke certificate`, `sq revoke subkey` and `sq revoke
userid` subcommands below the `sq key` namespace as `sq key revoke`,
`sq key subkey revoke` and `sq key userid revoke` (respectively). This
consolidates commands relevant to key management below `sq key`, which
is in line with already existing subcommands (e.g. `sq key generate`,
`sq key subkey add` or `sq key userid add`).
- Replace the use of a common `revoke()` with `CertificateRevocation`,
`SubkeyRevocation` and `UserIDRevocation` to reduce complexity and
allow for easier per target (i.e., certificate, subkey or userid)
command modification.
- Allow specifying an output file using `--output`/ `-o` for all
revocation subcommands (i.e., `sq key revoke`, `sq key subkey revoke`,
`sq key userid revoke`). If unspecified, output goes to stdout as
before.
- Add common test facilities to create a default certificate in a
temporary directory.
- Add common test function to compare a set of notations with those in
a `Signature`.
- Replace the integration tests which used to test a combined `sq
revoke` subcommand with integration tests for `sq key subkey revoke`,
`sq key userid revoke` and `sq key revoke` using direct and third
party revocation.
Fixes #93
2023-06-20 14:44:11 +02:00
let found_notations : Vec < ( & str , String ) > = signature
. notation_data ( )
. map ( | n | ( n . name ( ) , String ::from_utf8_lossy ( n . value ( ) ) . into ( ) ) )
. collect ( ) ;
for ( key , value ) in notations {
if ! found_notations . contains ( & ( key , String ::from ( * value ) ) ) {
return Err ( anyhow! ( format! (
" Expected notation \" {}: {} \" in {:?} " ,
key , value , found_notations
) ) ) ;
}
}
}
Ok ( ( ) )
}