Skip to content
This repository has been archived by the owner on Nov 25, 2024. It is now read-only.

Cross-signing groundwork #1953

Merged
merged 19 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/gobind-pinecone/monolith.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func (m *DendriteMonolith) Start() {
base, federation, rsAPI, keyRing, true,
)

keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
m.userAPI = userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
keyAPI.SetUserAPI(m.userAPI)

Expand Down
2 changes: 1 addition & 1 deletion build/gobind-yggdrasil/monolith.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (m *DendriteMonolith) Start() {
base, federation, rsAPI, keyRing, true,
)

keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
keyAPI.SetUserAPI(userAPI)

Expand Down
4 changes: 2 additions & 2 deletions clientapi/auth/password.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
if username == "" {
return nil, &util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: jsonerror.BadJSON("'user' must be supplied."),
JSON: jsonerror.BadJSON("A username must be supplied."),
}
}
localpart, err := userutil.ParseUsernameParam(username, &t.Config.Matrix.ServerName)
Expand All @@ -68,7 +68,7 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
// but that would leak the existence of the user.
return nil, &util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("username or password was incorrect, or the account does not exist"),
JSON: jsonerror.Forbidden("The username or password was incorrect or the account does not exist."),
}
}
return &r.Login, nil
Expand Down
4 changes: 2 additions & 2 deletions clientapi/auth/user_interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
if !ok {
return nil, &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("unknown auth.type: " + authType),
JSON: jsonerror.BadJSON("Unknown auth.type: " + authType),
}
}

Expand All @@ -231,7 +231,7 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
if !u.IsSingleStageFlow(authType) {
return nil, &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.Unknown("missing or unknown auth.session"),
JSON: jsonerror.Unknown("The auth.session is missing or unknown."),
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions clientapi/jsonerror/jsonerror.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ func GuestAccessForbidden(msg string) *MatrixError {
return &MatrixError{"M_GUEST_ACCESS_FORBIDDEN", msg}
}

// InvalidSignature is an error which is returned when the client tries
// to upload invalid signatures.
func InvalidSignature(msg string) *MatrixError {
return &MatrixError{"M_INVALID_SIGNATURE", msg}
}

// MissingParam is an error that is returned when a parameter was incorrect,
// traditionally with cross-signing.
func MissingParam(msg string) *MatrixError {
return &MatrixError{"M_MISSING_PARAM", msg}
}

type IncompatibleRoomVersionError struct {
RoomVersion string `json:"room_version"`
Error string `json:"error"`
Expand Down
125 changes: 125 additions & 0 deletions clientapi/routing/key_crosssigning.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package routing

import (
"encoding/json"
"io/ioutil"
"net/http"

"github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/keyserver/api"
"github.com/matrix-org/dendrite/setup/config"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/dendrite/userapi/storage/accounts"
"github.com/matrix-org/util"
)

func UploadCrossSigningDeviceKeys(
req *http.Request, userInteractiveAuth *auth.UserInteractive,
keyserverAPI api.KeyInternalAPI, device *userapi.Device,
accountDB accounts.Database, cfg *config.ClientAPI,
) util.JSONResponse {
uploadReq := &api.PerformUploadDeviceKeysRequest{}
uploadRes := &api.PerformUploadDeviceKeysResponse{}

ctx := req.Context()
defer req.Body.Close() // nolint:errcheck
bodyBytes, err := ioutil.ReadAll(req.Body)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()),
}
}

if _, err := userInteractiveAuth.Verify(ctx, bodyBytes, device); err != nil {
return *err
}

if err = json.Unmarshal(bodyBytes, &uploadReq); err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("The request body could not be unmarshalled: " + err.Error()),
}
}

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

if err := uploadRes.Error; err != nil {
switch {
case err.IsInvalidSignature:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.InvalidSignature(err.Error()),
}
case err.IsMissingParam:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.MissingParam(err.Error()),
}
default:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.Unknown(err.Error()),
}
}
}

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

func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.KeyInternalAPI, device *userapi.Device) util.JSONResponse {
uploadReq := &api.PerformUploadDeviceSignaturesRequest{}
uploadRes := &api.PerformUploadDeviceSignaturesResponse{}

if err := httputil.UnmarshalJSONRequest(req, &uploadReq.Signatures); err != nil {
return *err
}

uploadReq.UserID = device.UserID
keyserverAPI.PerformUploadDeviceSignatures(req.Context(), uploadReq, uploadRes)

if err := uploadRes.Error; err != nil {
switch {
case err.IsInvalidSignature:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.InvalidSignature(err.Error()),
}
case err.IsMissingParam:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.MissingParam(err.Error()),
}
default:
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.Unknown(err.Error()),
}
}
}

return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}
10 changes: 7 additions & 3 deletions clientapi/routing/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,23 +100,27 @@ func (r *queryKeysRequest) GetTimeout() time.Duration {
return time.Duration(r.Timeout) * time.Millisecond
}

func QueryKeys(req *http.Request, keyAPI api.KeyInternalAPI) util.JSONResponse {
func QueryKeys(req *http.Request, keyAPI api.KeyInternalAPI, device *userapi.Device) util.JSONResponse {
var r queryKeysRequest
resErr := httputil.UnmarshalJSONRequest(req, &r)
if resErr != nil {
return *resErr
}
queryRes := api.QueryKeysResponse{}
keyAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
UserID: device.UserID,
UserToDevices: r.DeviceKeys,
Timeout: r.GetTimeout(),
// TODO: Token?
}, &queryRes)
return util.JSONResponse{
Code: 200,
JSON: map[string]interface{}{
"device_keys": queryRes.DeviceKeys,
"failures": queryRes.Failures,
"device_keys": queryRes.DeviceKeys,
"master_keys": queryRes.MasterKeys,
"self_signing_keys": queryRes.SelfSigningKeys,
"user_signing_keys": queryRes.UserSigningKeys,
"failures": queryRes.Failures,
},
}
}
Expand Down
22 changes: 20 additions & 2 deletions clientapi/routing/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ func Setup(
rateLimits := newRateLimits(&cfg.RateLimiting)
userInteractiveAuth := auth.NewUserInteractive(accountDB.GetAccountByPassword, cfg)

unstableFeatures := make(map[string]bool)
unstableFeatures := map[string]bool{
//"org.matrix.e2e_cross_signing": true,
}
for _, msc := range cfg.MSCs.MSCs {
unstableFeatures["org.matrix."+msc] = true
}
Expand Down Expand Up @@ -1066,6 +1068,22 @@ func Setup(

// Deleting E2E Backup Keys

// Cross-signing device keys

postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, keyAPI, device, accountDB, cfg)
})

postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
return UploadCrossSigningDeviceSignatures(req, keyAPI, device)
})

r0mux.Handle("/keys/device_signing/upload", postDeviceSigningKeys).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/keys/signatures/upload", postDeviceSigningSignatures).Methods(http.MethodPost, http.MethodOptions)

unstableMux.Handle("/keys/device_signing/upload", postDeviceSigningKeys).Methods(http.MethodPost, http.MethodOptions)
unstableMux.Handle("/keys/signatures/upload", postDeviceSigningSignatures).Methods(http.MethodPost, http.MethodOptions)

// Supplying a device ID is deprecated.
r0mux.Handle("/keys/upload/{deviceID}",
httputil.MakeAuthAPI("keys_upload", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
Expand All @@ -1079,7 +1097,7 @@ func Setup(
).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/keys/query",
httputil.MakeAuthAPI("keys_query", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
return QueryKeys(req, keyAPI)
return QueryKeys(req, keyAPI, device)
}),
).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/keys/claim",
Expand Down
2 changes: 1 addition & 1 deletion cmd/dendrite-demo-libp2p/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func main() {

accountDB := base.Base.CreateAccountsDB()
federation := createFederationClient(base)
keyAPI := keyserver.NewInternalAPI(&base.Base.Cfg.KeyServer, federation)
keyAPI := keyserver.NewInternalAPI(&base.Base, &base.Base.Cfg.KeyServer, federation)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
keyAPI.SetUserAPI(userAPI)

Expand Down
2 changes: 1 addition & 1 deletion cmd/dendrite-demo-pinecone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func main() {
base, federation, rsAPI, keyRing, true,
)

keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
keyAPI.SetUserAPI(userAPI)

Expand Down
2 changes: 1 addition & 1 deletion cmd/dendrite-demo-yggdrasil/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func main() {
serverKeyAPI := &signing.YggdrasilKeys{}
keyRing := serverKeyAPI.KeyRing()

keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
keyAPI.SetUserAPI(userAPI)

Expand Down
2 changes: 1 addition & 1 deletion cmd/dendrite-monolith-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func main() {
// This is different to rsAPI which can be the http client which doesn't need this dependency
rsImpl.SetFederationSenderAPI(fsAPI)

keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
keyAPI.SetUserAPI(userAPI)
if traceInternal {
Expand Down
2 changes: 1 addition & 1 deletion cmd/dendrite-polylith-multi/personalities/keyserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (

func KeyServer(base *setup.BaseDendrite, cfg *config.Dendrite) {
fsAPI := base.FederationSenderHTTPClient()
intAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
intAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
intAPI.SetUserAPI(base.UserAPIClient())

keyserver.AddInternalRoutes(base.InternalAPIMux, intAPI)
Expand Down
2 changes: 1 addition & 1 deletion cmd/dendritejs-pinecone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func startup() {

accountDB := base.CreateAccountsDB()
federation := conn.CreateFederationClient(base, pSessions)
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
keyAPI.SetUserAPI(userAPI)

Expand Down
2 changes: 1 addition & 1 deletion cmd/dendritejs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func main() {

accountDB := base.CreateAccountsDB()
federation := createFederationClient(cfg, node)
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
keyAPI.SetUserAPI(userAPI)

Expand Down
33 changes: 32 additions & 1 deletion keyserver/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type KeyInternalAPI interface {
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse)
// PerformClaimKeys claims one-time keys for use in pre-key messages
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse)
PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse)
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse)
QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse)
Expand All @@ -40,7 +42,9 @@ type KeyInternalAPI interface {

// KeyError is returned if there was a problem performing/querying the server
type KeyError struct {
Err string
Err string `json:"error"`
IsInvalidSignature bool `json:"is_invalid_signature,omitempty"` // M_INVALID_SIGNATURE
IsMissingParam bool `json:"is_missing_param,omitempty"` // M_MISSING_PARAM
}

func (k *KeyError) Error() string {
Expand Down Expand Up @@ -151,7 +155,30 @@ type PerformClaimKeysResponse struct {
Error *KeyError
}

type PerformUploadDeviceKeysRequest struct {
gomatrixserverlib.CrossSigningKeys
// The user that uploaded the key, should be populated by the clientapi.
UserID string `json:"user_id"`
}

type PerformUploadDeviceKeysResponse struct {
Error *KeyError
}

type PerformUploadDeviceSignaturesRequest struct {
Signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice
// The user that uploaded the sig, should be populated by the clientapi.
UserID string `json:"user_id"`
}

type PerformUploadDeviceSignaturesResponse struct {
Error *KeyError
}

type QueryKeysRequest struct {
// The user ID asking for the keys, e.g. if from a client API request.
// Will not be populated if the key request came from federation.
UserID string
// Maps user IDs to a list of devices
UserToDevices map[string][]string
Timeout time.Duration
Expand All @@ -162,6 +189,10 @@ type QueryKeysResponse struct {
Failures map[string]interface{}
// Map of user_id to device_id to device_key
DeviceKeys map[string]map[string]json.RawMessage
// Maps of user_id to cross signing key
MasterKeys map[string]gomatrixserverlib.CrossSigningKey
SelfSigningKeys map[string]gomatrixserverlib.CrossSigningKey
UserSigningKeys map[string]gomatrixserverlib.CrossSigningKey
// Set if there was a fatal error processing this query
Error *KeyError
}
Expand Down
Loading