Skip to content

Add scan_tls TLS handshake metadata scanner module#275

Open
PaXXeco wants to merge 1 commit into
simsong:mainfrom
PaXXeco:feature/tls-scanner
Open

Add scan_tls TLS handshake metadata scanner module#275
PaXXeco wants to merge 1 commit into
simsong:mainfrom
PaXXeco:feature/tls-scanner

Conversation

@PaXXeco

@PaXXeco PaXXeco commented Jun 26, 2026

Copy link
Copy Markdown

Summary

This PR adds scan_tls, a new scanner module that extracts TLS handshake
metadata from reassembled TCP streams. It parses ClientHello and ServerHello
messages and records, per connection:

  • SNI (Server Name Indication)
  • Negotiated/offered cipher suites
  • TLS version
  • JA3 fingerprint

Output is appended to tls_sni.json in the tcpflow output directory.

Motivation

TLS now carries the majority of network traffic, but tcpflow had no built-in
way to surface handshake metadata. This is useful for traffic analysis,
fingerprinting, and inventorying TLS endpoints without decrypting payloads.
The module follows the existing scanner pattern (scan_http, scan_netviz, etc.)
so it integrates cleanly with the current architecture.

Changes

  • New files: src/scan_tls.cpp, src/scan_tls.h
  • Registered the scanner in src/Makefile.am
  • No new external dependencies
  • No changes to existing behavior — the scanner is opt-in like the others

Testing

Built locally with the standard autotools flow:

./configure
make

Ran against pcaps containing TLS traffic and confirmed tls_sni.json is
generated with correct SNI, cipher suites, version and JA3 values.

Notes

JA3 follows the original spec: legacy_version is kept raw for fingerprint
compliance, with a separate display_version field for TLS 1.3 detection.

@simsong

simsong commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Hi. I really appreciate this, but I am no longer actively maintaining this project. Would you like to take it over?

Failing that, I cannot accept pull requests that do not include tests for the new code. You describe a testing procedure, but I really need tests to cover the new functionality.

How would you like to proceed? Do you want to take over this project? Or would you like to join its maintenance? And do you have the ability to write a test to test your contribution?

Simson

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new built-in (but opt-in) scanner module, scan_tls, that parses TLS ClientHello/ServerHello from reassembled TCP streams to extract handshake metadata (e.g., SNI, version, and JA3) and append it to a JSON output file in the tcpflow output directory.

Changes:

  • Added new TLS scanner implementation (src/scan_tls.cpp) and associated parsing/data definitions (src/scan_tls.h).
  • Registered scan_tls with the built-in scanner list and exported its symbol (src/tcpflow.cpp, src/tcpflow.h).
  • Wired the new module into the build (src/Makefile.am).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/tcpflow.h Declares the scan_tls scanner symbol for linkage with the scanner system.
src/tcpflow.cpp Registers scan_tls in the built-in scanner list.
src/scan_tls.h Defines TLS record/handshake structs and metadata containers for parsed results.
src/scan_tls.cpp Implements TLS handshake parsing, JA3 computation, and JSON output appending.
src/Makefile.am Adds the new TLS scanner sources to the build.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/scan_tls.cpp
Comment on lines +18 to +29
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <cstring>
#include <ctime>
#include <mutex>

#ifdef HAVE_OPENSSL_MD5_H
#include <openssl/evp.h>
#endif
Comment thread src/scan_tls.cpp
Comment on lines +158 to +164
if (off + 2 <= eend) {
uint16_t curves_bytes = u16be(buf + off);
for (uint16_t i = 2; i + 1 < curves_bytes + 2 && off + i + 2 <= eend; i += 2) {
uint16_t curve = u16be(buf + off + i);
if (!is_grease(curve)) ch.elliptic_curves.push_back(curve);
}
}
Comment thread src/scan_tls.cpp
Comment on lines +169 to +174
if (off + 1 <= eend) {
uint8_t fmt_len = buf[off];
for (uint8_t i = 1; i <= fmt_len && off + i < eend; i++) {
ch.ec_point_formats.push_back(buf[off + i]);
}
}
Comment thread src/scan_tls.cpp
Comment on lines +333 to +340
uint16_t record_len = u16be(data + 3);
if ((size_t)(5 + record_len) > len) return;

uint8_t msg_type = data[5];
uint32_t msg_len = u24be(data + 6);
size_t body_off = 9;

if (body_off + msg_len > len) return;
Comment thread src/scan_tls.cpp
Comment on lines +273 to +280
time_t now = time(nullptr);
char ts[32];
struct tm *gmt = gmtime(&now);
strftime(ts, sizeof(ts), "%Y-%m-%dT%H:%M:%SZ", gmt);

std::lock_guard<std::mutex> lock(tls_output_mutex);
std::ofstream f(outpath, std::ios::app);
if (!f) return;
Comment thread src/scan_tls.cpp
Comment on lines +282 to +297
f << '{';
f << "\"flow\":\"" << flow_key << "\",";
f << "\"timestamp\":\"" << ts << "\"";

if (ch) {
f << ",\"sni\":\"" << ch->sni << "\"";
f << ",\"tls_version\":\"" << ch->tls_version << "\"";
f << ",\"ja3\":\"" << ch->ja3 << "\"";
f << ",\"ja3_hash\":\"" << ch->ja3_hash << "\"";
f << ",\"cipher_suites_offered\":" << ch->cipher_suites.size();
}

if (sh) {
f << ",\"negotiated_version\":\"" << sh->negotiated_version << "\"";
f << ",\"selected_cipher\":\"" << sh->selected_cipher << "\"";
}
Comment thread src/scan_tls.cpp
Comment on lines +315 to +319
if (sp.phase == scanner_params::PHASE_STARTUP) {
sp.info->name = "tls";
sp.info->author = "tcpflow scan_tls";
sp.info->flags = scanner_info::SCANNER_DISABLED; // enable with -e tls
return;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants