Skip to main content

dcrypt_algorithms/ec/p384/
mod.rs

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