Public Key: Curve25519
I use Dan Bernstein's Curve25519 from the NaCl library, because it's a very fast and proven secure way to establish a shared secret. This page explains how it works, and what the caveats are. I try to make it easy to understand; for anybody with deeper knowledge, Dan Bernstein explains it good enough, but as there is a lot of math behind it, this explanation is not easy to understand.
The essence is that elliptic curve cryptography (ECC) defines a multiplication operation in a mod(n) group, where n here is 2^255-19, which is a prime number. The public key is obtained from a secret key by multiplication of 9, so pk=sk*9. The good news here is that division in this group is very hard, so sk=pk/9 doesn't work, so the secret key remains secret.
The property for establishing a shared secret we use is that when we have two public/secret key pairs, sk1*pk2 = sk2*pk1. I.e. we make sure we know each other's public keys, and as we know our secret keys, we can establish a shared secret between the two parties which nobody else can know, as long as the secret keys really are secret.
Since the client sends its public key to the server, this can be a short-term public key, thus there will not be that many packets using the same key. But we use a stream cipher, so we really must make sure that each packet has a nonce, a unique stamp. The nonce is not only used to avoid known plain text attacks, but also to verify acknowledges.
I use wurstkessel for the symmetric part of the encryption, for three reasons:
- It's invented here ;-)
- It's fast
- As end state of the encryption, it contains a secure hash of the encrypted text
I use this secure hash as checksum for the message, to validate it. Only those who know the shared secret can produce this hash, and only when the message's integrity is perfect, it will pass the test.
For signatures, a different key pair is needed, since a different variant of the curve is necessary. Signatures don't work with a shared secret, they have to be encrypted with the secret key, and decrypted with the public key.
How to send a one-to-many packet with Curve25519? Encrypted email with RSA works like this: You encrypt your document key with each of the public keys of the receivers. This doesn't work with Curve25519, you have a shared secret with each of the receiving parties. However, you can use this: You encrypt the document key using this shared secret of each of your receivers. I.e. the key block starts with your public key, and then the encrypted secret keys follow. These encrypted secrets should contain a checksum or similar to validate the decrypted key for correctness. This allows the receiver to deny that he actually got that message - only by knowing the secret key you can find the document key. The check is quick, since we use the symmetric algorithm to decrypt - validation is, as usual, a cut-down hash of the encrypted block.