dcrypt_algorithms/ec/p256/mod.rs
1//! NIST P-256 Elliptic Curve Primitives
2//!
3//! This module implements the NIST P-256 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^256 - 2^224 + 2^192 + 2^96 - 1 (NIST P-256 prime)
6//! - The curve order n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
7//!
8//! All operations are implemented to be constant-time to prevent timing attacks.
9//! The implementation uses:
10//! - Montgomery reduction for field arithmetic
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 P256_FIELD_ELEMENT_SIZE, P256_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE, P256_POINT_COMPRESSED_SIZE,
21 P256_POINT_UNCOMPRESSED_SIZE, P256_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::Sha256;
29use crate::kdf::hkdf::Hkdf;
30use crate::kdf::KeyDerivationFunction as KdfTrait;
31use dcrypt_params::traditional::ecdsa::NIST_P256;
32use rand::{CryptoRng, RngCore};
33
34/// Get the standard base point G of the P-256 curve
35///
36/// Returns the generator point specified in the NIST P-256 standard.
37/// This point generates the cyclic subgroup used for ECDH and ECDSA.
38pub fn base_point_g() -> Point {
39 Point::new_uncompressed(&NIST_P256.g_x, &NIST_P256.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; P256_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-SHA256
95///
96/// Derives a cryptographically strong shared secret from the ECDH raw output.
97/// Uses HKDF (HMAC-based Key Derivation Function) with SHA-256 as specified
98/// in RFC 5869 for secure key derivation.
99///
100/// Parameters:
101/// - ikm: Input key material (raw ECDH output, e.g., x-coordinate)
102/// - info: Optional context information for domain separation
103///
104/// Returns a fixed-length derived key suitable for symmetric encryption.
105pub fn kdf_hkdf_sha256_for_ecdh_kem(
106 ikm: &[u8],
107 info: Option<&[u8]>,
108) -> Result<[u8; P256_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE]> {
109 let hkdf_instance = <Hkdf<Sha256, 16> as KdfTrait>::new();
110
111 // Perform HKDF key derivation
112 let derived_key_vec = hkdf_instance.derive_key(
113 ikm,
114 None, // No salt for ECDH applications (uses zero-length salt)
115 info, // Context info for domain separation
116 P256_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE,
117 )?;
118
119 // Convert to fixed-size array
120 let mut output_array = [0u8; P256_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE];
121 if derived_key_vec.len() == P256_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE {
122 output_array.copy_from_slice(&derived_key_vec);
123 Ok(output_array)
124 } else {
125 Err(Error::Length {
126 context: "KDF output for ECDH",
127 expected: P256_KEM_SHARED_SECRET_KDF_OUTPUT_SIZE,
128 actual: derived_key_vec.len(),
129 })
130 }
131}
132
133#[cfg(test)]
134mod tests;