Skip to main content

confium_tc_frost_p256/
scalar.rs

1//! P-256 scalar field helpers.
2//!
3//! Wraps `p256::Scalar` operations needed for Shamir secret sharing
4//! and Lagrange interpolation.
5
6use p256::elliptic_curve::subtle::CtOption;
7use p256::elliptic_curve::{Field, PrimeField};
8use p256::{FieldBytes, Scalar};
9use std::ops::Mul;
10
11/// Convert raw big-endian bytes (32 bytes) into a `Scalar`.
12/// Returns None if bytes are not 32 long or if the value is out of range.
13pub 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
24/// Convert a `Scalar` to 32 big-endian bytes.
25pub fn scalar_to_bytes(s: &Scalar) -> [u8; 32] {
26    let fb: FieldBytes = s.to_bytes();
27    fb.into()
28}
29
30/// Add two scalars.
31pub fn scalar_add(a: &Scalar, b: &Scalar) -> Scalar {
32    a.add(b)
33}
34
35/// Multiply two scalars.
36pub fn scalar_mul(a: &Scalar, b: &Scalar) -> Scalar {
37    a.mul(b)
38}
39
40/// Subtract one scalar from another.
41pub fn scalar_sub(a: &Scalar, b: &Scalar) -> Scalar {
42    a.sub(b)
43}
44
45/// Modular inverse of a scalar (1/a mod n). Returns ZERO if `a` is zero.
46pub fn scalar_invert(a: &Scalar) -> Scalar {
47    // Garbage-in-garbage-out on zero input; protocol callers pass
48    // non-zero scalars (sweep ledger: SEC-audit-notes).
49    let ct: CtOption<Scalar> = a.invert();
50    Option::<Scalar>::from(ct).unwrap_or(Scalar::ZERO)
51}
52
53/// Generate a random scalar.
54pub 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}