All fingerprinters inherit from BaseFingerprinter and share a common interface.
| Method | Returns | Description |
|---|---|---|
process_packet(packet) |
str or None |
Process a scapy packet. Returns fingerprint string if one is generated. |
get_fingerprints() |
list[dict] |
Returns all collected fingerprints as {"fingerprint": str, ...} dicts. |
reset() |
None |
Clears all collected fingerprints and internal state. |
TLS Client Hello fingerprinting.
from ja4plus import JA4Fingerprinter
fp = JA4Fingerprinter()
result = fp.process_packet(packet) # Returns JA4 string or None
raw = fp.get_raw_fingerprint(packet) # Returns unhashed fingerprintTLS Server Hello fingerprinting.
from ja4plus import JA4SFingerprinter
fp = JA4SFingerprinter()
result = fp.process_packet(packet)HTTP request fingerprinting.
from ja4plus import JA4HFingerprinter
fp = JA4HFingerprinter()
result = fp.process_packet(packet)TCP client fingerprinting from SYN packets.
from ja4plus import JA4TFingerprinter
fp = JA4TFingerprinter()
result = fp.process_packet(packet)TCP server fingerprinting from SYN-ACK packets.
from ja4plus import JA4TSFingerprinter
fp = JA4TSFingerprinter()
result = fp.process_packet(packet)Network latency estimation from TCP handshake timing.
from ja4plus import JA4LFingerprinter
fp = JA4LFingerprinter()
result = fp.process_packet(packet)X.509 certificate structure fingerprinting.
from ja4plus import JA4XFingerprinter
fp = JA4XFingerprinter()
result = fp.fingerprint_certificate(der_bytes) # From DER-encoded cert
result = fp.process_packet(packet) # From TLS packet
details = fp.get_cert_details(x509_cert) # Extract OID detailsSSH session classification.
from ja4plus import JA4SSHFingerprinter
fp = JA4SSHFingerprinter(packet_count=200)
result = fp.process_packet(packet)
info = fp.interpret_fingerprint(result) # Session type analysis
hassh = fp.get_hassh_fingerprints() # HASSH fingerprints
lookup = fp.lookup_hassh(hassh_value) # Known HASSH lookupOne-shot fingerprinting without maintaining state:
from ja4plus import (
generate_ja4,
generate_ja4s,
generate_ja4h,
generate_ja4t,
generate_ja4ts,
generate_ja4l,
generate_ja4x,
generate_ja4ssh,
)
# Each takes a scapy packet and returns a fingerprint string or None
result = generate_ja4(packet)| Function | Input | Description |
|---|---|---|
generate_ja4(packet) |
scapy packet | JA4 TLS client fingerprint |
generate_ja4s(packet) |
scapy packet | JA4S TLS server fingerprint |
generate_ja4h(packet) |
scapy packet | JA4H HTTP fingerprint |
generate_ja4t(packet) |
scapy packet | JA4T TCP client fingerprint |
generate_ja4ts(packet) |
scapy packet | JA4TS TCP server fingerprint |
generate_ja4l(packet) |
scapy packet | JA4L latency fingerprint |
generate_ja4x(cert_info) |
dict | JA4X certificate fingerprint (takes cert_info dict) |
generate_ja4ssh(packet) |
scapy packet | JA4SSH session fingerprint |
| Function | Description |
|---|---|
extract_tls_info(packet) |
Extract TLS handshake details from a packet |
is_grease_value(value) |
Check if a value is a GREASE value |
parse_client_hello(data) |
Parse raw ClientHello bytes |
parse_server_hello(data) |
Parse raw ServerHello bytes |
| Function | Description |
|---|---|
extract_http_info(packet) |
Extract HTTP request details from a packet |
is_http_request(data) |
Check if data is an HTTP request |
parse_http_request(data) |
Parse raw HTTP request bytes |
| Function | Description |
|---|---|
is_ssh_packet(data) |
Check if data is SSH traffic |
parse_ssh_packet(data) |
Parse SSH packet structure |
extract_hassh(data) |
Extract HASSH fingerprint from KEXINIT |
| Function | Description |
|---|---|
oid_to_hex(oid_string) |
Convert OID dotted string to ASN.1 hex encoding |
get_cert_details(cert) |
Extract issuer/subject RDNs and extensions from an x509 certificate |
extract_certificate_from_bytes(data) |
Find DER certificates in raw TLS record bytes |
extract_certificate_info(packet) |
Extract certificate details from a scapy packet |
| Function | Description |
|---|---|
parse_quic_initial(udp_payload) |
Parse QUIC Initial, return tls_info with is_quic=True or None |
derive_initial_secrets(dcid, version) |
Derive secrets from DCID (version 1 or 2) |
extract_crypto_frames(plaintext) |
Reassemble CRYPTO frames from decrypted payload |
| Class/Function | Description |
|---|---|
TCPStreamReassembler(max_streams, max_stream_bytes) |
Sequence-aware TCP stream reassembly |
.add_segment(key, seq, data) |
Add a TCP segment |
.get_stream(key) |
Get reassembled contiguous bytes |
.remove_stream(key) |
Remove a tracked stream |
| Function | Description |
|---|---|
get_ip_layer(packet) |
Return IP or IPv6 layer, or None |
get_ttl(packet) |
Return TTL (IPv4) or Hop Limit (IPv6), or None |
Command-line interface for JA4+ fingerprinting. Installed as the ja4plus command.
ja4plus analyze <pcap_file> # Fingerprint a PCAP file
ja4plus live <interface> # Live capture (requires root)
ja4plus cert <cert_file> # Fingerprint an X.509 certificate| Option | Description |
|---|---|
--format table|json|csv |
Output format (default: table) |
--types ja4,ja4s,... |
Filter to specific fingerprint types |
--lookup |
Identify fingerprints using bundled ja4db database |
--version |
Print version |
Fingerprint identification using FoxIO's ja4plus-mapping.csv database.
from ja4plus.ja4db import JA4DBClient, lookup
# Module-level convenience function
result = lookup("t13d1516h2_8daaf6152771_02713d6af862")
# {"application": "Chromium Browser", "type": "ja4", "notes": ""}
# Or use the client for caching across multiple lookups
client = JA4DBClient()
result = client.lookup(fingerprint_string)| Class/Function | Description |
|---|---|
JA4DBClient() |
Client with local cache and bundled database |
JA4DBClient.lookup(fingerprint) |
Look up a fingerprint, returns dict or None |
lookup(fingerprint) |
Module-level convenience using a shared client |