Rename serverkeyapi to signingkeyserver (#1492)
* Rename serverkeyapi to signingkeyserver We use "api" for public facing stuff and "server" for internal stuff. As the server key API is internal only, we call it 'signing key server', which also clarifies the type of key (as opposed to TLS keys, E2E keys, etc) * Convert docker/scripts to use signing-key-server * Rename missed bits
This commit is contained in:
parent
533006141e
commit
bf7e85848b
38 changed files with 97 additions and 96 deletions
40
signingkeyserver/api/api.go
Normal file
40
signingkeyserver/api/api.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type SigningKeyServerAPI interface {
|
||||
gomatrixserverlib.KeyDatabase
|
||||
|
||||
KeyRing() *gomatrixserverlib.KeyRing
|
||||
|
||||
InputPublicKeys(
|
||||
ctx context.Context,
|
||||
request *InputPublicKeysRequest,
|
||||
response *InputPublicKeysResponse,
|
||||
) error
|
||||
|
||||
QueryPublicKeys(
|
||||
ctx context.Context,
|
||||
request *QueryPublicKeysRequest,
|
||||
response *QueryPublicKeysResponse,
|
||||
) error
|
||||
}
|
||||
|
||||
type QueryPublicKeysRequest struct {
|
||||
Requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp `json:"requests"`
|
||||
}
|
||||
|
||||
type QueryPublicKeysResponse struct {
|
||||
Results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult `json:"results"`
|
||||
}
|
||||
|
||||
type InputPublicKeysRequest struct {
|
||||
Keys map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult `json:"keys"`
|
||||
}
|
||||
|
||||
type InputPublicKeysResponse struct {
|
||||
}
|
||||
270
signingkeyserver/internal/api.go
Normal file
270
signingkeyserver/internal/api.go
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/config"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ServerKeyAPI struct {
|
||||
api.SigningKeyServerAPI
|
||||
|
||||
ServerName gomatrixserverlib.ServerName
|
||||
ServerPublicKey ed25519.PublicKey
|
||||
ServerKeyID gomatrixserverlib.KeyID
|
||||
ServerKeyValidity time.Duration
|
||||
OldServerKeys []config.OldVerifyKeys
|
||||
|
||||
OurKeyRing gomatrixserverlib.KeyRing
|
||||
FedClient gomatrixserverlib.KeyClient
|
||||
}
|
||||
|
||||
func (s *ServerKeyAPI) KeyRing() *gomatrixserverlib.KeyRing {
|
||||
// Return a keyring that forces requests to be proxied through the
|
||||
// below functions. That way we can enforce things like validity
|
||||
// and keeping the cache up-to-date.
|
||||
return &gomatrixserverlib.KeyRing{
|
||||
KeyDatabase: s,
|
||||
KeyFetchers: []gomatrixserverlib.KeyFetcher{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerKeyAPI) StoreKeys(
|
||||
_ context.Context,
|
||||
results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
// Run in a background context - we don't want to stop this work just
|
||||
// because the caller gives up waiting.
|
||||
ctx := context.Background()
|
||||
|
||||
// Store any keys that we were given in our database.
|
||||
return s.OurKeyRing.KeyDatabase.StoreKeys(ctx, results)
|
||||
}
|
||||
|
||||
func (s *ServerKeyAPI) FetchKeys(
|
||||
_ context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
||||
// Run in a background context - we don't want to stop this work just
|
||||
// because the caller gives up waiting.
|
||||
ctx := context.Background()
|
||||
now := gomatrixserverlib.AsTimestamp(time.Now())
|
||||
results := map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult{}
|
||||
origRequests := map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp{}
|
||||
for k, v := range requests {
|
||||
origRequests[k] = v
|
||||
}
|
||||
|
||||
// First, check if any of these key checks are for our own keys. If
|
||||
// they are then we will satisfy them directly.
|
||||
s.handleLocalKeys(ctx, requests, results)
|
||||
|
||||
// Then consult our local database and see if we have the requested
|
||||
// keys. These might come from a cache, depending on the database
|
||||
// implementation used.
|
||||
if err := s.handleDatabaseKeys(ctx, now, requests, results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For any key requests that we still have outstanding, next try to
|
||||
// fetch them directly. We'll go through each of the key fetchers to
|
||||
// ask for the remaining keys
|
||||
for _, fetcher := range s.OurKeyRing.KeyFetchers {
|
||||
// If there are no more keys to look up then stop.
|
||||
if len(requests) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Ask the fetcher to look up our keys.
|
||||
if err := s.handleFetcherKeys(ctx, now, fetcher, requests, results); err != nil {
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"fetcher_name": fetcher.FetcherName(),
|
||||
}).Errorf("Failed to retrieve %d key(s)", len(requests))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we've actually satisfied all of the key requests that we
|
||||
// were given. We should report an error if we didn't.
|
||||
for req := range origRequests {
|
||||
if _, ok := results[req]; !ok {
|
||||
// The results don't contain anything for this specific request, so
|
||||
// we've failed to satisfy it from local keys, database keys or from
|
||||
// all of the fetchers. Report an error.
|
||||
logrus.Warnf("Failed to retrieve key %q for server %q", req.KeyID, req.ServerName)
|
||||
}
|
||||
}
|
||||
|
||||
// Return the keys.
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s *ServerKeyAPI) FetcherName() string {
|
||||
return fmt.Sprintf("ServerKeyAPI (wrapping %q)", s.OurKeyRing.KeyDatabase.FetcherName())
|
||||
}
|
||||
|
||||
// handleLocalKeys handles cases where the key request contains
|
||||
// a request for our own server keys, either current or old.
|
||||
func (s *ServerKeyAPI) handleLocalKeys(
|
||||
_ context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) {
|
||||
for req := range requests {
|
||||
if req.ServerName != s.ServerName {
|
||||
continue
|
||||
}
|
||||
if req.KeyID == s.ServerKeyID {
|
||||
// We found a key request that is supposed to be for our own
|
||||
// keys. Remove it from the request list so we don't hit the
|
||||
// database or the fetchers for it.
|
||||
delete(requests, req)
|
||||
|
||||
// Insert our own key into the response.
|
||||
results[req] = gomatrixserverlib.PublicKeyLookupResult{
|
||||
VerifyKey: gomatrixserverlib.VerifyKey{
|
||||
Key: gomatrixserverlib.Base64Bytes(s.ServerPublicKey),
|
||||
},
|
||||
ExpiredTS: gomatrixserverlib.PublicKeyNotExpired,
|
||||
ValidUntilTS: gomatrixserverlib.AsTimestamp(time.Now().Add(s.ServerKeyValidity)),
|
||||
}
|
||||
} else {
|
||||
// The key request doesn't match our current key. Let's see
|
||||
// if it matches any of our old verify keys.
|
||||
for _, oldVerifyKey := range s.OldServerKeys {
|
||||
if req.KeyID == oldVerifyKey.KeyID {
|
||||
// We found a key request that is supposed to be an expired
|
||||
// key.
|
||||
delete(requests, req)
|
||||
|
||||
// Insert our own key into the response.
|
||||
results[req] = gomatrixserverlib.PublicKeyLookupResult{
|
||||
VerifyKey: gomatrixserverlib.VerifyKey{
|
||||
Key: gomatrixserverlib.Base64Bytes(oldVerifyKey.PrivateKey.Public().(ed25519.PublicKey)),
|
||||
},
|
||||
ExpiredTS: oldVerifyKey.ExpiredAt,
|
||||
ValidUntilTS: gomatrixserverlib.PublicKeyNotValid,
|
||||
}
|
||||
|
||||
// No need to look at the other keys.
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleDatabaseKeys handles cases where the key requests can be
|
||||
// satisfied from our local database/cache.
|
||||
func (s *ServerKeyAPI) handleDatabaseKeys(
|
||||
ctx context.Context,
|
||||
now gomatrixserverlib.Timestamp,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
// Ask the database/cache for the keys.
|
||||
dbResults, err := s.OurKeyRing.KeyDatabase.FetchKeys(ctx, requests)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We successfully got some keys. Add them to the results.
|
||||
for req, res := range dbResults {
|
||||
// The key we've retrieved from the database/cache might
|
||||
// have passed its validity period, but right now, it's
|
||||
// the best thing we've got, and it might be sufficient to
|
||||
// verify a past event.
|
||||
results[req] = res
|
||||
|
||||
// If the key is valid right now then we can also remove it
|
||||
// from the request list as we don't need to fetch it again
|
||||
// in that case. If the key isn't valid right now, then by
|
||||
// leaving it in the 'requests' map, we'll try to update the
|
||||
// key using the fetchers in handleFetcherKeys.
|
||||
if res.WasValidAt(now, true) {
|
||||
delete(requests, req)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFetcherKeys handles cases where a fetcher can satisfy
|
||||
// the remaining requests.
|
||||
func (s *ServerKeyAPI) handleFetcherKeys(
|
||||
ctx context.Context,
|
||||
_ gomatrixserverlib.Timestamp,
|
||||
fetcher gomatrixserverlib.KeyFetcher,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"fetcher_name": fetcher.FetcherName(),
|
||||
}).Infof("Fetching %d key(s)", len(requests))
|
||||
|
||||
// Create a context that limits our requests to 30 seconds.
|
||||
fetcherCtx, fetcherCancel := context.WithTimeout(ctx, time.Second*30)
|
||||
defer fetcherCancel()
|
||||
|
||||
// Try to fetch the keys.
|
||||
fetcherResults, err := fetcher.FetchKeys(fetcherCtx, requests)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetcher.FetchKeys: %w", err)
|
||||
}
|
||||
|
||||
// Build a map of the results that we want to commit to the
|
||||
// database. We do this in a separate map because otherwise we
|
||||
// might end up trying to rewrite database entries.
|
||||
storeResults := map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult{}
|
||||
|
||||
// Now let's look at the results that we got from this fetcher.
|
||||
for req, res := range fetcherResults {
|
||||
if req.ServerName == s.ServerName {
|
||||
continue
|
||||
}
|
||||
|
||||
if prev, ok := results[req]; ok {
|
||||
// We've already got a previous entry for this request
|
||||
// so let's see if the newly retrieved one contains a more
|
||||
// up-to-date validity period.
|
||||
if res.ValidUntilTS > prev.ValidUntilTS {
|
||||
// This key is newer than the one we had so let's store
|
||||
// it in the database.
|
||||
storeResults[req] = res
|
||||
}
|
||||
} else {
|
||||
// We didn't already have a previous entry for this request
|
||||
// so store it in the database anyway for now.
|
||||
storeResults[req] = res
|
||||
}
|
||||
|
||||
// Update the results map with this new result. If nothing
|
||||
// else, we can try verifying against this key.
|
||||
results[req] = res
|
||||
|
||||
// Remove it from the request list so we won't re-fetch it.
|
||||
delete(requests, req)
|
||||
}
|
||||
|
||||
// Store the keys from our store map.
|
||||
if err = s.OurKeyRing.KeyDatabase.StoreKeys(context.Background(), storeResults); err != nil {
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"fetcher_name": fetcher.FetcherName(),
|
||||
"database_name": s.OurKeyRing.KeyDatabase.FetcherName(),
|
||||
}).Errorf("Failed to store keys in the database")
|
||||
return fmt.Errorf("server key API failed to store retrieved keys: %w", err)
|
||||
}
|
||||
|
||||
if len(storeResults) > 0 {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"fetcher_name": fetcher.FetcherName(),
|
||||
}).Infof("Updated %d of %d key(s) in database (%d keys remaining)", len(storeResults), len(results), len(requests))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
132
signingkeyserver/inthttp/client.go
Normal file
132
signingkeyserver/inthttp/client.go
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
package inthttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// HTTP paths for the internal HTTP APIs
|
||||
const (
|
||||
ServerKeyInputPublicKeyPath = "/signingkeyserver/inputPublicKey"
|
||||
ServerKeyQueryPublicKeyPath = "/signingkeyserver/queryPublicKey"
|
||||
)
|
||||
|
||||
// NewSigningKeyServerClient creates a SigningKeyServerAPI implemented by talking to a HTTP POST API.
|
||||
// If httpClient is nil an error is returned
|
||||
func NewSigningKeyServerClient(
|
||||
serverKeyAPIURL string,
|
||||
httpClient *http.Client,
|
||||
cache caching.ServerKeyCache,
|
||||
) (api.SigningKeyServerAPI, error) {
|
||||
if httpClient == nil {
|
||||
return nil, errors.New("NewSigningKeyServerClient: httpClient is <nil>")
|
||||
}
|
||||
return &httpServerKeyInternalAPI{
|
||||
serverKeyAPIURL: serverKeyAPIURL,
|
||||
httpClient: httpClient,
|
||||
cache: cache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type httpServerKeyInternalAPI struct {
|
||||
serverKeyAPIURL string
|
||||
httpClient *http.Client
|
||||
cache caching.ServerKeyCache
|
||||
}
|
||||
|
||||
func (s *httpServerKeyInternalAPI) KeyRing() *gomatrixserverlib.KeyRing {
|
||||
// This is a bit of a cheat - we tell gomatrixserverlib that this API is
|
||||
// both the key database and the key fetcher. While this does have the
|
||||
// rather unfortunate effect of preventing gomatrixserverlib from handling
|
||||
// key fetchers directly, we can at least reimplement this behaviour on
|
||||
// the other end of the API.
|
||||
return &gomatrixserverlib.KeyRing{
|
||||
KeyDatabase: s,
|
||||
KeyFetchers: []gomatrixserverlib.KeyFetcher{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *httpServerKeyInternalAPI) FetcherName() string {
|
||||
return "httpServerKeyInternalAPI"
|
||||
}
|
||||
|
||||
func (s *httpServerKeyInternalAPI) StoreKeys(
|
||||
_ context.Context,
|
||||
results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
// Run in a background context - we don't want to stop this work just
|
||||
// because the caller gives up waiting.
|
||||
ctx := context.Background()
|
||||
request := api.InputPublicKeysRequest{
|
||||
Keys: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult),
|
||||
}
|
||||
response := api.InputPublicKeysResponse{}
|
||||
for req, res := range results {
|
||||
request.Keys[req] = res
|
||||
s.cache.StoreServerKey(req, res)
|
||||
}
|
||||
return s.InputPublicKeys(ctx, &request, &response)
|
||||
}
|
||||
|
||||
func (s *httpServerKeyInternalAPI) FetchKeys(
|
||||
_ context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
||||
// Run in a background context - we don't want to stop this work just
|
||||
// because the caller gives up waiting.
|
||||
ctx := context.Background()
|
||||
result := make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult)
|
||||
request := api.QueryPublicKeysRequest{
|
||||
Requests: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp),
|
||||
}
|
||||
response := api.QueryPublicKeysResponse{
|
||||
Results: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult),
|
||||
}
|
||||
for req, ts := range requests {
|
||||
if res, ok := s.cache.GetServerKey(req, ts); ok {
|
||||
result[req] = res
|
||||
continue
|
||||
}
|
||||
request.Requests[req] = ts
|
||||
}
|
||||
err := s.QueryPublicKeys(ctx, &request, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for req, res := range response.Results {
|
||||
result[req] = res
|
||||
s.cache.StoreServerKey(req, res)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (h *httpServerKeyInternalAPI) InputPublicKeys(
|
||||
ctx context.Context,
|
||||
request *api.InputPublicKeysRequest,
|
||||
response *api.InputPublicKeysResponse,
|
||||
) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "InputPublicKey")
|
||||
defer span.Finish()
|
||||
|
||||
apiURL := h.serverKeyAPIURL + ServerKeyInputPublicKeyPath
|
||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
||||
}
|
||||
|
||||
func (h *httpServerKeyInternalAPI) QueryPublicKeys(
|
||||
ctx context.Context,
|
||||
request *api.QueryPublicKeysRequest,
|
||||
response *api.QueryPublicKeysResponse,
|
||||
) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryPublicKey")
|
||||
defer span.Finish()
|
||||
|
||||
apiURL := h.serverKeyAPIURL + ServerKeyQueryPublicKeyPath
|
||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
||||
}
|
||||
43
signingkeyserver/inthttp/server.go
Normal file
43
signingkeyserver/inthttp/server.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package inthttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/api"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
func AddRoutes(s api.SigningKeyServerAPI, internalAPIMux *mux.Router, cache caching.ServerKeyCache) {
|
||||
internalAPIMux.Handle(ServerKeyQueryPublicKeyPath,
|
||||
httputil.MakeInternalAPI("queryPublicKeys", func(req *http.Request) util.JSONResponse {
|
||||
request := api.QueryPublicKeysRequest{}
|
||||
response := api.QueryPublicKeysResponse{}
|
||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
keys, err := s.FetchKeys(req.Context(), request.Requests)
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
response.Results = keys
|
||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||
}),
|
||||
)
|
||||
internalAPIMux.Handle(ServerKeyInputPublicKeyPath,
|
||||
httputil.MakeInternalAPI("inputPublicKeys", func(req *http.Request) util.JSONResponse {
|
||||
request := api.InputPublicKeysRequest{}
|
||||
response := api.InputPublicKeysResponse{}
|
||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if err := s.StoreKeys(req.Context(), request.Keys); err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||
}),
|
||||
)
|
||||
}
|
||||
318
signingkeyserver/serverkeyapi_test.go
Normal file
318
signingkeyserver/serverkeyapi_test.go
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
package signingkeyserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/routing"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/config"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type server struct {
|
||||
name gomatrixserverlib.ServerName // server name
|
||||
validity time.Duration // key validity duration from now
|
||||
config *config.SigningKeyServer // skeleton config, from TestMain
|
||||
fedconfig *config.FederationAPI //
|
||||
fedclient *gomatrixserverlib.FederationClient // uses MockRoundTripper
|
||||
cache *caching.Caches // server-specific cache
|
||||
api api.SigningKeyServerAPI // server-specific server key API
|
||||
}
|
||||
|
||||
func (s *server) renew() {
|
||||
// This updates the validity period to be an hour in the
|
||||
// future, which is particularly useful in server A and
|
||||
// server C's cases which have validity either as now or
|
||||
// in the past.
|
||||
s.validity = time.Hour
|
||||
s.config.Matrix.KeyValidityPeriod = s.validity
|
||||
}
|
||||
|
||||
var (
|
||||
serverKeyID = gomatrixserverlib.KeyID("ed25519:auto")
|
||||
serverA = &server{name: "a.com", validity: time.Duration(0)} // expires now
|
||||
serverB = &server{name: "b.com", validity: time.Hour} // expires in an hour
|
||||
serverC = &server{name: "c.com", validity: -time.Hour} // expired an hour ago
|
||||
)
|
||||
|
||||
var servers = map[string]*server{
|
||||
"a.com": serverA,
|
||||
"b.com": serverB,
|
||||
"c.com": serverC,
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Set up the server key API for each "server" that we
|
||||
// will use in our tests.
|
||||
for _, s := range servers {
|
||||
// Generate a new key.
|
||||
_, testPriv, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
panic("can't generate identity key: " + err.Error())
|
||||
}
|
||||
|
||||
// Create a new cache but don't enable prometheus!
|
||||
s.cache, err = caching.NewInMemoryLRUCache(false)
|
||||
if err != nil {
|
||||
panic("can't create cache: " + err.Error())
|
||||
}
|
||||
|
||||
// Draw up just enough Dendrite config for the server key
|
||||
// API to work.
|
||||
cfg := &config.Dendrite{}
|
||||
cfg.Defaults()
|
||||
cfg.Global.ServerName = gomatrixserverlib.ServerName(s.name)
|
||||
cfg.Global.PrivateKey = testPriv
|
||||
cfg.Global.KeyID = serverKeyID
|
||||
cfg.Global.KeyValidityPeriod = s.validity
|
||||
cfg.SigningKeyServer.Database.ConnectionString = config.DataSource("file::memory:")
|
||||
s.config = &cfg.SigningKeyServer
|
||||
s.fedconfig = &cfg.FederationAPI
|
||||
|
||||
// Create a transport which redirects federation requests to
|
||||
// the mock round tripper. Since we're not *really* listening for
|
||||
// federation requests then this will return the key instead.
|
||||
transport := &http.Transport{}
|
||||
transport.RegisterProtocol("matrix", &MockRoundTripper{})
|
||||
|
||||
// Create the federation client.
|
||||
s.fedclient = gomatrixserverlib.NewFederationClientWithTransport(
|
||||
s.config.Matrix.ServerName, serverKeyID, testPriv, true, transport,
|
||||
)
|
||||
|
||||
// Finally, build the server key APIs.
|
||||
s.api = NewInternalAPI(s.config, s.fedclient, s.cache)
|
||||
}
|
||||
|
||||
// Now that we have built our server key APIs, start the
|
||||
// rest of the tests.
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
type MockRoundTripper struct{}
|
||||
|
||||
func (m *MockRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err error) {
|
||||
// Check if the request is looking for keys from a server that
|
||||
// we know about in the test. The only reason this should go wrong
|
||||
// is if the test is broken.
|
||||
s, ok := servers[req.Host]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("server not known: %s", req.Host)
|
||||
}
|
||||
|
||||
// We're intercepting /matrix/key/v2/server requests here, so check
|
||||
// that the URL supplied in the request is for that.
|
||||
if req.URL.Path != "/_matrix/key/v2/server" {
|
||||
return nil, fmt.Errorf("unexpected request path: %s", req.URL.Path)
|
||||
}
|
||||
|
||||
// Get the keys and JSON-ify them.
|
||||
keys := routing.LocalKeys(s.fedconfig)
|
||||
body, err := json.MarshalIndent(keys.JSON, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// And respond.
|
||||
res = &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(body)),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestServersRequestOwnKeys(t *testing.T) {
|
||||
// Each server will request its own keys. There's no reason
|
||||
// for this to fail as each server should know its own keys.
|
||||
|
||||
for name, s := range servers {
|
||||
req := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: s.name,
|
||||
KeyID: serverKeyID,
|
||||
}
|
||||
res, err := s.api.FetchKeys(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp{
|
||||
req: gomatrixserverlib.AsTimestamp(time.Now()),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("server could not fetch own key: %s", err)
|
||||
}
|
||||
if _, ok := res[req]; !ok {
|
||||
t.Fatalf("server didn't return its own key in the results")
|
||||
}
|
||||
t.Logf("%s's key expires at %s\n", name, res[req].ValidUntilTS.Time())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCachingBehaviour(t *testing.T) {
|
||||
// Server A will request Server B's key, which has a validity
|
||||
// period of an hour from now. We should retrieve the key and
|
||||
// it should make it into the cache automatically.
|
||||
|
||||
req := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: serverB.name,
|
||||
KeyID: serverKeyID,
|
||||
}
|
||||
ts := gomatrixserverlib.AsTimestamp(time.Now())
|
||||
|
||||
res, err := serverA.api.FetchKeys(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp{
|
||||
req: ts,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("server A failed to retrieve server B key: %s", err)
|
||||
}
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("server B should have returned one key but instead returned %d keys", len(res))
|
||||
}
|
||||
if _, ok := res[req]; !ok {
|
||||
t.Fatalf("server B isn't included in the key fetch response")
|
||||
}
|
||||
|
||||
// At this point, if the previous key request was a success,
|
||||
// then the cache should now contain the key. Check if that's
|
||||
// the case - if it isn't then there's something wrong with
|
||||
// the cache implementation or we failed to get the key.
|
||||
|
||||
cres, ok := serverA.cache.GetServerKey(req, ts)
|
||||
if !ok {
|
||||
t.Fatalf("server B key should be in cache but isn't")
|
||||
}
|
||||
if !reflect.DeepEqual(cres, res[req]) {
|
||||
t.Fatalf("the cached result from server B wasn't what server B gave us")
|
||||
}
|
||||
|
||||
// If we ask the cache for the same key but this time for an event
|
||||
// that happened in +30 minutes. Since the validity period is for
|
||||
// another hour, then we should get a response back from the cache.
|
||||
|
||||
_, ok = serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(time.Minute*30)),
|
||||
)
|
||||
if !ok {
|
||||
t.Fatalf("server B key isn't in cache when it should be (+30 minutes)")
|
||||
}
|
||||
|
||||
// If we ask the cache for the same key but this time for an event
|
||||
// that happened in +90 minutes then we should expect to get no
|
||||
// cache result. This is because the cache shouldn't return a result
|
||||
// that is obviously past the validity of the event.
|
||||
|
||||
_, ok = serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(time.Minute*90)),
|
||||
)
|
||||
if ok {
|
||||
t.Fatalf("server B key is in cache when it shouldn't be (+90 minutes)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenewalBehaviour(t *testing.T) {
|
||||
// Server A will request Server C's key but their validity period
|
||||
// is an hour in the past. We'll retrieve the key as, even though it's
|
||||
// past its validity, it will be able to verify past events.
|
||||
|
||||
req := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: serverC.name,
|
||||
KeyID: serverKeyID,
|
||||
}
|
||||
|
||||
res, err := serverA.api.FetchKeys(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp{
|
||||
req: gomatrixserverlib.AsTimestamp(time.Now()),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("server A failed to retrieve server C key: %s", err)
|
||||
}
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("server C should have returned one key but instead returned %d keys", len(res))
|
||||
}
|
||||
if _, ok := res[req]; !ok {
|
||||
t.Fatalf("server C isn't included in the key fetch response")
|
||||
}
|
||||
|
||||
// If we ask the cache for the server key for an event that happened
|
||||
// 90 minutes ago then we should get a cache result, as the key hadn't
|
||||
// passed its validity by that point. The fact that the key is now in
|
||||
// the cache is, in itself, proof that we successfully retrieved the
|
||||
// key before.
|
||||
|
||||
oldcached, ok := serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(-time.Minute*90)),
|
||||
)
|
||||
if !ok {
|
||||
t.Fatalf("server C key isn't in cache when it should be (-90 minutes)")
|
||||
}
|
||||
|
||||
// If we now ask the cache for the same key but this time for an event
|
||||
// that only happened 30 minutes ago then we shouldn't get a cached
|
||||
// result, as the event happened after the key validity expired. This
|
||||
// is really just for sanity checking.
|
||||
|
||||
_, ok = serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(-time.Minute*30)),
|
||||
)
|
||||
if ok {
|
||||
t.Fatalf("server B key is in cache when it shouldn't be (-30 minutes)")
|
||||
}
|
||||
|
||||
// We're now going to kick server C into renewing its key. Since we're
|
||||
// happy at this point that the key that we already have is from the past
|
||||
// then repeating a key fetch should cause us to try and renew the key.
|
||||
// If so, then the new key will end up in our cache.
|
||||
|
||||
serverC.renew()
|
||||
|
||||
res, err = serverA.api.FetchKeys(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp{
|
||||
req: gomatrixserverlib.AsTimestamp(time.Now()),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("server A failed to retrieve server C key: %s", err)
|
||||
}
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("server C should have returned one key but instead returned %d keys", len(res))
|
||||
}
|
||||
if _, ok = res[req]; !ok {
|
||||
t.Fatalf("server C isn't included in the key fetch response")
|
||||
}
|
||||
|
||||
// We're now going to ask the cache what the new key validity is. If
|
||||
// it is still the same as the previous validity then we've failed to
|
||||
// retrieve the renewed key. If it's newer then we've successfully got
|
||||
// the renewed key.
|
||||
|
||||
newcached, ok := serverA.cache.GetServerKey(
|
||||
req,
|
||||
gomatrixserverlib.AsTimestamp(time.Now().Add(-time.Minute*30)),
|
||||
)
|
||||
if !ok {
|
||||
t.Fatalf("server B key isn't in cache when it shouldn't be (post-renewal)")
|
||||
}
|
||||
if oldcached.ValidUntilTS >= newcached.ValidUntilTS {
|
||||
t.Fatalf("the server B key should have been renewed but wasn't")
|
||||
}
|
||||
t.Log(res)
|
||||
}
|
||||
107
signingkeyserver/signingkeyserver.go
Normal file
107
signingkeyserver/signingkeyserver.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package signingkeyserver
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/config"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/api"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/internal"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/inthttp"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/storage"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/storage/cache"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions
|
||||
// on the given input API.
|
||||
func AddInternalRoutes(router *mux.Router, intAPI api.SigningKeyServerAPI, caches *caching.Caches) {
|
||||
inthttp.AddRoutes(intAPI, router, caches)
|
||||
}
|
||||
|
||||
// NewInternalAPI returns a concerete implementation of the internal API. Callers
|
||||
// can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes.
|
||||
func NewInternalAPI(
|
||||
cfg *config.SigningKeyServer,
|
||||
fedClient gomatrixserverlib.KeyClient,
|
||||
caches *caching.Caches,
|
||||
) api.SigningKeyServerAPI {
|
||||
innerDB, err := storage.NewDatabase(
|
||||
&cfg.Database,
|
||||
cfg.Matrix.ServerName,
|
||||
cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey),
|
||||
cfg.Matrix.KeyID,
|
||||
)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panicf("failed to connect to server key database")
|
||||
}
|
||||
|
||||
serverKeyDB, err := cache.NewKeyDatabase(innerDB, caches)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panicf("failed to set up caching wrapper for server key database")
|
||||
}
|
||||
|
||||
internalAPI := internal.ServerKeyAPI{
|
||||
ServerName: cfg.Matrix.ServerName,
|
||||
ServerPublicKey: cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey),
|
||||
ServerKeyID: cfg.Matrix.KeyID,
|
||||
ServerKeyValidity: cfg.Matrix.KeyValidityPeriod,
|
||||
OldServerKeys: cfg.Matrix.OldVerifyKeys,
|
||||
FedClient: fedClient,
|
||||
OurKeyRing: gomatrixserverlib.KeyRing{
|
||||
KeyFetchers: []gomatrixserverlib.KeyFetcher{},
|
||||
KeyDatabase: serverKeyDB,
|
||||
},
|
||||
}
|
||||
|
||||
addDirectFetcher := func() {
|
||||
internalAPI.OurKeyRing.KeyFetchers = append(
|
||||
internalAPI.OurKeyRing.KeyFetchers,
|
||||
&gomatrixserverlib.DirectKeyFetcher{
|
||||
Client: fedClient,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if cfg.PreferDirectFetch {
|
||||
addDirectFetcher()
|
||||
} else {
|
||||
defer addDirectFetcher()
|
||||
}
|
||||
|
||||
var b64e = base64.StdEncoding.WithPadding(base64.NoPadding)
|
||||
for _, ps := range cfg.KeyPerspectives {
|
||||
perspective := &gomatrixserverlib.PerspectiveKeyFetcher{
|
||||
PerspectiveServerName: ps.ServerName,
|
||||
PerspectiveServerKeys: map[gomatrixserverlib.KeyID]ed25519.PublicKey{},
|
||||
Client: fedClient,
|
||||
}
|
||||
|
||||
for _, key := range ps.Keys {
|
||||
rawkey, err := b64e.DecodeString(key.PublicKey)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"server_name": ps.ServerName,
|
||||
"public_key": key.PublicKey,
|
||||
}).Warn("Couldn't parse perspective key")
|
||||
continue
|
||||
}
|
||||
perspective.PerspectiveServerKeys[key.KeyID] = rawkey
|
||||
}
|
||||
|
||||
internalAPI.OurKeyRing.KeyFetchers = append(
|
||||
internalAPI.OurKeyRing.KeyFetchers,
|
||||
perspective,
|
||||
)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"server_name": ps.ServerName,
|
||||
"num_public_keys": len(ps.Keys),
|
||||
}).Info("Enabled perspective key fetcher")
|
||||
}
|
||||
|
||||
return &internalAPI
|
||||
}
|
||||
68
signingkeyserver/storage/cache/keydb.go
vendored
Normal file
68
signingkeyserver/storage/cache/keydb.go
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// A Database implements gomatrixserverlib.KeyDatabase and is used to store
|
||||
// the public keys for other matrix servers.
|
||||
type KeyDatabase struct {
|
||||
inner gomatrixserverlib.KeyDatabase
|
||||
cache caching.ServerKeyCache
|
||||
}
|
||||
|
||||
func NewKeyDatabase(inner gomatrixserverlib.KeyDatabase, cache caching.ServerKeyCache) (*KeyDatabase, error) {
|
||||
if inner == nil {
|
||||
return nil, errors.New("inner database can't be nil")
|
||||
}
|
||||
if cache == nil {
|
||||
return nil, errors.New("cache can't be nil")
|
||||
}
|
||||
return &KeyDatabase{
|
||||
inner: inner,
|
||||
cache: cache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FetcherName implements KeyFetcher
|
||||
func (d KeyDatabase) FetcherName() string {
|
||||
return "InMemoryKeyCache"
|
||||
}
|
||||
|
||||
// FetchKeys implements gomatrixserverlib.KeyDatabase
|
||||
func (d *KeyDatabase) FetchKeys(
|
||||
ctx context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
||||
results := make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult)
|
||||
for req, ts := range requests {
|
||||
if res, cached := d.cache.GetServerKey(req, ts); cached {
|
||||
results[req] = res
|
||||
delete(requests, req)
|
||||
}
|
||||
}
|
||||
fromDB, err := d.inner.FetchKeys(ctx, requests)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
for req, res := range fromDB {
|
||||
results[req] = res
|
||||
d.cache.StoreServerKey(req, res)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// StoreKeys implements gomatrixserverlib.KeyDatabase
|
||||
func (d *KeyDatabase) StoreKeys(
|
||||
ctx context.Context,
|
||||
keyMap map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
for req, res := range keyMap {
|
||||
d.cache.StoreServerKey(req, res)
|
||||
}
|
||||
return d.inner.StoreKeys(ctx, keyMap)
|
||||
}
|
||||
13
signingkeyserver/storage/interface.go
Normal file
13
signingkeyserver/storage/interface.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type Database interface {
|
||||
FetcherName() string
|
||||
FetchKeys(ctx context.Context, requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error)
|
||||
StoreKeys(ctx context.Context, keyMap map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult) error
|
||||
}
|
||||
45
signingkeyserver/storage/keydb.go
Normal file
45
signingkeyserver/storage/keydb.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2020 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.
|
||||
|
||||
// +build !wasm
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/config"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/storage/postgres"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/storage/sqlite3"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// NewDatabase opens a database connection.
|
||||
func NewDatabase(
|
||||
dbProperties *config.DatabaseOptions,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
serverKey ed25519.PublicKey,
|
||||
serverKeyID gomatrixserverlib.KeyID,
|
||||
) (Database, error) {
|
||||
switch {
|
||||
case dbProperties.ConnectionString.IsSQLite():
|
||||
return sqlite3.NewDatabase(dbProperties, serverName, serverKey, serverKeyID)
|
||||
case dbProperties.ConnectionString.IsPostgres():
|
||||
return postgres.NewDatabase(dbProperties, serverName, serverKey, serverKeyID)
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected database type")
|
||||
}
|
||||
}
|
||||
50
signingkeyserver/storage/keydb_wasm.go
Normal file
50
signingkeyserver/storage/keydb_wasm.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2020 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.
|
||||
|
||||
// +build wasm
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/signingkeyserver/storage/sqlite3"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// NewDatabase opens a database connection.
|
||||
func NewDatabase(
|
||||
dataSourceName string,
|
||||
dbProperties sqlutil.DbProperties, // nolint:unparam
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
serverKey ed25519.PublicKey,
|
||||
serverKeyID gomatrixserverlib.KeyID,
|
||||
) (Database, error) {
|
||||
uri, err := url.Parse(dataSourceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch uri.Scheme {
|
||||
case "postgres":
|
||||
return nil, fmt.Errorf("Cannot use postgres implementation")
|
||||
case "file":
|
||||
return sqlite3.NewDatabase(dataSourceName, serverName, serverKey, serverKeyID)
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot use postgres implementation")
|
||||
}
|
||||
}
|
||||
91
signingkeyserver/storage/postgres/keydb.go
Normal file
91
signingkeyserver/storage/postgres/keydb.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2017-2018 New Vector Ltd
|
||||
// Copyright 2019-2020 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 postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/config"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// A Database implements gomatrixserverlib.KeyDatabase and is used to store
|
||||
// the public keys for other matrix servers.
|
||||
type Database struct {
|
||||
statements serverKeyStatements
|
||||
}
|
||||
|
||||
// NewDatabase prepares a new key database.
|
||||
// It creates the necessary tables if they don't already exist.
|
||||
// It prepares all the SQL statements that it will use.
|
||||
// Returns an error if there was a problem talking to the database.
|
||||
func NewDatabase(
|
||||
dbProperties *config.DatabaseOptions,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
serverKey ed25519.PublicKey,
|
||||
serverKeyID gomatrixserverlib.KeyID,
|
||||
) (*Database, error) {
|
||||
db, err := sqlutil.Open(dbProperties)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := &Database{}
|
||||
err = d.statements.prepare(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// FetcherName implements KeyFetcher
|
||||
func (d Database) FetcherName() string {
|
||||
return "PostgresKeyDatabase"
|
||||
}
|
||||
|
||||
// FetchKeys implements gomatrixserverlib.KeyDatabase
|
||||
func (d *Database) FetchKeys(
|
||||
ctx context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
||||
return d.statements.bulkSelectServerKeys(ctx, requests)
|
||||
}
|
||||
|
||||
// StoreKeys implements gomatrixserverlib.KeyDatabase
|
||||
func (d *Database) StoreKeys(
|
||||
ctx context.Context,
|
||||
keyMap map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
// TODO: Inserting all the keys within a single transaction may
|
||||
// be more efficient since the transaction overhead can be quite
|
||||
// high for a single insert statement.
|
||||
var lastErr error
|
||||
for request, keys := range keyMap {
|
||||
if err := d.statements.upsertServerKeys(ctx, request, keys); err != nil {
|
||||
// Rather than returning immediately on error we try to insert the
|
||||
// remaining keys.
|
||||
// Since we are inserting the keys outside of a transaction it is
|
||||
// possible for some of the inserts to succeed even though some
|
||||
// of the inserts have failed.
|
||||
// Ensuring that we always insert all the keys we can means that
|
||||
// this behaviour won't depend on the iteration order of the map.
|
||||
lastErr = err
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
143
signingkeyserver/storage/postgres/server_key_table.go
Normal file
143
signingkeyserver/storage/postgres/server_key_table.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2017-2018 New Vector Ltd
|
||||
// Copyright 2019-2020 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 postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
const serverKeysSchema = `
|
||||
-- A cache of signing keys downloaded from remote servers.
|
||||
CREATE TABLE IF NOT EXISTS keydb_server_keys (
|
||||
-- The name of the matrix server the key is for.
|
||||
server_name TEXT NOT NULL,
|
||||
-- The ID of the server key.
|
||||
server_key_id TEXT NOT NULL,
|
||||
-- Combined server name and key ID separated by the ASCII unit separator
|
||||
-- to make it easier to run bulk queries.
|
||||
server_name_and_key_id TEXT NOT NULL,
|
||||
-- When the key is valid until as a millisecond timestamp.
|
||||
-- 0 if this is an expired key (in which case expired_ts will be non-zero)
|
||||
valid_until_ts BIGINT NOT NULL,
|
||||
-- When the key expired as a millisecond timestamp.
|
||||
-- 0 if this is an active key (in which case valid_until_ts will be non-zero)
|
||||
expired_ts BIGINT NOT NULL,
|
||||
-- The base64-encoded public key.
|
||||
server_key TEXT NOT NULL,
|
||||
CONSTRAINT keydb_server_keys_unique UNIQUE (server_name, server_key_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS keydb_server_name_and_key_id ON keydb_server_keys (server_name_and_key_id);
|
||||
`
|
||||
|
||||
const bulkSelectServerKeysSQL = "" +
|
||||
"SELECT server_name, server_key_id, valid_until_ts, expired_ts, " +
|
||||
" server_key FROM keydb_server_keys" +
|
||||
" WHERE server_name_and_key_id = ANY($1)"
|
||||
|
||||
const upsertServerKeysSQL = "" +
|
||||
"INSERT INTO keydb_server_keys (server_name, server_key_id," +
|
||||
" server_name_and_key_id, valid_until_ts, expired_ts, server_key)" +
|
||||
" VALUES ($1, $2, $3, $4, $5, $6)" +
|
||||
" ON CONFLICT ON CONSTRAINT keydb_server_keys_unique" +
|
||||
" DO UPDATE SET valid_until_ts = $4, expired_ts = $5, server_key = $6"
|
||||
|
||||
type serverKeyStatements struct {
|
||||
bulkSelectServerKeysStmt *sql.Stmt
|
||||
upsertServerKeysStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (s *serverKeyStatements) prepare(db *sql.DB) (err error) {
|
||||
_, err = db.Exec(serverKeysSchema)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if s.bulkSelectServerKeysStmt, err = db.Prepare(bulkSelectServerKeysSQL); err != nil {
|
||||
return
|
||||
}
|
||||
if s.upsertServerKeysStmt, err = db.Prepare(upsertServerKeysSQL); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *serverKeyStatements) bulkSelectServerKeys(
|
||||
ctx context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
||||
var nameAndKeyIDs []string
|
||||
for request := range requests {
|
||||
nameAndKeyIDs = append(nameAndKeyIDs, nameAndKeyID(request))
|
||||
}
|
||||
stmt := s.bulkSelectServerKeysStmt
|
||||
rows, err := stmt.QueryContext(ctx, pq.StringArray(nameAndKeyIDs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "bulkSelectServerKeys: rows.close() failed")
|
||||
results := map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult{}
|
||||
for rows.Next() {
|
||||
var serverName string
|
||||
var keyID string
|
||||
var key string
|
||||
var validUntilTS int64
|
||||
var expiredTS int64
|
||||
if err = rows.Scan(&serverName, &keyID, &validUntilTS, &expiredTS, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: gomatrixserverlib.ServerName(serverName),
|
||||
KeyID: gomatrixserverlib.KeyID(keyID),
|
||||
}
|
||||
vk := gomatrixserverlib.VerifyKey{}
|
||||
err = vk.Key.Decode(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results[r] = gomatrixserverlib.PublicKeyLookupResult{
|
||||
VerifyKey: vk,
|
||||
ValidUntilTS: gomatrixserverlib.Timestamp(validUntilTS),
|
||||
ExpiredTS: gomatrixserverlib.Timestamp(expiredTS),
|
||||
}
|
||||
}
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
func (s *serverKeyStatements) upsertServerKeys(
|
||||
ctx context.Context,
|
||||
request gomatrixserverlib.PublicKeyLookupRequest,
|
||||
key gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
_, err := s.upsertServerKeysStmt.ExecContext(
|
||||
ctx,
|
||||
string(request.ServerName),
|
||||
string(request.KeyID),
|
||||
nameAndKeyID(request),
|
||||
key.ValidUntilTS,
|
||||
key.ExpiredTS,
|
||||
key.Key.Encode(),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func nameAndKeyID(request gomatrixserverlib.PublicKeyLookupRequest) string {
|
||||
return string(request.ServerName) + "\x1F" + string(request.KeyID)
|
||||
}
|
||||
99
signingkeyserver/storage/sqlite3/keydb.go
Normal file
99
signingkeyserver/storage/sqlite3/keydb.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2017-2018 New Vector Ltd
|
||||
// Copyright 2019-2020 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 sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/config"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// A Database implements gomatrixserverlib.KeyDatabase and is used to store
|
||||
// the public keys for other matrix servers.
|
||||
type Database struct {
|
||||
writer sqlutil.Writer
|
||||
statements serverKeyStatements
|
||||
}
|
||||
|
||||
// NewDatabase prepares a new key database.
|
||||
// It creates the necessary tables if they don't already exist.
|
||||
// It prepares all the SQL statements that it will use.
|
||||
// Returns an error if there was a problem talking to the database.
|
||||
func NewDatabase(
|
||||
dbProperties *config.DatabaseOptions,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
serverKey ed25519.PublicKey,
|
||||
serverKeyID gomatrixserverlib.KeyID,
|
||||
) (*Database, error) {
|
||||
db, err := sqlutil.Open(dbProperties)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := &Database{
|
||||
writer: sqlutil.NewExclusiveWriter(),
|
||||
}
|
||||
err = d.statements.prepare(db, d.writer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// FetcherName implements KeyFetcher
|
||||
func (d Database) FetcherName() string {
|
||||
return "SqliteKeyDatabase"
|
||||
}
|
||||
|
||||
// FetchKeys implements gomatrixserverlib.KeyDatabase
|
||||
func (d *Database) FetchKeys(
|
||||
ctx context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
||||
return d.statements.bulkSelectServerKeys(ctx, requests)
|
||||
}
|
||||
|
||||
// StoreKeys implements gomatrixserverlib.KeyDatabase
|
||||
func (d *Database) StoreKeys(
|
||||
ctx context.Context,
|
||||
keyMap map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
// TODO: Inserting all the keys within a single transaction may
|
||||
// be more efficient since the transaction overhead can be quite
|
||||
// high for a single insert statement.
|
||||
var lastErr error
|
||||
for request, keys := range keyMap {
|
||||
if err := d.statements.upsertServerKeys(ctx, request, keys); err != nil {
|
||||
// Rather than returning immediately on error we try to insert the
|
||||
// remaining keys.
|
||||
// Since we are inserting the keys outside of a transaction it is
|
||||
// possible for some of the inserts to succeed even though some
|
||||
// of the inserts have failed.
|
||||
// Ensuring that we always insert all the keys we can means that
|
||||
// this behaviour won't depend on the iteration order of the map.
|
||||
lastErr = err
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
159
signingkeyserver/storage/sqlite3/server_key_table.go
Normal file
159
signingkeyserver/storage/sqlite3/server_key_table.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2017-2018 New Vector Ltd
|
||||
// Copyright 2019-2020 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 sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
const serverKeysSchema = `
|
||||
-- A cache of signing keys downloaded from remote servers.
|
||||
CREATE TABLE IF NOT EXISTS keydb_server_keys (
|
||||
-- The name of the matrix server the key is for.
|
||||
server_name TEXT NOT NULL,
|
||||
-- The ID of the server key.
|
||||
server_key_id TEXT NOT NULL,
|
||||
-- Combined server name and key ID separated by the ASCII unit separator
|
||||
-- to make it easier to run bulk queries.
|
||||
server_name_and_key_id TEXT NOT NULL,
|
||||
-- When the key is valid until as a millisecond timestamp.
|
||||
-- 0 if this is an expired key (in which case expired_ts will be non-zero)
|
||||
valid_until_ts BIGINT NOT NULL,
|
||||
-- When the key expired as a millisecond timestamp.
|
||||
-- 0 if this is an active key (in which case valid_until_ts will be non-zero)
|
||||
expired_ts BIGINT NOT NULL,
|
||||
-- The base64-encoded public key.
|
||||
server_key TEXT NOT NULL,
|
||||
UNIQUE (server_name, server_key_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS keydb_server_name_and_key_id ON keydb_server_keys (server_name_and_key_id);
|
||||
`
|
||||
|
||||
const bulkSelectServerKeysSQL = "" +
|
||||
"SELECT server_name, server_key_id, valid_until_ts, expired_ts, " +
|
||||
" server_key FROM keydb_server_keys" +
|
||||
" WHERE server_name_and_key_id IN ($1)"
|
||||
|
||||
const upsertServerKeysSQL = "" +
|
||||
"INSERT INTO keydb_server_keys (server_name, server_key_id," +
|
||||
" server_name_and_key_id, valid_until_ts, expired_ts, server_key)" +
|
||||
" VALUES ($1, $2, $3, $4, $5, $6)" +
|
||||
" ON CONFLICT (server_name, server_key_id)" +
|
||||
" DO UPDATE SET valid_until_ts = $4, expired_ts = $5, server_key = $6"
|
||||
|
||||
type serverKeyStatements struct {
|
||||
db *sql.DB
|
||||
writer sqlutil.Writer
|
||||
bulkSelectServerKeysStmt *sql.Stmt
|
||||
upsertServerKeysStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (s *serverKeyStatements) prepare(db *sql.DB, writer sqlutil.Writer) (err error) {
|
||||
s.db = db
|
||||
s.writer = writer
|
||||
_, err = db.Exec(serverKeysSchema)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if s.bulkSelectServerKeysStmt, err = db.Prepare(bulkSelectServerKeysSQL); err != nil {
|
||||
return
|
||||
}
|
||||
if s.upsertServerKeysStmt, err = db.Prepare(upsertServerKeysSQL); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *serverKeyStatements) bulkSelectServerKeys(
|
||||
ctx context.Context,
|
||||
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
||||
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
||||
nameAndKeyIDs := make([]string, 0, len(requests))
|
||||
for request := range requests {
|
||||
nameAndKeyIDs = append(nameAndKeyIDs, nameAndKeyID(request))
|
||||
}
|
||||
results := make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, len(requests))
|
||||
iKeyIDs := make([]interface{}, len(nameAndKeyIDs))
|
||||
for i, v := range nameAndKeyIDs {
|
||||
iKeyIDs[i] = v
|
||||
}
|
||||
|
||||
err := sqlutil.RunLimitedVariablesQuery(
|
||||
ctx, bulkSelectServerKeysSQL, s.db, iKeyIDs, sqlutil.SQLite3MaxVariables,
|
||||
func(rows *sql.Rows) error {
|
||||
for rows.Next() {
|
||||
var serverName string
|
||||
var keyID string
|
||||
var key string
|
||||
var validUntilTS int64
|
||||
var expiredTS int64
|
||||
if err := rows.Scan(&serverName, &keyID, &validUntilTS, &expiredTS, &key); err != nil {
|
||||
return fmt.Errorf("bulkSelectServerKeys: %v", err)
|
||||
}
|
||||
r := gomatrixserverlib.PublicKeyLookupRequest{
|
||||
ServerName: gomatrixserverlib.ServerName(serverName),
|
||||
KeyID: gomatrixserverlib.KeyID(keyID),
|
||||
}
|
||||
vk := gomatrixserverlib.VerifyKey{}
|
||||
err := vk.Key.Decode(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bulkSelectServerKeys: %v", err)
|
||||
}
|
||||
results[r] = gomatrixserverlib.PublicKeyLookupResult{
|
||||
VerifyKey: vk,
|
||||
ValidUntilTS: gomatrixserverlib.Timestamp(validUntilTS),
|
||||
ExpiredTS: gomatrixserverlib.Timestamp(expiredTS),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s *serverKeyStatements) upsertServerKeys(
|
||||
ctx context.Context,
|
||||
request gomatrixserverlib.PublicKeyLookupRequest,
|
||||
key gomatrixserverlib.PublicKeyLookupResult,
|
||||
) error {
|
||||
return s.writer.Do(s.db, nil, func(txn *sql.Tx) error {
|
||||
stmt := sqlutil.TxStmt(txn, s.upsertServerKeysStmt)
|
||||
_, err := stmt.ExecContext(
|
||||
ctx,
|
||||
string(request.ServerName),
|
||||
string(request.KeyID),
|
||||
nameAndKeyID(request),
|
||||
key.ValidUntilTS,
|
||||
key.ExpiredTS,
|
||||
key.Key.Encode(),
|
||||
)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func nameAndKeyID(request gomatrixserverlib.PublicKeyLookupRequest) string {
|
||||
return string(request.ServerName) + "\x1F" + string(request.KeyID)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue