Skip to content

Commit

Permalink
mas: cross signing fixes after merge
Browse files Browse the repository at this point in the history
  • Loading branch information
mdnight committed Jan 17, 2025
1 parent 6833e99 commit b44f899
Showing 1 changed file with 87 additions and 58 deletions.
145 changes: 87 additions & 58 deletions clientapi/routing/key_crosssigning.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
}

Expand All @@ -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{}

Expand All @@ -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{
Expand All @@ -57,78 +63,101 @@ 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
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)
}
requireUIA := true

if requireUIA {
sessionID := uploadReq.Auth.Session
if sessionID == "" {
sessionID = util.RandomString(sessionIDLength)
}
if uploadReq.Auth.Type != authtypes.LoginTypePassword {
if hasMasterKey {
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.StatusUnauthorized,
JSON: newUserInteractiveResponse(
sessionID,
[]authtypes.Flow{
{
Stages: []authtypes.LoginType{authtypes.LoginTypePassword},
},
},
nil,
),
Code: http.StatusOK,
JSON: struct{}{},
}
}
typePassword := auth.LoginTypePassword{
GetAccountByPassword: accountAPI,
Config: cfg,
}
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
return *authErr
}
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
}

uploadReq.UserID = device.UserID
keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)
// 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 err := uploadRes.Error; err != nil {
switch {
case err.IsInvalidSignature:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidSignature(err.Error()),
if masterKeyResp.Error != nil {
logger.WithError(masterKeyResp.Error).Error("Failed to query master key")
return convertKeyError(masterKeyResp.Error)
}
case err.IsMissingParam:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.MissingParam(err.Error()),
if k := masterKeyResp.Key; k != nil && k.UpdatableWithoutUIABeforeMs != nil {
requireUIA = !(time.Now().UnixMilli() < *k.UpdatableWithoutUIABeforeMs)
}
case err.IsInvalidParam:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.InvalidParam(err.Error()),

if requireUIA {
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.",
}, " "),
),
}
}
default:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: spec.Unknown(err.Error()),
// XXX: is it necessary?
sessions.addCompletedSessionStage(sessionID, CrossSigningResetStage)
} else {
if uploadReq.Auth.Type != authtypes.LoginTypePassword {
return util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: newUserInteractiveResponse(
sessionID,
[]authtypes.Flow{
{
Stages: []authtypes.LoginType{authtypes.LoginTypePassword},
},
},
nil,
"",
),
}
}
typePassword := auth.LoginTypePassword{
GetAccountByPassword: accountAPI,
Config: cfg,
}
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
return *authErr
}
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
}
}

uploadReq.UserID = device.UserID
keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)

if err := uploadRes.Error; err != nil {
return convertKeyError(err)
}

return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
Expand Down

0 comments on commit b44f899

Please sign in to comment.