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