Skip to content

Commit

Permalink
feat(fsthttp): TLSInfo.JA3MD5 reports the client JA3 signature
Browse files Browse the repository at this point in the history
  • Loading branch information
cee-dub committed Jan 25, 2025
1 parent 4dca515 commit ec5ec9b
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 2 deletions.
6 changes: 5 additions & 1 deletion _examples/print-request/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ func main() {
fmt.Fprintf(w, "ProtoMajor: %d\n", r.ProtoMajor)
fmt.Fprintf(w, "ProtoMinor: %d\n", r.ProtoMinor)
fmt.Fprintf(w, "RemoteAddr: %q\n", r.RemoteAddr)
fmt.Fprintf(w, "TLSInfo: %#v\n", r.TLSInfo)
fmt.Fprintf(w, "TLSInfo:\n")
fmt.Fprintf(w, " Protocol: %s\n", r.TLSInfo.Protocol)
fmt.Fprintf(w, " CipherOpenSSLName: %s\n", r.TLSInfo.CipherOpenSSLName)
fmt.Fprintf(w, " JA3MD5: %#x\n", r.TLSInfo.JA3MD5)
fmt.Fprintf(w, " ClientHello: %#x\n", r.TLSInfo.ClientHello)

fmt.Fprintf(w, "\n")

Expand Down
11 changes: 10 additions & 1 deletion fsthttp/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func NewRequest(method string, uri string, body io.Reader) (*Request, error) {
}

// _parseRequestURI can be set by SetParseRequestURI
var _parseRequestURI func(string)(*url.URL, error) = url.ParseRequestURI
var _parseRequestURI func(string) (*url.URL, error) = url.ParseRequestURI

func newClientRequest() (*Request, error) {
abiReq, abiReqBody, err := fastly.BodyDownstreamGet()
Expand Down Expand Up @@ -206,6 +206,11 @@ func newClientRequest() (*Request, error) {
if err != nil {
return nil, fmt.Errorf("get TLS cipher name: %w", err)
}

tlsInfo.JA3MD5, err = fastly.DownstreamTLSJA3MD5()
if err != nil {
return nil, fmt.Errorf("get TLS JA3 MD5: %w", err)
}
}

// Setting the fsthttp.Request Host field to the url.URL Host field is
Expand Down Expand Up @@ -559,6 +564,10 @@ type TLSInfo struct {
// connection. The value returned will be consistent with the OpenSSL name
// for the cipher suite.
CipherOpenSSLName string

// JA3MD5 contains the bytes of the JA3 signature of the client TLS request.
// See https://www.fastly.com/blog/the-state-of-tls-fingerprinting-whats-working-what-isnt-and-whats-next
JA3MD5 []byte
}

// DecompressResponseOptions control the auto decompress response behaviour.
Expand Down
4 changes: 4 additions & 0 deletions internal/abi/fastly/hostcalls_noguest.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ func DownstreamTLSClientHello() ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}

func DownstreamTLSJA3MD5() ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}

func NewHTTPRequest() (*HTTPRequest, error) {
return nil, fmt.Errorf("not implemented")
}
Expand Down
29 changes: 29 additions & 0 deletions internal/abi/fastly/http_guest.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,35 @@ func DownstreamTLSClientHello() ([]byte, error) {
}
}

// witx:
//
// (@interface func (export "downstream_tls_ja3_md5")
// ;; must be a 16-byte array
// (param $cja3_md5_out (@witx pointer (@witx char8)))
// (result $err (expected $num_bytes (error $fastly_status)))
// )
//
//go:wasmimport fastly_http_req downstream_tls_ja3_md5
//go:noescape
func fastlyHTTPReqDownstreamTLSJA3MD5(
cJA3MD5Out prim.Pointer[prim.Char8],
nwrittenOut prim.Pointer[prim.Usize],
) FastlyStatus

// DownstreamTLSJA3MD5 returns the MD5 []byte representing the JA3 signature of the singleton downstream request, if any.
func DownstreamTLSJA3MD5() ([]byte, error) {
var p [16]byte
buf := prim.NewWriteBufferFromBytes(p[:])
err := fastlyHTTPReqDownstreamTLSJA3MD5(
prim.ToPointer(buf.Char8Pointer()),
prim.ToPointer(buf.NPointer()),
).toError()
if err != nil {
return nil, err
}
return buf.AsBytes(), nil
}

// witx:
//
// (@interface func (export "new")
Expand Down

0 comments on commit ec5ec9b

Please sign in to comment.