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