ECIES (Elliptical Curve Integrated Encryption Scheme) provides hybrid encryption scheme used for communicating data between two parties in a secure manner using ephemeral keys.
# Encryption Steps
## 1. Generate an ephemeral keypair unique to the message
A new ephemeral [[ECDSA]] keypair is generated for the purpose of encryption (and decryption, if the recipient sends an encrypted reply). This key can be thrown away once the process is complete.
The generated ephemeral public key is part of the payload sent to the recipient for decryption.
## 2. Derive a shared AES encryption key
This process involves key agreement between both parties.
Using [[ECDH]], a shared key can be created using the sender's private key and the recipient's public key. This, along with the usage of ephemeral keys, means that no sensitive key information can be gleamed from the payload.
Next, we derive an AES encryption key from the shared key using [[HKDF]]. This involves taking the shared key and HKDF Information that further seeds the key with additional knowledge, possibly including:
* The purpose of the key (e.g., `service-name|role=chat-message`)
* The version of the payload/process information (e.g., `v=2`)
* The cipher suite used for the ECIES encryption/decryption (e.g., `alg=P-384+HKDF-SHA256-AES-GCM`)
* The ephemeral public key
* Fingerprint of the target key
> [!tip] Use length-prefixing for dynamic content
> Anything dynamic (like a generated cipher suite, fingerprint, etc.) should avoid collisions with other information. One way of doing this is to insert bytes that represent the length of the content being added.
>
> Also, this is all just raw bytes, so no need to take, say, a public key as bytes and turn it into human-readable hex first.
The shared key from ECDH and the resulting AES key + salt from HKDF can then be used as part of the next step.
## 3. Encrypt using AES-GCM
The sender can then encrypt the payload data using [[AES-GCM]], utilizing the new AES shared key.
> [!tip] Include a timestamp in the message payload
> To avoid a [[replay attack]], the recipient will want to compare the nonce and timestamp against a validity window and previously-seen messages.
>
> The timestamp can be stored in the inner payload, and optionally in the outer payload in plaintext (to avoid performing ECIES decryption on anything clearly too old). However, it'd want to then compare a potentially-valid timestamp to the one in the inner payload (or include it as part of AAD).
>
> The nonce/IV for the outer payload should also be remembered during the validity window and compared to avoid the replay within the window.
An important part of this process is creating the [[AAD]] (Additional Authenticated Data), which must be common between the encryptor and the decryptor.
The AAD is similar in a way to the HKDF Information, but has a different purpose. It both authenticates the data provided and helps use that data to authenticate the payload. For example, if there are two payloads involved (say, an inner payload and an outer envelope payload), you might tie the two together using the IV of the outer payload in the inner's AAD, and a hash of the encrypted inner payload in the outer envelope's AAD.
This may also include other identifying information, such as:
* The purpose of the payload (e.g., `service-name|role=chat-message`)
* The version of the payload/process information (e.g., `v=2`)
* The [[KID]] (Key ID) of the key to target for decryption.
* The ephemeral public key
Like HKDF Information, this is just raw bytes, and is not secret.
The AAD, a [[Nonce/IV]], and the payload data can be passed in and an encrypted payload returned.
## 4. Assemble the result
The resulting information can be assembled together and sent to the recipient for decryption.
A message would include:
* The ephemeral public key
* The payload IV
* The encrypted payload data
* Ideally a timestamp to help avoid [[replay attacks]]
* Any additional information needed to help process the request (routing information, a version indicator matching the internal encryption process, etc).