Skip to content

Commit 66b40cd

Browse files
committed
make x502 signing test less flaky on macos
1 parent b2a655e commit 66b40cd

1 file changed

Lines changed: 92 additions & 52 deletions

File tree

asyncgit/src/sync/sign.rs

Lines changed: 92 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,82 @@ mod tests {
533533
Ok(())
534534
}
535535

536+
/// Export a PKCS#12 bundle for `key`+`cert` with the given `cipher_args`,
537+
/// import it into the gpgsm keyring under `home`, and return the imported
538+
/// secret key's fingerprint. Returns `None` when this openssl/gpgsm pair
539+
/// can't round-trip the bundle, so the caller can try another cipher.
540+
#[cfg(unix)]
541+
fn gpgsm_import_p12(
542+
home: &std::path::Path,
543+
key: &std::path::Path,
544+
cert: &std::path::Path,
545+
cipher_args: &[&str],
546+
) -> Option<String> {
547+
use std::process::Command;
548+
549+
let p12 = home.join("bundle.p12");
550+
// tolerates failure (unlike the test's `run`) so a failed cipher can
551+
// fall back to another.
552+
let run = |program: &str, args: &[&str]| {
553+
Command::new(program)
554+
.args(args)
555+
.env("GNUPGHOME", home)
556+
.output()
557+
.unwrap_or_else(|e| {
558+
panic!("failed to run {program}: {e}")
559+
})
560+
};
561+
562+
let mut export_args = vec![
563+
"pkcs12",
564+
"-export",
565+
"-inkey",
566+
key.to_str().unwrap(),
567+
"-in",
568+
cert.to_str().unwrap(),
569+
"-out",
570+
p12.to_str().unwrap(),
571+
"-passout",
572+
"pass:",
573+
];
574+
export_args.extend_from_slice(cipher_args);
575+
if !run("openssl", &export_args).status.success() {
576+
return None;
577+
}
578+
579+
run(
580+
"gpgsm",
581+
&[
582+
"--batch",
583+
"--pinentry-mode",
584+
"loopback",
585+
"--passphrase",
586+
"",
587+
"--import",
588+
p12.to_str().unwrap(),
589+
],
590+
);
591+
592+
// a listed secret key (fpr line) means the import gave us a key.
593+
let listing = run(
594+
"gpgsm",
595+
&["--batch", "--with-colons", "--list-secret-keys"],
596+
);
597+
String::from_utf8_lossy(&listing.stdout)
598+
.lines()
599+
.filter_map(|line| line.strip_prefix("fpr:"))
600+
.find_map(|rest| {
601+
rest.split(':')
602+
.find(|field| {
603+
field.len() == 40
604+
&& field
605+
.bytes()
606+
.all(|b| b.is_ascii_hexdigit())
607+
})
608+
.map(|field| field.to_string())
609+
})
610+
}
611+
536612
/// e2e x509 signing: set up a throwaway `gpgsm` identity, sign a real
537613
/// commit and verify it. Serial + unix-only: uses a process-wide `GNUPGHOME`.
538614
#[cfg(unix)]
@@ -609,7 +685,6 @@ mod tests {
609685

610686
let key = home.join("key.pem");
611687
let cert = home.join("cert.pem");
612-
let p12 = home.join("bundle.p12");
613688
run(
614689
"openssl",
615690
&[
@@ -628,59 +703,24 @@ mod tests {
628703
&format!("/CN=gitui test/emailAddress={email}"),
629704
],
630705
);
631-
run(
632-
"openssl",
633-
&[
634-
"pkcs12",
635-
"-export",
636-
"-inkey",
637-
key.to_str().unwrap(),
638-
"-in",
639-
cert.to_str().unwrap(),
640-
"-out",
641-
p12.to_str().unwrap(),
642-
"-passout",
643-
"pass:",
644-
// legacy PBE: gpgsm can't read OpenSSL 3's default PBES2/AES.
645-
"-keypbe",
646-
"PBE-SHA1-3DES",
647-
"-certpbe",
648-
"PBE-SHA1-3DES",
649-
"-macalg",
650-
"sha1",
651-
],
652-
);
653-
run(
654-
"gpgsm",
655-
&[
656-
"--batch",
657-
"--pinentry-mode",
658-
"loopback",
659-
"--passphrase",
660-
"",
661-
"--import",
662-
p12.to_str().unwrap(),
663-
],
664-
);
706+
// gpgsm's PKCS#12 reader accepts different ciphers across versions, so
707+
// try a legacy (3DES/SHA1) then a modern (OpenSSL default) bundle and
708+
// fail only if neither imports a usable secret key.
709+
let legacy = &[
710+
"-keypbe",
711+
"PBE-SHA1-3DES",
712+
"-certpbe",
713+
"PBE-SHA1-3DES",
714+
"-macalg",
715+
"sha1",
716+
];
717+
let fingerprint = gpgsm_import_p12(home, &key, &cert, legacy)
718+
.or_else(|| gpgsm_import_p12(home, &key, &cert, &[]))
719+
.expect(
720+
"gpgsm could not import a legacy or modern PKCS#12 bundle",
721+
);
665722

666723
// trust our self-signed root ("S" relaxes CA checks) so gpgsm will sign.
667-
let listing = run(
668-
"gpgsm",
669-
&["--batch", "--with-colons", "--list-secret-keys"],
670-
);
671-
let listing = String::from_utf8_lossy(&listing.stdout);
672-
let fingerprint = listing
673-
.lines()
674-
.filter_map(|line| line.strip_prefix("fpr:"))
675-
.find_map(|rest| {
676-
rest.split(':').find(|field| {
677-
field.len() == 40
678-
&& field
679-
.bytes()
680-
.all(|b| b.is_ascii_hexdigit())
681-
})
682-
})
683-
.expect("could not determine cert fingerprint");
684724
std::fs::write(
685725
home.join("trustlist.txt"),
686726
format!("{fingerprint} S\n"),

0 commit comments

Comments
 (0)