1
1
mirror of https://github.com/wader/fq.git synced 2024-12-29 00:22:38 +03:00
fq/format/tls/keylog/keylog.go
Mattias Wadman 9852f56b74 tls: Add TLS 1.0, 1.1, 1.2 decode and decryption
What it can do:
- Decodes records and most standard messages and extensions.
- Decryptes records and reassemples application data stream if a keylog is provided
  and the cipher suite is supported.
- Supports most recommended and used ciphers and a bunch of older ones.

What it can't do:
- SSL v3 maybe supported, is similar to TLS 1.0, not tested.
- Decryption and renegotiation/cipher change.
- Record defragmentation not supported, seems rare over TCP.
- TLS 1.3
- SSL v2 but v2 compat header is supported.
- Some key exchange messages not decoded yet

Decryption code is heavly based on golang crypto/tls and zmap/zcrypto.

Will be base for decoding http2 and other TLS based on protocols.

Fixes #587
2023-03-05 13:52:12 +01:00

102 lines
2.5 KiB
Go

// Package keylog parses NSS key log format
// https://firefox-source-docs.mozilla.org/security/nss/legacy/key_log_format/index.html
// <Label> <space> <ClientRandom> <space> <Secret> lines
package keylog
import (
"bufio"
"encoding/hex"
"fmt"
"strings"
)
const (
Rsa = iota
ClientRandom
ClientEarlyTrafficSecret
ClientHandshakeTrafficSecret
ServerHandshakeTrafficSecret
ClientTrafficSecret0
ServerTrafficSecret0
EarlyExporterSecret
ExporterSecret
)
var labelToEnum = map[string]int{
"RSA": Rsa,
"CLIENT_RANDOM": ClientRandom,
"CLIENT_EARLY_TRAFFIC_SECRET": ClientEarlyTrafficSecret,
"CLIENT_HANDSHAKE_TRAFFIC_SECRET": ClientHandshakeTrafficSecret,
"SERVER_HANDSHAKE_TRAFFIC_SECRET": ServerHandshakeTrafficSecret,
"CLIENT_TRAFFIC_SECRET_0": ClientTrafficSecret0,
"SERVER_TRAFFIC_SECRET_0": ServerTrafficSecret0,
"EARLY_EXPORTER_SECRET": EarlyExporterSecret,
"EXPORTER_SECRET": ExporterSecret,
}
type Entry struct {
Label int
ClientRandom [32]byte
}
type Map map[Entry][]byte
func (m Map) Lookup(label int, clientRandom [32]byte) ([]byte, bool) {
bs, ok := m[Entry{Label: label, ClientRandom: clientRandom}]
return bs, ok
}
// Parse NSS Key Log format
//
// # comment
// <Label> <space> <ClientRandom> <space> <Secret>
func Parse(s string) (Map, error) {
em := map[Entry][]byte{}
lines := bufio.NewScanner(strings.NewReader(s))
lineNr := 0
for lines.Scan() {
lineNr++
line := strings.TrimSpace(lines.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.Split(line, " ")
if len(parts) != 3 {
continue
// return nil, fmt.Errorf("parse error %d: %s", lineNr, line)
}
label, labelOk := labelToEnum[parts[0]]
if !labelOk {
// return nil, fmt.Errorf("unknown label %d: %s", lineNr, parts[0])
continue
}
clientRandom, clientRandomErr := hex.DecodeString(parts[1])
if clientRandomErr != nil {
return nil, fmt.Errorf("client random error %d: %w", lineNr, clientRandomErr)
}
if len(clientRandom) != 32 {
return nil, fmt.Errorf("client random not 32 bytes%d: %s (%d)", lineNr, clientRandom, len(clientRandom))
}
value, valueErr := hex.DecodeString(parts[2])
if valueErr != nil {
return nil, fmt.Errorf("value random error %d: %w", lineNr, valueErr)
}
e := Entry{Label: label}
copy(e.ClientRandom[:], clientRandom)
if _, ok := em[e]; ok {
return nil, fmt.Errorf("duplicate client random %d: %s", lineNr, line)
}
em[e] = value
}
return em, nil
}