Watch
1
0
Fork
You've already forked mautrix-signal
0
mirror of https://github.com/mautrix/signal.git synced 2026-09-17 08:12:05 -04:00
mautrix-signal/pkg/signalmeow/protobuf/org/signal/chat/backups.proto
2026-09-05 00:57:28 +03:00

554 lines
21 KiB
Protocol Buffer

/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.backup;
import "google/protobuf/empty.proto";
import "org/signal/chat/common.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/tag.proto";
// Service for backup operations that require account authentication.
//
// Most actual backup operations operate on the backup-id and cannot be linked
// to the caller's account, but setting up anonymous credentials and changing
// backup tier requires account authentication.
service Backups {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Set (blinded) backup-id(s) for the account.
//
// Each account may have a single active backup-id for each credential type
// that can be used to store and retrieve backups. Once the backup-id is set,
// BackupAuthCredentials can be generated using GetBackupAuthCredentials.
//
// The blinded backup-id and the key-pair used to blind it must be derived
// from a recoverable secret.
//
// At least one of the credential types must be set on the request.
// Only the primary device can set a blinded backup-id.
rpc SetBackupId(SetBackupIdRequest) returns (SetBackupIdResponse) {}
// Redeem a receipt acquired from /v1/subscription/{subscriberId}/receipt_credentials
// to mark the account as eligible for the paid backup tier.
//
// After successful redemption, subsequent requests to
// GetBackupAuthCredentials will return credentials with the level on the
// provided receipt until the expiration time on the receipt.
rpc RedeemReceipt(RedeemReceiptRequest) returns (RedeemReceiptResponse) {}
// After setting a blinded backup-id with PUT /v1/archives/, this fetches
// credentials that can be used to perform operations against that backup-id.
// Clients may (and should) request up to 7 days of credentials at a time.
//
// The redemption_start and redemption_end seconds must be UTC day aligned, and
// must not span more than 7 days.
//
// Each credential contains a receipt level which indicates the backup level
// the credential is good for. If the account has paid backup access that
// expires at some point in the provided redemption window, credentials with
// redemption times after the expiration may be on a lower backup level.
//
// Clients must validate the receipt level on the credential matches a known
// receipt level before using it.
rpc GetBackupAuthCredentials(GetBackupAuthCredentialsRequest) returns (GetBackupAuthCredentialsResponse) {}
}
message SetBackupIdRequest {
// A BackupAuthCredentialRequest containing a blinded encrypted backup-id,
// encoded in standard padded base64. This backup-id should be used for
// message backups only, and must have the message backup type set on the
// credential. If absent, the message credential request will not be updated.
bytes messages_backup_auth_credential_request = 1;
// A BackupAuthCredentialRequest containing a blinded encrypted backup-id,
// encoded in standard padded base64. This backup-id should be used for
// media only, and must have the media type set on the credential. If absent,
// the media credential request will not be updated.
bytes media_backup_auth_credential_request = 2;
}
message SetBackupIdResponse {}
message RedeemReceiptRequest {
// Presentation for a previously acquired receipt, serialized with libsignal
bytes presentation = 1;
}
message RedeemReceiptResponse {
oneof response {
// The receipt was successfully redeemed
google.protobuf.Empty success = 1;
// The target account does not have a backup-id commitment
errors.FailedPrecondition account_missing_commitment = 2 [(tag.reason) = "account_missing_commitment"];
// The provided receipt presentation was malformed or expired
errors.FailedPrecondition invalid_receipt = 3 [(tag.reason) = "invalid_receipt"];
}
}
message GetBackupAuthCredentialsRequest {
// The redemption time for the first credential. This must be a day-aligned
// seconds since epoch in UTC.
int64 redemption_start = 1 [(require.range).min = 1];
// The redemption time for the last credential. This must be a day-aligned
// seconds since epoch in UTC. The span between redemptionStart and
// redemptionEnd must not exceed 7 days.
int64 redemption_stop = 2 [(require.range).min = 1];
}
message GetBackupAuthCredentialsResponse {
message Credentials {
// The requested message backup ZkCredentials indexed by the start of their
// validity period. The smallest key should be for the requested
// redemption_start, the largest for the requested redemption_end.
map<int64, common.ZkCredential> message_credentials = 1;
// The requested media backup ZkCredentials indexed by the start of their
// validity period. The smallest key should be for the requested
// redemption_start, the largest for the requested redemption_end.
map<int64, common.ZkCredential> media_credentials = 2;
}
// The requested credentials. If absent, there was no existing blinded
// backup id associated with the provided account.
Credentials credentials = 1;
}
// Service for backup operations with anonymous credentials
//
// This service never requires account authentication. It instead requires a
// backup-id authenticated with an anonymous credential that cannot be linked
// to the account.
//
// To register an anonymous credential:
//
// 1. Set a backup-id on the authenticated channel via Backups::SetBackupId
// 2. Retrieve BackupAuthCredentials via Backups::GetBackupAuthCredentials
// 3. Generate a key pair and set the public key via
// BackupsAnonymous::SetPublicKey
//
// Unless otherwise noted, requests for this service require a
// SignedPresentation, which includes:
//
// - a presentation generated from a BackupAuthCredential issued by
// GetBackupAuthCredentials
// - a signature of that presentation using the private key of a key pair
// previously set with SetPublicKey.
service BackupsAnonymous {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Retrieve credentials used to read objects stored on the backup cdn
rpc GetCdnCredentials(GetCdnCredentialsRequest) returns (GetCdnCredentialsResponse) {}
// Retrieve credentials used to interact with the SecureValueRecoveryB service
rpc GetSvrBCredentials(GetSvrBCredentialsRequest) returns (GetSvrBCredentialsResponse) {}
// Retrieve information about the currently stored message backup
rpc GetMessageBackupInfo(GetBackupInfoRequest) returns (GetMessageBackupInfoResponse) {}
// Retrieve information about the currently stored media backup
rpc GetMediaBackupInfo(GetBackupInfoRequest) returns (GetMediaBackupInfoResponse) {}
// Permanently set the public key of an ED25519 key-pair for the backup-id.
// All requests (including this one!) must sign their BackupAuthCredential
// presentations with the private key corresponding to the provided public key.
rpc SetPublicKey(SetPublicKeyRequest) returns (SetPublicKeyResponse) {}
// Refresh the backup, indicating that the backup is still active. Clients
// must periodically upload new backups or perform a refresh. If a backup has
// not been active for 30 days, it may be deleted.
rpc Refresh(RefreshRequest) returns (RefreshResponse) {}
// Retrieve an upload form that can be used to perform a resumable upload
rpc GetUploadForm(GetUploadFormRequest) returns (GetUploadFormResponse) {}
// Copy and re-encrypt media from the attachments cdn into the backup cdn.
// The original, already encrypted, attachments will be encrypted with the
// provided key material before being copied.
//
// The copy operation is not atomic and responses will be returned as copy
// operations complete with detailed information about the outcome. If an
// error is encountered, not all requests may be reflected in the responses.
//
// On retries, a particular destination media id must not be reused with a
// different source media id or different encryption parameters.
//
// The response stream may be closed with STREAM_CLOSED error reason. In this
// case, a BackupStreamClosed message will be present in the error details.
rpc CopyMedia(CopyMediaRequest) returns (stream CopyMediaResponse) {}
// Retrieve a page of media objects stored for this backup-id. A client may
// have previously stored media objects that are no longer referenced in their
// current backup. To reclaim storage space used by these orphaned objects,
// perform a list operation and remove any unreferenced media objects
// via DeleteMedia.
rpc ListMedia(ListMediaRequest) returns (ListMediaResponse) {}
// Delete media objects stored with this backup-id. Streams the locations of
// media items back when the item has successfully been removed.
//
// The response stream may be closed with STREAM_CLOSED error reason. In this
// case, a BackupStreamClosed message will be present in the error details.
rpc DeleteMedia(DeleteMediaRequest) returns (stream DeleteMediaResponse) {}
// Delete all backup metadata, objects, and stored public key. To use
// backups again, a public key must be resupplied.
rpc DeleteAll(DeleteAllRequest) returns (DeleteAllResponse) {}
}
message SignedPresentation {
// Presentation of a BackupAuthCredential previously retrieved from
// GetBackupAuthCredentials on the authenticated channel
bytes presentation = 1 [(require.nonEmpty) = true];
// The presentation signed with the private key corresponding to the public
// key set with SetPublicKey
bytes presentation_signature = 2 [(require.nonEmpty) = true];
}
message SetPublicKeyRequest {
SignedPresentation signed_presentation = 1;
// The public key, serialized in libsignal's elliptic-curve public key format.
bytes public_key = 2 [(require.nonEmpty) = true];
}
message SetPublicKeyResponse {
oneof response {
// The public key was successfully set
google.protobuf.Empty success = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
//
// This may also be returned if there was an existing public key and the
// provided public key did not match.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetCdnCredentialsRequest {
SignedPresentation signed_presentation = 1;
uint32 cdn = 2;
}
message GetCdnCredentialsResponse {
message CdnCredentials {
map<string, string> headers = 1;
}
oneof response {
// Headers to include with requests to the read from the backup CDN. Includes
// time limited read-only credentials.
CdnCredentials cdn_credentials = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetSvrBCredentialsRequest {
SignedPresentation signed_presentation = 1;
}
message GetSvrBCredentialsResponse {
message SvrBCredentials {
// A username that can be presented to authenticate with SVRB
string username = 1;
// A password that can be presented to authenticate with SVRB
string password = 2;
}
oneof response {
SvrBCredentials svrb_credentials = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetBackupInfoRequest {
SignedPresentation signed_presentation = 1;
}
message GetMessageBackupInfoResponse {
message MessageBackupInfo {
// The base directory of your backup data on the cdn. Always non-empty, even
// if a backup has not actually been stored to the cdn. If a backup was
// previously uploaded and has not expired, it can be found in the returned
// cdn at /backup_dir/backup_name.
string backup_dir = 1;
// The CDN type where the message backup is stored. Media may be stored
// elsewhere.
uint32 cdn = 2;
// The location of the message backup on the cdn. Always non-empty, even
// if a backup has not actually been stored to the cdn. If a backup was
// previously uploaded and has not expired, it can be found in the returned
// cdn at /backup_dir/backup_name.
string backup_name = 3;
}
oneof response {
MessageBackupInfo backup_info = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetMediaBackupInfoResponse {
message MediaBackupInfo {
// The base directory of your backup data on the cdn. Always non-empty, even
// if no media has been stored to the cdn or the credential is for a tier
// that does not support media.
string backup_dir = 1;
// The prefix path component for media objects on a cdn. Stored media for a
// media_id can be found at /backup_dir/media_dir/media_id, where the
// media_id is encoded in unpadded url-safe base64. Always non-empty, even
// if no media has been stored to the cdn or the credential is for a tier
// that does not support media.
string media_dir = 2;
// The amount of space used to store media
uint64 used_space = 3;
}
oneof response {
MediaBackupInfo backup_info = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message RefreshRequest {
SignedPresentation signed_presentation = 1;
}
message RefreshResponse {
oneof response {
// The backup was successfully refreshed
google.protobuf.Empty success = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetUploadFormRequest {
SignedPresentation signed_presentation = 1;
message MessagesUploadType {}
message MediaUploadType {}
oneof upload_type {
// Retrieve an upload form that can be used to perform a resumable upload of
// a message backup. The finished upload will be available on the backup cdn.
MessagesUploadType messages = 2;
// Retrieve an upload form for a temporary location that can be used to
// perform a resumable upload of an attachment. After uploading, the
// attachment can be copied into the backup via CopyMedia.
//
// Behaves identically to the account authenticated version at /attachments.
MediaUploadType media = 3;
}
// The length of the attachment for the requested upload form. Uploads
// performed with this form will be limited to the provided length.
uint64 uploadLength = 4 [(require.range) = {min: 1}];
}
message GetUploadFormResponse {
oneof response {
common.UploadForm upload_form = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
// The request size was larger than the maximum supported upload size. The
// maximum upload size is subject to change and is governed by
// `global.attachments.maxBytes`
errors.FailedPrecondition exceeds_max_upload_length = 3 [(tag.reason) = "oversize_upload"];
}
}
message CopyMediaItem {
// The attachment cdn of the object to copy into the backup
uint32 source_attachment_cdn = 1 [(require.range).min = 1, (require.range).max = 3];
// The attachment key of the object to copy into the backup
string source_key = 2 [(require.nonEmpty) = true, (require.base64url) = true];
// The length of the source attachment before the encryption applied by the
// copy operation
uint64 object_length = 3;
// media_id to copy on to the backup CDN
bytes media_id = 4 [(require.exactlySize) = 15];
// A 32-byte key for the MAC
bytes hmac_key = 5 [(require.exactlySize) = 32];
// A 32-byte encryption key for AES
bytes encryption_key = 6 [(require.exactlySize) = 32];
}
message CopyMediaRequest {
SignedPresentation signed_presentation = 1;
// Items to copy
repeated CopyMediaItem items = 2 [(require.size) = {min: 1, max: 1000}];
}
message CopyMediaResponse {
message SourceNotFound {}
message WrongSourceLength {}
message OutOfSpace {}
message CopySuccess {
// The backup cdn where this media object is stored
uint32 cdn = 1;
}
// The 15-byte media_id from the corresponding CopyMediaItem in the request
bytes media_id = 1;
oneof response {
// The media item was successfully copied into the backup
CopySuccess success = 2;
// The source object was not found
SourceNotFound source_not_found = 3 [(tag.reason) = "source_not_found"];
// The provided object length was incorrect
WrongSourceLength wrong_source_length = 4 [(tag.reason) = "wrong_source_length"];
// All media capacity has been consumed. Free some space to continue.
OutOfSpace out_of_space = 5 [(tag.reason) = "out_of_space"];
}
}
// The reason why a media stream RPC is being prematurely closed by the server.
message BackupStreamClosed {
oneof reason {
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 1 [(tag.reason) = "failed_authentication"];
}
}
message ListMediaRequest {
SignedPresentation signed_presentation = 1;
// A cursor returned by a previous call to ListMedia, absent on the first call
optional string cursor = 2;
// If provided, the maximum number of entries to return in a page. If absent,
// a server-chosen default is used.
optional uint32 limit = 3 [(require.range) = {min: 1, max: 10000}];
}
message ListMediaResponse {
message ListEntry {
// The backup cdn where this media object is stored
uint32 cdn = 1;
// The media_id of the object
bytes media_id = 2;
// The length of the object in bytes
uint64 length = 3;
}
message ListResult {
// A page of media objects stored for this backup ID
repeated ListEntry page = 1;
// The base directory of the backup data on the cdn. The stored media can be
// found at /backup_dir/media_dir/media_id, where the media_id is encoded with
// unpadded url-safe base64.
string backup_dir = 2;
// The prefix path component for the media objects. The stored media for
// media_id can be found at /backup_dir/media_dir/media_id, where the media_id
// is encoded with unpadded url-safe base64.
string media_dir = 3;
// If set, the cursor value to pass to the next list request to continue
// listing. If absent, all objects have been listed
optional string cursor = 4;
}
oneof response {
ListResult list_result = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message DeleteAllRequest {
SignedPresentation signed_presentation = 1;
}
message DeleteAllResponse {
oneof response {
// The backup was successfully scheduled for deletion
google.protobuf.Empty success = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message DeleteMediaItem {
// The backup cdn where this media object is stored
uint32 cdn = 1;
// The media_id of the object to delete
bytes media_id = 2 [(require.exactlySize) = 15];
}
message DeleteMediaRequest {
SignedPresentation signed_presentation = 1;
repeated DeleteMediaItem items = 2 [(require.size) = {min: 1, max: 1000}];
}
message DeleteMediaResponse {
DeleteMediaItem deleted_item = 1;
}