Merge pull request #6283 from mpg/driver-only-hashes-wrap-up
Driver only hashes wrap-up
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cea12f1..319f02d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,8 +19,6 @@
1. Write a test which shows that the bug was fixed or that the feature works as expected.
1. Send a pull request (PR) and work with us until it gets merged and published. Contributions may need some modifications, so a few rounds of review and fixing may be necessary. We will include your name in the ChangeLog :)
1. For quick merging, the contribution should be short, and concentrated on a single feature or topic. The larger the contribution is, the longer it would take to review it and merge it.
-1. All new files should include the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) standard license header where possible.
-1. Ensure that each commit has at least one `Signed-off-by:` line from the committer. If anyone else contributes to the commit, they should also add their own `Signed-off-by:` line. By adding this line, contributor(s) certify that the contribution is made under the terms of the [Developer Certificate of Origin](dco.txt). The contribution licensing is described in the [License section of the README](README.md#License).
Backwards Compatibility
-----------------------
@@ -79,3 +77,12 @@
1. If needed, a Readme file is advised.
1. If a [Knowledge Base (KB)](https://tls.mbed.org/kb) article should be added, write this as a comment in the PR description.
1. A [ChangeLog](https://github.com/Mbed-TLS/mbedtls/blob/development/ChangeLog.d/00README.md) entry should be added for this contribution.
+
+License and Copyright
+---------------------
+
+All new files should include the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) standard license header where possible. For licensing details, please see the [License section of the README](README.md#License).
+
+The copyright on contributions is retained by the original authors of the code. Where possible for new files, this should be noted in a comment at the top of the file in the form: "Copyright The Mbed TLS Contributors".
+
+When contributing code to us, the committer and all authors are required to make the submission under the terms of the [Developer Certificate of Origin](dco.txt), confirming that the code submitted can (legally) become part of the project, and be subject to the same Apache 2.0 license. This is done by including the standard Git `Signed-off-by:` line in every commit message. If more than one person contributed to the commit, they should also add their own `Signed-off-by:` line.
diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h
index f6ecdbf..8359a9f 100644
--- a/include/mbedtls/mbedtls_config.h
+++ b/include/mbedtls/mbedtls_config.h
@@ -1551,6 +1551,26 @@
//#define MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE
/**
+ * \def MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE
+ *
+ * Maximum time difference in milliseconds tolerated between the age of a
+ * ticket from the server and client point of view.
+ * From the client point of view, the age of a ticket is the time difference
+ * between the time when the client proposes to the server to use the ticket
+ * (time of writing of the Pre-Shared Key Extension including the ticket) and
+ * the time the client received the ticket from the server.
+ * From the server point of view, the age of a ticket is the time difference
+ * between the time when the server receives a proposition from the client
+ * to use the ticket and the time when the ticket was created by the server.
+ * The server age is expected to be always greater than the client one and
+ * MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE defines the
+ * maximum difference tolerated for the server to accept the ticket.
+ * This is not used in TLS 1.2.
+ *
+ */
+#define MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE 6000
+
+/**
* \def MBEDTLS_SSL_TLS1_3_TICKET_NONCE_LENGTH
*
* Size in bytes of a ticket nonce. This is not used in TLS 1.2.
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index edbf446..2e35e6c 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -2457,16 +2457,6 @@
#endif
#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED)
-/* Check if we have any PSK to offer, returns 0 if PSK is available.
- * Assign the psk and ticket if pointers are present.
- */
-MBEDTLS_CHECK_RETURN_CRITICAL
-int mbedtls_ssl_get_psk_to_offer(
- const mbedtls_ssl_context *ssl,
- int *psk_type,
- const unsigned char **psk, size_t *psk_len,
- const unsigned char **psk_identity, size_t *psk_identity_len );
-
/**
* \brief Given an SSL context and its associated configuration, write the TLS
* 1.3 specific Pre-Shared key extension.
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index 505f8dd..33e8cc6 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -664,10 +664,59 @@
ssl->handshake->extensions_present |= MBEDTLS_SSL_EXT_PSK_KEY_EXCHANGE_MODES;
return ( 0 );
}
-#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */
+
+/* Check if we have any PSK to offer, returns 0 if a PSK is available. */
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls13_get_psk_to_offer(
+ const mbedtls_ssl_context *ssl,
+ int *psk_type,
+ const unsigned char **psk, size_t *psk_len,
+ const unsigned char **psk_identity, size_t *psk_identity_len )
+{
+ *psk = NULL;
+ *psk_len = 0;
+ *psk_identity = NULL;
+ *psk_identity_len = 0;
+ *psk_type = MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL;
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ /* Check if a ticket has been configured. */
+ if( ssl->session_negotiate != NULL &&
+ ssl->session_negotiate->ticket != NULL )
+ {
+#if defined(MBEDTLS_HAVE_TIME)
+ mbedtls_time_t now = mbedtls_time( NULL );
+ if( ssl->session_negotiate->ticket_received <= now &&
+ (uint64_t)( now - ssl->session_negotiate->ticket_received )
+ <= ssl->session_negotiate->ticket_lifetime )
+ {
+ *psk_type = MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION;
+ *psk = ssl->session_negotiate->resumption_key;
+ *psk_len = ssl->session_negotiate->resumption_key_len;
+ *psk_identity = ssl->session_negotiate->ticket;
+ *psk_identity_len = ssl->session_negotiate->ticket_len;
+ return( 0 );
+ }
+#endif /* MBEDTLS_HAVE_TIME */
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ticket expired" ) );
+ }
+#endif
+
+ /* Check if an external PSK has been configured. */
+ if( ssl->conf->psk != NULL )
+ {
+ *psk = ssl->conf->psk;
+ *psk_len = ssl->conf->psk_len;
+ *psk_identity = ssl->conf->psk_identity;
+ *psk_identity_len = ssl->conf->psk_identity_len;
+ return( 0 );
+ }
+
+ return( MBEDTLS_ERR_ERROR_GENERIC_ERROR );
+}
/*
- * mbedtls_ssl_tls13_write_pre_shared_key_ext() structure:
+ * mbedtls_ssl_tls13_write_identities_of_pre_shared_key_ext() structure:
*
* struct {
* opaque identity<1..2^16-1>;
@@ -689,9 +738,6 @@
* } PreSharedKeyExtension;
*
*/
-
-#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED)
-
int mbedtls_ssl_tls13_write_identities_of_pre_shared_key_ext(
mbedtls_ssl_context *ssl,
unsigned char *buf, unsigned char *end,
@@ -725,9 +771,8 @@
* configured, offer that.
* - Otherwise, skip the PSK extension.
*/
-
- if( mbedtls_ssl_get_psk_to_offer( ssl, &psk_type, &psk, &psk_len,
- &psk_identity, &psk_identity_len ) != 0 )
+ if( ssl_tls13_get_psk_to_offer( ssl, &psk_type, &psk, &psk_len,
+ &psk_identity, &psk_identity_len ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 3, ( "skip pre_shared_key extensions" ) );
return( 0 );
@@ -757,6 +802,26 @@
break;
}
}
+ else
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ if( psk_type == MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION )
+ {
+#if defined(MBEDTLS_HAVE_TIME)
+ mbedtls_time_t now = mbedtls_time( NULL );
+
+ obfuscated_ticket_age =
+ ( (uint32_t)( now - ssl->session_negotiate->ticket_received ) * 1000 )
+ + ssl->session_negotiate->ticket_age_add;
+#endif
+ }
+ else
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "write_identities_of_pre_shared_key_ext: "
+ "should never happen" ) );
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+ }
+
ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(
ssl->session_negotiate->ciphersuite );
@@ -831,8 +896,8 @@
unsigned char transcript[MBEDTLS_MD_MAX_SIZE];
size_t transcript_len;
- if( mbedtls_ssl_get_psk_to_offer( ssl, &psk_type, &psk, &psk_len,
- &psk_identity, &psk_identity_len ) != 0 )
+ if( ssl_tls13_get_psk_to_offer( ssl, &psk_type, &psk, &psk_len,
+ &psk_identity, &psk_identity_len ) != 0 )
{
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
@@ -1266,15 +1331,15 @@
size_t psk_len;
const unsigned char *psk_identity;
size_t psk_identity_len;
-
+ int psk_type;
/* Check which PSK we've offered.
*
* NOTE: Ultimately, we want to offer multiple PSKs, and in this
* case, we need to iterate over them here.
*/
- if( mbedtls_ssl_get_psk_to_offer( ssl, NULL, &psk, &psk_len,
- &psk_identity, &psk_identity_len ) != 0 )
+ if( ssl_tls13_get_psk_to_offer( ssl, &psk_type, &psk, &psk_len,
+ &psk_identity, &psk_identity_len ) != 0 )
{
/* If we haven't offered a PSK, the server must not send
* a PSK identity extension. */
@@ -1622,16 +1687,19 @@
/* Only the pre_shared_key extension was received */
case MBEDTLS_SSL_EXT_PRE_SHARED_KEY:
handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK;
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "key exchange mode: psk" ) );
break;
/* Only the key_share extension was received */
case MBEDTLS_SSL_EXT_KEY_SHARE:
handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL;
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "key exchange mode: ephemeral" ) );
break;
/* Both the pre_shared_key and key_share extensions were received */
case ( MBEDTLS_SSL_EXT_PRE_SHARED_KEY | MBEDTLS_SSL_EXT_KEY_SHARE ):
handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL;
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "key exchange mode: psk_ephemeral" ) );
break;
/* Neither pre_shared_key nor key_share extension was received */
@@ -1819,7 +1887,12 @@
*/
switch( extension_type )
{
+ case MBEDTLS_TLS_EXT_SERVERNAME:
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "found server_name extension" ) );
+ /* The server_name extension should be an empty extension */
+
+ break;
case MBEDTLS_TLS_EXT_SUPPORTED_GROUPS:
MBEDTLS_SSL_DEBUG_MSG( 3, ( "found extensions supported groups" ) );
break;
@@ -2237,11 +2310,11 @@
if( ret != 0 )
return( ret );
- ret = mbedtls_ssl_tls13_generate_resumption_master_secret( ssl );
+ ret = mbedtls_ssl_tls13_compute_resumption_master_secret( ssl );
if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1,
- "mbedtls_ssl_tls13_generate_resumption_master_secret ", ret );
+ "mbedtls_ssl_tls13_compute_resumption_master_secret ", ret );
return ( ret );
}
@@ -2405,6 +2478,9 @@
return( ret );
}
+ /* session has been updated, allow export */
+ session->exported = 0;
+
return( 0 );
}
diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c
index 6f60fab..f74cb40 100644
--- a/library/ssl_tls13_generic.c
+++ b/library/ssl_tls13_generic.c
@@ -1504,41 +1504,4 @@
}
#endif /* MBEDTLS_ECDH_C */
-#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED)
-/* Check if we have any PSK to offer, returns 0 if PSK is available.
- * Assign the psk and ticket if pointers are present.
- */
-int mbedtls_ssl_get_psk_to_offer(
- const mbedtls_ssl_context *ssl,
- int *psk_type,
- const unsigned char **psk, size_t *psk_len,
- const unsigned char **psk_identity, size_t *psk_identity_len )
-{
- int ptrs_present = 0;
-
- if( psk_type != NULL && psk != NULL && psk_len != NULL &&
- psk_identity != NULL && psk_identity_len != NULL )
- {
- ptrs_present = 1;
- }
-
- /* Check if an external PSK has been configured. */
- if( ssl->conf->psk != NULL )
- {
- if( ptrs_present )
- {
- *psk_type = MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL;
- *psk = ssl->conf->psk;
- *psk_len = ssl->conf->psk_len;
- *psk_identity = ssl->conf->psk_identity;
- *psk_identity_len = ssl->conf->psk_identity_len;
- }
-
- return( 0 );
- }
-
- return( 1 );
-}
-#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */
-
#endif /* MBEDTLS_SSL_TLS_C && MBEDTLS_SSL_PROTO_TLS1_3 */
diff --git a/library/ssl_tls13_keys.c b/library/ssl_tls13_keys.c
index 32a4f2a..48de3d0 100644
--- a/library/ssl_tls13_keys.c
+++ b/library/ssl_tls13_keys.c
@@ -1504,12 +1504,43 @@
return( ret );
}
-int mbedtls_ssl_tls13_generate_resumption_master_secret(
- mbedtls_ssl_context *ssl )
+int mbedtls_ssl_tls13_compute_resumption_master_secret( mbedtls_ssl_context *ssl )
{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ mbedtls_md_type_t md_type;
+ mbedtls_ssl_handshake_params *handshake = ssl->handshake;
+ unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE];
+ size_t transcript_len;
+
+ MBEDTLS_SSL_DEBUG_MSG( 2,
+ ( "=> mbedtls_ssl_tls13_compute_resumption_master_secret" ) );
+
+ md_type = handshake->ciphersuite_info->mac;
+
+ ret = mbedtls_ssl_get_handshake_transcript( ssl, md_type,
+ transcript, sizeof( transcript ),
+ &transcript_len );
+ if( ret != 0 )
+ return( ret );
+
+ ret = mbedtls_ssl_tls13_derive_resumption_master_secret(
+ mbedtls_psa_translate_md( md_type ),
+ handshake->tls13_master_secrets.app,
+ transcript, transcript_len,
+ &ssl->session_negotiate->app_secrets );
+ if( ret != 0 )
+ return( ret );
+
/* Erase master secrets */
- mbedtls_platform_zeroize( &ssl->handshake->tls13_master_secrets,
- sizeof( ssl->handshake->tls13_master_secrets ) );
+ mbedtls_platform_zeroize( &handshake->tls13_master_secrets,
+ sizeof( handshake->tls13_master_secrets ) );
+
+ MBEDTLS_SSL_DEBUG_BUF( 4, "Resumption master secret",
+ ssl->session_negotiate->app_secrets.resumption_master_secret,
+ PSA_HASH_LENGTH( mbedtls_psa_translate_md( md_type ) ) ) ;
+
+ MBEDTLS_SSL_DEBUG_MSG( 2,
+ ( "<= mbedtls_ssl_tls13_compute_resumption_master_secret" ) );
return( 0 );
}
diff --git a/library/ssl_tls13_keys.h b/library/ssl_tls13_keys.h
index d82bf7a..f3bdf37 100644
--- a/library/ssl_tls13_keys.h
+++ b/library/ssl_tls13_keys.h
@@ -636,8 +636,7 @@
* \returns A negative error code on failure.
*/
MBEDTLS_CHECK_RETURN_CRITICAL
-int mbedtls_ssl_tls13_generate_resumption_master_secret(
- mbedtls_ssl_context *ssl );
+int mbedtls_ssl_tls13_compute_resumption_master_secret( mbedtls_ssl_context *ssl );
/**
* \brief Calculate the verify_data value for the client or server TLS 1.3
diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c
index a10e59b..e185dc1 100644
--- a/library/ssl_tls13_server.c
+++ b/library/ssl_tls13_server.c
@@ -121,14 +121,170 @@
#define SSL_TLS1_3_OFFERED_PSK_NOT_MATCH 1
#define SSL_TLS1_3_OFFERED_PSK_MATCH 0
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls13_offered_psks_check_identity_match_ticket(
+ mbedtls_ssl_context *ssl,
+ const unsigned char *identity,
+ size_t identity_len,
+ uint32_t obfuscated_ticket_age,
+ mbedtls_ssl_session *session )
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ unsigned char *ticket_buffer;
+#if defined(MBEDTLS_HAVE_TIME)
+ mbedtls_time_t now;
+ uint64_t age_in_s;
+ int64_t age_diff_in_ms;
+#endif
+
+ ((void) obfuscated_ticket_age);
+
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> check_identity_match_ticket" ) );
+
+ /* Ticket parser is not configured, Skip */
+ if( ssl->conf->f_ticket_parse == NULL || identity_len == 0 )
+ return( 0 );
+
+ /* We create a copy of the encrypted ticket since the ticket parsing
+ * function is allowed to use its input buffer as an output buffer
+ * (in-place decryption). We do, however, need the original buffer for
+ * computing the PSK binder value.
+ */
+ ticket_buffer = mbedtls_calloc( 1, identity_len );
+ if( ticket_buffer == NULL )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "buffer too small" ) );
+ return ( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+ }
+ memcpy( ticket_buffer, identity, identity_len );
+
+ if( ( ret = ssl->conf->f_ticket_parse( ssl->conf->p_ticket,
+ session,
+ ticket_buffer, identity_len ) ) != 0 )
+ {
+ if( ret == MBEDTLS_ERR_SSL_INVALID_MAC )
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ticket is not authentic" ) );
+ else if( ret == MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED )
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ticket is expired" ) );
+ else
+ MBEDTLS_SSL_DEBUG_RET( 1, "ticket_parse", ret );
+ }
+
+ /* We delete the temporary buffer */
+ mbedtls_free( ticket_buffer );
+
+ if( ret != 0 )
+ goto exit;
+
+ ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
+#if defined(MBEDTLS_HAVE_TIME)
+ now = mbedtls_time( NULL );
+
+ if( now < session->start )
+ {
+ MBEDTLS_SSL_DEBUG_MSG(
+ 3, ( "Ticket expired: now=%" MBEDTLS_PRINTF_LONGLONG
+ ", start=%" MBEDTLS_PRINTF_LONGLONG,
+ (long long)now, (long long)session->start ) );
+ goto exit;
+ }
+
+ age_in_s = (uint64_t)( now - session->start );
+
+ /* RFC 8446 section 4.6.1
+ *
+ * Servers MUST NOT use any value greater than 604800 seconds (7 days).
+ *
+ * RFC 8446 section 4.2.11.1
+ *
+ * Clients MUST NOT attempt to use tickets which have ages greater than
+ * the "ticket_lifetime" value which was provided with the ticket.
+ *
+ * For time being, the age MUST be less than 604800 seconds (7 days).
+ */
+ if( age_in_s > 604800 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG(
+ 3, ( "Ticket expired: Ticket age exceed limitation ticket_age=%lu",
+ (long unsigned int)age_in_s ) );
+ goto exit;
+ }
+
+ /* RFC 8446 section 4.2.10
+ *
+ * For PSKs provisioned via NewSessionTicket, a server MUST validate that
+ * the ticket age for the selected PSK identity (computed by subtracting
+ * ticket_age_add from PskIdentity.obfuscated_ticket_age modulo 2^32) is
+ * within a small tolerance of the time since the ticket was issued.
+ *
+ * NOTE: When `now == session->start`, `age_diff_in_ms` may be negative
+ * as the age units are different on the server (s) and in the
+ * client (ms) side. Add a -1000 ms tolerance window to take this
+ * into account.
+ */
+ age_diff_in_ms = age_in_s * 1000;
+ age_diff_in_ms -= ( obfuscated_ticket_age - session->ticket_age_add );
+ if( age_diff_in_ms <= -1000 ||
+ age_diff_in_ms > MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE )
+ {
+ MBEDTLS_SSL_DEBUG_MSG(
+ 3, ( "Ticket expired: Ticket age outside tolerance window "
+ "( diff=%d )", (int)age_diff_in_ms ) );
+ goto exit;
+ }
+
+ ret = 0;
+
+#endif /* MBEDTLS_HAVE_TIME */
+
+exit:
+ if( ret != 0 )
+ mbedtls_ssl_session_free( session );
+
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= check_identity_match_ticket" ) );
+ return( ret );
+}
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
MBEDTLS_CHECK_RETURN_CRITICAL
static int ssl_tls13_offered_psks_check_identity_match(
mbedtls_ssl_context *ssl,
const unsigned char *identity,
size_t identity_len,
- int *psk_type )
+ uint32_t obfuscated_ticket_age,
+ int *psk_type,
+ mbedtls_ssl_session *session )
{
+ ((void) session);
+ ((void) obfuscated_ticket_age);
*psk_type = MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL;
+
+ MBEDTLS_SSL_DEBUG_BUF( 4, "identity", identity, identity_len );
+ ssl->handshake->resume = 0;
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ if( ssl_tls13_offered_psks_check_identity_match_ticket(
+ ssl, identity, identity_len, obfuscated_ticket_age,
+ session ) == SSL_TLS1_3_OFFERED_PSK_MATCH )
+ {
+ ssl->handshake->resume = 1;
+ *psk_type = MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION;
+ mbedtls_ssl_set_hs_psk( ssl,
+ session->resumption_key,
+ session->resumption_key_len );
+
+ MBEDTLS_SSL_DEBUG_BUF( 4, "Ticket-resumed PSK:",
+ session->resumption_key,
+ session->resumption_key_len );
+ MBEDTLS_SSL_DEBUG_MSG( 4, ( "ticket: obfuscated_ticket_age: %u",
+ (unsigned)obfuscated_ticket_age ) );
+ return( SSL_TLS1_3_OFFERED_PSK_MATCH );
+ }
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
/* Check identity with external configured function */
if( ssl->conf->f_psk != NULL )
{
@@ -256,6 +412,7 @@
return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
}
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
MBEDTLS_CHECK_RETURN_CRITICAL
static int ssl_tls13_select_ciphersuite_for_resumption(
mbedtls_ssl_context *ssl,
@@ -265,15 +422,46 @@
uint16_t *selected_ciphersuite,
const mbedtls_ssl_ciphersuite_t **selected_ciphersuite_info )
{
- ((void) ssl);
- ((void) session);
- ((void) cipher_suites);
- ((void) cipher_suites_end);
+
*selected_ciphersuite = 0;
*selected_ciphersuite_info = NULL;
- return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+ for( const unsigned char *p = cipher_suites; p < cipher_suites_end; p += 2 )
+ {
+ uint16_t cipher_suite = MBEDTLS_GET_UINT16_BE( p, 0 );
+ const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
+
+ if( cipher_suite != session->ciphersuite )
+ continue;
+
+ ciphersuite_info = ssl_tls13_validate_peer_ciphersuite( ssl,
+ cipher_suite );
+ if( ciphersuite_info == NULL )
+ continue;
+
+ *selected_ciphersuite = cipher_suite;
+ *selected_ciphersuite_info = ciphersuite_info;
+
+ return( 0 );
+ }
+
+ return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
}
+MBEDTLS_CHECK_RETURN_CRITICAL
+static int ssl_tls13_session_copy_ticket( mbedtls_ssl_session *dst,
+ const mbedtls_ssl_session *src )
+{
+ dst->ticket_age_add = src->ticket_age_add;
+ dst->ticket_flags = src->ticket_flags;
+ dst->resumption_key_len = src->resumption_key_len;
+ if( src->resumption_key_len == 0 )
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+ memcpy( dst->resumption_key, src->resumption_key, src->resumption_key_len );
+
+ return( 0 );
+}
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
/* Parser for pre_shared_key extension in client hello
* struct {
* opaque identity<1..2^16-1>;
@@ -343,17 +531,23 @@
{
const unsigned char *identity;
size_t identity_len;
+ uint32_t obfuscated_ticket_age;
const unsigned char *binder;
size_t binder_len;
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
int psk_type;
uint16_t cipher_suite;
const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ mbedtls_ssl_session session;
+ mbedtls_ssl_session_init( &session );
+#endif
MBEDTLS_SSL_CHK_BUF_READ_PTR( p_identity_len, identities_end, 2 + 1 + 4 );
identity_len = MBEDTLS_GET_UINT16_BE( p_identity_len, 0 );
identity = p_identity_len + 2;
MBEDTLS_SSL_CHK_BUF_READ_PTR( identity, identities_end, identity_len + 4 );
+ obfuscated_ticket_age = MBEDTLS_GET_UINT32_BE( identity , identity_len );
p_identity_len += identity_len + 6;
MBEDTLS_SSL_CHK_BUF_READ_PTR( p_binder_len, binders_end, 1 + 32 );
@@ -367,7 +561,8 @@
continue;
ret = ssl_tls13_offered_psks_check_identity_match(
- ssl, identity, identity_len, &psk_type );
+ ssl, identity, identity_len, obfuscated_ticket_age,
+ &psk_type, &session );
if( ret != SSL_TLS1_3_OFFERED_PSK_MATCH )
continue;
@@ -380,9 +575,15 @@
&cipher_suite, &ciphersuite_info );
break;
case MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION:
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
ret = ssl_tls13_select_ciphersuite_for_resumption(
- ssl, ciphersuites, ciphersuites_end, NULL,
+ ssl, ciphersuites, ciphersuites_end, &session,
&cipher_suite, &ciphersuite_info );
+ if( ret != 0 )
+ mbedtls_ssl_session_free( &session );
+#else
+ ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
+#endif
break;
default:
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
@@ -406,6 +607,9 @@
/* For security reasons, the handshake should be aborted when we
* fail to validate a binder value. See RFC 8446 section 4.2.11.2
* and appendix E.6. */
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ mbedtls_ssl_session_free( &session );
+#endif
MBEDTLS_SSL_DEBUG_MSG( 3, ( "Invalid binder." ) );
MBEDTLS_SSL_DEBUG_RET( 1,
"ssl_tls13_offered_psks_check_binder_match" , ret );
@@ -418,11 +622,20 @@
matched_identity = identity_id;
/* Update handshake parameters */
- ssl->session_negotiate->ciphersuite = cipher_suite;
ssl->handshake->ciphersuite_info = ciphersuite_info;
+ ssl->session_negotiate->ciphersuite = cipher_suite;
MBEDTLS_SSL_DEBUG_MSG( 2, ( "overwrite ciphersuite: %04x - %s",
cipher_suite, ciphersuite_info->name ) );
-
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ if( psk_type == MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION )
+ {
+ ret = ssl_tls13_session_copy_ticket( ssl->session_negotiate,
+ &session );
+ mbedtls_ssl_session_free( &session );
+ if( ret != 0 )
+ return( ret );
+ }
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
}
if( p_identity_len != identities_end || p_binder_len != binders_end )
@@ -2366,11 +2579,11 @@
if( ret != 0 )
return( ret );
- ret = mbedtls_ssl_tls13_generate_resumption_master_secret( ssl );
+ ret = mbedtls_ssl_tls13_compute_resumption_master_secret( ssl );
if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1,
- "mbedtls_ssl_tls13_generate_resumption_master_secret ", ret );
+ "mbedtls_ssl_tls13_compute_resumption_master_secret", ret );
}
mbedtls_ssl_handshake_set_state( ssl, MBEDTLS_SSL_HANDSHAKE_WRAPUP );
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index da85eb0..6beaa12 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -657,6 +657,58 @@
}
#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
+static int ssl_save_session_serialize( mbedtls_ssl_context *ssl,
+ unsigned char **session_data,
+ size_t *session_data_len )
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ mbedtls_ssl_session exported_session;
+
+ /* free any previously saved data */
+ if( *session_data != NULL )
+ {
+ mbedtls_platform_zeroize( *session_data, *session_data_len );
+ mbedtls_free( *session_data );
+ *session_data = NULL;
+ *session_data_len = 0;
+ }
+
+ mbedtls_ssl_session_init( &exported_session );
+ ret = mbedtls_ssl_get_session( ssl, &exported_session );
+ if( ret != 0 )
+ {
+ mbedtls_printf(
+ "failed\n ! mbedtls_ssl_get_session() returned -%#02x\n",
+ (unsigned) -ret );
+ goto exit;
+ }
+
+ /* get size of the buffer needed */
+ mbedtls_ssl_session_save( &exported_session, NULL, 0, session_data_len );
+ *session_data = mbedtls_calloc( 1, *session_data_len );
+ if( *session_data == NULL )
+ {
+ mbedtls_printf( " failed\n ! alloc %u bytes for session data\n",
+ (unsigned) *session_data_len );
+ ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
+ goto exit;
+ }
+
+ /* actually save session data */
+ if( ( ret = mbedtls_ssl_session_save( &exported_session,
+ *session_data, *session_data_len,
+ session_data_len ) ) != 0 )
+ {
+ mbedtls_printf( " failed\n ! mbedtls_ssl_session_saved returned -0x%04x\n\n",
+ (unsigned int) -ret );
+ goto exit;
+ }
+
+exit:
+ mbedtls_ssl_session_free( &exported_session );
+ return( ret );
+}
+
int main( int argc, char *argv[] )
{
int ret = 0, len, tail_len, i, written, frags, retry_left;
@@ -2360,57 +2412,21 @@
}
}
#endif /* MBEDTLS_SSL_DTLS_SRTP */
- if( opt.reconnect != 0 )
+ if( opt.reconnect != 0 && ssl.tls_version != MBEDTLS_SSL_VERSION_TLS1_3 )
{
mbedtls_printf(" . Saving session for reuse..." );
fflush( stdout );
if( opt.reco_mode == 1 )
{
- mbedtls_ssl_session exported_session;
-
- /* free any previously saved data */
- if( session_data != NULL )
+ if( ( ret = ssl_save_session_serialize( &ssl,
+ &session_data, &session_data_len ) ) != 0 )
{
- mbedtls_platform_zeroize( session_data, session_data_len );
- mbedtls_free( session_data );
- session_data = NULL;
- }
-
- mbedtls_ssl_session_init( &exported_session );
- ret = mbedtls_ssl_get_session( &ssl, &exported_session );
- if( ret != 0 )
- {
- mbedtls_printf(
- "failed\n ! mbedtls_ssl_get_session() returned -%#02x\n",
- (unsigned) -ret );
- goto exit;
- }
-
- /* get size of the buffer needed */
- mbedtls_ssl_session_save( &exported_session, NULL, 0, &session_data_len );
- session_data = mbedtls_calloc( 1, session_data_len );
- if( session_data == NULL )
- {
- mbedtls_printf( " failed\n ! alloc %u bytes for session data\n",
- (unsigned) session_data_len );
- mbedtls_ssl_session_free( &exported_session );
- ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
- goto exit;
- }
-
- /* actually save session data */
- if( ( ret = mbedtls_ssl_session_save( &exported_session,
- session_data, session_data_len,
- &session_data_len ) ) != 0 )
- {
- mbedtls_printf( " failed\n ! mbedtls_ssl_session_saved returned -0x%04x\n\n",
+ mbedtls_printf( " failed\n ! ssl_save_session_serialize returned -0x%04x\n\n",
(unsigned int) -ret );
- mbedtls_ssl_session_free( &exported_session );
goto exit;
}
- mbedtls_ssl_session_free( &exported_session );
}
else
{
@@ -2700,6 +2716,40 @@
/* We were waiting for application data but got
* a NewSessionTicket instead. */
mbedtls_printf( " got new session ticket.\n" );
+ if( opt.reconnect != 0 )
+ {
+ mbedtls_printf(" . Saving session for reuse..." );
+ fflush( stdout );
+
+ if( opt.reco_mode == 1 )
+ {
+ if( ( ret = ssl_save_session_serialize( &ssl,
+ &session_data, &session_data_len ) ) != 0 )
+ {
+ mbedtls_printf( " failed\n ! ssl_save_session_serialize returned -0x%04x\n\n",
+ (unsigned int) -ret );
+ goto exit;
+ }
+ }
+ else
+ {
+ if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 )
+ {
+ mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n",
+ (unsigned int) -ret );
+ goto exit;
+ }
+ }
+
+ mbedtls_printf( " ok\n" );
+
+ if( opt.reco_mode == 1 )
+ {
+ mbedtls_printf( " [ Saved %u bytes of session data]\n",
+ (unsigned) session_data_len );
+ }
+ }
+
continue;
#endif /* MBEDTLS_SSL_SESSION_TICKETS */
diff --git a/scripts/make_generated_files.bat b/scripts/make_generated_files.bat
index 662da98..e9d9275 100644
--- a/scripts/make_generated_files.bat
+++ b/scripts/make_generated_files.bat
@@ -10,4 +10,5 @@
python scripts\generate_ssl_debug_helpers.py || exit /b 1
perl scripts\generate_visualc_files.pl || exit /b 1
python scripts\generate_psa_constants.py || exit /b 1
+python tests\scripts\generate_bignum_tests.py || exit /b 1
python tests\scripts\generate_psa_tests.py || exit /b 1
diff --git a/scripts/mbedtls_dev/test_generation.py b/scripts/mbedtls_dev/test_generation.py
new file mode 100644
index 0000000..a88425f
--- /dev/null
+++ b/scripts/mbedtls_dev/test_generation.py
@@ -0,0 +1,219 @@
+"""Common test generation classes and main function.
+
+These are used both by generate_psa_tests.py and generate_bignum_tests.py.
+"""
+
+# Copyright The Mbed TLS Contributors
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import posixpath
+import re
+
+from abc import ABCMeta, abstractmethod
+from typing import Callable, Dict, Iterable, Iterator, List, Type, TypeVar
+
+from mbedtls_dev import build_tree
+from mbedtls_dev import test_case
+
+T = TypeVar('T') #pylint: disable=invalid-name
+
+
+class BaseTarget(metaclass=ABCMeta):
+ """Base target for test case generation.
+
+ Child classes of this class represent an output file, and can be referred
+ to as file targets. These indicate where test cases will be written to for
+ all subclasses of the file target, which is set by `target_basename`.
+
+ Attributes:
+ count: Counter for test cases from this class.
+ case_description: Short description of the test case. This may be
+ automatically generated using the class, or manually set.
+ dependencies: A list of dependencies required for the test case.
+ show_test_count: Toggle for inclusion of `count` in the test description.
+ target_basename: Basename of file to write generated tests to. This
+ should be specified in a child class of BaseTarget.
+ test_function: Test function which the class generates cases for.
+ test_name: A common name or description of the test function. This can
+ be `test_function`, a clearer equivalent, or a short summary of the
+ test function's purpose.
+ """
+ count = 0
+ case_description = ""
+ dependencies = [] # type: List[str]
+ show_test_count = True
+ target_basename = ""
+ test_function = ""
+ test_name = ""
+
+ def __new__(cls, *args, **kwargs):
+ # pylint: disable=unused-argument
+ cls.count += 1
+ return super().__new__(cls)
+
+ @abstractmethod
+ def arguments(self) -> List[str]:
+ """Get the list of arguments for the test case.
+
+ Override this method to provide the list of arguments required for
+ the `test_function`.
+
+ Returns:
+ List of arguments required for the test function.
+ """
+ raise NotImplementedError
+
+ def description(self) -> str:
+ """Create a test case description.
+
+ Creates a description of the test case, including a name for the test
+ function, an optional case count, and a description of the specific
+ test case. This should inform a reader what is being tested, and
+ provide context for the test case.
+
+ Returns:
+ Description for the test case.
+ """
+ if self.show_test_count:
+ return "{} #{} {}".format(
+ self.test_name, self.count, self.case_description
+ ).strip()
+ else:
+ return "{} {}".format(self.test_name, self.case_description).strip()
+
+
+ def create_test_case(self) -> test_case.TestCase:
+ """Generate TestCase from the instance."""
+ tc = test_case.TestCase()
+ tc.set_description(self.description())
+ tc.set_function(self.test_function)
+ tc.set_arguments(self.arguments())
+ tc.set_dependencies(self.dependencies)
+
+ return tc
+
+ @classmethod
+ @abstractmethod
+ def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
+ """Generate test cases for the class test function.
+
+ This will be called in classes where `test_function` is set.
+ Implementations should yield TestCase objects, by creating instances
+ of the class with appropriate input data, and then calling
+ `create_test_case()` on each.
+ """
+ raise NotImplementedError
+
+ @classmethod
+ def generate_tests(cls) -> Iterator[test_case.TestCase]:
+ """Generate test cases for the class and its subclasses.
+
+ In classes with `test_function` set, `generate_function_tests()` is
+ called to generate test cases first.
+
+ In all classes, this method will iterate over its subclasses, and
+ yield from `generate_tests()` in each. Calling this method on a class X
+ will yield test cases from all classes derived from X.
+ """
+ if cls.test_function:
+ yield from cls.generate_function_tests()
+ for subclass in sorted(cls.__subclasses__(), key=lambda c: c.__name__):
+ yield from subclass.generate_tests()
+
+
+class TestGenerator:
+ """Generate test cases and write to data files."""
+ def __init__(self, options) -> None:
+ self.test_suite_directory = self.get_option(options, 'directory',
+ 'tests/suites')
+ # Update `targets` with an entry for each child class of BaseTarget.
+ # Each entry represents a file generated by the BaseTarget framework,
+ # and enables generating the .data files using the CLI.
+ self.targets.update({
+ subclass.target_basename: subclass.generate_tests
+ for subclass in BaseTarget.__subclasses__()
+ })
+
+ @staticmethod
+ def get_option(options, name: str, default: T) -> T:
+ value = getattr(options, name, None)
+ return default if value is None else value
+
+ def filename_for(self, basename: str) -> str:
+ """The location of the data file with the specified base name."""
+ return posixpath.join(self.test_suite_directory, basename + '.data')
+
+ def write_test_data_file(self, basename: str,
+ test_cases: Iterable[test_case.TestCase]) -> None:
+ """Write the test cases to a .data file.
+
+ The output file is ``basename + '.data'`` in the test suite directory.
+ """
+ filename = self.filename_for(basename)
+ test_case.write_data_file(filename, test_cases)
+
+ # Note that targets whose names contain 'test_format' have their content
+ # validated by `abi_check.py`.
+ targets = {} # type: Dict[str, Callable[..., Iterable[test_case.TestCase]]]
+
+ def generate_target(self, name: str, *target_args) -> None:
+ """Generate cases and write to data file for a target.
+
+ For target callables which require arguments, override this function
+ and pass these arguments using super() (see PSATestGenerator).
+ """
+ test_cases = self.targets[name](*target_args)
+ self.write_test_data_file(name, test_cases)
+
+def main(args, description: str, generator_class: Type[TestGenerator] = TestGenerator):
+ """Command line entry point."""
+ parser = argparse.ArgumentParser(description=description)
+ parser.add_argument('--list', action='store_true',
+ help='List available targets and exit')
+ parser.add_argument('--list-for-cmake', action='store_true',
+ help='Print \';\'-separated list of available targets and exit')
+ parser.add_argument('--directory', metavar='DIR',
+ help='Output directory (default: tests/suites)')
+ # The `--directory` option is interpreted relative to the directory from
+ # which the script is invoked, but the default is relative to the root of
+ # the mbedtls tree. The default should not be set above, but instead after
+ # `build_tree.chdir_to_root()` is called.
+ parser.add_argument('targets', nargs='*', metavar='TARGET',
+ help='Target file to generate (default: all; "-": none)')
+ options = parser.parse_args(args)
+ build_tree.chdir_to_root()
+ generator = generator_class(options)
+ if options.list:
+ for name in sorted(generator.targets):
+ print(generator.filename_for(name))
+ return
+ # List in a cmake list format (i.e. ';'-separated)
+ if options.list_for_cmake:
+ print(';'.join(generator.filename_for(name)
+ for name in sorted(generator.targets)), end='')
+ return
+ if options.targets:
+ # Allow "-" as a special case so you can run
+ # ``generate_xxx_tests.py - $targets`` and it works uniformly whether
+ # ``$targets`` is empty or not.
+ options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
+ for target in options.targets
+ if target != '-']
+ else:
+ options.targets = sorted(generator.targets)
+ for target in options.targets:
+ generator.generate_target(target)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6049b74..57cf977 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -20,24 +20,54 @@
execute_process(
COMMAND
${MBEDTLS_PYTHON_EXECUTABLE}
+ ${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_bignum_tests.py
+ --list-for-cmake
+ --directory suites
+ WORKING_DIRECTORY
+ ${CMAKE_CURRENT_SOURCE_DIR}/..
+ OUTPUT_VARIABLE
+ base_bignum_generated_data_files)
+
+execute_process(
+ COMMAND
+ ${MBEDTLS_PYTHON_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_psa_tests.py
--list-for-cmake
--directory suites
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}/..
OUTPUT_VARIABLE
- base_generated_data_files)
+ base_psa_generated_data_files)
# Derive generated file paths in the build directory
-set(generated_data_files "")
-foreach(file ${base_generated_data_files})
- list(APPEND generated_data_files ${CMAKE_CURRENT_BINARY_DIR}/${file})
+set(base_generated_data_files ${base_bignum_generated_data_files} ${base_psa_generated_data_files})
+set(bignum_generated_data_files "")
+set(psa_generated_data_files "")
+foreach(file ${base_bignum_generated_data_files})
+ list(APPEND bignum_generated_data_files ${CMAKE_CURRENT_BINARY_DIR}/${file})
+endforeach()
+foreach(file ${base_psa_generated_data_files})
+ list(APPEND psa_generated_data_files ${CMAKE_CURRENT_BINARY_DIR}/${file})
endforeach()
if(GEN_FILES)
add_custom_command(
OUTPUT
- ${generated_data_files}
+ ${bignum_generated_data_files}
+ WORKING_DIRECTORY
+ ${CMAKE_CURRENT_SOURCE_DIR}/..
+ COMMAND
+ ${MBEDTLS_PYTHON_EXECUTABLE}
+ ${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_bignum_tests.py
+ --directory ${CMAKE_CURRENT_BINARY_DIR}/suites
+ DEPENDS
+ ${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_bignum_tests.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_case.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_generation.py
+ )
+ add_custom_command(
+ OUTPUT
+ ${psa_generated_data_files}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}/..
COMMAND
@@ -50,6 +80,7 @@
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/macro_collector.py
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/psa_storage.py
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_case.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_generation.py
${CMAKE_CURRENT_SOURCE_DIR}/../include/psa/crypto_config.h
${CMAKE_CURRENT_SOURCE_DIR}/../include/psa/crypto_values.h
${CMAKE_CURRENT_SOURCE_DIR}/../include/psa/crypto_extra.h
@@ -65,7 +96,8 @@
# they can cause race conditions in parallel builds.
# With this line, only 4 sub-makefiles include the above command, that reduces
# the risk of a race.
-add_custom_target(test_suite_generated_data DEPENDS ${generated_data_files})
+add_custom_target(test_suite_bignum_generated_data DEPENDS ${bignum_generated_data_files})
+add_custom_target(test_suite_psa_generated_data DEPENDS ${psa_generated_data_files})
# Test suites caught by SKIP_TEST_SUITES are built but not executed.
# "foo" as a skip pattern skips "test_suite_foo" and "test_suite_foo.bar"
# but not "test_suite_foobar".
@@ -82,23 +114,39 @@
# Get the test names of the tests with generated .data files
# from the generated_data_files list in parent scope.
- set(generated_data_names "")
- foreach(generated_data_file ${generated_data_files})
+ set(bignum_generated_data_names "")
+ set(psa_generated_data_names "")
+ foreach(generated_data_file ${bignum_generated_data_files})
# Get the plain filename
get_filename_component(generated_data_name ${generated_data_file} NAME)
# Remove the ".data" extension
get_name_without_last_ext(generated_data_name ${generated_data_name})
# Remove leading "test_suite_"
string(SUBSTRING ${generated_data_name} 11 -1 generated_data_name)
- list(APPEND generated_data_names ${generated_data_name})
+ list(APPEND bignum_generated_data_names ${generated_data_name})
+ endforeach()
+ foreach(generated_data_file ${psa_generated_data_files})
+ # Get the plain filename
+ get_filename_component(generated_data_name ${generated_data_file} NAME)
+ # Remove the ".data" extension
+ get_name_without_last_ext(generated_data_name ${generated_data_name})
+ # Remove leading "test_suite_"
+ string(SUBSTRING ${generated_data_name} 11 -1 generated_data_name)
+ list(APPEND psa_generated_data_names ${generated_data_name})
endforeach()
- if(";${generated_data_names};" MATCHES ";${data_name};")
+ if(";${bignum_generated_data_names};" MATCHES ";${data_name};")
set(data_file
${CMAKE_CURRENT_BINARY_DIR}/suites/test_suite_${data_name}.data)
+ set(dependency test_suite_bignum_generated_data)
+ elseif(";${psa_generated_data_names};" MATCHES ";${data_name};")
+ set(data_file
+ ${CMAKE_CURRENT_BINARY_DIR}/suites/test_suite_${data_name}.data)
+ set(dependency test_suite_psa_generated_data)
else()
set(data_file
${CMAKE_CURRENT_SOURCE_DIR}/suites/test_suite_${data_name}.data)
+ set(dependency test_suite_bignum_generated_data test_suite_psa_generated_data)
endif()
add_custom_command(
@@ -129,7 +177,7 @@
)
add_executable(test_suite_${data_name} test_suite_${data_name}.c $<TARGET_OBJECTS:mbedtls_test>)
- add_dependencies(test_suite_${data_name} test_suite_generated_data)
+ add_dependencies(test_suite_${data_name} ${dependency})
target_link_libraries(test_suite_${data_name} ${libs})
# Include test-specific header files from ./include and private header
# files (used by some invasive tests) from ../library. Public header
diff --git a/tests/Makefile b/tests/Makefile
index 0d08f84..8777ae9 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -66,28 +66,45 @@
endif
.PHONY: generated_files
-GENERATED_DATA_FILES := $(patsubst tests/%,%,$(shell \
+GENERATED_BIGNUM_DATA_FILES := $(patsubst tests/%,%,$(shell \
+ $(PYTHON) scripts/generate_bignum_tests.py --list || \
+ echo FAILED \
+))
+ifeq ($(GENERATED_BIGNUM_DATA_FILES),FAILED)
+$(error "$(PYTHON) scripts/generate_bignum_tests.py --list" failed)
+endif
+GENERATED_PSA_DATA_FILES := $(patsubst tests/%,%,$(shell \
$(PYTHON) scripts/generate_psa_tests.py --list || \
echo FAILED \
))
-ifeq ($(GENERATED_DATA_FILES),FAILED)
+ifeq ($(GENERATED_PSA_DATA_FILES),FAILED)
$(error "$(PYTHON) scripts/generate_psa_tests.py --list" failed)
endif
-GENERATED_FILES := $(GENERATED_DATA_FILES)
+GENERATED_FILES := $(GENERATED_PSA_DATA_FILES) $(GENERATED_BIGNUM_DATA_FILES)
generated_files: $(GENERATED_FILES)
-# generate_psa_tests.py spends more time analyzing inputs than generating
-# outputs. Its inputs are the same no matter which files are being generated.
+# generate_bignum_tests.py and generate_psa_tests.py spend more time analyzing
+# inputs than generating outputs. Its inputs are the same no matter which files
+# are being generated.
# It's rare not to want all the outputs. So always generate all of its outputs.
# Use an intermediate phony dependency so that parallel builds don't run
# a separate instance of the recipe for each output file.
-.SECONDARY: generated_psa_test_data
-$(GENERATED_DATA_FILES): generated_psa_test_data
+.SECONDARY: generated_bignum_test_data generated_psa_test_data
+$(GENERATED_BIGNUM_DATA_FILES): generated_bignum_test_data
+generated_bignum_test_data: scripts/generate_bignum_tests.py
+generated_bignum_test_data: ../scripts/mbedtls_dev/test_case.py
+generated_bignum_test_data: ../scripts/mbedtls_dev/test_generation.py
+generated_bignum_test_data:
+ echo " Gen $(GENERATED_BIGNUM_DATA_FILES)"
+ $(PYTHON) scripts/generate_bignum_tests.py
+
+$(GENERATED_PSA_DATA_FILES): generated_psa_test_data
generated_psa_test_data: scripts/generate_psa_tests.py
generated_psa_test_data: ../scripts/mbedtls_dev/crypto_knowledge.py
generated_psa_test_data: ../scripts/mbedtls_dev/macro_collector.py
generated_psa_test_data: ../scripts/mbedtls_dev/psa_storage.py
generated_psa_test_data: ../scripts/mbedtls_dev/test_case.py
+generated_psa_test_data: ../scripts/mbedtls_dev/test_generation.py
## The generated file only depends on the options that are present in
## crypto_config.h, not on which options are set. To avoid regenerating this
## file all the time when switching between configurations, don't declare
@@ -98,7 +115,7 @@
generated_psa_test_data: ../include/psa/crypto_extra.h
generated_psa_test_data: suites/test_suite_psa_crypto_metadata.data
generated_psa_test_data:
- echo " Gen $(GENERATED_DATA_FILES) ..."
+ echo " Gen $(GENERATED_PSA_DATA_FILES) ..."
$(PYTHON) scripts/generate_psa_tests.py
# A test application is built for each suites/test_suite_*.data file.
@@ -107,7 +124,7 @@
DATA_FILES := $(wildcard suites/test_suite_*.data)
# Make sure that generated data files are included even if they don't
# exist yet when the makefile is parsed.
-DATA_FILES += $(filter-out $(DATA_FILES),$(GENERATED_DATA_FILES))
+DATA_FILES += $(filter-out $(DATA_FILES),$(GENERATED_FILES))
APPS = $(basename $(subst suites/,,$(DATA_FILES)))
# Construct executable name by adding OS specific suffix $(EXEXT).
diff --git a/tests/scripts/check-generated-files.sh b/tests/scripts/check-generated-files.sh
index 1736f24..3006ec7 100755
--- a/tests/scripts/check-generated-files.sh
+++ b/tests/scripts/check-generated-files.sh
@@ -126,4 +126,5 @@
# the step that creates or updates these files.
check scripts/generate_visualc_files.pl visualc/VS2010
check scripts/generate_psa_constants.py programs/psa/psa_constant_names_generated.c
+check tests/scripts/generate_bignum_tests.py $(tests/scripts/generate_bignum_tests.py --list)
check tests/scripts/generate_psa_tests.py $(tests/scripts/generate_psa_tests.py --list)
diff --git a/tests/scripts/generate_bignum_tests.py b/tests/scripts/generate_bignum_tests.py
new file mode 100755
index 0000000..ceafa4a
--- /dev/null
+++ b/tests/scripts/generate_bignum_tests.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python3
+"""Generate test data for bignum functions.
+
+With no arguments, generate all test data. With non-option arguments,
+generate only the specified files.
+
+Class structure:
+
+Child classes of test_generation.BaseTarget (file targets) represent an output
+file. These indicate where test cases will be written to, for all subclasses of
+this target. Multiple file targets should not reuse a `target_basename`.
+
+Each subclass derived from a file target can either be:
+ - A concrete class, representing a test function, which generates test cases.
+ - An abstract class containing shared methods and attributes, not associated
+ with a test function. An example is BignumOperation, which provides
+ common features used for bignum binary operations.
+
+Both concrete and abstract subclasses can be derived from, to implement
+additional test cases (see BignumCmp and BignumCmpAbs for examples of deriving
+from abstract and concrete classes).
+
+
+Adding test case generation for a function:
+
+A subclass representing the test function should be added, deriving from a
+file target such as BignumTarget. This test class must set/implement the
+following:
+ - test_function: the function name from the associated .function file.
+ - test_name: a descriptive name or brief summary to refer to the test
+ function.
+ - arguments(): a method to generate the list of arguments required for the
+ test_function.
+ - generate_function_test(): a method to generate TestCases for the function.
+ This should create instances of the class with required input data, and
+ call `.create_test_case()` to yield the TestCase.
+
+Additional details and other attributes/methods are given in the documentation
+of BaseTarget in test_generation.py.
+"""
+
+# Copyright The Mbed TLS Contributors
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import itertools
+import sys
+import typing
+
+from abc import ABCMeta, abstractmethod
+from typing import Iterator, List, Tuple, TypeVar
+
+import scripts_path # pylint: disable=unused-import
+from mbedtls_dev import test_case
+from mbedtls_dev import test_generation
+
+T = TypeVar('T') #pylint: disable=invalid-name
+
+def hex_to_int(val: str) -> int:
+ return int(val, 16) if val else 0
+
+def quote_str(val) -> str:
+ return "\"{}\"".format(val)
+
+def combination_pairs(values: List[T]) -> List[Tuple[T, T]]:
+ """Return all pair combinations from input values.
+
+ The return value is cast, as older versions of mypy are unable to derive
+ the specific type returned by itertools.combinations_with_replacement.
+ """
+ return typing.cast(
+ List[Tuple[T, T]],
+ list(itertools.combinations_with_replacement(values, 2))
+ )
+
+
+class BignumTarget(test_generation.BaseTarget, metaclass=ABCMeta):
+ #pylint: disable=abstract-method
+ """Target for bignum (mpi) test case generation."""
+ target_basename = 'test_suite_mpi.generated'
+
+
+class BignumOperation(BignumTarget, metaclass=ABCMeta):
+ """Common features for bignum binary operations.
+
+ This adds functionality common in binary operation tests. This includes
+ generation of case descriptions, using descriptions of values and symbols
+ to represent the operation or result.
+
+ Attributes:
+ symbol: Symbol used for the operation in case description.
+ input_values: List of values to use as test case inputs. These are
+ combined to produce pairs of values.
+ input_cases: List of tuples containing pairs of test case inputs. This
+ can be used to implement specific pairs of inputs.
+ """
+ symbol = ""
+ input_values = [
+ "", "0", "7b", "-7b",
+ "0000000000000000123", "-0000000000000000123",
+ "1230000000000000000", "-1230000000000000000"
+ ] # type: List[str]
+ input_cases = [] # type: List[Tuple[str, str]]
+
+ def __init__(self, val_a: str, val_b: str) -> None:
+ self.arg_a = val_a
+ self.arg_b = val_b
+ self.int_a = hex_to_int(val_a)
+ self.int_b = hex_to_int(val_b)
+
+ def arguments(self) -> List[str]:
+ return [quote_str(self.arg_a), quote_str(self.arg_b), self.result()]
+
+ def description(self) -> str:
+ """Generate a description for the test case.
+
+ If not set, case_description uses the form A `symbol` B, where symbol
+ is used to represent the operation. Descriptions of each value are
+ generated to provide some context to the test case.
+ """
+ if not self.case_description:
+ self.case_description = "{} {} {}".format(
+ self.value_description(self.arg_a),
+ self.symbol,
+ self.value_description(self.arg_b)
+ )
+ return super().description()
+
+ @abstractmethod
+ def result(self) -> str:
+ """Get the result of the operation.
+
+ This could be calculated during initialization and stored as `_result`
+ and then returned, or calculated when the method is called.
+ """
+ raise NotImplementedError
+
+ @staticmethod
+ def value_description(val) -> str:
+ """Generate a description of the argument val.
+
+ This produces a simple description of the value, which is used in test
+ case naming to add context.
+ """
+ if val == "":
+ return "0 (null)"
+ if val == "0":
+ return "0 (1 limb)"
+
+ if val[0] == "-":
+ tmp = "negative"
+ val = val[1:]
+ else:
+ tmp = "positive"
+ if val[0] == "0":
+ tmp += " with leading zero limb"
+ elif len(val) > 10:
+ tmp = "large " + tmp
+ return tmp
+
+ @classmethod
+ def get_value_pairs(cls) -> Iterator[Tuple[str, str]]:
+ """Generator to yield pairs of inputs.
+
+ Combinations are first generated from all input values, and then
+ specific cases provided.
+ """
+ yield from combination_pairs(cls.input_values)
+ yield from cls.input_cases
+
+ @classmethod
+ def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
+ for a_value, b_value in cls.get_value_pairs():
+ cur_op = cls(a_value, b_value)
+ yield cur_op.create_test_case()
+
+
+class BignumCmp(BignumOperation):
+ """Test cases for bignum value comparison."""
+ count = 0
+ test_function = "mbedtls_mpi_cmp_mpi"
+ test_name = "MPI compare"
+ input_cases = [
+ ("-2", "-3"),
+ ("-2", "-2"),
+ ("2b4", "2b5"),
+ ("2b5", "2b6")
+ ]
+
+ def __init__(self, val_a, val_b) -> None:
+ super().__init__(val_a, val_b)
+ self._result = int(self.int_a > self.int_b) - int(self.int_a < self.int_b)
+ self.symbol = ["<", "==", ">"][self._result + 1]
+
+ def result(self) -> str:
+ return str(self._result)
+
+
+class BignumCmpAbs(BignumCmp):
+ """Test cases for absolute bignum value comparison."""
+ count = 0
+ test_function = "mbedtls_mpi_cmp_abs"
+ test_name = "MPI compare (abs)"
+
+ def __init__(self, val_a, val_b) -> None:
+ super().__init__(val_a.strip("-"), val_b.strip("-"))
+
+
+class BignumAdd(BignumOperation):
+ """Test cases for bignum value addition."""
+ count = 0
+ symbol = "+"
+ test_function = "mbedtls_mpi_add_mpi"
+ test_name = "MPI add"
+ input_cases = combination_pairs(
+ [
+ "1c67967269c6", "9cde3",
+ "-1c67967269c6", "-9cde3",
+ ]
+ )
+
+ def result(self) -> str:
+ return quote_str("{:x}".format(self.int_a + self.int_b))
+
+if __name__ == '__main__':
+ # Use the section of the docstring relevant to the CLI as description
+ test_generation.main(sys.argv[1:], "\n".join(__doc__.splitlines()[:4]))
diff --git a/tests/scripts/generate_psa_tests.py b/tests/scripts/generate_psa_tests.py
index 3d23edd..c788fd7 100755
--- a/tests/scripts/generate_psa_tests.py
+++ b/tests/scripts/generate_psa_tests.py
@@ -20,22 +20,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import argparse
import enum
-import os
-import posixpath
import re
import sys
-from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, TypeVar
+from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional
import scripts_path # pylint: disable=unused-import
-from mbedtls_dev import build_tree
from mbedtls_dev import crypto_knowledge
from mbedtls_dev import macro_collector
from mbedtls_dev import psa_storage
from mbedtls_dev import test_case
-
-T = TypeVar('T') #pylint: disable=invalid-name
+from mbedtls_dev import test_generation
def psa_want_symbol(name: str) -> str:
@@ -897,35 +892,11 @@
yield from super().generate_all_keys()
yield from self.all_keys_for_implicit_usage()
-class TestGenerator:
- """Generate test data."""
-
- def __init__(self, options) -> None:
- self.test_suite_directory = self.get_option(options, 'directory',
- 'tests/suites')
- self.info = Information()
-
- @staticmethod
- def get_option(options, name: str, default: T) -> T:
- value = getattr(options, name, None)
- return default if value is None else value
-
- def filename_for(self, basename: str) -> str:
- """The location of the data file with the specified base name."""
- return posixpath.join(self.test_suite_directory, basename + '.data')
-
- def write_test_data_file(self, basename: str,
- test_cases: Iterable[test_case.TestCase]) -> None:
- """Write the test cases to a .data file.
-
- The output file is ``basename + '.data'`` in the test suite directory.
- """
- filename = self.filename_for(basename)
- test_case.write_data_file(filename, test_cases)
-
+class PSATestGenerator(test_generation.TestGenerator):
+ """Test generator subclass including PSA targets and info."""
# Note that targets whose names contain 'test_format' have their content
# validated by `abi_check.py`.
- TARGETS = {
+ targets = {
'test_suite_psa_crypto_generate_key.generated':
lambda info: KeyGenerate(info).test_cases_for_key_generation(),
'test_suite_psa_crypto_not_supported.generated':
@@ -938,44 +909,12 @@
lambda info: StorageFormatV0(info).all_test_cases(),
} #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
- def generate_target(self, name: str) -> None:
- test_cases = self.TARGETS[name](self.info)
- self.write_test_data_file(name, test_cases)
+ def __init__(self, options):
+ super().__init__(options)
+ self.info = Information()
-def main(args):
- """Command line entry point."""
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('--list', action='store_true',
- help='List available targets and exit')
- parser.add_argument('--list-for-cmake', action='store_true',
- help='Print \';\'-separated list of available targets and exit')
- parser.add_argument('--directory', metavar='DIR',
- help='Output directory (default: tests/suites)')
- parser.add_argument('targets', nargs='*', metavar='TARGET',
- help='Target file to generate (default: all; "-": none)')
- options = parser.parse_args(args)
- build_tree.chdir_to_root()
- generator = TestGenerator(options)
- if options.list:
- for name in sorted(generator.TARGETS):
- print(generator.filename_for(name))
- return
- # List in a cmake list format (i.e. ';'-separated)
- if options.list_for_cmake:
- print(';'.join(generator.filename_for(name)
- for name in sorted(generator.TARGETS)), end='')
- return
- if options.targets:
- # Allow "-" as a special case so you can run
- # ``generate_psa_tests.py - $targets`` and it works uniformly whether
- # ``$targets`` is empty or not.
- options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
- for target in options.targets
- if target != '-']
- else:
- options.targets = sorted(generator.TARGETS)
- for target in options.targets:
- generator.generate_target(target)
+ def generate_target(self, name: str, *target_args) -> None:
+ super().generate_target(name, self.info)
if __name__ == '__main__':
- main(sys.argv[1:])
+ test_generation.main(sys.argv[1:], __doc__, PSATestGenerator)
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 9e14af1..84bcd3c 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -12096,7 +12096,7 @@
run_test "TLS 1.3, default suite, PSK" \
"$P_SRV nbio=2 debug_level=5 force_version=tls13 psk=010203 psk_identity=0a0b0c tls13_kex_modes=psk" \
"$P_CLI nbio=2 debug_level=5 force_version=tls13 psk=010203 psk_identity=0a0b0c tls13_kex_modes=psk" \
- 1 \
+ 0 \
-c "=> write client hello" \
-c "client hello, adding pre_shared_key extension, omitting PSK binder list" \
-c "client hello, adding psk_key_exchange_modes extension" \
@@ -12111,7 +12111,7 @@
run_test "TLS 1.3, default suite, PSK - openssl" \
"$O_NEXT_SRV -msg -debug -tls1_3 -psk_identity 0a0b0c -psk 010203 -allow_no_dhe_kex -nocert" \
"$P_CLI debug_level=4 psk=010203 psk_identity=0a0b0c tls13_kex_modes=psk" \
- 1 \
+ 0 \
-c "=> write client hello" \
-c "client hello, adding pre_shared_key extension, omitting PSK binder list" \
-c "client hello, adding psk_key_exchange_modes extension" \
@@ -12756,13 +12756,13 @@
requires_config_enabled MBEDTLS_SSL_CLI_C
requires_config_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE
run_test "TLS 1.3: NewSessionTicket: Basic check, m->O" \
- "$O_NEXT_SRV -msg -tls1_3 -no_resume_ephemeral -no_cache " \
- "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
+ "$O_NEXT_SRV -msg -tls1_3 -no_resume_ephemeral -no_cache --num_tickets 4" \
+ "$P_CLI debug_level=1 reco_mode=1 reconnect=1" \
0 \
-c "Protocol is TLSv1.3" \
- -c "MBEDTLS_SSL_NEW_SESSION_TICKET" \
-c "got new session ticket." \
-c "Saving session for reuse... ok" \
+ -c "Reconnecting with saved session" \
-c "HTTP/1.0 200 ok"
requires_gnutls_tls1_3
@@ -12771,27 +12771,15 @@
requires_config_enabled MBEDTLS_DEBUG_C
requires_config_enabled MBEDTLS_SSL_CLI_C
run_test "TLS 1.3: NewSessionTicket: Basic check, m->G" \
- "$G_NEXT_SRV --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:+PSK --disable-client-cert" \
- "$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
+ "$G_NEXT_SRV -d 10 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:+PSK --disable-client-cert" \
+ "$P_CLI debug_level=1 reco_mode=1 reconnect=1" \
0 \
-c "Protocol is TLSv1.3" \
- -c "MBEDTLS_SSL_NEW_SESSION_TICKET" \
-c "got new session ticket." \
-c "Saving session for reuse... ok" \
- -c "HTTP/1.0 200 OK"
-
-requires_openssl_tls1_3
-requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3
-requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
-requires_config_enabled MBEDTLS_SSL_SRV_C
-requires_config_enabled MBEDTLS_DEBUG_C
-run_test "TLS 1.3: NewSessionTicket: Basic check, O->m" \
- "$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=1" \
- "$O_NEXT_CLI -msg -debug -tls1_3 -no_middlebox" \
- 0 \
- -s "=> write NewSessionTicket msg" \
- -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET" \
- -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET_FLUSH"
+ -c "Reconnecting with saved session" \
+ -c "HTTP/1.0 200 OK" \
+ -s "This is a resumed session"
requires_gnutls_tls1_3
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3
@@ -12800,12 +12788,16 @@
requires_config_enabled MBEDTLS_DEBUG_C
run_test "TLS 1.3: NewSessionTicket: Basic check, G->m" \
"$P_SRV debug_level=4 crt_file=data_files/server5.crt key_file=data_files/server5.key force_version=tls13 tickets=1" \
- "$G_NEXT_CLI localhost -d 4 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:%DISABLE_TLS13_COMPAT_MODE -V" \
+ "$G_NEXT_CLI localhost -d 4 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:%DISABLE_TLS13_COMPAT_MODE -V -r" \
0 \
+ -c "Connecting again- trying to resume previous session" \
+ -c "NEW SESSION TICKET (4) was received" \
-s "=> write NewSessionTicket msg" \
-s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET" \
-s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET_FLUSH" \
- -c "NEW SESSION TICKET (4) was received"
+ -s "key exchange mode: ephemeral" \
+ -s "key exchange mode: psk_ephemeral" \
+ -s "found pre_shared_key extension"
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3
requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
@@ -12817,13 +12809,17 @@
"$P_CLI debug_level=4 reco_mode=1 reconnect=1" \
0 \
-c "Protocol is TLSv1.3" \
- -c "MBEDTLS_SSL_NEW_SESSION_TICKET" \
-c "got new session ticket." \
-c "Saving session for reuse... ok" \
+ -c "Reconnecting with saved session" \
-c "HTTP/1.0 200 OK" \
-s "=> write NewSessionTicket msg" \
-s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET" \
- -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET_FLUSH"
+ -s "server state: MBEDTLS_SSL_NEW_SESSION_TICKET_FLUSH" \
+ -s "key exchange mode: ephemeral" \
+ -s "key exchange mode: psk_ephemeral" \
+ -s "found pre_shared_key extension"
+
requires_openssl_tls1_3
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data
index 5332c07..4c0ed1c 100644
--- a/tests/suites/test_suite_ecp.data
+++ b/tests/suites/test_suite_ecp.data
@@ -309,6 +309,58 @@
depends_on:MBEDTLS_ECP_DP_SECP521R1_ENABLED
ecp_tls_write_read_point:MBEDTLS_ECP_DP_SECP521R1
+Check ECP group metadata #1 secp192k1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP192K1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP192K1:192:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"fffffffffffffffffffffffffffffffffffffffeffffee37":"000000000000000000000000000000000000000000000000":"000000000000000000000000000000000000000000000003":"db4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d":"9b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d":"fffffffffffffffffffffffe26f2fc170f69466a74defd8d":18
+
+Check ECP group metadata #2 secp192r1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP192R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP192R1:192:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"fffffffffffffffffffffffffffffffeffffffffffffffff":"":"64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1":"188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012":"07192b95ffc8da78631011ed6b24cdd573f977a11e794811":"ffffffffffffffffffffffff99def836146bc9b1b4d22831":19
+
+Check ECP group metadata #3 secp224k1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP224K1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP224K1:224:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"fffffffffffffffffffffffffffffffffffffffffffffffeffffe56d":"00000000000000000000000000000000000000000000000000000000":"00000000000000000000000000000000000000000000000000000005":"a1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c":"7e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5":"010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7":20
+
+Check ECP group metadata #4 secp224r1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP224R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP224R1:224:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"ffffffffffffffffffffffffffffffff000000000000000000000001":"":"b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4":"b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21":"bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34":"ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d":21
+
+Check ECP group metadata #5 secp256k1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP256K1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP256K1:256:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f":"0000000000000000000000000000000000000000000000000000000000000000":"0000000000000000000000000000000000000000000000000000000000000007":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798":"483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8":"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141":22
+
+Check ECP group metadata #6 secp256r1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP256R1:256:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"ffffffff00000001000000000000000000000000ffffffffffffffffffffffff":"":"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b":"6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296":"4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5":"ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551":23
+
+Check ECP group metadata #7 secp384r1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP384R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP384R1:384:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff":"":"b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef":"aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7":"3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f":"ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973":24
+
+Check ECP group metadata #8 secp521r1 (SEC 2)
+depends_on:MBEDTLS_ECP_DP_SECP521R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_SECP521R1:521:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff":"":"0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00":"00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66":"011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650":"01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409":25
+
+Check ECP group metadata #9 bp256r1 (RFC 5639)
+depends_on:MBEDTLS_ECP_DP_BP256R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_BP256R1:256:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377":"7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9":"26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6":"8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262":"547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997":"a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7":26
+
+Check ECP group metadata #10 bp384r1 (RFC 5639)
+depends_on:MBEDTLS_ECP_DP_BP384R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_BP384R1:384:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53":"7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826":"04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11":"1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e":"8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315":"8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565":27
+
+Check ECP group metadata #11 bp512r1 (RFC 5639)
+depends_on:MBEDTLS_ECP_DP_BP512R1_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_BP512R1:512:MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS:"aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3":"7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca":"3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723":"81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822":"7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892":"aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069":28
+
+Check ECP group metadata #12 curve25519 (RFC 7748)
+depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_CURVE25519:256:MBEDTLS_ECP_TYPE_MONTGOMERY:"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed":"76d06":"":"9":"":"1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed":29
+
+Check ECP group metadata #13 curve448 (RFC 7748)
+depends_on:MBEDTLS_ECP_DP_CURVE448_ENABLED
+mbedtls_ecp_group_metadata:MBEDTLS_ECP_DP_CURVE448:448:MBEDTLS_ECP_TYPE_MONTGOMERY:"fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff":"262a6":"":"5":"":"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3":30
+
ECP tls read group #1 (record too short)
mbedtls_ecp_tls_read_group:"0313":MBEDTLS_ERR_ECP_BAD_INPUT_DATA:0:0
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
index 65c7067..42d69b4 100644
--- a/tests/suites/test_suite_ecp.function
+++ b/tests/suites/test_suite_ecp.function
@@ -1,5 +1,7 @@
/* BEGIN_HEADER */
#include "mbedtls/ecp.h"
+#include "mbedtls/ecdsa.h"
+#include "mbedtls/ecdh.h"
#include "ecp_invasive.h"
@@ -788,6 +790,124 @@
}
/* END_CASE */
+/* BEGIN_CASE depends_on:MBEDTLS_ECDH_C:MBEDTLS_ECDSA_C */
+void mbedtls_ecp_group_metadata( int id, int bit_size, int crv_type,
+ char* P, char* A, char* B,
+ char* G_x, char* G_y, char* N,
+ int tls_id )
+{
+ mbedtls_ecp_group grp, grp_read, grp_cpy;
+ const mbedtls_ecp_group_id *g_id;
+ mbedtls_ecp_group_id read_g_id;
+ const mbedtls_ecp_curve_info *crv, *crv_tls_id, *crv_name;
+
+ mbedtls_mpi exp_P, exp_A, exp_B, exp_G_x, exp_G_y, exp_N;
+
+ unsigned char buf[3], ecparameters[3] = { 3, 0, tls_id };
+ const unsigned char *vbuf = buf;
+ size_t olen;
+
+ mbedtls_ecp_group_init( &grp );
+ mbedtls_ecp_group_init( &grp_read );
+ mbedtls_ecp_group_init( &grp_cpy );
+
+ mbedtls_mpi_init( &exp_P );
+ mbedtls_mpi_init( &exp_A );
+ mbedtls_mpi_init( &exp_B );
+ mbedtls_mpi_init( &exp_G_x );
+ mbedtls_mpi_init( &exp_G_y );
+ mbedtls_mpi_init( &exp_N );
+
+ // Read expected parameters
+ TEST_EQUAL( mbedtls_test_read_mpi( &exp_P, P ), 0 );
+ TEST_EQUAL( mbedtls_test_read_mpi( &exp_A, A ), 0 );
+ TEST_EQUAL( mbedtls_test_read_mpi( &exp_G_x, G_x ), 0 );
+ TEST_EQUAL( mbedtls_test_read_mpi( &exp_N, N ), 0 );
+ TEST_EQUAL( mbedtls_test_read_mpi( &exp_B, B ), 0 );
+ TEST_EQUAL( mbedtls_test_read_mpi( &exp_G_y, G_y ), 0 );
+
+ // Convert exp_A to internal representation (A+2)/4
+ if( crv_type == MBEDTLS_ECP_TYPE_MONTGOMERY )
+ {
+ TEST_EQUAL( mbedtls_mpi_add_int( &exp_A, &exp_A, 2 ), 0 );
+ TEST_EQUAL( mbedtls_mpi_div_int( &exp_A, NULL, &exp_A, 4 ), 0 );
+ }
+
+ // Load group
+ TEST_EQUAL( mbedtls_ecp_group_load( &grp, id ), 0 );
+
+ // Compare group with expected parameters
+ // A is NULL for SECPxxxR1 curves
+ // B and G_y are NULL for curve25519 and curve448
+ TEST_EQUAL( mbedtls_mpi_cmp_mpi( &exp_P, &grp.P ), 0 );
+ if( *A != 0 )
+ TEST_EQUAL( mbedtls_mpi_cmp_mpi( &exp_A, &grp.A ), 0 );
+ if( *B != 0 )
+ TEST_EQUAL( mbedtls_mpi_cmp_mpi( &exp_B, &grp.B ), 0 );
+ TEST_EQUAL( mbedtls_mpi_cmp_mpi( &exp_G_x, &grp.G.X ), 0 );
+ if( *G_y != 0 )
+ TEST_EQUAL( mbedtls_mpi_cmp_mpi( &exp_G_y, &grp.G.Y ), 0 );
+ TEST_EQUAL( mbedtls_mpi_cmp_mpi( &exp_N, &grp.N ), 0 );
+
+ // Load curve info and compare with known values
+ crv = mbedtls_ecp_curve_info_from_grp_id( id );
+ TEST_EQUAL( crv->grp_id, id );
+ TEST_EQUAL( crv->bit_size, bit_size );
+ TEST_EQUAL( crv->tls_id, tls_id );
+
+ // Load curve from TLS ID and name, and compare IDs
+ crv_tls_id = mbedtls_ecp_curve_info_from_tls_id( crv->tls_id );
+ crv_name = mbedtls_ecp_curve_info_from_name( crv->name );
+ TEST_EQUAL( crv_tls_id->grp_id, id );
+ TEST_EQUAL( crv_name->grp_id, id );
+
+ // Validate write_group against test data
+ TEST_EQUAL( mbedtls_ecp_tls_write_group( &grp, &olen,
+ buf, sizeof( buf ) ),
+ 0 );
+ TEST_EQUAL( mbedtls_test_hexcmp( buf, ecparameters, olen,
+ sizeof( ecparameters ) ),
+ 0 );
+
+ // Read group from buffer and compare with expected ID
+ TEST_EQUAL( mbedtls_ecp_tls_read_group_id( &read_g_id, &vbuf, olen ),
+ 0 );
+ TEST_EQUAL( read_g_id, id );
+ vbuf = buf;
+ TEST_EQUAL( mbedtls_ecp_tls_read_group( &grp_read, &vbuf, olen ),
+ 0 );
+ TEST_EQUAL( grp_read.id, id );
+
+ // Check curve type, and if it can be used for ECDH/ECDSA
+ TEST_EQUAL( mbedtls_ecp_get_type( &grp ), crv_type );
+ TEST_EQUAL( mbedtls_ecdh_can_do( id ), 1 );
+ TEST_EQUAL( mbedtls_ecdsa_can_do( id ),
+ crv_type == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS );
+
+ // Copy group and compare with original
+ TEST_EQUAL( mbedtls_ecp_group_copy( &grp_cpy, &grp ), 0 );
+ TEST_EQUAL( mbedtls_ecp_group_cmp( &grp, &grp_cpy ), 0 );
+
+ // Check curve is in curve list and group ID list
+ for( crv = mbedtls_ecp_curve_list( );
+ crv->grp_id != MBEDTLS_ECP_DP_NONE &&
+ crv->grp_id != (unsigned) id;
+ crv++ );
+ TEST_EQUAL( crv->grp_id, id );
+ for( g_id = mbedtls_ecp_grp_id_list( );
+ *g_id != MBEDTLS_ECP_DP_NONE && *g_id != (unsigned) id;
+ g_id++ );
+ TEST_EQUAL( *g_id, (unsigned) id );
+
+exit:
+ mbedtls_ecp_group_free( &grp ); mbedtls_ecp_group_free( &grp_cpy );
+ mbedtls_ecp_group_free( &grp_read );
+ mbedtls_mpi_free( &exp_P ); mbedtls_mpi_free( &exp_A );
+ mbedtls_mpi_free( &exp_B ); mbedtls_mpi_free( &exp_G_x );
+ mbedtls_mpi_free( &exp_G_y ); mbedtls_mpi_free( &exp_N );
+}
+/* END_CASE */
+
/* BEGIN_CASE */
void mbedtls_ecp_check_privkey( int id, char * key_hex, int ret )
{