Sign and verify manifests in Go with OpenPGP instead of running gpg (closes #181)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
mfer ran the gpg binary to sign, export keys and verify, so it 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. The embedded block may hold no DSA key and no secret key, and an armored field must be one well-formed block. Model: opus-5-5
This commit was merged in pull request #183.
This commit is contained in:
+20
-14
@@ -7,9 +7,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
"uuid"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
@@ -133,44 +135,48 @@ func (m *manifest) generateOuter(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Sign the manifest if signing options are provided
|
||||
if m.signingOptions != nil && m.signingOptions.KeyID != "" {
|
||||
if m.signingOptions != nil {
|
||||
return m.signOuter(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// signOuter signs the outer message with the configured GPG key and
|
||||
// embeds the signature, signer fingerprint, and public key. The signer
|
||||
// and public key are those of the key gpg reports it signed with, so that
|
||||
// a key ID matching more than one key cannot name or embed another key.
|
||||
// signOuter signs the outer message with the secret key in the signing
|
||||
// options and embeds the signature, the key's fingerprint and its public
|
||||
// key.
|
||||
func (m *manifest) signOuter(ctx context.Context) error {
|
||||
// Unlocking a protected key can take a while; do not start once ctx
|
||||
// has ended.
|
||||
err := ctx.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sigString, err := m.signatureString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("build signature string: %w", err)
|
||||
}
|
||||
|
||||
sig, signingKey, err := gpgSign(ctx, []byte(sigString), m.signingOptions.KeyID)
|
||||
key, err := readSigningKey(m.signingOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.pbOuter.Signature = sig
|
||||
var sig bytes.Buffer
|
||||
|
||||
// Listing the signing key, a subkey's included, puts its primary key's
|
||||
// fingerprint first.
|
||||
fingerprint, err := gpgGetKeyFingerprint(ctx, GPGKeyID(signingKey))
|
||||
err = openpgp.ArmoredDetachSign(&sig, key, strings.NewReader(sigString), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("sign manifest: %w", err)
|
||||
}
|
||||
|
||||
m.pbOuter.Signer = fingerprint
|
||||
|
||||
pubKey, err := gpgExportPublicKey(ctx, GPGKeyID(fingerprint))
|
||||
pubKey, err := armoredPublicKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.pbOuter.Signature = sig.Bytes()
|
||||
m.pbOuter.Signer = []byte(fingerprint(key))
|
||||
m.pbOuter.SigningPubKey = pubKey
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user