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;