From 9e54dc8b9734460a70f1aa40b35f6fa1eea15715 Mon Sep 17 00:00:00 2001 From: Dom Delnano Date: Fri, 6 Dec 2024 08:53:30 -0800 Subject: [PATCH] Add TLS protocol parser (#2050) Summary: Add TLS protocol parser This is the first piece of supporting TLS protocol tracing. Relevant Issues: N/A Type of change: /kind feature Test Plan: Parser test coverage verifies parser works for TLS v1.2 and later --------- Signed-off-by: Dom Del Nano --- .../socket_tracer/protocols/tls/BUILD.bazel | 47 +++ .../socket_tracer/protocols/tls/parse.cc | 218 +++++++++++ .../socket_tracer/protocols/tls/parse.h | 44 +++ .../socket_tracer/protocols/tls/parse_test.cc | 355 ++++++++++++++++++ .../socket_tracer/protocols/tls/types.h | 238 ++++++++++++ 5 files changed, 902 insertions(+) create mode 100644 src/stirling/source_connectors/socket_tracer/protocols/tls/BUILD.bazel create mode 100644 src/stirling/source_connectors/socket_tracer/protocols/tls/parse.cc create mode 100644 src/stirling/source_connectors/socket_tracer/protocols/tls/parse.h create mode 100644 src/stirling/source_connectors/socket_tracer/protocols/tls/parse_test.cc create mode 100644 src/stirling/source_connectors/socket_tracer/protocols/tls/types.h diff --git a/src/stirling/source_connectors/socket_tracer/protocols/tls/BUILD.bazel b/src/stirling/source_connectors/socket_tracer/protocols/tls/BUILD.bazel new file mode 100644 index 00000000000..45128debb39 --- /dev/null +++ b/src/stirling/source_connectors/socket_tracer/protocols/tls/BUILD.bazel @@ -0,0 +1,47 @@ +# Copyright 2018- The Pixie Authors. +# +# 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. +# +# SPDX-License-Identifier: Apache-2.0 + +load("//bazel:pl_build_system.bzl", "pl_cc_library", "pl_cc_test") + +package(default_visibility = ["//src/stirling:__subpackages__"]) + +pl_cc_library( + name = "cc_library", + srcs = glob( + [ + "*.cc", + ], + exclude = [ + "**/*_test.cc", + ], + ), + hdrs = glob( + [ + "*.h", + ], + ), + deps = [ + "//src/common/json:cc_library", + "//src/stirling/source_connectors/socket_tracer/protocols/common:cc_library", + "//src/stirling/utils:cc_library", + ], +) + +pl_cc_test( + name = "parse_test", + srcs = ["parse_test.cc"], + deps = [":cc_library"], +) diff --git a/src/stirling/source_connectors/socket_tracer/protocols/tls/parse.cc b/src/stirling/source_connectors/socket_tracer/protocols/tls/parse.cc new file mode 100644 index 00000000000..93d90cfa6ea --- /dev/null +++ b/src/stirling/source_connectors/socket_tracer/protocols/tls/parse.cc @@ -0,0 +1,218 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "src/stirling/source_connectors/socket_tracer/protocols/tls/parse.h" + +#include +#include +#include +#include + +#include + +#include "src/stirling/utils/binary_decoder.h" + +namespace px { +namespace stirling { +namespace protocols { +namespace tls { + +constexpr size_t kTLSRecordHeaderLength = 5; +constexpr size_t kExtensionMinimumLength = 4; +constexpr size_t kSNIExtensionMinimumLength = 3; + +// In TLS 1.3, Random is 32 bytes. +// In TLS 1.2 and earlier, gmt_unix_time is 4 bytes and Random is 28 bytes. +constexpr size_t kRandomStructLength = 32; + +StatusOr ExtractSNIExtension(std::map* exts, + BinaryDecoder* decoder) { + PX_ASSIGN_OR(auto server_name_list_length, decoder->ExtractBEInt(), + return ParseState::kInvalid); + std::vector server_names; + while (server_name_list_length > 0) { + PX_ASSIGN_OR(auto server_name_type, decoder->ExtractBEInt(), + return error::Internal("Failed to extract server name type")); + + // This is the only valid value for server_name_type and corresponds to host_name. + DCHECK_EQ(server_name_type, 0); + + PX_ASSIGN_OR(auto server_name_length, decoder->ExtractBEInt(), + return error::Internal("Failed to extract server name length")); + PX_ASSIGN_OR(auto server_name, decoder->ExtractString(server_name_length), + return error::Internal("Failed to extract server name")); + + server_names.push_back(std::string(server_name)); + server_name_list_length -= kSNIExtensionMinimumLength + server_name_length; + } + exts->insert({"server_name", ToJSONString(server_names)}); + return ParseState::kSuccess; +} + +/* + * The TLS wire protocol is best described in each of the RFCs for the protocol + * SSL v3.0: https://tools.ietf.org/html/rfc6101 + * TLS v1.0: https://tools.ietf.org/html/rfc2246 + * TLS v1.1: https://tools.ietf.org/html/rfc4346 + * TLS v1.2: https://tools.ietf.org/html/rfc5246 + * TLS v1.3: https://tools.ietf.org/html/rfc8446 + * + * These specs have c struct style definitions of the wire protocol. The wikipedia + * page is also a good resource to see it explained in a more typical ascii binary format + * diagram: https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_record + */ + +ParseState ParseFullFrame(BinaryDecoder* decoder, Frame* frame) { + PX_ASSIGN_OR(auto raw_content_type, decoder->ExtractBEInt(), + return ParseState::kInvalid); + auto content_type = magic_enum::enum_cast(raw_content_type); + if (!content_type.has_value()) { + return ParseState::kInvalid; + } + frame->content_type = content_type.value(); + + PX_ASSIGN_OR(auto legacy_version, decoder->ExtractBEInt(), return ParseState::kInvalid); + auto lv = magic_enum::enum_cast(legacy_version); + if (!lv.has_value()) { + return ParseState::kInvalid; + } + frame->legacy_version = lv.value(); + + PX_ASSIGN_OR(frame->length, decoder->ExtractBEInt(), return ParseState::kInvalid); + + if (frame->content_type == tls::ContentType::kApplicationData || + frame->content_type == tls::ContentType::kChangeCipherSpec || + frame->content_type == tls::ContentType::kAlert || + frame->content_type == tls::ContentType::kHeartbeat) { + if (!decoder->ExtractBufIgnore(frame->length).ok()) { + return ParseState::kInvalid; + } + return ParseState::kSuccess; + } + + PX_ASSIGN_OR(auto raw_handshake_type, decoder->ExtractBEInt(), + return ParseState::kInvalid); + auto handshake_type = magic_enum::enum_cast(raw_handshake_type); + if (!handshake_type.has_value()) { + return ParseState::kInvalid; + } + frame->handshake_type = handshake_type.value(); + + PX_ASSIGN_OR(auto handshake_length, decoder->ExtractBEInt(), + return ParseState::kInvalid); + frame->handshake_length = handshake_length; + + PX_ASSIGN_OR(auto raw_handshake_version, decoder->ExtractBEInt(), + return ParseState::kInvalid); + auto handshake_version = magic_enum::enum_cast(raw_handshake_version); + if (!handshake_version.has_value()) { + return ParseState::kInvalid; + } + frame->handshake_version = handshake_version.value(); + + // Skip the random struct. + if (!decoder->ExtractBufIgnore(kRandomStructLength).ok()) { + return ParseState::kInvalid; + } + + PX_ASSIGN_OR(auto session_id_len, decoder->ExtractBEInt(), return ParseState::kInvalid); + if (session_id_len > 32) { + return ParseState::kInvalid; + } + + if (session_id_len > 0) { + PX_ASSIGN_OR(frame->session_id, decoder->ExtractString(session_id_len), + return ParseState::kInvalid); + } + + PX_ASSIGN_OR(auto cipher_suite_length, decoder->ExtractBEInt(), + return ParseState::kInvalid); + if (frame->handshake_type == HandshakeType::kClientHello) { + if (!decoder->ExtractBufIgnore(cipher_suite_length).ok()) { + return ParseState::kInvalid; + } + } + + PX_ASSIGN_OR(auto compression_methods_length, decoder->ExtractBEInt(), + return ParseState::kInvalid); + if (frame->handshake_type == HandshakeType::kClientHello) { + if (!decoder->ExtractBufIgnore(compression_methods_length).ok()) { + return ParseState::kInvalid; + } + } + + // TODO(ddelnano): Test TLS 1.2 and earlier where extensions are not present + PX_ASSIGN_OR(auto extensions_length, decoder->ExtractBEInt(), + return ParseState::kInvalid); + if (extensions_length == 0) { + return ParseState::kSuccess; + } + + while (extensions_length > 0) { + PX_ASSIGN_OR(auto extension_type, decoder->ExtractBEInt(), + return ParseState::kInvalid); + PX_ASSIGN_OR(auto extension_length, decoder->ExtractBEInt(), + return ParseState::kInvalid); + + if (extension_length > 0) { + if (extension_type == 0x00) { + if (!ExtractSNIExtension(&frame->extensions, decoder).ok()) { + return ParseState::kInvalid; + } + } else { + if (!decoder->ExtractBufIgnore(extension_length).ok()) { + return ParseState::kInvalid; + } + } + } + + extensions_length -= kExtensionMinimumLength + extension_length; + } + + return ParseState::kSuccess; +} + +} // namespace tls + +template <> +ParseState ParseFrame(message_type_t, std::string_view* buf, tls::Frame* frame, NoState*) { + // TLS record header is 5 bytes. The size of the record is in bytes 4 and 5. + if (buf->length() < tls::kTLSRecordHeaderLength) { + return ParseState::kNeedsMoreData; + } + uint16_t length = static_cast((*buf)[3]) << 8 | static_cast((*buf)[4]); + if (buf->length() < length + tls::kTLSRecordHeaderLength) { + return ParseState::kNeedsMoreData; + } + + BinaryDecoder decoder(*buf); + auto parse_result = tls::ParseFullFrame(&decoder, frame); + if (parse_result == ParseState::kSuccess) { + buf->remove_prefix(length + tls::kTLSRecordHeaderLength); + } + return parse_result; +} + +template <> +size_t FindFrameBoundary(message_type_t, std::string_view, size_t, NoState*) { + // Not implemented. + return std::string::npos; +} + +} // namespace protocols +} // namespace stirling +} // namespace px diff --git a/src/stirling/source_connectors/socket_tracer/protocols/tls/parse.h b/src/stirling/source_connectors/socket_tracer/protocols/tls/parse.h new file mode 100644 index 00000000000..12e5bdbe29d --- /dev/null +++ b/src/stirling/source_connectors/socket_tracer/protocols/tls/parse.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "src/common/base/base.h" +#include "src/stirling/source_connectors/socket_tracer/protocols/common/interface.h" +#include "src/stirling/source_connectors/socket_tracer/protocols/tls/types.h" +#include "src/stirling/utils/binary_decoder.h" + +namespace px { +namespace stirling { +namespace protocols { +namespace tls { + +ParseState ParseFullFrame(BinaryDecoder* decoder, Frame* frame); + +} + +template <> +ParseState ParseFrame(message_type_t type, std::string_view* buf, tls::Frame* frame, NoState*); + +template <> +size_t FindFrameBoundary(message_type_t type, std::string_view buf, size_t start_pos, + NoState*); + +} // namespace protocols +} // namespace stirling +} // namespace px diff --git a/src/stirling/source_connectors/socket_tracer/protocols/tls/parse_test.cc b/src/stirling/source_connectors/socket_tracer/protocols/tls/parse_test.cc new file mode 100644 index 00000000000..bbffb9618f7 --- /dev/null +++ b/src/stirling/source_connectors/socket_tracer/protocols/tls/parse_test.cc @@ -0,0 +1,355 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "src/stirling/source_connectors/socket_tracer/protocols/tls/parse.h" + +#include "src/common/testing/testing.h" + +namespace px { +namespace stirling { +namespace protocols { + +using ::testing::Contains; + +// clang-format off + +constexpr uint8_t kChangeCipherSpec[] = { + 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, +}; + +constexpr uint8_t kApplicationData[] = { + 0x17, 0x03, 0x03, 0x00, 0x29, 0xec, 0x19, 0x69, 0x2d, 0x60, 0x95, 0x55, 0xaa, 0x49, 0xac, 0xa6, + 0x93, 0x2f, 0x3f, 0x63, 0xfa, 0x9a, 0x6f, 0x09, 0xe7, 0x9f, 0xf5, 0x1f, 0xbb, 0x12, 0x68, 0x47, + 0x4c, 0x8d, 0x03, 0x1d, 0x97, 0xba, 0x97, 0x6d, 0xdc, 0x81, 0xa4, 0x25, 0x83, 0x56 +}; + +constexpr uint8_t kClientHelloNeedsMoreData[] = { + 0x16, 0x03, 0x01, 0x01, 0x1a, 0x01, 0x00, 0x01, 0x16, 0x03, 0x03, 0x5f, 0xca, 0xbd, 0x4b, 0x62, + 0x28, 0x15, 0x8f, 0x91, 0x74, 0xdf, 0x43, 0xa2, 0x2c, 0xa6, 0x2d, 0xe7, 0x3b, 0x5b, 0x34, 0x91, +}; + +constexpr uint8_t kInvalidSessionID[] = { + 0x16, 0x03, 0x01, 0x01, 0x1a, 0x01, 0x00, 0x01, 0x16, 0x03, 0x03, 0x5f, 0xca, 0xbd, 0x4b, 0x62, + 0x28, 0x15, 0x8f, 0x91, 0x74, 0xdf, 0x43, 0xa2, 0x2c, 0xa6, 0x2d, 0xe7, 0x3b, 0x5b, 0x34, 0x91, + // 11th byte must be between 0 and 32 + 0xe0, 0x55, 0x1d, 0xc9, 0x26, 0xc5, 0xa4, 0x29, 0xad, 0x36, 0xe5, 0xff, 0x0b, 0x19, 0xc5, 0xa5, + 0xa1, 0x6c, 0xfa, 0x2e, 0x64, 0x42, 0x6f, 0xed, 0xe0, 0x06, 0xef, 0x08, 0x2f, 0x71, 0x58, 0x8b, + 0x25, 0x5c, 0xf0, 0x5d, 0xbf, 0x16, 0x2a, 0xa2, 0x52, 0x0b, 0xc2, 0xfe, 0x00, 0x26, 0xc0, 0x2b, + 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, + 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x13, 0x01, + 0x13, 0x02, 0x13, 0x03, 0x01, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x00, + 0x1a, 0x61, 0x72, 0x67, 0x6f, 0x63, 0x64, 0x2d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, + 0x72, 0x65, 0x70, 0x6f, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x18, 0x08, 0x04, + 0x04, 0x03, 0x08, 0x07, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x05, 0x03, + 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x05, 0x00, 0x03, 0x02, 0x68, 0x32, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x05, + 0x04, 0x03, 0x04, 0x03, 0x03, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xf4, + 0x82, 0x10, 0xb1, 0x65, 0x1a, 0xf3, 0x04, 0x82, 0xbb, 0x82, 0x83, 0x85, 0xbc, 0xc5, 0x01, 0x62, + 0xe4, 0x15, 0x84, 0x8f, 0x7b, 0x12, 0x37, 0xf1, 0xaf, 0x7b, 0x1f, 0xfd, 0xf0, 0xf5, 0x1b, +}; + +constexpr uint8_t kInvalidContentType[] = { + // First byte must be one of tls::ContentType. + 0x19, 0x03, 0x01, 0x01, 0x1a, 0x01, 0x00, 0x01, 0x16, 0x03, 0x03, 0x5f, 0xca, 0xbd, 0x4b, 0x62, + 0x28, 0x15, 0x8f, 0x91, 0x74, 0xdf, 0x43, 0xa2, 0x2c, 0xa6, 0x2d, 0xe7, 0x3b, 0x5b, 0x34, 0x91, + 0xe0, 0x55, 0x1d, 0xc9, 0x26, 0xc5, 0xa4, 0x29, 0xad, 0x36, 0xe5, 0x20, 0x0b, 0x19, 0xc5, 0xa5, + 0xa1, 0x6c, 0xfa, 0x2e, 0x64, 0x42, 0x6f, 0xed, 0xe0, 0x06, 0xef, 0x08, 0x2f, 0x71, 0x58, 0x8b, + 0x25, 0x5c, 0xf0, 0x5d, 0xbf, 0x16, 0x2a, 0xa2, 0x52, 0x0b, 0xc2, 0xfe, 0x00, 0x26, 0xc0, 0x2b, + 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, + 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x13, 0x01, + 0x13, 0x02, 0x13, 0x03, 0x01, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x00, + 0x1a, 0x61, 0x72, 0x67, 0x6f, 0x63, 0x64, 0x2d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, + 0x72, 0x65, 0x70, 0x6f, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x18, 0x08, 0x04, + 0x04, 0x03, 0x08, 0x07, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x05, 0x03, + 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x05, 0x00, 0x03, 0x02, 0x68, 0x32, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x05, + 0x04, 0x03, 0x04, 0x03, 0x03, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xf4, + 0x82, 0x10, 0xb1, 0x65, 0x1a, 0xf3, 0x04, 0x82, 0xbb, 0x82, 0x83, 0x85, 0xbc, 0xc5, 0x01, 0x62, + 0xe4, 0x15, 0x84, 0x8f, 0x7b, 0x12, 0x37, 0xf1, 0xaf, 0x7b, 0x1f, 0xfd, 0xf0, 0xf5, 0x1b, +}; + +constexpr uint8_t kInvalidLegacyVersion[] = { + // Second and third bytes are invalid legacy version (should be between 0x0300 and 0x0304). + 0x16, 0x02, 0x01, 0x01, 0x1a, 0x01, 0x00, 0x01, 0x16, 0x03, 0x03, 0x5f, 0xca, 0xbd, 0x4b, 0x62, + 0x28, 0x15, 0x8f, 0x91, 0x74, 0xdf, 0x43, 0xa2, 0x2c, 0xa6, 0x2d, 0xe7, 0x3b, 0x5b, 0x34, 0x91, + 0xe0, 0x55, 0x1d, 0xc9, 0x26, 0xc5, 0xa4, 0x29, 0xad, 0x36, 0xe5, 0x20, 0x0b, 0x19, 0xc5, 0xa5, + 0xa1, 0x6c, 0xfa, 0x2e, 0x64, 0x42, 0x6f, 0xed, 0xe0, 0x06, 0xef, 0x08, 0x2f, 0x71, 0x58, 0x8b, + 0x25, 0x5c, 0xf0, 0x5d, 0xbf, 0x16, 0x2a, 0xa2, 0x52, 0x0b, 0xc2, 0xfe, 0x00, 0x26, 0xc0, 0x2b, + 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, + 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x13, 0x01, + 0x13, 0x02, 0x13, 0x03, 0x01, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x00, + 0x1a, 0x61, 0x72, 0x67, 0x6f, 0x63, 0x64, 0x2d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, + 0x72, 0x65, 0x70, 0x6f, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x18, 0x08, 0x04, + 0x04, 0x03, 0x08, 0x07, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x05, 0x03, + 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x05, 0x00, 0x03, 0x02, 0x68, 0x32, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x05, + 0x04, 0x03, 0x04, 0x03, 0x03, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xf4, + 0x82, 0x10, 0xb1, 0x65, 0x1a, 0xf3, 0x04, 0x82, 0xbb, 0x82, 0x83, 0x85, 0xbc, 0xc5, 0x01, 0x62, + 0xe4, 0x15, 0x84, 0x8f, 0x7b, 0x12, 0x37, 0xf1, 0xaf, 0x7b, 0x1f, 0xfd, 0xf0, 0xf5, 0x1b, +}; + +constexpr uint8_t kInvalidHandshakeType[] = { + // Sixth byte must be one of tls::HandshakeType. + 0x16, 0x03, 0x01, 0x01, 0x1a, 0x19, 0x00, 0x01, 0x16, 0x03, 0x03, 0x5f, 0xca, 0xbd, 0x4b, 0x62, + 0x28, 0x15, 0x8f, 0x91, 0x74, 0xdf, 0x43, 0xa2, 0x2c, 0xa6, 0x2d, 0xe7, 0x3b, 0x5b, 0x34, 0x91, + 0xe0, 0x55, 0x1d, 0xc9, 0x26, 0xc5, 0xa4, 0x29, 0xad, 0x36, 0xe5, 0x20, 0x0b, 0x19, 0xc5, 0xa5, + 0xa1, 0x6c, 0xfa, 0x2e, 0x64, 0x42, 0x6f, 0xed, 0xe0, 0x06, 0xef, 0x08, 0x2f, 0x71, 0x58, 0x8b, + 0x25, 0x5c, 0xf0, 0x5d, 0xbf, 0x16, 0x2a, 0xa2, 0x52, 0x0b, 0xc2, 0xfe, 0x00, 0x26, 0xc0, 0x2b, + 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, + 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x13, 0x01, + 0x13, 0x02, 0x13, 0x03, 0x01, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x00, + 0x1a, 0x61, 0x72, 0x67, 0x6f, 0x63, 0x64, 0x2d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, + 0x72, 0x65, 0x70, 0x6f, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x18, 0x08, 0x04, + 0x04, 0x03, 0x08, 0x07, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x05, 0x03, + 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x05, 0x00, 0x03, 0x02, 0x68, 0x32, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x05, + 0x04, 0x03, 0x04, 0x03, 0x03, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xf4, + 0x82, 0x10, 0xb1, 0x65, 0x1a, 0xf3, 0x04, 0x82, 0xbb, 0x82, 0x83, 0x85, 0xbc, 0xc5, 0x01, 0x62, + 0xe4, 0x15, 0x84, 0x8f, 0x7b, 0x12, 0x37, 0xf1, 0xaf, 0x7b, 0x1f, 0xfd, 0xf0, 0xf5, 0x1b, +}; + +constexpr uint8_t kInvalidHandshakeVersion[] = { + // 10th and 11th bytes must be between 0x0300 and 0x0304. + 0x16, 0x03, 0x01, 0x01, 0x1a, 0x01, 0x00, 0x01, 0x16, 0x02, 0x03, 0x5f, 0xca, 0xbd, 0x4b, 0x62, + 0x28, 0x15, 0x8f, 0x91, 0x74, 0xdf, 0x43, 0xa2, 0x2c, 0xa6, 0x2d, 0xe7, 0x3b, 0x5b, 0x34, 0x91, + 0xe0, 0x55, 0x1d, 0xc9, 0x26, 0xc5, 0xa4, 0x29, 0xad, 0x36, 0xe5, 0x20, 0x0b, 0x19, 0xc5, 0xa5, + 0xa1, 0x6c, 0xfa, 0x2e, 0x64, 0x42, 0x6f, 0xed, 0xe0, 0x06, 0xef, 0x08, 0x2f, 0x71, 0x58, 0x8b, + 0x25, 0x5c, 0xf0, 0x5d, 0xbf, 0x16, 0x2a, 0xa2, 0x52, 0x0b, 0xc2, 0xfe, 0x00, 0x26, 0xc0, 0x2b, + 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, + 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x13, 0x01, + 0x13, 0x02, 0x13, 0x03, 0x01, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x00, + 0x1a, 0x61, 0x72, 0x67, 0x6f, 0x63, 0x64, 0x2d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, + 0x72, 0x65, 0x70, 0x6f, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x18, 0x08, 0x04, + 0x04, 0x03, 0x08, 0x07, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x05, 0x03, + 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x05, 0x00, 0x03, 0x02, 0x68, 0x32, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x05, + 0x04, 0x03, 0x04, 0x03, 0x03, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xf4, + 0x82, 0x10, 0xb1, 0x65, 0x1a, 0xf3, 0x04, 0x82, 0xbb, 0x82, 0x83, 0x85, 0xbc, 0xc5, 0x01, 0x62, + 0xe4, 0x15, 0x84, 0x8f, 0x7b, 0x12, 0x37, 0xf1, 0xaf, 0x7b, 0x1f, 0xfd, 0xf0, 0xf5, 0x1b, +}; + +// This exposed a bug with parsing the TLS record length +constexpr uint8_t kFailedTLSv1_2[] = { + 0x16, 0x03, 0x01, 0x00, 0xCB, 0x01, 0x00, 0x00, 0xC7, 0x03, 0x03, 0x7F, 0xC7, 0x41, 0xBC, 0xDE, + 0xF7, 0x3F, 0x09, 0x3A, 0xCB, 0x00, 0x91, 0x38, 0x72, 0xE7, 0x74, 0x81, 0xDE, 0x8D, 0xDD, 0x9F, + 0x0F, 0xA7, 0x0D, 0x29, 0xC0, 0x43, 0x7A, 0xD2, 0x4C, 0x4B, 0xBF, 0x00, 0x00, 0x38, 0xC0, 0x2C, + 0xC0, 0x30, 0x00, 0x9F, 0xCC, 0xA9, 0xCC, 0xA8, 0xCC, 0xAA, 0xC0, 0x2B, 0xC0, 0x2F, 0x00, 0x9E, + 0xC0, 0x24, 0xC0, 0x28, 0x00, 0x6B, 0xC0, 0x23, 0xC0, 0x27, 0x00, 0x67, 0xC0, 0x0A, 0xC0, 0x14, + 0x00, 0x39, 0xC0, 0x09, 0xC0, 0x13, 0x00, 0x33, 0x00, 0x9D, 0x00, 0x9C, 0x00, 0x3D, 0x00, 0x3C, + 0x00, 0x35, 0x00, 0x2F, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x66, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, + 0x01, 0x02, 0x00, 0x0A, 0x00, 0x0C, 0x00, 0x0A, 0x00, 0x1D, 0x00, 0x17, 0x00, 0x1E, 0x00, 0x19, + 0x00, 0x18, 0x00, 0x10, 0x00, 0x0E, 0x00, 0x0C, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, + 0x2F, 0x31, 0x2E, 0x31, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x30, + 0x00, 0x2E, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0A, + 0x08, 0x0B, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x03, 0x03, + 0x02, 0x03, 0x03, 0x01, 0x02, 0x01, 0x03, 0x02, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, +}; + +// This caught a bug where TLS extensions can have a length of 0. Prior to this we unconditionally +// tried to parse a server_name extension that had a length of 0. +constexpr uint8_t kServerHelloWithZeroLengthExtension[] = { + 0x16, 0x03, 0x03, 0x00, 0x70, 0x02, 0x00, 0x00, 0x6C, 0x03, 0x03, 0xBD, 0x83, 0x64, 0x96, 0xD6, + 0xFF, 0x59, 0x80, 0x83, 0x9C, 0x52, 0x71, 0x47, 0x7A, 0xCF, 0x8A, 0xA8, 0xAF, 0x6C, 0xC6, 0xD1, + 0x67, 0x84, 0xDA, 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x01, 0x20, 0xC3, 0x59, 0x22, 0x59, + 0x51, 0x13, 0x7E, 0x7C, 0x4C, 0xD0, 0xD3, 0xCD, 0x54, 0x10, 0x54, 0xB1, 0xF4, 0xCE, 0x27, 0x3E, + 0x50, 0xAD, 0x67, 0x04, 0xEF, 0x1D, 0x07, 0x50, 0x22, 0x82, 0x21, 0xBF, 0xC0, 0x30, 0x00, 0x00, + 0x24, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, + 0x01, 0x02, 0x00, 0x10, 0x00, 0x0B, 0x00, 0x09, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2F, 0x31, 0x2E, + 0x31, 0x00, 0x17, 0x00, 0x00, +}; + +constexpr uint8_t kValidClientHello[] = { + 0x16, 0x03, 0x01, 0x01, 0x1a, 0x01, 0x00, 0x01, 0x16, 0x03, 0x03, 0x5f, 0xca, 0xbd, 0x4b, 0x62, + 0x28, 0x15, 0x8f, 0x91, 0x74, 0xdf, 0x43, 0xa2, 0x2c, 0xa6, 0x2d, 0xe7, 0x3b, 0x5b, 0x34, 0x91, + 0xe0, 0x55, 0x1d, 0xc9, 0x26, 0xc5, 0xa4, 0x29, 0xad, 0x36, 0xe5, 0x20, 0x0b, 0x19, 0xc5, 0xa5, + 0xa1, 0x6c, 0xfa, 0x2e, 0x64, 0x42, 0x6f, 0xed, 0xe0, 0x06, 0xef, 0x08, 0x2f, 0x71, 0x58, 0x8b, + 0x25, 0x5c, 0xf0, 0x5d, 0xbf, 0x16, 0x2a, 0xa2, 0x52, 0x0b, 0xc2, 0xfe, 0x00, 0x26, 0xc0, 0x2b, + 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, + 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x13, 0x01, + 0x13, 0x02, 0x13, 0x03, 0x01, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x00, + 0x1a, 0x61, 0x72, 0x67, 0x6f, 0x63, 0x64, 0x2d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, + 0x72, 0x65, 0x70, 0x6f, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x18, 0x08, 0x04, + 0x04, 0x03, 0x08, 0x07, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x05, 0x03, + 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x05, 0x00, 0x03, 0x02, 0x68, 0x32, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x05, + 0x04, 0x03, 0x04, 0x03, 0x03, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xf4, + 0x82, 0x10, 0xb1, 0x65, 0x1a, 0xf3, 0x04, 0x82, 0xbb, 0x82, 0x83, 0x85, 0xbc, 0xc5, 0x01, 0x62, + 0xe4, 0x15, 0x84, 0x8f, 0x7b, 0x12, 0x37, 0xf1, 0xaf, 0x7b, 0x1f, 0xfd, 0xf0, 0xf5, 0x1b, +}; + +constexpr uint8_t kServerHello[] = { + 0x16, 0x03, 0x03, 0x00, 0x7a, 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, 0x66, 0x13, 0xca, 0x43, 0x26, + 0x96, 0x9a, 0xf6, 0x10, 0x0f, 0x5d, 0x1a, 0x3e, 0x59, 0x54, 0xd6, 0x45, 0x52, 0xe1, 0x07, 0xba, + 0x60, 0x67, 0x2f, 0x97, 0x5f, 0x27, 0x6b, 0xfc, 0x37, 0x5d, 0xaf, 0x20, 0x0b, 0x19, 0xc5, 0xa5, + 0xa1, 0x6c, 0xfa, 0x2e, 0x64, 0x42, 0x6f, 0xed, 0xe0, 0x06, 0xef, 0x08, 0x2f, 0x71, 0x58, 0x8b, + 0x25, 0x5c, 0xf0, 0x5d, 0xbf, 0x16, 0x2a, 0xa2, 0x52, 0x0b, 0xc2, 0xfe, 0x13, 0x01, 0x00, 0x00, + 0x2e, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x28, + 0xc5, 0x43, 0x52, 0x4f, 0x61, 0xf4, 0x7a, 0x2b, 0xc5, 0xcc, 0x43, 0x27, 0x0e, 0xe8, 0xa0, 0xc2, + 0x6e, 0x2a, 0x86, 0x60, 0x80, 0x96, 0x5d, 0xdf, 0xfb, 0x86, 0xe2, 0x46, 0xe9, 0x6e, 0x5c, +}; + +// clang-format on + +class TLSParserTest : public ::testing::Test {}; + +TEST_F(TLSParserTest, ParseValidChangeCipherSpec) { + auto frame_view = CreateStringView(CharArrayStringView(kChangeCipherSpec)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + + ASSERT_EQ(frame.content_type, tls::ContentType::kChangeCipherSpec); + ASSERT_EQ(frame.length, 0x1); + ASSERT_EQ(frame.legacy_version, tls::LegacyVersion::kTLS1_2); + ASSERT_EQ(state, ParseState::kSuccess); +} + +TEST_F(TLSParserTest, ParseValidApplicationData) { + auto frame_view = CreateStringView(CharArrayStringView(kApplicationData)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + + ASSERT_EQ(frame.content_type, tls::ContentType::kApplicationData); + ASSERT_EQ(frame.legacy_version, tls::LegacyVersion::kTLS1_2); + ASSERT_EQ(frame.length, 41); + ASSERT_EQ(state, ParseState::kSuccess); +} + +TEST_F(TLSParserTest, NeedsMoreData) { + auto frame_view = CreateStringView(CharArrayStringView(kClientHelloNeedsMoreData)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + ASSERT_EQ(state, ParseState::kNeedsMoreData); +} + +TEST_F(TLSParserTest, TLSv1_2_InvalidTLSRecordLengthBug) { + auto frame_view = CreateStringView(CharArrayStringView(kFailedTLSv1_2)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + ASSERT_EQ(state, ParseState::kSuccess); +} + +TEST_F(TLSParserTest, InvalidContentType) { + auto frame_view = CreateStringView(CharArrayStringView(kInvalidContentType)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + ASSERT_EQ(state, ParseState::kInvalid); +} + +TEST_F(TLSParserTest, InvalidLegacyVersion) { + auto frame_view = CreateStringView(CharArrayStringView(kInvalidLegacyVersion)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + ASSERT_EQ(state, ParseState::kInvalid); +} + +TEST_F(TLSParserTest, InvalidHandshakeType) { + auto frame_view = CreateStringView(CharArrayStringView(kInvalidHandshakeType)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + ASSERT_EQ(state, ParseState::kInvalid); +} + +TEST_F(TLSParserTest, InvalidHandshakeVersion) { + auto frame_view = CreateStringView(CharArrayStringView(kInvalidHandshakeVersion)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + ASSERT_EQ(state, ParseState::kInvalid); +} + +TEST_F(TLSParserTest, InvalidSessionID) { + auto frame_view = CreateStringView(CharArrayStringView(kInvalidSessionID)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + ASSERT_EQ(state, ParseState::kInvalid); +} + +TEST_F(TLSParserTest, ParseValidClientHello) { + auto frame_view = CreateStringView(CharArrayStringView(kValidClientHello)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + + ASSERT_EQ(frame.content_type, tls::ContentType::kHandshake); + ASSERT_EQ(frame.legacy_version, tls::LegacyVersion::kTLS1_0); + ASSERT_EQ(frame.length, 282); + + ASSERT_EQ(frame.handshake_type, tls::HandshakeType::kClientHello); + ASSERT_EQ(frame.handshake_length, 278); + ASSERT_EQ(frame.handshake_version, tls::LegacyVersion::kTLS1_2); + ASSERT_GT(frame.session_id.size(), 0); + + // Validate the SNI extension was parsed properly + ASSERT_EQ(frame.extensions.size(), 1); + ASSERT_EQ(frame.extensions["server_name"], "[\"argocd-cluster-repo-server\"]"); + ASSERT_EQ(state, ParseState::kSuccess); +} + +TEST_F(TLSParserTest, ParseValidServerHello) { + auto frame_view = CreateStringView(CharArrayStringView(kServerHello)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + + ASSERT_EQ(frame.content_type, tls::ContentType::kHandshake); + ASSERT_EQ(frame.legacy_version, tls::LegacyVersion::kTLS1_2); + ASSERT_EQ(frame.length, 122); + + ASSERT_EQ(frame.handshake_type, tls::HandshakeType::kServerHello); + ASSERT_EQ(frame.handshake_length, 118); + ASSERT_EQ(frame.handshake_version, tls::LegacyVersion::kTLS1_2); + ASSERT_GT(frame.session_id.size(), 0); + ASSERT_EQ(state, ParseState::kSuccess); +} + +TEST_F(TLSParserTest, ServerHelloExtsWithZeroLenExtension) { + auto frame_view = + CreateStringView(CharArrayStringView(kServerHelloWithZeroLengthExtension)); + + tls::Frame frame; + ParseState state = ParseFrame(message_type_t::kRequest, &frame_view, &frame); + + ASSERT_EQ(frame.content_type, tls::ContentType::kHandshake); + + ASSERT_EQ(frame.handshake_type, tls::HandshakeType::kServerHello); + ASSERT_EQ(state, ParseState::kSuccess); +} + +} // namespace protocols +} // namespace stirling +} // namespace px diff --git a/src/stirling/source_connectors/socket_tracer/protocols/tls/types.h b/src/stirling/source_connectors/socket_tracer/protocols/tls/types.h new file mode 100644 index 00000000000..c64da970554 --- /dev/null +++ b/src/stirling/source_connectors/socket_tracer/protocols/tls/types.h @@ -0,0 +1,238 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +#include "src/common/json/json.h" +#include "src/stirling/source_connectors/socket_tracer/protocols/common/event_parser.h" +#include "src/stirling/utils/utils.h" + +namespace px::stirling::protocols::tls { +// Forward declaration so enum_range can be specialized. +enum class LegacyVersion : uint16_t; + +} // namespace px::stirling::protocols::tls + +template <> +struct magic_enum::customize::enum_range { + static constexpr int min = 0x0300; + static constexpr int max = 0x0304; +}; + +namespace px { +namespace stirling { +namespace protocols { +namespace tls { + +using ::px::utils::ToJSONString; + +enum class ContentType : uint8_t { + kChangeCipherSpec = 0x14, + kAlert = 0x15, + kHandshake = 0x16, + kApplicationData = 0x17, + kHeartbeat = 0x18, +}; + +enum class LegacyVersion : uint16_t { + kSSL3 = 0x0300, + kTLS1_0 = 0x0301, + kTLS1_1 = 0x0302, + kTLS1_2 = 0x0303, + kTLS1_3 = 0x0304, +}; + +enum class AlertLevel : uint8_t { + kWarning = 1, + kFatal = 2, +}; + +enum class AlertDesc : uint8_t { + kCloseNotify = 0, + kUnexpectedMessage = 10, + kBadRecordMAC = 20, + kDecryptionFailed = 21, + kRecordOverflow = 22, + kDecompressionFailure = 30, + kHandshakeFailure = 40, + kNoCertificate = 41, + kBadCertificate = 42, + kUnsupportedCertificate = 43, + kCertificateRevoked = 44, + kCertificateExpired = 45, + kCertificateUnknown = 46, + kIllegalParameter = 47, + kUnknownCA = 48, + kAccessDenied = 49, + kDecodeError = 50, + kDecryptError = 51, + kExportRestriction = 60, + kProtocolVersion = 70, + kInsufficientSecurity = 71, + kInternalError = 80, + kInappropriateFallback = 86, + kUserCanceled = 90, + kNoRenegotiation = 100, + kUnsupportedExtension = 110, + kUnrecognizedName = 112, + kBadCertificateStatusResponse = 113, + kBadCertificateHashValue = 114, + kUnknownPSKIdentity = 115, + kCertificateRequired = 116, + kNoApplicationProtocol = 120, + // TODO(ddelnano): Find a better way to represent this. + kNoApplicationProtocol2 = 255, +}; + +enum class HandshakeType : uint8_t { + kHelloRequest = 0, + kClientHello = 1, + kServerHello = 2, + kNewSessionTicket = 4, + kEncryptedExtensions = 8, // TLS 1.3 only + kCertificate = 11, + kServerKeyExchange = 12, + kCertificateRequest = 13, + kServerHelloDone = 14, + kCertificateVerify = 15, + kClientKeyExchange = 16, + kFinished = 20, +}; + +// Defined from +// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1 +enum class ExtensionType : uint16_t { + kServerName = 0, + kMaxFragmentLength = 1, + kClientCertificateURL = 2, + kTrustedCAKeys = 3, + kTruncatedHMAC = 4, + kStatusRequest = 5, + kUserMapping = 6, + kClientAuthz = 7, + kServerAuthz = 8, + kCertType = 9, + kSupportedGroups = 10, + kECPointFormats = 11, + kSRP = 12, + kSignatureAlgorithms = 13, + kUseSRTP = 14, + kHeartbeat = 15, + kALPN = 16, + kStatusRequestV2 = 17, + kSignedCertificateTimestamp = 18, + kClientCertificateType = 19, + kServerCertificateType = 20, + kPadding = 21, + kEncryptThenMAC = 22, + kExtendedMasterSecret = 23, + kTokenBinding = 24, + kCachedInfo = 25, + kTLSLTS = 26, + kCompressCertificate = 27, + kRecordSizeLimit = 28, + kPwdProtect = 29, + kPwdClear = 30, + kPasswordSalt = 31, + kTicketPinning = 32, + kTLSCertWithExternalPSK = 33, + kDelegatedCredential = 34, + kSessionTicket = 35, + kTLMSP = 36, + kTLMSPProxy = 37, + kTLMSDelegate = 38, + kSupportedEktCiphers = 39, + kPreSharedKey = 41, + kEarlyData = 42, + kSupportedVersions = 43, + kCookie = 44, + kPSKKeyExchangeModes = 45, + kCertificateAuthorities = 47, + kOIDFilters = 48, + kPostHandshakeAuth = 49, + kSignatureAlgorithmsCert = 50, + kKeyShare = 51, + kTransparencyInfo = 52, + kConnectionIdDeprecated = 53, + kConnectionId = 54, + kExternalIdHash = 55, + kExternalSessionId = 56, + kQuicTransportParameters = 57, + kTicketRequest = 58, + kDNSSecChain = 59, + kSequenceNumberEncryptionAlgorithms = 60, + kRRC = 61, + kECHOuterExtensions = 64768, + kEncryptedClientHello = 65037, + kRenegotiationInfo = 65281, +}; + +struct Frame : public FrameBase { + ContentType content_type; + + LegacyVersion legacy_version; + + uint16_t length = 0; + + HandshakeType handshake_type; + + uint24_t handshake_length; + + LegacyVersion handshake_version; + + std::string session_id; + std::map extensions; + + bool consumed = false; + + size_t ByteSize() const override { return sizeof(Frame); } + + std::string ToString() const override { + return absl::Substitute( + "TLS Frame [len=$0 content_type=$1 legacy_version=$2 handshake_version=$3 " + "handshake_type=$4 extensions=$5]", + length, content_type, legacy_version, handshake_version, handshake_type, + ToJSONString(extensions)); + } +}; + +struct Record { + Frame req; + Frame resp; + + std::string ToString() const { + return absl::Substitute("req=[$0] resp=[$1]", req.ToString(), resp.ToString()); + } +}; + +using stream_id_t = uint16_t; +struct ProtocolTraits : public BaseProtocolTraits { + using frame_type = Frame; + using record_type = Record; + using state_type = NoState; + using key_type = stream_id_t; +}; + +} // namespace tls +} // namespace protocols +} // namespace stirling +} // namespace px