Model: fable-5-1
This commit is contained in:
+42
-38
@@ -4,14 +4,18 @@ package cli
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -108,11 +112,10 @@ func TestVerifyRequiredSignerMessages(t *testing.T) {
|
||||
// manifest. The signing key's fingerprint is whatever the generated key
|
||||
// produced, so it is read back from the checker and substituted into the
|
||||
// expected string; the required signer is a fixed value that cannot match
|
||||
// it. Requires gpg and is skipped where it is absent, as the other signing
|
||||
// tests are.
|
||||
//
|
||||
//nolint:paralleltest // signedManifest calls t.Setenv, which bars t.Parallel
|
||||
// it.
|
||||
func TestSignerMismatchMessage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
chk := signedChecker(t,
|
||||
signedManifest(t, map[string][]byte{"f.txt": []byte("signed file")}))
|
||||
|
||||
@@ -123,43 +126,42 @@ func TestSignerMismatchMessage(t *testing.T) {
|
||||
" does not match required "+msgFpB)
|
||||
}
|
||||
|
||||
// signedManifest returns a manifest of files signed by a throwaway GPG key
|
||||
// generated in a temporary GNUPGHOME, which it leaves set for the rest of
|
||||
// the test.
|
||||
// testSecretKey returns a new OpenPGP key with its secret key, armored, as
|
||||
// gpg --export-secret-keys --armor writes it, and the key's fingerprint.
|
||||
// The key is protected by passphrase unless that is nil. It is an Ed25519
|
||||
// key, which is quick to make.
|
||||
func testSecretKey(t *testing.T, passphrase []byte) ([]byte, string) {
|
||||
t.Helper()
|
||||
|
||||
key, err := openpgp.NewEntity("MFER Test Key", "", "test@mfer.test",
|
||||
&packet.Config{Algorithm: packet.PubKeyAlgoEdDSA})
|
||||
require.NoError(t, err)
|
||||
|
||||
if passphrase != nil {
|
||||
require.NoError(t, key.EncryptPrivateKeys(passphrase, nil))
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
w, err := armor.Encode(&buf, openpgp.PrivateKeyType, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, key.SerializePrivateWithoutSigning(w, nil))
|
||||
require.NoError(t, w.Close())
|
||||
|
||||
return buf.Bytes(), strings.ToUpper(hex.EncodeToString(key.PrimaryKey.Fingerprint))
|
||||
}
|
||||
|
||||
// signedManifest returns a manifest of files signed by a new OpenPGP key.
|
||||
func signedManifest(t *testing.T, files map[string][]byte) []byte {
|
||||
t.Helper()
|
||||
|
||||
_, err := exec.LookPath("gpg")
|
||||
if err != nil {
|
||||
t.Skip("gpg not installed, skipping signing test")
|
||||
}
|
||||
|
||||
gpgHome := t.TempDir()
|
||||
params := "%no-protection\n" +
|
||||
"Key-Type: RSA\nKey-Length: 2048\n" +
|
||||
"Name-Real: MFER Test Key\nName-Email: test@mfer.test\n" +
|
||||
"Expire-Date: 0\n%commit\n"
|
||||
paramsFile := filepath.Join(gpgHome, "key-params")
|
||||
require.NoError(t, os.WriteFile(paramsFile, []byte(params), 0o600))
|
||||
|
||||
//nolint:gosec // paramsFile is a test-controlled path inside t.TempDir()
|
||||
cmd := exec.CommandContext(context.Background(), "gpg",
|
||||
"--batch", "--gen-key", paramsFile)
|
||||
|
||||
cmd.Env = append(os.Environ(), "GNUPGHOME="+gpgHome)
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Skipf("failed to generate test GPG key: %v: %s", err, out)
|
||||
}
|
||||
|
||||
t.Setenv("GNUPGHOME", gpgHome)
|
||||
secretKey, _ := testSecretKey(t, nil)
|
||||
|
||||
b := mfer.NewBuilder()
|
||||
b.SetSigningOptions(&mfer.SigningOptions{KeyID: mfer.GPGKeyID("test@mfer.test")})
|
||||
b.SetSigningOptions(&mfer.SigningOptions{SecretKey: secretKey})
|
||||
|
||||
for path, content := range files {
|
||||
_, err = b.AddFile(mfer.RelFilePath(path), mfer.FileSize(len(content)),
|
||||
_, err := b.AddFile(mfer.RelFilePath(path), mfer.FileSize(len(content)),
|
||||
mfer.ModTime{}, 0, bytes.NewReader(content), nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -190,8 +192,8 @@ func signedChecker(t *testing.T, manifest []byte) *mfer.Checker {
|
||||
}
|
||||
|
||||
// manifestSignedByAnotherKey returns a manifest of files and the
|
||||
// fingerprint of a throwaway key, the required key, that did not sign it.
|
||||
// The manifest is signed by a second throwaway key; its embedded public key
|
||||
// fingerprint of a new key, the required key, that did not sign it.
|
||||
// The manifest is signed by a second new key; its embedded public key
|
||||
// block holds the required key followed by the second key, and its signer
|
||||
// field names the required key.
|
||||
func manifestSignedByAnotherKey(
|
||||
@@ -207,8 +209,10 @@ func manifestSignedByAnotherKey(
|
||||
require.NoError(t, proto.Unmarshal(
|
||||
signedManifest(t, files)[len(mfer.MAGIC):], outer))
|
||||
|
||||
// Armored blocks start on a line of their own; mfer, unlike gpg, ends
|
||||
// one without a newline.
|
||||
outer.SigningPubKey = slices.Concat(
|
||||
required.GetSigningPubKey(), outer.GetSigningPubKey())
|
||||
required.GetSigningPubKey(), []byte("\n"), outer.GetSigningPubKey())
|
||||
outer.Signer = required.GetSigner()
|
||||
|
||||
data, err := proto.Marshal(outer)
|
||||
|
||||
Reference in New Issue
Block a user