Sign and verify manifests in Go with OpenPGP instead of running gpg (closes #181)
check / check (push) Waiting to run

mfer ran the gpg binary to sign, export keys and verify, so signing and
loading signed manifests failed wherever gpg is missing. It now uses
github.com/ProtonMail/go-crypto/openpgp. --sign-key and MFER_SIGN_KEY
name a file holding one version 4 OpenPGP secret key; a protected key's
passphrase comes from MFER_SIGN_KEY_PASSPHRASE or a terminal prompt. gen
and freshen check that the key can sign before they read any file.
Verification keeps the rules of the --require-signature fix: one primary
key in the embedded block, counted from its packets, exactly one
signature, made by that key or a subkey, and signer equal to its
fingerprint. A DSA key is refused, and so is an armored field that is
not one well-formed block.

Model: opus-5-5
This commit is contained in:
2026-10-08 05:21:16 +00:00
committed by sneak
parent c23367c216
commit e00ec787e8
25 changed files with 1585 additions and 1178 deletions
+13 -6
View File
@@ -124,7 +124,7 @@ func (mfa *CLIApp) outputPath(cmd *cli.Command) (string, error) {
// the path the manifest is written to.
func (mfa *CLIApp) buildScannerOptions(
cmd *cli.Command, output string,
) *mfer.ScannerOptions {
) (*mfer.ScannerOptions, error) {
opts := &mfer.ScannerOptions{
IncludeDotfiles: cmd.Bool("include-dotfiles"),
FollowSymLinks: cmd.Bool("follow-symlinks"),
@@ -145,13 +145,15 @@ func (mfa *CLIApp) buildScannerOptions(
// Set up signing options if sign-key is provided
if signKey := cmd.String("sign-key"); signKey != "" {
opts.SigningOptions = &mfer.SigningOptions{
KeyID: mfer.GPGKeyID(signKey),
signing, err := mfa.signingOptions(signKey)
if err != nil {
return nil, err
}
log.Infof("signing manifest with GPG key: %s", signKey)
opts.SigningOptions = signing
}
return opts
return opts, nil
}
// enumerateInputs runs the enumeration phase over the argument paths,
@@ -275,7 +277,12 @@ func (mfa *CLIApp) generateManifestOperation(
return err
}
s := mfer.NewScannerWithOptions(mfa.buildScannerOptions(cmd, outputPath))
opts, err := mfa.buildScannerOptions(cmd, outputPath)
if err != nil {
return err
}
s := mfer.NewScannerWithOptions(opts)
// Phase 1: Enumeration - collect paths and stat files
err = mfa.runEnumeratePhase(cmd, s)