77package routing
88
99import (
10+ "context"
1011 "net/http"
12+ "time"
13+
14+ "github.com/matrix-org/gomatrixserverlib/fclient"
15+ "github.com/sirupsen/logrus"
1116
1217 "github.com/element-hq/dendrite/clientapi/auth"
1318 "github.com/element-hq/dendrite/clientapi/auth/authtypes"
@@ -23,10 +28,15 @@ type crossSigningRequest struct {
2328 Auth newPasswordAuth `json:"auth"`
2429}
2530
31+ type UploadKeysAPI interface {
32+ QueryKeys (ctx context.Context , req * api.QueryKeysRequest , res * api.QueryKeysResponse )
33+ api.UploadDeviceKeysAPI
34+ }
35+
2636func UploadCrossSigningDeviceKeys (
27- req * http.Request , userInteractiveAuth * auth. UserInteractive ,
28- keyserverAPI api. ClientKeyAPI , device * api.Device ,
29- accountAPI api. ClientUserAPI , cfg * config.ClientAPI ,
37+ req * http.Request ,
38+ keyserverAPI UploadKeysAPI , device * api.Device ,
39+ accountAPI auth. GetAccountByPassword , cfg * config.ClientAPI ,
3040) util.JSONResponse {
3141 uploadReq := & crossSigningRequest {}
3242 uploadRes := & api.PerformUploadDeviceKeysResponse {}
@@ -35,32 +45,59 @@ func UploadCrossSigningDeviceKeys(
3545 if resErr != nil {
3646 return * resErr
3747 }
38- sessionID := uploadReq .Auth .Session
39- if sessionID == "" {
40- sessionID = util .RandomString (sessionIDLength )
41- }
42- if uploadReq .Auth .Type != authtypes .LoginTypePassword {
48+
49+ // Query existing keys to determine if UIA is required
50+ keyResp := api.QueryKeysResponse {}
51+ keyserverAPI .QueryKeys (req .Context (), & api.QueryKeysRequest {
52+ UserID : device .UserID ,
53+ UserToDevices : map [string ][]string {device .UserID : {device .ID }},
54+ Timeout : time .Second * 10 ,
55+ }, & keyResp )
56+
57+ if keyResp .Error != nil {
58+ logrus .WithError (keyResp .Error ).Error ("Failed to query keys" )
4359 return util.JSONResponse {
44- Code : http .StatusUnauthorized ,
45- JSON : newUserInteractiveResponse (
46- sessionID ,
47- []authtypes.Flow {
48- {
49- Stages : []authtypes.LoginType {authtypes .LoginTypePassword },
50- },
51- },
52- nil ,
53- ),
60+ Code : http .StatusBadRequest ,
61+ JSON : spec .Unknown (keyResp .Error .Error ()),
5462 }
5563 }
56- typePassword := auth.LoginTypePassword {
57- GetAccountByPassword : accountAPI .QueryAccountByPassword ,
58- Config : cfg ,
64+
65+ existingMasterKey , hasMasterKey := keyResp .MasterKeys [device .UserID ]
66+ requireUIA := false
67+ if hasMasterKey {
68+ // If we have a master key, check if any of the existing keys differ. If they do,
69+ // we need to re-authenticate the user.
70+ requireUIA = keysDiffer (existingMasterKey , keyResp , uploadReq , device .UserID )
5971 }
60- if _ , authErr := typePassword .Login (req .Context (), & uploadReq .Auth .PasswordRequest ); authErr != nil {
61- return * authErr
72+
73+ if requireUIA {
74+ sessionID := uploadReq .Auth .Session
75+ if sessionID == "" {
76+ sessionID = util .RandomString (sessionIDLength )
77+ }
78+ if uploadReq .Auth .Type != authtypes .LoginTypePassword {
79+ return util.JSONResponse {
80+ Code : http .StatusUnauthorized ,
81+ JSON : newUserInteractiveResponse (
82+ sessionID ,
83+ []authtypes.Flow {
84+ {
85+ Stages : []authtypes.LoginType {authtypes .LoginTypePassword },
86+ },
87+ },
88+ nil ,
89+ ),
90+ }
91+ }
92+ typePassword := auth.LoginTypePassword {
93+ GetAccountByPassword : accountAPI ,
94+ Config : cfg ,
95+ }
96+ if _ , authErr := typePassword .Login (req .Context (), & uploadReq .Auth .PasswordRequest ); authErr != nil {
97+ return * authErr
98+ }
99+ sessions .addCompletedSessionStage (sessionID , authtypes .LoginTypePassword )
62100 }
63- sessions .addCompletedSessionStage (sessionID , authtypes .LoginTypePassword )
64101
65102 uploadReq .UserID = device .UserID
66103 keyserverAPI .PerformUploadDeviceKeys (req .Context (), & uploadReq .PerformUploadDeviceKeysRequest , uploadRes )
@@ -96,6 +133,21 @@ func UploadCrossSigningDeviceKeys(
96133 }
97134}
98135
136+ func keysDiffer (existingMasterKey fclient.CrossSigningKey , keyResp api.QueryKeysResponse , uploadReq * crossSigningRequest , userID string ) bool {
137+ masterKeyEqual := existingMasterKey .Equal (& uploadReq .MasterKey )
138+ if ! masterKeyEqual {
139+ return true
140+ }
141+ existingSelfSigningKey := keyResp .SelfSigningKeys [userID ]
142+ selfSigningEqual := existingSelfSigningKey .Equal (& uploadReq .SelfSigningKey )
143+ if ! selfSigningEqual {
144+ return true
145+ }
146+ existingUserSigningKey := keyResp .UserSigningKeys [userID ]
147+ userSigningEqual := existingUserSigningKey .Equal (& uploadReq .UserSigningKey )
148+ return ! userSigningEqual
149+ }
150+
99151func UploadCrossSigningDeviceSignatures (req * http.Request , keyserverAPI api.ClientKeyAPI , device * api.Device ) util.JSONResponse {
100152 uploadReq := & api.PerformUploadDeviceSignaturesRequest {}
101153 uploadRes := & api.PerformUploadDeviceSignaturesResponse {}
0 commit comments