Fix a group $G$ of order $q$ in which discrete logs are hard, and fix a standard base point $g \in G$. Fix an authenticated cipher $E_k$ of bit strings.
In (EC)IES, roughly: A public key is a point $h \in G$.
To encrypt a message $m$, the sender:
- picks an exponent $y \in \mathbb Z/q\mathbb Z$ uniformly at random,
- computes an ephemeral public key $t = g^y$,
- computes an ephemeral shared secret $s = h^y$, and
- sends $t$ alongside $c = E_k(m)$ where $k = H(s)$ is a hash of the shared secret.
Note: The total computational cost is two exponentiations; the total ciphertext overhead is one group element. Effectively, we are generating an ephemeral Diffie–Hellman key pair $(y, t)$ and doing a Diffie–Hellman key agreement with it.
The receiver, who knows the secret exponent $x \in \mathbb Z/q\mathbb Z$ such that $h = g^x$, computes $k = H(t^x)$ and decrypts $c$ with $k$.
Note: The total computational cost is one exponentiation.
In (EC-)Elgamal: A public key is a point $h \in G$.
To encrypt a message $m$, the sender:
- picks an exponent $y \in \mathbb Z/q\mathbb Z$ uniformly at random,
- computes an ephemeral public key $t = g^y$,
- computes an ephemeral shared secret $s = h^y$,
- picks $k$ uniformly at random from some subset of $G$,
- computes the product $z = k \cdot s$, and
- sends $t$, and $z$, alongside $c = E_k(m)$.
Note: The total cost is two exponentiations and one multiplication; the total ciphertext overhead is two group elements.
The receiver, who knows the secret exponent $x \in \mathbb Z/q\mathbb Z$ such that $h = g^x$, computes $k = z\cdot t^{-x}$ and decrypts $c$ with $k$.
Note: The total cost is one exponentiation and one multiplication.
As you read, you may see a resemblance here! Elgamal does essentially everything IES does, plus some extra work that adds no security, which I have written in bold.
In the elliptic curve case, Elgamal is even trickier. Why?
- In the finite field case where $G = \mathbb Z/p\mathbb Z$, the integers modulo a prime $p$ (or where $G = \operatorname{GF}(2^n)$), $k$ can be (say) a 256-bit string that serves a dual purpose as an AES key and, interpreted as an integer in little-endian, as an element of $G$.
- In the elliptic curve case, there's no natural map between bit strings and elements of $G$, so you need to choose some correspondence between random elements of $G$ and (say) AES keys—like a hash function $H(k)$. But if you were going to hash an element of $G$ into a key anyway, you might as well have just used ECIES!
Even worse, Elgamal requires computing multiplication in $G$, not just exponentiation $G$, which means that you can't take advantage of DH functions like X25519 which support only the equivalent of exponentiation (‘$x$-restricted scalar multiplication on Curve25519’, using the fast constant-time Montgomery ladder) but not the equivalent of multiplication (‘point addition on Curve25519’).
The only reason to use Elgamal is exotic applications that require expert guidance like voting systems, where the message $m$ is concealed directly, rather than some key $k$ used for an authenticated cipher; this happens only if you are exploiting the homomorphic properties of Elgamal, and doesn't work with arbitrary bit string messages but rather requires you to be very judicious about what messages are and how they are chosen.
If you want public-key anonymous encryption, you should use libsodium crypto_box_seal, which is conceptually the same idea as ECIES (with all the details I go into done differently). Note, of course, that public-key anonymous encryption is a kind of weird thing to do that most applications, outside journalists accepting leaks, don't need. More likely, you want public-key authenticated encryption, for which you should use NaCl/libsodium crypto_box_curve25519xsalsa20poly1305, if you have a definite notion of a sender and receiver who know one another's public keys and want to exchange unforgeable secret messages.