client,crypto,appservice: add MSC3202 features

This commit is contained in:
Tulir Asokan 2024-08-28 22:06:43 +03:00
commit 238cacf2d5
6 changed files with 66 additions and 14 deletions

View file

@ -28,6 +28,7 @@ type Registration struct {
SoruEphemeralEvents bool `yaml:"de.sorunome.msc2409.push_ephemeral,omitempty" json:"de.sorunome.msc2409.push_ephemeral,omitempty"`
EphemeralEvents bool `yaml:"push_ephemeral,omitempty" json:"push_ephemeral,omitempty"`
MSC3202 bool `yaml:"org.matrix.msc3202,omitempty" json:"org.matrix.msc3202,omitempty"`
}
// CreateRegistration creates a Registration with random appservice and homeserver tokens.

View file

@ -110,6 +110,9 @@ type Client struct {
// Should the ?user_id= query parameter be set in requests?
// See https://spec.matrix.org/v1.6/application-service-api/#identity-assertion
SetAppServiceUserID bool
// Should the org.matrix.msc3202.device_id query parameter be set in requests?
// See https://github.com/matrix-org/matrix-spec-proposals/pull/3202
SetAppServiceDeviceID bool
syncingID uint32 // Identifies the current Sync. Only one Sync can be active at any given time.
}

View file

@ -38,6 +38,8 @@ type CryptoHelper struct {
LoginAs *mautrix.ReqLogin
ASEventProcessor crypto.ASEventProcessor
DBAccountID string
}
@ -58,7 +60,7 @@ func NewCryptoHelper(cli *mautrix.Client, pickleKey []byte, store any) (*CryptoH
return nil, fmt.Errorf("pickle key must be provided")
}
_, isExtensible := cli.Syncer.(mautrix.ExtensibleSyncer)
if !isExtensible {
if !cli.SetAppServiceDeviceID && !isExtensible {
return nil, fmt.Errorf("the client syncer must implement ExtensibleSyncer")
}
@ -111,7 +113,11 @@ func (helper *CryptoHelper) Init(ctx context.Context) error {
}
syncer, ok := helper.client.Syncer.(mautrix.ExtensibleSyncer)
if !ok {
return fmt.Errorf("the client syncer must implement ExtensibleSyncer")
if !helper.client.SetAppServiceDeviceID {
return fmt.Errorf("the client syncer must implement ExtensibleSyncer")
} else if helper.ASEventProcessor == nil {
return fmt.Errorf("an appservice must be provided when using appservice mode encryption")
}
}
var stateStore crypto.StateStore
@ -140,7 +146,27 @@ func (helper *CryptoHelper) Init(ctx context.Context) error {
if err != nil {
return fmt.Errorf("failed to find existing device ID: %w", err)
}
if helper.LoginAs != nil {
if helper.LoginAs != nil && helper.LoginAs.Type == mautrix.AuthTypeAppservice && helper.client.SetAppServiceDeviceID {
if storedDeviceID == "" {
helper.log.Debug().
Str("username", helper.LoginAs.Identifier.User).
Msg("Logging in with appservice")
var resp *mautrix.RespLogin
resp, err = helper.client.Login(ctx, helper.LoginAs)
if err != nil {
return err
}
managedCryptoStore.DeviceID = resp.DeviceID
helper.client.DeviceID = resp.DeviceID
} else {
helper.log.Debug().
Str("username", helper.LoginAs.Identifier.User).
Stringer("device_id", storedDeviceID).
Msg("Using existing device")
managedCryptoStore.DeviceID = storedDeviceID
helper.client.DeviceID = storedDeviceID
}
} else if helper.LoginAs != nil {
if storedDeviceID != "" {
helper.LoginAs.DeviceID = storedDeviceID
}
@ -177,16 +203,29 @@ func (helper *CryptoHelper) Init(ctx context.Context) error {
return err
}
syncer.OnSync(helper.mach.ProcessSyncResponse)
syncer.OnEventType(event.StateMember, helper.mach.HandleMemberEvent)
if _, ok = helper.client.Syncer.(mautrix.DispatchableSyncer); ok {
syncer.OnEventType(event.EventEncrypted, helper.HandleEncrypted)
if syncer != nil {
syncer.OnSync(helper.mach.ProcessSyncResponse)
syncer.OnEventType(event.StateMember, helper.mach.HandleMemberEvent)
if _, ok = helper.client.Syncer.(mautrix.DispatchableSyncer); ok {
syncer.OnEventType(event.EventEncrypted, helper.HandleEncrypted)
} else {
helper.log.Warn().Msg("Client syncer does not implement DispatchableSyncer. Events will not be decrypted automatically.")
}
if helper.managedStateStore != nil {
syncer.OnEvent(helper.client.StateStoreSyncHandler)
}
} else {
helper.log.Warn().Msg("Client syncer does not implement DispatchableSyncer. Events will not be decrypted automatically.")
helper.mach.AddAppserviceListener(helper.ASEventProcessor)
helper.ASEventProcessor.On(event.EventEncrypted, helper.HandleEncrypted)
}
if helper.managedStateStore != nil {
syncer.OnEvent(helper.client.StateStoreSyncHandler)
if helper.client.SetAppServiceDeviceID {
err = helper.mach.ShareKeys(ctx, -1)
if err != nil {
return fmt.Errorf("failed to share keys: %w", err)
}
}
return nil
}
@ -281,7 +320,11 @@ func (helper *CryptoHelper) HandleEncrypted(ctx context.Context, evt *event.Even
func (helper *CryptoHelper) postDecrypt(ctx context.Context, decrypted *event.Event) {
decrypted.Mautrix.EventSource |= event.SourceDecrypted
helper.client.Syncer.(mautrix.DispatchableSyncer).Dispatch(ctx, decrypted)
if helper.ASEventProcessor != nil {
helper.ASEventProcessor.Dispatch(ctx, decrypted)
} else {
helper.client.Syncer.(mautrix.DispatchableSyncer).Dispatch(ctx, decrypted)
}
}
func (helper *CryptoHelper) RequestSession(ctx context.Context, roomID id.RoomID, senderKey id.SenderKey, sessionID id.SessionID, userID id.UserID, deviceID id.DeviceID) {

View file

@ -208,13 +208,14 @@ func (mach *OlmMachine) OwnIdentity() *id.Device {
}
}
type asEventProcessor interface {
type ASEventProcessor interface {
On(evtType event.Type, handler func(ctx context.Context, evt *event.Event))
OnOTK(func(ctx context.Context, otk *mautrix.OTKCount))
OnDeviceList(func(ctx context.Context, lists *mautrix.DeviceLists, since string))
Dispatch(ctx context.Context, evt *event.Event)
}
func (mach *OlmMachine) AddAppserviceListener(ep asEventProcessor) {
func (mach *OlmMachine) AddAppserviceListener(ep ASEventProcessor) {
// ToDeviceForwardedRoomKey and ToDeviceRoomKey should only be present inside encrypted to-device events
ep.On(event.ToDeviceEncrypted, mach.HandleToDeviceEvent)
ep.On(event.ToDeviceRoomKeyRequest, mach.HandleToDeviceEvent)

View file

@ -225,7 +225,7 @@ func (otk *OneTimeKey) MarshalJSON() ([]byte, error) {
type ReqUploadKeys struct {
DeviceKeys *DeviceKeys `json:"device_keys,omitempty"`
OneTimeKeys map[id.KeyID]OneTimeKey `json:"one_time_keys"`
OneTimeKeys map[id.KeyID]OneTimeKey `json:"one_time_keys,omitempty"`
}
type ReqKeysSignatures struct {

4
url.go
View file

@ -102,6 +102,10 @@ func (cli *Client) BuildURLWithQuery(urlPath PrefixableURLPath, urlQuery map[str
if cli.SetAppServiceUserID {
query.Set("user_id", string(cli.UserID))
}
if cli.SetAppServiceDeviceID && cli.DeviceID != "" {
query.Set("device_id", string(cli.DeviceID))
query.Set("org.matrix.msc3202.device_id", string(cli.DeviceID))
}
if urlQuery != nil {
for k, v := range urlQuery {
query.Set(k, v)