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