Skip to content

Implement TlsPacket #256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pcap4j-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
module org.pcap4j.core {
exports org.pcap4j.core;
exports org.pcap4j.packet;

exports org.pcap4j.packet.tls.records;
exports org.pcap4j.packet.tls.records.handshakes;
exports org.pcap4j.packet.tls.extensions;
exports org.pcap4j.packet.tls.extensions.keyshare;
exports org.pcap4j.packet.tls.keys.enums;
exports org.pcap4j.packet.namednumber.tls;

exports org.pcap4j.packet.factory;
exports org.pcap4j.packet.namednumber;
exports org.pcap4j.packet.constant;
Expand Down
216 changes: 216 additions & 0 deletions pcap4j-core/src/main/java/org/pcap4j/packet/TlsPacket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package org.pcap4j.packet;

import org.pcap4j.packet.namednumber.tls.ContentType;
import org.pcap4j.packet.namednumber.tls.TlsVersion;
import org.pcap4j.packet.tls.records.*;
import org.pcap4j.util.ByteArrays;

import java.util.ArrayList;
import java.util.List;

import static org.pcap4j.util.ByteArrays.BYTE_SIZE_IN_BYTES;
import static org.pcap4j.util.ByteArrays.SHORT_SIZE_IN_BYTES;

public class TlsPacket extends AbstractPacket {

private final TlsPacket.TlsHeader header;
private final Packet payload;

public static TlsPacket newPacket(byte[] rawData, int offset, int length) throws IllegalRawDataException {
ByteArrays.validateBounds(rawData, offset, length);
return new TlsPacket(rawData, offset, length);
}

private TlsPacket(byte[] rawData, int offset, int length) throws IllegalRawDataException {
this.header = new TlsPacket.TlsHeader(rawData, offset, length);

int payloadLength = length - header.length();
if (payloadLength > 0) {
this.payload = TlsPacket.newPacket(rawData, offset + header.length(), payloadLength);
} else {
this.payload = null;
}
}

private TlsPacket(TlsPacket.Builder builder) {
if (builder == null) {
throw new NullPointerException("builder: null");
}

this.payload = builder.payloadBuilder != null ? builder.payloadBuilder.build() : null;
this.header = new TlsPacket.TlsHeader(builder);
}

@Override
public TlsHeader getHeader() {
return header;
}

@Override
public Packet getPayload() {
return payload;
}

@Override
public Builder getBuilder() {
return new Builder(this);
}

@Override
protected String buildString() {
StringBuilder sb = new StringBuilder(getHeader().toString());

TlsPacket p = (TlsPacket) getPayload();

if (p != null) {
sb.append('\n');
sb.append(p.toString());
}

return sb.toString();
}

public static final class TlsHeader extends AbstractHeader {

/*
0x0 - Content Type
0x1 - Version
0x3 - Length
0x5 - Record content
*/

private static final int CONTENT_TYPE_OFFSET = 0;
private static final int VERSION_OFFSET = CONTENT_TYPE_OFFSET + BYTE_SIZE_IN_BYTES;
private static final int LENGTH_OFFSET = VERSION_OFFSET + SHORT_SIZE_IN_BYTES;
private static final int RECORD_OFFSET = LENGTH_OFFSET + SHORT_SIZE_IN_BYTES;

private final ContentType contentType;
private final TlsVersion version;
private final short recordLength;
private final TlsRecord record;

private TlsHeader(Builder builder) {
this.contentType = builder.contentType;
this.version = builder.version;
this.recordLength = builder.recordLength;
this.record = builder.record;
}

private TlsHeader(byte[] rawData, int offset, int length) {
ByteArrays.validateBounds(rawData, offset, RECORD_OFFSET);
this.contentType = ContentType.getInstance(ByteArrays.getByte(rawData, CONTENT_TYPE_OFFSET + offset));
this.version = TlsVersion.getInstance(ByteArrays.getShort(rawData, VERSION_OFFSET + offset));
this.recordLength = ByteArrays.getShort(rawData, LENGTH_OFFSET + offset);

if (contentType == ContentType.HANDSHAKE) {
this.record = HandshakeRecord.newInstance(rawData, offset + RECORD_OFFSET, recordLength);
} else if (contentType == ContentType.CHANGE_CIPHER_SPEC) {
this.record = ChangeCipherSpecRecord.newInstance(rawData, offset + RECORD_OFFSET, recordLength);
} else if (contentType == ContentType.APPLICATION_DATA) {
this.record = ApplicationDataRecord.newInstance(rawData, offset + RECORD_OFFSET, recordLength);
} else if (contentType == ContentType.ALERT) {
this.record = AlertRecord.newInstance(rawData, offset + RECORD_OFFSET, recordLength);
} else if (contentType == ContentType.HEARTBEAT) {
this.record = HeartbeatRecord.newInstance(rawData, offset + RECORD_OFFSET, recordLength);
} else {
throw new IllegalArgumentException("Unknown content type: " + contentType);
}
}

public ContentType getContentType() {
return contentType;
}

public TlsVersion getVersion() {
return version;
}

public short getRecordLength() {
return recordLength;
}

public TlsRecord getRecord() {
return record;
}

@Override
protected List<byte[]> getRawFields() {
List<byte[]> rawFields = new ArrayList<byte[]>();
rawFields.add(new byte[]{contentType.value()});
rawFields.add(ByteArrays.toByteArray(version.value()));
rawFields.add(ByteArrays.toByteArray(recordLength));
rawFields.add(record.toByteArray());
return rawFields;
}

@Override
protected int calcLength() {
return RECORD_OFFSET + recordLength;
}

@Override
protected String buildString() {
return "[TLS Header (" + length() + " bytes)]\n" +
" Version: " + version + "\n" +
" Type: " + contentType + "\n" +
record.toString();
}
}

public static final class Builder extends AbstractBuilder {

private ContentType contentType;
private TlsVersion version;
private short recordLength;
private TlsRecord record;

private Packet.Builder payloadBuilder;

public Builder() {
}

public Builder(TlsPacket packet) {
this.payloadBuilder = packet.payload != null ? packet.payload.getBuilder() : null;
this.contentType = packet.header.contentType;
this.version = packet.header.version;
this.recordLength = packet.header.recordLength;
this.record = packet.header.record;
}

public Builder contentType(ContentType contentType) {
this.contentType = contentType;
return this;
}

public Builder version(TlsVersion version) {
this.version = version;
return this;
}

public Builder recordLength(short recordLength) {
this.recordLength = recordLength;
return this;
}

public Builder record(TlsRecord record) {
this.record = record;
return this;
}

@Override
public TlsPacket.Builder payloadBuilder(Packet.Builder payloadBuilder) {
this.payloadBuilder = payloadBuilder;
return this;
}

@Override
public Packet.Builder getPayloadBuilder() {
return payloadBuilder;
}

@Override
public TlsPacket build() {
return new TlsPacket(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.pcap4j.packet.namednumber.tls;

import org.pcap4j.packet.namednumber.NamedNumber;

import java.util.HashMap;
import java.util.Map;

public class AlertDescription extends NamedNumber<Byte, AlertDescription> {

private static final Map<Byte, AlertDescription> registry = new HashMap<Byte, AlertDescription>();

// https://techcommunity.microsoft.com/t5/iis-support-blog/ssl-tls-alert-protocol-and-the-alert-codes/ba-p/377132

public static AlertDescription close_notify = new AlertDescription((byte) 0, "close_notify");
public static AlertDescription unexpected_message = new AlertDescription((byte) 10, "unexpected_message");
public static AlertDescription bad_record_mac = new AlertDescription((byte) 20, "bad_record_mac");
public static AlertDescription decryption_failed_RESERVED = new AlertDescription((byte) 21, "decryption_failed_RESERVED");
public static AlertDescription record_overflow = new AlertDescription((byte) 22, "record_overflow");
public static AlertDescription decompression_failure_RESERVED = new AlertDescription((byte) 30, "decompression_failure_RESERVED");
public static AlertDescription handshake_failure = new AlertDescription((byte) 40, "handshake_failure");
public static AlertDescription no_certificate_RESERVED = new AlertDescription((byte) 41, "no_certificate_RESERVED");
public static AlertDescription bad_certificate = new AlertDescription((byte) 42, "bad_certificate");
public static AlertDescription unsupported_certificate = new AlertDescription((byte) 43, "unsupported_certificate");
public static AlertDescription certificate_revoked = new AlertDescription((byte) 44, "certificate_revoked");
public static AlertDescription certificate_expired = new AlertDescription((byte) 45, "certificate_expired");
public static AlertDescription certificate_unknown = new AlertDescription((byte) 46, "certificate_unknown");
public static AlertDescription illegal_parameter = new AlertDescription((byte) 47, "illegal_parameter");
public static AlertDescription unknown_ca = new AlertDescription((byte) 48, "unknown_ca");
public static AlertDescription access_denied = new AlertDescription((byte) 49, "access_denied");
public static AlertDescription decode_error = new AlertDescription((byte) 50, "decode_error");
public static AlertDescription decrypt_error = new AlertDescription((byte) 51, "decrypt_error");
public static AlertDescription export_restriction_RESERVED = new AlertDescription((byte) 60, "export_restriction_RESERVED");
public static AlertDescription protocol_version = new AlertDescription((byte) 70, "protocol_version");
public static AlertDescription insufficient_security = new AlertDescription((byte) 71, "insufficient_security");
public static AlertDescription internal_error = new AlertDescription((byte) 80, "internal_error");
public static AlertDescription inappropriate_fallback = new AlertDescription((byte) 86, "inappropriate_fallback");
public static AlertDescription user_canceled = new AlertDescription((byte) 90, "user_canceled");
public static AlertDescription no_renegotiation_RESERVED = new AlertDescription((byte) 100, "no_renegotiation_RESERVED");
public static AlertDescription missing_extension = new AlertDescription((byte) 109, "missing_extension");
public static AlertDescription unsupported_extension = new AlertDescription((byte) 110, "unsupported_extension");
public static AlertDescription certificate_unobtainable_RESERVED = new AlertDescription((byte) 111, "certificate_unobtainable_RESERVED");
public static AlertDescription unrecognized_name = new AlertDescription((byte) 112, "unrecognized_name");
public static AlertDescription bad_certificate_status_response = new AlertDescription((byte) 113, "bad_certificate_status_response");
public static AlertDescription bad_certificate_hash_value_RESERVED = new AlertDescription((byte) 114, "bad_certificate_hash_value_RESERVED");
public static AlertDescription unknown_psk_identity = new AlertDescription((byte) 115, "unknown_psk_identity");
public static AlertDescription certificate_required = new AlertDescription((byte) 116, "certificate_required");
public static AlertDescription no_application_protocol = new AlertDescription((byte) 120, "no_application_protocol");

public AlertDescription(Byte value, String name) {
super(value, name);
registry.put(value, this);
}

public static AlertDescription getInstance(Byte value) {
if (registry.containsKey(value)) {
return registry.get(value);
} else {
throw new IllegalArgumentException("Unknown alert description: " + value);
}
}

@Override
public int compareTo(AlertDescription o) {
return value().compareTo(o.value());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.pcap4j.packet.namednumber.tls;

import org.pcap4j.packet.namednumber.NamedNumber;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("unused")
public class AlertLevel extends NamedNumber<Byte, AlertLevel> {

private static final Map<Byte, AlertLevel> registry = new HashMap<Byte, AlertLevel>();

public static final AlertLevel WARNING = new AlertLevel((byte) 1, "warning");
public static final AlertLevel FATAL = new AlertLevel((byte) 2, "fatal");

public static final AlertLevel ENCRYPTED_ALERT = new AlertLevel((byte) 0, "encrypted alert");

public AlertLevel(Byte value, String name) {
super(value, name);
registry.put(value, this);
}

public static AlertLevel getInstance(Byte value) {
if (registry.containsKey(value)) {
return registry.get(value);
} else {
return ENCRYPTED_ALERT;
}
}

@Override
public int compareTo(AlertLevel o) {
return value().compareTo(o.value());
}

}
Loading