Skip to content

Commit

Permalink
Add TestBackupWrongRecoveryKeyFails
Browse files Browse the repository at this point in the history
  • Loading branch information
kegsay committed Jan 17, 2024
1 parent 7435846 commit 260216b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 3 deletions.
2 changes: 1 addition & 1 deletion TEST_HITLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Membership ACLs:

Key backups:
- [x] New device for Alice cannot decrypt previous messages. Backups can be made on Alice's first device. Alice's new device can download the backup and decrypt the messages. Check backups work cross-platform (e.g create on rust, restore on JS and vice versa).
- [ ] Inputting the wrong recovery key fails to decrypt the backup.
- [x] Inputting the wrong recovery key fails to decrypt the backup.

One-time Keys:
- [ ] When Alice runs out of OTKs, local users use the fallback key.
Expand Down
8 changes: 8 additions & 0 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type Client interface {
MustBackupKeys(t ct.TestLike) (recoveryKey string)
// MustLoadBackup will recover E2EE keys from the latest backup, else fail the test.
MustLoadBackup(t ct.TestLike, recoveryKey string)
// LoadBackup will recover E2EE keys from the latest backup, else return an error.
LoadBackup(t ct.TestLike, recoveryKey string) error
// Log something to stdout and the underlying client log file
Logf(t ct.TestLike, format string, args ...interface{})
// The user for this client
Expand Down Expand Up @@ -147,6 +149,12 @@ func (c *LoggedClient) MustLoadBackup(t ct.TestLike, recoveryKey string) {
c.Client.MustLoadBackup(t, recoveryKey)
}

func (c *LoggedClient) LoadBackup(t ct.TestLike, recoveryKey string) error {
t.Helper()
c.Logf(t, "%s LoadBackup key=%s", c.logPrefix(), recoveryKey)
return c.Client.LoadBackup(t, recoveryKey)
}

func (c *LoggedClient) DeletePersistentStorage(t ct.TestLike) {
t.Helper()
c.Logf(t, "%s DeletePersistentStorage", c.logPrefix())
Expand Down
7 changes: 6 additions & 1 deletion internal/api/js/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,11 @@ func (c *JSClient) MustBackupKeys(t ct.TestLike) (recoveryKey string) {
}

func (c *JSClient) MustLoadBackup(t ct.TestLike, recoveryKey string) {
chrome.MustRunAsyncFn[chrome.Void](t, c.browser.Ctx, fmt.Sprintf(`
must.NotError(t, "failed to load backup", c.LoadBackup(t, recoveryKey))
}

func (c *JSClient) LoadBackup(t ct.TestLike, recoveryKey string) error {
_, err := chrome.RunAsyncFn[chrome.Void](t, c.browser.Ctx, fmt.Sprintf(`
// we assume the recovery key is the private key for the default key id so
// figure out what that key id is.
const keyId = await window.__client.secretStorage.getDefaultKeyId();
Expand All @@ -401,6 +405,7 @@ func (c *JSClient) MustLoadBackup(t ct.TestLike, recoveryKey string) {
console.log("key backup: ", JSON.stringify(keyBackupCheck));
await window.__client.restoreKeyBackupWithSecretStorage(keyBackupCheck ? keyBackupCheck.backupInfo : null, undefined, undefined);`,
recoveryKey))
return err
}

func (c *JSClient) WaitUntilEventInRoom(t ct.TestLike, roomID string, checker func(e api.Event) bool) api.Waiter {
Expand Down
7 changes: 6 additions & 1 deletion internal/api/rust/rust.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,14 @@ func (c *RustClient) MustBackupKeys(t ct.TestLike) (recoveryKey string) {
return recoveryKey
}

func (c *RustClient) LoadBackup(t ct.TestLike, recoveryKey string) error {
t.Helper()
return c.FFIClient.Encryption().Recover(recoveryKey)
}

func (c *RustClient) MustLoadBackup(t ct.TestLike, recoveryKey string) {
t.Helper()
must.NotError(t, "Recover", c.FFIClient.Encryption().Recover(recoveryKey))
c.LoadBackup(t, recoveryKey)
}

func (c *RustClient) WaitUntilEventInRoom(t ct.TestLike, roomID string, checker func(api.Event) bool) api.Waiter {
Expand Down
68 changes: 68 additions & 0 deletions tests/key_backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,71 @@ func TestCanBackupKeys(t *testing.T) {
must.Equal(t, ev.Text, body, "bob's new device failed to see the clear text message")
})
}

func TestBackupWrongRecoveryKeyFails(t *testing.T) {
ClientTypeMatrix(t, func(t *testing.T, clientTypeA, clientTypeB api.ClientType) {
if clientTypeA.HS != clientTypeB.HS {
t.Skipf("client A and B must be on the same HS as this is testing key backups so A=backup creator B=backup restorer")
return
}
t.Logf("backup creator = %s backup restorer = %s", clientTypeA.Lang, clientTypeB.Lang)
deployment := Deploy(t)
csapiAlice := deployment.Register(t, clientTypeA.HS, helpers.RegistrationOpts{
LocalpartSuffix: "alice",
Password: "complement-crypto-password",
})
roomID := csapiAlice.MustCreateRoom(t, map[string]interface{}{
"name": t.Name(),
"preset": "public_chat", // shared history visibility
"invite": []string{},
"initial_state": []map[string]interface{}{
{
"type": "m.room.encryption",
"state_key": "",
"content": map[string]interface{}{
"algorithm": "m.megolm.v1.aes-sha2",
},
},
},
})

// SDK testing below
// -----------------

backupCreator := LoginClientFromComplementClient(t, deployment, csapiAlice, clientTypeA)
defer backupCreator.Close(t)
stopSyncing := backupCreator.MustStartSyncing(t)
defer stopSyncing()

body := "An encrypted message"
waiter := backupCreator.WaitUntilEventInRoom(t, roomID, api.CheckEventHasBody(body))
evID := backupCreator.SendMessage(t, roomID, body)
t.Logf("backupCreator (%s) waiting for event %s", backupCreator.Type(), evID)
waiter.Wait(t, 5*time.Second)

// Now backupCreator backs up his keys. Some clients may automatically do this, but let's be explicit about it.
recoveryKey := backupCreator.MustBackupKeys(t)
t.Logf("recovery key -> %s", recoveryKey)

// Now login on a new device
csapiAlice2 := deployment.Login(t, clientTypeB.HS, csapiAlice, helpers.LoginOpts{
DeviceID: "BACKUP_RESTORER",
Password: "complement-crypto-password",
})
backupRestorer := LoginClientFromComplementClient(t, deployment, csapiAlice2, clientTypeB)
defer backupRestorer.Close(t)

// load the key backup using a valid but wrong recovery key
wrongRecoveryKey := "EsU1 591R iFs4 8xe6 kR79 7wKu 8XTG xdmx 9PVW 8pX9 LAnC Pe5r"
backupRestorer.LoadBackup(t, wrongRecoveryKey)

// new device cannot decrypt the encrypted message
backupRestorerStopSyncing := backupRestorer.MustStartSyncing(t)
defer backupRestorerStopSyncing()
time.Sleep(time.Second)
backupRestorer.MustBackpaginate(t, roomID, 5) // get the old message

ev := backupRestorer.MustGetEvent(t, roomID, evID)
must.Equal(t, ev.FailedToDecrypt, true, "bob's new device decrypted the event: insecure backup?")
})
}

0 comments on commit 260216b

Please sign in to comment.