blob: eff372aeea5d0b2a323a9649462f38bfa95245b3 [file] [log] [blame] [view]
Alex Turner26a95e12022-03-24 17:04:001# Aggregation service payload encryption details
2
3Here, we briefly describe the precise details of payload encryption for
4aggregatable reports used in the aggregation service. The format of the payload
5itself is provided in the
Andrew Paseltinera5e25ee2022-06-02 16:13:266[explainer](https://github.com/WICG/attribution-reporting-api/blob/3d0a541c708391d73905afafa155d6753c8565af/AGGREGATE.md#encrypted-payload)[^1],
Alex Turner26a95e12022-03-24 17:04:007but some of these details arent. Note that these details are subject to change,
8but reflect the current API offered by `aggregation_service/`.
9
10## Implementation links
11
12The generic function used to encrypt data, including specifying various
13parameter choices (e.g. AEAD function), is `EncryptWithHpke()` and is defined in
14[this file](./aggregatable_report.cc). The inputs to that function are provided
15in the `CreateFromRequestAndPublicKeys()` function in the same file.
16
17## Inputs
18
19### Plaintext
20
21The unencrypted payload is first generated as a [CBOR](https://cbor.io/) map
22with the format described in the
Alex Turnere9dfb8b2024-07-08 19:55:2123[spec](https://patcg-individual-drafts.github.io/private-aggregation-api/pr-preview/refs/pull/128/merge/index.html#obtain-the-plaintext-payload)[^1].
Alex Turner26a95e12022-03-24 17:04:0024This map is serialized to binary and used as the plaintext input.
25
26### Associated data
27
28The associated data is a string encoded as UTF-8. It consists of a prefix and a
29variable body. The prefix is a constant and is used for domain separation[^2];
Alex Turner57a65e032022-06-08 20:57:3730its value is "`aggregation_service`". (In an earlier version, this prefix also
31included a null character at the end.) The body is exactly the value of the
32`shared_info` string provided in the report plaintext. This is generated by the
33browser and encodes information both needed by the aggregation service and
34available for use by the reporting origin.
Alex Turner26a95e12022-03-24 17:04:0035
Charlie Harrisonef5308d2022-06-07 15:41:4236An example shared\_info field for use in the Attribution Reporting API is:
Alex Turner26a95e12022-03-24 17:04:0037
38```jsonc
Charlie Harrisonef5308d2022-06-07 15:41:4239"shared_info": "{\"attribution_destination\":\"https://advertiser.example\",\"report_id\":\"[UUID]\",\"reporting_origin\":\"https://reporter.example\",\"scheduled_report_time\":\"[timestamp in seconds]\",\"source_registration_time\":\"[timestamp in seconds]\",\"version\":\"[api version]\"}"
Alex Turner26a95e12022-03-24 17:04:0040```
41
Alex Turner57a65e032022-06-08 20:57:3742The corresponding associated data would then be the following (encoded as
43UTF-8):
Alex Turner26a95e12022-03-24 17:04:0044
45```jsonc
Alex Turner57a65e032022-06-08 20:57:3746aggregation_service{"attribution_destination":"https://advertiser.example","report_id":"[UUID]","reporting_origin":"https://reporter.example","scheduled_report_time":"[timestamp in seconds]","source_registration_time":"[timestamp in seconds]","version":"[api version]"}"
Alex Turner26a95e12022-03-24 17:04:0047```
48
49Note that, for the decryption to succeed, the associated data used must be
50byte-for-byte identical to what was used for encryption.
51
52### Public key
53
54The public key is a 32-byte (256-bit) bytestring. The browser downloads public
55keys from the aggregation service according to the process described in the
Andrew Paseltinera5e25ee2022-06-02 16:13:2656[explainer](https://github.com/WICG/attribution-reporting-api/blob/3d0a541c708391d73905afafa155d6753c8565af/AGGREGATE.md#encrypted-payload)[^1]
Alex Turner26a95e12022-03-24 17:04:0057and picks one uniformly at random (if multiple are specified). Note that this
58key must be base64 decoded by the client. The browser provides the matching `id`
59of the key used to encrypt the payload as `key_id`.
60
61## Encryption process and parameters
62
63The encryption context is first set up (using `EVP_HPKE_CTX_setup_sender()`)
64with the following algorithms used for each encryption primitive:
65
66* Key encapsulation mechanism (KEM): DHKEM(X25519, HKDF-SHA256) (i.e.
67 `EVP_hpke_x25519_hkdf_sha256()`)
68* Key derivation function (KDF): HKDF-SHA256 (i.e. `EVP_hpke_hkdf_sha256()`)
69* Authenticated encryption with additional data (AEAD) encryption function:
70 ChaCha20Poly1305 (i.e. `EVP_hpke_chacha20_poly1305()`)
71
72The public key is provided in this call. The associated data is also provided
73while setting up this encryption context, i.e. as the `info` and `info_len`
74parameters.[^3] Setting up the encryption context generates an encapsulated
75shared secret, which should have length 32 bytes.
76
77Then, the plaintext is symmetrically encrypted as a single message with this
78context (using `EVP_HPKE_CTX_seal()`[^4]). No associated data is provided here,
79i.e. `ad_len` is 0.
80
81The ciphertext, i.e. encrypted payload, returned is a single bytestring
82consisting of the encapsulated shared string concatenated with the symmetrically
83encrypted message. This bytestring is then base64 encoded before inclusion in
84the report.
85
86## Notes
87
Alex Turnere9dfb8b2024-07-08 19:55:2188[^1]: Note that these links point to a specific commit of the spec that reflects
89 what is currently implemented as of the latest update to this file. The
90 [up-to-date spec](https://patcg-individual-drafts.github.io/private-aggregation-api/#obtain-the-plaintext-payload)
Alex Turner26a95e12022-03-24 17:04:0091 may have recent changes that have not yet been implemented.
92
93[^2]: This ensures that ciphertexts for one API cannot be accepted for a
94 different API, even if an encryption key is reused.
95
96[^3]: To decrypt, this data should therefore be provided in
97 `EVP_HPKE_CTX_setup_receipient()`.
98
99[^4]: The equivalent decryption call is `EVP_HPKE_CTX_open()`. As during
100 encryption, no associated data should be provided.