mas: cross signing fixes after merge

This commit is contained in:
Roman Isaev 2025-01-17 02:56:17 +00:00
parent 6833e99558
commit b44f899637
No known key found for this signature in database
GPG key ID: 7BE2B6A6C89AEC7F

View file

@ -9,10 +9,9 @@ package routing
import ( import (
"context" "context"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/sirupsen/logrus"
"github.com/element-hq/dendrite/clientapi/auth" "github.com/element-hq/dendrite/clientapi/auth"
"github.com/element-hq/dendrite/clientapi/auth/authtypes" "github.com/element-hq/dendrite/clientapi/auth/authtypes"
"github.com/element-hq/dendrite/clientapi/httputil" "github.com/element-hq/dendrite/clientapi/httputil"
@ -32,6 +31,7 @@ type crossSigningRequest struct {
type UploadKeysAPI interface { type UploadKeysAPI interface {
QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse)
QueryMasterKeys(ctx context.Context, req *api.QueryMasterKeysRequest, res *api.QueryMasterKeysResponse)
api.UploadDeviceKeysAPI api.UploadDeviceKeysAPI
} }
@ -40,6 +40,7 @@ func UploadCrossSigningDeviceKeys(
keyserverAPI UploadKeysAPI, device *api.Device, keyserverAPI UploadKeysAPI, device *api.Device,
accountAPI auth.GetAccountByPassword, cfg *config.ClientAPI, accountAPI auth.GetAccountByPassword, cfg *config.ClientAPI,
) util.JSONResponse { ) util.JSONResponse {
logger := util.GetLogger(req.Context())
uploadReq := &crossSigningRequest{} uploadReq := &crossSigningRequest{}
uploadRes := &api.PerformUploadDeviceKeysResponse{} uploadRes := &api.PerformUploadDeviceKeysResponse{}
@ -48,6 +49,11 @@ func UploadCrossSigningDeviceKeys(
return *resErr return *resErr
} }
sessionID := uploadReq.Auth.Session
if sessionID == "" {
sessionID = util.RandomString(sessionIDLength)
}
// Query existing keys to determine if UIA is required // Query existing keys to determine if UIA is required
keyResp := api.QueryKeysResponse{} keyResp := api.QueryKeysResponse{}
keyserverAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{ keyserverAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
@ -57,26 +63,68 @@ func UploadCrossSigningDeviceKeys(
}, &keyResp) }, &keyResp)
if keyResp.Error != nil { if keyResp.Error != nil {
logrus.WithError(keyResp.Error).Error("Failed to query keys") logger.WithError(keyResp.Error).Error("Failed to query keys")
return util.JSONResponse{ return convertKeyError(keyResp.Error)
Code: http.StatusBadRequest,
JSON: spec.Unknown(keyResp.Error.Error()),
}
} }
existingMasterKey, hasMasterKey := keyResp.MasterKeys[device.UserID] existingMasterKey, hasMasterKey := keyResp.MasterKeys[device.UserID]
requireUIA := false requireUIA := true
if hasMasterKey { if hasMasterKey {
// If we have a master key, check if any of the existing keys differ. If they do, if !keysDiffer(existingMasterKey, keyResp, uploadReq, device.UserID) {
// we need to re-authenticate the user. // If we have a master key, check if any of the existing keys differ. If they don't
requireUIA = keysDiffer(existingMasterKey, keyResp, uploadReq, device.UserID) // we return 200 as keys are still valid and there's nothing to do.
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}
// With MSC3861, UIA is not possible. Instead, the auth service has to explicitly mark the master key as replaceable.
if cfg.MSCs.MSC3861Enabled() {
masterKeyResp := api.QueryMasterKeysResponse{}
keyserverAPI.QueryMasterKeys(req.Context(), &api.QueryMasterKeysRequest{UserID: device.UserID}, &masterKeyResp)
if masterKeyResp.Error != nil {
logger.WithError(masterKeyResp.Error).Error("Failed to query master key")
return convertKeyError(masterKeyResp.Error)
}
if k := masterKeyResp.Key; k != nil && k.UpdatableWithoutUIABeforeMs != nil {
requireUIA = !(time.Now().UnixMilli() < *k.UpdatableWithoutUIABeforeMs)
} }
if requireUIA { if requireUIA {
sessionID := uploadReq.Auth.Session url := ""
if sessionID == "" { if m := cfg.MSCs.MSC3861; m.AccountManagementURL != "" {
sessionID = util.RandomString(sessionIDLength) url = strings.Join([]string{m.AccountManagementURL, "?action=", CrossSigningResetStage}, "")
} else {
url = m.Issuer
} }
return util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: newUserInteractiveResponse(
"dummy",
[]authtypes.Flow{
{
Stages: []authtypes.LoginType{CrossSigningResetStage},
},
},
map[string]interface{}{
CrossSigningResetStage: map[string]string{
"url": url,
},
},
strings.Join([]string{
"To reset your end-to-end encryption cross-signing identity, you first need to approve it at",
url,
"and then try again.",
}, " "),
),
}
}
// XXX: is it necessary?
sessions.addCompletedSessionStage(sessionID, CrossSigningResetStage)
} else {
if uploadReq.Auth.Type != authtypes.LoginTypePassword { if uploadReq.Auth.Type != authtypes.LoginTypePassword {
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
@ -88,6 +136,7 @@ func UploadCrossSigningDeviceKeys(
}, },
}, },
nil, nil,
"",
), ),
} }
} }
@ -100,33 +149,13 @@ func UploadCrossSigningDeviceKeys(
} }
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword) sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
} }
}
uploadReq.UserID = device.UserID uploadReq.UserID = device.UserID
keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes) keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)
if err := uploadRes.Error; err != nil { if err := uploadRes.Error; err != nil {
switch { return convertKeyError(err)
case err.IsInvalidSignature:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidSignature(err.Error()),
}
case err.IsMissingParam:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.MissingParam(err.Error()),
}
case err.IsInvalidParam:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidParam(err.Error()),
}
default:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown(err.Error()),
}
}
} }
return util.JSONResponse{ return util.JSONResponse{