From 2990e09c6c5276882a0c6d80ccaaa846dffeef0f Mon Sep 17 00:00:00 2001 From: Caio Lins Date: Sat, 24 Oct 2026 18:28:03 +0000 Subject: [PATCH] fix: stop writing E2EE key files to a missing data/data folder The bridge saved an encrypted copy of the login key map to data/lcs_secure_.json, relative to its working directory. Docker and the README's native steps both start the bridge inside data/, so the write targeted data/data/ and failed on every login with "failed to save E2EE secure data". The key map is already stored in the user login metadata, which is loaded last and wins, so the file was never needed. Stop writing the file and keep reading old files as a fallback. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YLetjAVceT9hFiBGm9qiTG --- pkg/connector/client.go | 9 --------- pkg/connector/connector.go | 13 ------------- pkg/connector/login_keys_test.go | 12 ------------ pkg/e2ee/manager.go | 19 ++----------------- 4 files changed, 2 insertions(+), 51 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 4369df38..7cde4121 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -906,12 +906,6 @@ func (lc *LineClient) tryLogin(ctx context.Context) error { if mgr != nil { lc.missingE2EEKeyMarked = false } - if mgr != nil { - if err := mgr.SaveSecureDataToFile(loginSecureDataID(&staged, string(lc.UserLogin.ID)), map[string]any{"exportedKeyMap": exported}); err != nil { - lc.UserLogin.Bridge.Log.Warn().Err(err).Msg("Failed to save E2EE secure data after re-login") - } - } - lc.UserLogin.Bridge.Log.Info().Msg("Login successful!") return nil } @@ -965,9 +959,6 @@ func (lc *LineClient) refreshLoginE2EEKeys(res *line.LoginResult, meta *UserLogi return errLineClientSuperseded } lc.applyRefreshedLoginE2EEKeys(meta, res, exported) - if err := mgr.SaveSecureDataToFile(loginSecureDataID(meta, string(lc.UserLogin.ID)), map[string]any{"exportedKeyMap": exported}); err != nil { - return fmt.Errorf("save E2EE secure data after re-login: %w", err) - } lc.UserLogin.Bridge.Log.Info().Int("keys", len(exported)).Msg("Refreshed E2EE keys after re-login") return nil } diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index ccf3e06f..3edd5319 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -828,12 +828,6 @@ func (ll *LineEmailLogin) finishLogin(ctx context.Context, res *line.LoginResult retireLineLogin(ll.ExistingLogin) } - if loginManager != nil { - if err := loginManager.SaveSecureDataToFile(loginSecureDataID(meta, string(ll.User.MXID)), map[string]any{"exportedKeyMap": meta.ExportedKeyMap}); err != nil { - ll.User.Bridge.Log.Warn().Err(err).Msg("Login: failed to save E2EE secure data") - } - } - go newClient.Connect(context.Background()) return &bridgev2.LoginStep{ @@ -946,13 +940,6 @@ func shouldPreserveExistingE2EEKeys(exportedKeys bool, existing *UserLoginMetada return !exportedKeys && existing != nil && len(existing.ExportedKeyMap) > 0 && !existing.ForceFullE2EELogin } -func loginSecureDataID(meta *UserLoginMetadata, fallback string) string { - if meta.Mid != "" { - return meta.Mid - } - return fallback -} - func (ll *LineEmailLogin) fetchLoginKeys(res *line.LoginResult, meta *UserLoginMetadata, client *line.Client) (*e2ee.Manager, error) { if res.EncryptedKeyChain == "" || res.E2EEPublicKey == "" { return nil, nil diff --git a/pkg/connector/login_keys_test.go b/pkg/connector/login_keys_test.go index 554f464e..3449d524 100644 --- a/pkg/connector/login_keys_test.go +++ b/pkg/connector/login_keys_test.go @@ -89,18 +89,6 @@ func TestCopyLoginE2EEKeyMetadataClonesExportedKeyMap(t *testing.T) { } } -func TestLoginSecureDataIDPrefersLineMID(t *testing.T) { - meta := &UserLoginMetadata{Mid: "u-line-mid"} - if got := loginSecureDataID(meta, "@user:example.com"); got != "u-line-mid" { - t.Fatalf("loginSecureDataID = %q, want LINE MID", got) - } - - meta.Mid = "" - if got := loginSecureDataID(meta, "@user:example.com"); got != "@user:example.com" { - t.Fatalf("loginSecureDataID fallback = %q, want Matrix fallback", got) - } -} - func TestStartWithOverrideForcesFullReconnectWithoutStoredE2EEKeys(t *testing.T) { oldLogin := loginWithCredentials var gotCertificate string diff --git a/pkg/e2ee/manager.go b/pkg/e2ee/manager.go index e146dd7e..038fde2d 100644 --- a/pkg/e2ee/manager.go +++ b/pkg/e2ee/manager.go @@ -258,14 +258,8 @@ func (m *Manager) DecryptSecureBlob(b64Cipher string) (map[string]any, error) { return out, nil } -func (m *Manager) EncryptSecureBlob(data map[string]any) (string, error) { - buf, err := json.Marshal(data) - if err != nil { - return "", err - } - return m.runner.StorageEncrypt(string(buf)) -} - +// LoadSecureDataFromFile reads key files written by older bridge versions. +// New keys are only stored in the user login metadata. func (m *Manager) LoadSecureDataFromFile(id string) (map[string]any, error) { path := filepath.Join("data", fmt.Sprintf("lcs_secure_%s.json", id)) b, err := os.ReadFile(path) @@ -275,15 +269,6 @@ func (m *Manager) LoadSecureDataFromFile(id string) (map[string]any, error) { return m.DecryptSecureBlob(strings.TrimSpace(string(b))) } -func (m *Manager) SaveSecureDataToFile(id string, data map[string]any) error { - ct, err := m.EncryptSecureBlob(data) - if err != nil { - return err - } - path := filepath.Join("data", fmt.Sprintf("lcs_secure_%s.json", id)) - return os.WriteFile(path, []byte(ct), 0o600) -} - func (m *Manager) InitFromLoginKeyChain(serverPubB64, encryptedKeyChainB64 string) (map[string]string, error) { keys, err := m.runner.LoginUnwrapKeyChain(serverPubB64, encryptedKeyChainB64) if err != nil {