From e835d5e6c35b19137430c14ee5dc6f69bba8e094 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 17 Feb 2021 13:14:03 +0100 Subject: [PATCH 01/12] add MSC --- proposals/3013-encrypted-push.md | 171 +++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 proposals/3013-encrypted-push.md diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md new file mode 100644 index 00000000000..d24224464db --- /dev/null +++ b/proposals/3013-encrypted-push.md @@ -0,0 +1,171 @@ +# MSC3013: Encrypted Push + +Push notifications have the problem that they typically go through third-party gateways in order to +be delivered, e.g. FCM (Google) or APNs (Apple) and an app-specific gateway (sygnal). In order to +prevent these push gateways from being able to read any sensitive information the `event_id_only` format +was introduced, which only pushes the `event_id` and `room_id` of an event down the push. After +receiving the push message the client can hit the `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}` +to fetch the full event, and then create the notification based on that. + +This, however, introduces the issue of having to perform an additional HTTP request to be able to +display the full event notification. On some systems (e.g. some weird vendor-specific android phones, +or while driving through ~~rural germany~~ places with patchy cellular network availability) this isn't +always. + +This proposal adds a method to encrypt the push message in a way only the recipient client can decrypt +it, allowing the server to send the full event over push again, with this MSC and [MSC2782](https://github.com/matrix-org/matrix-doc/pull/2782). + +## Proposal + +A new pusher kind `http.encrypted.curve25519-aes-sha2` is introduced. It behaves the same as the pusher +kind `http`, except that the content it pushes is encrypted. That means it inherits all the pusher +data requirements, all the formats etc. It adds a new pusher data field, `public_key`, which is a +(optionally unpadded) base64-encoded curve25519 public key. This new field is not to be added to the +actual push payload being sent to push gateways. As such, setting such a pusher could look as +following: + +``` +POST /_matrix/client/r0/pushers/set HTTP/1.1 +Content-Type: application/json + +{ + "lang": "en", + "kind": "http.encrypted.curve25519-aes-sha2", + "app_display_name": "Mat Rix", + "device_display_name": "iPhone 9", + "profile_tag": "xxyyzz", + "app_id": "com.example.app.ios", + "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ", + "data": { + "url": "https://push-gateway.location.here/_matrix/push/v1/notify", + "format": "event_id_only", + "public_key": "GkZgmbbxnYZfFtywxF4K7NUPqA50Kb7TEsyHeVWyHBI" + }, + "append": false +} +``` + +Now, when the homeserver pushes out the message, it is to perform the `notification` dict as with the +http pusher, and then encrypt all of its contents, apart from the `devices` key, using the following +algorithm: + +1. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral key and the backup's + public key to generate a shared secret. The public half of the ephemeral key, encoded using unpadded + base64, becomes the `ephemeral` property of the new payload. +2. Using the shared secret, generate 80 bytes by performing an HKDF using SHA-256 as the hash, with + a salt of 32 bytes of 0, and with the empty string as the info. The first 32 bytes are used as the + AES key, the next 32 bytes are used as the MAC key, and the last 16 bytes are used as the AES + initialization vector. +3. Stringify the JSON object, and encrypt it using AES-CBC-256 with PKCS#7 padding. This encrypted + data, encoded using unpadded base64, becomes the `ciphertext` property of the new payload. +4. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256 using the MAC key + generated above. The first 8 bytes of the resulting MAC are base64-encoded, and become the `mac` + property of the new payload. + +This is the same algorithm used currently in the unstable spec for megolm backup, as such it is +comptible with libolms PkEncryption / PkDecryption methods. + +### Example: +Suppose a normal http pusher would push out the following content: +```json +{ + "notification": { + "event_id": "$3957tyerfgewrf384", + "room_id": "!slw48wfj34rtnrf:example.com", + "type": "m.room.message", + "sender": "@exampleuser:matrix.org", + "sender_display_name": "Major Tom", + "room_name": "Mission Control", + "room_alias": "#exampleroom:matrix.org", + "prio": "high", + "content": { + "msgtype": "m.text", + "body": "I'm floating in a most peculiar way." + }, + "counts": { + "unread": 2, + "missed_calls": 1 + }, + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data": {}, + "tweaks": { + "sound": "bing" + } + } + ] + } +} +``` + +The following object would have to be json-encoded to encrypt: + +```json +{ + "event_id": "$3957tyerfgewrf384", + "room_id": "!slw48wfj34rtnrf:example.com", + "type": "m.room.message", + "sender": "@exampleuser:matrix.org", + "sender_display_name": "Major Tom", + "room_name": "Mission Control", + "room_alias": "#exampleroom:matrix.org", + "prio": "high", + "content": { + "msgtype": "m.text", + "body": "I'm floating in a most peculiar way." + }, + "counts": { + "unread": 2, + "missed_calls": 1 + } +} +``` + +Resulting in the following final message being pushed out to the push gateway: + +```json +{ + "notification": { + "ephemeral": "base64_of_ephemeral_public_key", + "ciphertext": "base64_of_ciphertext", + "mac": "base64_of_mac", + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data": {}, + "tweaks": { + "sound": "bing" + } + } + ] + } +} +``` + +## Potential issues + +It is currently implied that a homeserver could push the same notification out to multiple devices +at once, by populating the `devices` array with more than one element. Due to the nature of cryptography, +this won't be possible anymore. + +It is still unclear how well this will work with iOS and its limitations, especially concerning badge-only +updates if a message was read on another device. + +If the gateway does additional processing, like marking call attempts differently, the relevant data +musn't be encrypted. + +## Security considerations + +In a first draft symmetric encryption was used. However, using asymmetric encryption seams like the +proper way to go here, as, in the case of the server being compromised, there is no need to re-negotiate +a new key to encrypt the push message. + +## Unstable prefix + +Is this needed here? If so, how? Soru is kinda confused as the pusher kinds are just `http` and `email`, +not having any `m.` prefix. From c57fd71f75ffe855d6774028b0562da6a708808e Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 17 Feb 2021 13:25:36 +0100 Subject: [PATCH 02/12] finish unfinished sentence --- proposals/3013-encrypted-push.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index d24224464db..99c1a3162ea 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -10,7 +10,7 @@ to fetch the full event, and then create the notification based on that. This, however, introduces the issue of having to perform an additional HTTP request to be able to display the full event notification. On some systems (e.g. some weird vendor-specific android phones, or while driving through ~~rural germany~~ places with patchy cellular network availability) this isn't -always. +always the case. This proposal adds a method to encrypt the push message in a way only the recipient client can decrypt it, allowing the server to send the full event over push again, with this MSC and [MSC2782](https://github.com/matrix-org/matrix-doc/pull/2782). From 9677a5b72922a2475a566009b79857e7b101c028 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 17 Feb 2021 14:53:26 +0100 Subject: [PATCH 03/12] fix typo --- proposals/3013-encrypted-push.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 99c1a3162ea..0bb73d2c128 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -161,7 +161,7 @@ musn't be encrypted. ## Security considerations -In a first draft symmetric encryption was used. However, using asymmetric encryption seams like the +In a first draft symmetric encryption was used. However, using asymmetric encryption seems like the proper way to go here, as, in the case of the server being compromised, there is no need to re-negotiate a new key to encrypt the push message. From 24686caeaee4b77f87691f4eddbf54856e9e4ae1 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 17 Feb 2021 15:07:51 +0100 Subject: [PATCH 04/12] update example --- proposals/3013-encrypted-push.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 0bb73d2c128..1ef5ade3029 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -22,7 +22,7 @@ kind `http`, except that the content it pushes is encrypted. That means it inher data requirements, all the formats etc. It adds a new pusher data field, `public_key`, which is a (optionally unpadded) base64-encoded curve25519 public key. This new field is not to be added to the actual push payload being sent to push gateways. As such, setting such a pusher could look as -following: +following (assuming [MSC2782](https://github.com/matrix-org/matrix-doc/pull/2782) has been merged): ``` POST /_matrix/client/r0/pushers/set HTTP/1.1 @@ -38,7 +38,7 @@ Content-Type: application/json "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ", "data": { "url": "https://push-gateway.location.here/_matrix/push/v1/notify", - "format": "event_id_only", + "format": "full_event", "public_key": "GkZgmbbxnYZfFtywxF4K7NUPqA50Kb7TEsyHeVWyHBI" }, "append": false From c46a21c9c2386835935275b90583e868ffcf36ad Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 17 Feb 2021 15:15:28 +0100 Subject: [PATCH 05/12] add alternative suggestion --- proposals/3013-encrypted-push.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 1ef5ade3029..8c51358c8d4 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -159,6 +159,13 @@ updates if a message was read on another device. If the gateway does additional processing, like marking call attempts differently, the relevant data musn't be encrypted. +## Alternatives + +Instead of this MSC, the push gateway for a specific app could encrypt the contents so that FCM/APNs +are still unable to see them. As the push gateway is set by app, that would mean that another server +than your homeserver gets full event contents for your push notifications. So this is also questionable +from a privacy point of view. + ## Security considerations In a first draft symmetric encryption was used. However, using asymmetric encryption seems like the From 7b45e05a2aa2da319bf08603f888b1e9fced34af Mon Sep 17 00:00:00 2001 From: Sorunome Date: Sat, 20 Feb 2021 14:21:00 +0100 Subject: [PATCH 06/12] specify correct public key --- proposals/3013-encrypted-push.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 8c51358c8d4..ad2fa126b95 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -49,9 +49,9 @@ Now, when the homeserver pushes out the message, it is to perform the `notificat http pusher, and then encrypt all of its contents, apart from the `devices` key, using the following algorithm: -1. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral key and the backup's - public key to generate a shared secret. The public half of the ephemeral key, encoded using unpadded - base64, becomes the `ephemeral` property of the new payload. +1. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral key and the public key + specified when setting the pusher to generate a shared secret. The public half of the ephemeral key, + encoded using unpadded base64, becomes the `ephemeral` property of the new payload. 2. Using the shared secret, generate 80 bytes by performing an HKDF using SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string as the info. The first 32 bytes are used as the AES key, the next 32 bytes are used as the MAC key, and the last 16 bytes are used as the AES From d43e5ff31f85cee3e72220f1cbf615c788af9f0d Mon Sep 17 00:00:00 2001 From: Sorunome Date: Mon, 25 Oct 2021 15:33:52 +0200 Subject: [PATCH 07/12] change the encrypted stuff to not be a new pusher kind, but instead a flag to the http pusher kind --- proposals/3013-encrypted-push.md | 41 +++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index ad2fa126b95..ff230394862 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -10,19 +10,29 @@ to fetch the full event, and then create the notification based on that. This, however, introduces the issue of having to perform an additional HTTP request to be able to display the full event notification. On some systems (e.g. some weird vendor-specific android phones, or while driving through ~~rural germany~~ places with patchy cellular network availability) this isn't -always the case. +always possible. This proposal adds a method to encrypt the push message in a way only the recipient client can decrypt it, allowing the server to send the full event over push again, with this MSC and [MSC2782](https://github.com/matrix-org/matrix-doc/pull/2782). ## Proposal -A new pusher kind `http.encrypted.curve25519-aes-sha2` is introduced. It behaves the same as the pusher -kind `http`, except that the content it pushes is encrypted. That means it inherits all the pusher -data requirements, all the formats etc. It adds a new pusher data field, `public_key`, which is a -(optionally unpadded) base64-encoded curve25519 public key. This new field is not to be added to the -actual push payload being sent to push gateways. As such, setting such a pusher could look as -following (assuming [MSC2782](https://github.com/matrix-org/matrix-doc/pull/2782) has been merged): +A new pusher data field, `algorithm`, is introduced for pushers of the kind `http`. It is an enum, +represented as string. The currently allowed values are `m.plain` and `m.curve25519-aes-sha2`. Is the +field absent, then an algorithm of `m.plain` is assumed. The algorithms are defined as following: + +### `m.plain` algorithm + +The `m.plain` algorithm (the default algorithm) denotes that push is to be delivered in plaintext. +That is no change to current http pushers, thus this MSC is backwards compatible. + +### `m.curve25519-aes-sha2` algorithm + +The `m.curve25519-aes-sha2` algorithm indicates that the push payloads are to be sent encrypted. +For this, another pusher data field, `public_key`, is required. This key is an (optionally unpadded) +base64-encoded curve25519 public key. This new field is not to be added to the actual push payload +being sent to push gateways. As such, setting such a pusher could look as following (assuming +[MSC2782](https://github.com/matrix-org/matrix-doc/pull/2782) has been merged): ``` POST /_matrix/client/r0/pushers/set HTTP/1.1 @@ -30,13 +40,14 @@ Content-Type: application/json { "lang": "en", - "kind": "http.encrypted.curve25519-aes-sha2", + "kind": "http", "app_display_name": "Mat Rix", "device_display_name": "iPhone 9", "profile_tag": "xxyyzz", "app_id": "com.example.app.ios", "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ", "data": { + "algorithm": "m.curve25519-aes-sha2", "url": "https://push-gateway.location.here/_matrix/push/v1/notify", "format": "full_event", "public_key": "GkZgmbbxnYZfFtywxF4K7NUPqA50Kb7TEsyHeVWyHBI" @@ -137,7 +148,9 @@ Resulting in the following final message being pushed out to the push gateway: "app_id": "org.matrix.matrixConsole.ios", "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", "pushkey_ts": 12345678, - "data": {}, + "data": { + "algorithm": "m.curve25519-aes-sha2" + }, "tweaks": { "sound": "bing" } @@ -153,11 +166,11 @@ It is currently implied that a homeserver could push the same notification out t at once, by populating the `devices` array with more than one element. Due to the nature of cryptography, this won't be possible anymore. -It is still unclear how well this will work with iOS and its limitations, especially concerning badge-only -updates if a message was read on another device. +~~It is still unclear how well this will work with iOS and its limitations, especially concerning badge-only +updates if a message was read on another device.~~ --> This should work? If the gateway does additional processing, like marking call attempts differently, the relevant data -musn't be encrypted. +musn't be encrypted. That means that clients which rely on that can't use this kind of pusher. ## Alternatives @@ -174,5 +187,5 @@ a new key to encrypt the push message. ## Unstable prefix -Is this needed here? If so, how? Soru is kinda confused as the pusher kinds are just `http` and `email`, -not having any `m.` prefix. +The unstable prefix for the algorithms are `com.famedly`, meaning the unstable prefixes for the new +algorithms introduced are `com.famedly.plain` and `com.famedly.curve25519-aes-sha2`. From 0fe441fc4f04e9a571278a6a1b6daecd9b883c24 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 27 Oct 2021 10:33:05 +0200 Subject: [PATCH 08/12] add advertisement in /versions endpoint --- proposals/3013-encrypted-push.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index ff230394862..5c024de00a0 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -189,3 +189,16 @@ a new key to encrypt the push message. The unstable prefix for the algorithms are `com.famedly`, meaning the unstable prefixes for the new algorithms introduced are `com.famedly.plain` and `com.famedly.curve25519-aes-sha2`. + +Additionally, the feature is to be advertised as unstable feature in the `GET /_matrix/client/versions` +response, with the key `com.famedly.msc3013` set to `true`. So, the response could look then as +following: + +```json +{ + "versions": ["r0.6.0"], + "unstable_features": { + "com.famedly.msc3013": true + } +} +``` From 6d8e7ff311e5531296e6ce6d13fc7371975899ff Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 8 Dec 2021 10:58:45 +0100 Subject: [PATCH 09/12] Fix re-wording and add paragraph on the devices-array not having been used anyways --- proposals/3013-encrypted-push.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 5c024de00a0..5991153772a 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -18,8 +18,8 @@ it, allowing the server to send the full event over push again, with this MSC an ## Proposal A new pusher data field, `algorithm`, is introduced for pushers of the kind `http`. It is an enum, -represented as string. The currently allowed values are `m.plain` and `m.curve25519-aes-sha2`. Is the -field absent, then an algorithm of `m.plain` is assumed. The algorithms are defined as following: +represented as string. The currently allowed values are `m.plain` and `m.curve25519-aes-sha2`. If the +field is absent, then an algorithm of `m.plain` is assumed. The algorithms are defined as following: ### `m.plain` algorithm @@ -164,7 +164,12 @@ Resulting in the following final message being pushed out to the push gateway: It is currently implied that a homeserver could push the same notification out to multiple devices at once, by populating the `devices` array with more than one element. Due to the nature of cryptography, -this won't be possible anymore. +this won't be possible anymore. However, homeserver implementations such as synapse +[1](https://github.com/matrix-org/synapse/blob/d6fb96e056f79de220d8d59429d89a61498e9af3/synapse/push/httppusher.py#L331-L338) +[2](https://github.com/matrix-org/synapse/blob/d6fb96e056f79de220d8d59429d89a61498e9af3/synapse/push/httppusher.py#L357-L365) +[3](https://github.com/matrix-org/synapse/blob/d6fb96e056f79de220d8d59429d89a61498e9af3/synapse/push/httppusher.py#L419-L426) +even hard-code that `devices` array to only contain a single entry, making it unlikely for this flexibility +having been used in the wild. ~~It is still unclear how well this will work with iOS and its limitations, especially concerning badge-only updates if a message was read on another device.~~ --> This should work? From 5e5d979860eaebde644c72cdc6c44b5310e82b21 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Mon, 27 Dec 2021 10:53:06 +0100 Subject: [PATCH 10/12] add `counts_only_type` setting, resolve iOS concerns --- proposals/3013-encrypted-push.md | 167 +++++++++++++++++++++++++++++-- 1 file changed, 158 insertions(+), 9 deletions(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 5991153772a..1235f0174a9 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -29,9 +29,23 @@ That is no change to current http pushers, thus this MSC is backwards compatible ### `m.curve25519-aes-sha2` algorithm The `m.curve25519-aes-sha2` algorithm indicates that the push payloads are to be sent encrypted. -For this, another pusher data field, `public_key`, is required. This key is an (optionally unpadded) -base64-encoded curve25519 public key. This new field is not to be added to the actual push payload -being sent to push gateways. As such, setting such a pusher could look as following (assuming +For this, two new pusher data fields are added: `public_key` and `counts_only_type`. Both fields are +to be omitted from the actual push payloads being sent to the push gateways. + +The field `public_key` is required. This key is an (optionally unpadded) base64-encoded curve25519 +public key. This new field is not to be added to the actual push payload being sent to push gateways. + +The field `counts_only_type` is an optional enum which denotes how push frames should handle counts-only +push payloads, without any events attached to them (e.g. if the user reads a notification and thus the +unread count decreases by one). The possible values are: + + - `none` (default): The unread status is never included in the plaintext payload + - `boolean`: If the push frame only affects the counts a boolean (`is_counts_only`) denoting + this is included in the plaintext payload. + - `full`: If the push frame only affects the counts the full `counts` dict is included in the plaintext + payload. + +As such, setting such a pusher could look as following (assuming [MSC2782](https://github.com/matrix-org/matrix-doc/pull/2782) has been merged): ``` @@ -50,7 +64,8 @@ Content-Type: application/json "algorithm": "m.curve25519-aes-sha2", "url": "https://push-gateway.location.here/_matrix/push/v1/notify", "format": "full_event", - "public_key": "GkZgmbbxnYZfFtywxF4K7NUPqA50Kb7TEsyHeVWyHBI" + "public_key": "GkZgmbbxnYZfFtywxF4K7NUPqA50Kb7TEsyHeVWyHBI", + "counts_only_type": "full" }, "append": false } @@ -76,7 +91,10 @@ algorithm: This is the same algorithm used currently in the unstable spec for megolm backup, as such it is comptible with libolms PkEncryption / PkDecryption methods. -### Example: +Next, the `unread` or `is_counts_only` keys are optionally populated according to how the `counts_only_type` +pusher data is set. + +### Example event notification: Suppose a normal http pusher would push out the following content: ```json { @@ -160,6 +178,123 @@ Resulting in the following final message being pushed out to the push gateway: } ``` +### Example counts only: +Suppose a normal http pusher would push out the following content: +```json +{ + "notification": { + "prio": "high", + "counts": { + "unread": 2, + "missed_calls": 1 + }, + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data": {}, + "tweaks": { + "sound": "bing" + } + } + ] + } +} +``` + +The following object would have to be json-encoded to encrypt: + +```json +{ + "prio": "high", + "counts": { + "unread": 2, + "missed_calls": 1 + } +} +``` + +If `counts_only_type` is `none` the final message is: + +```json +{ + "notification": { + "ephemeral": "base64_of_ephemeral_public_key", + "ciphertext": "base64_of_ciphertext", + "mac": "base64_of_mac", + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data": { + "algorithm": "m.curve25519-aes-sha2" + }, + "tweaks": { + "sound": "bing" + } + } + ] + } +} +``` + +If `counts_only_type` is `boolean` the final message is: + +```json +{ + "notification": { + "ephemeral": "base64_of_ephemeral_public_key", + "ciphertext": "base64_of_ciphertext", + "mac": "base64_of_mac", + "is_counts_only": true, + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data": { + "algorithm": "m.curve25519-aes-sha2" + }, + "tweaks": { + "sound": "bing" + } + } + ] + } +} +``` + +If `counts_only_type` is `full` the final message is: + +```json +{ + "notification": { + "ephemeral": "base64_of_ephemeral_public_key", + "ciphertext": "base64_of_ciphertext", + "mac": "base64_of_mac", + "counts": { + "unread": 2, + "missed_calls": 1 + }, + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data": { + "algorithm": "m.curve25519-aes-sha2" + }, + "tweaks": { + "sound": "bing" + } + } + ] + } +} +``` + ## Potential issues It is currently implied that a homeserver could push the same notification out to multiple devices @@ -169,14 +304,28 @@ this won't be possible anymore. However, homeserver implementations such as syna [2](https://github.com/matrix-org/synapse/blob/d6fb96e056f79de220d8d59429d89a61498e9af3/synapse/push/httppusher.py#L357-L365) [3](https://github.com/matrix-org/synapse/blob/d6fb96e056f79de220d8d59429d89a61498e9af3/synapse/push/httppusher.py#L419-L426) even hard-code that `devices` array to only contain a single entry, making it unlikely for this flexibility -having been used in the wild. - -~~It is still unclear how well this will work with iOS and its limitations, especially concerning badge-only -updates if a message was read on another device.~~ --> This should work? +having been used in the wild. If the gateway does additional processing, like marking call attempts differently, the relevant data musn't be encrypted. That means that clients which rely on that can't use this kind of pusher. +### iOS + +Sadly iOS is pretty limiting concerning push notifications. While modifying the content of a push frame, +and thus e2ee push notifications, is possible on iOS, these modified push frames *have* to result in +a user-visible notification banner. This limitation becomes a problem when only the `counts` dictionary +is updated, so e.g. when a message is being read. For this, the `counts_only_type` setting has been, +so that the push gateway can do additional logic, based on if the notification frame is such a silent +update. + +If the `counts_only_type` is set to `boolean` then the push gateway could send the encrypted push payload +as an APNS background message to the device. This isn't that reliable, sadly, but might be good enough +for an app. + +If the `counts_only_type` is set to `full` then the push gateway could send a badge-update notification +to APNS directly. While this works for sure, that would also mean that the APNS servers get to read +a real-time update of the exact unread count for each user. + ## Alternatives Instead of this MSC, the push gateway for a specific app could encrypt the contents so that FCM/APNs From b66bad38afc171d68e80f72365321f6353e1264e Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 29 Dec 2021 16:01:59 +0100 Subject: [PATCH 11/12] require unpadded base64 --- proposals/3013-encrypted-push.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 1235f0174a9..26967eab851 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -32,7 +32,7 @@ The `m.curve25519-aes-sha2` algorithm indicates that the push payloads are to be For this, two new pusher data fields are added: `public_key` and `counts_only_type`. Both fields are to be omitted from the actual push payloads being sent to the push gateways. -The field `public_key` is required. This key is an (optionally unpadded) base64-encoded curve25519 +The field `public_key` is required. This key is an unpadded base64-encoded curve25519 public key. This new field is not to be added to the actual push payload being sent to push gateways. The field `counts_only_type` is an optional enum which denotes how push frames should handle counts-only From 417591f79b71f8087f58c75881b5d6583a1b08cd Mon Sep 17 00:00:00 2001 From: Sorunome Date: Fri, 31 Dec 2021 11:35:28 +0100 Subject: [PATCH 12/12] clarify wording on the iOS section about `counts_only_type` --- proposals/3013-encrypted-push.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/3013-encrypted-push.md b/proposals/3013-encrypted-push.md index 26967eab851..05900c3b8e8 100644 --- a/proposals/3013-encrypted-push.md +++ b/proposals/3013-encrypted-push.md @@ -314,9 +314,9 @@ musn't be encrypted. That means that clients which rely on that can't use this k Sadly iOS is pretty limiting concerning push notifications. While modifying the content of a push frame, and thus e2ee push notifications, is possible on iOS, these modified push frames *have* to result in a user-visible notification banner. This limitation becomes a problem when only the `counts` dictionary -is updated, so e.g. when a message is being read. For this, the `counts_only_type` setting has been, -so that the push gateway can do additional logic, based on if the notification frame is such a silent -update. +is updated, so e.g. when a message is being read. For this, the `counts_only_type` setting has been +to this proposal, so that the push gateway can do additional logic, based on if the notification frame +is such a silent update. If the `counts_only_type` is set to `boolean` then the push gateway could send the encrypted push payload as an APNS background message to the device. This isn't that reliable, sadly, but might be good enough