Skip to main content

noxtls_crypto/pkc/primitive/
mod.rs

1// Copyright (c) 2019-2026, Argenox Technologies LLC
2// All rights reserved.
3//
4// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
5//
6// This file is part of the NoxTLS Library.
7//
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by the
10// Free Software Foundation; version 2 of the License.
11//
12// Alternatively, this file may be used under the terms of a commercial
13// license from Argenox Technologies LLC.
14//
15// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
16// CONTACT: info@argenox.com
17
18//! Concrete public-key implementations (RSA, ECC, X25519/X448, Ed25519, ML-KEM, ML-DSA).
19//!
20//! Submodules are private; this module re-exports the supported API and provides
21//! [`noxtls_ecc_generate_keypair_auto`] for unified ECC key generation.
22
23mod bignum;
24mod ed25519;
25mod ed448;
26mod mldsa;
27mod mlkem;
28mod named_curve;
29mod p256;
30mod p384;
31mod pq_selftest;
32mod rsa;
33mod x25519;
34mod x448;
35
36use core::cmp::Ordering;
37
38use crate::drbg::HmacDrbgSha256;
39use crate::internal_alloc::Vec;
40use bignum::BigUint;
41#[cfg(not(feature = "hazardous-legacy-crypto"))]
42use noxtls_core::Error;
43use noxtls_core::Result;
44
45pub use ed25519::{
46    noxtls_ed25519_generate_private_key_auto,
47    noxtls_ed25519_public_key_from_subject_public_key_info, noxtls_ed25519_verify,
48    Ed25519PrivateKey, Ed25519PublicKey,
49};
50pub use ed448::{
51    noxtls_ed448_generate_private_key_auto, noxtls_ed448_public_key_from_subject_public_key_info,
52    noxtls_ed448_verify, Ed448PrivateKey, Ed448PublicKey,
53};
54pub use mldsa::{
55    noxtls_mldsa_generate_keypair_auto, noxtls_mldsa_public_key_from_subject_public_key_info,
56    noxtls_mldsa_verify, MlDsaPrivateKey, MlDsaPublicKey, OID_ID_MLDSA65,
57};
58pub use mlkem::{
59    noxtls_mlkem_decapsulate, noxtls_mlkem_encapsulate_auto, noxtls_mlkem_generate_keypair_auto,
60    noxtls_mlkem_generate_keypair_auto_for_parameter_set, MlKemParameterSet, MlKemPrivateKey,
61    MlKemPublicKey, MLKEM1024_CIPHERTEXT_LEN, MLKEM1024_PRIVATE_KEY_LEN, MLKEM1024_PUBLIC_KEY_LEN,
62    MLKEM512_CIPHERTEXT_LEN, MLKEM512_PRIVATE_KEY_LEN, MLKEM512_PUBLIC_KEY_LEN,
63    MLKEM_CIPHERTEXT_LEN, MLKEM_PRIVATE_KEY_LEN, MLKEM_PUBLIC_KEY_LEN, MLKEM_SHARED_SECRET_LEN,
64};
65pub use named_curve::{
66    noxtls_named_curve_from_mbedtls_name, noxtls_named_curve_info,
67    noxtls_named_ec_generate_private_key_auto, noxtls_named_ecdh_shared_secret,
68    noxtls_named_ecdsa_sign_digest, noxtls_named_ecdsa_verify_digest,
69    noxtls_secp256k1_ecdsa_sign_sha256, noxtls_secp521r1_ecdsa_sign_sha512, NamedCurve,
70    NamedCurveInfo, NamedEcPrivateKey, NamedEcPublicKey, NOXTLS_MBEDTLS_ECC_CURVES,
71};
72pub use p256::{
73    noxtls_p256_ecdh_shared_secret, noxtls_p256_ecdsa_sign_digest,
74    noxtls_p256_ecdsa_sign_digest_auto, noxtls_p256_ecdsa_sign_sha256,
75    noxtls_p256_ecdsa_sign_sha256_auto, noxtls_p256_ecdsa_verify_digest,
76    noxtls_p256_ecdsa_verify_sha256, noxtls_p256_generate_private_key_auto, P256PrivateKey,
77    P256PublicKey,
78};
79pub use p384::{
80    noxtls_p384_ecdh_shared_secret, noxtls_p384_ecdsa_sign_digest,
81    noxtls_p384_ecdsa_sign_digest_auto, noxtls_p384_ecdsa_sign_sha384,
82    noxtls_p384_ecdsa_sign_sha384_auto, noxtls_p384_ecdsa_verify_digest,
83    noxtls_p384_ecdsa_verify_sha384, noxtls_p384_generate_private_key_auto, P384PrivateKey,
84    P384PublicKey,
85};
86pub use pq_selftest::noxtls_run_pq_self_tests;
87#[cfg(feature = "hazardous-legacy-crypto")]
88pub use rsa::{noxtls_rsa_generate_keypair_auto, noxtls_rsa_generate_keypair_with_exponent_auto};
89pub use rsa::{
90    noxtls_rsa_generate_keypair_secure_auto, noxtls_rsa_generate_keypair_with_policy_auto,
91    noxtls_rsaes_oaep_sha256_decrypt, noxtls_rsaes_oaep_sha256_decrypt_crt_only,
92    noxtls_rsaes_oaep_sha256_encrypt_auto, noxtls_rsaes_pkcs1_v15_decrypt,
93    noxtls_rsaes_pkcs1_v15_decrypt_crt_only, noxtls_rsaes_pkcs1_v15_encrypt_auto,
94    noxtls_rsassa_pss_sha256_sign, noxtls_rsassa_pss_sha256_sign_auto,
95    noxtls_rsassa_pss_sha256_verify, noxtls_rsassa_pss_sha384_sign,
96    noxtls_rsassa_pss_sha384_sign_auto, noxtls_rsassa_pss_sha384_verify, noxtls_rsassa_pss_sign,
97    noxtls_rsassa_pss_verify, noxtls_rsassa_sha1_sign, noxtls_rsassa_sha1_verify,
98    noxtls_rsassa_sha256_sign, noxtls_rsassa_sha256_verify, noxtls_rsassa_sha384_sign,
99    noxtls_rsassa_sha384_verify, noxtls_rsassa_sha512_sign, noxtls_rsassa_sha512_verify,
100    RsaKeySizePolicy, RsaPrivateKey, RsaPssHashAlgorithm, RsaPublicKey,
101};
102pub use x25519::{
103    noxtls_x25519, noxtls_x25519_basepoint, noxtls_x25519_generate_private_key_auto,
104    noxtls_x25519_shared_secret, X25519PrivateKey, X25519PublicKey,
105};
106#[cfg(feature = "hazardous-legacy-crypto")]
107pub use x448::noxtls_x448_generate_private_key_auto;
108#[cfg(feature = "hazardous-legacy-crypto")]
109pub use x448::{noxtls_x448, noxtls_x448_basepoint, noxtls_x448_shared_secret};
110pub use x448::{X448PrivateKey, X448PublicKey};
111
112fn noxtls_parse_ffdhe_value(value: &[u8], label: &'static str) -> Result<BigUint> {
113    if value.is_empty() {
114        return Err(Error::InvalidLength(label));
115    }
116    Ok(BigUint::from_be_bytes(value))
117}
118
119fn noxtls_validate_ffdhe_prime(prime: &BigUint) -> Result<()> {
120    if prime.cmp(&BigUint::from_u128(3)) != Ordering::Greater || !prime.is_odd() {
121        return Err(Error::CryptoFailure(
122            "ffdhe prime must be an odd integer greater than 3",
123        ));
124    }
125    Ok(())
126}
127
128fn noxtls_validate_ffdhe_private(private_key: &BigUint, prime: &BigUint) -> Result<()> {
129    if private_key.cmp(&BigUint::from_u128(1)) != Ordering::Greater {
130        return Err(Error::CryptoFailure(
131            "ffdhe private key must be greater than 1",
132        ));
133    }
134    let upper = prime.sub(&BigUint::from_u128(1));
135    if private_key.cmp(&upper) != Ordering::Less {
136        return Err(Error::CryptoFailure(
137            "ffdhe private key must be less than p - 1",
138        ));
139    }
140    Ok(())
141}
142
143fn noxtls_validate_ffdhe_public(public_key: &BigUint, prime: &BigUint) -> Result<()> {
144    if public_key.cmp(&BigUint::from_u128(1)) != Ordering::Greater {
145        return Err(Error::CryptoFailure(
146            "ffdhe public key must be greater than 1",
147        ));
148    }
149    let upper = prime.sub(&BigUint::from_u128(1));
150    if public_key.cmp(&upper) != Ordering::Less {
151        return Err(Error::CryptoFailure(
152            "ffdhe public key must be less than p - 1",
153        ));
154    }
155    Ok(())
156}
157
158/// Computes a finite-field Diffie-Hellman public key for TLS DHE suites.
159pub fn noxtls_ffdhe_public_key(
160    private_key: &[u8],
161    generator: &[u8],
162    prime: &[u8],
163) -> Result<Vec<u8>> {
164    let p = noxtls_parse_ffdhe_value(prime, "ffdhe prime must not be empty")?;
165    let g = noxtls_parse_ffdhe_value(generator, "ffdhe generator must not be empty")?;
166    let x = noxtls_parse_ffdhe_value(private_key, "ffdhe private key must not be empty")?;
167    noxtls_validate_ffdhe_prime(&p)?;
168    noxtls_validate_ffdhe_private(&x, &p)?;
169    noxtls_validate_ffdhe_public(&g, &p)?;
170    Ok(BigUint::mod_exp(&g, &x, &p).to_be_bytes())
171}
172
173/// Computes the TLS DHE pre-master secret from a peer public key.
174pub fn noxtls_ffdhe_shared_secret(
175    private_key: &[u8],
176    peer_public_key: &[u8],
177    prime: &[u8],
178) -> Result<Vec<u8>> {
179    let p = noxtls_parse_ffdhe_value(prime, "ffdhe prime must not be empty")?;
180    let x = noxtls_parse_ffdhe_value(private_key, "ffdhe private key must not be empty")?;
181    let y = noxtls_parse_ffdhe_value(peer_public_key, "ffdhe peer public key must not be empty")?;
182    noxtls_validate_ffdhe_prime(&p)?;
183    noxtls_validate_ffdhe_private(&x, &p)?;
184    noxtls_validate_ffdhe_public(&y, &p)?;
185    Ok(BigUint::mod_exp(&y, &x, &p).to_be_bytes())
186}
187
188/// Selects one supported elliptic-curve key noxtls_algorithm for unified key generation.
189#[derive(Debug, Clone, Copy, Eq, PartialEq)]
190pub enum EccKeyAlgorithm {
191    /// NIST P-192 (secp192r1) key material.
192    Secp192R1,
193    /// NIST P-224 (secp224r1) key material.
194    Secp224R1,
195    /// NIST P-256 (secp256r1) key material.
196    P256,
197    /// NIST P-384 (secp384r1) key material.
198    P384,
199    /// NIST P-521 (secp521r1) key material.
200    P521,
201    /// Koblitz secp192k1 key material.
202    Secp192K1,
203    /// Koblitz secp224k1 key material.
204    Secp224K1,
205    /// Koblitz secp256k1 key material.
206    Secp256K1,
207    /// BrainpoolP256r1 key material.
208    BrainpoolP256R1,
209    /// BrainpoolP384r1 key material.
210    BrainpoolP384R1,
211    /// BrainpoolP512r1 key material.
212    BrainpoolP512R1,
213    /// Curve25519 X25519 key-exchange key material.
214    X25519,
215    /// Curve448 X448 key-exchange key material.
216    X448,
217    /// Ed25519 signing key material.
218    Ed25519,
219    /// Ed448 signing key material.
220    Ed448,
221}
222
223/// Wraps one generated ECC private key variant.
224#[derive(Debug, Clone)]
225pub enum EccPrivateKey {
226    /// P-256 private scalar.
227    P256(P256PrivateKey),
228    /// P-384 private scalar.
229    P384(P384PrivateKey),
230    /// Generic named-curve private scalar.
231    Named(NamedEcPrivateKey),
232    /// X25519 private scalar.
233    X25519(X25519PrivateKey),
234    /// X448 private scalar.
235    X448(X448PrivateKey),
236    /// Ed25519 signing seed/key.
237    Ed25519(Ed25519PrivateKey),
238    /// Ed448 signing seed/key.
239    Ed448(Ed448PrivateKey),
240}
241
242/// Wraps one generated ECC public key variant.
243#[derive(Debug, Clone, Eq, PartialEq)]
244pub enum EccPublicKey {
245    /// P-256 public point.
246    P256(P256PublicKey),
247    /// P-384 public point.
248    P384(P384PublicKey),
249    /// Generic named-curve public point.
250    Named(NamedEcPublicKey),
251    /// X25519 public u-coordinate.
252    X25519(X25519PublicKey),
253    /// X448 public u-coordinate.
254    X448(X448PublicKey),
255    /// Ed25519 verifying key.
256    Ed25519(Ed25519PublicKey),
257    /// Ed448 verifying key.
258    Ed448(Ed448PublicKey),
259}
260
261/// Generates one ECC private/public keypair for the selected noxtls_algorithm using DRBG entropy.
262///
263/// # Arguments
264/// * `noxtls_algorithm`: ECC noxtls_algorithm to generate.
265/// * `drbg`: DRBG source used for private-key randomness.
266///
267/// # Returns
268/// `(private_key, public_key)` pair wrapped by enum variants matching `noxtls_algorithm`.
269///
270/// # Errors
271///
272/// Returns any error produced by the noxtls_algorithm-specific DRBG-driven generators (for example P-256 field validation, DRBG state errors, or malformed lengths from underlying calls).
273///
274/// # Panics
275///
276/// This function does not panic.
277pub fn noxtls_ecc_generate_keypair_auto(
278    noxtls_algorithm: EccKeyAlgorithm,
279    drbg: &mut HmacDrbgSha256,
280) -> Result<(EccPrivateKey, EccPublicKey)> {
281    match noxtls_algorithm {
282        EccKeyAlgorithm::Secp192R1 => {
283            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::Secp192R1, drbg)?;
284            let public = private.public_key()?;
285            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
286        }
287        EccKeyAlgorithm::Secp224R1 => {
288            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::Secp224R1, drbg)?;
289            let public = private.public_key()?;
290            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
291        }
292        EccKeyAlgorithm::P256 => {
293            let private = noxtls_p256_generate_private_key_auto(drbg)?;
294            let public = private.public_key()?;
295            Ok((EccPrivateKey::P256(private), EccPublicKey::P256(public)))
296        }
297        EccKeyAlgorithm::P384 => {
298            let private = noxtls_p384_generate_private_key_auto(drbg)?;
299            let public = private.public_key()?;
300            Ok((EccPrivateKey::P384(private), EccPublicKey::P384(public)))
301        }
302        EccKeyAlgorithm::P521 => {
303            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::Secp521R1, drbg)?;
304            let public = private.public_key()?;
305            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
306        }
307        EccKeyAlgorithm::Secp192K1 => {
308            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::Secp192K1, drbg)?;
309            let public = private.public_key()?;
310            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
311        }
312        EccKeyAlgorithm::Secp224K1 => {
313            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::Secp224K1, drbg)?;
314            let public = private.public_key()?;
315            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
316        }
317        EccKeyAlgorithm::Secp256K1 => {
318            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::Secp256K1, drbg)?;
319            let public = private.public_key()?;
320            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
321        }
322        EccKeyAlgorithm::BrainpoolP256R1 => {
323            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::BrainpoolP256R1, drbg)?;
324            let public = private.public_key()?;
325            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
326        }
327        EccKeyAlgorithm::BrainpoolP384R1 => {
328            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::BrainpoolP384R1, drbg)?;
329            let public = private.public_key()?;
330            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
331        }
332        EccKeyAlgorithm::BrainpoolP512R1 => {
333            let private = noxtls_named_ec_generate_private_key_auto(NamedCurve::BrainpoolP512R1, drbg)?;
334            let public = private.public_key()?;
335            Ok((EccPrivateKey::Named(private), EccPublicKey::Named(public)))
336        }
337        EccKeyAlgorithm::X25519 => {
338            let private = noxtls_x25519_generate_private_key_auto(drbg)?;
339            let public = private.clone().public_key();
340            Ok((EccPrivateKey::X25519(private), EccPublicKey::X25519(public)))
341        }
342        #[cfg(feature = "hazardous-legacy-crypto")]
343        EccKeyAlgorithm::X448 => {
344            let private = noxtls_x448_generate_private_key_auto(drbg)?;
345            let public = private.clone().public_key();
346            Ok((EccPrivateKey::X448(private), EccPublicKey::X448(public)))
347        }
348        #[cfg(not(feature = "hazardous-legacy-crypto"))]
349        EccKeyAlgorithm::X448 => Err(Error::StateError(
350            "x448 operations are disabled by default; enable `hazardous-legacy-crypto` to use non-constant-time x448 implementation",
351        )),
352        EccKeyAlgorithm::Ed25519 => {
353            let private = noxtls_ed25519_generate_private_key_auto(drbg)?;
354            let public = private.verifying_key();
355            Ok((
356                EccPrivateKey::Ed25519(private),
357                EccPublicKey::Ed25519(public),
358            ))
359        }
360        EccKeyAlgorithm::Ed448 => {
361            let private = noxtls_ed448_generate_private_key_auto(drbg)?;
362            let public = private.verifying_key();
363            Ok((
364                EccPrivateKey::Ed448(private),
365                EccPublicKey::Ed448(public),
366            ))
367        }
368    }
369}
370
371#[cfg(test)]
372mod tests {
373    use super::*;
374
375    #[test]
376    fn ecc_keygen_includes_standard_named_weierstrass_curves() {
377        let algorithms = [
378            EccKeyAlgorithm::Secp192R1,
379            EccKeyAlgorithm::Secp224R1,
380            EccKeyAlgorithm::P384,
381            EccKeyAlgorithm::P521,
382            EccKeyAlgorithm::Secp192K1,
383            EccKeyAlgorithm::Secp224K1,
384            EccKeyAlgorithm::Secp256K1,
385            EccKeyAlgorithm::BrainpoolP256R1,
386            EccKeyAlgorithm::BrainpoolP384R1,
387            EccKeyAlgorithm::BrainpoolP512R1,
388            EccKeyAlgorithm::Ed448,
389        ];
390
391        for algorithm in algorithms {
392            let mut drbg =
393                HmacDrbgSha256::noxtls_new(b"ecc-standard-curves-keygen-seed", b"nonce", b"pkc")
394                    .expect("drbg init");
395            let (private, public) =
396                noxtls_ecc_generate_keypair_auto(algorithm, &mut drbg).expect("curve keygen");
397            match algorithm {
398                EccKeyAlgorithm::P384 => {
399                    assert!(matches!(private, EccPrivateKey::P384(_)));
400                    assert!(matches!(public, EccPublicKey::P384(_)));
401                }
402                EccKeyAlgorithm::Ed448 => {
403                    assert!(matches!(private, EccPrivateKey::Ed448(_)));
404                    assert!(matches!(public, EccPublicKey::Ed448(_)));
405                }
406                _ => {
407                    assert!(matches!(private, EccPrivateKey::Named(_)));
408                    assert!(matches!(public, EccPublicKey::Named(_)));
409                }
410            }
411        }
412    }
413}