p256/
p256.rs

1use hacspec_lib::{hacspec_helper::*, i2osp, Randomness};
2use hmac::{hkdf_expand, hkdf_extract};
3
4#[derive(Debug)]
5pub enum Error {
6    InvalidAddition,
7    DeserializeError,
8    PointAtInfinity,
9    SamplingError,
10}
11
12impl From<hacspec_lib::Error> for Error {
13    fn from(_value: hacspec_lib::Error) -> Self {
14        Self::SamplingError
15    }
16}
17
18const BITS: u128 = 256;
19
20#[derive(Hash, PartialOrd, Ord)]
21#[nat_mod("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 32)]
22pub struct P256FieldElement {}
23
24#[nat_mod("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 32)]
25pub struct P256Scalar {}
26
27/// Generate a random P256 scalar using rejection sampling.
28///
29/// Inputs:
30/// - `randomness`: Random bytes
31/// - `dst`: Domain separation tag
32///
33/// Outputs:
34/// - A random P256 scalar
35///
36/// Raises:
37/// - `SamplingError`: If no valid scalar can be found within 256 sampling attempts
38///
39/// Panics:
40/// - If the provided random bytes are insufficient
41pub fn random_scalar(randomness: &mut Randomness, dst: &[u8]) -> Result<P256Scalar, Error> {
42    let dkp_prk = hkdf_extract(dst, randomness.bytes(32).unwrap());
43
44    let mut sk = P256Scalar::zero();
45
46    for counter in 0..255 {
47        let mut bytes = hkdf_expand(&dkp_prk, &i2osp(counter, 1), 32);
48
49        bytes[0] &= 0xffu8;
50        if p256_validate_private_key(&bytes) {
51            sk = P256Scalar::from_be_bytes(&bytes);
52        }
53    }
54    if sk == P256Scalar::zero() {
55        Err(Error::SamplingError)
56    } else {
57        Ok(sk)
58    }
59}
60
61pub type Affine = (P256FieldElement, P256FieldElement);
62pub type AffineResult = Result<Affine, Error>;
63type P256Jacobian = (P256FieldElement, P256FieldElement, P256FieldElement);
64type JacobianResult = Result<P256Jacobian, Error>;
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
67pub enum P256Point {
68    NonInf(Affine),
69    AtInfinity,
70}
71
72impl From<P256Point> for Affine {
73    fn from(value: P256Point) -> Self {
74        match value {
75            P256Point::AtInfinity => panic!("No affine representation of Point at Infinity."),
76            P256Point::NonInf(affine) => affine,
77        }
78    }
79}
80
81impl From<Affine> for P256Point {
82    fn from(value: Affine) -> Self {
83        P256Point::NonInf(value)
84    }
85}
86
87pub fn jacobian_to_affine(p: P256Jacobian) -> Affine {
88    let (x, y, z) = p;
89    let z2 = z.pow(2);
90    let z2i = z2.inv();
91    let z3 = z * z2;
92    let z3i = z3.inv();
93    let x = x * z2i;
94    let y = y * z3i;
95    (x, y)
96}
97
98impl std::ops::Neg for P256FieldElement {
99    type Output = P256FieldElement;
100
101    fn neg(self) -> Self::Output {
102        NatMod::neg(self)
103    }
104}
105
106pub fn is_square(x: &P256FieldElement) -> bool {
107    let exp = P256FieldElement::from_u128(1).neg() * P256FieldElement::from_u128(2).inv();
108    let test = x.pow_felem(&exp);
109    test == P256FieldElement::zero() || test == P256FieldElement::one()
110}
111
112pub fn sgn0(x: &P256FieldElement) -> bool {
113    x.bit(0)
114}
115
116pub fn sqrt(x: &P256FieldElement) -> P256FieldElement {
117    let c1 = P256FieldElement::one() * P256FieldElement::from_u128(4).inv();
118    x.pow_felem(&c1)
119}
120
121impl std::ops::Neg for P256Point {
122    type Output = P256Point;
123    fn neg(self) -> Self::Output {
124        match self {
125            P256Point::AtInfinity => self,
126            P256Point::NonInf((x, y)) => (x, NatMod::neg(y)).into(),
127        }
128    }
129}
130
131fn affine_to_jacobian(p: Affine) -> P256Jacobian {
132    let (x, y) = p;
133    (x, y, P256FieldElement::from_u128(1))
134}
135
136fn point_double(p: P256Jacobian) -> P256Jacobian {
137    let (x1, y1, z1) = p;
138    let delta = z1.pow(2);
139    let gamma = y1.pow(2);
140
141    let beta = x1 * gamma;
142
143    let alpha_1 = x1 - delta;
144    let alpha_2 = x1 + delta;
145    let alpha = P256FieldElement::from_u128(3) * (alpha_1 * alpha_2);
146
147    let x3 = alpha.pow(2) - (P256FieldElement::from_u128(8) * beta);
148
149    let z3_ = (y1 + z1).pow(2);
150    let z3 = z3_ - (gamma + delta);
151
152    let y3_1 = (P256FieldElement::from_u128(4) * beta) - x3;
153    let y3_2 = P256FieldElement::from_u128(8) * (gamma * gamma);
154    let y3 = (alpha * y3_1) - y3_2;
155    (x3, y3, z3)
156}
157
158fn is_point_at_infinity(p: P256Jacobian) -> bool {
159    let (_x, _y, z) = p;
160    z == P256FieldElement::from_u128(0)
161}
162
163fn s1_equal_s2(s1: P256FieldElement, s2: P256FieldElement) -> JacobianResult {
164    if s1 == s2 {
165        JacobianResult::Err(Error::InvalidAddition)
166    } else {
167        Ok((
168            P256FieldElement::from_u128(0),
169            P256FieldElement::from_u128(1),
170            P256FieldElement::from_u128(0),
171        ))
172    }
173}
174
175fn point_add_jacob(p: P256Jacobian, q: P256Jacobian) -> JacobianResult {
176    let mut result = Ok(q);
177    if !is_point_at_infinity(p) {
178        if is_point_at_infinity(q) {
179            result = Ok(p);
180        } else {
181            let (x1, y1, z1) = p;
182            let (x2, y2, z2) = q;
183            let z1z1 = z1.pow(2);
184            let z2z2 = z2.pow(2);
185            let u1 = x1 * z2z2;
186            let u2 = x2 * z1z1;
187            let s1 = (y1 * z2) * z2z2;
188            let s2 = (y2 * z1) * z1z1;
189
190            if u1 == u2 {
191                result = s1_equal_s2(s1, s2);
192            } else {
193                let h = u2 - u1;
194                let i = (P256FieldElement::from_u128(2) * h).pow(2);
195                let j = h * i;
196                let r = P256FieldElement::from_u128(2) * (s2 - s1);
197                let v = u1 * i;
198
199                let x3_1 = P256FieldElement::from_u128(2) * v;
200                let x3_2 = r.pow(2) - j;
201                let x3 = x3_2 - x3_1;
202
203                let y3_1 = (P256FieldElement::from_u128(2) * s1) * j;
204                let y3_2 = r * (v - x3);
205                let y3 = y3_2 - y3_1;
206
207                let z3_ = (z1 + z2).pow(2);
208                let z3 = (z3_ - (z1z1 + z2z2)) * h;
209                result = Ok((x3, y3, z3));
210            }
211        }
212    };
213    result
214}
215
216fn ltr_mul(k: P256Scalar, p: P256Jacobian) -> JacobianResult {
217    let mut q = (
218        P256FieldElement::from_u128(0),
219        P256FieldElement::from_u128(1),
220        P256FieldElement::from_u128(0),
221    );
222    for i in 0..BITS {
223        q = point_double(q);
224        if k.bit(BITS - 1 - i) {
225            q = point_add_jacob(q, p)?;
226        }
227    }
228    Ok(q)
229}
230
231pub type P256SerializedPoint = [u8; 33];
232
233/// SerializeElement(A): Implemented using the compressed Elliptic-
234///     Curve-Point-to-Octet-String method according to [SEC1]; Ne =
235///     33.
236///
237pub fn serialize_point(p: &P256Point) -> P256SerializedPoint {
238    let mut out = [0u8; 33];
239    match p {
240        P256Point::AtInfinity => out,
241        P256Point::NonInf((x, y)) => {
242            let x_serialized = x.to_be_bytes();
243
244            for (to, from) in out.iter_mut().skip(1).zip(x_serialized.iter()) {
245                *to = *from
246            }
247            out[0] = if y.bit(0) { 3 } else { 2 };
248
249            out
250        }
251    }
252}
253impl P256Point {
254    pub fn raw_bytes(&self) -> [u8; 64] {
255        match self {
256            P256Point::NonInf((x, y)) => {
257                let mut out = [0u8; 64];
258                out[0..32].copy_from_slice(&x.to_be_bytes());
259                out[32..64].copy_from_slice(&y.to_be_bytes());
260                out
261            }
262            P256Point::AtInfinity => panic!("Tried to serialize point at infitiy"),
263        }
264    }
265
266    pub fn from_raw_bytes(bytes: [u8; 64]) -> Result<P256Point, Error> {
267        let x = P256FieldElement::from_be_bytes(&bytes[0..32]);
268        let y = P256FieldElement::from_be_bytes(&bytes[32..64]);
269        let candidate = P256Point::NonInf((x, y));
270        if p256_validate_public_key(candidate) {
271            Ok(candidate)
272        } else {
273            Err(Error::DeserializeError)
274        }
275    }
276
277    pub fn x(&self) -> Result<P256FieldElement, Error> {
278        match self {
279            P256Point::NonInf((x, _)) => Ok(*x),
280            P256Point::AtInfinity => Err(Error::PointAtInfinity),
281        }
282    }
283    pub fn y(&self) -> Result<P256FieldElement, Error> {
284        match self {
285            P256Point::NonInf((_, y)) => Ok(*y),
286            P256Point::AtInfinity => Err(Error::PointAtInfinity),
287        }
288    }
289}
290
291#[allow(unused)]
292pub fn deserialize_point(pm: P256SerializedPoint) -> Result<P256Point, Error> {
293    if pm == [0u8; 33] {
294        return Err(Error::DeserializeError);
295    }
296
297    let x = P256FieldElement::from_be_bytes(&pm[1..33]);
298
299    let ym = pm[0];
300    let yp_sign: bool = match ym {
301        0x02 => false,
302        0x03 => true,
303        _ => return Err(Error::DeserializeError),
304    };
305
306    let a = P256FieldElement::from_u128(3u128).neg();
307    let b = P256FieldElement::from_hex(
308        "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
309    );
310
311    let alpha = x.pow(3) + a * x + b;
312    let beta = sqrt(&alpha);
313
314    let y: P256FieldElement = if beta.bit(0) == yp_sign {
315        beta
316    } else {
317        beta.neg()
318    };
319
320    Ok((x, y).into())
321}
322
323pub fn p256_point_mul(k: P256Scalar, p: P256Point) -> Result<P256Point, Error> {
324    let jac = ltr_mul(k, affine_to_jacobian(p.into()))?;
325    Ok(jacobian_to_affine(jac).into())
326}
327
328pub fn p256_point_mul_base(k: P256Scalar) -> Result<P256Point, Error> {
329    let base_point = (
330        P256FieldElement::from_be_bytes(&[
331            0x6Bu8, 0x17u8, 0xD1u8, 0xF2u8, 0xE1u8, 0x2Cu8, 0x42u8, 0x47u8, 0xF8u8, 0xBCu8, 0xE6u8,
332            0xE5u8, 0x63u8, 0xA4u8, 0x40u8, 0xF2u8, 0x77u8, 0x03u8, 0x7Du8, 0x81u8, 0x2Du8, 0xEBu8,
333            0x33u8, 0xA0u8, 0xF4u8, 0xA1u8, 0x39u8, 0x45u8, 0xD8u8, 0x98u8, 0xC2u8, 0x96u8,
334        ]),
335        P256FieldElement::from_be_bytes(&[
336            0x4Fu8, 0xE3u8, 0x42u8, 0xE2u8, 0xFEu8, 0x1Au8, 0x7Fu8, 0x9Bu8, 0x8Eu8, 0xE7u8, 0xEBu8,
337            0x4Au8, 0x7Cu8, 0x0Fu8, 0x9Eu8, 0x16u8, 0x2Bu8, 0xCEu8, 0x33u8, 0x57u8, 0x6Bu8, 0x31u8,
338            0x5Eu8, 0xCEu8, 0xCBu8, 0xB6u8, 0x40u8, 0x68u8, 0x37u8, 0xBFu8, 0x51u8, 0xF5u8,
339        ]),
340    )
341        .into();
342    p256_point_mul(k, base_point)
343}
344
345fn point_add_distinct(p: Affine, q: Affine) -> AffineResult {
346    let r = point_add_jacob(affine_to_jacobian(p), affine_to_jacobian(q))?;
347    Ok(jacobian_to_affine(r))
348}
349
350pub fn point_add(p: P256Point, q: P256Point) -> Result<P256Point, Error> {
351    match p {
352        P256Point::AtInfinity => Ok(q),
353        P256Point::NonInf(p) => match q {
354            P256Point::AtInfinity => Ok(P256Point::AtInfinity),
355            P256Point::NonInf(q) => point_add_noninf(p, q).map(P256Point::NonInf),
356        },
357    }
358}
359
360fn point_add_noninf(p: Affine, q: Affine) -> AffineResult {
361    if p != q {
362        point_add_distinct(p, q)
363    } else {
364        Ok(jacobian_to_affine(point_double(affine_to_jacobian(p))))
365    }
366}
367
368/// Verify that k != 0 && k < ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
369pub fn p256_validate_private_key(k: &[u8]) -> bool {
370    let mut valid = true;
371    // XXX: This should fail.
372    let k_element = P256Scalar::from_be_bytes(k);
373    let k_element_bytes = k_element.to_be_bytes();
374    let mut all_zero = true;
375    for i in 0..k.len() {
376        if !k[i] == U8(0u8) {
377            all_zero = false;
378        }
379        if !k_element_bytes[i] == k[i] {
380            valid = false;
381        }
382    }
383    valid && !all_zero
384}
385
386/// Verify that the point `p` is a valid public key.
387pub fn p256_validate_public_key(p: P256Point) -> bool {
388    let b = P256FieldElement::from_be_bytes(&[
389        0x5au8, 0xc6u8, 0x35u8, 0xd8u8, 0xaau8, 0x3au8, 0x93u8, 0xe7u8, 0xb3u8, 0xebu8, 0xbdu8,
390        0x55u8, 0x76u8, 0x98u8, 0x86u8, 0xbcu8, 0x65u8, 0x1du8, 0x06u8, 0xb0u8, 0xccu8, 0x53u8,
391        0xb0u8, 0xf6u8, 0x3bu8, 0xceu8, 0x3cu8, 0x3eu8, 0x27u8, 0xd2u8, 0x60u8, 0x4bu8,
392    ]);
393    let point_at_infinity = is_point_at_infinity(affine_to_jacobian(p.into()));
394    let (x, y) = p.into();
395    let on_curve = y * y == x * x * x - P256FieldElement::from_u128(3) * x + b;
396
397    !point_at_infinity && on_curve
398}
399
400// Calculate w, which is -y or +y, from x. See RFC 6090, Appendix C.
401pub fn p256_calculate_w(x: P256FieldElement) -> P256FieldElement {
402    let b = P256FieldElement::from_be_bytes(&[
403        0x5au8, 0xc6u8, 0x35u8, 0xd8u8, 0xaau8, 0x3au8, 0x93u8, 0xe7u8, 0xb3u8, 0xebu8, 0xbdu8,
404        0x55u8, 0x76u8, 0x98u8, 0x86u8, 0xbcu8, 0x65u8, 0x1du8, 0x06u8, 0xb0u8, 0xccu8, 0x53u8,
405        0xb0u8, 0xf6u8, 0x3bu8, 0xceu8, 0x3cu8, 0x3eu8, 0x27u8, 0xd2u8, 0x60u8, 0x4bu8,
406    ]);
407    // (p+1)/4 calculated offline
408    let pow = P256FieldElement::from_be_bytes(&[
409        0x3fu8, 0xffu8, 0xffu8, 0xffu8, 0xc0u8, 0x00u8, 0x00u8, 0x00u8, 0x40u8, 0x00u8, 0x00u8,
410        0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x40u8, 0x00u8,
411        0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8, 0x00u8,
412    ]);
413    // w = (x^3 + a*x + b)^((p+1)/4) (mod p). [RFC6090, Appendix C]
414    let z = x * x * x - P256FieldElement::from_u128(3) * x + b;
415    // z to power of pow
416    z.pow_felem(&pow)
417}