RFC 8291
Message Encryption for Web Push
RFC 8291: Message Encryption for Web Push ๐
Okay bestie, here's the deets! ๐ This is RFC 8291, called Message Encryption for Web Push, from the Internet Engineering Task Force (IETF). The author is M. Thomson at Mozilla. It's a Standards Track doc dated November 2017, with ISSN 2070-1721. The title block doesn't list anything it obsoletes or updates. โจ
Abstract ๐ธ
So this doc is all about a message encryption scheme for Web Push ๐ It keeps messages private and tamper-proof on their way from an application server to a user agent. It's giving confidentiality and integrity, no cap ๐ซถ
Status of This Memo ๐ฆ
Okay so this is an Internet Standards Track document. ๐
It's a product of the IETF, which means it reflects the consensus of the IETF community. It went through public review and got approved for publication by the Internet Engineering Steering Group (IESG). If you want more info on Internet Standards, check out Section 2 of RFC 7841. ๐ง
For the current status of this document, any errata, and how to send feedback, head to https://www.rfc-editor.org/info/rfc8291. ๐
Copyright Notice ๐
Copyright (c) 2017 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they were on the publication date. Please actually read these, bestie, because they explain your rights and restrictions for this doc ๐ Any Code Components pulled out of this document have to include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as described in the Simplified BSD License. โญ
Table of Contents ๐
1. Introduction ....................................................2
1.1. Notational Conventions .....................................3
2. Push Message Encryption Overview ................................3
2.1. Key and Secret Distribution ................................4
3. Push Message Encryption .........................................4
3.1. Diffie-Hellman Key Agreement ...............................5
3.2. Push Message Authentication ................................5
3.3. Combining Shared and Authentication Secrets ................5
3.4. Encryption Summary .........................................6
4. Restrictions on Use of "aes128gcm" Content Coding ...............7
5. Push Message Encryption Example .................................8
6. IANA Considerations .............................................8
7. Security Considerations .........................................8
8. References .....................................................10
8.1. Normative References ......................................10
8.2. Informative References ....................................11
Appendix A. Intermediate Values for Encryption ...................12
Author's Address ..................................................13
1. Introduction ๐ง
Okay so the Web Push protocol [RFC8030] is intermediated by necessity ๐ Messages from an application server get delivered to a user agent (UA) through a push service, as shown in Figure 1. It's a whole relay situation ๐ฆ
+-------+ +--------------+ +-------------+
| UA | | Push Service | | Application |
+-------+ +--------------+ +-------------+
| | |
| Setup | |
|<====================>| |
| Provide Subscription |
|-------------------------------------------->|
| | |
: : :
| | Push Message |
| Push Message |<---------------------|
|<---------------------| |
| | |
Figure 1
This doc explains how messages sent with this protocol can be kept safe from being inspected, modified, and forged by a push service ๐ฅ
Web Push messages are the payload of an HTTP message [RFC7230]. They get encrypted with an encrypted content encoding [RFC8188]. This doc covers how that content encoding is applied, plus a recommended key management scheme ๐ซถ
Lowkey, lots of Web Push users on the same user agent share one central agent that bundles up push functionality. That agent can enforce this encryption scheme on apps that use push messaging โจ An agent that only delivers properly encrypted messages is strongly encouraging end-to-end protection, and that's iconic ๐
A web browser that implements the Push API [API] can enforce encryption too, by only forwarding messages that were properly encrypted ๐
1.1. Notational Conventions ๐
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. That's the rule, bestie, the caps are the whole point ๐
This doc uses the terminology from [RFC8030], mainly "user agent", "push service", and "application server" ๐
2. Push Message Encryption Overview ๐
Encrypting a push message uses Elliptic Curve Diffie-Hellman (ECDH) [ECDH] on the P-256 curve [FIPS186]. That gets you a shared secret (see Section 3.1) and a symmetric secret for authentication (see Section 3.2) ๐ฆ
A user agent makes an ECDH key pair and an authentication secret for each subscription it creates. It sends the ECDH public key and the authentication secret to the application server along with the other details of the push subscription ๐
When sending a message, an application server makes an ECDH key pair and a random salt. The ECDH public key goes into the "keyid" parameter of the encrypted content coding header, and the salt goes into the "salt" parameter of that same header (see Section 2.1 of [RFC8188]). The ECDH key pair can be tossed once the message is encrypted ๐ง
The message content gets encrypted or decrypted with a content encryption key and nonce. These values are derived by taking the "keyid" and "salt" as input to the process described in Section 3 โจ
2.1. Key and Secret Distribution ๐ซถ
The application using the subscription hands the subscription public key and authentication secret to an authorized application server. This can ride along with other subscription info from the user agent, like the push subscription URI ๐
An application MUST use an authenticated, confidentiality-protected communications medium for this. Not optional, bestie. On top of the reasons in [RFC8030], this keeps the authentication secret away from unauthorized entities. If they got it, they could generate push messages that the user agent would accept ๐ญ
Most apps that use push messaging already have a relationship with an application server that can handle subscription data distribution ๐ธ An authenticated mechanism with enough confidentiality and integrity protection, like HTTPS [RFC2818], is good enough ๐
3. Push Message Encryption ๐
Push message encryption happens in four phases:
- A shared secret is derived using ECDH [ECDH] (see Section 3.1 of this document).
- The shared secret is then combined with the authentication secret to produce the input keying material (IKM) used in [RFC8188] (see Section 3.3 of this document).
- A content encryption key and nonce are derived using the process in [RFC8188].
- Encryption or decryption follows according to [RFC8188].
The key derivation process is summarized in Section 3.4. Restrictions on the use of the encrypted content coding are described in Section 4 ๐ฅ
3.1. Diffie-Hellman Key Agreement ๐
For each new subscription the user agent makes for an app, it also makes a P-256 [FIPS186] key pair for ECDH [ECDH] ๐ฆ
When sending a push message, the application server also makes a new ECDH key pair on that same P-256 curve ๐
The application server's ECDH public key is included as the "keyid" parameter in the encrypted content coding header (see Section 2.1 of [RFC8188]) ๐
The application server combines its ECDH private key with the public key from the user agent using the process in [ECDH]. When the push message arrives, the user agent combines its private key with the public key the application server sent in the "keyid" parameter, the same way. Both sides end up with the same ECDH shared secret, which is iconic ๐
3.2. Push Message Authentication ๐ง
To make sure push messages get authenticated correctly, a symmetric authentication secret gets added to the info the user agent generates. That authentication secret gets mixed into the key derivation process in Section 3.3 โจ
A user agent MUST generate and provide a hard-to-guess sequence of 16 octets for authenticating push messages. It SHOULD be generated by a cryptographically strong random number generator [RFC4086]. Translation: make it unguessable, bestie ๐
3.3. Combining Shared and Authentication Secrets ๐
The shared secret from ECDH gets combined with the authentication secret using the HMAC-based key derivation function (HKDF) [RFC5869]. The result is the input keying material used by [RFC8188] ๐ซถ
The HKDF function uses the SHA-256 hash algorithm [FIPS180-4] with these inputs:
- salt: the authentication secret
- IKM: the shared secret derived using ECDH
- info: the concatenation of the ASCII-encoded string "WebPush: info" (this string is not NUL-terminated), a zero octet, the user agent ECDH public key, and the application server ECDH public key, (both ECDH public keys are in the uncompressed point form defined in [X9.62]. That is:
key_info = "WebPush: info" || 0x00 || ua_public || as_public
- L: 32 octets (i.e., the output is the length of the underlying SHA-256 HMAC function output)
3.4. Encryption Summary โจ
okay so this is where it all comes together, bestie ๐ we end up with one final content encryption key and nonce. Here's the whole sequence in pseudocode, with HKDF split into separate steps and run through HMAC with SHA-256 ๐
-- For a user agent:
ecdh_secret = ECDH(ua_private, as_public)
auth_secret = random(16)
salt = <from content coding header>
-- For an application server:
ecdh_secret = ECDH(as_private, ua_public)
auth_secret = <from user agent>
salt = random(16)
-- For both:
## Use HKDF to combine the ECDH and authentication secrets
# HKDF-Extract(salt=auth_secret, IKM=ecdh_secret)
PRK_key = HMAC-SHA-256(auth_secret, ecdh_secret)
# HKDF-Expand(PRK_key, key_info, L_key=32)
key_info = "WebPush: info" || 0x00 || ua_public || as_public
IKM = HMAC-SHA-256(PRK_key, key_info || 0x01)
## HKDF calculations from RFC 8188
# HKDF-Extract(salt, IKM)
PRK = HMAC-SHA-256(salt, IKM)
# HKDF-Expand(PRK, cek_info, L_cek=16)
cek_info = "Content-Encoding: aes128gcm" || 0x00
CEK = HMAC-SHA-256(PRK, cek_info || 0x01)[0..15]
# HKDF-Expand(PRK, nonce_info, L_nonce=12)
nonce_info = "Content-Encoding: nonce" || 0x00
NONCE = HMAC-SHA-256(PRK, nonce_info || 0x01)[0..11]
Quick heads-up: this skips the exclusive-OR of the final nonce with the record sequence number. Why? Push messages only ever have a single record (see Section 4), and the sequence number of that first record is zero, so there's nothing to XOR ๐ซถ
4. Restrictions on Use of "aes128gcm" Content Coding ๐
okay this section is basically the rules of the group chat, and they're strict ๐ค
An application server MUST encrypt a push message with a single record. Why? It lets the receiver get away with a super minimal implementation that only has to handle one record ๐ฆ The application server MUST also set the "rs" parameter in the "aes128gcm" content coding header to a size that is greater than the sum of the lengths of the plaintext, the padding delimiter (1 octet), any padding, and the authentication tag (16 octets). No cutting corners on that math, periodt ๐
A push message MUST include the application server ECDH public key in the "keyid" parameter of the encrypted content coding header. The uncompressed point form defined in [X9.62] (that is, a 65-octet sequence that starts with a 0x04 octet) forms the entirety of the "keyid". Lowkey unexpected, but this means the "keyid" parameter will not be valid UTF-8 as recommended in [RFC8188] ๐ธ
A push service is not required to support more than 4096 octets of payload body (see Section 7.2 of [RFC8030]). Once you take away the header (86 octets), the padding (minimum 1 octet), and the expansion for AEAD_AES_128_GCM (16 octets), you're left with, at most, 3993 octets of plaintext ๐งฎ
An application server MUST NOT use other content encodings for push messages. Compression is the big no-no here, because content encodings that compress could result in leaking of push message contents ๐ So the Content-Encoding header field has exactly one value, which is "aes128gcm". Multiple "aes128gcm" values are not permitted. Not even a little bit ๐
A user agent is not required to support multiple records. A user agent MAY ignore the "rs" parameter. If a record size is unchecked, decryption will fail with high probability for all valid cases. The padding delimiter octet MUST be checked; values other than 0x02 MUST cause the message to be discarded. Basically, if the delimiter isn't 0x02, it's getting tossed ๐๏ธ
Real talk: the padding delimiter check is non-negotiable. Skip it and your receiver is letting in stuff it shouldn't ๐ญ
5. Push Message Encryption Example ๐ง
okay, time for the fun part, a real example! Here's a push message getting sent to a push service ๐
POST /push/JzLQ3raZJfFBR0aqvOMsLrt54w4rJUsV HTTP/1.1
Host: push.example.net
TTL: 10
Content-Length: 145
Content-Encoding: aes128gcm
DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27ml
mlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPT
pK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN
The ASCII-encoded string in there is "When I grow up, I want to be a watermelon" ๐ The content body is shown with line wrapping and URL-safe base64url [RFC4648] encoding so it fits on the page ๐
Here are the keys, using the uncompressed form [X9.62] encoded with base64url โจ
Authentication Secret: BTBZMqHH6r4Tts7J_aSIgg
Receiver:
private key: q1dXpw3UpT5VOmu_cf_v6ih07Aems3njxI-JWgLcM94
public key: BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcx
aOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4
Sender:
private key: yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw
public key: BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIg
Dll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8
If you want the intermediate values, they're all in Appendix A ๐
6. IANA Considerations ๐ท
This document does not require any IANA actions. Nothing to register, no forms to file, we're literally chilling ๐
7. Security Considerations ๐
okay this is the serious bit, so pay attention bestie ๐ Everything here matters, no skipping ๐ญ
The privacy and security considerations of [RFC8030] all apply to the use of this mechanism. So all of that stuff applies here too ๐ซก
The Security Considerations section of [RFC8188] describes the limitations of the content encoding. The big one: no HTTP header fields are protected by the content encoding scheme. So a user agent MUST consider HTTP header fields to have come from the push service. Trust the header fields as coming from the push service, not from the app server ๐
Though header fields might be necessary for processing an HTTP response correctly, they are not needed for correct operation of the protocol. An application on the user agent that uses information from header fields to alter their processing of a push message is exposed to a risk of attack by the push service. So be careful if your app is doing that, it's a whole vulnerability ๐
The timing and length of communication cannot be hidden from the push service. An outside observer might see individual messages intermixed with each other, but the push service will see which application server is talking to which user agent and the subscription that is used. Also, the length of messages could be revealed unless the padding provided by the content encoding scheme is used to obscure length. So use that padding ๐
Public key validation is a MUST-level vibe check, and it's a big deal. Failing to validate a public key can allow an attacker to extract a private key. Yikes ๐ฑ
The user agent and application MUST verify that the public key they receive is on the P-256 curve. The appropriate validation procedures are defined in Section 4.3.7 of [X9.62] and, alternatively, in Section 5.6.2.3 of [KEYAGREEMENT]. This process consists of three steps:
- Verify that Y is not the point at infinity (O),
- Verify that for Y = (x, y), both integers are in the correct interval,
- Ensure that (x, y) is a correct solution to the elliptic curve equation.
For these curves, implementers do not need to verify membership in the correct subgroup. Less work for you, bestie ๐ฆ
If this encryption scheme ever needs replacing, a new content coding scheme could be defined. To manage progressive deployment of the new scheme, the user agent can expose information on the content coding schemes that it supports. The supportedContentEncodings parameter of the Push API [API] is an example of how this might be done ๐
8. References ๐
8.1. Normative References
- [ECDH] SECG, "SEC 1: Elliptic Curve Cryptography", Version 2.0, May 2009,
<http://www.secg.org/>. - [FIPS180-4] National Institute of Standards and Technology (NIST), "Secure Hash Standard (SHS)", FIPS PUB 180-4, DOI 10.6028/NIST.FIPS.180-4, August 2015.
- [FIPS186] National Institute of Standards and Technology (NIST), "Digital Signature Standard (DSS)", FIPS PUB 186-4, DOI 10.6028/NIST.FIPS.186-4, July 2013.
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>. - [RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005,
<https://www.rfc-editor.org/info/rfc4086>. - [RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)", RFC 5869, DOI 10.17487/RFC5869, May 2010,
<https://www.rfc-editor.org/info/rfc5869>. - [RFC8030] Thomson, M., Damaggio, E., and B. Raymor, Ed., "Generic Event Delivery Using HTTP Push", RFC 8030, DOI 10.17487/RFC8030, December 2016,
<https://www.rfc-editor.org/info/rfc8030>. - [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017,
<https://www.rfc-editor.org/info/rfc8174>. - [RFC8188] Thomson, M., "Encrypted Content-Encoding for HTTP", RFC 8188, DOI 10.17487/RFC8188, June 2017,
<https://www.rfc-editor.org/info/rfc8188>. - [X9.62] ANSI, "Public Key Cryptography for the Financial Services Industry: the Elliptic Curve Digital Signature Algorithm (ECDSA)", ANSI X9.62, 2005.
8.2. Informative References
- [API] Beverloo, P., Thomson, M., van Ouwerkerk, M., Sullivan, B., and E. Fullea, "Push API", October 2017,
<https://www.w3.org/TR/push-api/>. - [KEYAGREEMENT] Barker, E., Chen, L., Roginsky, A., and M. Smid, "Recommendation for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography", NIST Special Publication 800-56A, Revision 2, DOI 10.6028/NIST.SP.800-56Ar2, May 2013.
- [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, DOI 10.17487/RFC2818, May 2000,
<https://www.rfc-editor.org/info/rfc2818>. - [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006,
<https://www.rfc-editor.org/info/rfc4648>. - [RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing", RFC 7230, DOI 10.17487/RFC7230, June 2014,
<https://www.rfc-editor.org/info/rfc7230>.
Appendix A. Intermediate Values for Encryption โจ
okay bestie, this is the behind-the-scenes closet ๐ง every intermediate value from the Section 5 example, all laid out so you can check the math yourself. the base64url values have whitespace in them that you can totally remove ๐
First, the inputs to the calculation:
Plaintext: V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0byBiZSBhIHdhdGVybWVsb24
Application server public key (as_public):
BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIg
Dll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8
Application server private key (as_private):
yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw
User agent public key (ua_public): BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-
JvLexhqUzORcx aOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4
User agent private key (ua_private):
q1dXpw3UpT5VOmu_cf_v6ih07Aems3njxI-JWgLcM94
Salt: DGv6ra1nlYgDCS1FRnbzlw
Authentication secret (auth_secret): BTBZMqHH6r4Tts7J_aSIgg
Note that knowledge of just one of the private keys is necessary. ๐ The application server randomly generates the salt value, whereas salt is input to the receiver. ๐ฅบ
Now for the intermediate values this produces:
Shared ECDH secret (ecdh_secret):
kyrL1jIIOHEzg3sM2ZWRHDRB62YACZhhSlknJ672kSs
Pseudorandom key (PRK) for key combining (PRK_key):
Snr3JMxaHVDXHWJn5wdC52WjpCtd2EIEGBykDcZW32k
Info for key combining (key_info): V2ViUHVzaDogaW5mbwAEJXGyvs3942BVG
q8e0PTNNmwR zr5VX4m8t7GGpTM5FzFo7OLr4BhZe9MEebhuPI-OztV3
ylkYfpJGmQ22ggCLDgT-M_SrDepxkU21WCP3O1SUj0Ew
bZIHMtu5pZpTKGSCIA5Zent7wmC6HCJ5mFgJkuk5cwAv MBKiiujwa7t45ewP
Input keying material for content encryption key derivation (IKM):
S4lYMb_L0FxCeq0WhDx813KgSYqU26kOyzWUdsXYyrg
PRK for content encryption (PRK):
09_eUZGrsvxChDCGRCdkLiDXrReGOEVeSCdCcPBSJSc
Info for content encryption key derivation (cek_info):
Q29udGVudC1FbmNvZGluZzogYWVzMTI4Z2NtAA
Content encryption key (CEK): oIhVW04MRdy2XN9CiKLxTg
Info for content encryption nonce derivation (nonce_info):
Q29udGVudC1FbmNvZGluZzogbm9uY2UA
Nonce (NONCE): 4h_95klXJ5E_qnoN
The salt, record size of 4096, and application server public key produce an 86-octet header of: ๐
DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z 9KsN6nGRTbVYI_c7VJSPQTBtkgcy27ml
mlMoZIIgDll6e3vCYLocInmYWAmS6Tlz AC8wEqKK6PBru3jl7A8
Then the push message plaintext gets the padding delimiter octet (0x02) appended, so it becomes: ๐
V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0 byBiZSBhIHdhdGVybWVsb24C
The plaintext is then encrypted with AES-GCM, which emits this ciphertext: ๐
8pfeW0KbunFT06SuDKoJH9Ql87S1QUrd irN6GcG7sFz1y1sqLgVi1VhjVkHsUoEs
bI_0LpXMuGvnzQ
And finally, the header and ciphertext get concatenated and produce the result shown in Section 5. That's the whole glow-up, periodt! ๐ฆโจ
Author's Address ๐ซถ
Martin Thomson Mozilla
Email: martin.thomson@gmail.com
๐ฆ where this page comes from
- ietf: rfc8291 2017-11
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms