Cross-signing validation for self-sigs, expose signatures over `/user/keys/query` and `/user/devices/{userId}` (#1962)

* Enable unstable feature again

* Try to verify when a device signs a key

* Try to verify when a key signs a device

* It's the self-signing key, not the master key

* Fix error

* Try to verify master key uploads

* Actually we can't guarantee we can do that so nevermind

* Add signatures into /devices/list request

* Fix nil pointer

* Reprioritise map creation

* Don't skip devices that don't have signatures

* Add some debug logging

* Fix logic error in QuerySignatures

* Fix bugs

* Expose master and self-signing keys on /devices/list hopefully

* maps are tedious

* Expose signatures via /keys/query

* Upload signatures when uploading keys

* Fixes

* Disable the feature again
main
Neil Alexander 2021-08-06 10:13:35 +01:00 committed by GitHub
parent 8e5a0139b5
commit e95b1fd238
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 341 additions and 37 deletions

View File

@ -37,12 +37,28 @@ func GetUserDevices(
return jsonerror.InternalServerError() return jsonerror.InternalServerError()
} }
sigReq := &keyapi.QuerySignaturesRequest{
TargetIDs: map[string][]gomatrixserverlib.KeyID{},
}
sigRes := &keyapi.QuerySignaturesResponse{}
for _, dev := range res.Devices {
sigReq.TargetIDs[userID] = append(sigReq.TargetIDs[userID], gomatrixserverlib.KeyID(dev.DeviceID))
}
keyAPI.QuerySignatures(req.Context(), sigReq, sigRes)
response := gomatrixserverlib.RespUserDevices{ response := gomatrixserverlib.RespUserDevices{
UserID: userID, UserID: userID,
StreamID: res.StreamID, StreamID: res.StreamID,
Devices: []gomatrixserverlib.RespUserDevice{}, Devices: []gomatrixserverlib.RespUserDevice{},
} }
if masterKey, ok := sigRes.MasterKeys[userID]; ok {
response.MasterKey = &masterKey
}
if selfSigningKey, ok := sigRes.SelfSigningKeys[userID]; ok {
response.SelfSigningKey = &selfSigningKey
}
for _, dev := range res.Devices { for _, dev := range res.Devices {
var key gomatrixserverlib.RespUserDeviceKeys var key gomatrixserverlib.RespUserDeviceKeys
err := json.Unmarshal(dev.DeviceKeys.KeyJSON, &key) err := json.Unmarshal(dev.DeviceKeys.KeyJSON, &key)
@ -56,6 +72,20 @@ func GetUserDevices(
DisplayName: dev.DisplayName, DisplayName: dev.DisplayName,
Keys: key, Keys: key,
} }
if targetUser, ok := sigRes.Signatures[userID]; ok {
if targetKey, ok := targetUser[gomatrixserverlib.KeyID(dev.DeviceID)]; ok {
for sourceUserID, forSourceUser := range targetKey {
for sourceKeyID, sourceKey := range forSourceUser {
if _, ok := device.Keys.Signatures[sourceUserID]; !ok {
device.Keys.Signatures[sourceUserID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
}
device.Keys.Signatures[sourceUserID][sourceKeyID] = sourceKey
}
}
}
}
response.Devices = append(response.Devices, device) response.Devices = append(response.Devices, device)
} }

View File

@ -20,6 +20,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/matrix-org/dendrite/keyserver/types"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
) )
@ -38,6 +39,7 @@ type KeyInternalAPI interface {
QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse) QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse)
QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse) QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse)
QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse) QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse)
QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse)
} }
// KeyError is returned if there was a problem performing/querying the server // KeyError is returned if there was a problem performing/querying the server
@ -242,6 +244,24 @@ type QueryDeviceMessagesResponse struct {
Error *KeyError Error *KeyError
} }
type QuerySignaturesRequest struct {
// A map of target user ID -> target key/device IDs to retrieve signatures for
TargetIDs map[string][]gomatrixserverlib.KeyID `json:"target_ids"`
}
type QuerySignaturesResponse struct {
// A map of target user ID -> target key/device ID -> origin user ID -> origin key/device ID -> signatures
Signatures map[string]map[gomatrixserverlib.KeyID]types.CrossSigningSigMap
// A map of target user ID -> cross-signing master key
MasterKeys map[string]gomatrixserverlib.CrossSigningKey
// A map of target user ID -> cross-signing self-signing key
SelfSigningKeys map[string]gomatrixserverlib.CrossSigningKey
// A map of target user ID -> cross-signing user-signing key
UserSigningKeys map[string]gomatrixserverlib.CrossSigningKey
// The request error, if any
Error *KeyError
}
type InputDeviceListUpdateRequest struct { type InputDeviceListUpdateRequest struct {
Event gomatrixserverlib.DeviceListUpdateEvent Event gomatrixserverlib.DeviceListUpdateEvent
} }

View File

@ -17,6 +17,7 @@ package internal
import ( import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings" "strings"
@ -104,7 +105,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
// If the user hasn't given a new master key, then let's go and get their // If the user hasn't given a new master key, then let's go and get their
// existing keys from the database. // existing keys from the database.
if !hasMasterKey { if !hasMasterKey {
existingKeys, err := a.DB.CrossSigningKeysForUser(ctx, req.UserID) existingKeys, err := a.DB.CrossSigningKeysDataForUser(ctx, req.UserID)
if err != nil { if err != nil {
res.Error = &api.KeyError{ res.Error = &api.KeyError{
Err: "Retrieving cross-signing keys from database failed: " + err.Error(), Err: "Retrieving cross-signing keys from database failed: " + err.Error(),
@ -177,21 +178,24 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
} }
for purpose, key := range toVerify { for purpose, key := range toVerify {
// Collect together the key IDs we need to verify with. This will include // Collect together the key IDs we need to verify with. This will include
// all of the key IDs specified in the signatures. We don't do this for // all of the key IDs specified in the signatures.
// the master key because we have no means to verify the signatures - we keyJSON, err := json.Marshal(key)
// instead just need to store them. if err != nil {
if purpose != gomatrixserverlib.CrossSigningKeyPurposeMaster { res.Error = &api.KeyError{
// Marshal the specific key back into JSON so that we can verify the Err: fmt.Sprintf("The JSON of the key section is invalid: %s", err.Error()),
// signature of it.
keyJSON, err := json.Marshal(key)
if err != nil {
res.Error = &api.KeyError{
Err: fmt.Sprintf("The JSON of the key section is invalid: %s", err.Error()),
}
return
} }
return
}
// Now check if the subkey is signed by the master key. switch purpose {
case gomatrixserverlib.CrossSigningKeyPurposeMaster:
// The master key might have a signature attached to it from the
// previous key, or from a device key, but there's no real need
// to verify it. Clients will perform key checks when the master
// key changes.
default:
// Sub-keys should be signed by the master key.
if err := gomatrixserverlib.VerifyJSON(req.UserID, masterKeyID, ed25519.PublicKey(masterKey), keyJSON); err != nil { if err := gomatrixserverlib.VerifyJSON(req.UserID, masterKeyID, ed25519.PublicKey(masterKey), keyJSON); err != nil {
res.Error = &api.KeyError{ res.Error = &api.KeyError{
Err: fmt.Sprintf("The %q sub-key failed master key signature verification: %s", purpose, err.Error()), Err: fmt.Sprintf("The %q sub-key failed master key signature verification: %s", purpose, err.Error()),
@ -212,10 +216,44 @@ func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.P
res.Error = &api.KeyError{ res.Error = &api.KeyError{
Err: fmt.Sprintf("a.DB.StoreCrossSigningKeysForUser: %s", err), Err: fmt.Sprintf("a.DB.StoreCrossSigningKeysForUser: %s", err),
} }
return
}
// Now upload any signatures that were included with the keys.
for _, key := range toVerify {
var targetKeyID gomatrixserverlib.KeyID
for targetKey := range key.Keys { // iterates once, see sanityCheckKey
targetKeyID = targetKey
}
for sigUserID, forSigUserID := range key.Signatures {
if sigUserID != req.UserID {
continue
}
for sigKeyID, sigBytes := range forSigUserID {
if err := a.DB.StoreCrossSigningSigsForTarget(ctx, sigUserID, sigKeyID, req.UserID, targetKeyID, sigBytes); err != nil {
res.Error = &api.KeyError{
Err: fmt.Sprintf("a.DB.StoreCrossSigningSigsForTarget: %s", err),
}
return
}
}
}
} }
} }
func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req *api.PerformUploadDeviceSignaturesRequest, res *api.PerformUploadDeviceSignaturesResponse) { func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req *api.PerformUploadDeviceSignaturesRequest, res *api.PerformUploadDeviceSignaturesResponse) {
// Before we do anything, we need the master and self-signing keys for this user.
// Then we can verify the signatures make sense.
queryReq := &api.QueryKeysRequest{
UserID: req.UserID,
UserToDevices: map[string][]string{},
}
queryRes := &api.QueryKeysResponse{}
for userID := range req.Signatures {
queryReq.UserToDevices[userID] = []string{}
}
a.QueryKeys(ctx, queryReq, queryRes)
selfSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} selfSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
otherSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{} otherSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
@ -254,14 +292,14 @@ func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req
} }
} }
if err := a.processSelfSignatures(ctx, req.UserID, selfSignatures); err != nil { if err := a.processSelfSignatures(ctx, req.UserID, queryRes, selfSignatures); err != nil {
res.Error = &api.KeyError{ res.Error = &api.KeyError{
Err: fmt.Sprintf("a.processSelfSignatures: %s", err), Err: fmt.Sprintf("a.processSelfSignatures: %s", err),
} }
return return
} }
if err := a.processOtherSignatures(ctx, req.UserID, otherSignatures); err != nil { if err := a.processOtherSignatures(ctx, req.UserID, queryRes, otherSignatures); err != nil {
res.Error = &api.KeyError{ res.Error = &api.KeyError{
Err: fmt.Sprintf("a.processOtherSignatures: %s", err), Err: fmt.Sprintf("a.processOtherSignatures: %s", err),
} }
@ -270,7 +308,7 @@ func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req
} }
func (a *KeyInternalAPI) processSelfSignatures( func (a *KeyInternalAPI) processSelfSignatures(
ctx context.Context, _ string, ctx context.Context, _ string, queryRes *api.QueryKeysResponse,
signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice, signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice,
) error { ) error {
// Here we will process: // Here we will process:
@ -281,8 +319,39 @@ func (a *KeyInternalAPI) processSelfSignatures(
for targetKeyID, signature := range forTargetUserID { for targetKeyID, signature := range forTargetUserID {
switch sig := signature.CrossSigningBody.(type) { switch sig := signature.CrossSigningBody.(type) {
case *gomatrixserverlib.CrossSigningKey: case *gomatrixserverlib.CrossSigningKey:
// The user is signing their master key with one of their devices
// The QueryKeys response should contain the device key hopefully.
// First we need to marshal the blob back into JSON so we can verify
// it.
j, err := json.Marshal(sig)
if err != nil {
return fmt.Errorf("json.Marshal: %w", err)
}
for originUserID, forOriginUserID := range sig.Signatures { for originUserID, forOriginUserID := range sig.Signatures {
originDeviceKeys, ok := queryRes.DeviceKeys[originUserID]
if !ok {
return fmt.Errorf("missing device keys for user %q", originUserID)
}
for originKeyID, originSig := range forOriginUserID { for originKeyID, originSig := range forOriginUserID {
originDeviceKeyID := gomatrixserverlib.KeyID("ed25519:" + originKeyID)
var originKey gomatrixserverlib.DeviceKeys
if err := json.Unmarshal(originDeviceKeys[string(originKeyID)], &originKey); err != nil {
return fmt.Errorf("json.Unmarshal: %w", err)
}
originSigningKey, ok := originKey.Keys[originDeviceKeyID]
if !ok {
return fmt.Errorf("missing origin signing key %q", originDeviceKeyID)
}
originSigningKeyPublic := ed25519.PublicKey(originSigningKey)
if err := gomatrixserverlib.VerifyJSON(originUserID, originDeviceKeyID, originSigningKeyPublic, j); err != nil {
return fmt.Errorf("gomatrixserverlib.VerifyJSON: %w", err)
}
if err := a.DB.StoreCrossSigningSigsForTarget( if err := a.DB.StoreCrossSigningSigsForTarget(
ctx, originUserID, originKeyID, targetUserID, targetKeyID, originSig, ctx, originUserID, originKeyID, targetUserID, targetKeyID, originSig,
); err != nil { ); err != nil {
@ -292,8 +361,35 @@ func (a *KeyInternalAPI) processSelfSignatures(
} }
case *gomatrixserverlib.DeviceKeys: case *gomatrixserverlib.DeviceKeys:
// The user is signing one of their devices with their self-signing key
// The QueryKeys response should contain the master key hopefully.
// First we need to marshal the blob back into JSON so we can verify
// it.
j, err := json.Marshal(sig)
if err != nil {
return fmt.Errorf("json.Marshal: %w", err)
}
for originUserID, forOriginUserID := range sig.Signatures { for originUserID, forOriginUserID := range sig.Signatures {
for originKeyID, originSig := range forOriginUserID { for originKeyID, originSig := range forOriginUserID {
originSelfSigningKeys, ok := queryRes.SelfSigningKeys[originUserID]
if !ok {
return fmt.Errorf("missing self-signing key for user %q", originUserID)
}
var originSelfSigningKeyID gomatrixserverlib.KeyID
var originSelfSigningKey gomatrixserverlib.Base64Bytes
for keyID, key := range originSelfSigningKeys.Keys {
originSelfSigningKeyID, originSelfSigningKey = keyID, key
break
}
originSelfSigningKeyPublic := ed25519.PublicKey(originSelfSigningKey)
if err := gomatrixserverlib.VerifyJSON(originUserID, originSelfSigningKeyID, originSelfSigningKeyPublic, j); err != nil {
return fmt.Errorf("gomatrixserverlib.VerifyJSON: %w", err)
}
if err := a.DB.StoreCrossSigningSigsForTarget( if err := a.DB.StoreCrossSigningSigsForTarget(
ctx, originUserID, originKeyID, targetUserID, targetKeyID, originSig, ctx, originUserID, originKeyID, targetUserID, targetKeyID, originSig,
); err != nil { ); err != nil {
@ -312,7 +408,7 @@ func (a *KeyInternalAPI) processSelfSignatures(
} }
func (a *KeyInternalAPI) processOtherSignatures( func (a *KeyInternalAPI) processOtherSignatures(
ctx context.Context, userID string, ctx context.Context, userID string, queryRes *api.QueryKeysResponse,
signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice, signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice,
) error { ) error {
// Here we will process: // Here we will process:
@ -331,20 +427,14 @@ func (a *KeyInternalAPI) crossSigningKeysFromDatabase(
continue continue
} }
for keyType, keyData := range keys { for keyType, key := range keys {
b64 := keyData.Encode() var keyID gomatrixserverlib.KeyID
keyID := gomatrixserverlib.KeyID("ed25519:" + b64) for id := range key.Keys {
key := gomatrixserverlib.CrossSigningKey{ keyID = id
UserID: userID, break
Usage: []gomatrixserverlib.CrossSigningKeyPurpose{
keyType,
},
Keys: map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{
keyID: keyData,
},
} }
sigs, err := a.DB.CrossSigningSigsForTarget(ctx, userID, keyID) sigMap, err := a.DB.CrossSigningSigsForTarget(ctx, userID, keyID)
if err != nil { if err != nil {
logrus.WithError(err).Errorf("Failed to get cross-signing signatures for user %q key %q", userID, keyID) logrus.WithError(err).Errorf("Failed to get cross-signing signatures for user %q key %q", userID, keyID)
continue continue
@ -360,7 +450,7 @@ func (a *KeyInternalAPI) crossSigningKeysFromDatabase(
key.Signatures[originUserID][originKeyID] = signature key.Signatures[originUserID][originKeyID] = signature
} }
for originUserID, forOrigin := range sigs { for originUserID, forOrigin := range sigMap {
for originKeyID, signature := range forOrigin { for originKeyID, signature := range forOrigin {
switch { switch {
case req.UserID != "" && originUserID == req.UserID: case req.UserID != "" && originUserID == req.UserID:
@ -387,3 +477,70 @@ func (a *KeyInternalAPI) crossSigningKeysFromDatabase(
} }
} }
} }
func (a *KeyInternalAPI) QuerySignatures(ctx context.Context, req *api.QuerySignaturesRequest, res *api.QuerySignaturesResponse) {
for targetUserID, forTargetUser := range req.TargetIDs {
for _, targetKeyID := range forTargetUser {
keyMap, err := a.DB.CrossSigningKeysForUser(ctx, targetUserID)
if err != nil {
if err == sql.ErrNoRows {
continue
}
res.Error = &api.KeyError{
Err: fmt.Sprintf("a.DB.CrossSigningKeysForUser: %s", err),
}
}
for targetPurpose, targetKey := range keyMap {
switch targetPurpose {
case gomatrixserverlib.CrossSigningKeyPurposeMaster:
if res.MasterKeys == nil {
res.MasterKeys = map[string]gomatrixserverlib.CrossSigningKey{}
}
res.MasterKeys[targetUserID] = targetKey
case gomatrixserverlib.CrossSigningKeyPurposeSelfSigning:
if res.SelfSigningKeys == nil {
res.SelfSigningKeys = map[string]gomatrixserverlib.CrossSigningKey{}
}
res.SelfSigningKeys[targetUserID] = targetKey
case gomatrixserverlib.CrossSigningKeyPurposeUserSigning:
if res.UserSigningKeys == nil {
res.UserSigningKeys = map[string]gomatrixserverlib.CrossSigningKey{}
}
res.UserSigningKeys[targetUserID] = targetKey
}
}
sigMap, err := a.DB.CrossSigningSigsForTarget(ctx, targetUserID, targetKeyID)
if err != nil {
if err == sql.ErrNoRows {
continue
}
res.Error = &api.KeyError{
Err: fmt.Sprintf("a.DB.CrossSigningSigsForTarget: %s", err),
}
return
}
for sourceUserID, forSourceUser := range sigMap {
for sourceKeyID, sourceSig := range forSourceUser {
if res.Signatures == nil {
res.Signatures = map[string]map[gomatrixserverlib.KeyID]types.CrossSigningSigMap{}
}
if _, ok := res.Signatures[targetUserID]; !ok {
res.Signatures[targetUserID] = map[gomatrixserverlib.KeyID]types.CrossSigningSigMap{}
}
if _, ok := res.Signatures[targetUserID][targetKeyID]; !ok {
res.Signatures[targetUserID][targetKeyID] = types.CrossSigningSigMap{}
}
if _, ok := res.Signatures[targetUserID][targetKeyID][sourceUserID]; !ok {
res.Signatures[targetUserID][targetKeyID][sourceUserID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
}
res.Signatures[targetUserID][targetKeyID][sourceUserID][sourceKeyID] = sourceSig
}
}
}
}
}

View File

@ -300,12 +300,38 @@ func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysReques
// attempt to satisfy key queries from the local database first as we should get device updates pushed to us // attempt to satisfy key queries from the local database first as we should get device updates pushed to us
domainToDeviceKeys = a.remoteKeysFromDatabase(ctx, res, domainToDeviceKeys) domainToDeviceKeys = a.remoteKeysFromDatabase(ctx, res, domainToDeviceKeys)
if len(domainToDeviceKeys) == 0 && len(domainToCrossSigningKeys) == 0 { if len(domainToDeviceKeys) > 0 || len(domainToCrossSigningKeys) > 0 {
return // nothing to query // perform key queries for remote devices
a.queryRemoteKeys(ctx, req.Timeout, res, domainToDeviceKeys, domainToCrossSigningKeys)
} }
// perform key queries for remote devices // Finally, append signatures that we know about
a.queryRemoteKeys(ctx, req.Timeout, res, domainToDeviceKeys, domainToCrossSigningKeys) // TODO: This is horrible because we need to round-trip the signature from
// JSON, add the signatures and marshal it again, for some reason?
for userID, forUserID := range res.DeviceKeys {
for keyID, key := range forUserID {
sigMap, err := a.DB.CrossSigningSigsForTarget(ctx, userID, gomatrixserverlib.KeyID(keyID))
if err != nil {
logrus.WithError(err).Errorf("a.DB.CrossSigningSigsForTarget failed")
continue
}
if len(sigMap) == 0 {
continue
}
var deviceKey gomatrixserverlib.DeviceKeys
if err = json.Unmarshal(key, &deviceKey); err != nil {
continue
}
for sourceUserID, forSourceUser := range sigMap {
for sourceKeyID, sourceSig := range forSourceUser {
deviceKey.Signatures[sourceUserID][sourceKeyID] = sourceSig
}
}
if js, err := json.Marshal(deviceKey); err == nil {
res.DeviceKeys[userID][keyID] = js
}
}
}
} }
func (a *KeyInternalAPI) remoteKeysFromDatabase( func (a *KeyInternalAPI) remoteKeysFromDatabase(

View File

@ -36,6 +36,7 @@ const (
QueryKeyChangesPath = "/keyserver/queryKeyChanges" QueryKeyChangesPath = "/keyserver/queryKeyChanges"
QueryOneTimeKeysPath = "/keyserver/queryOneTimeKeys" QueryOneTimeKeysPath = "/keyserver/queryOneTimeKeys"
QueryDeviceMessagesPath = "/keyserver/queryDeviceMessages" QueryDeviceMessagesPath = "/keyserver/queryDeviceMessages"
QuerySignaturesPath = "/keyserver/querySignatures"
) )
// NewKeyServerClient creates a KeyInternalAPI implemented by talking to a HTTP POST API. // NewKeyServerClient creates a KeyInternalAPI implemented by talking to a HTTP POST API.
@ -211,3 +212,20 @@ func (h *httpKeyInternalAPI) PerformUploadDeviceSignatures(
} }
} }
} }
func (h *httpKeyInternalAPI) QuerySignatures(
ctx context.Context,
request *api.QuerySignaturesRequest,
response *api.QuerySignaturesResponse,
) {
span, ctx := opentracing.StartSpanFromContext(ctx, "QuerySignatures")
defer span.Finish()
apiURL := h.apiURL + QuerySignaturesPath
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
if err != nil {
response.Error = &api.KeyError{
Err: err.Error(),
}
}
}

View File

@ -124,4 +124,15 @@ func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response} return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}), }),
) )
internalAPIMux.Handle(QuerySignaturesPath,
httputil.MakeInternalAPI("querySignatures", func(req *http.Request) util.JSONResponse {
request := api.QuerySignaturesRequest{}
response := api.QuerySignaturesResponse{}
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error())
}
s.QuerySignatures(req.Context(), &request, &response)
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
} }

View File

@ -78,7 +78,8 @@ type Database interface {
// MarkDeviceListStale sets the stale bit for this user to isStale. // MarkDeviceListStale sets the stale bit for this user to isStale.
MarkDeviceListStale(ctx context.Context, userID string, isStale bool) error MarkDeviceListStale(ctx context.Context, userID string, isStale bool) error
CrossSigningKeysForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error) CrossSigningKeysForUser(ctx context.Context, userID string) (map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey, error)
CrossSigningKeysDataForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error)
CrossSigningSigsForTarget(ctx context.Context, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error) CrossSigningSigsForTarget(ctx context.Context, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error)
StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error

View File

@ -159,7 +159,46 @@ func (d *Database) MarkDeviceListStale(ctx context.Context, userID string, isSta
} }
// CrossSigningKeysForUser returns the latest known cross-signing keys for a user, if any. // CrossSigningKeysForUser returns the latest known cross-signing keys for a user, if any.
func (d *Database) CrossSigningKeysForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error) { func (d *Database) CrossSigningKeysForUser(ctx context.Context, userID string) (map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey, error) {
keyMap, err := d.CrossSigningKeysTable.SelectCrossSigningKeysForUser(ctx, nil, userID)
if err != nil {
return nil, fmt.Errorf("d.CrossSigningKeysTable.SelectCrossSigningKeysForUser: %w", err)
}
results := map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey{}
for purpose, key := range keyMap {
keyID := gomatrixserverlib.KeyID("ed25519:" + key.Encode())
result := gomatrixserverlib.CrossSigningKey{
UserID: userID,
Usage: []gomatrixserverlib.CrossSigningKeyPurpose{purpose},
Keys: map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{
keyID: key,
},
}
sigMap, err := d.CrossSigningSigsTable.SelectCrossSigningSigsForTarget(ctx, nil, userID, keyID)
if err != nil {
continue
}
for sigUserID, forSigUserID := range sigMap {
if userID != sigUserID {
continue
}
if result.Signatures == nil {
result.Signatures = map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
}
if _, ok := result.Signatures[sigUserID]; !ok {
result.Signatures[sigUserID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
}
for sigKeyID, sigBytes := range forSigUserID {
result.Signatures[sigUserID][sigKeyID] = sigBytes
}
}
results[purpose] = result
}
return results, nil
}
// CrossSigningKeysForUser returns the latest known cross-signing keys for a user, if any.
func (d *Database) CrossSigningKeysDataForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error) {
return d.CrossSigningKeysTable.SelectCrossSigningKeysForUser(ctx, nil, userID) return d.CrossSigningKeysTable.SelectCrossSigningKeysForUser(ctx, nil, userID)
} }

View File

@ -49,6 +49,8 @@ func (k *mockKeyAPI) QueryDeviceMessages(ctx context.Context, req *keyapi.QueryD
} }
func (k *mockKeyAPI) InputDeviceListUpdate(ctx context.Context, req *keyapi.InputDeviceListUpdateRequest, res *keyapi.InputDeviceListUpdateResponse) { func (k *mockKeyAPI) InputDeviceListUpdate(ctx context.Context, req *keyapi.InputDeviceListUpdateRequest, res *keyapi.InputDeviceListUpdateResponse) {
}
func (k *mockKeyAPI) QuerySignatures(ctx context.Context, req *keyapi.QuerySignaturesRequest, res *keyapi.QuerySignaturesResponse) {
} }
type mockRoomserverAPI struct { type mockRoomserverAPI struct {