wip snapshot after account logout; squash into the unit commit
check / check (push) Waiting to run

Model: fable-5-1
This commit is contained in:
2026-10-08 00:13:24 +00:00
parent e35cd4a045
commit fec2ddfb7c
25 changed files with 1160 additions and 1196 deletions
+42 -38
View File
@@ -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)