diff --git a/tools/verify-signatures.sh b/tools/verify-signatures.sh index 92e8983d..6dcb03b1 100755 --- a/tools/verify-signatures.sh +++ b/tools/verify-signatures.sh @@ -30,7 +30,7 @@ REPORT="" NOTES="$(dirname "${BASH_SOURCE[0]}")/source-notes.tsv" for a in "$@"; do case "$a" in - --refresh) rm -rf "$GNUPGHOME" "$GNU_KEYRING" ;; + --refresh) rm -rf "$GNUPGHOME" "$GNU_KEYRING" "${GNU_KEYRING}.imported" ;; --fetch-unknown-keys) FETCH_UNKNOWN=1 ;; --strict) STRICT=1 ;; --report=*) REPORT="${a#--report=}" ;; @@ -150,40 +150,51 @@ import_keys() { # Fetched over the network, so it gives "signed by whoever the keyring # says" unless its keys are checked out of band (docs/supply-chain.md). + # The keyring counts as imported only when the GNU keyring and every + # pinned key are in it; what a run could not fetch, the next fetches again. + local count log "fetching GNU keyring" if [[ ! -s "$GNU_KEYRING" ]]; then - quiet_fetch "${CANONICAL_GNU}/gnu-keyring.gpg" "$GNU_KEYRING" \ - || { rm -f "$GNU_KEYRING"; warn "could not fetch GNU keyring"; } + quiet_fetch "${CANONICAL_GNU}/gnu-keyring.gpg" "$GNU_KEYRING" || rm -f "$GNU_KEYRING" fi - - if [[ -s "$GNU_KEYRING" ]]; then + if [[ -s "$GNU_KEYRING" && ! -f "${GNU_KEYRING}.imported" ]]; then log "importing GNU keyring (a few thousand keys, this takes a moment)" gpg --batch --quiet --import "$GNU_KEYRING" 2>/dev/null || true + count="$(gpg --batch --list-keys 2>/dev/null | grep -c '^pub' || true)" + [[ "$count" =~ ^[0-9]+$ && "$count" -ge 100 ]] && : > "${GNU_KEYRING}.imported" + fi + # Without it nothing a GNU maintainer signed can be checked: a run that + # could not fetch it is not a pass. + if [[ ! -f "${GNU_KEYRING}.imported" ]]; then + rm -f "$IMPORTED_MARK" + if [[ "$STRICT" -eq 1 ]]; then + err "the GNU keyring could not be fetched from ${CANONICAL_GNU}" + die "Without it nothing a GNU maintainer signed can be authenticated, and +--strict will not report a run that could not check anything as a pass. +Run again: what was fetched is kept." + fi + warn "the GNU keyring could not be fetched: what GNU maintainers signed is unverifiable this run" fi - # Safe from a keyserver: it cannot serve another key under a full fingerprint. + # Safe from a keyserver: it cannot serve another key under a full + # fingerprint. One it does not serve is fetched again next run, and the + # sources that key signs are unverifiable until then. log "fetching pinned maintainer keys (${#PINNED_FPRS[@]})" - local fpr + local fpr missing=0 for fpr in "${PINNED_FPRS[@]}"; do - recv_key "$fpr" || warn "could not fetch pinned key ${fpr}" + gpg --batch --list-keys "$fpr" >/dev/null 2>&1 && continue + recv_key "$fpr" || { warn "could not fetch pinned key ${fpr}"; missing=$((missing + 1)); } done - local count count="$(gpg --batch --list-keys 2>/dev/null | grep -c '^pub' || true)" [[ "$count" =~ ^[0-9]+$ ]] || count=0 - printf '%s' "$count" > "$IMPORTED_MARK" - - if [[ "$count" -lt 2 ]]; then - if [[ "$STRICT" -eq 1 ]]; then - err "only ${count} key(s) imported" - die "Without the maintainer keys nothing can be authenticated, and ---strict will not report a run that could not check anything as a pass. -Restore network access, or re-run without --strict for an informational pass." - fi - warn "only ${count} key(s) imported - verification will be mostly unverifiable" + if [[ -f "${GNU_KEYRING}.imported" && "$missing" -eq 0 ]]; then + printf '%s' "$count" > "$IMPORTED_MARK" else - ok "keyring ready (${count} public keys)" + rm -f "$IMPORTED_MARK" fi + ok "keyring ready (${count} public keys)" + [[ "$missing" -eq 0 ]] || warn "${missing} pinned key(s) not fetched; fetched again next run" } # --- verification ----------------------------------------------------------