Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple encrypted channels for a single event #386

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

paragonie-security
Copy link

@paragonie-security paragonie-security commented Apr 23, 2024

Note: This assumes all channels have the same master secret.

If you're interested in an E2EE design that doesn't have this requirement, crypto_box_seal only requires the recipient's public key.


To encrypt a message against multiple channels, two things are needed:

  1. A method of encoding which channels are included in the key derivation that's order-independent.
  2. A method of deriving a shared secret for multiple channels.

This implements both and integrates it into the pusher workflow.

A message encrypted to multiple recipients encodes the channel metadata into an encrypted header.

For example:

private-encrypted-multi-00000000000000207b2263223a5b22626172222c22626c61222c22666f6f225d2c2272223a22227d:{"nonce":"MyNLvxXvz3nJ\/lMv+oRtK991TXW\/00Kx","ciphertext":"+nkcgt+SUDY42NqKCsxB5DgfGz1uhbNgx\/DJkHeL4ffH0JAkbUGgSpd+AzXGx9C6uQ=="}

This hex string is encoded as strlen(data) + data. It encodes two pieces of information:

  1. A list of suffixes for the encrypted channel names.
  2. Optional additional randomness for key derivation.

The suffixes can be used at runtime to re-assemble the channel names.

Given a list of channel names, one can additionally derive a shared secret for the message that all channels should be able to decrypt.

The algorithm it uses is HMAC-SHA256 with an input that consists of lengths of segments followed by the segments' raw data. This design was inspired by PAE from PASETO and TupleHash from the NIST standard.

https://www.nist.gov/publications/sha-3-derived-functions-cshake-kmac-tuplehash-and-parallelhash

Each segment in calculating the shared secret is that channel's shared secret.

The first commit does not update the documentation or guidance around limitations. A congruent change would need to land in every other Pusher implementation before it could be updated.

Description

Fixes #383

To encrypt a message against multiple channels, two things are needed:

1. A method of encoding _which_ channels are included in the key derivation that's order-independent.
2. A method of deriving a shared secret for multiple channels.

This implements both and integrates it into the pusher workflow.

A message encrypted to multiple recipients encodes the channel metadata into an encrypted header.

For example:

```
private-encrypted-multi-00000000000000207b2263223a5b22626172222c22626c61222c22666f6f225d2c2272223a22227d:{"nonce":"MyNLvxXvz3nJ\/lMv+oRtK991TXW\/00Kx","ciphertext":"+nkcgt+SUDY42NqKCsxB5DgfGz1uhbNgx\/DJkHeL4ffH0JAkbUGgSpd+AzXGx9C6uQ=="}
```

This hex string is encoded as strlen(data) + data. It encodes two pieces of information:

1. A list of suffixes for the encrypted channel names.
2. Optional additional randomness for key derivation.

The suffixes can be used at runtime to re-assemble the channel names.

Given a list of channel names, one can additionally derive a shared secret for the message that all channels should be able to decrypt.

The algorithm it uses is HMAC-SHA256 with an input that consists of lengths of segments followed by the segments' raw data. This design was inspired by PAE from PASETO and TupleHash from the NIST standard.

https://www.nist.gov/publications/sha-3-derived-functions-cshake-kmac-tuplehash-and-parallelhash

 Each segment in calculating the shared secret is that channel's shared secret.

 This commit does not update the documentation or guidance around limitations. A congruent change would need to land in every other Pusher implementation before it could be updated.
$pos = strlen(self::ENCRYPTED_PREFIX);
foreach ($sorted as $ch) {
if (!self::is_encrypted_channel($ch)) {
continue;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to throw here instead. The intent for this method is to only allow encrypted channels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Triggering single event on multiple encrypted channel fails
1 participant