fluentbase_runtime/syscall_handler/weierstrass/
weierstrass_decompress.rs1use crate::syscall_handler::syscall_process_exit_code;
3use crate::RuntimeContext;
4use amcl::bls381::bls381::utils::deserialize_g1;
5use fluentbase_types::{
6 ExitCode, BLS12381_G1_COMPRESSED_SIZE, BLS12381_G1_RAW_AFFINE_SIZE,
7 SECP256K1_G1_COMPRESSED_SIZE, SECP256K1_G1_RAW_AFFINE_SIZE, SECP256R1_G1_COMPRESSED_SIZE,
8 SECP256R1_G1_RAW_AFFINE_SIZE,
9};
10use k256::elliptic_curve::{point::DecompressPoint, sec1::ToEncodedPoint, subtle::Choice};
11use num::{BigUint, Num};
12use rwasm::{StoreTr, TrapCode, Value};
13use sp1_curves::{
14 weierstrass::{bls12_381::Bls12381, secp256k1::Secp256k1, secp256r1::Secp256r1},
15 AffinePoint, CurveType, EllipticCurve,
16};
17
18pub fn syscall_secp256k1_decompress_handler(
19 ctx: &mut impl StoreTr<RuntimeContext>,
20 params: &[Value],
21 _result: &mut [Value],
22) -> Result<(), TrapCode> {
23 syscall_weierstrass_decompress_handler::<
24 Secp256k1,
25 { SECP256K1_G1_COMPRESSED_SIZE },
26 { SECP256K1_G1_RAW_AFFINE_SIZE },
27 >(ctx, params, _result)
28}
29pub fn syscall_secp256r1_decompress_handler(
30 ctx: &mut impl StoreTr<RuntimeContext>,
31 params: &[Value],
32 _result: &mut [Value],
33) -> Result<(), TrapCode> {
34 syscall_weierstrass_decompress_handler::<
35 Secp256r1,
36 { SECP256R1_G1_COMPRESSED_SIZE },
37 { SECP256R1_G1_RAW_AFFINE_SIZE },
38 >(ctx, params, _result)
39}
40pub fn syscall_bls12381_decompress_handler(
41 ctx: &mut impl StoreTr<RuntimeContext>,
42 params: &[Value],
43 _result: &mut [Value],
44) -> Result<(), TrapCode> {
45 syscall_weierstrass_decompress_handler::<
46 Bls12381,
47 { BLS12381_G1_COMPRESSED_SIZE },
48 { BLS12381_G1_RAW_AFFINE_SIZE },
49 >(ctx, params, _result)
50}
51
52fn syscall_weierstrass_decompress_handler<
53 E: EllipticCurve,
54 const COMPRESSED_SIZE: usize,
55 const DECOMPRESSED_SIZE: usize,
56>(
57 ctx: &mut impl StoreTr<RuntimeContext>,
58 params: &[Value],
59 _result: &mut [Value],
60) -> Result<(), TrapCode> {
61 let (yx_ptr, sign_bit) = (
62 params[0].i32().unwrap() as usize,
63 params[1].i32().unwrap() as u32,
64 );
65
66 let mut x_bytes_le = [0u8; COMPRESSED_SIZE];
67 ctx.memory_read(yx_ptr + COMPRESSED_SIZE, &mut x_bytes_le)?;
68
69 let yx_bytes_le = syscall_weierstrass_decompress_impl::<E, COMPRESSED_SIZE, DECOMPRESSED_SIZE>(
70 x_bytes_le, sign_bit,
71 )
72 .map_err(|exit_code| syscall_process_exit_code(ctx, exit_code))?;
73
74 ctx.memory_write(yx_ptr, &yx_bytes_le)?;
75 Ok(())
76}
77
78pub fn syscall_secp256k1_decompress_impl(
92 x_bytes_le: [u8; SECP256K1_G1_COMPRESSED_SIZE],
93 sign_bit: u32,
94) -> Result<[u8; SECP256K1_G1_RAW_AFFINE_SIZE], ExitCode> {
95 syscall_weierstrass_decompress_impl::<
96 Secp256k1,
97 { SECP256K1_G1_COMPRESSED_SIZE },
98 { SECP256K1_G1_RAW_AFFINE_SIZE },
99 >(x_bytes_le, sign_bit)
100}
101
102pub fn syscall_secp256r1_decompress_impl(
116 x_bytes_le: [u8; SECP256R1_G1_COMPRESSED_SIZE],
117 sign_bit: u32,
118) -> Result<[u8; SECP256R1_G1_RAW_AFFINE_SIZE], ExitCode> {
119 syscall_weierstrass_decompress_impl::<
120 Secp256r1,
121 { SECP256R1_G1_COMPRESSED_SIZE },
122 { SECP256R1_G1_RAW_AFFINE_SIZE },
123 >(x_bytes_le, sign_bit)
124}
125
126pub fn syscall_bls12381_decompress_impl(
140 x_bytes_le: [u8; BLS12381_G1_COMPRESSED_SIZE],
141 sign_bit: u32,
142) -> Result<[u8; BLS12381_G1_RAW_AFFINE_SIZE], ExitCode> {
143 syscall_weierstrass_decompress_impl::<
144 Bls12381,
145 { BLS12381_G1_COMPRESSED_SIZE },
146 { BLS12381_G1_RAW_AFFINE_SIZE },
147 >(x_bytes_le, sign_bit)
148}
149
150fn syscall_weierstrass_decompress_impl<
151 E: EllipticCurve,
152 const COMPRESSED_SIZE: usize,
153 const DECOMPRESSED_SIZE: usize,
154>(
155 x_bytes_le: [u8; COMPRESSED_SIZE],
156 sign_bit: u32,
157) -> Result<[u8; DECOMPRESSED_SIZE], ExitCode> {
158 if sign_bit > 1 {
159 return Err(ExitCode::MalformedBuiltinParams);
160 }
161
162 let computed_point = match E::CURVE_TYPE {
167 CurveType::Secp256k1 => {
168 let mut x_bytes_be = x_bytes_le;
169 x_bytes_be.reverse();
170 let computed_point = k256::AffinePoint::decompress(
171 x_bytes_be.as_slice().into(),
172 Choice::from(sign_bit as u8),
173 );
174 if computed_point.is_none().into() {
175 return Err(ExitCode::MalformedBuiltinParams);
176 }
177 let point = computed_point.unwrap().to_encoded_point(false);
178 let x = BigUint::from_bytes_be(point.x().unwrap());
179 let y = BigUint::from_bytes_be(point.y().unwrap());
180 AffinePoint::<E>::new(x, y)
181 }
182 CurveType::Secp256r1 => {
183 let mut x_bytes_be = x_bytes_le;
184 x_bytes_be.reverse();
185 let computed_point = p256::AffinePoint::decompress(
186 x_bytes_be.as_slice().into(),
187 Choice::from(sign_bit as u8),
188 );
189 if computed_point.is_none().into() {
190 return Err(ExitCode::MalformedBuiltinParams);
191 }
192 let point = computed_point.unwrap().to_encoded_point(false);
193 let x = BigUint::from_bytes_be(point.x().unwrap());
194 let y = BigUint::from_bytes_be(point.y().unwrap());
195 AffinePoint::<E>::new(x, y)
196 }
197 CurveType::Bls12381 => {
198 let mut g1_bytes_be = x_bytes_le;
199 g1_bytes_be.reverse();
200
201 const COMPRESSION_FLAG: u8 = 0b_1000_0000;
202 const Y_IS_ODD_FLAG: u8 = 0b_0010_0000;
203
204 let mut flags = COMPRESSION_FLAG;
205 if sign_bit == 1 {
206 flags |= Y_IS_ODD_FLAG;
207 };
208
209 g1_bytes_be[0] |= flags;
211 let point =
212 deserialize_g1(&g1_bytes_be).map_err(|_| ExitCode::MalformedBuiltinParams)?;
213
214 let x_str = point.getx().to_string();
217 let x = BigUint::from_str_radix(x_str.as_str(), 16).unwrap();
218 let y_str = point.gety().to_string();
219 let y = BigUint::from_str_radix(y_str.as_str(), 16).unwrap();
220
221 AffinePoint::new(x, y)
222 }
223 _ => panic!("unsupported curve: {}", E::CURVE_TYPE),
224 };
225
226 let y_bytes_le = computed_point.y.to_bytes_le();
227
228 let mut result_bytes_le = [0u8; DECOMPRESSED_SIZE];
229 let (result_y, result_x) = result_bytes_le.split_at_mut(DECOMPRESSED_SIZE / 2);
230 result_y[..y_bytes_le.len()].clone_from_slice(&y_bytes_le);
231 result_x[..x_bytes_le.len()].clone_from_slice(&x_bytes_le);
232 Ok(result_bytes_le)
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use amcl::{
239 bls381::bls381::{basic::key_pair_generate_g2, utils::deserialize_g1},
240 rand::RAND,
241 };
242 use fluentbase_types::hex;
243 use rand::{rng, Rng, RngCore};
244
245 pub fn p256_decompress(compressed_key: &[u8]) -> [u8; SECP256R1_G1_RAW_AFFINE_SIZE] {
246 assert_eq!(compressed_key.len(), 33);
247 let is_odd = match compressed_key[0] {
248 2 => false,
249 3 => true,
250 _ => unreachable!(),
251 };
252 let mut compressed_key: [u8; 32] = compressed_key[1..].try_into().unwrap();
253 compressed_key.reverse();
254 let mut result: [u8; 64] =
255 syscall_secp256r1_decompress_impl(compressed_key, is_odd as u32).unwrap();
256 result.reverse();
257 result
258 }
259
260 #[test]
265 fn test_secp256r1_sp1_decompress() {
266 let mut rng = rng();
267 for _ in 0..100 {
268 let mut random_private_key = [0u8; 32];
269 rng.fill_bytes(&mut random_private_key);
270 let secret_key = p256::SecretKey::from_slice(&random_private_key).unwrap();
271 let public_key = secret_key.public_key();
272 let decompressed = public_key.to_encoded_point(false).to_bytes();
273 let compressed = public_key.to_encoded_point(true).to_bytes();
274 let result = p256_decompress(&compressed);
275 assert_eq!(hex::encode(result), hex::encode(&decompressed[1..]));
276 }
277 }
278
279 #[test]
280 #[should_panic(
281 expected = "called `Result::unwrap()` on an `Err` value: MalformedBuiltinParams"
282 )]
283 fn test_secp256r1_bad_point() {
284 let mut point: [u8; 33] = [0xff; 33];
285 point[0] = 0x03;
286 let _ = p256_decompress(&point);
287 }
288
289 pub fn k256_decompress(compressed_key: &[u8]) -> [u8; SECP256K1_G1_RAW_AFFINE_SIZE] {
290 assert_eq!(compressed_key.len(), 33);
291 let is_odd = match compressed_key[0] {
292 2 => false,
293 3 => true,
294 _ => panic!("Invalid compressed key"),
295 };
296 let mut compressed_key: [u8; 32] = compressed_key[1..].try_into().unwrap();
297 compressed_key.reverse();
298 let mut result: [u8; 64] =
299 syscall_secp256k1_decompress_impl(compressed_key, is_odd as u32).unwrap();
300 result.reverse();
301 result
302 }
303
304 #[test]
305 fn test_secp256k1_sp1_decompress() {
306 let mut rng = rng();
307 for _ in 0..100 {
308 let mut random_private_key = [0u8; 32];
309 rng.fill_bytes(&mut random_private_key);
310 let secret_key = k256::SecretKey::from_slice(&random_private_key).unwrap();
311 let public_key = secret_key.public_key();
312 let decompressed = public_key.to_encoded_point(false).to_bytes();
313 let compressed = public_key.to_encoded_point(true).to_bytes();
314 let result = k256_decompress(&compressed);
315 assert_eq!(hex::encode(result), hex::encode(&decompressed[1..]));
316 }
317 }
318
319 #[test]
320 #[should_panic(
321 expected = "called `Result::unwrap()` on an `Err` value: MalformedBuiltinParams"
322 )]
323 fn test_secp256k1_bad_point() {
324 let mut point: [u8; 33] = [0xff; 33];
325 point[0] = 0x03;
326 let _ = k256_decompress(&point);
327 }
328
329 pub fn bls12381_decompress(
330 compressed_key: [u8; BLS12381_G1_COMPRESSED_SIZE],
331 ) -> [u8; BLS12381_G1_RAW_AFFINE_SIZE] {
332 let mut compressed_key_unsigned = [0u8; BLS12381_G1_COMPRESSED_SIZE];
333 compressed_key_unsigned.copy_from_slice(&compressed_key);
334 let sign_bit = ((compressed_key_unsigned[0] & 0b_0010_0000) >> 5) == 1;
335 compressed_key_unsigned[0] &= 0b_0001_1111;
336 compressed_key_unsigned.reverse();
337 let mut result: [u8; 96] =
338 syscall_bls12381_decompress_impl(compressed_key_unsigned, sign_bit as u32).unwrap();
339 result.reverse();
340 result
341 }
342
343 #[test]
344 fn test_bls12381_sp1_decompress() {
345 let mut rng = rng();
346 let mut rand = RAND::new();
347 let len = 100;
348 let num_tests = 10;
349 let random_slice = (0..len).map(|_| rng.random::<u8>()).collect::<Vec<u8>>();
350 rand.seed(len, &random_slice);
351 for _ in 0..num_tests {
352 let (_, compressed) = key_pair_generate_g2(&mut rand);
353 let point = deserialize_g1(&compressed).unwrap();
354 let x = point.getx().to_string();
355 let y = point.gety().to_string();
356 let result = bls12381_decompress(compressed);
357 assert_eq!(
358 hex::encode(result).to_lowercase(),
359 format!("{x}{y}").to_lowercase()
360 );
361 }
362 }
363
364 #[test]
365 #[should_panic(
366 expected = "called `Result::unwrap()` on an `Err` value: MalformedBuiltinParams"
367 )]
368 fn test_bls12381_bad_point() {
369 let point: [u8; 48] = [0xff; 48];
370 let _ = bls12381_decompress(point);
371 }
372}