1use std::str::FromStr;
2
3use generic_array::GenericArray;
4use num::{BigUint, Num, One};
5use serde::{Deserialize, Serialize};
6use typenum::{U32, U62};
7
8use crate::{
9 curve25519_dalek::CompressedEdwardsY,
10 edwards::{EdwardsCurve, EdwardsParameters},
11 params::{FieldParameters, NumLimbs},
12 AffinePoint, CurveType, EllipticCurveParameters,
13};
14
15pub type Ed25519 = EdwardsCurve<Ed25519Parameters>;
16
17#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
18pub struct Ed25519Parameters;
19
20#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
21pub struct Ed25519BaseField;
22
23impl FieldParameters for Ed25519BaseField {
24 const MODULUS: &'static [u8] = &[
25 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
26 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127,
27 ];
28
29 const WITNESS_OFFSET: usize = 1usize << 14;
30
31 fn modulus() -> BigUint {
32 (BigUint::one() << 255) - BigUint::from(19u32)
33 }
34}
35
36impl NumLimbs for Ed25519BaseField {
37 type Limbs = U32;
38 type Witness = U62;
39}
40
41impl EllipticCurveParameters for Ed25519Parameters {
42 type BaseField = Ed25519BaseField;
43 const CURVE_TYPE: CurveType = CurveType::Ed25519;
44}
45
46impl EdwardsParameters for Ed25519Parameters {
47 const D: GenericArray<u8, U32> = GenericArray::from_array([
48 163, 120, 89, 19, 202, 77, 235, 117, 171, 216, 65, 65, 77, 10, 112, 0, 152, 232, 121, 119,
49 121, 64, 199, 140, 115, 254, 111, 43, 238, 108, 3, 82,
50 ]);
51
52 fn prime_group_order() -> BigUint {
53 BigUint::from(2u32).pow(252) + BigUint::from(27742317777372353535851937790883648493u128)
54 }
55
56 fn generator() -> (BigUint, BigUint) {
57 let x = BigUint::from_str_radix(
58 "15112221349535400772501151409588531511454012693041857206046113283949847762202",
59 10,
60 )
61 .unwrap();
62 let y = BigUint::from_str_radix(
63 "46316835694926478169428394003475163141307993866256225615783033603165251855960",
64 10,
65 )
66 .unwrap();
67 (x, y)
68 }
69}
70
71pub fn ed25519_sqrt(a: &BigUint) -> Option<BigUint> {
76 let modulus = Ed25519BaseField::modulus();
81 let mut beta = a.modpow(
83 &BigUint::from_str(
84 "7237005577332262213973186563042994240829374041602535252466099000494570602494",
85 )
86 .unwrap(),
87 &modulus,
88 );
89
90 let sqrt_m1 = BigUint::from_str(
96 "19681161376707505956807079304988542015446066515923890162744021073123829784752",
97 )
98 .unwrap();
99
100 let beta_squared = &beta * &beta % &modulus;
101 let neg_a = &modulus - a;
102
103 if beta_squared == neg_a {
104 beta = (&beta * &sqrt_m1) % &modulus;
105 }
106
107 let correct_sign_sqrt = &beta_squared == a;
108 let flipped_sign_sqrt = beta_squared == neg_a;
109
110 if !correct_sign_sqrt && !flipped_sign_sqrt {
111 return None;
112 }
113
114 let beta_bytes = beta.to_bytes_le();
115 if (beta_bytes[0] & 1) == 1 {
116 beta = (&modulus - &beta) % &modulus;
117 }
118
119 Some(beta)
120}
121
122pub fn decompress(compressed_point: &CompressedEdwardsY) -> Option<AffinePoint<Ed25519>> {
123 let mut point_bytes = *compressed_point.as_bytes();
124 let sign = point_bytes[31] >> 7 == 1;
125 point_bytes[31] &= 0b0111_1111;
127 let modulus = &Ed25519BaseField::modulus();
128
129 let y = &BigUint::from_bytes_le(&point_bytes);
130 let yy = &((y * y) % modulus);
131 let u = (yy + modulus - BigUint::one()) % modulus; let v = &((yy * &Ed25519Parameters::d_biguint()) + &BigUint::one()) % modulus; let v_inv = v.modpow(&(modulus - BigUint::from(2u64)), modulus);
135 let u_div_v = (u * &v_inv) % modulus;
136
137 let mut x = ed25519_sqrt(&u_div_v)?;
138
139 if sign {
142 x = (modulus - &x) % modulus;
143 }
144
145 Some(AffinePoint::new(x, y.clone()))
146}
147
148#[cfg(test)]
149mod tests {
150
151 use super::*;
152 use num::traits::ToBytes;
153
154 const NUM_TEST_CASES: usize = 100;
155
156 #[test]
157 fn test_ed25519_decompress() {
158 let mut point = {
162 let (x, y) = Ed25519Parameters::generator();
163 AffinePoint::<EdwardsCurve<Ed25519Parameters>>::new(x, y)
164 };
165 for _ in 0..NUM_TEST_CASES {
166 let compressed_point = {
169 let x = point.x.to_le_bytes();
170 let y = point.y.to_le_bytes();
171 let mut compressed = [0u8; 32];
172
173 compressed[..y.len()].copy_from_slice(&y);
175
176 compressed[31] |= (x[0] & 1) << 7;
178
179 CompressedEdwardsY(compressed)
180 };
181 assert_eq!(point, decompress(&compressed_point).unwrap());
182
183 point = point.clone() + point.clone();
185 }
186 }
187}