| // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "quiche/quic/core/crypto/crypto_utils.h" | |
| #include <string> | |
| #include "absl/base/macros.h" | |
| #include "absl/strings/escaping.h" | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/quic/core/quic_utils.h" | |
| #include "quiche/quic/platform/api/quic_test.h" | |
| #include "quiche/quic/test_tools/quic_test_utils.h" | |
| #include "quiche/common/test_tools/quiche_test_utils.h" | |
| namespace quic { | |
| namespace test { | |
| namespace { | |
| class CryptoUtilsTest : public QuicTest {}; | |
| TEST_F(CryptoUtilsTest, HandshakeFailureReasonToString) { | |
| EXPECT_STREQ("HANDSHAKE_OK", | |
| CryptoUtils::HandshakeFailureReasonToString(HANDSHAKE_OK)); | |
| EXPECT_STREQ("CLIENT_NONCE_UNKNOWN_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| CLIENT_NONCE_UNKNOWN_FAILURE)); | |
| EXPECT_STREQ("CLIENT_NONCE_INVALID_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| CLIENT_NONCE_INVALID_FAILURE)); | |
| EXPECT_STREQ("CLIENT_NONCE_NOT_UNIQUE_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| CLIENT_NONCE_NOT_UNIQUE_FAILURE)); | |
| EXPECT_STREQ("CLIENT_NONCE_INVALID_ORBIT_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| CLIENT_NONCE_INVALID_ORBIT_FAILURE)); | |
| EXPECT_STREQ("CLIENT_NONCE_INVALID_TIME_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| CLIENT_NONCE_INVALID_TIME_FAILURE)); | |
| EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT)); | |
| EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| CLIENT_NONCE_STRIKE_REGISTER_FAILURE)); | |
| EXPECT_STREQ("SERVER_NONCE_DECRYPTION_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SERVER_NONCE_DECRYPTION_FAILURE)); | |
| EXPECT_STREQ("SERVER_NONCE_INVALID_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SERVER_NONCE_INVALID_FAILURE)); | |
| EXPECT_STREQ("SERVER_NONCE_NOT_UNIQUE_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SERVER_NONCE_NOT_UNIQUE_FAILURE)); | |
| EXPECT_STREQ("SERVER_NONCE_INVALID_TIME_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SERVER_NONCE_INVALID_TIME_FAILURE)); | |
| EXPECT_STREQ("SERVER_NONCE_REQUIRED_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SERVER_NONCE_REQUIRED_FAILURE)); | |
| EXPECT_STREQ("SERVER_CONFIG_INCHOATE_HELLO_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SERVER_CONFIG_INCHOATE_HELLO_FAILURE)); | |
| EXPECT_STREQ("SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE)); | |
| EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_INVALID_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SOURCE_ADDRESS_TOKEN_INVALID_FAILURE)); | |
| EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE)); | |
| EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_PARSE_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SOURCE_ADDRESS_TOKEN_PARSE_FAILURE)); | |
| EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE)); | |
| EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE)); | |
| EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE)); | |
| EXPECT_STREQ("INVALID_EXPECTED_LEAF_CERTIFICATE", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| INVALID_EXPECTED_LEAF_CERTIFICATE)); | |
| EXPECT_STREQ("MAX_FAILURE_REASON", | |
| CryptoUtils::HandshakeFailureReasonToString(MAX_FAILURE_REASON)); | |
| EXPECT_STREQ( | |
| "INVALID_HANDSHAKE_FAILURE_REASON", | |
| CryptoUtils::HandshakeFailureReasonToString( | |
| static_cast<HandshakeFailureReason>(MAX_FAILURE_REASON + 1))); | |
| } | |
| TEST_F(CryptoUtilsTest, AuthTagLengths) { | |
| for (const auto& version : AllSupportedVersions()) { | |
| for (QuicTag algo : {kAESG, kCC20}) { | |
| SCOPED_TRACE(version); | |
| std::unique_ptr<QuicEncrypter> encrypter( | |
| QuicEncrypter::Create(version, algo)); | |
| size_t auth_tag_size = 12; | |
| if (version.UsesInitialObfuscators()) { | |
| auth_tag_size = 16; | |
| } | |
| EXPECT_EQ(encrypter->GetCiphertextSize(0), auth_tag_size); | |
| } | |
| } | |
| } | |
| TEST_F(CryptoUtilsTest, ValidateChosenVersion) { | |
| for (const ParsedQuicVersion& v1 : AllSupportedVersions()) { | |
| for (const ParsedQuicVersion& v2 : AllSupportedVersions()) { | |
| std::string error_details; | |
| bool success = CryptoUtils::ValidateChosenVersion( | |
| CreateQuicVersionLabel(v1), v2, &error_details); | |
| EXPECT_EQ(success, v1 == v2); | |
| EXPECT_EQ(success, error_details.empty()); | |
| } | |
| } | |
| } | |
| TEST_F(CryptoUtilsTest, ValidateServerVersionsNoVersionNegotiation) { | |
| QuicVersionLabelVector version_information_other_versions; | |
| ParsedQuicVersionVector client_original_supported_versions; | |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { | |
| std::string error_details; | |
| EXPECT_TRUE(CryptoUtils::ValidateServerVersions( | |
| version_information_other_versions, version, | |
| client_original_supported_versions, &error_details)); | |
| EXPECT_TRUE(error_details.empty()); | |
| } | |
| } | |
| TEST_F(CryptoUtilsTest, ValidateServerVersionsWithVersionNegotiation) { | |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { | |
| QuicVersionLabelVector version_information_other_versions{ | |
| CreateQuicVersionLabel(version)}; | |
| ParsedQuicVersionVector client_original_supported_versions{ | |
| ParsedQuicVersion::ReservedForNegotiation(), version}; | |
| std::string error_details; | |
| EXPECT_TRUE(CryptoUtils::ValidateServerVersions( | |
| version_information_other_versions, version, | |
| client_original_supported_versions, &error_details)); | |
| EXPECT_TRUE(error_details.empty()); | |
| } | |
| } | |
| TEST_F(CryptoUtilsTest, ValidateServerVersionsWithDowngrade) { | |
| if (AllSupportedVersions().size() <= 1) { | |
| // We are not vulnerable to downgrade if we only support one version. | |
| return; | |
| } | |
| ParsedQuicVersion client_version = AllSupportedVersions().front(); | |
| ParsedQuicVersion server_version = AllSupportedVersions().back(); | |
| ASSERT_NE(client_version, server_version); | |
| QuicVersionLabelVector version_information_other_versions{ | |
| CreateQuicVersionLabel(client_version)}; | |
| ParsedQuicVersionVector client_original_supported_versions{ | |
| ParsedQuicVersion::ReservedForNegotiation(), server_version}; | |
| std::string error_details; | |
| EXPECT_FALSE(CryptoUtils::ValidateServerVersions( | |
| version_information_other_versions, server_version, | |
| client_original_supported_versions, &error_details)); | |
| EXPECT_FALSE(error_details.empty()); | |
| } | |
| // Test that the library is using the correct labels for each version, and | |
| // therefore generating correct obfuscators, using the test vectors in appendix | |
| // A of each RFC or internet-draft. | |
| TEST_F(CryptoUtilsTest, ValidateCryptoLabels) { | |
| // if the number of HTTP/3 QUIC versions has changed, we need to change the | |
| // expected_keys hardcoded into this test. Regrettably, this is not a | |
| // compile-time constant. | |
| EXPECT_EQ(AllSupportedVersionsWithTls().size(), 3u); | |
| const char draft_29_key[] = {// test vector from draft-ietf-quic-tls-29, A.1 | |
| 0x14, | |
| static_cast<char>(0x9d), | |
| 0x0b, | |
| 0x16, | |
| 0x62, | |
| static_cast<char>(0xab), | |
| static_cast<char>(0x87), | |
| 0x1f, | |
| static_cast<char>(0xbe), | |
| 0x63, | |
| static_cast<char>(0xc4), | |
| static_cast<char>(0x9b), | |
| 0x5e, | |
| 0x65, | |
| 0x5a, | |
| 0x5d}; | |
| const char v1_key[] = {// test vector from RFC 9001, A.1 | |
| static_cast<char>(0xcf), | |
| 0x3a, | |
| 0x53, | |
| 0x31, | |
| 0x65, | |
| 0x3c, | |
| 0x36, | |
| 0x4c, | |
| static_cast<char>(0x88), | |
| static_cast<char>(0xf0), | |
| static_cast<char>(0xf3), | |
| 0x79, | |
| static_cast<char>(0xb6), | |
| 0x06, | |
| 0x7e, | |
| 0x37}; | |
| const char v2_08_key[] = {// test vector from draft-ietf-quic-v2-08 | |
| static_cast<char>(0x82), | |
| static_cast<char>(0xdb), | |
| static_cast<char>(0x63), | |
| static_cast<char>(0x78), | |
| static_cast<char>(0x61), | |
| static_cast<char>(0xd5), | |
| static_cast<char>(0x5e), | |
| 0x1d, | |
| static_cast<char>(0x01), | |
| static_cast<char>(0x1f), | |
| 0x19, | |
| static_cast<char>(0xea), | |
| 0x71, | |
| static_cast<char>(0xd5), | |
| static_cast<char>(0xd2), | |
| static_cast<char>(0xa7)}; | |
| const char connection_id[] = // test vector from both docs | |
| {static_cast<char>(0x83), | |
| static_cast<char>(0x94), | |
| static_cast<char>(0xc8), | |
| static_cast<char>(0xf0), | |
| 0x3e, | |
| 0x51, | |
| 0x57, | |
| 0x08}; | |
| const QuicConnectionId cid(connection_id, sizeof(connection_id)); | |
| const char* key_str; | |
| size_t key_size; | |
| for (const ParsedQuicVersion& version : AllSupportedVersionsWithTls()) { | |
| if (version == ParsedQuicVersion::Draft29()) { | |
| key_str = draft_29_key; | |
| key_size = sizeof(draft_29_key); | |
| } else if (version == ParsedQuicVersion::RFCv1()) { | |
| key_str = v1_key; | |
| key_size = sizeof(v1_key); | |
| } else { // draft-ietf-quic-v2-01 | |
| key_str = v2_08_key; | |
| key_size = sizeof(v2_08_key); | |
| } | |
| const absl::string_view expected_key{key_str, key_size}; | |
| CrypterPair crypters; | |
| CryptoUtils::CreateInitialObfuscators(Perspective::IS_SERVER, version, cid, | |
| &crypters); | |
| EXPECT_EQ(crypters.encrypter->GetKey(), expected_key); | |
| } | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quic |