diff --git a/clientapi/routing/membership.go b/clientapi/routing/membership.go index 03e85edbf..d74b3b52b 100644 --- a/clientapi/routing/membership.go +++ b/clientapi/routing/membership.go @@ -419,11 +419,6 @@ func buildMembershipEvent( return nil, err } - identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) - if err != nil { - return nil, err - } - userID, err := spec.NewUserID(device.UserID, true) if err != nil { return nil, err @@ -441,6 +436,17 @@ func buildMembershipEvent( if err != nil { return nil, err } + + validRoomID, err := spec.NewRoomID(roomID) + if err != nil { + return nil, err + } + + identity, err := rsAPI.SigningIdentityFor(ctx, *validRoomID, *userID) + if err != nil { + return nil, err + } + return buildMembershipEventDirect(ctx, targetSenderID, reason, profile.DisplayName, profile.AvatarURL, senderID, device.UserDomain(), membership, roomID, isDirect, identity.KeyID, identity.PrivateKey, evTime, rsAPI) } diff --git a/clientapi/routing/profile.go b/clientapi/routing/profile.go index e734e2e4f..bda918384 100644 --- a/clientapi/routing/profile.go +++ b/clientapi/routing/profile.go @@ -151,7 +151,7 @@ func SetAvatarURL( } } - response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, cfg, evTime) + response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, evTime) if err != nil { return response } @@ -246,7 +246,7 @@ func SetDisplayName( } } - response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, cfg, evTime) + response, err := updateProfile(req.Context(), rsAPI, device, profile, userID, evTime) if err != nil { return response } @@ -260,7 +260,7 @@ func SetDisplayName( func updateProfile( ctx context.Context, rsAPI api.ClientRoomserverAPI, device *userapi.Device, profile *authtypes.Profile, - userID string, cfg *config.ClientAPI, evTime time.Time, + userID string, evTime time.Time, ) (util.JSONResponse, error) { var res api.QueryRoomsForUserResponse err := rsAPI.QueryRoomsForUser(ctx, &api.QueryRoomsForUserRequest{ @@ -285,7 +285,7 @@ func updateProfile( } events, err := buildMembershipEvents( - ctx, device, res.RoomIDs, *profile, userID, cfg, evTime, rsAPI, + ctx, res.RoomIDs, *profile, userID, evTime, rsAPI, ) switch e := err.(type) { case nil: @@ -356,9 +356,8 @@ func getProfile( func buildMembershipEvents( ctx context.Context, - device *userapi.Device, roomIDs []string, - newProfile authtypes.Profile, userID string, cfg *config.ClientAPI, + newProfile authtypes.Profile, userID string, evTime time.Time, rsAPI api.ClientRoomserverAPI, ) ([]*types.HeaderedEvent, error) { evs := []*types.HeaderedEvent{} @@ -391,12 +390,22 @@ func buildMembershipEvents( return nil, err } - identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + validRoomID, err := spec.NewRoomID(roomID) if err != nil { return nil, err } - event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, evTime, rsAPI, nil) + user, err := spec.NewUserID(userID, true) + if err != nil { + return nil, err + } + + identity, err := rsAPI.SigningIdentityFor(ctx, *validRoomID, *user) + if err != nil { + return nil, err + } + + event, err := eventutil.QueryAndBuildEvent(ctx, &proto, &identity, evTime, rsAPI, nil) if err != nil { return nil, err } diff --git a/clientapi/routing/redaction.go b/clientapi/routing/redaction.go index da48e84de..ce4c17c9f 100644 --- a/clientapi/routing/redaction.go +++ b/clientapi/routing/redaction.go @@ -143,7 +143,15 @@ func SendRedaction( } } - identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + validRoomID, err := spec.NewRoomID(roomID) + if err != nil { + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{}, + } + } + + identity, err := rsAPI.SigningIdentityFor(req.Context(), *validRoomID, *deviceUserID) if err != nil { return util.JSONResponse{ Code: http.StatusInternalServerError, @@ -152,7 +160,7 @@ func SendRedaction( } var queryRes roomserverAPI.QueryLatestEventsAndStateResponse - e, err := eventutil.QueryAndBuildEvent(req.Context(), &proto, identity, time.Now(), rsAPI, &queryRes) + e, err := eventutil.QueryAndBuildEvent(req.Context(), &proto, &identity, time.Now(), rsAPI, &queryRes) if errors.Is(err, eventutil.ErrRoomNoExists{}) { return util.JSONResponse{ Code: http.StatusNotFound, diff --git a/clientapi/routing/sendevent.go b/clientapi/routing/sendevent.go index 4d0a9f24a..c6e8fbde8 100644 --- a/clientapi/routing/sendevent.go +++ b/clientapi/routing/sendevent.go @@ -129,7 +129,7 @@ func SendEvent( } } - e, resErr := generateSendEvent(req.Context(), r, device, roomID, eventType, stateKey, cfg, rsAPI, evTime) + e, resErr := generateSendEvent(req.Context(), r, device, roomID, eventType, stateKey, rsAPI, evTime) if resErr != nil { return *resErr } @@ -261,7 +261,6 @@ func generateSendEvent( r map[string]interface{}, device *userapi.Device, roomID, eventType string, stateKey *string, - cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI, evTime time.Time, ) (gomatrixserverlib.PDU, *util.JSONResponse) { @@ -297,7 +296,15 @@ func generateSendEvent( } } - identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + validRoomID, err := spec.NewRoomID(roomID) + if err != nil { + return nil, &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{}, + } + } + + identity, err := rsAPI.SigningIdentityFor(ctx, *validRoomID, *fullUserID) if err != nil { return nil, &util.JSONResponse{ Code: http.StatusInternalServerError, @@ -306,7 +313,7 @@ func generateSendEvent( } var queryRes api.QueryLatestEventsAndStateResponse - e, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, evTime, rsAPI, &queryRes) + e, err := eventutil.QueryAndBuildEvent(ctx, &proto, &identity, evTime, rsAPI, &queryRes) switch specificErr := err.(type) { case nil: case eventutil.ErrRoomNoExists: diff --git a/clientapi/routing/server_notices.go b/clientapi/routing/server_notices.go index 7006ced46..66258a68a 100644 --- a/clientapi/routing/server_notices.go +++ b/clientapi/routing/server_notices.go @@ -221,7 +221,7 @@ func SendServerNotice( "body": r.Content.Body, "msgtype": r.Content.MsgType, } - e, resErr := generateSendEvent(ctx, request, senderDevice, roomID, "m.room.message", nil, cfgClient, rsAPI, time.Now()) + e, resErr := generateSendEvent(ctx, request, senderDevice, roomID, "m.room.message", nil, rsAPI, time.Now()) if resErr != nil { logrus.Errorf("failed to send message: %+v", resErr) return *resErr @@ -350,7 +350,7 @@ func getSenderDevice( if len(deviceRes.Devices) > 0 { // If there were changes to the profile, create a new membership event if displayNameChanged || avatarChanged { - _, err = updateProfile(ctx, rsAPI, &deviceRes.Devices[0], profile, accRes.Account.UserID, cfg, time.Now()) + _, err = updateProfile(ctx, rsAPI, &deviceRes.Devices[0], profile, accRes.Account.UserID, time.Now()) if err != nil { return nil, err } diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index d14801921..2a6d85477 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -64,7 +64,13 @@ func MakeJoin( } createJoinTemplate := func(proto *gomatrixserverlib.ProtoEvent) (gomatrixserverlib.PDU, []gomatrixserverlib.PDU, error) { - identity, signErr := cfg.Matrix.SigningIdentityFor(request.Destination()) + // TODO: remove this once the join dance understands pseudo IDs + var dummyUserID *spec.UserID + dummyUserID, err = spec.NewUserID(fmt.Sprintf("@dummy:%s", request.Destination()), true) + if err != nil { + return nil, nil, err + } + identity, signErr := rsAPI.SigningIdentityFor(httpReq.Context(), roomID, *dummyUserID) if signErr != nil { util.GetLogger(httpReq.Context()).WithError(signErr).Errorf("obtaining signing identity for %s failed", request.Destination()) return nil, nil, spec.NotFound(fmt.Sprintf("Server name %q does not exist", request.Destination())) @@ -73,7 +79,7 @@ func MakeJoin( queryRes := api.QueryLatestEventsAndStateResponse{ RoomVersion: roomVersion, } - event, signErr := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, identity, time.Now(), rsAPI, &queryRes) + event, signErr := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, &identity, time.Now(), rsAPI, &queryRes) switch e := signErr.(type) { case nil: case eventutil.ErrRoomNoExists: diff --git a/federationapi/routing/leave.go b/federationapi/routing/leave.go index 716276bec..fdcd5819c 100644 --- a/federationapi/routing/leave.go +++ b/federationapi/routing/leave.go @@ -59,14 +59,20 @@ func MakeLeave( } createLeaveTemplate := func(proto *gomatrixserverlib.ProtoEvent) (gomatrixserverlib.PDU, []gomatrixserverlib.PDU, error) { - identity, signErr := cfg.Matrix.SigningIdentityFor(request.Destination()) + // TODO: remove this once the leave dance understands pseudo IDs + var dummyUserID *spec.UserID + dummyUserID, err = spec.NewUserID(fmt.Sprintf("@dummy:%s", request.Destination()), true) + if err != nil { + return nil, nil, err + } + identity, signErr := rsAPI.SigningIdentityFor(httpReq.Context(), roomID, *dummyUserID) if signErr != nil { util.GetLogger(httpReq.Context()).WithError(signErr).Errorf("obtaining signing identity for %s failed", request.Destination()) return nil, nil, spec.NotFound(fmt.Sprintf("Server name %q does not exist", request.Destination())) } queryRes := api.QueryLatestEventsAndStateResponse{} - event, buildErr := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, identity, time.Now(), rsAPI, &queryRes) + event, buildErr := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, &identity, time.Now(), rsAPI, &queryRes) switch e := buildErr.(type) { case nil: case eventutil.ErrRoomNoExists: diff --git a/roomserver/api/api.go b/roomserver/api/api.go index fec28841e..99a68efae 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -5,6 +5,7 @@ import ( "crypto/ed25519" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/matrix-org/util" @@ -210,6 +211,7 @@ type ClientRoomserverAPI interface { PerformForget(ctx context.Context, req *PerformForgetRequest, resp *PerformForgetResponse) error SetRoomAlias(ctx context.Context, req *SetRoomAliasRequest, res *SetRoomAliasResponse) error RemoveRoomAlias(ctx context.Context, req *RemoveRoomAliasRequest, res *RemoveRoomAliasResponse) error + SigningIdentityFor(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error) } type UserRoomserverAPI interface { @@ -228,7 +230,7 @@ type FederationRoomserverAPI interface { QueryLatestEventsAndStateAPI QueryBulkStateContentAPI QuerySenderIDAPI - + SigningIdentityFor(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error) // QueryServerBannedFromRoom returns whether a server is banned from a room by server ACLs. QueryServerBannedFromRoom(ctx context.Context, req *QueryServerBannedFromRoomRequest, res *QueryServerBannedFromRoomResponse) error QueryMembershipForUser(ctx context.Context, req *QueryMembershipForUserRequest, res *QueryMembershipForUserResponse) error diff --git a/roomserver/internal/alias.go b/roomserver/internal/alias.go index c950024ad..41de52fc2 100644 --- a/roomserver/internal/alias.go +++ b/roomserver/internal/alias.go @@ -114,6 +114,7 @@ func (r *RoomserverInternalAPI) GetAliasesForRoomID( } // RemoveRoomAlias implements alias.RoomserverInternalAPI +// nolint: gocyclo func (r *RoomserverInternalAPI) RemoveRoomAlias( ctx context.Context, request *api.RemoveRoomAliasRequest, @@ -182,9 +183,11 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias( return err } - senderDomain := sender.Domain() - - identity, err := r.Cfg.Global.SigningIdentityFor(senderDomain) + validRoomID, err := spec.NewRoomID(roomID) + if err != nil { + return err + } + identity, err := r.SigningIdentityFor(ctx, *validRoomID, *sender) if err != nil { return err } @@ -210,7 +213,7 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias( return err } - newEvent, err := eventutil.BuildEvent(ctx, proto, identity, time.Now(), &eventsNeeded, stateRes) + newEvent, err := eventutil.BuildEvent(ctx, proto, &identity, time.Now(), &eventsNeeded, stateRes) if err != nil { return err } diff --git a/roomserver/internal/api.go b/roomserver/internal/api.go index 4bcd3f3ed..97b35764f 100644 --- a/roomserver/internal/api.go +++ b/roomserver/internal/api.go @@ -6,6 +6,7 @@ import ( "github.com/getsentry/sentry-go" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/matrix-org/util" "github.com/nats-io/nats.go" @@ -110,11 +111,6 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio r.fsAPI = fsAPI r.KeyRing = keyRing - identity, err := r.Cfg.Global.SigningIdentityFor(r.ServerName) - if err != nil { - logrus.Panic(err) - } - r.Inputer = &input.Inputer{ Cfg: &r.Cfg.RoomServer, ProcessContext: r.ProcessContext, @@ -125,7 +121,7 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio NATSClient: r.NATSClient, Durable: nats.Durable(r.Durable), ServerName: r.ServerName, - SigningIdentity: identity, + SigningIdentity: r.SigningIdentityFor, FSAPI: fsAPI, KeyRing: keyRing, ACLs: r.ServerACLs, @@ -291,3 +287,34 @@ func (r *RoomserverInternalAPI) GetOrCreateUserRoomPrivateKey(ctx context.Contex } return key, nil } + +func (r *RoomserverInternalAPI) SigningIdentityFor(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error) { + roomVersion, ok := r.Cache.GetRoomVersion(roomID.String()) + if !ok { + roomInfo, err := r.DB.RoomInfo(ctx, roomID.String()) + if err != nil { + return fclient.SigningIdentity{}, err + } + if roomInfo != nil { + roomVersion = roomInfo.RoomVersion + } + } + if roomVersion == gomatrixserverlib.RoomVersionPseudoIDs { + privKey, err := r.GetOrCreateUserRoomPrivateKey(ctx, senderID, roomID) + if err != nil { + return fclient.SigningIdentity{}, err + } + logrus.Infof("XXX: using user signing key") + return fclient.SigningIdentity{ + PrivateKey: privKey, + KeyID: "ed25519", + ServerName: "self", + }, nil + } + logrus.Infof("XXX: using config signing key") + identity, err := r.Cfg.Global.SigningIdentityFor(senderID.Domain()) + if err != nil { + return fclient.SigningIdentity{}, err + } + return *identity, err +} diff --git a/roomserver/internal/input/input.go b/roomserver/internal/input/input.go index 3db2d0a67..dea8f8c87 100644 --- a/roomserver/internal/input/input.go +++ b/roomserver/internal/input/input.go @@ -81,7 +81,7 @@ type Inputer struct { JetStream nats.JetStreamContext Durable nats.SubOpt ServerName spec.ServerName - SigningIdentity *fclient.SigningIdentity + SigningIdentity func(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error) FSAPI fedapi.RoomserverFederationAPI KeyRing gomatrixserverlib.JSONVerifier ACLs *acls.ServerACLs diff --git a/roomserver/internal/input/input_events.go b/roomserver/internal/input/input_events.go index 7bb401632..8ee32b3c2 100644 --- a/roomserver/internal/input/input_events.go +++ b/roomserver/internal/input/input_events.go @@ -886,7 +886,22 @@ func (r *Inputer) kickGuests(ctx context.Context, event gomatrixserverlib.PDU, r return err } - event, err := eventutil.BuildEvent(ctx, fledglingEvent, r.SigningIdentity, time.Now(), &eventsNeeded, latestRes) + validRoomID, err := spec.NewRoomID(event.RoomID()) + if err != nil { + return err + } + + userID, err := spec.NewUserID(stateKey, true) + if err != nil { + return err + } + + signingIdentity, err := r.SigningIdentity(ctx, *validRoomID, *userID) + if err != nil { + return err + } + + event, err := eventutil.BuildEvent(ctx, fledglingEvent, &signingIdentity, time.Now(), &eventsNeeded, latestRes) if err != nil { return err } diff --git a/roomserver/internal/perform/perform_create_room.go b/roomserver/internal/perform/perform_create_room.go index 02ea6edd5..2c08f363a 100644 --- a/roomserver/internal/perform/perform_create_room.go +++ b/roomserver/internal/perform/perform_create_room.go @@ -150,7 +150,7 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo } // get the signing identity - identity, err := c.Cfg.Matrix.SigningIdentityFor(userID.Domain()) + identity, err := c.Cfg.Matrix.SigningIdentityFor(userID.Domain()) // we MUST use the server signing mxid_mapping if err != nil { logrus.WithError(err).WithField("domain", userID.Domain()).Error("unable to find signing identity for domain") return "", &util.JSONResponse{ @@ -187,8 +187,8 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo // sign all events with the pseudo ID key identity = &fclient.SigningIdentity{ - ServerName: userID.Domain(), - KeyID: "self", + ServerName: "self", + KeyID: "ed25519", PrivateKey: pseudoIDKey, } } @@ -404,6 +404,7 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo inputs := make([]api.InputRoomEvent, 0, len(builtEvents)) for _, event := range builtEvents { + logrus.Infof("XXX: built event: %s", string(event.JSON())) inputs = append(inputs, api.InputRoomEvent{ Kind: api.KindNew, Event: event, diff --git a/roomserver/internal/perform/perform_join.go b/roomserver/internal/perform/perform_join.go index b99cc56e8..63c8ced85 100644 --- a/roomserver/internal/perform/perform_join.go +++ b/roomserver/internal/perform/perform_join.go @@ -285,7 +285,7 @@ func (r *Joiner) performJoinRoomByID( // but everyone has since left. I suspect it does the wrong thing. var buildRes rsAPI.QueryLatestEventsAndStateResponse - identity, err := r.Cfg.Matrix.SigningIdentityFor(userDomain) + identity, err := r.RSAPI.SigningIdentityFor(ctx, *roomID, *userID) if err != nil { return "", "", fmt.Errorf("error joining local room: %q", err) } @@ -311,9 +311,9 @@ func (r *Joiner) performJoinRoomByID( req.Content["mxid_mapping"] = mapping // sign the event with the pseudo ID key - identity = &fclient.SigningIdentity{ - ServerName: userID.Domain(), - KeyID: "self", + identity = fclient.SigningIdentity{ + ServerName: "self", + KeyID: "ed25519", PrivateKey: pseudoIDKey, } } @@ -322,7 +322,7 @@ func (r *Joiner) performJoinRoomByID( return "", "", fmt.Errorf("eb.SetContent: %w", err) } - event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, time.Now(), r.RSAPI, &buildRes) + event, err := eventutil.QueryAndBuildEvent(ctx, &proto, &identity, time.Now(), r.RSAPI, &buildRes) switch err.(type) { case nil: diff --git a/roomserver/internal/perform/perform_leave.go b/roomserver/internal/perform/perform_leave.go index 1b23cc1ff..2c4da2606 100644 --- a/roomserver/internal/perform/perform_leave.go +++ b/roomserver/internal/perform/perform_leave.go @@ -173,12 +173,17 @@ func (r *Leaver) performLeaveRoomByID( // TODO: Check what happens if the room exists on the server // but everyone has since left. I suspect it does the wrong thing. + validRoomID, err := spec.NewRoomID(req.RoomID) + if err != nil { + return nil, err + } + var buildRes rsAPI.QueryLatestEventsAndStateResponse - identity, err := r.Cfg.Matrix.SigningIdentityFor(req.Leaver.Domain()) + identity, err := r.RSAPI.SigningIdentityFor(ctx, *validRoomID, req.Leaver) if err != nil { return nil, fmt.Errorf("SigningIdentityFor: %w", err) } - event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, time.Now(), r.RSAPI, &buildRes) + event, err := eventutil.QueryAndBuildEvent(ctx, &proto, &identity, time.Now(), r.RSAPI, &buildRes) if err != nil { return nil, fmt.Errorf("eventutil.QueryAndBuildEvent: %w", err) }