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 (
"context"
"net/http"
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/element-hq/dendrite/clientapi/auth"
"github.com/element-hq/dendrite/clientapi/auth/authtypes"
"github.com/element-hq/dendrite/clientapi/httputil"
@ -32,6 +31,7 @@ type crossSigningRequest struct {
type UploadKeysAPI interface {
QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse)
QueryMasterKeys(ctx context.Context, req *api.QueryMasterKeysRequest, res *api.QueryMasterKeysResponse)
api.UploadDeviceKeysAPI
}
@ -40,6 +40,7 @@ func UploadCrossSigningDeviceKeys(
keyserverAPI UploadKeysAPI, device *api.Device,
accountAPI auth.GetAccountByPassword, cfg *config.ClientAPI,
) util.JSONResponse {
logger := util.GetLogger(req.Context())
uploadReq := &crossSigningRequest{}
uploadRes := &api.PerformUploadDeviceKeysResponse{}
@ -48,6 +49,11 @@ func UploadCrossSigningDeviceKeys(
return *resErr
}
sessionID := uploadReq.Auth.Session
if sessionID == "" {
sessionID = util.RandomString(sessionIDLength)
}
// Query existing keys to determine if UIA is required
keyResp := api.QueryKeysResponse{}
keyserverAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
@ -57,26 +63,68 @@ func UploadCrossSigningDeviceKeys(
}, &keyResp)
if keyResp.Error != nil {
logrus.WithError(keyResp.Error).Error("Failed to query keys")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown(keyResp.Error.Error()),
}
logger.WithError(keyResp.Error).Error("Failed to query keys")
return convertKeyError(keyResp.Error)
}
existingMasterKey, hasMasterKey := keyResp.MasterKeys[device.UserID]
requireUIA := false
requireUIA := true
if hasMasterKey {
// If we have a master key, check if any of the existing keys differ. If they do,
// we need to re-authenticate the user.
requireUIA = keysDiffer(existingMasterKey, keyResp, uploadReq, device.UserID)
if !keysDiffer(existingMasterKey, keyResp, uploadReq, device.UserID) {
// If we have a master key, check if any of the existing keys differ. If they don't
// 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 {
sessionID := uploadReq.Auth.Session
if sessionID == "" {
sessionID = util.RandomString(sessionIDLength)
url := ""
if m := cfg.MSCs.MSC3861; m.AccountManagementURL != "" {
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 {
return util.JSONResponse{
Code: http.StatusUnauthorized,
@ -88,6 +136,7 @@ func UploadCrossSigningDeviceKeys(
},
},
nil,
"",
),
}
}
@ -100,33 +149,13 @@ func UploadCrossSigningDeviceKeys(
}
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
}
}
uploadReq.UserID = device.UserID
keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)
if err := uploadRes.Error; err != nil {
switch {
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 convertKeyError(err)
}
return util.JSONResponse{