From 1e9ae4a4bebe71ec2067ae41232bab6261d08880 Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Thu, 11 Jan 2024 17:38:13 -0700 Subject: [PATCH] wip Signed-off-by: Sumner Evans --- client.go | 11 ++++---- crypto/backup/ed25519aessha2.go | 22 ++++++++++++--- crypto/machine.go | 50 +++++++++++++++++++++++++++++++++ responses.go | 22 +++++++-------- 4 files changed, 85 insertions(+), 20 deletions(-) diff --git a/client.go b/client.go index 5dc1c0be9..c0e047711 100644 --- a/client.go +++ b/client.go @@ -21,6 +21,7 @@ import ( "go.mau.fi/util/retryafter" "maunium.net/go/maulogger/v2/maulogadapt" + "maunium.net/go/mautrix/crypto/backup" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" "maunium.net/go/mautrix/pushrules" @@ -1944,7 +1945,7 @@ func (cli *Client) GetKeyChanges(ctx context.Context, from, to string) (resp *Re // GetKeyBackup retrieves the keys from the backup. // // See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keyskeys -func (cli *Client) GetKeyBackup(ctx context.Context, version string) (resp *RespRoomKeys, err error) { +func (cli *Client) GetKeyBackup(ctx context.Context, version string) (resp *RespRoomKeys[backup.EncryptedMegolmSessionData], err error) { urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys"}, map[string]string{ "version": version, }) @@ -1977,7 +1978,7 @@ func (cli *Client) DeleteKeyBackup(ctx context.Context, version string) (resp *R // GetKeyBackupForRoom retrieves the keys from the backup for the given room. // // See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keyskeysroomid -func (cli *Client) GetKeyBackupForRoom(ctx context.Context, version string, roomID id.RoomID) (resp *RespRoomKeyBackup, err error) { +func (cli *Client) GetKeyBackupForRoom(ctx context.Context, version string, roomID id.RoomID) (resp *RespRoomKeyBackup[backup.EncryptedMegolmSessionData], err error) { urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String()}, map[string]string{ "version": version, }) @@ -2011,7 +2012,7 @@ func (cli *Client) DeleteKeysFromBackupForRoom(ctx context.Context, version stri // GetKeyBackupForRoomAndSession retrieves a key from the backup. // // See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keyskeysroomidsessionid -func (cli *Client) GetKeyBackupForRoomAndSession(ctx context.Context, version string, roomID id.RoomID, sessionID id.SessionID) (resp *RespKeyBackupData, err error) { +func (cli *Client) GetKeyBackupForRoomAndSession(ctx context.Context, version string, roomID id.RoomID, sessionID id.SessionID) (resp *RespKeyBackupData[backup.EncryptedMegolmSessionData], err error) { urlPath := cli.BuildURLWithQuery(ClientURLPath{"v3", "room_keys", "keys", roomID.String(), sessionID.String()}, map[string]string{ "version": version, }) @@ -2044,7 +2045,7 @@ func (cli *Client) DeleteKeysInBackupForRoomAndSession(ctx context.Context, vers // GetKeyBackupLatestVersion returns information about the latest backup version. // // See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keysversion -func (cli *Client) GetKeyBackupLatestVersion(ctx context.Context) (resp *RespRoomKeysVersion, err error) { +func (cli *Client) GetKeyBackupLatestVersion(ctx context.Context) (resp *RespRoomKeysVersion[backup.MegolmAuthData], err error) { urlPath := cli.BuildClientURL("v3", "room_keys", "version") _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) return @@ -2062,7 +2063,7 @@ func (cli *Client) CreateKeyBackupVersion(ctx context.Context, req *ReqRoomKeysV // GetKeyBackupVersion returns information about an existing key backup. // // See: https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3room_keysversionversion -func (cli *Client) GetKeyBackupVersion(ctx context.Context, version string) (resp *RespRoomKeysVersion, err error) { +func (cli *Client) GetKeyBackupVersion(ctx context.Context, version string) (resp *RespRoomKeysVersion[backup.MegolmAuthData], err error) { urlPath := cli.BuildClientURL("v3", "room_keys", "version", version) _, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, nil, &resp) return diff --git a/crypto/backup/ed25519aessha2.go b/crypto/backup/ed25519aessha2.go index 53462156d..bfed1e9b7 100644 --- a/crypto/backup/ed25519aessha2.go +++ b/crypto/backup/ed25519aessha2.go @@ -1,12 +1,14 @@ package backup import ( + "crypto/ed25519" + "maunium.net/go/mautrix/crypto/signatures" "maunium.net/go/mautrix/id" ) // MegolmAuthData is the auth_data when the key backup is created with -// the [id.KeyBackupAlgorithmCurve25519AESSHA2] algorithm as defined in +// the [id.KeyBackupAlgorithmMegolmBackupV1] algorithm as defined in // [Section 11.12.3.2.2 of the Spec]. // // [Section 11.12.3.2.2 of the Spec]: https://spec.matrix.org/v1.9/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 @@ -15,11 +17,23 @@ type MegolmAuthData struct { Signatures signatures.Signatures `json:"signatures"` } -// MegolmSessionData is the session_data when the key backup is created -// with the [id.KeyBackupAlgorithmCurve25519AESSHA2] algorithm as defined in -// [Section 11.12.3.2.2 of the Spec]. +// EncryptedSessionData is the encrypted session_data field of a key backup as +// defined in [Section 11.12.3.2.2 of the Spec]. +// +// The type parameter B represents the format of the session data contained in +// the encrypted payload. // // [Section 11.12.3.2.2 of the Spec]: https://spec.matrix.org/v1.9/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 +type EncryptedSessionData[T any] struct { + Ciphertext []byte `json:"ciphertext"` + Ephemeral []byte `json:"ephemeral"` + Mac []byte `json:"mac"` +} + +func (e *EncryptedSessionData[T]) Encrypt(recoveryKey ed25519.PrivateKey) (*T, error) { + return nil, nil +} + type MegolmSessionData struct { Algorithm id.Algorithm `json:"algorithm"` ForwardingCurve25519KeyChain []string `json:"forwarding_curve25519_key_chain"` diff --git a/crypto/machine.go b/crypto/machine.go index fc0f17426..e438c418f 100644 --- a/crypto/machine.go +++ b/crypto/machine.go @@ -15,6 +15,7 @@ import ( "github.com/rs/zerolog" + "maunium.net/go/mautrix/crypto/olm" "maunium.net/go/mautrix/crypto/ssss" "maunium.net/go/mautrix/id" @@ -688,3 +689,52 @@ func (mach *OlmMachine) ExpiredKeyDeleteLoop(ctx context.Context) { } } } + +func (mach *OlmMachine) DownloadAndStoreLatestKeyBackup(ctx context.Context) error { + log := mach.machOrContextLog(ctx).With(). + Str("action", "download and store latest key backup"). + Logger() + versionInfo, err := mach.Client.GetKeyBackupLatestVersion(ctx) + if err != nil { + return err + } + + if versionInfo.Algorithm != id.KeyBackupAlgorithmMegolmBackupV1 { + return fmt.Errorf("unsupported key backup algorithm: %s", versionInfo.Algorithm) + } + + log = log.With(). + Int("count", versionInfo.Count). + Str("etag", versionInfo.ETag). + Str("key_backup_version", versionInfo.Version). + Logger() + + if versionInfo.Count == 0 { + log.Debug().Msg("No keys found in key backup") + return nil + } + + keys, err := mach.Client.GetKeyBackup(ctx, versionInfo.Version) + if err != nil { + return err + } + + for roomID, backup := range keys.Rooms { + fmt.Printf("Sessions for %s\n", roomID) + for sessionID, keyBackupData := range backup.Sessions { + // TODO + fmt.Printf("\t%s: %v\n", sessionID, keyBackupData) + + olm.NewInboundGroupSession([]byte(keyBackupData.SessionData.SessionKey)) + + err := mach.CryptoStore.PutGroupSession(ctx, roomID, keyBackupData.SessionData.SenderKey, sessionID, &InboundGroupSession{ + Internal: olm.InboundGroupSession, + }) + if err != nil { + return nil + } + } + } + + return nil +} diff --git a/responses.go b/responses.go index 4599fb192..b8552b58e 100644 --- a/responses.go +++ b/responses.go @@ -596,27 +596,27 @@ type RespRoomKeysVersionCreate struct { Version string `json:"version"` } -type RespRoomKeysVersion struct { +type RespRoomKeysVersion[A any] struct { Algorithm id.KeyBackupAlgorithm `json:"algorithm"` - AuthData json.RawMessage `json:"auth_data"` + AuthData A `json:"auth_data"` Count int `json:"count"` ETag string `json:"etag"` Version string `json:"version"` } -type RespRoomKeys struct { - Rooms map[id.RoomID]RespRoomKeyBackup `json:"rooms"` +type RespRoomKeys[S any] struct { + Rooms map[id.RoomID]RespRoomKeyBackup[S] `json:"rooms"` } -type RespRoomKeyBackup struct { - Sessions map[id.SessionID]RespKeyBackupData `json:"sessions"` +type RespRoomKeyBackup[S any] struct { + Sessions map[id.SessionID]RespKeyBackupData[S] `json:"sessions"` } -type RespKeyBackupData struct { - FirstMessageIndex int `json:"first_message_index"` - ForwardedCount int `json:"forwarded_count"` - IsVerified bool `json:"is_verified"` - SessionData json.RawMessage `json:"session_data"` +type RespKeyBackupData[S any] struct { + FirstMessageIndex int `json:"first_message_index"` + ForwardedCount int `json:"forwarded_count"` + IsVerified bool `json:"is_verified"` + SessionData S `json:"session_data"` } type RespRoomKeysUpdate struct {