I'm building a PHP Web Push notification Listener for Mastodon. I successfully created a subscription by sending the following post data to the Mastodon subscription API URL:
$post_data = array(
"subscription" => array(
"endpoint" => $env["endpoint"], // URL of endpoint where PHP script is
"keys" => array(
"p256dh" => $env["vapid_public"], // Public key
"auth" => $env["vapid_private"] // Private key
)
),
"data" => array(
"alerts" => array(
"mention" => true // receive notifications for mentions
)
)
);
When I mention or send a DM to the account the subscription is set up for, the PHP script receives a POST request with the Web Push notification. The notification payload is picked up by using file_get_contents("php://input"). This is binary data.
My question now is how do I decrypt this binary data. I have tried converting the payload to hex first, and then feeding it to openssl_decrypt() but I'm not sure what keys to use and so I'm kinda in the dark here. Any help is appreciated.
Here's a copy of the POST headers received by the Web Push Notification from Mastodon The actual values of the keys is replaced with some words, because of security. Dots [.], identifiers [xx=] and semicolons[;] are left in place.
[Authorization] => WebPush some_key_1.some_key_2.some_key_3 // This is the JSON Web Token https://web.dev/articles/push-notifications-web-push-protocol#json_web_token
[Crypto-Key] => dh=crypto_key_1;p256ecdsa=crypto_key_2
[Encryption] => salt=salt_key
[Content-Encoding] => aesgcm
[Urgency] => normal
[Ttl] => 172800
[Content-Type] => application/octet-stream
[Digest] => SHA-256=digest_key
[Accept-Encoding] => gzip
[Date] => Wed, 22 Nov 2023 16:02:27 GMT
[User-Agent] => http.rb/5.1.1 (Mastodon/4.2.1; +https://hidden_url.com/)
[Content-Length] => 334
[Connection] => close
[Host] => other_hidden_url.com
[X-Real-Port] => xxxxx
[X-Port] => 443
[X-Https] => on
[X-Real-Ip] => hidden_ip
[X-Forwarded-By] => other_hidden_ip
openssl_decrypt()
uses the following parameters:
function openssl_decrypt(
string $data,
string $cipher_algo,
string $passphrase,
int $options = 0,
string $iv = "",
string|null $tag = null,
string $aad = ""
): string|false
I guess the challenge now is to map the keys from the headers and the keys I used for creating the subscription to these parameters. I have tried different combinations, but no success yet.
Documentation used:
- Mastodon Web Push API: https://docs.joinmastodon.org/methods/push/
- openssl_decrypt() PHP: https://www.php.net/manual/en/function.openssl-decrypt.php
- Mozilla Push API: https://developer.mozilla.org/en-US/docs/Web/API/Push_API