Cross-signing storage code (#1959)
parent
ed04eed441
commit
7a9a2547b3
2
go.mod
2
go.mod
|
@ -31,7 +31,7 @@ require (
|
||||||
github.com/matrix-org/go-http-js-libp2p v0.0.0-20200518170932-783164aeeda4
|
github.com/matrix-org/go-http-js-libp2p v0.0.0-20200518170932-783164aeeda4
|
||||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20210709140738-b0d1ba599a6d
|
github.com/matrix-org/go-sqlite3-js v0.0.0-20210709140738-b0d1ba599a6d
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20210722110442-5061d6986876
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20210802144451-bec8d2252d83
|
||||||
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0
|
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0
|
||||||
github.com/matrix-org/pinecone v0.0.0-20210623102758-74f885644c1b
|
github.com/matrix-org/pinecone v0.0.0-20210623102758-74f885644c1b
|
||||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
|
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -994,8 +994,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20210709140738-b0d1ba599a6d/go.mod h1
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0=
|
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0=
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20210722110442-5061d6986876 h1:6ypwCtgRLK0v/hGWvnd847+KTo9BSkP9N0A4qSniP4E=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20210802144451-bec8d2252d83 h1:fkUmeKj/U5TnWXTsJnVjEL0FQiVhf1r9WL4VWI00q2k=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20210722110442-5061d6986876/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20210802144451-bec8d2252d83/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
|
||||||
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0 h1:HZCzy4oVzz55e+cOMiX/JtSF2UOY1evBl2raaE7ACcU=
|
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0 h1:HZCzy4oVzz55e+cOMiX/JtSF2UOY1evBl2raaE7ACcU=
|
||||||
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0/go.mod h1:sjyPyRxKM5uw1nD2cJ6O2OxI6GOqyVBfNXqKjBZTBZE=
|
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0/go.mod h1:sjyPyRxKM5uw1nD2cJ6O2OxI6GOqyVBfNXqKjBZTBZE=
|
||||||
github.com/matrix-org/pinecone v0.0.0-20210623102758-74f885644c1b h1:5X5vdWQ13xrNkJVqaJHPsrt7rKkMJH5iac0EtfOuxSg=
|
github.com/matrix-org/pinecone v0.0.0-20210623102758-74f885644c1b h1:5X5vdWQ13xrNkJVqaJHPsrt7rKkMJH5iac0EtfOuxSg=
|
||||||
|
|
|
@ -18,11 +18,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Database interface {
|
type Database interface {
|
||||||
|
internal.PartitionStorer
|
||||||
|
|
||||||
// ExistingOneTimeKeys returns a map of keyIDWithAlgorithm to key JSON for the given parameters. If no keys exist with this combination
|
// ExistingOneTimeKeys returns a map of keyIDWithAlgorithm to key JSON for the given parameters. If no keys exist with this combination
|
||||||
// of user/device/key/algorithm 4-uple then it is omitted from the map. Returns an error when failing to communicate with the database.
|
// of user/device/key/algorithm 4-uple then it is omitted from the map. Returns an error when failing to communicate with the database.
|
||||||
ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error)
|
ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error)
|
||||||
|
@ -73,4 +77,10 @@ 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)
|
||||||
|
CrossSigningSigsForTarget(ctx context.Context, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error)
|
||||||
|
|
||||||
|
StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error
|
||||||
|
StoreCrossSigningSigsForTarget(ctx context.Context, originUserID string, originKeyID gomatrixserverlib.KeyID, targetUserID string, targetKeyID gomatrixserverlib.KeyID, signature gomatrixserverlib.Base64Bytes) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningKeysSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
key_type SMALLINT NOT NULL,
|
||||||
|
key_data TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (user_id, key_type)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningKeysForUserSQL = "" +
|
||||||
|
"SELECT key_type, key_data FROM keyserver_cross_signing_keys" +
|
||||||
|
" WHERE user_id = $1"
|
||||||
|
|
||||||
|
const upsertCrossSigningKeysForUserSQL = "" +
|
||||||
|
"INSERT INTO keyserver_cross_signing_keys (user_id, key_type, key_data)" +
|
||||||
|
" VALUES($1, $2, $3)" +
|
||||||
|
" ON CONFLICT (user_id, key_type) DO UPDATE SET key_data = $3"
|
||||||
|
|
||||||
|
type crossSigningKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||||
|
s := &crossSigningKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||||
|
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string,
|
||||||
|
) (r types.CrossSigningKeyMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningKeysForUserStmt).QueryContext(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningKeysForUserStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningKeyMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var keyTypeInt int16
|
||||||
|
var keyData gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&keyTypeInt, &keyData); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||||
|
}
|
||||||
|
r[keyType] = keyData
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown key purpose %q", keyType)
|
||||||
|
}
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningKeysForUserStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningSigsSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_sigs (
|
||||||
|
origin_user_id TEXT NOT NULL,
|
||||||
|
origin_key_id TEXT NOT NULL,
|
||||||
|
target_user_id TEXT NOT NULL,
|
||||||
|
target_key_id TEXT NOT NULL,
|
||||||
|
signature TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (origin_user_id, target_user_id, target_key_id)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"SELECT origin_user_id, origin_key_id, signature FROM keyserver_cross_signing_sigs" +
|
||||||
|
" WHERE target_user_id = $1 AND target_key_id = $2"
|
||||||
|
|
||||||
|
const upsertCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"INSERT INTO keyserver_cross_signing_sigs (origin_user_id, origin_key_id, target_user_id, target_key_id, signature)" +
|
||||||
|
" VALUES($1, $2, $3, $4, $5)" +
|
||||||
|
" ON CONFLICT (origin_user_id, target_user_id, target_key_id) DO UPDATE SET (origin_key_id, signature) = ($2, $5)"
|
||||||
|
|
||||||
|
type crossSigningSigsStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
upsertCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresCrossSigningSigsTable(db *sql.DB) (tables.CrossSigningSigs, error) {
|
||||||
|
s := &crossSigningSigsStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningSigsSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningSigsForTargetStmt, selectCrossSigningSigsForTargetSQL},
|
||||||
|
{&s.upsertCrossSigningSigsForTargetStmt, upsertCrossSigningSigsForTargetSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) SelectCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx, targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
) (r types.CrossSigningSigMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningSigsForTargetStmt).QueryContext(ctx, targetUserID, targetKeyID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningSigsForTargetStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningSigMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var userID string
|
||||||
|
var keyID gomatrixserverlib.KeyID
|
||||||
|
var signature gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&userID, &keyID, &signature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := r[userID]; !ok {
|
||||||
|
r[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
|
||||||
|
}
|
||||||
|
r[userID][keyID] = signature
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) UpsertCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
originUserID string, originKeyID gomatrixserverlib.KeyID,
|
||||||
|
targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
signature gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningSigsForTargetStmt).ExecContext(ctx, originUserID, originKeyID, targetUserID, targetKeyID, signature); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningSigsForTargetStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -43,12 +43,26 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*shared.Database, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &shared.Database{
|
csk, err := NewPostgresCrossSigningKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
css, err := NewPostgresCrossSigningSigsTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := &shared.Database{
|
||||||
DB: db,
|
DB: db,
|
||||||
Writer: sqlutil.NewDummyWriter(),
|
Writer: sqlutil.NewDummyWriter(),
|
||||||
OneTimeKeysTable: otk,
|
OneTimeKeysTable: otk,
|
||||||
DeviceKeysTable: dk,
|
DeviceKeysTable: dk,
|
||||||
KeyChangesTable: kc,
|
KeyChangesTable: kc,
|
||||||
StaleDeviceListsTable: sdl,
|
StaleDeviceListsTable: sdl,
|
||||||
}, nil
|
CrossSigningKeysTable: csk,
|
||||||
|
CrossSigningSigsTable: css,
|
||||||
|
}
|
||||||
|
if err = d.PartitionOffsetStatements.Prepare(db, d.Writer, "keyserver"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,6 +34,9 @@ type Database struct {
|
||||||
DeviceKeysTable tables.DeviceKeys
|
DeviceKeysTable tables.DeviceKeys
|
||||||
KeyChangesTable tables.KeyChanges
|
KeyChangesTable tables.KeyChanges
|
||||||
StaleDeviceListsTable tables.StaleDeviceLists
|
StaleDeviceListsTable tables.StaleDeviceLists
|
||||||
|
CrossSigningKeysTable tables.CrossSigningKeys
|
||||||
|
CrossSigningSigsTable tables.CrossSigningSigs
|
||||||
|
sqlutil.PartitionOffsetStatements
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error) {
|
func (d *Database) ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error) {
|
||||||
|
@ -152,3 +157,40 @@ func (d *Database) MarkDeviceListStale(ctx context.Context, userID string, isSta
|
||||||
return d.StaleDeviceListsTable.InsertStaleDeviceList(ctx, userID, isStale)
|
return d.StaleDeviceListsTable.InsertStaleDeviceList(ctx, userID, isStale)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return d.CrossSigningKeysTable.SelectCrossSigningKeysForUser(ctx, nil, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CrossSigningSigsForTarget returns the signatures for a given user's key ID, if any.
|
||||||
|
func (d *Database) CrossSigningSigsForTarget(ctx context.Context, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error) {
|
||||||
|
return d.CrossSigningSigsTable.SelectCrossSigningSigsForTarget(ctx, nil, targetUserID, targetKeyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreCrossSigningKeysForUser stores the latest known cross-signing keys for a user.
|
||||||
|
func (d *Database) StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error {
|
||||||
|
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||||
|
for keyType, keyData := range keyMap {
|
||||||
|
if err := d.CrossSigningKeysTable.UpsertCrossSigningKeysForUser(ctx, txn, userID, keyType, keyData); err != nil {
|
||||||
|
return fmt.Errorf("d.CrossSigningKeysTable.InsertCrossSigningKeysForUser: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreCrossSigningSigsForTarget stores a signature for a target user ID and key/dvice.
|
||||||
|
func (d *Database) StoreCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context,
|
||||||
|
originUserID string, originKeyID gomatrixserverlib.KeyID,
|
||||||
|
targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
signature gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||||
|
if err := d.CrossSigningSigsTable.UpsertCrossSigningSigsForTarget(ctx, nil, originUserID, originKeyID, targetUserID, targetKeyID, signature); err != nil {
|
||||||
|
return fmt.Errorf("d.CrossSigningSigsTable.InsertCrossSigningSigsForTarget: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningKeysSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
key_type INTEGER NOT NULL,
|
||||||
|
key_data TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (user_id, key_type)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningKeysForUserSQL = "" +
|
||||||
|
"SELECT key_type, key_data FROM keyserver_cross_signing_keys" +
|
||||||
|
" WHERE user_id = $1"
|
||||||
|
|
||||||
|
const upsertCrossSigningKeysForUserSQL = "" +
|
||||||
|
"INSERT OR REPLACE INTO keyserver_cross_signing_keys (user_id, key_type, key_data)" +
|
||||||
|
" VALUES($1, $2, $3)"
|
||||||
|
|
||||||
|
type crossSigningKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||||
|
s := &crossSigningKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||||
|
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string,
|
||||||
|
) (r types.CrossSigningKeyMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningKeysForUserStmt).QueryContext(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningKeysForUserStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningKeyMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var keyTypeInt int16
|
||||||
|
var keyData gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&keyTypeInt, &keyData); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||||
|
}
|
||||||
|
r[keyType] = keyData
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown key purpose %q", keyType)
|
||||||
|
}
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningKeysForUserStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningSigsSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_sigs (
|
||||||
|
origin_user_id TEXT NOT NULL,
|
||||||
|
origin_key_id TEXT NOT NULL,
|
||||||
|
target_user_id TEXT NOT NULL,
|
||||||
|
target_key_id TEXT NOT NULL,
|
||||||
|
signature TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (origin_user_id, target_user_id, target_key_id)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"SELECT origin_user_id, origin_key_id, signature FROM keyserver_cross_signing_sigs" +
|
||||||
|
" WHERE target_user_id = $1 AND target_key_id = $2"
|
||||||
|
|
||||||
|
const upsertCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"INSERT OR REPLACE INTO keyserver_cross_signing_sigs (origin_user_id, origin_key_id, target_user_id, target_key_id, signature)" +
|
||||||
|
" VALUES($1, $2, $3, $4, $5)"
|
||||||
|
|
||||||
|
type crossSigningSigsStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
upsertCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteCrossSigningSigsTable(db *sql.DB) (tables.CrossSigningSigs, error) {
|
||||||
|
s := &crossSigningSigsStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningSigsSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningSigsForTargetStmt, selectCrossSigningSigsForTargetSQL},
|
||||||
|
{&s.upsertCrossSigningSigsForTargetStmt, upsertCrossSigningSigsForTargetSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) SelectCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx, targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
) (r types.CrossSigningSigMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningSigsForTargetStmt).QueryContext(ctx, targetUserID, targetKeyID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningSigsForTargetStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningSigMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var userID string
|
||||||
|
var keyID gomatrixserverlib.KeyID
|
||||||
|
var signature gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&userID, &keyID, &signature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := r[userID]; !ok {
|
||||||
|
r[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
|
||||||
|
}
|
||||||
|
r[userID][keyID] = signature
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) UpsertCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
originUserID string, originKeyID gomatrixserverlib.KeyID,
|
||||||
|
targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
signature gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningSigsForTargetStmt).ExecContext(ctx, originUserID, originKeyID, targetUserID, targetKeyID, signature); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningSigsForTargetStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -41,12 +41,26 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*shared.Database, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &shared.Database{
|
csk, err := NewSqliteCrossSigningKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
css, err := NewSqliteCrossSigningSigsTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := &shared.Database{
|
||||||
DB: db,
|
DB: db,
|
||||||
Writer: sqlutil.NewExclusiveWriter(),
|
Writer: sqlutil.NewExclusiveWriter(),
|
||||||
OneTimeKeysTable: otk,
|
OneTimeKeysTable: otk,
|
||||||
DeviceKeysTable: dk,
|
DeviceKeysTable: dk,
|
||||||
KeyChangesTable: kc,
|
KeyChangesTable: kc,
|
||||||
StaleDeviceListsTable: sdl,
|
StaleDeviceListsTable: sdl,
|
||||||
}, nil
|
CrossSigningKeysTable: csk,
|
||||||
|
CrossSigningSigsTable: css,
|
||||||
|
}
|
||||||
|
if err = d.PartitionOffsetStatements.Prepare(db, d.Writer, "keyserver"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,3 +53,13 @@ type StaleDeviceLists interface {
|
||||||
InsertStaleDeviceList(ctx context.Context, userID string, isStale bool) error
|
InsertStaleDeviceList(ctx context.Context, userID string, isStale bool) error
|
||||||
SelectUserIDsWithStaleDeviceLists(ctx context.Context, domains []gomatrixserverlib.ServerName) ([]string, error)
|
SelectUserIDsWithStaleDeviceLists(ctx context.Context, domains []gomatrixserverlib.ServerName) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CrossSigningKeys interface {
|
||||||
|
SelectCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string) (r types.CrossSigningKeyMap, err error)
|
||||||
|
UpsertCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CrossSigningSigs interface {
|
||||||
|
SelectCrossSigningSigsForTarget(ctx context.Context, txn *sql.Tx, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (r types.CrossSigningSigMap, err error)
|
||||||
|
UpsertCrossSigningSigsForTarget(ctx context.Context, txn *sql.Tx, originUserID string, originKeyID gomatrixserverlib.KeyID, targetUserID string, targetKeyID gomatrixserverlib.KeyID, signature gomatrixserverlib.Base64Bytes) error
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
// KeyTypePurposeToInt maps a purpose to an integer, which is used in the
|
||||||
|
// database to reduce the amount of space taken up by this column.
|
||||||
|
var KeyTypePurposeToInt = map[gomatrixserverlib.CrossSigningKeyPurpose]int16{
|
||||||
|
gomatrixserverlib.CrossSigningKeyPurposeMaster: 1,
|
||||||
|
gomatrixserverlib.CrossSigningKeyPurposeSelfSigning: 2,
|
||||||
|
gomatrixserverlib.CrossSigningKeyPurposeUserSigning: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyTypeIntToPurpose maps an integer to a purpose, which is used in the
|
||||||
|
// database to reduce the amount of space taken up by this column.
|
||||||
|
var KeyTypeIntToPurpose = map[int16]gomatrixserverlib.CrossSigningKeyPurpose{
|
||||||
|
1: gomatrixserverlib.CrossSigningKeyPurposeMaster,
|
||||||
|
2: gomatrixserverlib.CrossSigningKeyPurposeSelfSigning,
|
||||||
|
3: gomatrixserverlib.CrossSigningKeyPurposeUserSigning,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of purpose -> public key
|
||||||
|
type CrossSigningKeyMap map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.Base64Bytes
|
||||||
|
|
||||||
|
// Map of user ID -> key ID -> signature
|
||||||
|
type CrossSigningSigMap map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes
|
Loading…
Reference in New Issue