Score:1

Age: stream cipher with public key cryptography?

cc flag

I have some rudimentary cryptography knowledge but am by no means an expert.

I generally understand stream ciphers, such as such as ChaCha20-Poly1305, to be symmetric. I am wondering how age (https://github.com/FiloSottile/age) uses public and private keys to encrypt data with ChaCha20-Poly1305. Is it similar to how in some protocols asymmetric encryption is used to establish a shared symmetric key, or is age’s protocol entirely different? If so, what is the asymmetric algorithm used to share the symmetric key?

If there’s a broader concept that I’m missing here, I would love any recommendations for introductory further reading.

knaccc avatar
es flag
It's X25519 key exchange between an ephemeral key pair and the recipient's public key. Yes, it's a standard approach that is used widely elsewhere
Maarten Bodewes avatar
in flag
It seems that `scrypt` is also an option as "recipient type". For the protocol for X25519 it seems that the *identity* is the party that decrypts using the static private key (also called `identity`, nice), and `recipient` consists of the static public key. The ephemeral keys are called `ephemeral secret` which is what we normally call a private key and the public key is called `ephemeral share` (ugh), which are used to calculate an `shared secret`. I'm not sure who was naming things here, but it is **very** confusing. Must be the "stanza" thing or something, poetic :|
Score:2
bs flag

The specification for age is here. As explained in the X25519 recipient stanza section, an ephemeral key pair is generated, and the ephemeral private key is used to perform an X25519 key exchange with the recipient public key. This computes a shared secret that's used to derive a key using HKDF-SHA-256. Then the ephemeral public key is included in the encrypted file to allow the recipient to compute the same shared secret using their private key, enabling decryption.

ephemeral secret = read(CSPRNG, 32)
ephemeral share = X25519(ephemeral secret, basepoint)

salt = ephemeral share || recipient
info = "age-encryption.org/v1/X25519"
shared secret = X25519(ephemeral secret, recipient)
wrap key = HKDF-SHA-256(ikm = shared secret, salt, info)
body = ChaCha20-Poly1305(key = wrap key, plaintext = file key)

The downside of this approach is that it provides no sender authentication. It's like libsodium's sealed box in that the sender's identity is anonymous. In the context of age, this can be considered a design flaw because if you receive a file from someone, you want to know that it came from them and was not replaced by an attacker.

The best further reading on this topic is a series of blog posts by Neil Madden on public key authenticated encryption. Part 2, which I've linked above, explains what age is doing and better alternatives.

Additionally, I would strongly recommend Real-World Cryptography by David Wong, which is by far the best introductory book I've come across and has some facts/stories even more experienced people may not know. It covers Curve25519/X25519 and hybrid encryption with ECIES like age is doing.

Another important thing to read is the Curve25519/X25519 RFC, particularly the Security Considerations section, which has an important detail that's often overlooked. There's the original paper. Finally, there are various blog posts like this and this.

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.