1use k256::elliptic_curve::group::{Group, GroupEncoding};
7use k256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
8use k256::{AffinePoint, ProjectivePoint, Scalar};
9
10use crate::prelude::{Bigint, ByteString, Point};
11
12fn point_to_projective(p: &[u8]) -> ProjectivePoint {
14 assert_eq!(p.len(), 64, "Point must be exactly 64 bytes");
15
16 if p.iter().all(|&b| b == 0) {
18 return ProjectivePoint::IDENTITY;
19 }
20
21 let mut sec1 = vec![0x04u8];
23 sec1.extend_from_slice(p);
24 let encoded = k256::EncodedPoint::from_bytes(&sec1)
25 .expect("invalid SEC1 encoding");
26 let affine = AffinePoint::from_encoded_point(&encoded)
27 .expect("point not on curve");
28 ProjectivePoint::from(affine)
29}
30
31fn projective_to_point(p: &ProjectivePoint) -> Point {
33 if p.is_identity().into() {
34 return vec![0u8; 64];
35 }
36 let affine = p.to_affine();
37 let encoded = affine.to_encoded_point(false); let bytes = encoded.as_bytes(); bytes[1..65].to_vec()
40}
41
42fn i64_to_scalar(k: Bigint) -> Scalar {
44 if k >= 0 {
45 Scalar::from(k as u64)
46 } else {
47 Scalar::ZERO - Scalar::from((-k) as u64)
49 }
50}
51
52pub fn ec_add(a: &[u8], b: &[u8]) -> Point {
54 let pa = point_to_projective(a);
55 let pb = point_to_projective(b);
56 projective_to_point(&(pa + pb))
57}
58
59pub fn ec_mul(p: &[u8], k: Bigint) -> Point {
61 let pp = point_to_projective(p);
62 let s = i64_to_scalar(k);
63 projective_to_point(&(pp * s))
64}
65
66pub fn ec_mul_gen(k: Bigint) -> Point {
68 let s = i64_to_scalar(k);
69 projective_to_point(&(ProjectivePoint::GENERATOR * s))
70}
71
72pub fn ec_negate(p: &[u8]) -> Point {
74 let pp = point_to_projective(p);
75 projective_to_point(&(-pp))
76}
77
78pub fn ec_on_curve(p: &[u8]) -> bool {
80 if p.len() != 64 {
81 return false;
82 }
83 if p.iter().all(|&b| b == 0) {
85 return true;
86 }
87 let mut sec1 = vec![0x04u8];
88 sec1.extend_from_slice(p);
89 let Ok(enc) = k256::EncodedPoint::from_bytes(&sec1) else { return false };
90 let ct = AffinePoint::from_encoded_point(&enc);
91 ct.is_some().into()
92}
93
94pub fn ec_mod_reduce(value: Bigint, m: Bigint) -> Bigint {
96 let r = value % m;
97 if r < 0 { r + m } else { r }
98}
99
100pub fn ec_encode_compressed(p: &[u8]) -> ByteString {
102 let pp = point_to_projective(p);
103 let affine = pp.to_affine();
104 affine.to_bytes().to_vec()
105}
106
107pub fn ec_make_point(x: Bigint, y: Bigint) -> Point {
109 let mut buf = vec![0u8; 64];
110 let xb = (x as u64).to_be_bytes();
111 let yb = (y as u64).to_be_bytes();
112 buf[24..32].copy_from_slice(&xb);
113 buf[56..64].copy_from_slice(&yb);
114 buf
115}
116
117pub fn ec_point_x(p: &[u8]) -> Bigint {
120 assert_eq!(p.len(), 64, "Point must be exactly 64 bytes");
121 let mut bytes = [0u8; 8];
123 bytes.copy_from_slice(&p[24..32]);
124 u64::from_be_bytes(bytes) as i64
125}
126
127pub fn ec_point_y(p: &[u8]) -> Bigint {
130 assert_eq!(p.len(), 64, "Point must be exactly 64 bytes");
131 let mut bytes = [0u8; 8];
132 bytes.copy_from_slice(&p[56..64]);
133 u64::from_be_bytes(bytes) as i64
134}
135
136#[cfg(test)]
141mod tests {
142 use super::*;
143
144 fn ec_g() -> Point {
146 ec_mul_gen(1)
147 }
148
149 #[test]
150 fn ec_g_is_64_bytes() {
151 assert_eq!(ec_g().len(), 64);
152 }
153
154 #[test]
155 fn ec_g_is_on_curve() {
156 assert!(ec_on_curve(&ec_g()));
157 }
158
159 #[test]
160 fn ec_add_g_g_equals_ec_mul_g_2() {
161 let g = ec_g();
162 let sum = ec_add(&g, &g);
163 let doubled = ec_mul(&g, 2);
164 assert_eq!(sum, doubled);
165 }
166
167 #[test]
168 fn ec_add_g_g_equals_ec_mul_gen_2() {
169 let g = ec_g();
170 let sum = ec_add(&g, &g);
171 let gen2 = ec_mul_gen(2);
172 assert_eq!(sum, gen2);
173 }
174
175 #[test]
176 fn ec_mul_gen_1_equals_g() {
177 let g = ec_g();
178 let gen1 = ec_mul_gen(1);
179 assert_eq!(gen1, g);
180 }
181
182 #[test]
183 fn ec_negate_produces_on_curve_point() {
184 let g = ec_g();
185 let neg = ec_negate(&g);
186 assert_eq!(neg.len(), 64);
187 assert!(ec_on_curve(&neg));
188 assert_ne!(neg, g);
190 }
191
192 #[test]
193 fn ec_negate_double_negate_is_identity() {
194 let g = ec_g();
195 let double_neg = ec_negate(&ec_negate(&g));
196 assert_eq!(double_neg, g);
197 }
198
199 #[test]
200 fn ec_add_point_and_negation_is_identity() {
201 let g = ec_g();
202 let neg = ec_negate(&g);
203 let sum = ec_add(&g, &neg);
204 assert_eq!(sum, vec![0u8; 64]);
206 }
207
208 #[test]
209 fn ec_make_point_round_trip() {
210 let x: Bigint = 12345;
211 let y: Bigint = 67890;
212 let p = ec_make_point(x, y);
213 assert_eq!(p.len(), 64);
214 assert_eq!(ec_point_x(&p), x);
215 assert_eq!(ec_point_y(&p), y);
216 }
217
218 #[test]
219 fn ec_encode_compressed_produces_33_bytes() {
220 let g = ec_g();
221 let compressed = ec_encode_compressed(&g);
222 assert_eq!(compressed.len(), 33);
223 assert!(compressed[0] == 0x02 || compressed[0] == 0x03);
225 }
226
227 #[test]
228 fn ec_on_curve_rejects_invalid_point() {
229 let bad_point = vec![0xffu8; 64];
231 assert!(!ec_on_curve(&bad_point));
232 }
233
234 #[test]
235 fn ec_on_curve_rejects_wrong_length() {
236 assert!(!ec_on_curve(&[0u8; 32]));
237 }
238
239 #[test]
240 fn ec_on_curve_accepts_identity() {
241 assert!(ec_on_curve(&vec![0u8; 64]));
242 }
243
244 #[test]
245 fn ec_mod_reduce_basic() {
246 assert_eq!(ec_mod_reduce(10, 3), 1);
247 assert_eq!(ec_mod_reduce(-1, 5), 4);
248 assert_eq!(ec_mod_reduce(0, 7), 0);
249 }
250
251 #[test]
252 fn ec_mul_associative() {
253 let g = ec_g();
255 let g3 = ec_mul(&g, 3);
256 let g3x2 = ec_mul(&g3, 2);
257 let g6 = ec_mul_gen(6);
258 assert_eq!(g3x2, g6);
259 }
260}