dcrypt_algorithms/ec/p521/
mod.rs

1//! NIST P-521 Elliptic Curve Primitives
2//!
3//! This module implements the NIST P-521 elliptic curve operations in constant time.
4//! The curve equation is y² = x³ - 3x + b over the prime field F_p where:
5//! - p = 2^521 - 1 (NIST P-521 prime, a Mersenne prime)
6//! - The curve order n = 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409
7//!
8//! All operations are implemented to be constant-time to prevent timing attacks.
9//! The implementation uses:
10//! - Mersenne reduction for field arithmetic (2^521 ≡ 1 mod p)
11//! - Jacobian projective coordinates for efficient point operations
12//! - Binary scalar multiplication with constant-time point selection
13
14mod constants;
15mod field;
16mod point;
17mod scalar;
18
19pub use constants::{
20    P521_FIELD_ELEMENT_SIZE, P521_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE, P521_POINT_COMPRESSED_SIZE,
21    P521_POINT_UNCOMPRESSED_SIZE, P521_SCALAR_SIZE,
22};
23pub use field::FieldElement;
24pub use point::{Point, PointFormat};
25pub use scalar::Scalar;
26
27use crate::error::{Error, Result};
28use crate::hash::sha2::Sha512;
29use crate::kdf::hkdf::Hkdf;
30use crate::kdf::KeyDerivationFunction as KdfTrait;
31use dcrypt_params::traditional::ecdsa::NIST_P521;
32use rand::{CryptoRng, RngCore};
33
34/// Get the standard base point G of the P-521 curve
35///
36/// Returns the generator point specified in the NIST P-521 standard.
37/// This point generates the cyclic subgroup used for ECDH and ECDSA.
38pub fn base_point_g() -> Point {
39    Point::new_uncompressed(&NIST_P521.g_x, &NIST_P521.g_y)
40        .expect("Standard base point must be valid")
41}
42
43/// Scalar multiplication with the base point: scalar * G
44///
45/// Efficiently computes scalar multiplication with the standard generator.
46/// This is the core operation for generating public keys from private keys.
47pub fn scalar_mult_base_g(scalar: &Scalar) -> Result<Point> {
48    let g = base_point_g();
49    g.mul(scalar)
50}
51
52/// Generate a cryptographically secure ECDH keypair
53///
54/// Uses rejection sampling to ensure the private key scalar is uniformly
55/// distributed in the range [1, n-1]. The public key is computed as
56/// private_key * G where G is the standard base point.
57///
58/// Returns (private_key, public_key) pair suitable for ECDH key agreement.
59pub fn generate_keypair<R: CryptoRng + RngCore>(rng: &mut R) -> Result<(Scalar, Point)> {
60    let mut scalar_bytes = [0u8; P521_SCALAR_SIZE];
61
62    // Use rejection sampling for uniform distribution
63    loop {
64        rng.fill_bytes(&mut scalar_bytes);
65
66        // Attempt to create a valid scalar (non-zero, < n)
67        match Scalar::new(scalar_bytes) {
68            Ok(private_key) => {
69                // Compute corresponding public key
70                let public_key = scalar_mult_base_g(&private_key)?;
71                return Ok((private_key, public_key));
72            }
73            Err(_) => {
74                // Invalid scalar generated, retry with new random bytes
75                continue;
76            }
77        }
78    }
79}
80
81/// General scalar multiplication: compute scalar * point
82///
83/// Performs scalar multiplication with an arbitrary point on the curve.
84/// Used in ECDH key agreement and signature verification.
85pub fn scalar_mult(scalar: &Scalar, point: &Point) -> Result<Point> {
86    if point.is_identity() {
87        // scalar * O = O (identity element)
88        return Ok(Point::identity());
89    }
90
91    point.mul(scalar)
92}
93
94/// Key derivation function for ECDH shared secret using HKDF-SHA512
95///
96/// Derives a cryptographically strong shared secret from the ECDH raw output.
97/// Uses HKDF (HMAC-based Key Derivation Function) with SHA-512 as specified
98/// in RFC 5869 for secure key derivation.
99///
100/// SHA-512 is more appropriate for P-521 due to the larger curve size.
101///
102/// Parameters:
103/// - ikm: Input key material (raw ECDH output, e.g., x-coordinate)
104/// - info: Optional context information for domain separation
105///
106/// Returns a fixed-length derived key suitable for symmetric encryption.
107pub fn kdf_hkdf_sha512_for_ecdh_kem(
108    ikm: &[u8],
109    info: Option<&[u8]>,
110) -> Result<[u8; P521_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE]> {
111    let hkdf_instance = <Hkdf<Sha512, 16> as KdfTrait>::new();
112
113    // Perform HKDF key derivation
114    let derived_key_vec = hkdf_instance.derive_key(
115        ikm,
116        None, // No salt for ECDH applications (uses zero-length salt)
117        info, // Context info for domain separation
118        P521_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE,
119    )?;
120
121    // Convert to fixed-size array
122    let mut output_array = [0u8; P521_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE];
123    if derived_key_vec.len() == P521_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE {
124        output_array.copy_from_slice(&derived_key_vec);
125        Ok(output_array)
126    } else {
127        Err(Error::Length {
128            context: "KDF output for ECDH",
129            expected: P521_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE,
130            actual: derived_key_vec.len(),
131        })
132    }
133}
134
135#[cfg(test)]
136mod tests;