test: improve coverage across package

This commit is contained in:
2026-05-01 14:09:10 +05:30
parent e1c68ec7d0
commit e3f1f5046a
18 changed files with 2652 additions and 6 deletions

View File

@@ -0,0 +1,319 @@
package internal
import (
"reflect"
"testing"
"git.difuse.io/Difuse/Mellaris/analyzer/utils"
)
func buildClientHelloMsg(t *testing.T) *utils.ByteBuffer {
t.Helper()
// Bytes taken from the standard TLS test vector, starting after the record
// header (5 bytes) and handshake header (4 bytes).
body := []byte{
0x03, 0x03, // version TLS 1.2
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // random
0x00, // session ID length = 0
0x00, 0x20, // cipher suites length = 32 bytes = 16 suites
0xcc, 0xa8, 0xcc, 0xa9, 0xc0, 0x2f, 0xc0, 0x30,
0xc0, 0x2b, 0xc0, 0x2c, 0xc0, 0x13, 0xc0, 0x09,
0xc0, 0x14, 0xc0, 0x0a, 0x00, 0x9c, 0x00, 0x9d,
0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, // ciphers
0x01, // compression methods length = 1
0x00, // compression method = null
0x00, 0x58, // extensions length = 88
// extension: server_name
0x00, 0x00, // type = server_name
0x00, 0x18, // length = 24
0x00, 0x16, // server name list length = 22
0x00, // name type = hostname
0x00, 0x13, // name length = 19
'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'u', 'l', 'f', 'h', 'e', 'i', 'm', '.', 'n', 'e', 't',
// extension: status_request
0x00, 0x05, // type = status_request
0x00, 0x05, // length = 5
0x01, 0x00, 0x00, 0x00, 0x00,
// extension: supported_groups
0x00, 0x0a, // type = supported_groups
0x00, 0x0a, // length = 10
0x00, 0x08, // list length = 8
0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19,
// extension: ec_point_formats
0x00, 0x0b, // type = ec_point_formats
0x00, 0x02, // length = 2
0x01, 0x00,
// extension: signature_algorithms
0x00, 0x0d, // type = signature_algorithms
0x00, 0x12, // length = 18
0x00, 0x10, // list length = 16
0x04, 0x01, 0x04, 0x03, 0x05, 0x01, 0x05, 0x03,
0x06, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02, 0x03,
// extension: renegotiation_info
0xff, 0x01, // type = renegotiation_info
0x00, 0x01, // length = 1
0x00,
// extension: extended_master_secret (empty)
0x00, 0x17, // type = extended_master_secret
0x00, 0x00, // length = 0
// extension: session_ticket (empty)
0x00, 0x23, // type = session_ticket
0x00, 0x00, // length = 0
}
return &utils.ByteBuffer{Buf: body}
}
func TestParseTLSClientHelloMsgData(t *testing.T) {
chBuf := buildClientHelloMsg(t)
m := ParseTLSClientHelloMsgData(chBuf)
if m == nil {
t.Fatal("ParseTLSClientHelloMsgData returned nil")
}
wantVersion := uint16(0x0303)
if v, ok := m["version"].(uint16); !ok || v != wantVersion {
t.Errorf("version = %v, want %v", m["version"], wantVersion)
}
wantCiphers := []uint16{52392, 52393, 49199, 49200, 49195, 49196, 49171, 49161, 49172, 49162, 156, 157, 47, 53, 49170, 10}
if c, ok := m["ciphers"].([]uint16); ok {
if !reflect.DeepEqual(c, wantCiphers) {
t.Errorf("ciphers = %v, want %v", c, wantCiphers)
}
} else {
t.Errorf("ciphers missing or wrong type: %T", m["ciphers"])
}
if sni, ok := m["sni"].(string); !ok || sni != "example.ulfheim.net" {
t.Errorf("sni = %q, want %q", m["sni"], "example.ulfheim.net")
}
if _, ok := m["compression"]; !ok {
t.Error("compression key missing")
}
if _, ok := m["session"]; !ok {
t.Error("session key missing")
}
if _, ok := m["random"]; !ok {
t.Error("random key missing")
}
}
func TestParseTLSServerHelloMsgData(t *testing.T) {
// ServerHello message body (after record header + handshake header)
body := []byte{
0x03, 0x03, // version TLS 1.2
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // random
0x00, // session ID length = 0
0xc0, 0x13, // cipher suite
0x00, // compression method
0x00, 0x05, // extensions length = 5
// extension: renegotiation_info
0xff, 0x01, // type
0x00, 0x01, // length = 1
0x00,
}
m := ParseTLSServerHelloMsgData(&utils.ByteBuffer{Buf: body})
if m == nil {
t.Fatal("ParseTLSServerHelloMsgData returned nil")
}
wantCipher := uint16(0xc013)
if c, ok := m["cipher"].(uint16); !ok || c != wantCipher {
t.Errorf("cipher = %v, want %v", m["cipher"], wantCipher)
}
wantCompression := uint8(0)
if c, ok := m["compression"].(uint8); !ok || c != wantCompression {
t.Errorf("compression = %v, want %v", m["compression"], wantCompression)
}
}
func TestParseTLSServerHelloMsgData_NoExtensions(t *testing.T) {
// ServerHello message without extensions
body := []byte{
0x03, 0x03, // version
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // random
0x00, // session ID length
0x00, 0xff, // cipher suite
0x00, // compression method
}
m := ParseTLSServerHelloMsgData(&utils.ByteBuffer{Buf: body})
if m == nil {
t.Fatal("ParseTLSServerHelloMsgData returned nil for no-extensions case")
}
if _, ok := m["cipher"]; !ok {
t.Error("cipher key missing")
}
}
func TestParseTLSClientHelloMsgData_Truncated(t *testing.T) {
tests := []struct {
name string
buf []byte
}{
{"too short for session id", []byte{0x03, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}},
{"odd cipher suites length", []byte{
0x03, 0x03, // version
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // random
0x00, // session ID length
0x00, 0x03, // odd cipher suites length
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := ParseTLSClientHelloMsgData(&utils.ByteBuffer{Buf: tt.buf})
if m != nil {
t.Error("expected nil for truncated input")
}
})
}
}
func TestParseTLSClientHelloMsgData_ECH(t *testing.T) {
// ClientHello with ECH extension
body := []byte{
0x03, 0x03, // version
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // random
0x00, // session ID length
0x00, 0x02, // cipher suites length = 2
0x13, 0x01, // TLS_AES_128_GCM_SHA256
0x01, // compression methods length = 1
0x00, // null compression
0x00, 0x07, // extensions length = 7
// ECH extension
0xfe, 0x0d, // type = encrypted_client_hello
0x00, 0x03, // length = 3
0x01, 0x02, 0x03, // some ECH data
}
m := ParseTLSClientHelloMsgData(&utils.ByteBuffer{Buf: body})
if m == nil {
t.Fatal("ParseTLSClientHelloMsgData returned nil")
}
ech, ok := m["ech"].(bool)
if !ok || !ech {
t.Errorf("ech = %v, want true", m["ech"])
}
}
func TestParseTLSClientHelloMsgData_ALPN(t *testing.T) {
// ClientHello with ALPN extension
body := []byte{
0x03, 0x03, // version
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // random
0x00, // session ID length
0x00, 0x02, // cipher suites length = 2
0x13, 0x01, // cipher suite
0x01, // compression methods length = 1
0x00, // compression method
0x00, 0x12, // extensions length = 18
// ALPN extension
0x00, 0x10, // type = ALPN
0x00, 0x0e, // length = 14
0x00, 0x0c, // list length = 12
0x08, 'h', 't', 't', 'p', '/', '1', '.', '1',
0x02, 'h', '2',
}
m := ParseTLSClientHelloMsgData(&utils.ByteBuffer{Buf: body})
if m == nil {
t.Fatal("ParseTLSClientHelloMsgData returned nil")
}
alpn, ok := m["alpn"].([]string)
if !ok {
t.Fatalf("alpn missing or wrong type: %T", m["alpn"])
}
if len(alpn) != 2 || alpn[0] != "http/1.1" || alpn[1] != "h2" {
t.Errorf("alpn = %v, want [http/1.1 h2]", alpn)
}
}
func TestParseTLSClientHelloMsgData_SupportedVersionsClient(t *testing.T) {
// ClientHello with supported_versions extension (client format - list)
body := []byte{
0x03, 0x03, // version
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // random
0x00, // session ID length
0x00, 0x02, // cipher suites length = 2
0x13, 0x01, // cipher suite
0x01, // compression methods length = 1
0x00, // compression method
0x00, 0x0b, // extensions length = 11
// supported_versions (client list format)
0x00, 0x2b, // type = supported_versions
0x00, 0x07, // length = 7
0x06, // list length = 6
0x03, 0x04, // TLS 1.3
0x03, 0x03, // TLS 1.2
0x03, 0x01, // TLS 1.0
}
m := ParseTLSClientHelloMsgData(&utils.ByteBuffer{Buf: body})
if m == nil {
t.Fatal("ParseTLSClientHelloMsgData returned nil")
}
versions, ok := m["supported_versions"].([]uint16)
if !ok {
t.Fatalf("supported_versions missing or wrong type: %T", m["supported_versions"])
}
want := []uint16{0x0304, 0x0303, 0x0301}
if !reflect.DeepEqual(versions, want) {
t.Errorf("supported_versions = %v, want %v", versions, want)
}
}
func TestParseTLSServerHelloMsgData_SupportedVersionsServer(t *testing.T) {
// ServerHello with supported_versions extension (server format - single value)
body := []byte{
0x03, 0x03, // version
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // random
0x00, // session ID length
0x13, 0x01, // cipher suite
0x00, // compression method
0x00, 0x06, // extensions length = 6
// supported_versions (server format - single value)
0x00, 0x2b, // type
0x00, 0x02, // length = 2
0x03, 0x04, // TLS 1.3
}
m := ParseTLSServerHelloMsgData(&utils.ByteBuffer{Buf: body})
if m == nil {
t.Fatal("ParseTLSServerHelloMsgData returned nil")
}
v, ok := m["supported_versions"].(uint16)
if !ok {
t.Fatalf("supported_versions missing or wrong type: %T", m["supported_versions"])
}
if v != 0x0304 {
t.Errorf("supported_versions = 0x%04x, want 0x0304", v)
}
}