This PR addresses #3182 .

t/05_ca_hashes_up_to_date.t was relying on time stamps but those do
not work in GHA.  This PR now uses git diff which is reliable.

Also it check the wording of the generator utils/create_ca_hashes.sh
also it cleans up the generated crtl files which can cause a problem
when ones run it locally.

The test t/05_ca_hashes_up_to_date.t compared the regenerated etc/ca_hashes.txt
byte-for-byte against the committed file, but two aspects are not stable across
platforms:

- The name column comes from 'openssl x509 -subject', which formats
  differently between LibreSSL (macOS) and OpenSSL (Linux).
- The line order comes from 'sort', whose collation differs between
  BSD libc and glibc even under an identically-named locale.

Only the SPKI hash (field 1) is deterministic, and it is the sole
column testssl.sh consumes. So:

- The test now compares the set of SPKI hashes (field 1) between the
  committed and regenerated files, order-independently, using
  'LC_ALL=C sort -u' for a stable, platform-independent collation.
- create_ca_hashes.sh pins the name format ('-nameopt compat') and
  sort collation ('LC_ALL=C') so the generated file is stable.

Verified on macOS (LibreSSL) and Ubuntu 24.04 (OpenSSL).
This commit is contained in:
Dirk Wetter
2026-10-17 21:37:59 +02:00
parent 88fb973974
commit 0779e0a423
2 changed files with 32 additions and 9 deletions
+27 -5
View File
@@ -3,14 +3,36 @@
use strict;
use Test::More;
printf "\n%s\n", "Make sure CA certificate stores are older than their SPKI hashes \"~/etc/ca_hashes.txt\" ...";
# Verify that etc/ca_hashes.txt is in sync with the CA bundles (etc/*.pem).
# Regenerates the hashes and compares content, rather than relying on
# file modification times (which git does not preserve).
my $newer_bundles=`find etc/*.pem -newer etc/ca_hashes.txt`;
# Regenerate ca_hashes.txt from the .pem bundles
my $rc = system('./utils/create_ca_hashes.sh');
is($rc, 0, "create_ca_hashes.sh exited successfully");
#1
is($newer_bundles,"","Checking if there's an output with a *.pem file. If so: run \"~/utils/create_ca_hashes.sh\"");
# Compare the set of SPKI hashes (field 1) between the committed file and
# the freshly regenerated one, order-independently. We do NOT compare the
# whole file byte-for-byte: the name column (field 2) and the line order
# are cosmetic and are not stable across platforms. The name comes from
# `openssl x509 -subject`, which formats differently between LibreSSL
# (macOS) and OpenSSL (Linux); the order comes from `sort`, whose collation
# differs between BSD libc and glibc even under an identically-named locale.
# Only the SPKI hash is deterministic, and it is the sole column testssl.sh
# consumes (grep -h "$hpkp_spki"), so that is what "up to date" means here.
# LC_ALL=C pins the collation so the set comparison is byte-order and
# platform-independent.
my $committed = `git show HEAD:etc/ca_hashes.txt 2>/dev/null | cut -d' ' -f1 | LC_ALL=C sort -u`;
my $regenerated = `cut -d' ' -f1 etc/ca_hashes.txt 2>/dev/null | LC_ALL=C sort -u`;
my $has_diff = ($committed eq $regenerated) ? "0" : "1";
# Restore the original file and remove the .bak the script creates
system('git checkout -- etc/ca_hashes.txt 2>/dev/null');
unlink 'etc/ca_hashes.txt.bak';
is($has_diff, "0",
"ca_hashes.txt is up to date with the .pem bundles (run ./utils/create_ca_hashes.sh if not)");
printf "\n";
done_testing;