diff --git a/bin/echo.c b/bin/echo.c index e3f5b31d5c1..e755b02d908 100644 --- a/bin/echo.c +++ b/bin/echo.c @@ -51,6 +51,11 @@ const char *sig_hash_strs[] = { [S2N_TLS_HASH_MD5_SHA1] = "MD5_SHA1", }; +/* Careful: don't change this without updating the integration tests that check for it! + * Negative cases may stop working but not fail when this is updated. + */ +const char *pq_enabled_note = "PQ key exchange enabled"; + void print_s2n_error(const char *app_error) { fprintf(stderr, "[%d] %s: '%s' : '%s'\n", getpid(), app_error, s2n_strerror(s2n_errno, "EN"), @@ -175,9 +180,19 @@ int print_connection_info(struct s2n_connection *conn) printf("Application protocol: %s\n", s2n_get_application_protocol(conn)); } - printf("Curve: %s\n", s2n_connection_get_curve(conn)); - printf("KEM: %s\n", s2n_connection_get_kem_name(conn)); - printf("KEM Group: %s\n", s2n_connection_get_kem_group_name(conn)); + const char *kem = s2n_connection_get_kem_name(conn); + if (strcmp(kem, "NONE") != 0) { + printf("Legacy TLS1.2 KEM: %s (%s, but TLS1.3 PQ hybrid key exchange strongly recommended instead. " + "See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch15-post-quantum.md)\n", + kem, pq_enabled_note); + } + + const char *kem_group = s2n_connection_get_kem_group_name(conn); + if (strcmp(kem_group, "NONE") != 0) { + printf("KEM Group: %s (%s)\n", kem_group, pq_enabled_note); + } else { + printf("Curve: %s\n", s2n_connection_get_curve(conn)); + } uint32_t length = 0; const uint8_t *status = s2n_connection_get_ocsp_response(conn, &length); diff --git a/nix/README.md b/nix/README.md index 509a3f61af0..57221bcb742 100644 --- a/nix/README.md +++ b/nix/README.md @@ -28,6 +28,11 @@ To enter the development shell, run `nix develop` at the root of the project. There are some helper scripts in the environment to make building easier, but if you're familiar with Nix, note that these are separate from the buildPhase, configurePhase and checkPhase. + +### Specific libcrypto + +By default, the devShell uses Openssl-3. To run the devShell with a different libcrypto like awslc, use `nix develop .#awslc`. The currently supported options are awslc, openssl111, openssl102, and libressl. See flake.nix in the root directory. + ### Configure and build From inside the devShell: `configure; build`. diff --git a/tests/integrationv2/test_pq_handshake.py b/tests/integrationv2/test_pq_handshake.py index d91176fafad..e31b9420375 100644 --- a/tests/integrationv2/test_pq_handshake.py +++ b/tests/integrationv2/test_pq_handshake.py @@ -8,6 +8,8 @@ from utils import invalid_test_parameters, get_parameter_name, to_bytes from global_flags import get_flag, S2N_PROVIDER_VERSION +PQ_ENABLED_FLAG = "PQ key exchange enabled" + CIPHERS = [ None, # `None` will default to the appropriate `test_all` cipher preference in the S2N client provider Ciphers.KMS_PQ_TLS_1_0_2019_06, @@ -31,120 +33,120 @@ # The tuple keys have the form (client_{cipher, kem_group}, server_{cipher, kem_group}) (Ciphers.KMS_PQ_TLS_1_0_2019_06, Ciphers.KMS_PQ_TLS_1_0_2019_06): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2019_06, Ciphers.KMS_PQ_TLS_1_0_2020_02): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2019_06, Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_02, Ciphers.KMS_PQ_TLS_1_0_2019_06): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_02, Ciphers.KMS_PQ_TLS_1_0_2020_02): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_02, Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_07, Ciphers.KMS_PQ_TLS_1_0_2019_06): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_07, Ciphers.KMS_PQ_TLS_1_0_2020_02): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_07, Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-KYBER-RSA-AES256-GCM-SHA384", - "kem": "kyber512r3", "kem_group": "NONE"}, + "kem": "kyber512r3", "kem_group": None}, (Ciphers.PQ_SIKE_TEST_TLS_1_0_2019_11, Ciphers.KMS_PQ_TLS_1_0_2019_06): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.PQ_SIKE_TEST_TLS_1_0_2019_11, Ciphers.KMS_PQ_TLS_1_0_2020_02): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.PQ_SIKE_TEST_TLS_1_0_2019_11, Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.PQ_SIKE_TEST_TLS_1_0_2020_02, Ciphers.KMS_PQ_TLS_1_0_2019_06): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.PQ_SIKE_TEST_TLS_1_0_2020_02, Ciphers.KMS_PQ_TLS_1_0_2020_02): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.PQ_SIKE_TEST_TLS_1_0_2020_02, Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2019_06, Ciphers.KMS_TLS_1_0_2018_10): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_02, Ciphers.KMS_TLS_1_0_2018_10): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_07, Ciphers.KMS_TLS_1_0_2018_10): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_TLS_1_0_2018_10, Ciphers.KMS_PQ_TLS_1_0_2019_06): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_TLS_1_0_2018_10, Ciphers.KMS_PQ_TLS_1_0_2020_02): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, (Ciphers.KMS_TLS_1_0_2018_10, Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", - "kem": "NONE", "kem_group": "NONE"}, + "kem": None, "kem_group": None}, # The expected kem_group string for this case purposefully excludes a curve; # depending on how s2n was compiled, the curve may be either x25519 or one # of the NIST curves. (Ciphers.PQ_TLS_1_0_2020_12, Ciphers.PQ_TLS_1_0_2020_12): {"cipher": "TLS_AES_256_GCM_SHA384", - "kem": "NONE", "kem_group": "_kyber-512-r3"}, + "kem": None, "kem_group": "_kyber-512-r3"}, (Ciphers.PQ_TLS_1_0_2020_12, Ciphers.PQ_TLS_1_0_2023_01): {"cipher": "TLS_AES_256_GCM_SHA384", - "kem": "NONE", "kem_group": "_kyber-512-r3"}, + "kem": None, "kem_group": "_kyber-512-r3"}, (Ciphers.PQ_TLS_1_0_2023_01, Ciphers.PQ_TLS_1_0_2023_01): {"cipher": "TLS_AES_256_GCM_SHA384", - "kem": "NONE", "kem_group": "_kyber-512-r3"}, + "kem": None, "kem_group": "_kyber-512-r3"}, (Ciphers.PQ_TLS_1_0_2023_01, Ciphers.PQ_TLS_1_0_2020_12): {"cipher": "TLS_AES_256_GCM_SHA384", - "kem": "NONE", "kem_group": "_kyber-512-r3"}, + "kem": None, "kem_group": "_kyber-512-r3"}, (Ciphers.PQ_TLS_1_0_2020_12, Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-KYBER-RSA-AES256-GCM-SHA384", - "kem": "kyber512r3", "kem_group": "NONE"}, + "kem": "kyber512r3", "kem_group": None}, (Ciphers.KMS_PQ_TLS_1_0_2020_07, Ciphers.PQ_TLS_1_0_2020_12): {"cipher": "ECDHE-KYBER-RSA-AES256-GCM-SHA384", - "kem": "kyber512r3", "kem_group": "NONE"}, + "kem": "kyber512r3", "kem_group": None}, (Ciphers.PQ_TLS_1_0_2020_12, KemGroups.P256_KYBER512R3): - {"cipher": "AES256_GCM_SHA384", "kem": "NONE", + {"cipher": "AES256_GCM_SHA384", "kem": None, "kem_group": "secp256r1_kyber-512-r3"}, (KemGroups.P256_KYBER512R3, Ciphers.PQ_TLS_1_0_2020_12): - {"cipher": "AES256_GCM_SHA384", "kem": "NONE", + {"cipher": "AES256_GCM_SHA384", "kem": None, "kem_group": "secp256r1_kyber-512-r3"}, (KemGroups.P256_KYBER512R3, Ciphers.PQ_TLS_1_0_2023_01): - {"cipher": "AES256_GCM_SHA384", "kem": "NONE", + {"cipher": "AES256_GCM_SHA384", "kem": None, "kem_group": "secp256r1_kyber-512-r3"}, (KemGroups.P256_KYBER512R3, Ciphers.PQ_TLS_1_3_2023_06_01): - {"cipher": "AES256_GCM_SHA384", "kem": "NONE", + {"cipher": "AES256_GCM_SHA384", "kem": None, "kem_group": "secp256r1_kyber-512-r3"}, (KemGroups.P384_KYBER768R3, Ciphers.PQ_TLS_1_3_2023_06_01): - {"cipher": "AES256_GCM_SHA384", "kem": "NONE", + {"cipher": "AES256_GCM_SHA384", "kem": None, "kem_group": "secp384r1_kyber-768-r3"}, (KemGroups.P521_KYBER1024R3, Ciphers.PQ_TLS_1_3_2023_06_01): - {"cipher": "AES256_GCM_SHA384", "kem": "NONE", + {"cipher": "AES256_GCM_SHA384", "kem": None, "kem_group": "secp521r1_kyber-1024-r3"}, (Ciphers.PQ_TLS_1_3_2023_06_01, KemGroups.X25519Kyber768Draft00): {"cipher": "TLS_AES_256_GCM_SHA384", - "kem": "NONE", + "kem": None, "kem_group": "X25519Kyber768Draft00"}, (Ciphers.PQ_TLS_1_3_2023_06_01, KemGroups.SecP256r1Kyber768Draft00): {"cipher": "TLS_AES_256_GCM_SHA384", - "kem": "NONE", + "kem": None, "kem_group": "SecP256r1Kyber768Draft00"}, } @@ -181,11 +183,18 @@ def assert_s2n_negotiation_parameters(s2n_results, expected_result): if expected_result is not None: assert to_bytes( ("Cipher negotiated: " + expected_result['cipher'])) in s2n_results.stdout - assert to_bytes( - ("KEM: " + expected_result['kem'])) in s2n_results.stdout - # Purposefully leave off the "KEM Group: " prefix in order to perform partial matches - # without specifying the curve. - assert to_bytes(expected_result['kem_group']) in s2n_results.stdout + if expected_result['kem']: + assert to_bytes( + ("KEM: " + expected_result['kem'])) in s2n_results.stdout + assert to_bytes(PQ_ENABLED_FLAG) in s2n_results.stdout + if expected_result['kem_group']: + # Purposefully leave off the "KEM Group: " prefix in order to perform partial matches + # without specifying the curve. + assert to_bytes(expected_result['kem_group']) in s2n_results.stdout + assert to_bytes(PQ_ENABLED_FLAG) in s2n_results.stdout + if not expected_result['kem'] and not expected_result['kem_group']: + assert to_bytes(PQ_ENABLED_FLAG) not in s2n_results.stdout + assert to_bytes("Curve:") in s2n_results.stdout def assert_awslc_negotiation_parameters(awslc_results, expected_result): @@ -256,7 +265,7 @@ def test_s2nc_to_s2nd_pq_handshake(managed_process, protocol, certificate, clien # If PQ is not enabled in s2n, we expect classic handshakes to be negotiated. # Leave the expected cipher blank, as there are multiple possibilities - the # important thing is that kem and kem_group are NONE. - expected_result = {"cipher": "", "kem": "NONE", "kem_group": "NONE"} + expected_result = {"cipher": "", "kem": None, "kem_group": None} # Client and server are both s2n; can make meaningful assertions about negotiation for both for results in client.get_results(): diff --git a/tests/integrationv2/test_well_known_endpoints.py b/tests/integrationv2/test_well_known_endpoints.py index fbfc291a927..ba6df48e3da 100644 --- a/tests/integrationv2/test_well_known_endpoints.py +++ b/tests/integrationv2/test_well_known_endpoints.py @@ -6,6 +6,7 @@ from fixtures import managed_process # lgtm [py/unused-import] from global_flags import get_flag, is_criterion_on, S2N_FIPS_MODE, S2N_USE_CRITERION from providers import Provider, S2N +from test_pq_handshake import PQ_ENABLED_FLAG from utils import invalid_test_parameters, get_parameter_name, to_bytes @@ -60,28 +61,28 @@ if pq_enabled(): EXPECTED_RESULTS = { ("kms.us-east-1.amazonaws.com", Ciphers.KMS_PQ_TLS_1_0_2019_06): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, ("kms.us-east-1.amazonaws.com", Ciphers.PQ_SIKE_TEST_TLS_1_0_2019_11): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, ("kms.us-east-1.amazonaws.com", Ciphers.KMS_PQ_TLS_1_0_2020_07): {"cipher": "ECDHE-KYBER-RSA-AES256-GCM-SHA384", "kem": "kyber512r3"}, ("kms.us-east-1.amazonaws.com", Ciphers.KMS_PQ_TLS_1_0_2020_02): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, ("kms.us-east-1.amazonaws.com", Ciphers.PQ_SIKE_TEST_TLS_1_0_2020_02): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, } else: EXPECTED_RESULTS = { ("kms.us-east-1.amazonaws.com", Ciphers.KMS_PQ_TLS_1_0_2019_06): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, ("kms.us-east-1.amazonaws.com", Ciphers.PQ_SIKE_TEST_TLS_1_0_2019_11): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, ("kms.us-east-1.amazonaws.com", Ciphers.KMS_PQ_TLS_1_0_2020_07): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, ("kms.us-east-1.amazonaws.com", Ciphers.KMS_PQ_TLS_1_0_2020_02): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, ("kms.us-east-1.amazonaws.com", Ciphers.PQ_SIKE_TEST_TLS_1_0_2020_02): - {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": "NONE"}, + {"cipher": "ECDHE-RSA-AES256-GCM-SHA384", "kem": None}, } @@ -122,4 +123,9 @@ def test_well_known_endpoints(managed_process, protocol, endpoint, provider, cip if expected_result is not None: assert to_bytes(expected_result['cipher']) in results.stdout - assert to_bytes(expected_result['kem']) in results.stdout + if expected_result['kem']: + assert to_bytes(expected_result['kem']) in results.stdout + assert to_bytes(PQ_ENABLED_FLAG) in results.stdout + else: + assert to_bytes('KEM:') not in results.stdout + assert to_bytes(PQ_ENABLED_FLAG) not in results.stdout