Elliptic curves
Elliptic curves constructed over finite fields are another important cryptographic tool.
We use elliptic curves because they provide a cryptographic group, i.e. a group in which the discrete logarithm problem (discussed below) is hard.
There are several ways to define the curve equation, but for our purposes, let be a large (255-bit) field, and then let the set of solutions to for some constant define the -rational points on an elliptic curve . These coordinates are called "affine coordinates". Each of the -rational points, together with a "point at infinity" that serves as the group identity, can be interpreted as an element of a group. By convention, elliptic curve groups are written additively.
"Three points on a line sum to zero, which is the point at infinity."
The group addition law is simple: to add two points together, find the line that intersects both points and obtain the third point, and then negate its -coordinate. The case that a point is being added to itself, called point doubling, requires special handling: we find the line tangent to the point, and then find the single other point that intersects this line and then negate. Otherwise, in the event that a point is being "added" to its negation, the result is the point at infinity.
The ability to add and double points naturally gives us a way to scale them by integers, called scalars. The number of points on the curve is the group order. If this number is a prime , then the scalars can be considered as elements of a scalar field, .
Elliptic curves, when properly designed, have an important security property. Given two random elements finding such that , otherwise known as the discrete log of with respect to , is considered computationally infeasible with classical computers. This is called the elliptic curve discrete log assumption.
If an elliptic curve group has prime order (like the ones used in Halo 2), then it is a finite cyclic group. Recall from the section on groups that this implies it is isomorphic to , or equivalently, to the scalar field . Each possible generator fixes the isomorphism; then an element on the scalar side is precisely the discrete log of the corresponding group element with respect to . In the case of a cryptographically secure elliptic curve, the isomorphism is hard to compute in the direction because the elliptic curve discrete log problem is hard.
It is sometimes helpful to make use of this isomorphism by thinking of group-based cryptographic protocols and algorithms in terms of the scalars instead of in terms of the group elements. This can make proofs and notation simpler.
For instance, it has become common in papers on proof systems to use the notation to denote a group element with discrete log , where the generator is implicit.
We also used this idea in the "distinct-x theorem", in order to prove correctness of optimizations for elliptic curve scalar multiplication in Sapling, and an endomorphism-based optimization in Appendix C of the original Halo paper.
Curve arithmetic
Point doubling
The simplest situation is doubling a point . Continuing with our example , this is done first by computing the derivative
To obtain expressions for we consider
To get the expression for we substitute into the elliptic curve equation:
Comparing coefficients for the term gives us
Projective coordinates
This unfortunately requires an expensive inversion of . We can avoid this by arranging our equations to "defer" the computation of the inverse, since we often do not need the actual affine coordinate of the resulting point immediately after an individual curve operation. Let's introduce a third coordinate and scale our curve equation by like so:
Our original curve is just this curve at the restriction . If we allow the affine point to be represented by , and then we have the homogenous projective curve
Obtaining from is as simple as computing when . (When we are dealing with the point at infinity .) In this form, we now have a convenient way to defer the inversion required by doubling a point. The general strategy is to express as rational functions using and , rearrange to make their denominators the same, and then take the resulting point to have be the shared denominator and .
Projective coordinates are often, but not always, more efficient than affine coordinates. There may be exceptions to this when either we have a different way to apply Montgomery's trick, or when we're in the circuit setting where multiplications and inversions are about equally as expensive (at least in terms of circuit size).
The following shows an example of doubling a point without an inversion. Substituting with gives us
and gives us
Notice how the denominators of and are the same. Thus, instead of computing we can compute with and set to the corresponding numerators such that and . This completely avoids the need to perform an inversion when doubling, and something analogous to this can be done when adding two distinct points.
Point addition
We now add two points with distinct -coordinates, and where to obtain The line has slope
Using the expression for , we compute -coordinate of as:
Plugging the expression for into the curve equation yields
Comparing coefficients for the term gives us .
Important notes:
- There exist efficient formulae1 for point addition that do not have edge cases (so-called "complete" formulae) and that unify the addition and doubling cases together. The result of adding a point to its negation using those formulae produces , which represents the point at infinity.
- In addition, there are other models like the Jacobian representation where where the curve is rescaled by instead of , and this representation has even more efficient arithmetic but no unified/complete formulae.
- We can easily compare two curve points and for equality in the homogenous projective coordinate space by "homogenizing" their Z-coordinates; the checks become and .
Curve endomorphisms
Imagine that has a primitive cube root of unity, or in other words that and so an element generates a -order multiplicative subgroup. Notice that a point on our example elliptic curve has two cousin points: , because the computation effectively kills the component of the -coordinate. Applying the map is an application of an endomorphism over the curve. The exact mechanics involved are complicated, but when the curve has a prime number of points (and thus a prime "order") the effect of the endomorphism is to multiply the point by a scalar in which is also a primitive cube root in the scalar field.
Curve point compression
Given a point on the curve , we know that its negation is also on the curve. To uniquely specify a point, we need only encode its -coordinate along with the sign of its -coordinate.
Serialization
As mentioned in the Fields section, we can interpret the least significant
bit of a field element as its "sign", since its additive inverse will always have the
opposite LSB. So we record the LSB of the -coordinate as sign
.
Pallas and Vesta are defined over the and fields, which
elements can be expressed in bits. This conveniently leaves one unused bit in a
32-byte representation. We pack the -coordinate sign
bit into the highest bit in
the representation of the -coordinate:
<----------------------------------- x --------------------------------->
Enc(P) = [_ _ _ _ _ _ _ _] [_ _ _ _ _ _ _ _] ... [_ _ _ _ _ _ _ _] [_ _ _ _ _ _ _ sign]
^ <-------------------------------------> ^
LSB 30 bytes MSB
The "point at infinity" that serves as the group identity, does not have an affine representation. However, it turns out that there are no points on either the Pallas or Vesta curve with or . We therefore use the "fake" affine coordinates to encode , which results in the all-zeroes 32-byte array.
Deserialization
When deserializing a compressed curve point, we first read the most significant bit as
ysign
, the sign of the -coordinate. Then, we set this bit to zero to recover the
original -coordinate.
If we return the "point at infinity" . Otherwise, we proceed
to compute Here, we read the least significant bit of as sign
.
If sign == ysign
, we already have the correct sign and simply return the curve point
. Otherwise, we negate and return .
Cycles of curves
Let be an elliptic curve over a finite field where is a prime. We denote this by and we denote the group of points of over with order For this curve, we call the "base field" and the "scalar field".
We instantiate our proof system over the elliptic curve . This allows us to prove statements about -arithmetic circuit satisfiability.
(aside) If our curve is over why is the arithmetic circuit instead in ? The proof system is basically working on encodings of the scalars in the circuit (or more precisely, commitments to polynomials whose coefficients are scalars). The scalars are in when their encodings/commitments are elliptic curve points in .
However, most of the verifier's arithmetic computations are over the base field and are thus efficiently expressed as an -arithmetic circuit.
(aside) Why are the verifier's computations (mainly) over ? The Halo 2 verifier actually has to perform group operations using information output by the circuit. Group operations like point doubling and addition use arithmetic in , because the coordinates of points are in
This motivates us to construct another curve with scalar field , which has an -arithmetic circuit that can efficiently verify proofs from the first curve. As a bonus, if this second curve had base field it would generate proofs that could be efficiently verified in the first curve's -arithmetic circuit. In other words, we instantiate a second proof system over forming a 2-cycle with the first:
TODO: Pallas-Vesta curves
Reference: https://github.com/zcash/pasta
Hashing to curves
Sometimes it is useful to be able to produce a random point on an elliptic curve corresponding to some input, in such a way that no-one will know its discrete logarithm (to any other base).
This is described in detail in the Internet draft on Hashing to Elliptic Curves. Several algorithms can be used depending on efficiency and security requirements. The framework used in the Internet Draft makes use of several functions:
hash_to_field
: takes a byte sequence input and maps it to a element in the base fieldmap_to_curve
: takes an element and maps it to .
TODO: Simplified SWU
Reference: https://eprint.iacr.org/2019/403.pdf