aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Alexander <neilalexander@users.noreply.github.com>2020-09-22 14:40:54 +0100
committerGitHub <noreply@github.com>2020-09-22 14:40:54 +0100
commita14b29b52617c06a548145a18b4d7cee6e529b79 (patch)
tree813e96e05884248ac97959d64b0458eba69e0665
parenta7563ede3d61efa626095b8b9069af9f16e7dd3d (diff)
Initial notary support (#1436)
* Initial work on notary support * Somewhat working (but not properly filtered) notary support, other tweaks * Update gomatrixserverlib
-rw-r--r--federationapi/routing/keys.go62
-rw-r--r--federationapi/routing/routing.go22
-rw-r--r--federationsender/api/api.go2
-rw-r--r--federationsender/internal/api.go24
-rw-r--r--federationsender/inthttp/client.go72
-rw-r--r--federationsender/inthttp/server.go44
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--serverkeyapi/internal/api.go2
-rw-r--r--serverkeyapi/serverkeyapi.go6
-rw-r--r--sytest-whitelist4
11 files changed, 229 insertions, 15 deletions
diff --git a/federationapi/routing/keys.go b/federationapi/routing/keys.go
index f1ed4176..785be090 100644
--- a/federationapi/routing/keys.go
+++ b/federationapi/routing/keys.go
@@ -19,11 +19,14 @@ import (
"net/http"
"time"
+ "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
+ federationSenderAPI "github.com/matrix-org/dendrite/federationsender/api"
"github.com/matrix-org/dendrite/internal/config"
"github.com/matrix-org/dendrite/keyserver/api"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
+ "github.com/sirupsen/logrus"
"golang.org/x/crypto/ed25519"
)
@@ -160,3 +163,62 @@ func localKeys(cfg *config.FederationAPI, validUntil time.Time) (*gomatrixserver
return &keys, nil
}
+
+func NotaryKeys(
+ httpReq *http.Request, cfg *config.FederationAPI,
+ fsAPI federationSenderAPI.FederationSenderInternalAPI,
+ req *gomatrixserverlib.PublicKeyNotaryLookupRequest,
+) util.JSONResponse {
+ if req == nil {
+ req = &gomatrixserverlib.PublicKeyNotaryLookupRequest{}
+ if reqErr := httputil.UnmarshalJSONRequest(httpReq, &req); reqErr != nil {
+ return *reqErr
+ }
+ }
+
+ var response struct {
+ ServerKeys []json.RawMessage `json:"server_keys"`
+ }
+ response.ServerKeys = []json.RawMessage{}
+
+ for serverName := range req.ServerKeys {
+ var keys *gomatrixserverlib.ServerKeys
+ if serverName == cfg.Matrix.ServerName {
+ if k, err := localKeys(cfg, time.Now().Add(cfg.Matrix.KeyValidityPeriod)); err == nil {
+ keys = k
+ } else {
+ return util.ErrorResponse(err)
+ }
+ } else {
+ if k, err := fsAPI.GetServerKeys(httpReq.Context(), serverName); err == nil {
+ keys = &k
+ } else {
+ return util.ErrorResponse(err)
+ }
+ }
+ if keys == nil {
+ continue
+ }
+
+ j, err := json.Marshal(keys)
+ if err != nil {
+ logrus.WithError(err).Errorf("Failed to marshal %q response", serverName)
+ return jsonerror.InternalServerError()
+ }
+
+ js, err := gomatrixserverlib.SignJSON(
+ string(cfg.Matrix.ServerName), cfg.Matrix.KeyID, cfg.Matrix.PrivateKey, j,
+ )
+ if err != nil {
+ logrus.WithError(err).Errorf("Failed to sign %q response", serverName)
+ return jsonerror.InternalServerError()
+ }
+
+ response.ServerKeys = append(response.ServerKeys, js)
+ }
+
+ return util.JSONResponse{
+ Code: http.StatusOK,
+ JSON: response,
+ }
+}
diff --git a/federationapi/routing/routing.go b/federationapi/routing/routing.go
index 71a09d42..06ed57af 100644
--- a/federationapi/routing/routing.go
+++ b/federationapi/routing/routing.go
@@ -61,6 +61,26 @@ func Setup(
return LocalKeys(cfg)
})
+ notaryKeys := httputil.MakeExternalAPI("notarykeys", func(req *http.Request) util.JSONResponse {
+ vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
+ if err != nil {
+ return util.ErrorResponse(err)
+ }
+ var pkReq *gomatrixserverlib.PublicKeyNotaryLookupRequest
+ serverName := gomatrixserverlib.ServerName(vars["serverName"])
+ keyID := gomatrixserverlib.KeyID(vars["keyID"])
+ if serverName != "" && keyID != "" {
+ pkReq = &gomatrixserverlib.PublicKeyNotaryLookupRequest{
+ ServerKeys: map[gomatrixserverlib.ServerName]map[gomatrixserverlib.KeyID]gomatrixserverlib.PublicKeyNotaryQueryCriteria{
+ serverName: {
+ keyID: gomatrixserverlib.PublicKeyNotaryQueryCriteria{},
+ },
+ },
+ }
+ }
+ return NotaryKeys(req, cfg, fsAPI, pkReq)
+ })
+
// Ignore the {keyID} argument as we only have a single server key so we always
// return that key.
// Even if we had more than one server key, we would probably still ignore the
@@ -68,6 +88,8 @@ func Setup(
v2keysmux.Handle("/server/{keyID}", localKeys).Methods(http.MethodGet)
v2keysmux.Handle("/server/", localKeys).Methods(http.MethodGet)
v2keysmux.Handle("/server", localKeys).Methods(http.MethodGet)
+ v2keysmux.Handle("/query", notaryKeys).Methods(http.MethodPost)
+ v2keysmux.Handle("/query/{serverName}/{keyID}", notaryKeys).Methods(http.MethodGet)
v1fedmux.Handle("/send/{txnID}", httputil.MakeFedAPI(
"federation_send", cfg.Matrix.ServerName, keys, wakeup,
diff --git a/federationsender/api/api.go b/federationsender/api/api.go
index adc3b34c..5ae419be 100644
--- a/federationsender/api/api.go
+++ b/federationsender/api/api.go
@@ -20,6 +20,8 @@ type FederationClient interface {
ClaimKeys(ctx context.Context, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (res gomatrixserverlib.RespClaimKeys, err error)
QueryKeys(ctx context.Context, s gomatrixserverlib.ServerName, keys map[string][]string) (res gomatrixserverlib.RespQueryKeys, err error)
GetEvent(ctx context.Context, s gomatrixserverlib.ServerName, eventID string) (res gomatrixserverlib.Transaction, err error)
+ GetServerKeys(ctx context.Context, matrixServer gomatrixserverlib.ServerName) (gomatrixserverlib.ServerKeys, error)
+ LookupServerKeys(ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp) ([]gomatrixserverlib.ServerKeys, error)
}
// FederationClientError is returned from FederationClient methods in the event of a problem.
diff --git a/federationsender/internal/api.go b/federationsender/internal/api.go
index 49c53755..f9d35357 100644
--- a/federationsender/internal/api.go
+++ b/federationsender/internal/api.go
@@ -189,3 +189,27 @@ func (a *FederationSenderInternalAPI) GetEvent(
}
return ires.(gomatrixserverlib.Transaction), nil
}
+
+func (a *FederationSenderInternalAPI) GetServerKeys(
+ ctx context.Context, s gomatrixserverlib.ServerName,
+) (gomatrixserverlib.ServerKeys, error) {
+ ires, err := a.doRequest(s, func() (interface{}, error) {
+ return a.federation.GetServerKeys(ctx, s)
+ })
+ if err != nil {
+ return gomatrixserverlib.ServerKeys{}, err
+ }
+ return ires.(gomatrixserverlib.ServerKeys), nil
+}
+
+func (a *FederationSenderInternalAPI) LookupServerKeys(
+ ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
+) ([]gomatrixserverlib.ServerKeys, error) {
+ ires, err := a.doRequest(s, func() (interface{}, error) {
+ return a.federation.LookupServerKeys(ctx, s, keyRequests)
+ })
+ if err != nil {
+ return []gomatrixserverlib.ServerKeys{}, err
+ }
+ return ires.([]gomatrixserverlib.ServerKeys), nil
+}
diff --git a/federationsender/inthttp/client.go b/federationsender/inthttp/client.go
index 5bfe6089..e0783ee1 100644
--- a/federationsender/inthttp/client.go
+++ b/federationsender/inthttp/client.go
@@ -23,13 +23,15 @@ const (
FederationSenderPerformServersAlivePath = "/federationsender/performServersAlive"
FederationSenderPerformBroadcastEDUPath = "/federationsender/performBroadcastEDU"
- FederationSenderGetUserDevicesPath = "/federationsender/client/getUserDevices"
- FederationSenderClaimKeysPath = "/federationsender/client/claimKeys"
- FederationSenderQueryKeysPath = "/federationsender/client/queryKeys"
- FederationSenderBackfillPath = "/federationsender/client/backfill"
- FederationSenderLookupStatePath = "/federationsender/client/lookupState"
- FederationSenderLookupStateIDsPath = "/federationsender/client/lookupStateIDs"
- FederationSenderGetEventPath = "/federationsender/client/getEvent"
+ FederationSenderGetUserDevicesPath = "/federationsender/client/getUserDevices"
+ FederationSenderClaimKeysPath = "/federationsender/client/claimKeys"
+ FederationSenderQueryKeysPath = "/federationsender/client/queryKeys"
+ FederationSenderBackfillPath = "/federationsender/client/backfill"
+ FederationSenderLookupStatePath = "/federationsender/client/lookupState"
+ FederationSenderLookupStateIDsPath = "/federationsender/client/lookupStateIDs"
+ FederationSenderGetEventPath = "/federationsender/client/getEvent"
+ FederationSenderGetServerKeysPath = "/federationsender/client/getServerKeys"
+ FederationSenderLookupServerKeysPath = "/federationsender/client/lookupServerKeys"
)
// NewFederationSenderClient creates a FederationSenderInternalAPI implemented by talking to a HTTP POST API.
@@ -358,3 +360,59 @@ func (h *httpFederationSenderInternalAPI) GetEvent(
}
return *response.Res, nil
}
+
+type getServerKeys struct {
+ S gomatrixserverlib.ServerName
+ ServerKeys gomatrixserverlib.ServerKeys
+ Err *api.FederationClientError
+}
+
+func (h *httpFederationSenderInternalAPI) GetServerKeys(
+ ctx context.Context, s gomatrixserverlib.ServerName,
+) (gomatrixserverlib.ServerKeys, error) {
+ span, ctx := opentracing.StartSpanFromContext(ctx, "GetServerKeys")
+ defer span.Finish()
+
+ request := getServerKeys{
+ S: s,
+ }
+ var response getServerKeys
+ apiURL := h.federationSenderURL + FederationSenderGetServerKeysPath
+ err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
+ if err != nil {
+ return gomatrixserverlib.ServerKeys{}, err
+ }
+ if response.Err != nil {
+ return gomatrixserverlib.ServerKeys{}, response.Err
+ }
+ return response.ServerKeys, nil
+}
+
+type lookupServerKeys struct {
+ S gomatrixserverlib.ServerName
+ KeyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp
+ ServerKeys []gomatrixserverlib.ServerKeys
+ Err *api.FederationClientError
+}
+
+func (h *httpFederationSenderInternalAPI) LookupServerKeys(
+ ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
+) ([]gomatrixserverlib.ServerKeys, error) {
+ span, ctx := opentracing.StartSpanFromContext(ctx, "LookupServerKeys")
+ defer span.Finish()
+
+ request := lookupServerKeys{
+ S: s,
+ KeyRequests: keyRequests,
+ }
+ var response lookupServerKeys
+ apiURL := h.federationSenderURL + FederationSenderLookupServerKeysPath
+ err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, &request, &response)
+ if err != nil {
+ return []gomatrixserverlib.ServerKeys{}, err
+ }
+ if response.Err != nil {
+ return []gomatrixserverlib.ServerKeys{}, response.Err
+ }
+ return response.ServerKeys, nil
+}
diff --git a/federationsender/inthttp/server.go b/federationsender/inthttp/server.go
index dfbff1c0..53e1183e 100644
--- a/federationsender/inthttp/server.go
+++ b/federationsender/inthttp/server.go
@@ -263,4 +263,48 @@ func AddRoutes(intAPI api.FederationSenderInternalAPI, internalAPIMux *mux.Route
return util.JSONResponse{Code: http.StatusOK, JSON: request}
}),
)
+ internalAPIMux.Handle(
+ FederationSenderGetServerKeysPath,
+ httputil.MakeInternalAPI("GetServerKeys", func(req *http.Request) util.JSONResponse {
+ var request getServerKeys
+ if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
+ return util.MessageResponse(http.StatusBadRequest, err.Error())
+ }
+ res, err := intAPI.GetServerKeys(req.Context(), request.S)
+ if err != nil {
+ ferr, ok := err.(*api.FederationClientError)
+ if ok {
+ request.Err = ferr
+ } else {
+ request.Err = &api.FederationClientError{
+ Err: err.Error(),
+ }
+ }
+ }
+ request.ServerKeys = res
+ return util.JSONResponse{Code: http.StatusOK, JSON: request}
+ }),
+ )
+ internalAPIMux.Handle(
+ FederationSenderLookupServerKeysPath,
+ httputil.MakeInternalAPI("LookupServerKeys", func(req *http.Request) util.JSONResponse {
+ var request lookupServerKeys
+ if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
+ return util.MessageResponse(http.StatusBadRequest, err.Error())
+ }
+ res, err := intAPI.LookupServerKeys(req.Context(), request.S, request.KeyRequests)
+ if err != nil {
+ ferr, ok := err.(*api.FederationClientError)
+ if ok {
+ request.Err = ferr
+ } else {
+ request.Err = &api.FederationClientError{
+ Err: err.Error(),
+ }
+ }
+ }
+ request.ServerKeys = res
+ return util.JSONResponse{Code: http.StatusOK, JSON: request}
+ }),
+ )
}
diff --git a/go.mod b/go.mod
index 6b1c03b5..6d367bda 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,7 @@ require (
github.com/matrix-org/go-http-js-libp2p v0.0.0-20200518170932-783164aeeda4
github.com/matrix-org/go-sqlite3-js v0.0.0-20200522092705-bc8506ccbcf3
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd
- github.com/matrix-org/gomatrixserverlib v0.0.0-20200907151926-38f437f2b2a6
+ github.com/matrix-org/gomatrixserverlib v0.0.0-20200922131600-dce167edcce4
github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
github.com/mattn/go-sqlite3 v1.14.2
diff --git a/go.sum b/go.sum
index 5c4f27a5..990fa21a 100644
--- a/go.sum
+++ b/go.sum
@@ -569,8 +569,8 @@ github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26 h1:Hr3zjRsq2bh
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0=
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd h1:xVrqJK3xHREMNjwjljkAUaadalWc0rRbmVuQatzmgwg=
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
-github.com/matrix-org/gomatrixserverlib v0.0.0-20200907151926-38f437f2b2a6 h1:43gla6bLt4opWY1mQkAasF/LUCipZl7x2d44TY0wf40=
-github.com/matrix-org/gomatrixserverlib v0.0.0-20200907151926-38f437f2b2a6/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20200922131600-dce167edcce4 h1:jBUEVUTgXc5a9luTRvb9vOkuLB+F528CE3Z05nUzGeM=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20200922131600-dce167edcce4/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91 h1:HJ6U3S3ljJqNffYMcIeAncp5qT/i+ZMiJ2JC2F0aXP4=
github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91/go.mod h1:sjyPyRxKM5uw1nD2cJ6O2OxI6GOqyVBfNXqKjBZTBZE=
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7 h1:ntrLa/8xVzeSs8vHFHK25k0C+NV74sYMJnNSg5NoSRo=
diff --git a/serverkeyapi/internal/api.go b/serverkeyapi/internal/api.go
index 02028c60..bc02ac2d 100644
--- a/serverkeyapi/internal/api.go
+++ b/serverkeyapi/internal/api.go
@@ -20,7 +20,7 @@ type ServerKeyAPI struct {
ServerKeyValidity time.Duration
OurKeyRing gomatrixserverlib.KeyRing
- FedClient *gomatrixserverlib.FederationClient
+ FedClient gomatrixserverlib.KeyClient
}
func (s *ServerKeyAPI) KeyRing() *gomatrixserverlib.KeyRing {
diff --git a/serverkeyapi/serverkeyapi.go b/serverkeyapi/serverkeyapi.go
index fbaaefad..783402b2 100644
--- a/serverkeyapi/serverkeyapi.go
+++ b/serverkeyapi/serverkeyapi.go
@@ -26,7 +26,7 @@ func AddInternalRoutes(router *mux.Router, intAPI api.ServerKeyInternalAPI, cach
// can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes.
func NewInternalAPI(
cfg *config.ServerKeyAPI,
- fedClient *gomatrixserverlib.FederationClient,
+ fedClient gomatrixserverlib.KeyClient,
caches *caching.Caches,
) api.ServerKeyInternalAPI {
innerDB, err := storage.NewDatabase(
@@ -53,7 +53,7 @@ func NewInternalAPI(
OurKeyRing: gomatrixserverlib.KeyRing{
KeyFetchers: []gomatrixserverlib.KeyFetcher{
&gomatrixserverlib.DirectKeyFetcher{
- Client: fedClient.Client,
+ Client: fedClient,
},
},
KeyDatabase: serverKeyDB,
@@ -65,7 +65,7 @@ func NewInternalAPI(
perspective := &gomatrixserverlib.PerspectiveKeyFetcher{
PerspectiveServerName: ps.ServerName,
PerspectiveServerKeys: map[gomatrixserverlib.KeyID]ed25519.PublicKey{},
- Client: fedClient.Client,
+ Client: fedClient,
}
for _, key := range ps.Keys {
diff --git a/sytest-whitelist b/sytest-whitelist
index 553df1f1..84706b6c 100644
--- a/sytest-whitelist
+++ b/sytest-whitelist
@@ -474,4 +474,6 @@ Peeked rooms only turn up in the sync for the device who peeked them
Room state at a rejected message event is the same as its predecessor
Room state at a rejected state event is the same as its predecessor
Inbound federation correctly soft fails events
-Inbound federation accepts a second soft-failed event \ No newline at end of file
+Inbound federation accepts a second soft-failed event
+Federation key API can act as a notary server via a POST request
+Federation key API can act as a notary server via a GET request