Address feedback and generate access_token on the client side

This commit is contained in:
Andrea Maria Piana 2020-06-25 13:57:49 +02:00
parent ff82702c27
commit 7decaea2ff
2 changed files with 131 additions and 116 deletions

View File

@ -32,8 +32,8 @@ title: 16/PUSH-NOTIFICATION-SERVER
- [PushNotificationRegister](#pushnotificationregister) - [PushNotificationRegister](#pushnotificationregister)
- [PushNotificationPreferences](#pushnotificationpreferences) - [PushNotificationPreferences](#pushnotificationpreferences)
- [PushNotificationDeviceToken](#pushnotificationdevicetoken) - [PushNotificationDeviceToken](#pushnotificationdevicetoken)
- [PushNotificationOptions](#pushnotificationoptions) - [PushNotificationFilterSettings](#pushnotificationfiltersettings)
- [PushNotificationPreferencesResponse](#pushnotificationpreferencesresponse) - [PushNotificationRegistrationResponse](#pushnotificationregistrationresponse)
- [ContactCodeAdvertisement](#contactcodeadvertisement) - [ContactCodeAdvertisement](#contactcodeadvertisement)
- [PushNotificationAdvertisementInfo](#pushnotificationadvertisementinfo) - [PushNotificationAdvertisementInfo](#pushnotificationadvertisementinfo)
- [PushNotificationQuery](#pushnotificationquery) - [PushNotificationQuery](#pushnotificationquery)
@ -55,8 +55,8 @@ title: 16/PUSH-NOTIFICATION-SERVER
Push notifications for iOS devices and some Android devices can only be Push notifications for iOS devices and some Android devices can only be
implemented by relying on [APN service](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) for iOS or [Firebase](https://firebase.google.com/). implemented by relying on [APN service](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) for iOS or [Firebase](https://firebase.google.com/).
This is useful for Android devices that do not support background services or that often kill the This is useful for Android devices that do not support foreground services or that often kill the
background service. foreground service.
iOS only allows certain kind of applications to keep a connection open when in the iOS only allows certain kind of applications to keep a connection open when in the
background, VoIP for example, which current status client does not qualify for. background, VoIP for example, which current status client does not qualify for.
@ -70,12 +70,12 @@ Therefore Status provides a set of Push notification services that can be used
to achieve this functionality. to achieve this functionality.
Because this can't be safely implemented in a privacy preserving manner, clients Because this can't be safely implemented in a privacy preserving manner, clients
MUST be given an option not to enable push notifications and not to send push notifications. MUST be given an option to opt-in to receiving and sending push notifications. They are disabled by default.
## Requirements ## Requirements
The party releasing the app needs to run their own [gorush](https://github.com/appleboy/gorush) The party releasing the app MUST possess a certificate for the Apple Push Notification service and its has to run a [gorush](https://github.com/appleboy/gorush) publicly accessible server for sending the actual notification.
publicly accessible server for sending the actual notification. This cannot be community run as it requires sharing private credentials that only status has access to. The party releasing the app, Status in this case, needs to run its own [gorush](https://github.com/appleboy/gorush)
## Components ## Components
@ -86,16 +86,15 @@ available, this will be used only by push notification servers.
### Push notification server ### Push notification server
A push notification server used by clients to register for push notification and A push notification server used by clients to register for receiving and sending push notifications.
sending push notifications.
### Registering client ### Registering client
A status client that wants to receive push notifications A Status client that wants to receive push notifications
### Sending client ### Sending client
A status client that wants to send push notifications A Status client that wants to send push notifications
## Registering with the push notification service ## Registering with the push notification service
@ -110,11 +109,10 @@ The content of the message MUST contain the following [protobuf record](https://
```protobuf ```protobuf
message PushNotificationOptions { message PushNotificationFilterSettings {
boolean enabled = 1; boolean enabled = 1;
repeated string allowed_user_list = 2; repeated string allowed_key_list = 2;
repeated string block_user_list = 3; repeated string blocked_chat_list = 3;
repeated string block_chat_list = 4;
} }
message PushNotificationDeviceToken { message PushNotificationDeviceToken {
@ -126,13 +124,14 @@ message PushNotificationDeviceToken {
TokenType token_type = 1; TokenType token_type = 1;
string token = 2; string token = 2;
string installation_id = 3; string installation_id = 3;
PushNotificationPreferences preferences = 4; PushNotificationFilterSettings filter_settings = 4;
} }
message PushNotificationPreferences { message PushNotificationPreferences {
repeated DeviceToken device_tokens = 1; repeated PushNotificationDeviceToken device_tokens = 1;
uint version = 2; uint version = 2;
boolean unregister = 3; boolean unregister = 3;
string access_token = 4;
} }
message PushNotificationRegister { message PushNotificationRegister {
@ -147,8 +146,9 @@ A push notification server will handle the message according to the following ru
- it MUST verify that `token_type` is supported - it MUST verify that `token_type` is supported
- it MUST verify that `token` is non empty - it MUST verify that `token` is non empty
- it MUST verify that `installation_id` is non empty - it MUST verify that `installation_id` is non empty
- it MUST verify that `version` is non-zero and greater than the currently stored, if any - it MUST verify that `version` is non-zero and greater than the currently stored version for the public key of the sender, if any
- it MUST verify that `device_tokens` is non empty - it MUST verify that `device_tokens` is non empty
- it MUST verify that `access_token` is a valid [`uuid`](https://tools.ietf.org/html/rfc4122)
If `signature` does not match the public key of the sender, the message MUST be discarded. If `signature` does not match the public key of the sender, the message MUST be discarded.
@ -163,26 +163,23 @@ be sent with `error` set to `VERSION_MISMATCH`.
If any other error occurs the `error` should be set to `INTERNAL_ERROR`. If any other error occurs the `error` should be set to `INTERNAL_ERROR`.
If the request is successful, an access token MUST be generated and sent back to the client
with `success` set to `true`. with `success` set to `true`.
Otherwise a response MUST be sent with `success` set to `false`. Otherwise a response MUST be sent with `success` set to `false`.
It is RECOMMENDED to use a randomly generated string for `access_token` of at least 36 characters.
`request_id` should be set to the `SHA3-256` of the signature sent by the client. `request_id` should be set to the `SHA3-256` of the signature sent by the client.
The request MUST be sent on the [partitioned topic][./10-waku-usage.md#partitioned-topic] of the sender. The response MUST be sent on the [partitioned topic][./10-waku-usage.md#partitioned-topic] of the sender.
The payload of the response is: The payload of the response is:
```protobuf ```protobuf
message PushNotificationPreferencesResponse { message PushNotificationRegistrationResponse {
boolean success = 1; boolean success = 1;
ErrorType error = 2; ErrorType error = 2;
bytes request_id = 3; bytes request_id = 3;
PushNotificationPreferences preferences = 4; PushNotificationPreferences preferences = 4;
string access_token = 5;
enum ErrorType { enum ErrorType {
UNKNOWN_ERROR_TYPE = 0; UNKNOWN_ERROR_TYPE = 0;
@ -202,11 +199,11 @@ If `success` is `true` the client has registered successfully.
If `success` is `false`: If `success` is `false`:
- If `MALFORMED_MESSAGE` is returned, the request SHOULD not be retried without ensuring - If `MALFORMED_MESSAGE` is returned, the request SHOULD NOT be retried without ensuring
that is correctly formed. that it is correctly formed.
- If `INTERNAL_ERROR` is returned, the request MAY be retried, but the client MUST be - If `INTERNAL_ERROR` is returned, the request MAY be retried, but the client MUST
backoff exponentially backoff exponentially
- If `VERSION_MISMATCH` is returned, the client SHOULD ensure that the remote version returned in `preferences` is integrated, and the version should be incremented and the request MAY be sent again. - If `VERSION_MISMATCH` is returned, the client SHOULD ensure that the version is incremented to beyond the remote version returned in preferences and then the request MAY be sent again
A client MAY register with multiple Push Notification Servers in order to increase availability. A client MAY register with multiple Push Notification Servers in order to increase availability.
@ -214,8 +211,7 @@ A client SHOULD make sure that all the notification services they registered wit
If no response is returned the request SHOULD be considered failed and MAY be retried with the same server or a different one, but clients MUST exponentially backoff after each trial. If no response is returned the request SHOULD be considered failed and MAY be retried with the same server or a different one, but clients MUST exponentially backoff after each trial.
If the request is successful a token is returned. This SHOULD be If the request is successful the token SHOULD be [advertised](#advertising-a-push-notification-server) as described below if no public key filtering is necessary.
[advertised](#advertising-a-push-notification-server) as described below if no public key filtering is necessary.
## Re-registering with the push notification server ## Re-registering with the push notification server
@ -242,7 +238,7 @@ The server MUST remove all data about this user if `unregistering` is `true`,
apart from the `hash` of the public key and the `version` of the last options, apart from the `hash` of the public key and the `version` of the last options,
in order to make sure that old messages are not processed. in order to make sure that old messages are not processed.
A client MAY unregister from a server on explicit logout if multiple accounts A client MAY unregister from a server on explicit logout if multiple chat keys
are used on a single device. are used on a single device.
## Advertising a push notification server ## Advertising a push notification server
@ -299,15 +295,14 @@ message PushNotificationQuery {
MUST be sent to the server. MUST be sent to the server.
The server MUST be checking the signature of the message and comparing A response MUST be sent:
that with the latest `PushNotificationPreferences` for the given public key.
If the requester has access to any access token, a response MUST be sent:
```protobuf ```protobuf
message PushNotificationQueryInfo { message PushNotificationQueryInfo {
string access_token = 1; string access_token = 1;
string installation_id = 2; string installation_id = 2;
bytes public_key = 3;
repeated encrypted_access_tokens = 4;
} }
message PushNotificationQueryResponse { message PushNotificationQueryResponse {
@ -318,14 +313,22 @@ message PushNotificationQueryResponse {
Otherwise a response MUST NOT be sent. Otherwise a response MUST NOT be sent.
Multiple queries MAY be sent for the same token from different public keys to If `allowed_key_list` is not set `access_token` MUST be set and `encrypted_access_tokens` MUST NOT
increase deniability. be set.
A client MAY send request to the server using an ephemeral key at first, in order not to reveal If `allowed_key_list` is set `encrypted_access_tokens` MUST be set and `access_token` MUST NOT be set.
their identity.
If the request is not successful, they should assume that some public key filtering is If `access_token` is returned, the `access_token` SHOULD be used to send push notifications.
in place at the server level and they MAY retry the request using their real identity.
[//]: TODO: Add more details on the exact encryption method, AES-CTR etc
If `encrypted_access_tokens` are returned, the client SHOULD decrypt each
token by generating an `AES` symmetric key from the DiffieHellman between the
target client and itself
If AES decryption succeeds it will return a valid [`uuid`](https://tools.ietf.org/html/rfc4122) which is what is used for access_token.
The token SHOULD be used to send push notifications.
When querying a notification server an ephemeral key-pair MAY be used.
## Sending a push notification ## Sending a push notification
@ -351,7 +354,7 @@ message PushNotificationRequest {
repeated PushNotification requests = 1; repeated PushNotification requests = 1;
bytes message = 2; bytes message = 2;
string message_id = 3; string message_id = 3;
string ack_required = 4; string ack_key = 4;
} }
``` ```
@ -361,12 +364,11 @@ Where `message` is the encrypted payload of the message and `chat_id` is the
If multiple server are available for a given push notification, only one notification If multiple server are available for a given push notification, only one notification
MUST be sent. MUST be sent.
In such cases `ack_required` MAY bet set and the sender SHOULD be listening The sender SHOULD be listening on the topic derived from the first 4 bytes of `SHA3-256(ack_key)` and with
on the topic derived from the first 4 bytes of `SHA3-256(ack_required)` and with a waku AES symmetric encryption key of `ack_key`.
a waku AES symmetric encryption key of `ack_required`.
If no response is received If no response is received
in 3 seconds, it MAY be retried against a different server. a client SHOULD wait at least 3 seconds, after which the request MAY be retried against a different server
[//]: Can someone replay this message? a uuid could be added to avoid this [//]: Can someone replay this message? a uuid could be added to avoid this
@ -396,17 +398,32 @@ following data:
Where platform is `1` for IOS and `2` for Firebase, according to the [gorush Where platform is `1` for IOS and `2` for Firebase, according to the [gorush
documentation](https://github.com/appleboy/gorush) documentation](https://github.com/appleboy/gorush)
If the `ack_required` is set, a server MUST be sending back a message: A server MUST return a response message:
```protobuf ```protobuf
message PushNotificationAcknowledgement { message PushNotificationAcknowledgement {
string id = 1; string id = 1;
bool success = 2;
ErrorType error = 3;
enum ErrorType {
UNKNOWN_ERROR_TYPE = 0;
BAD_TOKEN = 1;
INTERNAL_ERROR = 2;
}
} }
``` ```
Where id is the `ack_required` sent by the client. Where id is the `ack_key` sent by the client.
The topic and encryption key used MUST be the same as described above. The topic and encryption key used MUST be the same as described above.
If the request is accepted `success` MUST be set to `true`.
Otherwise `success` MUST be set to `false`.
If `error` is `BAD_TOKEN` the client MAY query again the server for the token and
retry the request.
If `error` is `INTERNAL_ERROR` the client MAY retry the request.
## Flow ## Flow
### Registration process ### Registration process
@ -446,19 +463,20 @@ A `PushNotificationRegister` is used to register with a Push Notification server
#### Data disclosed #### Data disclosed
- Public key of the author - Chat key of the author
### PushNotificationPreferences ### PushNotificationPreferences
A push notification preferences message describes the push notification options and tokens for all the devices associated with `PublicKeyClient`. A push notification preferences message describes the push notification options and tokens for all the devices associated with `PublicKeyClient`.
`device_tokens`: a list of `PushNotificationPreferences`, one for each device owned by the user. `device_tokens`: a list of `PushNotificationDeviceToken`, one for each device owned by the user.
`version`: an atomically increasing number identifying the current `PushNotificationPreferences`. Any time anything is changed in the record it MUST be increased by the client, otherwise the request will not be accepted. `version`: a monotonically increasing number identifying the current `PushNotificationPreferences`. Any time anything is changed in the record it MUST be increased by the client, otherwise the request will not be accepted.
`unregister`: whether the account should be unregistered `unregister`: whether the account should be unregistered
`access_token`: the access token that will be given to clients to send push notifications
#### Data disclosed #### Data disclosed
- Number of devices with push notifications enabled for a given public key - Number of devices with push notifications enabled for a given chat key
- The times a push notification record has been modified by the user - The times a push notification record has been modified by the user
### PushNotificationDeviceToken ### PushNotificationDeviceToken
@ -469,37 +487,34 @@ A push notification preferences message describes the push notification options
Notification service and `FIREBASE_TOKEN` for `Firebase`. Notification service and `FIREBASE_TOKEN` for `Firebase`.
`token`: the actual push notification token sent by `Firebase` or `APN` `token`: the actual push notification token sent by `Firebase` or `APN`
`installation_id`: the [`installation_id`](./2-account.md) of the device `installation_id`: the [`installation_id`](./2-account.md) of the device
`preferences`: the push notification preferences for this device. `filter_setttings`: the push notification filters for this device.
#### Data disclosed #### Data disclosed
- Type of device owned by a given user - Type of device owned by a given user
- The `FIREBASE` or `APN` push notification token - The `FIREBASE` or `APN` push notification token
### PushNotificationOptions ### PushNotificationFilterSettings
[//]: (Any of these can be sha3 in order not to store it on the server) [//]: (Any of these can be sha3 in order not to store it on the server)
`enabled`: whether the device wants to be sent push notifications `enabled`: whether the device wants to be sent push notifications
`allowed_user_list`: a list of `SHA3-256` of public keys hex encoded as a string. If non-empty `allowed_key_list`: a list of `access_token` encrypted with the AES key generated
only those public keys will be issued a token. by DiffieHellman between the publisher and the allowed
`block_user_list`: a list of `SHA3-256` of public keys hex encoded as a string. Any public key contact.
in this list MUST not be issued a token. `blocked_chat_list`: a list of `SHA2-256` hashes of chat ids.
`block_chat_list`: a list of `SHA3-256` hashes of chat ids. Any chat id in this list will not trigger a notification. Any chat id in this list will not trigger a notification.
#### Data disclosed #### Data disclosed
- Hash of the public keys a user will allow/block
- Hash of the chat_id a user is not interested in for notifications - Hash of the chat_id a user is not interested in for notifications
### PushNotificationPreferencesResponse ### PushNotificationRegistrationResponse
`success`: whether the registration was successful `success`: whether the registration was successful
`error`: the error type, if any `error`: the error type, if any
`request_id`: the `SHA3-256` hash of the `signature` of the request `request_id`: the `SHA3-256` hash of the `signature` of the request
`preferences`: the server stored preferences in case of an error `preferences`: the server stored preferences in case of an error
`access_token`: the token that needs to be used by client to send a push notification,
in case the request is successful. This is generated by the push notification server.
### ContactCodeAdvertisement ### ContactCodeAdvertisement
@ -507,7 +522,7 @@ in case the request is successful. This is generated by the push notification se
#### Data disclosed #### Data disclosed
- The public key of the sender - The chat key of the sender
### PushNotificationAdvertisementInfo ### PushNotificationAdvertisementInfo
@ -528,13 +543,15 @@ in case the request is successful. This is generated by the push notification se
#### Data disclosed #### Data disclosed
- The user public key
- The hash of the public keys the user is interested in - The hash of the public keys the user is interested in
### PushNotificationQueryInfo ### PushNotificationQueryInfo
`access_token`: the access token used to send a push notification `access_token`: the access token used to send a push notification
`installation_id`: the `installation_id` of the device associated with the `access_token` `installation_id`: the `installation_id` of the device associated with the `access_token`
`public_key`: the `SHA3-256` of the public key associated with this `access_token` and `installation_id`
`encrypted_access_tokens`: a list of encrypted access tokens to be returned
to the client in case there's any filtering on public keys in place.
### PushNotificationQueryResponse ### PushNotificationQueryResponse
@ -554,7 +571,7 @@ in case the request is successful. This is generated by the push notification se
`requests`: a list of `PushNotification` `requests`: a list of `PushNotification`
`message`: the encrypted message that we want to notify on `message`: the encrypted message that we want to notify on
`message_id`: the [status message id](./6-payloads.md) `message_id`: the [status message id](./6-payloads.md)
`ack_required`: a 32 bytes long AES key, only set if an ack is required `ack_key`: a 32 bytes long AES key
### Data disclosed ### Data disclosed
@ -563,7 +580,7 @@ in case the request is successful. This is generated by the push notification se
### PushNotificationAcknowledgement ### PushNotificationAcknowledgement
`id`: the `ack_required` string passed by the client `id`: the `ack_key` string passed by the client
## Partitioned topic ## Partitioned topic
@ -596,19 +613,16 @@ responsibility of propagating information about the user is left to the client,
in order to preserve privacy. in order to preserve privacy.
A client in anonymous mode can register with the server using a key different A client in anonymous mode can register with the server using a key different
from their identity key. from their chat key.
This will hide their real identity key. This will hide their real chat key.
This identity key is effectively a secret and SHOULD only be disclosed to clients that you the user wants to be notified by. This public key is effectively a secret and SHOULD only be disclosed to clients that you the user wants to be notified by.
A client MAY advertise the access token on the contact-code topic of the key generated. A client MAY advertise the access token on the contact-code topic of the key generated.
A client MAY share their identity key through [contact updates](./6-payloads.md#contact-update) A client MAY share their public key through [contact updates](./6-payloads.md#contact-update)
A client receiving a push notification identity key SHOULD listen to the contact code A client receiving a push notification public key SHOULD listen to the contact code
topic of the push notification identity key for updates. topic of the push notification public key for updates.
If a client has received a push notification identity key they MAY request
the access token using an ephemeral key when querying the server.
The method described above effectively does not share the identity of the sender The method described above effectively does not share the identity of the sender
nor the receiver to the server, but MAY result in missing push notifications as nor the receiver to the server, but MAY result in missing push notifications as
@ -617,30 +631,29 @@ the propagation of the secret is left to the client.
This can be mitigated by [device syncing](./6-payloads.md), but not completely This can be mitigated by [device syncing](./6-payloads.md), but not completely
addressed. addressed.
[//]: This section can be removed, for now leaving it here in order to help with the
review process. Point can be integrated, suggestion welcome.
## Security considerations ## Security considerations
If no anonymous mode is used, when registering with a push notification service a client discloses: If no anonymous mode is used, when registering with a push notification service a client discloses:
- The public key - The chat key
- The devices that will receive notifications - The devices that will receive notifications
A client MAY disclose: A client MAY disclose:
- The hash of the chat_ids they want to filter out - The hash of the chat_ids they want to filter out
- The hash of the public keys that the client is interested/not interested in
When running in anonymous mode, the client's public key is not disclosed. When running in anonymous mode, the client's chat key is not disclosed.
If no anonymous mode is used, when querying a notification service a client discloses: When querying a push notification server a client will disclose:
- The public key - That it is interested in sending push notification to another client,
but the querying client's chat key is not disclosed
In any case clients will disclose: When sending a push notification a client discloses:
- The `SHA3-256` of the chat id
- The fact that is interested in sending push notification to a given client [//]: This section can be removed, for now leaving it here in order to help with the
review process. Point can be integrated, suggestion welcome.
## FAQ ## FAQ
@ -692,14 +705,14 @@ The data disclosed with each message sent by the client is above, but for a summ
When you register with a push notification service you may disclose: When you register with a push notification service you may disclose:
1) Your public key 1) Your chat key
2) Which devices you have 2) Which devices you have
3) The hash of the chat_ids you want to filter out 3) The hash of the chat_ids you want to filter out
4) The has of the public keys you are interested/not interested in 4) The hash of the public keys you are interested/not interested in
When you query a notification service you may disclose: When you query a notification service you may disclose:
1) Your public key 1) Your chat key
2) The fact that you are interested in sending push notification to a given user 2) The fact that you are interested in sending push notification to a given user
Effectively this is fairly revealing if the user has a whitelist implemented. Effectively this is fairly revealing if the user has a whitelist implemented.
@ -720,11 +733,12 @@ Most of the request have a target, so protocol negotiation can happen. We cannot
the advertisement as that's effectively a broadcast, but those info should not change and we can the advertisement as that's effectively a broadcast, but those info should not change and we can
always accrete the message. always accrete the message.
### Why ack_required? ### Why ack_key?
That's necessary to avoid duplicated push notifications. That's necessary to avoid duplicated push notifications and allow for the retry
in case the notification is not successful.
Deduplication of the push notification is done on the client side, to relive a bit Deduplication of the push notification is done on the client side, to reduce a bit
of centralization and also in order not to have to modify gorush. of centralization and also in order not to have to modify gorush.
### Can I run my own node? ### Can I run my own node?

View File

@ -195,9 +195,10 @@ puk
PushNotificationRegister PushNotificationRegister
PushNotificationPreferences PushNotificationPreferences
PushNotificationOptions PushNotificationOptions
PushNotificationFilterSettings
PushNotificationDeviceToken PushNotificationDeviceToken
PushNotificationOptions PushNotificationOptions
PushNotificationPreferencesResponse PushNotificationRegistrationResponse
PushNotificationAdvertisementInfo PushNotificationAdvertisementInfo
PushNotificationQuery PushNotificationQuery
PushNotificationQueryInfo PushNotificationQueryInfo