diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..eb4f935
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,3 @@
+
+doc:
+ cargo rustdoc --features "nightly yolocrypto" -- --html-in-header katex-header.html
diff --git a/katex-header.html b/katex-header.html
new file mode 100644
index 0000000..695c735
--- /dev/null
+++ b/katex-header.html
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/src/ristretto.rs b/src/ristretto.rs
index a608696..1a63da8 100644
--- a/src/ristretto.rs
+++ b/src/ristretto.rs
@@ -8,12 +8,377 @@
// - Isis Agora Lovecruft
// - Henry de Valence
-//! An implementation of Mike Hamburg's Ristretto cofactor-eliminating
-//! point-compression scheme, providing a prime-order group on top of
-//! Curve25519.
+//! An implementation of Ristretto, which provides a prime-order group.
+//!
+//! Ristretto is a modification of Mike Hamburg's [Decaf
+//! cofactor-eliminating point-compression
+//! scheme](https://eprint.iacr.org/2015/673.pdf) to work on top of the
+//! Curve25519 group.
//!
//! Note: this code is currently feature-gated with the `yolocrypto`
//! feature flag, because our implementation is still unfinished.
+//!
+//! # Notes on Ristretto
+//!
+//! ## Decaf
+//!
+//! The introduction of the Decaf paper, [_Decaf: Eliminating cofactors
+//! through point compression_](https://eprint.iacr.org/2015/673.pdf)
+//! notes that while most cryptographic systems require a group of prime
+//! order, most concrete implementations using elliptic curve groups
+//! fall short -- they either provide a group of prime order, but with
+//! incomplete or variable-time addition formulae (for instance, most
+//! Weierstrass models), or else they provide a fast and safe
+//! implementation of a group whose order is not quite a prime \\(q\\),
+//! but \\(hq\\) for a small cofactor \\(h\\) (for instance, Edwards
+//! curves, which have cofactor at least \\(4\\)).
+//!
+//! This abstraction mismatch requires ad-hoc protocol modifications to
+//! ensure security; these modifications require careful analysis and
+//! are a recurring source of vulnerabilities.
+//!
+//! The Decaf suggestion is to use a quotient group, such as \\(\mathcal
+//! E / \mathcal E[4]\\) or \\(2 \mathcal E / \mathcal E[2] \\), to
+//! implement a prime-order group.
+//!
+//! This requires only changing
+//!
+//! 1. the function for equality checking (so that two representatives
+//! of the same coset are considered equal);
+//! 2. the function for encoding (so that two representatives of the
+//! same coset are encoded as identical bitstrings);
+//! 3. the function for decoding (so that only the canonical encoding of
+//! a coset is accepted).
+//!
+//! Internally, each coset is represented by a curve point; two points
+//! may represent the same coset in the same way that two points with
+//! different \\(X,Y,Z\\) coordinates may represent the same point. The
+//! group operations are carried out using the fast, safe Edwards
+//! formulas.
+//!
+//! The Decaf paper suggests implementing the compression and
+//! decompression routines using an isogeny from a Jacobi quartic; for
+//! curves of cofactor \\(4\\), this eliminates the cofactor, and
+//! explains the name: Decaf is named "after the procedure which divides
+//! the effect of coffee by \\(4\\)". However, Curve25519 has a
+//! cofactor of \\(8\\). To eliminate its cofactor, we tweak Decaf to
+//! restrict further. This gives the
+//! [Ristretto](https://en.wikipedia.org/wiki/Ristretto) encoding.
+//!
+//! ## The Jacobi Quartic
+//!
+//! The Jacobi quartic is parameterized by \\(e, A\\), and is of the
+//! form $$ \mathcal J\_{e,A} : t\^2 = es\^4 + 2As\^2 + 1, $$ with
+//! identity point \\((0,1)\\). For more details on the Jacobi quartic,
+//! see the [Decaf paper](https://eprint.iacr.org/2015/673.pdf) or
+//! [_Jacobi Quartic Curves
+//! Revisited_](https://eprint.iacr.org/2009/312.pdf) by Hisil, Wong,
+//! Carter, and Dawson).
+//!
+//! When \\(e = a\^2\\), \\(\mathcal J\_{e,A}\\) has full
+//! \\(2\\)-torsion (i.e., \\(\mathcal J[2] \cong \mathbb Z /2 \times
+//! \mathbb Z/2\\)), and
+//! we can write the \\(\mathcal J[2]\\)-coset of a point \\(P =
+//! (s,t)\\) as
+//! $$
+//! P + \mathcal J[2] = \left\\{
+//! (s,t),
+//! (-s,-t),
+//! (1/as, -t/as\^2),
+//! (-1/as, t/as\^2) \right\\}.
+//! $$
+//! Notice that replacing \\(a\\) by \\(-a\\) just swaps the last two
+//! points, so this set does not depend on the choice of \\(a\\). In
+//! what follows we require \\(a = \pm 1\\).
+//!
+//! ## Encoding \\(\mathcal J / \mathcal J[2]\\)
+//!
+//! To encode points on \\(\mathcal J\\) modulo \\(\mathcal J[2]\\),
+//! we need to choose a canonical representative of the above coset.
+//! To do this, it's sufficient to make two independent sign choices:
+//! the Decaf paper suggests choosing \\((s,t)\\) with \\(s\\)
+//! non-negative and finite, and \\(t/s\\) non-negative or infinite.
+//!
+//! The encoding is then the (canonical byte encoding of the)
+//! \\(s\\)-value of the canonical representative.
+//!
+//! ## The Edwards Curve
+//!
+//! Our primary internal model for Curve25519 points are the [_Extended
+//! Twisted Edwards Coordinates_](https://eprint.iacr.org/2008/522.pdf)
+//! of Hisil, Wong, Carter, and Dawson.
+//! These correspond to the affine model
+//!
+//! $$\mathcal E\_{a,d} : ax\^2 + y\^2 = 1 + dx\^2y\^2.$$
+//!
+//! In projective coordinates, we represent a point as \\((X:Y:Z:T)\\)
+//! with $$XY = ZT, \quad aX\^2 + Y\^2 = Z\^2 + dT\^2.$$ (For more
+//! details on this model, see the documentation for the `edwards`
+//! module). The case \\(a = 1\\) is the _untwisted_ case; we only
+//! consider \\(a = \pm 1\\), and in particular we focus on the twisted
+//! Edwards form of Curve25519, which has \\(a = -1, d =
+//! -121665/121666\\). When not otherwise specified, we write
+//! \\(\mathcal E\\) for \\(\mathcal E\_{-1, -121665/121666}\\).
+//!
+//! When both \\(d\\) and \\(ad\\) are nonsquare (which forces \\(a\\)
+//! to be square), the curve is *complete*. In this case the
+//! four-torsion subgroup is cyclic, and we
+//! can write it explicitly as
+//! $$
+//! \mathcal E\_{a,d}[4] = \\{ (0,1),\; (1/\sqrt a, 0),\; (0, -1),\; (-1/\sqrt{a}, 0)\\}.
+//! $$
+//! These are the only points with \\(xy = 0\\); the points with \\( y
+//! \neq 0 \\) are \\(2\\)-torsion. The \\(\mathcal
+//! E\_{a,d}[4]\\)-coset of \\(P = (x,y)\\) is then
+//! $$
+//! P + \mathcal E\_{a,d}[4] = \\{ (x,y),\; (y/\sqrt a, -x\sqrt a),\; (-x, -y),\; (-y/\sqrt a, x\sqrt a)\\}.
+//! $$
+//! Notice that if \\(xy \neq 0 \\), then exactly two of
+//! these points have \\( xy \\) non-negative, and they differ by the
+//! \\(2\\)-torsion point \\( (0,-1) \\). This means that we can select
+//! a representative modulo \\(\mathcal
+//! E\_{a,d}[2] \\) by requiring \\(xy\\) nonnegative and \\(y \neq
+//! 0\\), and we can ensure this condition by conditionally adding a
+//! \\(4\\)-torsion point if \\(xy\\) is negative or \\(y = 0\\).
+//!
+//! This procedure gives a canonical lift from \\(\mathcal E / \mathcal
+//! E[4]\\) to \\(\mathcal E / \mathcal E[2]\\). Since it involves a
+//! conditional rotation, we refer to it as *torquing* the point.
+//!
+//! The structure of the Curve25519 group is \\( \mathcal E(\mathbb
+//! F\_p) \cong \mathbb Z / 8 \times \mathbb Z / \ell\\), where \\( \ell
+//! = 2\^{252} + \cdots \\) is a large prime. Because \\(\mathcal E[8]
+//! \cong \mathbb Z / 8\\), we have \\(\[2\](\mathcal E[8]) = \mathcal
+//! E[4]\\), \\(\mathcal E[4] \cong \mathbb Z / 4
+//! \\) and \\( \mathcal E[2] \cong \mathbb Z / 2\\). In particular
+//! this tells us that the group
+//! $$
+//! \frac{\[2\](\mathcal E)}{\mathcal E[4]}
+//! $$
+//! is well-defined and has prime order \\( (8\ell / 2) / 4 = \ell \\).
+//! This is the group we will construct using Ristretto.
+//!
+//! ## The Isogeny
+//!
+//! For \\(a = \pm 1\\), we have a \\(2\\)-isogeny
+//! $$
+//! \theta\_{a,d} : \mathcal J\_{a\^2, -a(a+d)/(a-d)} \longrightarrow \mathcal E\_{a,d}
+//! $$
+//! (or simply \\(\theta\\)) defined by
+//! $$
+//! \theta\_{a,d} : (s,t) \mapsto \left( \frac{1}{\sqrt{ad-1}} \cdot \frac{2s}{t},\quad \frac{1+as\^2}{1-as\^2} \right).
+//! $$
+//!
+//! XXX Its dual is ... ?
+//!
+//! The kernel of the isogeny is \\( \{(0, \pm 1)\} \\).
+//! The image of the isogeny is \\(\[2\](\mathcal E)\\). To see this,
+//! first note that because \\( \theta \circ \hat{\theta} = [2] \\), we
+//! know that \\( \[2\](\mathcal E) \subseteq \theta(\mathcal J)\\); then, to see that
+//! \\(\theta(\mathcal J)\\) is exactly \\(\[2\](\mathcal E)\\),
+//! recall that isogenous elliptic curves over a finite field have the
+//! same number of points (exercise 5.4 of Silverman), so that
+//! $$
+//! \\# \theta(\mathcal J) = \frac {\\# \mathcal J} {\\# \ker \theta}
+//! = \frac {\\# \mathcal E}{2} = \\# \[2\](\mathcal E).
+//! $$
+//!
+//! To determine the image \\(\theta(\mathcal J[2])\\) of the
+//! \\(2\\)-torsion, we consider the image of the coset \\(\theta((s,t)
+//! + \mathcal J[2])\\). Let \\((x,y) = \theta(s,t)\\); then
+//! \\(\theta(-s,-t) = (x,y)\\) and \\(\theta(1/as, -t/as\^2) = (-x,
+//! -y)\\), so that \\(\theta(\mathcal J[2]) = \mathcal E[2]\\).
+//!
+//! The Decaf paper recalls that, for a group \\( G \\) with normal
+//! subgroup \\(G' \leq G\\), a group homomorphism \\( \phi : G
+//! \rightarrow H \\) induces a homomorphism
+//! $$
+//! \bar{\phi} : \frac G {G'} \longrightarrow \frac {\phi(G)}{\phi(G')} \leq \frac {H} {\phi(G')},
+//! $$
+//! and that the induced homomorphism \\(\bar{\phi}\\) is injective if
+//! \\( \ker \phi \leq G' \\). In our context, the kernel of
+//! \\(\theta\\) is \\( \\{(0, \pm 1)\\} \leq \mathcal J[2] \\),
+//! so \\(\theta\\) gives an isomorphism
+//! $$
+//! \frac {\mathcal J} {\mathcal J[2]}
+//! \cong
+//! \frac {\theta(\mathcal J)} {\theta(\mathcal J[2])}
+//! \cong
+//! \frac {\[2\](\mathcal E)} {\mathcal E[2]}.
+//! $$
+//!
+//! We can use the isomorphism to transfer the encoding of \\(\mathcal
+//! J / \mathcal J[2] \\) defined above to \\(\[2\](\mathcal E)/\mathcal
+//! E[2]\\), by encoding the Edwards point \\((x,y)\\) using the Jacobi
+//! quartic encoding of \\(\theta\^{-1}(x,y)\\).
+//!
+//! Since \\(\\# (\[2\](\mathcal E) / \mathcal E[2]) = (\\#\mathcal
+//! E)/4\\), if \\(\mathcal E\\) has cofactor \\(4\\), we're done.
+//! Otherwise, if \\(\mathcal E\\) has cofactor \\(8\\), as in the
+//! Curve25519 case, we use the torquing procedure to lift \\(\mathcal E
+//! / \mathcal E[4]\\) to \\(\mathcal E / \mathcal E[2]\\), and then
+//! apply the encoding for \\( \[2\](\mathcal E) / \mathcal E[2] \\).
+//!
+//! ## The Ristretto Encoding
+//!
+//! We can write the above encoding/decoding procedure concretely (in affine
+//! coordinates) as follows:
+//!
+//! ### Encoding
+//!
+//! On input \\( (x,y) \in \[2\](\mathcal E)\\), a representative for a
+//! coset in \\( \[2\](\mathcal E) / \mathcal E[4] \\):
+//!
+//! 1. Check if \\( xy \\) is negative or \\( x = 0 \\); if so, torque
+//! the point by setting \\( (x,y) \gets (x,y) + P_4 \\), where
+//! \\(P_4\\) is a \\(4\\)-torsion point.
+//!
+//! 2. Check if \\(x\\) is negative or \\( y = -1 \\); if so, set
+//! \\( (x,y) \gets (x,y) + (0,-1) = (-x, -y) \\).
+//!
+//! 3. Compute $$ s = +\sqrt {(-a) \frac {1 - y} {1 + y} }, $$ choosing
+//! the positive square root.
+//!
+//! The output is then the (canonical) byte-encoding of \\(s\\).
+//!
+//! If \\(\mathcal E\\) has cofactor \\(4\\), we skip the first step,
+//! since our input already represents a coset in
+//! \\( \[2\](\mathcal E) / \mathcal E[2] \\).
+//!
+//! To see that this corresponds to the encoding procedure above, notice
+//! that the first step lifts from \\( \mathcal E / \mathcal E[4] \\) to
+//! \\(\mathcal E / \mathcal E[2]\\). To understand steps 2 and 3,
+//! notice that the \\(y\\)-coordinate of \\(\theta(s,t)\\) is
+//! $$
+//! y = \frac {1 + as\^2}{1 - as\^2},
+//! $$
+//! so that the \\(s\\)-coordinate of \\(\theta\^{-1}(x,y)\\) has
+//! $$
+//! s\^2 = (-a)\frac {1-y}{1+y}.
+//! $$
+//! Since
+//! $$
+//! x = \frac 1 {\sqrt {ad - 1}} \frac {2s} {t},
+//! $$
+//! we also have
+//! $$
+//! \frac s t = x \frac {\sqrt {ad-1}} 2,
+//! $$
+//! so that the sign of \\(s/t\\) is determined by the sign of \\(x\\).
+//!
+//! Recall that to choose a canonical representative of \\( (s,t) +
+//! \mathcal J[2] \\), it's sufficient to make two sign choices: the
+//! sign of \\(s\\) and the sign of \\(s/t\\). Step 2 determines the
+//! sign of \\(s/t\\), while step 3 computes \\(s\\) and determines its
+//! sign (by choosing the positive square root). Finally, the check
+//! that \\(y \neq -1\\) prevents division-by-zero when encoding the
+//! identity; it falls out of the optimized formulas below.
+//!
+//! ### Decoding
+//!
+//! On input `s_bytes`, decoding proceeds as follows:
+//!
+//! 1. Decode `s_bytes` to \\(s\\); reject if `s_bytes` is not the
+//! canonical encoding of \\(s\\).
+//!
+//! 2. Check whether \\(s\\) is negative; if so, reject.
+//!
+//! 3. Compute
+//! $$
+//! y \gets \frac {1 + as\^2}{1 - as\^2}.
+//! $$
+//!
+//! 4. Compute
+//! $$
+//! x \gets +\sqrt{ \frac{4s\^2} {ad(1+as\^2)\^2 - (1-as\^2)\^2}},
+//! $$
+//! choosing the positive square root, or reject if the square root does
+//! not exist.
+//!
+//! 5. Check whether \\(xy\\) is negative or \\(y = 0\\); if so, reject.
+//!
+//! ## Encoding in Extended Coordinates
+//!
+//! The formulas above are given in affine coordinates, but the usual
+//! internal representation is extended twisted Edwards coordinates \\(
+//! (X:Y:Z:T) \\) with \\( x = X/Z \\), \\(y = Y/Z\\), \\(xy = T/Z \\).
+//! Selecting the distinguished representative of the coset
+//! requires the affine coordinates \\( (x,y) \\), and computing \\( s
+//! \\) requires an inverse square root.
+//! As inversions are expensive, we'd like to be able to do this
+//! whole computation with only one inverse square root, by batching
+//! together the inversion and the inverse square root.
+//!
+//! However, it is not obvious how to do this, since the inverse square
+//! root computation depends on the affine coordinates (which select the
+//! distinguished representative).
+//!
+//! In what follows we consider only the case
+//! \\(a = -1\\); a similar argument applies to the case \\( a = 1\\).
+//!
+//! Since \\(y = Y/Z\\), in extended coordinates the formula for \\(s\\) becomes
+//! $$
+//! s = \sqrt{ \frac{ 1 - Y/Z}{1+Y/Z}} = \sqrt{\frac{Z - Y}{Z+Y}}
+//! = \frac {Z - Y} {\sqrt{Z\^2 - Y\^2}}.
+//! $$
+//!
+//! Here \\( (X:Y:Z:T) \\) are the coordinates of the distinguished
+//! representative of the coset.
+//! Write \\( (X\_0 : Y\_0 : Z\_0 : T\_0) \\)
+//! for the coordinates of the initial representative. Then the
+//! torquing procedure in step 1 replaces \\( (X\_0 : Y\_0 : Z\_0 :
+//! T\_0) \\) by \\( (iY\_0 : iX\_0 : Z\_0 : -T\_0) \\). This means we
+//! want to obtain either
+//! $$
+//! \frac {1} { \sqrt{Z\_0\^2 - Y\_0\^2}}
+//! \quad \text{or} \quad
+//! \frac {1} { \sqrt{Z\_0\^2 + X\_0\^2}}.
+//! $$
+//!
+//! We can relate these using the identity
+//! $$
+//! (a-d)X\^2Y\^2 = (Z\^2 - aX\^2)(Z\^2 - Y\^2),
+//! $$
+//! which is valid for all curve points. To see this, recall from the curve equation that
+//! $$
+//! -dX\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2,
+//! $$
+//! so that
+//! $$
+//! (a-d)X\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2 + aX\^2Y\^2 = (Z\^2 - Y\^2)(Z\^2 + X\^2).
+//! $$
+//!
+//! The encoding procedure is as follows:
+//!
+//! 1. \\(u\_1 \gets (Z\_0 + Y\_0)(Z\_0 - Y\_0) = Z\_0\^2 - Y\_0\^2 \\)
+//! 2. \\(u\_2 \gets X\_0 Y\_0 \\)
+//! 3. \\(I \gets \mathrm{invsqrt}(u\_1 u\_2\^2) = 1/\sqrt{X\_0\^2 Y\_0\^2 (Z\_0\^2 - Y\_0\^2)} \\)
+//! 4. \\(D\_1 \gets u\_1 I = \sqrt{(Z\_0\^2 - Y\_0\^2)/(X\_0\^2 Y\_0\^2)} \\)
+//! 5. \\(D\_2 \gets u\_2 I = \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} \\)
+//! 6. \\(Z\_{inv} \gets D\_1 D\_2 T\_0 = (u\_1 u\_2)/(u\_1 u\_2\^2) T\_0 = T\_0 / X\_0 Y\_0 = 1/Z\_0 \\)
+//! 7. If \\( T\_0 Z\_{inv} = x\_0 y\_0 \\) is negative:
+//! 1. \\( X \gets iY\_0 \\)
+//! 2. \\( Y \gets iX\_0 \\)
+//! 3. \\( D \gets D\_1 / \sqrt{a-d} = 1/\sqrt{Z\_0\^2 + X\_0\^2} \\)
+//! 8. Otherwise:
+//! 1. \\( X \gets X\_0 \\)
+//! 2. \\( Y \gets Y\_0 \\)
+//! 3. \\( D \gets D\_2 = \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} \\)
+//! 9. If \\( X Z\_{inv} = x \\) is negative, set \\( Y \gets - Y\\)
+//! 10. Compute \\( s \gets (Z - Y) D = (Z - Y) / \sqrt{Z\^2 - Y\^2} \\) and return.
+//!
+//! ## Decoding to Extended Coordinates
+//!
+//! ## Equality Testing
+//!
+//! ## Elligator
+//!
+//! ## The Double-Ristretto Encoding
+//!
+//! It's possible to do batch encoding of \\( [2]P \\) using the dual
+//! isogeny \\(\hat{\theta}\\). Defer this for now.
+//!
+//! ## ???
// We allow non snake_case names because coordinates in projective space are
// traditionally denoted by the capitalisation of their respective
@@ -93,9 +458,10 @@ impl CompressedRistretto {
}
// Step 2. The rest. (XXX write comments)
+ let one = FieldElement::one();
let ss = s.square();
- let yden = &FieldElement::one() + &ss;
- let ynum = &FieldElement::one() - &ss;
+ let yden = &one + &ss; // 1 - a*s^2
+ let ynum = &one - &ss; // 1 + a*s^2
let yden_sqr = yden.square();
let xden_sqr = &(&(-&constants::d) * &ynum.square()) - &yden_sqr;
@@ -104,19 +470,17 @@ impl CompressedRistretto {
let xden_inv = &invsqrt * &yden;
let yden_inv = &invsqrt * &(&xden_inv * &xden_sqr);
- let mut x = &(&s + &s) * &xden_inv;
+ let mut x = &(&s + &s) * &xden_inv; // 2*s*xden_inv
let x_is_negative = x.is_negative_ed25519();
x.conditional_negate(x_is_negative);
let y = &ynum * &yden_inv;
let t = &x * &y;
- if ok == 0u8 || t.is_negative_ed25519() == 1u8 || x.is_zero() == 1u8 {
+ if ok == 0u8 || t.is_negative_ed25519() == 1u8 || y.is_zero() == 1u8 {
return None;
} else {
- return Some(RistrettoPoint(ExtendedPoint{
- X: x, Y: y, Z: FieldElement::one(), T: t
- }));
+ return Some(RistrettoPoint(ExtendedPoint{X: x, Y: y, Z: one, T: t}));
}
}
}
@@ -186,24 +550,86 @@ impl<'de> Deserialize<'de> for RistrettoPoint {
// Internal point representations
// ------------------------------------------------------------------------
-/// A point in a prime-order group.
+/// A `RistrettoPoint` represents a point in the Ristretto group for
+/// Curve25519. Ristretto, a variant of Decaf, constructs a
+/// prime-order group as a quotient group of a subgroup of (the
+/// Edwards form of) Curve25519.
///
-/// XXX think about how this API should work
+/// Internally, a `RistrettoPoint` is a wrapper type around
+/// `ExtendedPoint`, with custom equality, compression, and
+/// decompression routines to account for the quotient.
#[derive(Copy, Clone)]
pub struct RistrettoPoint(pub ExtendedPoint);
impl RistrettoPoint {
/// Compress in Ristretto format.
+ ///
+ /// # Implementation Notes
+ ///
+ /// The Ristretto encoding is as follows, on input in affine coordinates `(x,y)`:
+ ///
+ /// 1. If `xy` is negative or `x = 0`, "rotate" the point by
+ /// setting `(x,y) = (iy, ix)`.
+ /// 2. If `x` is negative, set `(x,y) = (-x, -y)`.
+ /// 3. Compute `s = +sqrt((1-y)/(1+y))`.
+ /// 4. Return the little-endian 32-byte encoding of `s`.
+ ///
+ /// However, our input is in extended twisted Edwards coordinates
+ /// `(X:Y:Z:T)` with `x = X/Z`, `y = Y/Z`, `xy = T/Z` (see the
+ /// module-level documentation on curve representations for more
+ /// details). Since inversions are expensive, we'd like to be
+ /// able to do this whole computation with only one inversion.
+ ///
+ /// Since `y = Y/Z`, in extended coordinates the formula for `s` becomes
+ ///
+ /// s = sqrt((1 - Y/Z)/(1 + Y/Z)) = sqrt((Z-Y)/(Z+Y)). (1)
+ ///
+ /// We can compute this as
+ ///
+ /// s = (Z - Y) / sqrt((Z-Y)(Z+Y)). (1)
+ ///
+ /// The denominator is
+ ///
+ /// invsqrt((Z-Y)(Z+Y)) = invsqrt(Z² - Y²). (1)
+ ///
+ /// Write the input point as `(X₀:Y₀:Z₀:T₀)`. The rotation in
+ /// step 1 of the encoding procedure replaces `(X₀:Y₀:Z₀:T₀)` by
+ /// `(iY₀:iX₀:Z₀:-T₀)`. We therefore wish to relate the
+ /// computation of
+ ///
+ /// invsqrt(Z² - Y²) = invsqrt(Z₀² - Y₀²) [non-rotated case]
+ ///
+ /// with the computation of
+ ///
+ /// invsqrt(Z² - Y²) = invsqrt(Z₀² + X₀²). [rotated case]
+ ///
+ /// Recall the curve equation (in the 𝗣² model):
+ ///
+ /// (-X² + Y²)Z² = Z⁴ + dX²Y². (1)
+ ///
+ /// This means that, for any point `(X:Y:Z:T)` in extended coordinates, we have
+ ///
+ /// -dX²Y² = Z⁴ + Z²X² - Z²Y², (2)
+ ///
+ /// so that
+ ///
+ /// (-1-d)X²Y² = Z⁴ + Z²X² - Z²Y² - X²Y², (3)
+ ///
+ /// and hence
+ ///
+ /// (-1-d)X²Y² = (Z² - Y²)(Z² + X²). (4)
+ ///
+ /// Taking inverse square roots gives
+ ///
+ /// invsqrt(Z² + X²) = invsqrt(-1-d) sqrt((Z² - Y²)/(X²Y²)). (4)
+ ///
+ ///
pub fn compress(&self) -> CompressedRistretto {
let mut X = self.0.X;
let mut Y = self.0.Y;
let Z = &self.0.Z;
let T = &self.0.T;
- println!("{:?}", self);
- println!("Z = {:?}", self.0.Z);
- println!("Y = {:?}", self.0.Y);
-
let u1 = &(Z + &Y) * &(Z - &Y);
let u2 = &X * &Y;
// Ignore return value since this is always square
@@ -227,8 +653,6 @@ impl RistrettoPoint {
Y.conditional_negate((&X * &z_inv).is_negative_ed25519());
let mut s = &den_inv * &(Z - &Y);
- let s_is_zero = s.is_zero();
- s.conditional_assign(&FieldElement::one(), s_is_zero);
let s_is_negative = s.is_negative_ed25519();
s.conditional_negate(s_is_negative);