mirror of
https://github.com/espressif/mbedtls.git
synced 2026-08-14 07:27:47 +00:00
feat(pqc): hybrid PQC TLS 1.3 — X25519MLKEM768 + ML-DSA-65
Add hybrid post-quantum TLS 1.3 support, baselined on 582ff4820:
- X25519MLKEM768 hybrid key exchange (IANA group 0x11EC)
- pure ML-DSA-65 authentication via liboqs (TLS sig scheme 0x0905,
X.509 id-ml-dsa-65, RFC 9881) — new library/mldsa65_oqs.{c,h}
- hybrid KEM glue: library/ssl_hybrid_tls.c, ssl_tls13_hybrid_kem.h
- OID / PK / TLS 1.3 wiring across ssl_tls13_*, x509_crt, pk, oid,
tf-psa-crypto builtin drivers
ML-DSA-65 verify is guarded on MBEDTLS_SSL_TLS1_3_SIG_MLDSA65 so the tree
also builds with liboqs disabled (classical X25519+ECDSA baseline).
This commit is contained in:
@@ -796,6 +796,28 @@
|
||||
*/
|
||||
#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
|
||||
|
||||
/**
|
||||
* \def MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768
|
||||
*
|
||||
* Enable support for X25519MLKEM768 hybrid post-quantum key exchange
|
||||
* in TLS 1.3. Requires liboqs to be linked.
|
||||
*/
|
||||
#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768
|
||||
|
||||
/**
|
||||
* \def MBEDTLS_SSL_TLS1_3_SIG_MLDSA65
|
||||
*
|
||||
* Enable pure ML-DSA-65 signatures (TLS \c SignatureScheme \c 0x0905,
|
||||
* X.509 OID from RFC 9881 id-ml-dsa-65) for TLS 1.3 and X.509. Verification
|
||||
* uses liboqs (OQS_SIG_alg_ml_dsa_65). TLS CertificateVerify uses pure ML-DSA
|
||||
* (message is the verify structure, not a pre-hash).
|
||||
*
|
||||
* Requires: \c MBEDTLS_SSL_PROTO_TLS1_3, X.509, and liboqs.
|
||||
*
|
||||
* Comment out to omit ML-DSA-65 signature support from the build.
|
||||
*/
|
||||
#define MBEDTLS_SSL_TLS1_3_SIG_MLDSA65
|
||||
|
||||
/**
|
||||
* \def MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED
|
||||
*
|
||||
|
||||
@@ -265,6 +265,12 @@
|
||||
* ecdsa-with-SHA2(3) 4 } */
|
||||
#define MBEDTLS_OID_ECDSA_SHA512 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x04"
|
||||
|
||||
/*
|
||||
* id-ml-dsa-65 (RFC 9881): { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) mldsa(3) 18 }
|
||||
* Pure ML-DSA-65 for X.509. Enabled with \c MBEDTLS_SSL_TLS1_3_SIG_MLDSA65.
|
||||
*/
|
||||
#define MBEDTLS_OID_MLDSA65 MBEDTLS_OID_NIST_ALG "\x03\x12"
|
||||
|
||||
#if defined(MBEDTLS_X509_USE_C)
|
||||
/**
|
||||
* \brief Translate an ASN.1 OID into its numeric representation
|
||||
|
||||
@@ -237,6 +237,8 @@
|
||||
#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE4096 0x0102
|
||||
#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE6144 0x0103
|
||||
#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE8192 0x0104
|
||||
/** X25519 + ML-KEM-768 hybrid (draft-ietf-tls-ecdhe-mlkem) */
|
||||
#define MBEDTLS_SSL_IANA_TLS_GROUP_X25519MLKEM768 0x11EC
|
||||
|
||||
/*
|
||||
* TLS 1.3 Key Exchange Modes
|
||||
@@ -500,6 +502,9 @@
|
||||
#define MBEDTLS_TLS1_3_SIG_ED25519 0x0807
|
||||
#define MBEDTLS_TLS1_3_SIG_ED448 0x0808
|
||||
|
||||
/* Pure ML-DSA-65 (FIPS 204 Level 3), per draft-ietf-tls-mldsa. */
|
||||
#define MBEDTLS_TLS1_3_SIG_MLDSA65 0x0905
|
||||
|
||||
/* RSASSA-PSS algorithms with public key OID RSASSA-PSS */
|
||||
#define MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA256 0x0809
|
||||
#define MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA384 0x080A
|
||||
|
||||
@@ -24,6 +24,7 @@ set(src_tls
|
||||
ssl_cookie.c
|
||||
ssl_debug_helpers_generated.c
|
||||
ssl_msg.c
|
||||
ssl_hybrid_tls.c
|
||||
ssl_ticket.c
|
||||
ssl_tls.c
|
||||
ssl_tls12_client.c
|
||||
|
||||
@@ -264,6 +264,11 @@ static int ssl_write_supported_groups_ext(mbedtls_ssl_context *ssl,
|
||||
if (mbedtls_ssl_tls13_named_group_is_ffdh(*group_list)) {
|
||||
propose_group = 1;
|
||||
}
|
||||
#endif
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(*group_list)) {
|
||||
propose_group = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED */
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/*
|
||||
* TLS 1.3 hybrid post-quantum key exchange: X25519 + ML-KEM-768
|
||||
*
|
||||
* Wire format (client key share, 1216 bytes total):
|
||||
* ML-KEM-768 ek : 1184 bytes
|
||||
* X25519 public key : 32 bytes
|
||||
*
|
||||
* Wire format (server key share, 1120 bytes total):
|
||||
* ML-KEM-768 ciphertext : 1088 bytes
|
||||
* X25519 public key : 32 bytes
|
||||
*
|
||||
* Combined shared secret (64 bytes) fed into TLS key schedule:
|
||||
* ML-KEM shared secret (32) || X25519 shared secret (32)
|
||||
*
|
||||
* Reference: draft-ietf-tls-ecdhe-mlkem-04
|
||||
* IANA codepoint: 0x11EC
|
||||
*/
|
||||
|
||||
#include "ssl_misc.h"
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
|
||||
#include <string.h>
|
||||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/platform.h"
|
||||
#include "mbedtls/psa_util.h"
|
||||
#include "debug_internal.h"
|
||||
#include "psa/crypto.h"
|
||||
#include "ssl_tls13_hybrid_kem.h"
|
||||
|
||||
/* Suppress typedef-redefinition warnings from liboqs headers (Clang only). */
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wtypedef-redefinition"
|
||||
#endif
|
||||
#include <oqs/oqsconfig.h>
|
||||
#include <oqs/kem_ml_kem.h>
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
/* Local PSA→mbedtls error translation, same pattern as ssl_tls13_generic.c */
|
||||
static int local_err_translation(psa_status_t status)
|
||||
{
|
||||
return psa_status_to_mbedtls(status, psa_to_ssl_errors,
|
||||
ARRAY_LENGTH(psa_to_ssl_errors),
|
||||
psa_generic_status_to_mbedtls);
|
||||
}
|
||||
#define PSA_TO_MBEDTLS_ERR(status) local_err_translation(status)
|
||||
|
||||
/*
|
||||
* Client side: generate X25519 + ML-KEM-768 keypair, write key share.
|
||||
*
|
||||
* Writes MLKEM768_ek (1184) || X25519_pub (32) = 1216 bytes into buf
|
||||
* (caller wraps this in KeyShareEntry with length prefix).
|
||||
* Stores X25519 private key handle in handshake->xxdh_psa_privkey (PSA).
|
||||
* Stores ML-KEM-768 secret key in handshake->mlkem_sk until decapsulation.
|
||||
*/
|
||||
int mbedtls_ssl_tls13_hybrid_x25519mlkem768_write_client_share(
|
||||
mbedtls_ssl_context *ssl,
|
||||
unsigned char *buf,
|
||||
unsigned char *end,
|
||||
size_t *out_len)
|
||||
{
|
||||
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
size_t x25519_pub_len = 0;
|
||||
OQS_STATUS oqs_ret;
|
||||
mbedtls_ssl_handshake_params *handshake = ssl->handshake;
|
||||
|
||||
*out_len = 0;
|
||||
|
||||
MBEDTLS_SSL_CHK_BUF_PTR(buf, end, MBEDTLS_X25519MLKEM768_CLIENT_SHARE_LEN);
|
||||
|
||||
/* --- Part 1: ML-KEM-768 keypair via liboqs --- */
|
||||
/* Wire format: MLKEM ek (1184) || X25519 pub (32) per draft-ietf-tls-ecdhe-mlkem-04 */
|
||||
/* ek (public key) goes into first 1184 bytes of buf */
|
||||
/* dk (secret key) is stored in handshake struct for later decaps */
|
||||
oqs_ret = OQS_KEM_ml_kem_768_keypair(
|
||||
buf,
|
||||
handshake->mlkem_sk);
|
||||
|
||||
if (oqs_ret != OQS_SUCCESS) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("OQS_KEM_ml_kem_768_keypair failed"));
|
||||
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "ML-KEM-768 ek", buf, MBEDTLS_MLKEM768_PUBLIC_KEY_LEN);
|
||||
|
||||
/* --- Part 2: X25519 keypair via PSA --- */
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE);
|
||||
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH);
|
||||
psa_set_key_type(&key_attributes,
|
||||
PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY));
|
||||
psa_set_key_bits(&key_attributes, MBEDTLS_X25519_KEY_BITS);
|
||||
|
||||
status = psa_generate_key(&key_attributes, &handshake->xxdh_psa_privkey);
|
||||
if (status != PSA_SUCCESS) {
|
||||
ret = PSA_TO_MBEDTLS_ERR(status);
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "psa_generate_key (X25519)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Export X25519 public key into last 32 bytes of buf (after ML-KEM ek) */
|
||||
status = psa_export_public_key(handshake->xxdh_psa_privkey,
|
||||
buf + MBEDTLS_MLKEM768_PUBLIC_KEY_LEN,
|
||||
MBEDTLS_X25519_PUBLIC_KEY_LEN,
|
||||
&x25519_pub_len);
|
||||
if (status != PSA_SUCCESS) {
|
||||
ret = PSA_TO_MBEDTLS_ERR(status);
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "psa_export_public_key (X25519)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (x25519_pub_len != MBEDTLS_X25519_PUBLIC_KEY_LEN) {
|
||||
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "X25519 public key",
|
||||
buf + MBEDTLS_MLKEM768_PUBLIC_KEY_LEN,
|
||||
MBEDTLS_X25519_PUBLIC_KEY_LEN);
|
||||
|
||||
*out_len = MBEDTLS_X25519MLKEM768_CLIENT_SHARE_LEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Client side: process server key share, produce combined shared secret.
|
||||
*
|
||||
* Server KeyShareEntry: opaque key_exchange<1..2^16-1> =
|
||||
* uint16 length (1120) || MLKEM768_ct (1088) || X25519_pub (32)
|
||||
* (buf points at the start of key_exchange, i.e. length || payload).
|
||||
*
|
||||
* Produces combined_secret = MLKEM_ss (32) || X25519_ss (32) = 64 bytes
|
||||
* stored in handshake->xxdh_psa_peerkey for the TLS 1.3 key schedule.
|
||||
* Order per draft-ietf-tls-ecdhe-mlkem-04 Section 4.3.
|
||||
*
|
||||
* The X25519 private key (xxdh_psa_privkey) is destroyed here after use.
|
||||
* The ML-KEM secret key (mlkem_sk) is wiped here after decapsulation.
|
||||
* ssl_tls13_keys.c detects xxdh_psa_privkey == INIT and uses peerkey
|
||||
* directly as the pre-computed shared secret input to HKDF.
|
||||
*/
|
||||
int mbedtls_ssl_tls13_hybrid_x25519mlkem768_read_server_share(
|
||||
mbedtls_ssl_context *ssl,
|
||||
const unsigned char *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
psa_status_t status;
|
||||
OQS_STATUS oqs_ret;
|
||||
mbedtls_ssl_handshake_params *handshake = ssl->handshake;
|
||||
|
||||
uint8_t x25519_ss[MBEDTLS_X25519_SHARED_SECRET_LEN];
|
||||
uint8_t mlkem_ss[MBEDTLS_MLKEM768_SHARED_SECRET_LEN];
|
||||
size_t x25519_ss_len = 0;
|
||||
|
||||
uint16_t ke_payload_len;
|
||||
const unsigned char *payload;
|
||||
|
||||
/* RFC 8446 KeyShareEntry: NamedGroup || opaque key_exchange<1..2^16-1> */
|
||||
if (buf_len < MBEDTLS_SSL_KEY_SHARE_ENTRY_LEN_SIZE + MBEDTLS_X25519MLKEM768_SERVER_SHARE_LEN) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("server key share too short: %u < %u",
|
||||
(unsigned) buf_len,
|
||||
(unsigned) (MBEDTLS_SSL_KEY_SHARE_ENTRY_LEN_SIZE + MBEDTLS_X25519MLKEM768_SERVER_SHARE_LEN)));
|
||||
MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE,
|
||||
MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE);
|
||||
return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE;
|
||||
}
|
||||
|
||||
ke_payload_len = MBEDTLS_GET_UINT16_BE(buf, 0);
|
||||
if (ke_payload_len != MBEDTLS_X25519MLKEM768_SERVER_SHARE_LEN) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad hybrid server key_exchange length: %u != %u",
|
||||
(unsigned) ke_payload_len,
|
||||
(unsigned) MBEDTLS_X25519MLKEM768_SERVER_SHARE_LEN));
|
||||
MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER,
|
||||
MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER);
|
||||
return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER;
|
||||
}
|
||||
|
||||
payload = buf + MBEDTLS_SSL_KEY_SHARE_ENTRY_LEN_SIZE;
|
||||
/*
|
||||
* draft-ietf-tls-ecdhe-mlkem-04 §4.2: server share is
|
||||
* ML-KEM ciphertext (1088) || X25519 server public key (32).
|
||||
*/
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "Server KeyShareEntry payload (MLKEM768_ct || X25519_pub)",
|
||||
payload, MBEDTLS_X25519MLKEM768_SERVER_SHARE_LEN);
|
||||
|
||||
const unsigned char *mlkem_ct = payload;
|
||||
const unsigned char *x25519_peer_pub = payload + MBEDTLS_MLKEM768_CIPHERTEXT_LEN;
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "Server X25519 public key (from payload)",
|
||||
x25519_peer_pub, MBEDTLS_X25519_PUBLIC_KEY_LEN);
|
||||
|
||||
/* --- Part 1: ML-KEM-768 decapsulation --- */
|
||||
oqs_ret = OQS_KEM_ml_kem_768_decaps(
|
||||
mlkem_ss,
|
||||
mlkem_ct,
|
||||
handshake->mlkem_sk);
|
||||
|
||||
/* Wipe the ML-KEM secret key from the handshake struct immediately */
|
||||
mbedtls_platform_zeroize(handshake->mlkem_sk, sizeof(handshake->mlkem_sk));
|
||||
|
||||
if (oqs_ret != OQS_SUCCESS) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("OQS_KEM_ml_kem_768_decaps failed"));
|
||||
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "ML-KEM-768 shared secret",
|
||||
mlkem_ss, MBEDTLS_MLKEM768_SHARED_SECRET_LEN);
|
||||
|
||||
/* --- Part 2: X25519 key agreement --- */
|
||||
status = psa_raw_key_agreement(
|
||||
PSA_ALG_ECDH,
|
||||
handshake->xxdh_psa_privkey, /* our X25519 private key (PSA handle) */
|
||||
x25519_peer_pub, /* server's X25519 public key (32 bytes) */
|
||||
MBEDTLS_X25519_PUBLIC_KEY_LEN,
|
||||
x25519_ss,
|
||||
sizeof(x25519_ss),
|
||||
&x25519_ss_len);
|
||||
|
||||
/* Destroy the ephemeral X25519 private key immediately after use */
|
||||
psa_destroy_key(handshake->xxdh_psa_privkey);
|
||||
handshake->xxdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT;
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
ret = PSA_TO_MBEDTLS_ERR(status);
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "psa_raw_key_agreement (X25519)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "X25519 shared secret", x25519_ss, x25519_ss_len);
|
||||
|
||||
/* --- Part 3: concatenate for TLS 1.3 key schedule (IKM to HKDF-Extract) --- */
|
||||
if (sizeof(handshake->xxdh_psa_peerkey) < MBEDTLS_X25519MLKEM768_SHARED_SECRET_LEN) {
|
||||
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* IKM for HKDF: ML-KEM SS || X25519 SS (draft-ietf-tls-ecdhe-mlkem-04 §4.3) */
|
||||
memcpy(handshake->xxdh_psa_peerkey,
|
||||
mlkem_ss, MBEDTLS_MLKEM768_SHARED_SECRET_LEN);
|
||||
memcpy(handshake->xxdh_psa_peerkey + MBEDTLS_MLKEM768_SHARED_SECRET_LEN,
|
||||
x25519_ss, MBEDTLS_X25519_SHARED_SECRET_LEN);
|
||||
handshake->xxdh_psa_peerkey_len = MBEDTLS_X25519MLKEM768_SHARED_SECRET_LEN;
|
||||
|
||||
/* Wipe local copies of both secrets */
|
||||
mbedtls_platform_zeroize(x25519_ss, sizeof(x25519_ss));
|
||||
mbedtls_platform_zeroize(mlkem_ss, sizeof(mlkem_ss));
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "Combined shared secret (MLKEM||X25519)",
|
||||
handshake->xxdh_psa_peerkey,
|
||||
handshake->xxdh_psa_peerkey_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768 */
|
||||
+30
-1
@@ -25,6 +25,7 @@ extern const mbedtls_error_pair_t psa_to_ssl_errors[7];
|
||||
|
||||
#include "mbedtls/pk.h"
|
||||
#include "ssl_ciphersuites_internal.h"
|
||||
#include "ssl_tls13_hybrid_kem.h"
|
||||
#include "x509_internal.h"
|
||||
|
||||
/* Shorthand for restartable ECC */
|
||||
@@ -762,6 +763,11 @@ struct mbedtls_ssl_handshake_params {
|
||||
size_t xxdh_psa_peerkey_len;
|
||||
#endif /* MBEDTLS_KEY_EXCHANGE_SOME_XXDH_PSA_ANY_ENABLED */
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
/** ML-KEM-768 secret key (liboqs) between client key share and decapsulation */
|
||||
uint8_t mlkem_sk[MBEDTLS_MLKEM768_SECRET_KEY_LEN];
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
|
||||
psa_pake_operation_t psa_pake_ctx; /*!< EC J-PAKE key exchange */
|
||||
mbedtls_svc_key_id_t psa_pake_password;
|
||||
@@ -2227,6 +2233,13 @@ static inline int mbedtls_ssl_tls13_named_group_is_ffdh(uint16_t named_group)
|
||||
named_group <= MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE8192;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
static inline int mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(uint16_t named_group)
|
||||
{
|
||||
return named_group == MBEDTLS_SSL_IANA_TLS_GROUP_X25519MLKEM768;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int mbedtls_ssl_named_group_is_offered(
|
||||
const mbedtls_ssl_context *ssl, uint16_t named_group)
|
||||
{
|
||||
@@ -2260,7 +2273,13 @@ static inline int mbedtls_ssl_named_group_is_supported(uint16_t named_group)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#if !defined(PSA_WANT_ALG_ECDH) && !defined(PSA_WANT_ALG_FFDH)
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(named_group)) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#if !defined(PSA_WANT_ALG_ECDH) && !defined(PSA_WANT_ALG_FFDH) && \
|
||||
!defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
(void) named_group;
|
||||
#endif
|
||||
return 0;
|
||||
@@ -2333,6 +2352,10 @@ static inline int mbedtls_ssl_tls13_sig_alg_for_cert_verify_is_supported(
|
||||
break;
|
||||
#endif /* PSA_WANT_ALG_SHA_512 */
|
||||
#endif /* PSA_WANT_ALG_RSA_PSS */
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
case MBEDTLS_TLS1_3_SIG_MLDSA65:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -2418,6 +2441,12 @@ static inline int mbedtls_ssl_get_pk_sigalg_and_md_alg_from_sig_alg(
|
||||
break;
|
||||
#endif /* PSA_WANT_ALG_SHA_512 */
|
||||
#endif /* PSA_WANT_ALG_RSA_PSS */
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
case MBEDTLS_TLS1_3_SIG_MLDSA65:
|
||||
*md_alg = MBEDTLS_MD_NONE;
|
||||
*pk_type = MBEDTLS_PK_MLDSA65;
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
@@ -2392,6 +2392,9 @@ void mbedtls_ssl_conf_sig_algs(mbedtls_ssl_config *conf,
|
||||
* about this list.
|
||||
*/
|
||||
static const uint16_t ssl_preset_default_groups[] = {
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
MBEDTLS_SSL_IANA_TLS_GROUP_X25519MLKEM768,
|
||||
#endif
|
||||
#if defined(PSA_WANT_ECC_MONTGOMERY_255)
|
||||
MBEDTLS_SSL_IANA_TLS_GROUP_X25519,
|
||||
#endif
|
||||
@@ -4552,6 +4555,10 @@ void mbedtls_ssl_handshake_free(mbedtls_ssl_context *ssl)
|
||||
}
|
||||
#endif /* MBEDTLS_KEY_EXCHANGE_SOME_XXDH_PSA_ANY_ENABLED */
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
mbedtls_platform_zeroize(handshake->mlkem_sk, sizeof(handshake->mlkem_sk));
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
|
||||
mbedtls_ssl_transform_free(handshake->transform_handshake);
|
||||
mbedtls_free(handshake->transform_handshake);
|
||||
@@ -5324,6 +5331,10 @@ static const uint16_t ssl_preset_default_sig_algs[] = {
|
||||
MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256,
|
||||
#endif /* PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC && PSA_WANT_ALG_SHA_256 */
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
MBEDTLS_TLS1_3_SIG_MLDSA65,
|
||||
#endif
|
||||
|
||||
MBEDTLS_TLS_SIG_NONE
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "ssl_debug_helpers.h"
|
||||
#include "mbedtls/psa_util.h"
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
#include "ssl_tls13_hybrid_kem.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED)
|
||||
/* Define a local translating function to save code size by not using too many
|
||||
* arguments in each translating place. */
|
||||
@@ -180,6 +184,24 @@ static int ssl_tls13_reset_key_share(mbedtls_ssl_context *ssl)
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED)
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(group_id)) {
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
||||
|
||||
status = psa_destroy_key(ssl->handshake->xxdh_psa_privkey);
|
||||
if (status != PSA_SUCCESS) {
|
||||
ret = PSA_TO_MBEDTLS_ERR(status);
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "psa_destroy_key", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssl->handshake->xxdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT;
|
||||
mbedtls_platform_zeroize(ssl->handshake->mlkem_sk,
|
||||
sizeof(ssl->handshake->mlkem_sk));
|
||||
return 0;
|
||||
} else
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768 */
|
||||
if (mbedtls_ssl_tls13_named_group_is_ecdhe(group_id) ||
|
||||
mbedtls_ssl_tls13_named_group_is_ffdh(group_id)) {
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
@@ -223,6 +245,12 @@ static int ssl_tls13_get_default_group_id(mbedtls_ssl_context *ssl,
|
||||
}
|
||||
|
||||
for (; *group_list != 0; group_list++) {
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(*group_list)) {
|
||||
*group_id = *group_list;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#if defined(PSA_WANT_ALG_ECDH)
|
||||
if ((mbedtls_ssl_get_psa_curve_info_from_tls_id(
|
||||
*group_list, NULL, NULL) == PSA_SUCCESS) &&
|
||||
@@ -286,7 +314,11 @@ static int ssl_tls13_write_key_share_ext(mbedtls_ssl_context *ssl,
|
||||
/* HRR could already have requested something else. */
|
||||
group_id = ssl->handshake->offered_group_id;
|
||||
if (!mbedtls_ssl_tls13_named_group_is_ecdhe(group_id) &&
|
||||
!mbedtls_ssl_tls13_named_group_is_ffdh(group_id)) {
|
||||
!mbedtls_ssl_tls13_named_group_is_ffdh(group_id)
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
&& !mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(group_id)
|
||||
#endif
|
||||
) {
|
||||
MBEDTLS_SSL_PROC_CHK(ssl_tls13_get_default_group_id(ssl,
|
||||
&group_id));
|
||||
}
|
||||
@@ -300,6 +332,25 @@ static int ssl_tls13_write_key_share_ext(mbedtls_ssl_context *ssl,
|
||||
* only one key share entry is allowed.
|
||||
*/
|
||||
client_shares = p;
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(group_id)) {
|
||||
unsigned char *group = p;
|
||||
size_t key_exchange_len = 0;
|
||||
|
||||
MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4);
|
||||
p += 4;
|
||||
ret = mbedtls_ssl_tls13_hybrid_x25519mlkem768_write_client_share(
|
||||
ssl, p, end, &key_exchange_len);
|
||||
p += key_exchange_len;
|
||||
if (ret != 0) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("client hello: failed hybrid X25519MLKEM768 key share"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
MBEDTLS_PUT_UINT16_BE(group_id, group, 0);
|
||||
MBEDTLS_PUT_UINT16_BE(key_exchange_len, group, 2);
|
||||
} else
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768 */
|
||||
#if defined(PSA_WANT_ALG_ECDH) || defined(PSA_WANT_ALG_FFDH)
|
||||
if (mbedtls_ssl_tls13_named_group_is_ecdhe(group_id) ||
|
||||
mbedtls_ssl_tls13_named_group_is_ffdh(group_id)) {
|
||||
@@ -407,6 +458,12 @@ static int ssl_tls13_parse_hrr_key_share_ext(mbedtls_ssl_context *ssl,
|
||||
if (*group_list != selected_group) {
|
||||
continue;
|
||||
}
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(*group_list)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if defined(PSA_WANT_ALG_ECDH)
|
||||
if (mbedtls_ssl_tls13_named_group_is_ecdhe(*group_list)) {
|
||||
if (mbedtls_ssl_get_psa_curve_info_from_tls_id(
|
||||
@@ -494,6 +551,17 @@ static int ssl_tls13_parse_key_share_ext(mbedtls_ssl_context *ssl,
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED)
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(group)) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(2,
|
||||
("DHE group name: %s", mbedtls_ssl_named_group_to_str(group)));
|
||||
ret = mbedtls_ssl_tls13_hybrid_x25519mlkem768_read_server_share(
|
||||
ssl, p, end - p);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
} else
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768 */
|
||||
if (mbedtls_ssl_tls13_named_group_is_ecdhe(group) ||
|
||||
mbedtls_ssl_tls13_named_group_is_ffdh(group)) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(2,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "mbedtls/error.h"
|
||||
#include "debug_internal.h"
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/oid.h"
|
||||
#include "mbedtls/platform.h"
|
||||
#include "mbedtls/constant_time.h"
|
||||
@@ -26,6 +27,10 @@
|
||||
|
||||
#include "psa/crypto.h"
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
#include "mbedtls/mldsa65_oqs.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED)
|
||||
/* Define a local translating function to save code size by not using too many
|
||||
* arguments in each translating place. */
|
||||
@@ -305,9 +310,14 @@ static int ssl_tls13_parse_certificate_verify(mbedtls_ssl_context *ssl,
|
||||
goto error;
|
||||
}
|
||||
|
||||
hash_alg = mbedtls_md_psa_alg_from_type(md_alg);
|
||||
if (hash_alg == 0) {
|
||||
goto error;
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
if (sig_alg != MBEDTLS_PK_SIGALG_MLDSA65)
|
||||
#endif
|
||||
{
|
||||
hash_alg = mbedtls_md_psa_alg_from_type(md_alg);
|
||||
if (hash_alg == 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
MBEDTLS_SSL_DEBUG_MSG(3, ("Certificate Verify: Signature algorithm ( %04x )",
|
||||
@@ -316,6 +326,15 @@ static int ssl_tls13_parse_certificate_verify(mbedtls_ssl_context *ssl,
|
||||
/*
|
||||
* Check the certificate's key type matches the signature alg
|
||||
*/
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
if (sig_alg == MBEDTLS_PK_SIGALG_MLDSA65) {
|
||||
if (mbedtls_pk_get_type(&ssl->session_negotiate->peer_cert->pk) !=
|
||||
MBEDTLS_PK_MLDSA65) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("signature algorithm doesn't match cert key"));
|
||||
goto error;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (!mbedtls_pk_can_do_psa(&ssl->session_negotiate->peer_cert->pk,
|
||||
mbedtls_psa_alg_from_pk_sigalg(sig_alg, hash_alg),
|
||||
PSA_KEY_USAGE_VERIFY_HASH)) {
|
||||
@@ -328,6 +347,22 @@ static int ssl_tls13_parse_certificate_verify(mbedtls_ssl_context *ssl,
|
||||
p += 2;
|
||||
MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, signature_len);
|
||||
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
if (sig_alg == MBEDTLS_PK_SIGALG_MLDSA65) {
|
||||
/* Pure ML-DSA: sign the verify structure, not a hash of it. */
|
||||
ret = mbedtls_pk_mldsa65_oqs_verify(
|
||||
&ssl->session_negotiate->peer_cert->pk,
|
||||
verify_buffer, verify_buffer_len,
|
||||
p, signature_len);
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_pk_mldsa65_oqs_verify", ret);
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = psa_hash_compute(hash_alg,
|
||||
verify_buffer,
|
||||
verify_buffer_len,
|
||||
@@ -873,6 +908,11 @@ cleanup:
|
||||
int mbedtls_ssl_tls13_check_sig_alg_cert_key_match(uint16_t sig_alg,
|
||||
mbedtls_pk_context *key)
|
||||
{
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
if (sig_alg == MBEDTLS_TLS1_3_SIG_MLDSA65) {
|
||||
return mbedtls_pk_get_type(key) == MBEDTLS_PK_MLDSA65;
|
||||
}
|
||||
#endif
|
||||
mbedtls_pk_type_t pk_type = (mbedtls_pk_type_t) mbedtls_ssl_sig_from_pk(key);
|
||||
size_t key_size = mbedtls_pk_get_bitlen(key);
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* TLS 1.3 hybrid post-quantum key exchange: X25519MLKEM768 sizes and helpers.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef MBEDTLS_SSL_TLS13_HYBRID_KEM_H
|
||||
#define MBEDTLS_SSL_TLS13_HYBRID_KEM_H
|
||||
#include "mbedtls/ssl.h"
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
|
||||
/* Key and secret sizes for the X25519MLKEM768 hybrid group
|
||||
* (draft-ietf-tls-ecdhe-mlkem-04, IANA codepoint 0x11EC). */
|
||||
#define MBEDTLS_X25519_KEY_BITS 255
|
||||
#define MBEDTLS_X25519_PUBLIC_KEY_LEN 32
|
||||
#define MBEDTLS_X25519_SHARED_SECRET_LEN 32
|
||||
#define MBEDTLS_MLKEM768_PUBLIC_KEY_LEN 1184 /* ek */
|
||||
#define MBEDTLS_MLKEM768_SECRET_KEY_LEN 2400 /* dk */
|
||||
#define MBEDTLS_MLKEM768_CIPHERTEXT_LEN 1088
|
||||
#define MBEDTLS_MLKEM768_SHARED_SECRET_LEN 32
|
||||
|
||||
/* Client key share: ML-KEM-768 ek || X25519 public key (1216 bytes). */
|
||||
#define MBEDTLS_X25519MLKEM768_CLIENT_SHARE_LEN \
|
||||
(MBEDTLS_MLKEM768_PUBLIC_KEY_LEN + MBEDTLS_X25519_PUBLIC_KEY_LEN)
|
||||
|
||||
/* Server key share: ML-KEM-768 ciphertext || X25519 public key (1120 bytes). */
|
||||
#define MBEDTLS_X25519MLKEM768_SERVER_SHARE_LEN \
|
||||
(MBEDTLS_MLKEM768_CIPHERTEXT_LEN + MBEDTLS_X25519_PUBLIC_KEY_LEN)
|
||||
|
||||
/* IKM for the TLS 1.3 key schedule: ML-KEM ss || X25519 ss (64 bytes). */
|
||||
#define MBEDTLS_X25519MLKEM768_SHARED_SECRET_LEN \
|
||||
(MBEDTLS_MLKEM768_SHARED_SECRET_LEN + MBEDTLS_X25519_SHARED_SECRET_LEN)
|
||||
|
||||
/* RFC 8446 KeyShareEntry: uint16 length prefix ahead of opaque key_exchange. */
|
||||
#define MBEDTLS_SSL_KEY_SHARE_ENTRY_LEN_SIZE 2
|
||||
|
||||
int mbedtls_ssl_tls13_hybrid_x25519mlkem768_write_client_share(
|
||||
mbedtls_ssl_context *ssl, unsigned char *buf, unsigned char *end,
|
||||
size_t *out_len);
|
||||
int mbedtls_ssl_tls13_hybrid_x25519mlkem768_read_server_share(
|
||||
mbedtls_ssl_context *ssl, const unsigned char *buf, size_t buf_len);
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768 */
|
||||
#endif /* MBEDTLS_SSL_TLS13_HYBRID_KEM_H */
|
||||
@@ -1473,6 +1473,19 @@ static int ssl_tls13_key_schedule_stage_handshake(mbedtls_ssl_context *ssl)
|
||||
|
||||
handshake->xxdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT;
|
||||
#endif /* PSA_WANT_ALG_ECDH || PSA_WANT_ALG_FFDH */
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768)
|
||||
} else if (mbedtls_ssl_tls13_named_group_is_hybrid_x25519mlkem768(
|
||||
handshake->offered_group_id)) {
|
||||
shared_secret_len = handshake->xxdh_psa_peerkey_len;
|
||||
shared_secret = mbedtls_calloc(1, shared_secret_len);
|
||||
if (shared_secret == NULL) {
|
||||
return MBEDTLS_ERR_SSL_ALLOC_FAILED;
|
||||
}
|
||||
memcpy(shared_secret, handshake->xxdh_psa_peerkey, shared_secret_len);
|
||||
mbedtls_platform_zeroize(handshake->xxdh_psa_peerkey,
|
||||
sizeof(handshake->xxdh_psa_peerkey));
|
||||
handshake->xxdh_psa_peerkey_len = 0;
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_GROUP_X25519MLKEM768 */
|
||||
} else {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("Group not supported."));
|
||||
return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
|
||||
|
||||
+59
-28
@@ -232,6 +232,16 @@ static int x509_profile_check_key(const mbedtls_x509_crt_profile *profile,
|
||||
}
|
||||
#endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65) && \
|
||||
defined(MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS)
|
||||
if (pk_alg == MBEDTLS_PK_MLDSA65) {
|
||||
if (mbedtls_pk_get_bitlen(pk) > 0) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2018,6 +2028,8 @@ static int x509_crt_verifycrl(mbedtls_x509_crt *crt, mbedtls_x509_crt *ca,
|
||||
unsigned char hash[MBEDTLS_MD_MAX_SIZE];
|
||||
psa_algorithm_t psa_algorithm;
|
||||
size_t hash_length;
|
||||
const unsigned char *verify_input;
|
||||
size_t verify_input_len;
|
||||
|
||||
if (ca == NULL) {
|
||||
return flags;
|
||||
@@ -2050,16 +2062,23 @@ static int x509_crt_verifycrl(mbedtls_x509_crt *crt, mbedtls_x509_crt *ca,
|
||||
flags |= MBEDTLS_X509_BADCRL_BAD_PK;
|
||||
}
|
||||
|
||||
psa_algorithm = mbedtls_md_psa_alg_from_type(crl_list->sig_md);
|
||||
if (psa_hash_compute(psa_algorithm,
|
||||
crl_list->tbs.p,
|
||||
crl_list->tbs.len,
|
||||
hash,
|
||||
sizeof(hash),
|
||||
&hash_length) != PSA_SUCCESS) {
|
||||
/* Note: this can't happen except after an internal error */
|
||||
flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED;
|
||||
break;
|
||||
if (crl_list->sig_pk == MBEDTLS_PK_SIGALG_MLDSA65) {
|
||||
verify_input = crl_list->tbs.p;
|
||||
verify_input_len = crl_list->tbs.len;
|
||||
} else {
|
||||
psa_algorithm = mbedtls_md_psa_alg_from_type(crl_list->sig_md);
|
||||
if (psa_hash_compute(psa_algorithm,
|
||||
crl_list->tbs.p,
|
||||
crl_list->tbs.len,
|
||||
hash,
|
||||
sizeof(hash),
|
||||
&hash_length) != PSA_SUCCESS) {
|
||||
/* Note: this can't happen except after an internal error */
|
||||
flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED;
|
||||
break;
|
||||
}
|
||||
verify_input = hash;
|
||||
verify_input_len = hash_length;
|
||||
}
|
||||
|
||||
if (x509_profile_check_key(profile, &ca->pk) != 0) {
|
||||
@@ -2067,7 +2086,7 @@ static int x509_crt_verifycrl(mbedtls_x509_crt *crt, mbedtls_x509_crt *ca,
|
||||
}
|
||||
|
||||
if (mbedtls_pk_verify_ext(crl_list->sig_pk, &ca->pk,
|
||||
crl_list->sig_md, hash, hash_length,
|
||||
crl_list->sig_md, verify_input, verify_input_len,
|
||||
crl_list->sig.p, crl_list->sig.len) != 0) {
|
||||
flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED;
|
||||
break;
|
||||
@@ -2112,30 +2131,42 @@ static int x509_crt_check_signature(const mbedtls_x509_crt *child,
|
||||
{
|
||||
size_t hash_len;
|
||||
unsigned char hash[MBEDTLS_MD_MAX_SIZE];
|
||||
psa_algorithm_t hash_alg = mbedtls_md_psa_alg_from_type(child->sig_md);
|
||||
psa_algorithm_t hash_alg;
|
||||
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
||||
const unsigned char *verify_input;
|
||||
size_t verify_input_len;
|
||||
|
||||
/* Skip expensive computation on obvious mismatch */
|
||||
if (!mbedtls_pk_can_do_psa(&parent->pk,
|
||||
mbedtls_psa_alg_from_pk_sigalg(child->sig_pk, hash_alg),
|
||||
PSA_KEY_USAGE_VERIFY_HASH)) {
|
||||
return -1;
|
||||
}
|
||||
if (child->sig_pk == MBEDTLS_PK_SIGALG_MLDSA65) {
|
||||
/* Pure ML-DSA: verify over the raw TBS, no pre-hash. */
|
||||
verify_input = child->tbs.p;
|
||||
verify_input_len = child->tbs.len;
|
||||
} else {
|
||||
hash_alg = mbedtls_md_psa_alg_from_type(child->sig_md);
|
||||
|
||||
status = psa_hash_compute(hash_alg,
|
||||
child->tbs.p,
|
||||
child->tbs.len,
|
||||
hash,
|
||||
sizeof(hash),
|
||||
&hash_len);
|
||||
if (status != PSA_SUCCESS) {
|
||||
return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
|
||||
/* Skip expensive computation on obvious mismatch */
|
||||
if (!mbedtls_pk_can_do_psa(&parent->pk,
|
||||
mbedtls_psa_alg_from_pk_sigalg(child->sig_pk, hash_alg),
|
||||
PSA_KEY_USAGE_VERIFY_HASH)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = psa_hash_compute(hash_alg,
|
||||
child->tbs.p,
|
||||
child->tbs.len,
|
||||
hash,
|
||||
sizeof(hash),
|
||||
&hash_len);
|
||||
if (status != PSA_SUCCESS) {
|
||||
return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
|
||||
}
|
||||
verify_input = hash;
|
||||
verify_input_len = hash_len;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
||||
if (rs_ctx != NULL && child->sig_pk == MBEDTLS_PK_SIGALG_ECDSA) {
|
||||
return mbedtls_pk_verify_restartable(&parent->pk,
|
||||
child->sig_md, hash, hash_len,
|
||||
child->sig_md, verify_input, verify_input_len,
|
||||
child->sig.p, child->sig.len, &rs_ctx->pk);
|
||||
}
|
||||
#else
|
||||
@@ -2143,7 +2174,7 @@ static int x509_crt_check_signature(const mbedtls_x509_crt *child,
|
||||
#endif
|
||||
|
||||
return mbedtls_pk_verify_ext(child->sig_pk, &parent->pk,
|
||||
child->sig_md, hash, hash_len,
|
||||
child->sig_md, verify_input, verify_input_len,
|
||||
child->sig.p, child->sig.len);
|
||||
}
|
||||
|
||||
|
||||
@@ -472,6 +472,13 @@ static const oid_sig_alg_t oid_sig_alg[] =
|
||||
MBEDTLS_MD_NONE, MBEDTLS_PK_SIGALG_RSA_PSS,
|
||||
},
|
||||
#endif /* PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC */
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65) && defined(PSA_WANT_ALG_SHA_512)
|
||||
{
|
||||
OID_DESCRIPTOR(MBEDTLS_OID_MLDSA65, "id-ml-dsa-65", "ML-DSA-65"),
|
||||
/* TBSCertificate is digested with SHA-512 before ML-DSA verify (X.509). */
|
||||
MBEDTLS_MD_SHA512, MBEDTLS_PK_SIGALG_MLDSA65,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
NULL_OID_DESCRIPTOR,
|
||||
MBEDTLS_MD_NONE, MBEDTLS_PK_SIGALG_NONE,
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* ML-DSA-65 (pure) signature verification using liboqs.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "tf_psa_crypto_common.h"
|
||||
|
||||
/* Requires the ML-DSA-65 integration to be enabled — this TU depends on
|
||||
* liboqs headers, which are absent when CONFIG_LIBOQS_ENABLED=n (e.g. the
|
||||
* classical-TLS baseline build). */
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65) && \
|
||||
(defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_SSL_PROTO_TLS1_3))
|
||||
|
||||
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
|
||||
#include "mbedtls/pk.h"
|
||||
#include "pk_internal.h"
|
||||
|
||||
#include "mbedtls/mldsa65_oqs.h"
|
||||
|
||||
#include "mbedtls/private/error_common.h"
|
||||
#include "mbedtls/platform.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wtypedef-redefinition"
|
||||
#endif
|
||||
#include <oqs/sig.h>
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "psa/crypto.h"
|
||||
|
||||
int mbedtls_pk_mldsa65_oqs_verify(mbedtls_pk_context *pk,
|
||||
const unsigned char *msg, size_t msg_len,
|
||||
const unsigned char *sig, size_t sig_len)
|
||||
{
|
||||
OQS_SIG *mldsa;
|
||||
OQS_STATUS status;
|
||||
|
||||
if (pk == NULL || (msg == NULL && msg_len != 0) || sig == NULL) {
|
||||
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
if (pk->pub_raw_len != MBEDTLS_MLDSA65_PUBLIC_KEY_LEN) {
|
||||
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
if (sig_len != MBEDTLS_MLDSA65_SIGNATURE_LEN) {
|
||||
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
|
||||
mldsa = OQS_SIG_new(OQS_SIG_alg_ml_dsa_65);
|
||||
if (mldsa == NULL) {
|
||||
return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
status = OQS_SIG_verify(mldsa, msg, msg_len, sig, sig_len, pk->pub_raw);
|
||||
OQS_SIG_free(mldsa);
|
||||
|
||||
if (status != OQS_SUCCESS) {
|
||||
mbedtls_printf("mldsa65_oqs: OQS_SIG_verify failed\n");
|
||||
}
|
||||
|
||||
return (status == OQS_SUCCESS) ? 0 : MBEDTLS_ERR_PK_INVALID_ALG;
|
||||
}
|
||||
|
||||
#else /* MBEDTLS_SSL_TLS1_3_SIG_MLDSA65 && (MBEDTLS_X509_USE_C || MBEDTLS_SSL_PROTO_TLS1_3) */
|
||||
|
||||
#include "mbedtls/pk.h"
|
||||
#include "mbedtls/private/error_common.h"
|
||||
#include "mbedtls/mldsa65_oqs.h"
|
||||
|
||||
int mbedtls_pk_mldsa65_oqs_verify(mbedtls_pk_context *pk,
|
||||
const unsigned char *msg, size_t msg_len,
|
||||
const unsigned char *sig, size_t sig_len)
|
||||
{
|
||||
(void) pk;
|
||||
(void) msg;
|
||||
(void) msg_len;
|
||||
(void) sig;
|
||||
(void) sig_len;
|
||||
return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_SIG_MLDSA65 && (MBEDTLS_X509_USE_C || MBEDTLS_SSL_PROTO_TLS1_3) */
|
||||
@@ -116,6 +116,8 @@ const mbedtls_pk_info_t *mbedtls_pk_info_from_type(mbedtls_pk_type_t pk_type)
|
||||
case MBEDTLS_PK_ECDSA:
|
||||
return &mbedtls_ecdsa_info;
|
||||
#endif /* PSA_HAVE_ALG_SOME_ECDSA */
|
||||
case MBEDTLS_PK_MLDSA65:
|
||||
return &mbedtls_mldsa65_info;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -1219,6 +1221,13 @@ int mbedtls_pk_verify_ext(mbedtls_pk_sigalg_t type,
|
||||
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
if (type == MBEDTLS_PK_SIGALG_MLDSA65) {
|
||||
if (mbedtls_pk_get_type(ctx) != MBEDTLS_PK_MLDSA65) {
|
||||
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
|
||||
}
|
||||
return mbedtls_pk_verify(ctx, md_alg, hash, hash_len, sig, sig_len);
|
||||
}
|
||||
|
||||
if (!mbedtls_pk_can_do(ctx, (mbedtls_pk_type_t) type)) {
|
||||
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
|
||||
}
|
||||
|
||||
@@ -664,4 +664,36 @@ const mbedtls_pk_info_t mbedtls_rsa_opaque_info = {
|
||||
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
||||
};
|
||||
|
||||
#include "mbedtls/mldsa65_oqs.h"
|
||||
|
||||
static int mldsa65_can_do(mbedtls_pk_type_t type)
|
||||
{
|
||||
return type == MBEDTLS_PK_MLDSA65;
|
||||
}
|
||||
|
||||
static int mldsa65_verify_wrap(mbedtls_pk_context *pk, mbedtls_md_type_t md_alg,
|
||||
const unsigned char *hash, size_t hash_len,
|
||||
const unsigned char *sig, size_t sig_len)
|
||||
{
|
||||
(void) md_alg;
|
||||
/* Verify exactly the byte string provided by the caller.
|
||||
* TLS 1.3 passes the full verify structure; X.509 passes the selected
|
||||
* TBSCertificate/CRL verification input from the X.509 verify path. */
|
||||
return mbedtls_pk_mldsa65_oqs_verify(pk, hash, hash_len, sig, sig_len);
|
||||
}
|
||||
|
||||
const mbedtls_pk_info_t mbedtls_mldsa65_info = {
|
||||
.type = MBEDTLS_PK_MLDSA65,
|
||||
.name = "MLDSA65",
|
||||
.can_do = mldsa65_can_do,
|
||||
.verify_func = mldsa65_verify_wrap,
|
||||
.sign_func = NULL,
|
||||
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
||||
.verify_rs_func = NULL,
|
||||
.sign_rs_func = NULL,
|
||||
.rs_alloc_func = NULL,
|
||||
.rs_free_func = NULL,
|
||||
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
||||
};
|
||||
|
||||
#endif /* MBEDTLS_PK_C */
|
||||
|
||||
@@ -87,6 +87,8 @@ extern const mbedtls_pk_info_t mbedtls_eckeydh_info;
|
||||
extern const mbedtls_pk_info_t mbedtls_ecdsa_info;
|
||||
#endif
|
||||
|
||||
extern const mbedtls_pk_info_t mbedtls_mldsa65_info;
|
||||
|
||||
extern const mbedtls_pk_info_t mbedtls_ecdsa_opaque_info;
|
||||
extern const mbedtls_pk_info_t mbedtls_rsa_opaque_info;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "mbedtls/private/error_common.h"
|
||||
#include "mbedtls/private/ecp.h"
|
||||
#include "pk_internal.h"
|
||||
#include "mbedtls/mldsa65_oqs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -497,6 +498,12 @@ static int pk_get_pk_alg(unsigned char **p,
|
||||
return MBEDTLS_ERR_PK_INVALID_ALG;
|
||||
}
|
||||
|
||||
if (*pk_alg == MBEDTLS_PK_MLDSA65 &&
|
||||
((params->tag != MBEDTLS_ASN1_NULL && params->tag != 0) ||
|
||||
params->len != 0)) {
|
||||
return MBEDTLS_ERR_PK_INVALID_ALG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -574,7 +581,22 @@ int mbedtls_pk_parse_subpubkey(unsigned char **p, const unsigned char *end,
|
||||
}
|
||||
} else
|
||||
#endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
|
||||
ret = MBEDTLS_ERR_PK_UNKNOWN_PK_ALG;
|
||||
if (pk_alg == MBEDTLS_PK_MLDSA65) {
|
||||
if (len != MBEDTLS_MLDSA65_PUBLIC_KEY_LEN) {
|
||||
ret = MBEDTLS_ERR_PK_INVALID_PUBKEY;
|
||||
} else if (len > MBEDTLS_PK_MAX_PUBKEY_RAW_LEN) {
|
||||
ret = MBEDTLS_ERR_PK_BUFFER_TOO_SMALL;
|
||||
} else {
|
||||
memcpy(pk->pub_raw, *p, len);
|
||||
pk->pub_raw_len = len;
|
||||
pk->bits = len * 8;
|
||||
*p += len;
|
||||
ret = 0;
|
||||
}
|
||||
} else
|
||||
{
|
||||
ret = MBEDTLS_ERR_PK_UNKNOWN_PK_ALG;
|
||||
}
|
||||
|
||||
if (ret == 0 && *p != end) {
|
||||
ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef MBEDTLS_MLDSA65_OQS_H
|
||||
#define MBEDTLS_MLDSA65_OQS_H
|
||||
|
||||
#include "tf-psa-crypto/build_info.h"
|
||||
|
||||
#include "mbedtls/pk.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/** ML-DSA-65 raw public key size in bytes (FIPS 204, RFC 9881). */
|
||||
#define MBEDTLS_MLDSA65_PUBLIC_KEY_LEN 1952
|
||||
|
||||
/** ML-DSA-65 signature size in bytes (FIPS 204, RFC 9881). */
|
||||
#define MBEDTLS_MLDSA65_SIGNATURE_LEN 3309
|
||||
|
||||
/**
|
||||
* \brief Verify a pure ML-DSA-65 signature (liboqs).
|
||||
*
|
||||
* \param msg Message that was signed (e.g. TLS 1.3 verify structure, or
|
||||
* digest for X.509 depending on the caller).
|
||||
* \param msg_len Length of \p msg.
|
||||
*/
|
||||
int mbedtls_pk_mldsa65_oqs_verify(mbedtls_pk_context *pk,
|
||||
const unsigned char *msg, size_t msg_len,
|
||||
const unsigned char *sig, size_t sig_len);
|
||||
|
||||
#endif /* MBEDTLS_MLDSA65_OQS_H */
|
||||
@@ -51,6 +51,8 @@ typedef enum {
|
||||
MBEDTLS_PK_SIGALG_RSA_PKCS1V15, // PSA_ALG_RSA_PKCS1V15_SIGN
|
||||
MBEDTLS_PK_SIGALG_RSA_PSS, // PSA_ALG_RSA_PSS_ANY_SALT
|
||||
MBEDTLS_PK_SIGALG_ECDSA, // MBEDTLS_PK_ALG_ECDSA
|
||||
/** ML-DSA-65 (FIPS 204, RFC 9881); must match \c MBEDTLS_PK_MLDSA65 */
|
||||
MBEDTLS_PK_SIGALG_MLDSA65 = 7,
|
||||
} mbedtls_pk_sigalg_t;
|
||||
|
||||
/**
|
||||
@@ -100,6 +102,20 @@ typedef struct mbedtls_pk_info_t mbedtls_pk_info_t;
|
||||
#define MBEDTLS_PK_MAX_PUBKEY_RAW_LEN MBEDTLS_PK_MAX_RSA_PUBKEY_RAW_LEN
|
||||
#endif
|
||||
|
||||
/* PQC pubkey headroom, only when ML-DSA-65 is enabled since pub_raw is inline in every mbedtls_pk_context and some live on the stack; 1952 is the FIPS 204 raw key length pkparse.c copies in, spelled out because mldsa65_oqs.h includes this header. */
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_SIG_MLDSA65)
|
||||
#define MBEDTLS_PK_PQC_MAX_PUBKEY_RAW_LEN 1952
|
||||
#if MBEDTLS_PK_PQC_MAX_PUBKEY_RAW_LEN > MBEDTLS_PK_MAX_PUBKEY_RAW_LEN
|
||||
#undef MBEDTLS_PK_MAX_PUBKEY_RAW_LEN
|
||||
#define MBEDTLS_PK_MAX_PUBKEY_RAW_LEN MBEDTLS_PK_PQC_MAX_PUBKEY_RAW_LEN
|
||||
#endif
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_SIG_MLDSA65 */
|
||||
|
||||
typedef enum {
|
||||
MBEDTLS_PK_RSA_PKCS_V15 = 0,
|
||||
MBEDTLS_PK_RSA_PKCS_V21,
|
||||
} mbedtls_pk_rsa_padding_t;
|
||||
|
||||
/**
|
||||
* \brief Public key container
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef enum {
|
||||
MBEDTLS_PK_ECKEY,
|
||||
MBEDTLS_PK_ECKEY_DH,
|
||||
MBEDTLS_PK_OPAQUE,
|
||||
MBEDTLS_PK_MLDSA65 = 7,
|
||||
} mbedtls_pk_type_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -160,6 +160,9 @@
|
||||
#define MBEDTLS_OID_PKCS5_PBKDF2 MBEDTLS_OID_PKCS5 "\x0c" /**< id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} */
|
||||
#define MBEDTLS_OID_PKCS5_PBES2 MBEDTLS_OID_PKCS5 "\x0d" /**< id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} */
|
||||
|
||||
/** id-ml-dsa-65 (RFC 9881) */
|
||||
#define MBEDTLS_OID_MLDSA65 MBEDTLS_OID_NIST_ALG "\x03\x12" /**< id-ml-dsa-65 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) sigAlgs(3) 18 } */
|
||||
|
||||
/*
|
||||
* EC key algorithms from RFC 5480
|
||||
*/
|
||||
|
||||
@@ -132,6 +132,10 @@ static const oid_pk_alg_t oid_pk_alg[] =
|
||||
OID_DESCRIPTOR(MBEDTLS_OID_EC_ALG_ECDH, "id-ecDH", "EC key for ECDH"),
|
||||
MBEDTLS_PK_ECKEY_DH,
|
||||
},
|
||||
{
|
||||
OID_DESCRIPTOR(MBEDTLS_OID_MLDSA65, "id-ml-dsa-65", "ML-DSA-65"),
|
||||
MBEDTLS_PK_MLDSA65,
|
||||
},
|
||||
{
|
||||
NULL_OID_DESCRIPTOR,
|
||||
MBEDTLS_PK_NONE,
|
||||
|
||||
Reference in New Issue
Block a user