Skip to content

Commit 0d36460

Browse files
authored
Add GetEventShield to language bindings (#182)
Support for checking the event shield from tests.
1 parent 68d4425 commit 0d36460

5 files changed

Lines changed: 181 additions & 0 deletions

File tree

internal/api/client.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ type Client interface {
5555
Backpaginate(t ct.TestLike, roomID string, count int) error
5656
// GetEvent will return the client's view of this event, or returns an error if the event cannot be found.
5757
GetEvent(t ct.TestLike, roomID, eventID string) (*Event, error)
58+
// GetEventShield will return the "shield state" for this event, or `nil` if the event should have no shield, or an error if the event cannot be found.
59+
GetEventShield(t ct.TestLike, roomID, eventID string) (*EventShield, error)
5860
// BackupKeys will backup E2EE keys, else return an error.
5961
BackupKeys(t ct.TestLike) (recoveryKey string, err error)
6062
// LoadBackup will recover E2EE keys from the latest backup, else return an error.
@@ -366,6 +368,11 @@ type Event struct {
366368
FailedToDecrypt bool
367369
}
368370

371+
type EventShield struct {
372+
Colour string // "Red" or "Grey"
373+
Code string // "VerificationViolation" or similar
374+
}
375+
369376
type Waiter interface {
370377
// Wait for something to happen, up until the timeout s. If nothing happens,
371378
// fail the test with the formatted string provided.

internal/api/js/js.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,102 @@ func (c *JSClient) GetEvent(t ct.TestLike, roomID, eventID string) (*api.Event,
551551
return ev, nil
552552
}
553553

554+
func (c *JSClient) GetEventShield(t ct.TestLike, roomID, eventID string) (*api.EventShield, error) {
555+
t.Helper()
556+
// Serialized output:
557+
//
558+
// {
559+
// shieldColour: 0 | 1 | 2, // NONE | GREY | RED
560+
// shieldReason: 0 ... 7
561+
// }
562+
encryptionInfoSerialised, err := chrome.RunAsyncFn[string](t, c.browser.Ctx, fmt.Sprintf(`
563+
const ev = window.__client.getRoom("%s")?.getLiveTimeline()?.getEvents().filter((ev, i) => {
564+
return ev.getId() === "%s";
565+
})[0];
566+
const encryptionInfo = await window.__client.getCrypto().getEncryptionInfoForEvent(ev);
567+
return JSON.stringify(encryptionInfo);
568+
`, roomID, eventID))
569+
if err != nil {
570+
return nil, fmt.Errorf("failed to get shield for event %s: %s", eventID, err)
571+
}
572+
573+
var encryptionInfo struct {
574+
ShieldColour uint `json:"shieldColour"`
575+
ShieldReason uint `json:"shieldReason"`
576+
}
577+
578+
if err := json.Unmarshal([]byte(*encryptionInfoSerialised), &encryptionInfo); err != nil {
579+
return nil, fmt.Errorf("failed to unmarshal encryption info: %s", err)
580+
}
581+
582+
if encryptionInfo.ShieldColour == 0 {
583+
// No shield
584+
return nil, nil
585+
}
586+
587+
var eventShield api.EventShield
588+
switch encryptionInfo.ShieldColour {
589+
case 1:
590+
eventShield.Colour = "grey"
591+
case 2:
592+
eventShield.Colour = "red"
593+
default:
594+
return nil, fmt.Errorf("unknown shield colour: %d", encryptionInfo.ShieldColour)
595+
}
596+
597+
switch encryptionInfo.ShieldReason {
598+
case 0:
599+
/** An unknown reason from the crypto library (if you see this, it is a bug in matrix-js-sdk). */
600+
eventShield.Code = "Unknown"
601+
602+
case 1:
603+
/** "Encrypted by an unverified user." */
604+
eventShield.Code = "UnverifiedIdentity"
605+
606+
case 2:
607+
/** "Encrypted by a device not verified by its owner." */
608+
eventShield.Code = "UnsignedDevice"
609+
610+
case 3:
611+
/** "Encrypted by an unknown or deleted device." */
612+
eventShield.Code = "UnknownDevice"
613+
614+
case 4:
615+
/**
616+
* "The authenticity of this encrypted message can't be guaranteed on this device."
617+
*
618+
* i.e.: the key has been forwarded, or retrieved from an insecure backup.
619+
*/
620+
eventShield.Code = "AuthenticityNotGuaranteed"
621+
622+
case 5:
623+
/**
624+
* The (deprecated) sender_key field in the event does not match the Ed25519 key of the device that sent us the
625+
* decryption keys.
626+
*
627+
* No longer used with rust crypto stack, since it doesn't check the sender_key field.
628+
*/
629+
eventShield.Code = "MismatchedSenderKey"
630+
631+
case 6:
632+
/**
633+
* The event was sent unencrypted in an encrypted room.
634+
*/
635+
eventShield.Code = "SentInClear"
636+
637+
case 7:
638+
/**
639+
* The sender was previously verified but changed their identity.
640+
*/
641+
eventShield.Code = "VerificationViolation"
642+
643+
default:
644+
return nil, fmt.Errorf("unknown shield reason code: %d", encryptionInfo.ShieldReason)
645+
}
646+
647+
return &eventShield, nil
648+
}
649+
554650
// StartSyncing to begin syncing from sync v2 / sliding sync.
555651
// Tests should call stopSyncing() at the end of the test.
556652
func (c *JSClient) StartSyncing(t ct.TestLike) (stopSyncing func(), err error) {

internal/api/rust/rust.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package rust
22

33
import (
44
"fmt"
5+
"github.com/matrix-org/complement-crypto/internal/api/rust/matrix_sdk_common"
6+
"log"
57
"os"
68
"path/filepath"
79
"strings"
@@ -339,6 +341,60 @@ func (c *RustClient) GetEvent(t ct.TestLike, roomID, eventID string) (*api.Event
339341
return ev, nil
340342
}
341343

344+
func (c *RustClient) GetEventShield(t ct.TestLike, roomID, eventID string) (*api.EventShield, error) {
345+
t.Helper()
346+
room := c.findRoom(t, roomID)
347+
timelineItem, err := mustGetTimeline(t, room).GetEventTimelineItemByEventId(eventID)
348+
if err != nil {
349+
return nil, fmt.Errorf("failed to GetEventTimelineItemByEventId(%s): %s", eventID, err)
350+
}
351+
shieldState := timelineItem.LazyProvider.GetShields(false)
352+
353+
codeToString := func(code matrix_sdk_common.ShieldStateCode) string {
354+
var result string
355+
switch code {
356+
case matrix_sdk_common.ShieldStateCodeAuthenticityNotGuaranteed:
357+
result = "AuthenticityNotGuaranteed"
358+
case matrix_sdk_common.ShieldStateCodeUnknownDevice:
359+
result = "UnknownDevice"
360+
case matrix_sdk_common.ShieldStateCodeUnsignedDevice:
361+
result = "UnsignedDevice"
362+
case matrix_sdk_common.ShieldStateCodeUnverifiedIdentity:
363+
result = "UnverifiedIdentity"
364+
case matrix_sdk_common.ShieldStateCodeSentInClear:
365+
result = "SentInClear"
366+
case matrix_sdk_common.ShieldStateCodeVerificationViolation:
367+
result = "VerificationViolation"
368+
default:
369+
log.Panicf("Unknown shield code %d", code)
370+
}
371+
return result
372+
}
373+
374+
var eventShield *api.EventShield
375+
376+
if shieldState != nil {
377+
shield := *shieldState
378+
switch shield.(type) {
379+
case matrix_sdk_ffi.ShieldStateNone:
380+
// no-op
381+
382+
case matrix_sdk_ffi.ShieldStateGrey:
383+
eventShield = &api.EventShield{
384+
Colour: "grey",
385+
Code: codeToString(shield.(matrix_sdk_ffi.ShieldStateGrey).Code),
386+
}
387+
388+
case matrix_sdk_ffi.ShieldStateRed:
389+
eventShield = &api.EventShield{
390+
Colour: "red",
391+
Code: codeToString(shield.(matrix_sdk_ffi.ShieldStateRed).Code),
392+
}
393+
}
394+
}
395+
return eventShield, nil
396+
}
397+
342398
// StartSyncing to begin syncing from sync v2 / sliding sync.
343399
// Tests should call stopSyncing() at the end of the test.
344400
func (c *RustClient) StartSyncing(t ct.TestLike) (stopSyncing func(), err error) {

internal/deploy/rpc/client.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,17 @@ func (c *RPCClient) GetEvent(t ct.TestLike, roomID, eventID string) (*api.Event,
294294
return &ev, err
295295
}
296296

297+
// GetEventShield will return the "shield state" for this event, or `nil` if the event should have no shield, or an error if the event cannot be found.
298+
func (c *RPCClient) GetEventShield(t ct.TestLike, roomID, eventID string) (*api.EventShield, error) {
299+
var shield *api.EventShield
300+
err := c.client.Call("Server.GetEventShield", RPCGetEvent{
301+
TestName: t.Name(),
302+
RoomID: roomID,
303+
EventID: eventID,
304+
}, &shield)
305+
return shield, err
306+
}
307+
297308
// BackupKeys will backup E2EE keys, else return an error.
298309
func (c *RPCClient) BackupKeys(t ct.TestLike) (recoveryKey string, err error) {
299310
err = c.client.Call("Server.BackupKeys", 0, &recoveryKey)

internal/deploy/rpc/server.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,17 @@ func (s *Server) GetEvent(input RPCGetEvent, output *api.Event) error {
269269
return nil
270270
}
271271

272+
// GetEventShield will return the "shield state" for this event, or `nil` if the event should have no shield, or an error if the event cannot be found.
273+
func (s *Server) GetEventShield(input RPCGetEvent, output **api.EventShield) error {
274+
defer s.keepAlive()
275+
shield, err := s.activeClient.GetEventShield(&api.MockT{TestName: input.TestName}, input.RoomID, input.EventID)
276+
if err != nil {
277+
return err
278+
}
279+
*output = shield
280+
return nil
281+
}
282+
272283
// BackupKeys will backup E2EE keys, else fail the test.
273284
func (s *Server) BackupKeys(testName string, recoveryKey *string) error {
274285
defer s.keepAlive()

0 commit comments

Comments
 (0)