Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea @raucao @lain @sozialwelten @koteisaev Thank you for the advice. I need to start with something simple in order to learn how cryptography works. Then it will be replaced with a more secure scheme.

From what I learned so far, the core principle is pretty much the same in all modern encryption schemes, they differ in how shared symmetric key is generated. Is that correct?

Replying to @⁨silverpill@mitra.social⁩

@silverpill @raucao @lain @sozialwelten @koteisaev

Sorry for the delayed response, but yes.

The naive approach would be:

- Convert both party's Ed25519 keys to X25519 keys
- ECDH(Sender static, Recipient static) -> k
- HKDF(k) -> Shared secret

The problem with this approach is that, if either side's private key gets compromised, all historical traffic is trivially decryptable. (Static-Static ECDH)

Perfect forward secrecy (compromise of either key does not give attacker the ability to read past messages) requires at least one extra round trip, which is not great in this setting.

The tweak I suggested is:

- Convert both party's Ed25519 keys to X25519 keys
- Sender generates a ephemeral (throw away) X25519 key
- ECDH(Sender static, Recipient static) -> k_1
- ECDH(Sender ephemeral, Recipient static) -> k_2
- HKDF(k_1 | k_2) -> Shared secret
- Sender discards the ephemeral key

With this improvement, sender long term key compromise does not allow decryption of past traffic, because the ephemeral keypair is long gone into the void (one-way forward secrecy, Static-Static + Ephemeral-Static ECDH).

Note: While the theoretical safety of reusing a Ed25519 key for also doing X25519 was an open question, there are proofs that it is safe.

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea Hi, I tried to implement the mechanism described in the FEP and decided to use HPKE (RFC-9180). What do you think of it?

HPKE seems to address the issue you pointed out back then: https://www.rfc-editor.org/rfc/rfc9180.html#name-forward-secrecy.

At least if used properly. The library I am using hard-codes "info" and "aad" strings: https://github.com/rustonbsd/ed25519-dalek-hpke/blob/27b010a9ae25ba4ce051510eadfd103a179dd715/src/lib.rs#L98-L104

Anyway, here's an updated FEP: https://codeberg.org/silverpill/feps/src/branch/main/0806/fep-0806.md

@raucao @lain @sozialwelten @koteisaev

www.rfc-editor.orgRFC 9180: Hybrid Public Key Encryption This document describes a scheme for hybrid public key encryption (HPKE). This scheme provides a variant of public key encryption of arbitrary-sized plaintexts for a recipient public key. It also includes three authenticated variants, including one that authenticates possession of a pre-shared key and two optional ones that authenticate possession of a key encapsulation mechanism (KEM) private key. HPKE works for any combination of an asymmetric KEM, key derivation function (KDF), and authenticated encryption with additional data (AEAD) encryption function. Some authenticated variants may not be supported by all KEMs. We provide instantiations of the scheme using widely used and efficient primitives, such as Elliptic Curve Diffie-Hellman (ECDH) key agreement, HMAC-based key derivation function (HKDF), and SHA2. This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.

Replying to @⁨silverpill@mitra.social⁩

@silverpill @raucao @lain @sozialwelten @koteisaev

HPKE has the benefit of being standardized, and imperfect forward secrecy is the best you can do with 0-RTT (which probably fits the fediverse model better).

Just be very careful about the "use correctly" and it's "fine". I'm partial to Noise (https://noiseprotocol.org/) for these sort of use cases because it has less foot+guns and there's more flexibility, but I wouldn't freak out or scream if I saw HPKE in use.

As a side note, it is indeed safe to convert ed25519 keys to X25519 keys assuming a common sense step is taken when using the latter (https://eprint.iacr.org/2021/509).

noiseprotocol.orgNoise Protocol Framework

Replying to @⁨silverpill@mitra.social⁩

@silverpill @raucao @lain @sozialwelten @koteisaev

WhatsApp.

It's biggest claim to fame is non-group messaging (WireGuard), and the fact that the designer based it on the Signal wire protocol design (same person). The latter research that adds concrete security proofs is an added bonus, as well as decent library availability.

But, there's only so many ways to do NIKE (Non-Interactive Key Exchange), and the IETF/IRTF has a preference for reusing their own standards.

TLDR: Noise over HPKE is a personal preference, I think it's harder to blow your foot off with Noise and it's easier to implement (having done multiple Noise impls, and dealt with HPKE more than I should), but honestly either is a reasonable choice, and if you want something more standardized, HPKE is reasonable.

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea I added HPKE algorithm parameters to the FEP:

https://codeberg.org/silverpill/feps/src/branch/main/0806/fep-0806.md#algorithms

KEM: DHKEM(X25519, HKDF-SHA256)
AEAD: ChaCha20Poly1305
KDF: HKDF-SHA256
"info" string: fep-0806-info
"aad" string: fep-0806-aad

Does it look right?

KEM/AEAD/KDF choices are not my preferences, I just copied them from another HPKE+ed25519 implementation.

Summary card of repository silverpill/feps, described as: My FEPsCodeberg.orgfeps/0806/fep-0806.md at mainfeps - My FEPs

Replying to @⁨silverpill@mitra.social⁩

@silverpill

The primitive choices are sensible.

The "aad" is sent in the clear (or just public as it is now) so you could do something like "fep-0806-aad" | ':' | <id> | ':' | <actor> |':' | <to>. If the proof covers all of that then it's not strictly needed, but it's cheap paranoia.

I was going to say add version info to the labels, but fep0806:cipherAlgorithm covers that.

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea

>If the proof covers all of that then it's not strictly needed, but it's cheap paranoia.

Do you mean id/actor/to of a plaintext activity, or of an envelope (EncryptedActivity)?

My understanding is that the properties of an envelope have no effect on security. It can even have a different actor.

I added an example of a plaintext activity: https://codeberg.org/silverpill/feps/src/branch/main/0806/fep-0806.md#plaintext-activity

Also added HPKE mode to the parameter list - mode_base.

Summary card of repository silverpill/feps, described as: My FEPsCodeberg.orgfeps/0806/fep-0806.md at mainfeps - My FEPs

Replying to @⁨silverpill@mitra.social⁩

@silverpill

Of an envelope. If actor being changed mid-transit isn't a problem, then omit it from the aad. Including the from/to as part of the AAD binds the ciphertext to the origin/recipient (the HPKE ciphertext does not carry any of this information).

It is worth noting that, as the FEP is specified now, using the sender and recipient's keypairs to do HPKE will result in the same shared secret being generated for every message. This is catastrophic to security as the nonce for the symmetric portion of the HPKE encryption is deterministic (RFC 9180 5.1).

This can be avoided by either: having the sender generate an ephemeral keypair (include the public key envelope), or using a unique HPKE info per envelope. The former is better as it gives imperfect forward secrecy (sender identity key compromise does not reveal past plaintexts), the latter is cheaper to do.

Replying to @⁨silverpill@mitra.social⁩

@silverpill

No, the recipient would also generates an ephemeral key, and uses that when replying.

So it's always a one shot ephemeral (sender)/static (recipient) HPKE exchange, with sender authenticity guaranteed by the signature. Could also use the shared secret from the original post but force a different IV, but "replies are just the sender and receiver being flipped, code path is the same" is easier.

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea I assume this is "Authentication Using an Asymmetric Key" (https://www.rfc-editor.org/rfc/rfc9180.html#name-authentication-using-an-asy)?

Because in "Encryption to a Public Key" mode, sender's key is not used.

If I understand the idea correctly, the ephemeral key would need to be added to fep0806:cipherData. That is, instead of encap_key | ciphertext it will be ephemeral_key_pub | encap_key | ciphertext.

www.rfc-editor.orgRFC 9180: Hybrid Public Key Encryption This document describes a scheme for hybrid public key encryption (HPKE). This scheme provides a variant of public key encryption of arbitrary-sized plaintexts for a recipient public key. It also includes three authenticated variants, including one that authenticates possession of a pre-shared key and two optional ones that authenticate possession of a key encapsulation mechanism (KEM) private key. HPKE works for any combination of an asymmetric KEM, key derivation function (KDF), and authenticated encryption with additional data (AEAD) encryption function. Some authenticated variants may not be supported by all KEMs. We provide instantiations of the scheme using widely used and efficient primitives, such as Elliptic Curve Diffie-Hellman (ECDH) key agreement, HMAC-based key derivation function (HKDF), and SHA2. This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea Yes, proof covers all properties of the activity. But I can't omit encap_key, it is required by SetupBaseR function defined in section 5.1.1

I've read a bit more about DHKEM and now I am even more confused. Isn't encap_key an ephemeral key? The library I am using describes it as such:

https://docs.rs/hpke/latest/hpke/trait.Kem.html#tymethod.encap_with_rng

Derives a shared secret and an ephemeral pubkey that the owner of the reciepint’s pubkey can use to derive the same shared secret.

This "ephemeral pubkey" is what I labeled as encap_key. It is later included in fep0806:cipherData

-----

I think it's okay to have no forward secrecy, the goal of this FEP is to describe a simple encryption mechanism that anyone can implement. I'll just warn readers about its weakness.

www.rfc-editor.orgRFC 9180: Hybrid Public Key Encryption This document describes a scheme for hybrid public key encryption (HPKE). This scheme provides a variant of public key encryption of arbitrary-sized plaintexts for a recipient public key. It also includes three authenticated variants, including one that authenticates possession of a pre-shared key and two optional ones that authenticate possession of a key encapsulation mechanism (KEM) private key. HPKE works for any combination of an asymmetric KEM, key derivation function (KDF), and authenticated encryption with additional data (AEAD) encryption function. Some authenticated variants may not be supported by all KEMs. We provide instantiations of the scheme using widely used and efficient primitives, such as Elliptic Curve Diffie-Hellman (ECDH) key agreement, HMAC-based key derivation function (HKDF), and SHA2. This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.
en

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@silverpill

Think 5.1.3 (AuthEncap) is better since it still uses an ephemeral key, but does an extra DH to authenticate the sender. 5.1.1 (Encap) is simpler, but unless the aad contains at least the sender's identity, there is nothing preventing someone from extracting a ciphertext, and re-signing it (forging the origin).

Having a trivial amount of AAD is computationally cheaper than doing the extra DH, but the extra DH would be sub 100 usec, so this is "pick something to taste".

ps: Spent the day chasing LLVM bugs, errors in this would be all mine.

Replying to @⁨silverpill@mitra.social⁩

@silverpill

Ah , mea culpa, you're right. 5.1.1 avoids the nonce reuse issue and provides imperfect forward secrecy. I've been fighting u-boot on "new" exotic target the last day so my thinking isn't as sharp as it should be.

Replies should go through the same process (avoids sender having to store the shared secret, less state is more better in this case).

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea Alright, I implemented the auth mode. It's not difficult, but it requires the actor of outer activity to be the same as the actor of inner (plaintext) activity. With base mode (5.1.1), the actor of outer activity could be different, which means we can hide the real sender.

Do you think it's worth doing this?

I think authentication of the sender at the HPKE level shouldn't be necessary because the inner activity is self-authenticating (includes integrity proof).

Replying to @⁨greyarea@mitra.vpclmulqdq.moe⁩

@greyarea

I can think of two scenarios where HPKE-level sender authentication or aad binding might be important:

1. The sender creates an activity that is attributed to somebody else, encrypts it, and sends to the recipient. This shouldn't be a problem, because the inner activity MUST be portable, and therefore required to have an integrity proof. A compliant recipient can't be fooled into thinking that misattributed activity is real.
2. Somebody (e.g. server operator) takes the fep0806:cipherText out of EncryptedActivity, and creates a new activity with the same fep0806:cipherText but different id, actor and/or to. This shouldn't be a problem either. In the worst case, the activity will be delivered to somebody else who will not be able to decrypt it. Replacing id and actor may be even a good thing (obfuscation).

it was a giant mess of w3c specs.

It's an ever-expanding mess of W3C specs, RFCs and FEPs.

Integrity proofs are relatively new, they are described in Data Integrity W3C spec: https://www.w3.org/TR/vc-data-integrity/

www.w3.orgVerifiable Credential Data Integrity 1.0This specification describes mechanisms for ensuring the authenticity and integrity of verifiable credentials and similar types of constrained digital documents using cryptography, especially through the use of digital signatures and related mathematical proofs.