elastic_elgamal/group/
curve25519.rs1use core::convert::TryInto;
2
3use elliptic_curve::rand_core::{CryptoRng, RngCore};
4
5use crate::{
6 curve25519::{
7 constants::{ED25519_BASEPOINT_POINT, ED25519_BASEPOINT_TABLE},
8 edwards::{CompressedEdwardsY, EdwardsPoint},
9 scalar::Scalar,
10 traits::{Identity, IsIdentity, MultiscalarMul, VartimeMultiscalarMul},
11 },
12 group::{ElementOps, Group, RandomBytesProvider, ScalarOps},
13};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28#[cfg_attr(
29 docsrs,
30 doc(cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng")))
31)]
32pub struct Curve25519Subgroup(());
33
34impl ScalarOps for Curve25519Subgroup {
35 type Scalar = Scalar;
36
37 const SCALAR_SIZE: usize = 32;
38
39 fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar {
40 let mut scalar_bytes = [0_u8; 64];
41 rng.fill_bytes(&mut scalar_bytes[..]);
42 Scalar::from_bytes_mod_order_wide(&scalar_bytes)
43 }
44
45 fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar {
46 let mut scalar_bytes = [0_u8; 64];
47 source.fill_bytes(&mut scalar_bytes);
48 Scalar::from_bytes_mod_order_wide(&scalar_bytes)
49 }
50
51 fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
52 scalar.invert()
53 }
54
55 #[cfg(feature = "curve25519-dalek")]
56 fn invert_scalars(scalars: &mut [Self::Scalar]) {
57 Scalar::invert_batch_alloc(scalars);
58 }
59
60 #[cfg(feature = "curve25519-dalek-ng")]
61 fn invert_scalars(scalars: &mut [Self::Scalar]) {
62 Scalar::batch_invert(scalars);
63 }
64
65 fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8]) {
66 buffer.copy_from_slice(&scalar.to_bytes());
67 }
68
69 #[cfg(feature = "curve25519-dalek")]
70 fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar> {
71 let bytes: &[u8; 32] = buffer.try_into().expect("input has incorrect byte size");
72 Scalar::from_canonical_bytes(*bytes).into()
73 }
74
75 #[cfg(feature = "curve25519-dalek-ng")]
76 fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar> {
77 let bytes: &[u8; 32] = buffer.try_into().expect("input has incorrect byte size");
78 Scalar::from_canonical_bytes(*bytes)
79 }
80}
81
82impl ElementOps for Curve25519Subgroup {
83 type Element = EdwardsPoint;
84
85 const ELEMENT_SIZE: usize = 32;
86
87 fn identity() -> Self::Element {
88 EdwardsPoint::identity()
89 }
90
91 fn is_identity(element: &Self::Element) -> bool {
92 element.is_identity()
93 }
94
95 fn generator() -> Self::Element {
96 ED25519_BASEPOINT_POINT
97 }
98
99 fn serialize_element(element: &Self::Element, buffer: &mut [u8]) {
100 buffer.copy_from_slice(&element.compress().to_bytes());
101 }
102
103 #[cfg(feature = "curve25519-dalek")]
104 fn deserialize_element(buffer: &[u8]) -> Option<Self::Element> {
105 CompressedEdwardsY::from_slice(buffer)
106 .ok()?
107 .decompress()
108 .filter(EdwardsPoint::is_torsion_free)
109 }
110
111 #[cfg(feature = "curve25519-dalek-ng")]
112 fn deserialize_element(buffer: &[u8]) -> Option<Self::Element> {
113 CompressedEdwardsY::from_slice(buffer)
114 .decompress()
115 .filter(EdwardsPoint::is_torsion_free)
116 }
117}
118
119impl Group for Curve25519Subgroup {
120 #[cfg(feature = "curve25519-dalek")]
121 fn mul_generator(k: &Scalar) -> Self::Element {
122 k * ED25519_BASEPOINT_TABLE
123 }
124
125 #[cfg(feature = "curve25519-dalek-ng")]
126 fn mul_generator(k: &Scalar) -> Self::Element {
127 k * &ED25519_BASEPOINT_TABLE
128 }
129
130 fn vartime_mul_generator(k: &Scalar) -> Self::Element {
131 #[cfg(feature = "curve25519-dalek")]
132 let zero = Scalar::ZERO;
133 #[cfg(feature = "curve25519-dalek-ng")]
134 let zero = Scalar::zero();
135
136 EdwardsPoint::vartime_double_scalar_mul_basepoint(&zero, &EdwardsPoint::identity(), k)
137 }
138
139 fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
140 where
141 I: IntoIterator<Item = &'a Self::Scalar>,
142 J: IntoIterator<Item = Self::Element>,
143 {
144 EdwardsPoint::multiscalar_mul(scalars, elements)
145 }
146
147 fn vartime_double_mul_generator(
148 k: &Scalar,
149 k_element: Self::Element,
150 r: &Scalar,
151 ) -> Self::Element {
152 EdwardsPoint::vartime_double_scalar_mul_basepoint(k, &k_element, r)
153 }
154
155 fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
156 where
157 I: IntoIterator<Item = &'a Self::Scalar>,
158 J: IntoIterator<Item = Self::Element>,
159 {
160 EdwardsPoint::vartime_multiscalar_mul(scalars, elements)
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::{
168 PublicKeyConversionError,
169 curve25519::{constants::EIGHT_TORSION, scalar::Scalar, traits::Identity},
170 };
171
172 type PublicKey = crate::PublicKey<Curve25519Subgroup>;
173
174 #[test]
175 fn mangled_point_is_invalid_public_key() {
176 let mut rng = rand::rng();
177 for _ in 0..100 {
178 let mut point =
179 Curve25519Subgroup::mul_generator(&Curve25519Subgroup::generate_scalar(&mut rng));
180 point += EIGHT_TORSION[1];
181 assert!(!point.is_torsion_free());
182 let bytes = point.compress().to_bytes();
183 assert!(matches!(
184 PublicKey::from_bytes(&bytes).unwrap_err(),
185 PublicKeyConversionError::InvalidGroupElement
186 ));
187 }
188 }
189
190 #[test]
191 fn small_order_points_are_invalid_public_keys() {
192 let small_order = Scalar::from(8_u32);
193 for point in EIGHT_TORSION.iter().skip(1) {
196 assert_eq!(point * small_order, EdwardsPoint::identity());
197 let bytes = point.compress().to_bytes();
198 assert!(matches!(
199 PublicKey::from_bytes(&bytes).unwrap_err(),
200 PublicKeyConversionError::InvalidGroupElement
201 ));
202 }
203 }
204}