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;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum PointFormat {
14 Identity,
16 Uncompressed,
18 Compressed,
20}
21
22#[derive(Clone, 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 Point {
46 pub fn new_uncompressed(
50 x: &[u8; B283K_FIELD_ELEMENT_SIZE],
51 y: &[u8; B283K_FIELD_ELEMENT_SIZE],
52 ) -> Result<Self> {
53 let x_fe = FieldElement::from_bytes(x)?;
54 let y_fe = FieldElement::from_bytes(y)?;
55 if !Self::is_on_curve(&x_fe, &y_fe) {
56 return Err(Error::param(
57 "B283k Point",
58 "Point coordinates do not satisfy curve equation",
59 ));
60 }
61 Ok(Point {
62 is_identity: Choice::from(0),
63 x: x_fe,
64 y: y_fe,
65 })
66 }
67
68 pub fn identity() -> Self {
70 Point {
71 is_identity: Choice::from(1),
72 x: FieldElement::zero(),
73 y: FieldElement::zero(),
74 }
75 }
76
77 pub fn is_identity(&self) -> bool {
79 self.is_identity.into()
80 }
81
82 pub fn x_coordinate_bytes(&self) -> [u8; B283K_FIELD_ELEMENT_SIZE] {
84 self.x.to_bytes()
85 }
86
87 pub fn y_coordinate_bytes(&self) -> [u8; B283K_FIELD_ELEMENT_SIZE] {
89 self.y.to_bytes()
90 }
91
92 pub fn serialize_compressed(&self) -> [u8; B283K_POINT_COMPRESSED_SIZE] {
96 let mut out = [0u8; B283K_POINT_COMPRESSED_SIZE];
97 if self.is_identity() {
98 return out;
99 }
100
101 let y_tilde = self.x.invert().unwrap().mul(&self.y).trace();
102 out[0] = if y_tilde == 1 { 0x03 } else { 0x02 };
103 out[1..].copy_from_slice(&self.x.to_bytes());
104 out
105 }
106
107 pub fn deserialize_compressed(bytes: &[u8]) -> Result<Self> {
112 validate::length(
113 "B283k Compressed Point",
114 bytes.len(),
115 B283K_POINT_COMPRESSED_SIZE,
116 )?;
117 if bytes.iter().all(|&b| b == 0) {
118 return Ok(Self::identity());
119 }
120 let tag = bytes[0];
121 if tag != 0x02 && tag != 0x03 {
122 return Err(Error::param(
123 "B283k Point",
124 "Invalid compressed point prefix",
125 ));
126 }
127 let mut x_bytes = [0u8; B283K_FIELD_ELEMENT_SIZE];
128 x_bytes.copy_from_slice(&bytes[1..]);
129 let x = FieldElement::from_bytes(&x_bytes)?;
130 if x.is_zero() {
131 return Ok(Point {
132 is_identity: Choice::from(0),
133 x,
134 y: FieldElement::one().sqrt(),
135 });
136 }
137
138 let rhs = x.add(&x.square().invert().unwrap());
142
143 if rhs.trace() != 0 {
145 return Err(Error::param("B283k Point", "Cannot decompress point"));
146 }
147
148 let mut z = Self::half_trace(&rhs);
150
151 if z.trace() != (tag as u64 - 2) {
153 z = z.add(&FieldElement::one());
154 }
155
156 let y = x.mul(&z);
157 Ok(Point {
158 is_identity: Choice::from(0),
159 x,
160 y,
161 })
162 }
163
164 fn half_trace(a: &FieldElement) -> FieldElement {
169 let mut ht = *a; let mut t = *a;
172 for _ in 0..141 {
173 t = t.square(); t = t.square(); ht = ht.add(&t); }
177 ht
178 }
179
180 pub fn add(&self, other: &Self) -> Self {
182 if self.is_identity() {
183 return other.clone();
184 }
185 if other.is_identity() {
186 return self.clone();
187 }
188
189 if self.x == other.x {
190 if self.y == other.y {
191 return self.double();
192 } else {
193 return Self::identity();
194 }
195 }
196
197 let lambda = (self.y.add(&other.y)).mul(&(self.x.add(&other.x)).invert().unwrap());
198 let x3 = lambda.square().add(&lambda).add(&self.x).add(&other.x);
199 let y3 = lambda.mul(&(self.x.add(&x3))).add(&x3).add(&self.y);
200 Point {
201 is_identity: Choice::from(0),
202 x: x3,
203 y: y3,
204 }
205 }
206
207 pub fn double(&self) -> Self {
209 if self.is_identity() || self.x.is_zero() {
210 return Self::identity();
211 }
212
213 let lambda = self.x.add(&self.y.mul(&self.x.invert().unwrap()));
214 let x2 = lambda.square().add(&lambda);
215 let y2 = self.x.square().add(&lambda.mul(&x2)).add(&x2);
216 Point {
217 is_identity: Choice::from(0),
218 x: x2,
219 y: y2,
220 }
221 }
222
223 pub fn mul(&self, scalar: &Scalar) -> Result<Self> {
227 if scalar.is_zero() {
228 return Ok(Self::identity());
229 }
230 let scalar_bytes = scalar.as_secret_buffer().as_ref();
231 let mut res = Self::identity();
232 let mut temp = self.clone();
233
234 for byte in scalar_bytes.iter().rev() {
235 for i in 0..8 {
236 if (byte >> i) & 1 == 1 {
237 res = res.add(&temp);
238 }
239 temp = temp.double();
240 }
241 }
242 Ok(res)
243 }
244
245 fn is_on_curve(x: &FieldElement, y: &FieldElement) -> bool {
246 let y_sq = y.square();
248 let xy = x.mul(y);
249 let lhs = y_sq.add(&xy);
250
251 let x_cubed = x.square().mul(x);
252 let rhs = x_cubed.add(&FieldElement::one());
253
254 lhs == rhs
255 }
256}