Add GPG signature verification on manifest load
- Implement gpgVerify function that creates a temporary keyring to verify detached signatures against embedded public keys - Signature verification happens during deserialization after hash validation but before decompression - Extract signatureString() as a method on manifest for generating the canonical signature string (MAGIC-UUID-MULTIHASH) - Add --require-signature flag to check command to mandate signature from a specific GPG key ID - Expose IsSigned() and Signer() methods on Checker for signature status
This commit is contained in:
+63
@@ -3,7 +3,9 @@ package mfer
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -88,3 +90,64 @@ func gpgGetKeyFingerprint(keyID GPGKeyID) ([]byte, error) {
|
||||
|
||||
return nil, fmt.Errorf("fingerprint not found for key: %s", keyID)
|
||||
}
|
||||
|
||||
// gpgVerify verifies a detached signature against data using the provided public key.
|
||||
// It creates a temporary keyring to import the public key for verification.
|
||||
func gpgVerify(data, signature, pubKey []byte) error {
|
||||
// Create temporary directory for GPG operations
|
||||
tmpDir, err := os.MkdirTemp("", "mfer-gpg-verify-*")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temp dir: %w", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Set restrictive permissions
|
||||
if err := os.Chmod(tmpDir, 0o700); err != nil {
|
||||
return fmt.Errorf("failed to set temp dir permissions: %w", err)
|
||||
}
|
||||
|
||||
// Write public key to temp file
|
||||
pubKeyFile := filepath.Join(tmpDir, "pubkey.asc")
|
||||
if err := os.WriteFile(pubKeyFile, pubKey, 0o600); err != nil {
|
||||
return fmt.Errorf("failed to write public key: %w", err)
|
||||
}
|
||||
|
||||
// Write signature to temp file
|
||||
sigFile := filepath.Join(tmpDir, "signature.asc")
|
||||
if err := os.WriteFile(sigFile, signature, 0o600); err != nil {
|
||||
return fmt.Errorf("failed to write signature: %w", err)
|
||||
}
|
||||
|
||||
// Write data to temp file
|
||||
dataFile := filepath.Join(tmpDir, "data")
|
||||
if err := os.WriteFile(dataFile, data, 0o600); err != nil {
|
||||
return fmt.Errorf("failed to write data: %w", err)
|
||||
}
|
||||
|
||||
// Import the public key into the temporary keyring
|
||||
importCmd := exec.Command("gpg",
|
||||
"--homedir", tmpDir,
|
||||
"--import",
|
||||
pubKeyFile,
|
||||
)
|
||||
var importStderr bytes.Buffer
|
||||
importCmd.Stderr = &importStderr
|
||||
if err := importCmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to import public key: %w: %s", err, importStderr.String())
|
||||
}
|
||||
|
||||
// Verify the signature
|
||||
verifyCmd := exec.Command("gpg",
|
||||
"--homedir", tmpDir,
|
||||
"--verify",
|
||||
sigFile,
|
||||
dataFile,
|
||||
)
|
||||
var verifyStderr bytes.Buffer
|
||||
verifyCmd.Stderr = &verifyStderr
|
||||
if err := verifyCmd.Run(); err != nil {
|
||||
return fmt.Errorf("signature verification failed: %w: %s", err, verifyStderr.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user