confium_tc_frost_p256/
scalar.rs1use p256::elliptic_curve::subtle::CtOption;
7use p256::elliptic_curve::{Field, PrimeField};
8use p256::{FieldBytes, Scalar};
9use std::ops::Mul;
10
11pub fn scalar_from_bytes(bytes: &[u8]) -> Option<Scalar> {
14 if bytes.len() != 32 {
15 return None;
16 }
17 let mut arr = [0u8; 32];
18 arr.copy_from_slice(bytes);
19 let fb = FieldBytes::from(arr);
20 let ct: CtOption<Scalar> = Scalar::from_repr(fb);
21 Option::<Scalar>::from(ct)
22}
23
24pub fn scalar_to_bytes(s: &Scalar) -> [u8; 32] {
26 let fb: FieldBytes = s.to_bytes();
27 fb.into()
28}
29
30pub fn scalar_add(a: &Scalar, b: &Scalar) -> Scalar {
32 a.add(b)
33}
34
35pub fn scalar_mul(a: &Scalar, b: &Scalar) -> Scalar {
37 a.mul(b)
38}
39
40pub fn scalar_sub(a: &Scalar, b: &Scalar) -> Scalar {
42 a.sub(b)
43}
44
45pub fn scalar_invert(a: &Scalar) -> Scalar {
47 let ct: CtOption<Scalar> = a.invert();
50 Option::<Scalar>::from(ct).unwrap_or(Scalar::ZERO)
51}
52
53pub fn random_scalar() -> Scalar {
55 Scalar::random(&mut p256::elliptic_curve::rand_core::UnwrapErr(
56 getrandom::SysRng,
57 ))
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn scalar_round_trip() {
66 let s = random_scalar();
67 let bytes = scalar_to_bytes(&s);
68 let recovered = scalar_from_bytes(&bytes).unwrap();
69 assert_eq!(s, recovered);
70 }
71
72 #[test]
73 fn scalar_add_inverse_of_sub() {
74 let a = random_scalar();
75 let b = random_scalar();
76 let sum = scalar_add(&a, &b);
77 let back = scalar_sub(&sum, &b);
78 assert_eq!(back, a);
79 }
80
81 #[test]
82 fn scalar_invert_mul_is_identity() {
83 let a = random_scalar();
84 if a == Scalar::ZERO {
85 return;
86 }
87 let inv = scalar_invert(&a);
88 let product = scalar_mul(&a, &inv);
89 assert_eq!(product, Scalar::ONE);
90 }
91
92 #[test]
93 fn rejects_wrong_length_bytes() {
94 assert!(scalar_from_bytes(&[0u8; 16]).is_none());
95 }
96}