Cross-signing storage code
MODIFY
go.mod
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-sqlite3-js v0.0.0-20210709140738-b0d1ba599a6d
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/pinecone v0.0.0-20210623102758-74f885644c1b
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4

MODIFY
go.sum
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-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
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-20210722110442-5061d6986876/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU=
+github.com/matrix-org/gomatrixserverlib v0.0.0-20210802144451-bec8d2252d83 h1:fkUmeKj/U5TnWXTsJnVjEL0FQiVhf1r9WL4VWI00q2k=
+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/go.mod h1:sjyPyRxKM5uw1nD2cJ6O2OxI6GOqyVBfNXqKjBZTBZE=
github.com/matrix-org/pinecone v0.0.0-20210623102758-74f885644c1b h1:5X5vdWQ13xrNkJVqaJHPsrt7rKkMJH5iac0EtfOuxSg=

MODIFY
keyserver/storage/interface.go
keyserver/storage/interface.go
@@ -18,11 +18,15 @@ import (
"context"
"encoding/json"
+ "github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/keyserver/api"
+ "github.com/matrix-org/dendrite/keyserver/types"
"github.com/matrix-org/gomatrixserverlib"
)
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
// 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)
@@ -73,4 +77,10 @@ type Database interface {
// MarkDeviceListStale sets the stale bit for this user to isStale.
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
}

ADD
keyserver/storage/postgres/cross_signing_keys_table.go
+// 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
+}

ADD
keyserver/storage/postgres/cross_signing_sigs_table.go
+// 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
+}

MODIFY
keyserver/storage/postgres/storage.go
keyserver/storage/postgres/storage.go
@@ -43,12 +43,26 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*shared.Database, error)
if err != nil {
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,
Writer: sqlutil.NewDummyWriter(),
OneTimeKeysTable: otk,
DeviceKeysTable: dk,
KeyChangesTable: kc,
StaleDeviceListsTable: sdl,
- }, nil
+ CrossSigningKeysTable: csk,
+ CrossSigningSigsTable: css,
+ }
+ if err = d.PartitionOffsetStatements.Prepare(db, d.Writer, "keyserver"); err != nil {
+ return nil, err
+ }
+ return d, nil
}

MODIFY
keyserver/storage/shared/storage.go
keyserver/storage/shared/storage.go
@@ -18,10 +18,12 @@ import (
"context"
"database/sql"
"encoding/json"
+ "fmt"
"github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/keyserver/api"
"github.com/matrix-org/dendrite/keyserver/storage/tables"
+ "github.com/matrix-org/dendrite/keyserver/types"
"github.com/matrix-org/gomatrixserverlib"
)
@@ -32,6 +34,9 @@ type Database struct {
DeviceKeysTable tables.DeviceKeys
KeyChangesTable tables.KeyChanges
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) {
@@ -152,3 +157,40 @@ func (d *Database) MarkDeviceListStale(ctx context.Context, userID string, isSta
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
+ })
+}

ADD
keyserver/storage/sqlite3/cross_signing_keys_table.go
+// 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
+}

ADD
keyserver/storage/sqlite3/cross_signing_sigs_table.go
+// 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
+}

MODIFY
keyserver/storage/sqlite3/storage.go
keyserver/storage/sqlite3/storage.go
@@ -41,12 +41,26 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*shared.Database, error)
if err != nil {
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,
Writer: sqlutil.NewExclusiveWriter(),
OneTimeKeysTable: otk,
DeviceKeysTable: dk,
KeyChangesTable: kc,
StaleDeviceListsTable: sdl,
- }, nil
+ CrossSigningKeysTable: csk,
+ CrossSigningSigsTable: css,
+ }
+ if err = d.PartitionOffsetStatements.Prepare(db, d.Writer, "keyserver"); err != nil {
+ return nil, err
+ }
+ return d, nil
}

MODIFY
keyserver/storage/tables/interface.go
keyserver/storage/tables/interface.go
@@ -20,6 +20,7 @@ import (
"encoding/json"
"github.com/matrix-org/dendrite/keyserver/api"
+ "github.com/matrix-org/dendrite/keyserver/types"
"github.com/matrix-org/gomatrixserverlib"
)
@@ -52,3 +53,13 @@ type StaleDeviceLists interface {
InsertStaleDeviceList(ctx context.Context, userID string, isStale bool) 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
+}

ADD
keyserver/types/storage.go
+// 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

Cross-signing storage code (#1959)