dcrypt_algorithms/ec/b283k/
point.rs1use crate::ec::b283k::{
4 constants::{B283K_FIELD_ELEMENT_SIZE, B283K_POINT_COMPRESSED_SIZE},
5 field::FieldElement,
6 scalar::Scalar,
7};
8use crate::error::{validate, Error, Result};
9use subtle::{Choice, ConditionallySelectable};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum PointFormat {
14 Identity,
16 Uncompressed,
18 Compressed,
20}
21
22#[derive(Clone, Copy, Debug)]
24pub struct Point {
25 pub(crate) is_identity: Choice,
26 pub(crate) x: FieldElement,
27 pub(crate) y: FieldElement,
28}
29
30impl PartialEq for Point {
31 fn eq(&self, other: &Self) -> bool {
32 let self_is_identity: bool = self.is_identity.into();
33 let other_is_identity: bool = other.is_identity.into();
34
35 if self_is_identity || other_is_identity {
36 return self_is_identity == other_is_identity;
37 }
38
39 self.x == other.x && self.y == other.y
40 }
41}
42
43impl Eq for Point {}
44
45impl ConditionallySelectable for Point {
46 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
47 Self {
48 is_identity: Choice::conditional_select(&a.is_identity, &b.is_identity, choice),
49 x: FieldElement::conditional_select(&a.x, &b.x, choice),
50 y: FieldElement::conditional_select(&a.y, &b.y, choice),
51 }
52 }
53}
54
55impl Point {
56 pub fn new_uncompressed(
60 x: &[u8; B283K_FIELD_ELEMENT_SIZE],
61 y: &[u8; B283K_FIELD_ELEMENT_SIZE],
62 ) -> Result<Self> {
63 let x_fe = FieldElement::from_bytes(x)?;
64 let y_fe = FieldElement::from_bytes(y)?;
65 if !Self::is_on_curve(&x_fe, &y_fe) {
66 return Err(Error::param(
67 "B283k Point",
68 "Point coordinates do not satisfy curve equation",
69 ));
70 }
71 Ok(Point {
72 is_identity: Choice::from(0),
73 x: x_fe,
74 y: y_fe,
75 })
76 }
77
78 pub fn identity() -> Self {
80 Point {
81 is_identity: Choice::from(1),
82 x: FieldElement::zero(),
83 y: FieldElement::zero(),
84 }
85 }
86
87 pub fn is_identity(&self) -> bool {
89 self.is_identity.into()
90 }
91
92 pub fn x_coordinate_bytes(&self) -> [u8; B283K_FIELD_ELEMENT_SIZE] {
94 self.x.to_bytes()
95 }
96
97 pub fn y_coordinate_bytes(&self) -> [u8; B283K_FIELD_ELEMENT_SIZE] {
99 self.y.to_bytes()
100 }
101
102 pub fn serialize_compressed(&self) -> [u8; B283K_POINT_COMPRESSED_SIZE] {
106 let mut out = [0u8; B283K_POINT_COMPRESSED_SIZE];
107 if self.is_identity() {
108 return out;
109 }
110
111 let y_tilde = self.x.invert().unwrap().mul(&self.y).trace();
112 out[0] = if y_tilde == 1 { 0x03 } else { 0x02 };
113 out[1..].copy_from_slice(&self.x.to_bytes());
114 out
115 }
116
117 pub fn deserialize_compressed(bytes: &[u8]) -> Result<Self> {
122 validate::length(
123 "B283k Compressed Point",
124 bytes.len(),
125 B283K_POINT_COMPRESSED_SIZE,
126 )?;
127 if bytes.iter().all(|&b| b == 0) {
128 return Ok(Self::identity());
129 }
130 let tag = bytes[0];
131 if tag != 0x02 && tag != 0x03 {
132 return Err(Error::param(
133 "B283k Point",
134 "Invalid compressed point prefix",
135 ));
136 }
137 let mut x_bytes = [0u8; B283K_FIELD_ELEMENT_SIZE];
138 x_bytes.copy_from_slice(&bytes[1..]);
139 let x = FieldElement::from_bytes(&x_bytes)?;
140 if x.is_zero() {
141 return Ok(Point {
142 is_identity: Choice::from(0),
143 x,
144 y: FieldElement::one().sqrt(),
145 });
146 }
147
148 let rhs = x.add(&x.square().invert().unwrap());
149
150 if rhs.trace() != 0 {
151 return Err(Error::param("B283k Point", "Cannot decompress point"));
152 }
153
154 let mut z = Self::half_trace(&rhs);
155
156 if z.trace() != (tag as u64 - 2) {
157 z = z.add(&FieldElement::one());
158 }
159
160 let y = x.mul(&z);
161 Ok(Point {
162 is_identity: Choice::from(0),
163 x,
164 y,
165 })
166 }
167
168 fn half_trace(a: &FieldElement) -> FieldElement {
169 let mut ht = *a;
170 let mut t = *a;
171 for _ in 0..141 {
172 t = t.square();
173 t = t.square();
174 ht = ht.add(&t);
175 }
176 ht
177 }
178
179 pub fn add(&self, other: &Self) -> Self {
183 let x_eq = self.x == other.x;
185 let y_eq = self.y == other.y;
186
187 let p_eq_q = Choice::from((x_eq && y_eq) as u8);
189 let p_eq_neg_q = Choice::from((x_eq && !y_eq) as u8);
191
192 let sum_y = self.y.add(&other.y);
195 let sum_x = self.x.add(&other.x);
196
197 let sum_x_is_zero = Choice::from(sum_x.is_zero() as u8);
200 let denom = FieldElement::conditional_select(&sum_x, &FieldElement::one(), sum_x_is_zero);
201 let inv_denom = denom.invert().unwrap_or(FieldElement::zero());
202
203 let lambda = sum_y.mul(&inv_denom);
204 let x3 = lambda.square().add(&lambda).add(&self.x).add(&other.x);
205 let y3 = lambda.mul(&(self.x.add(&x3))).add(&x3).add(&self.y);
206
207 let generic = Point {
208 is_identity: Choice::from(0),
209 x: x3,
210 y: y3,
211 };
212
213 let double = self.double();
215
216 let mut result = Self::conditional_select(&generic, &double, p_eq_q);
219
220 result = Self::conditional_select(&result, &Self::identity(), p_eq_neg_q);
222
223 result = Self::conditional_select(&result, other, self.is_identity);
225 result = Self::conditional_select(&result, self, other.is_identity);
226
227 result
228 }
229
230 pub fn double(&self) -> Self {
234 let x_is_zero = Choice::from(self.x.is_zero() as u8);
237 let denom = FieldElement::conditional_select(&self.x, &FieldElement::one(), x_is_zero);
238 let inv_x = denom.invert().unwrap_or(FieldElement::zero());
239
240 let term = self.y.mul(&inv_x);
241 let lambda = self.x.add(&term);
242
243 let x2 = lambda.square().add(&lambda);
244 let y2 = self.x.square().add(&lambda.mul(&x2)).add(&x2);
245
246 let result = Point {
247 is_identity: Choice::from(0),
248 x: x2,
249 y: y2,
250 };
251
252 Self::conditional_select(&result, &Self::identity(), self.is_identity)
253 }
254
255 pub fn mul(&self, scalar: &Scalar) -> Result<Self> {
259 if scalar.is_zero() {
260 return Ok(Self::identity());
261 }
262 let scalar_bytes = scalar.as_secret_buffer().as_ref();
263 let mut res = Self::identity();
264 let mut temp = self.clone();
265
266 for byte in scalar_bytes.iter().rev() {
267 for i in 0..8 {
268 let bit = (byte >> i) & 1;
269 let choice = Choice::from(bit);
270
271 let res_added = res.add(&temp);
273
274 res = Point::conditional_select(&res, &res_added, choice);
276
277 temp = temp.double();
279 }
280 }
281 Ok(res)
282 }
283
284 fn is_on_curve(x: &FieldElement, y: &FieldElement) -> bool {
285 let y_sq = y.square();
286 let xy = x.mul(y);
287 let lhs = y_sq.add(&xy);
288
289 let x_cubed = x.square().mul(x);
290 let rhs = x_cubed.add(&FieldElement::one());
291
292 lhs == rhs
293 }
294}