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