package federation import ( "context" "encoding/json" "net/http" ) // The end-to-end encryption half of the Server-Server API: asking another // server for its users' device keys, claiming their one-time keys, and the // EDUs that say a user's keys have changed. // // Keys travel as json.RawMessage. They are signed objects, and the only // safe thing to do with a signed object on its way through is not to // re-encode it. // UserKeysQueryRequest is the body of POST /user/keys/query: user ID to the // device IDs wanted, where an empty list asks for every device. type UserKeysQueryRequest struct { DeviceKeys map[string][]string `json:"device_keys"` } // UserKeysQueryResponse is the answer to POST /user/keys/query. type UserKeysQueryResponse struct { DeviceKeys map[string]map[string]json.RawMessage `json:"device_keys"` MasterKeys map[string]json.RawMessage `json:"master_keys,omitempty"` SelfSigningKeys map[string]json.RawMessage `json:"self_signing_keys,omitempty"` } // UserKeysClaimRequest is the body of POST /user/keys/claim: user ID, then // device ID, then the algorithm of the key wanted. type UserKeysClaimRequest struct { OneTimeKeys map[string]map[string]string `json:"one_time_keys"` } // UserKeysClaimResponse is the answer to POST /user/keys/claim: user ID, // then device ID, then key ID, then the key. type UserKeysClaimResponse struct { OneTimeKeys map[string]map[string]map[string]json.RawMessage `json:"one_time_keys"` } // UserDevicesResponse is the answer to GET /user/devices/{userId}: the // whole of a user's device list, which a server asks for when the updates // it was sent do not add up. type UserDevicesResponse struct { UserID string `json:"user_id"` StreamID int64 `json:"stream_id"` Devices []UserDevice `json:"devices"` MasterKey json.RawMessage `json:"master_key,omitempty"` SelfSigningKey json.RawMessage `json:"self_signing_key,omitempty"` } // UserDevice is one device in a UserDevicesResponse. type UserDevice struct { DeviceID string `json:"device_id"` Keys json.RawMessage `json:"keys"` DeviceDisplayName string `json:"device_display_name,omitempty"` } // DeviceListUpdateContent is the content of an m.device_list_update EDU: // one device of one user changed, or went away. type DeviceListUpdateContent struct { UserID string `json:"user_id"` DeviceID string `json:"device_id"` DeviceDisplayName string `json:"device_display_name,omitempty"` StreamID int64 `json:"stream_id"` PrevID []int64 `json:"prev_id,omitempty"` Deleted bool `json:"deleted,omitempty"` Keys json.RawMessage `json:"keys,omitempty"` } // SigningKeyUpdateContent is the content of an m.signing_key_update EDU: a // user's cross-signing keys changed. type SigningKeyUpdateContent struct { UserID string `json:"user_id"` MasterKey json.RawMessage `json:"master_key,omitempty"` SelfSigningKey json.RawMessage `json:"self_signing_key,omitempty"` } // QueryUserKeys asks a server for its users' device and cross-signing // keys. func (c *Client) QueryUserKeys(ctx context.Context, destination string, req UserKeysQueryRequest) (UserKeysQueryResponse, error) { var out UserKeysQueryResponse err := c.do(ctx, request{ method: http.MethodPost, destination: destination, signed: true, path: "/_matrix/federation/v1/user/keys/query", body: req, }, &out) return out, err } // ClaimUserKeys claims one-time keys of a server's users' devices. func (c *Client) ClaimUserKeys(ctx context.Context, destination string, req UserKeysClaimRequest) (UserKeysClaimResponse, error) { var out UserKeysClaimResponse err := c.do(ctx, request{ method: http.MethodPost, destination: destination, signed: true, path: "/_matrix/federation/v1/user/keys/claim", body: req, }, &out) return out, err } // UserDevices asks a server for one of its users' whole device list. func (c *Client) UserDevices(ctx context.Context, destination, userID string) (UserDevicesResponse, error) { var out UserDevicesResponse err := c.do(ctx, request{ method: http.MethodGet, destination: destination, signed: true, path: "/_matrix/federation/v1/user/devices/" + esc(userID), }, &out) return out, err }