diff --git a/ruma-client-api/Cargo.toml b/ruma-client-api/Cargo.toml index 53c8ab4d..3fb6a753 100644 --- a/ruma-client-api/Cargo.toml +++ b/ruma-client-api/Cargo.toml @@ -31,3 +31,6 @@ strum = { version = "0.18.0", features = ["derive"] } [dev-dependencies] maplit = "1.0.2" matches = "0.1.8" + +[features] +unstable-pre-spec = [] diff --git a/ruma-client-api/src/r0.rs b/ruma-client-api/src/r0.rs index 23154ee7..d1577682 100644 --- a/ruma-client-api/src/r0.rs +++ b/ruma-client-api/src/r0.rs @@ -3,6 +3,8 @@ pub mod account; pub mod alias; pub mod appservice; +#[cfg(feature = "unstable-pre-spec")] +pub mod backup; pub mod capabilities; pub mod config; pub mod contact; diff --git a/ruma-client-api/src/r0/backup.rs b/ruma-client-api/src/r0/backup.rs new file mode 100644 index 00000000..f25b41ff --- /dev/null +++ b/ruma-client-api/src/r0/backup.rs @@ -0,0 +1,53 @@ +//! Endpoints for server-side key backups. + +pub mod add_backup_keys; +pub mod create_backup; +pub mod get_backup; +pub mod get_backup_keys; +pub mod get_latest_backup; +pub mod update_backup; + +use js_int::UInt; +use ruma_identifiers::UserId; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +use crate::r0::keys::AlgorithmAndDeviceId; + +/// The algorithm used for storing backups. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "algorithm", content = "auth_data")] +pub enum BackupAlgorithm { + /// `m.megolm_backup.v1.curve25519-aes-sha2` backup algorithm. + #[serde(rename = "m.megolm_backup.v1.curve25519-aes-sha2")] + MegolmBackupV1Curve25519AesSha2 { + /// The curve25519 public key used to encrypt the backups, encoded in unpadded base64. + public_key: String, + /// Signatures of the auth_data as Signed JSON. + signatures: BTreeMap>, + }, +} + +/// The key data. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct KeyData { + /// The index of the first message in the session that the key can decrypt. + first_message_index: UInt, + /// The number of times this key has been forwarded via key-sharing between devices. + forwarded_count: UInt, + /// Whether the device backing up the key verified the device that the key is from. + is_verified: bool, + /// Data about the session. + session_data: SessionData, +} + +/// The algorithm used for storing backups. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SessionData { + /// Unpadded base64-encoded public half of the ephemeral key. + ephemeral: String, + /// Ciphertext, encrypted using AES-CBC-256 with PKCS#7 padding, encoded in base64. + ciphertext: String, + /// First 8 bytes of MAC key, encoded in base64. + mac: String, +} diff --git a/ruma-client-api/src/r0/backup/add_backup_keys.rs b/ruma-client-api/src/r0/backup/add_backup_keys.rs new file mode 100644 index 00000000..22b025f5 --- /dev/null +++ b/ruma-client-api/src/r0/backup/add_backup_keys.rs @@ -0,0 +1,52 @@ +//! [PUT /_matrix/client/r0/room_keys/keys](https://matrix.org/docs/spec/client_server/unstable#put-matrix-client-r0-room-keys-keys) + +use std::collections::BTreeMap; + +use js_int::UInt; +use ruma_api::ruma_api; +use ruma_identifiers::RoomId; +use serde::{Deserialize, Serialize}; + +use super::KeyData; + +ruma_api! { + metadata: { + description: "Store several keys in the backup.", + method: PUT, + name: "add_backup_keys", + path: "/_matrix/client/r0/room_keys/keys", + rate_limited: true, + requires_authentication: true, + } + + request: { + /// The backup version. Must be the current backup. + #[ruma_api(query)] + pub version: String, + + /// A map from room IDs to session IDs to key data. + /// + /// Note: synapse has the `sessions: {}` wrapper, the Matrix spec does not. + pub rooms: BTreeMap, + } + + response: { + /// An opaque string representing stored keys in the backup. Clients can compare it with + /// the etag value they received in the request of their last key storage request. + pub etag: String, + + /// The number of keys stored in the backup. + pub count: UInt, + } + + error: crate::Error +} + +// TODO: remove +/// A wrapper around a mapping of session IDs to key data. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Sessions { + // TODO: remove + /// A map of session IDs to key data. + pub sessions: BTreeMap, +} diff --git a/ruma-client-api/src/r0/backup/create_backup.rs b/ruma-client-api/src/r0/backup/create_backup.rs new file mode 100644 index 00000000..1e91c526 --- /dev/null +++ b/ruma-client-api/src/r0/backup/create_backup.rs @@ -0,0 +1,27 @@ +//! [POST /_matrix/client/r0/room_keys/version](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-room-keys-version) + +use ruma_api::ruma_api; + +ruma_api! { + metadata: { + description: "Creates a new backup.", + method: POST, + name: "create_backup", + path: "/_matrix/client/r0/room_keys/version", + rate_limited: true, + requires_authentication: true, + } + + request: { + /// The algorithm used for storing backups. + #[serde(flatten)] + pub algorithm: super::BackupAlgorithm, + } + + response: { + /// The backup version. This is an opaque string. + pub version: String, + } + + error: crate::Error +} diff --git a/ruma-client-api/src/r0/backup/get_backup.rs b/ruma-client-api/src/r0/backup/get_backup.rs new file mode 100644 index 00000000..8affef51 --- /dev/null +++ b/ruma-client-api/src/r0/backup/get_backup.rs @@ -0,0 +1,39 @@ +//! [GET /_matrix/client/r0/room_keys/version](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-room-keys-version) + +use js_int::UInt; +use ruma_api::ruma_api; + +ruma_api! { + metadata: { + description: "Get information about an existing backup.", + method: GET, + name: "get_backup", + path: "/_matrix/client/r0/room_keys/version/:version", + rate_limited: true, + requires_authentication: true, + } + + request: { + /// The backup version. + #[ruma_api(path)] + pub version: String, + } + + response: { + /// The algorithm used for storing backups. + #[serde(flatten)] + pub algorithm: super::BackupAlgorithm, + + /// The number of keys stored in the backup. + pub count: UInt, + + /// An opaque string representing stored keys in the backup. Clients can compare it with + /// the etag value they received in the request of their last key storage request. + pub etag: String, + + /// The backup version. This is an opaque string. + pub version: String, + } + + error: crate::Error +} diff --git a/ruma-client-api/src/r0/backup/get_backup_keys.rs b/ruma-client-api/src/r0/backup/get_backup_keys.rs new file mode 100644 index 00000000..ffc11a3e --- /dev/null +++ b/ruma-client-api/src/r0/backup/get_backup_keys.rs @@ -0,0 +1,44 @@ +//! [GET /_matrix/client/r0/room_keys/keys](https://matrix.org/docs/spec/client_server/unstable#get-matrix-client-r0-room-keys-keys) + +use std::collections::BTreeMap; + +use ruma_api::ruma_api; +use ruma_identifiers::RoomId; +use serde::{Deserialize, Serialize}; + +use super::KeyData; + +ruma_api! { + metadata: { + description: "Retrieve all keys from a backup.", + method: GET, + name: "get_backup_keys", + path: "/_matrix/client/r0/room_keys/keys", + rate_limited: true, + requires_authentication: true, + } + + request: { + /// The backup version. Must be the current backup. + #[ruma_api(query)] + pub version: String, + } + + response: { + /// A map from room IDs to session IDs to key data. + /// + /// Note: synapse has the `sessions: {}` wrapper, the Matrix spec does not. + pub rooms: BTreeMap, + } + + error: crate::Error +} + +// TODO: remove +/// A wrapper around a mapping of session IDs to key data. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Sessions { + // TODO: remove + /// A map of session IDs to key data. + pub sessions: BTreeMap, +} diff --git a/ruma-client-api/src/r0/backup/get_latest_backup.rs b/ruma-client-api/src/r0/backup/get_latest_backup.rs new file mode 100644 index 00000000..385caee9 --- /dev/null +++ b/ruma-client-api/src/r0/backup/get_latest_backup.rs @@ -0,0 +1,35 @@ +//! [GET /_matrix/client/r0/room_keys/version](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-room-keys-version) + +use js_int::UInt; +use ruma_api::ruma_api; + +ruma_api! { + metadata: { + description: "Get information about the latest backup.", + method: GET, + name: "get_latest_backup", + path: "/_matrix/client/r0/room_keys/version", + rate_limited: true, + requires_authentication: true, + } + + request: {} + + response: { + /// The algorithm used for storing backups. + #[serde(flatten)] + pub algorithm: super::BackupAlgorithm, + + /// The number of keys stored in the backup. + pub count: UInt, + + /// An opaque string represetning stored keys in the backup. Clients can compare it with + /// the etag value they received in the request of their last key storage request. + pub etag: String, + + /// The backup version. This is an opaque string. + pub version: String, + } + + error: crate::Error +} diff --git a/ruma-client-api/src/r0/backup/update_backup.rs b/ruma-client-api/src/r0/backup/update_backup.rs new file mode 100644 index 00000000..09cc9079 --- /dev/null +++ b/ruma-client-api/src/r0/backup/update_backup.rs @@ -0,0 +1,28 @@ +//! [POST /_matrix/client/r0/room_keys/version](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-room-keys-version) + +use ruma_api::ruma_api; + +ruma_api! { + metadata: { + description: "Update information about an existing backup.", + method: POST, + name: "update_backup", + path: "/_matrix/client/r0/room_keys/version/:version", + rate_limited: true, + requires_authentication: true, + } + + request: { + /// The backup version. + #[ruma_api(path)] + pub version: String, + + /// The algorithm used for storing backups. + #[serde(flatten)] + pub algorithm: super::BackupAlgorithm, + } + + response: {} + + error: crate::Error +} diff --git a/ruma-client-api/src/r0/keys.rs b/ruma-client-api/src/r0/keys.rs index b2769b90..3f82ab9e 100644 --- a/ruma-client-api/src/r0/keys.rs +++ b/ruma-client-api/src/r0/keys.rs @@ -18,6 +18,11 @@ pub mod get_key_changes; pub mod get_keys; pub mod upload_keys; +#[cfg(feature = "unstable-pre-spec")] +pub mod upload_signatures; +#[cfg(feature = "unstable-pre-spec")] +pub mod upload_signing_keys; + /// The basic key algorithms in the specification #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub enum KeyAlgorithm { @@ -161,3 +166,29 @@ pub enum OneTimeKey { /// A string-valued key, for the Ed25519 and Curve25519 algorithms. Key(String), } + +/// A cross signing key. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CrossSigningKey { + /// The ID of the user the key belongs to. + pub user_id: UserId, + /// What the key is used for. + pub usage: Vec, + /// The public key. The object must have exactly one property. + pub keys: BTreeMap, + /// Signatures of the key. Only optional for master key. + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + pub signatures: BTreeMap>, +} + +/// The usage of a cross signing key. +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum KeyUsage { + /// Master key. + Master, + /// Self-signing key. + SelfSigning, + /// User-signing key. + UserSigning, +} diff --git a/ruma-client-api/src/r0/keys/upload_signatures.rs b/ruma-client-api/src/r0/keys/upload_signatures.rs new file mode 100644 index 00000000..acd8c9c8 --- /dev/null +++ b/ruma-client-api/src/r0/keys/upload_signatures.rs @@ -0,0 +1,27 @@ +//! [POST /_matrix/client/r0/keys/signatures/upload](https://13301-24998719-gh.circle-artifacts.com/0/scripts/gen/client_server/unstable.html#post-matrix-client-r0-keys-signatures-upload) + +use std::collections::BTreeMap; + +use ruma_api::ruma_api; +use ruma_identifiers::UserId; + +ruma_api! { + metadata: { + description: "Publishes cross-signing signatures for the user.", + method: POST, + name: "upload_signatures", + path: "/_matrix/client/r0/keys/signatures/upload", + rate_limited: false, + requires_authentication: true, + } + + request: { + /// Signed keys. + #[ruma_api(body)] + pub signed_keys: BTreeMap>, + } + + response: {} + + error: crate::Error +} diff --git a/ruma-client-api/src/r0/keys/upload_signing_keys.rs b/ruma-client-api/src/r0/keys/upload_signing_keys.rs new file mode 100644 index 00000000..1f832ab8 --- /dev/null +++ b/ruma-client-api/src/r0/keys/upload_signing_keys.rs @@ -0,0 +1,41 @@ +//! [POST /_matrix/client/r0/keys/device_signing/upload](https://13301-24998719-gh.circle-artifacts.com/0/scripts/gen/client_server/unstable.html#post-matrix-client-r0-keys-device-signing-upload) + +use ruma_api::ruma_api; + +use super::CrossSigningKey; +use crate::r0::uiaa::AuthData; + +ruma_api! { + metadata: { + description: "Publishes cross signing keys for the user.", + method: POST, + name: "upload_signing_keys", + path: "/_matrix/client/r0/keys/device_signing/upload", + rate_limited: false, + requires_authentication: true, + } + + request: { + /// Additional authentication information for the user-interactive authentication API. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option, + + /// The user's master key. + #[serde(skip_serializing_if = "Option::is_none")] + pub master_key: Option, + + /// The user's self-signing key. Must be signed with the accompanied master, or by the + /// user's most recently uploaded master key if no master key is included in the request. + #[serde(skip_serializing_if = "Option::is_none")] + pub self_signing_key: Option, + + /// The user's user-signing key. Must be signed with the accompanied master, or by the + /// user's most recently uploaded master key if no master key is included in the request. + #[serde(skip_serializing_if = "Option::is_none")] + pub user_signing_key: Option, + } + + response: {} + + error: crate::Error +} diff --git a/ruma/Cargo.toml b/ruma/Cargo.toml index edec030d..1126b45d 100644 --- a/ruma/Cargo.toml +++ b/ruma/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" [features] either = ["ruma-identifiers/either"] rand = ["ruma-identifiers/rand"] +unstable-pre-spec = ["ruma-client-api/unstable-pre-spec"] appservice-api = ["ruma-api", "ruma-appservice-api", "ruma-events"] client-api = ["ruma-api", "ruma-client-api", "ruma-events"]