feat: add E2EE diagnostics logging and testing support for crypto initialization
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package matrix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
@@ -8,6 +9,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"maunium.net/go/mautrix/crypto"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// fakeCryptoHelper implements cryptoHelper for testing.
|
||||
@@ -170,3 +174,229 @@ func TestInitHelper_SetsAccountID(t *testing.T) {
|
||||
t.Errorf("expected accountID='my-agent', got '%s'", helper.accountID)
|
||||
}
|
||||
}
|
||||
|
||||
// --- diagMachine fake for testing diagnostics ---
|
||||
|
||||
type fakeDiagMachine struct {
|
||||
pubKeys *crypto.CrossSigningPublicKeysCache
|
||||
ownDevice *id.Device
|
||||
seeds crypto.CrossSigningSeeds
|
||||
seedsPanic bool // simulate ExportCrossSigningKeys panic
|
||||
trustState id.TrustState
|
||||
trustErr error
|
||||
deviceTrusted bool
|
||||
}
|
||||
|
||||
func (f *fakeDiagMachine) GetOwnCrossSigningPublicKeys(ctx context.Context) *crypto.CrossSigningPublicKeysCache {
|
||||
return f.pubKeys
|
||||
}
|
||||
|
||||
func (f *fakeDiagMachine) OwnIdentity() *id.Device {
|
||||
return f.ownDevice
|
||||
}
|
||||
|
||||
func (f *fakeDiagMachine) ExportCrossSigningKeys() crypto.CrossSigningSeeds {
|
||||
if f.seedsPanic {
|
||||
panic("nil pointer dereference")
|
||||
}
|
||||
return f.seeds
|
||||
}
|
||||
|
||||
func (f *fakeDiagMachine) ResolveTrustContext(ctx context.Context, device *id.Device) (id.TrustState, error) {
|
||||
return f.trustState, f.trustErr
|
||||
}
|
||||
|
||||
func (f *fakeDiagMachine) IsDeviceTrusted(device *id.Device) bool {
|
||||
return f.deviceTrusted
|
||||
}
|
||||
|
||||
// testLogger returns a logger that writes to a buffer for assertions.
|
||||
func testLogger(buf *bytes.Buffer) *slog.Logger {
|
||||
return slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
}
|
||||
|
||||
func TestLogCryptoDiagnosticsCore_NilOwnDevice(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
machine := &fakeDiagMachine{
|
||||
pubKeys: &crypto.CrossSigningPublicKeysCache{MasterKey: "abc", SelfSigningKey: "def", UserSigningKey: "ghi"},
|
||||
ownDevice: nil, // nil device — was causing panic before the fix
|
||||
seeds: crypto.CrossSigningSeeds{MasterKey: []byte("x"), SelfSigningKey: []byte("y"), UserSigningKey: []byte("z")},
|
||||
}
|
||||
|
||||
logCryptoDiagnosticsCore(context.Background(), machine, "@bot:test", "DEVICE1", logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "own device identity is nil") {
|
||||
t.Errorf("expected warning about nil device identity, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogCryptoDiagnosticsCore_NilPublicKeys(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
machine := &fakeDiagMachine{
|
||||
pubKeys: nil, // no cross-signing public keys
|
||||
ownDevice: nil,
|
||||
seeds: crypto.CrossSigningSeeds{},
|
||||
}
|
||||
|
||||
logCryptoDiagnosticsCore(context.Background(), machine, "@bot:test", "DEVICE1", logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "NO cross-signing public keys found") {
|
||||
t.Errorf("expected warning about missing public keys, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogCrossSigningSeeds_PanicRecovery(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
machine := &fakeDiagMachine{seedsPanic: true}
|
||||
|
||||
// Must not panic — should recover gracefully.
|
||||
logCrossSigningSeeds(machine, logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "cross-signing private keys not available") {
|
||||
t.Errorf("expected recovery warning, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogCrossSigningSeeds_AllPresent(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
machine := &fakeDiagMachine{
|
||||
seeds: crypto.CrossSigningSeeds{
|
||||
MasterKey: []byte("master"),
|
||||
SelfSigningKey: []byte("self"),
|
||||
UserSigningKey: []byte("user"),
|
||||
},
|
||||
}
|
||||
|
||||
logCrossSigningSeeds(machine, logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "cross-signing private keys in store") {
|
||||
t.Errorf("expected info about keys in store, got:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "self-signing private key NOT in store") {
|
||||
t.Error("should not warn when self-signing key is present")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogCrossSigningSeeds_MissingSelfSigning(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
machine := &fakeDiagMachine{
|
||||
seeds: crypto.CrossSigningSeeds{
|
||||
MasterKey: []byte("master"),
|
||||
// SelfSigningKey intentionally missing
|
||||
},
|
||||
}
|
||||
|
||||
logCrossSigningSeeds(machine, logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "self-signing private key NOT in store") {
|
||||
t.Errorf("expected warning about missing self-signing key, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogDeviceTrust_Trusted(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
device := &id.Device{DeviceID: "DEV1"}
|
||||
machine := &fakeDiagMachine{
|
||||
trustState: id.TrustStateCrossSignedTOFU,
|
||||
deviceTrusted: true,
|
||||
}
|
||||
|
||||
logDeviceTrust(context.Background(), machine, device, logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "own device trust state") {
|
||||
t.Errorf("expected trust state log, got:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "device is NOT cross-signed") {
|
||||
t.Error("should not warn for trusted device")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogDeviceTrust_Untrusted(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
device := &id.Device{DeviceID: "DEV2"}
|
||||
machine := &fakeDiagMachine{
|
||||
trustState: id.TrustStateUnset,
|
||||
deviceTrusted: false,
|
||||
}
|
||||
|
||||
logDeviceTrust(context.Background(), machine, device, logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "device is NOT cross-signed") {
|
||||
t.Errorf("expected cross-sign warning, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogDeviceTrust_ResolveTrustError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
device := &id.Device{DeviceID: "DEV3"}
|
||||
machine := &fakeDiagMachine{
|
||||
trustErr: errors.New("crypto store unavailable"),
|
||||
}
|
||||
|
||||
logDeviceTrust(context.Background(), machine, device, logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "failed to resolve device trust") {
|
||||
t.Errorf("expected trust resolve error, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogCryptoDiagnosticsCore_FullHappyPath(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := testLogger(&buf)
|
||||
|
||||
machine := &fakeDiagMachine{
|
||||
pubKeys: &crypto.CrossSigningPublicKeysCache{
|
||||
MasterKey: "masterkey123",
|
||||
SelfSigningKey: "selfkey456",
|
||||
UserSigningKey: "userkey789",
|
||||
},
|
||||
ownDevice: &id.Device{DeviceID: "MYDEV"},
|
||||
trustState: id.TrustStateCrossSignedTOFU,
|
||||
deviceTrusted: true,
|
||||
seeds: crypto.CrossSigningSeeds{
|
||||
MasterKey: []byte("m"),
|
||||
SelfSigningKey: []byte("s"),
|
||||
UserSigningKey: []byte("u"),
|
||||
},
|
||||
}
|
||||
|
||||
logCryptoDiagnosticsCore(context.Background(), machine, "@bot:hs", "MYDEV", logger)
|
||||
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "device info") {
|
||||
t.Error("expected device info log")
|
||||
}
|
||||
if !strings.Contains(out, "cross-signing public keys found") {
|
||||
t.Error("expected public keys log")
|
||||
}
|
||||
if !strings.Contains(out, "own device trust state") {
|
||||
t.Error("expected trust state log")
|
||||
}
|
||||
if !strings.Contains(out, "cross-signing private keys in store") {
|
||||
t.Error("expected private keys log")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user