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