1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00

Parse sniffer keylog file line by line for RFC 9850

The keylog reader used fscanf with three whitespace delimited tokens,
which is not line aware. RFC 9850 Section 1 requires readers to ignore
empty lines and lines whose first character is '#', and Section 2
recommends ignoring lines that do not conform to the format so secrets
can still be recovered from corrupted files. A comment line such as
"# note" was parsed as three fields spanning the line boundary, which
desynchronized the rest of the file.

Read one line at a time with fgets, skip empty and comment lines, and
parse each line with sscanf, skipping any line that does not yield the
three expected fields instead of aborting. Clear the field buffers each
iteration so a short field cannot pick up stale bytes from a previous
line, validate that the fixed length client random field is the exact
expected length, and leave the secret length variable because it depends
on the negotiated hash. Zero the new line buffer on every return path,
matching the existing ForceZero handling of the secret buffers.

Add a comment line, a blank line, and a malformed line to the TLS 1.3
keylog test data so the sniffer keylog test exercises the skip paths.
This commit is contained in:
Tobias Frauenschläger
2026-07-16 17:21:16 +02:00
parent bfdecbc644
commit 34a2a2f389
2 changed files with 39 additions and 5 deletions

View File

@@ -1,3 +1,7 @@
# RFC 9850: reader must ignore comment lines, blank lines, and malformed
# lines such as the INCOMPLETE_KEYLOG_LINE entry below
INCOMPLETE_KEYLOG_LINE deadbeef
CLIENT_HANDSHAKE_TRAFFIC_SECRET b28ffa242be7ecc3ef669ea686dc842c0de9efe938a0f047c28cb359710991f8 cfe4b2e9ef157938936ed15627e65338e6b5f5b251b1718e28b028eabecf3633
CLIENT_HANDSHAKE_TRAFFIC_SECRET b28ffa242be7ecc3ef669ea686dc842c0de9efe938a0f047c28cb359710991f8 cfe4b2e9ef157938936ed15627e65338e6b5f5b251b1718e28b028eabecf3633
SERVER_HANDSHAKE_TRAFFIC_SECRET b28ffa242be7ecc3ef669ea686dc842c0de9efe938a0f047c28cb359710991f8 737fd782b2e50c4982011f0ec7249d3f91b7e9e25529631c4794cfd4a83ceaf1

View File

@@ -7582,7 +7582,10 @@ static int parseKeyLogFile(const char* fileName, char* error)
/* 2 chars for Hexadecimal representation, plus null terminator */
char clientRandomHex[2 * CLIENT_RANDOM_LENGTH + 1] = {0};
char secretHex[2 * SECRET_LENGTH + 1] = {0};
/* One keylog record. RFC 9850 requires line based parsing so that comment
* and malformed lines cannot desynchronize the fields. Sized to hold the
* longest valid line plus separators and terminator. */
char line[256];
file = fopen(fileName, "r");
if (file == NULL) {
@@ -7591,10 +7594,27 @@ static int parseKeyLogFile(const char* fileName, char* error)
return WOLFSSL_SNIFFER_ERROR;
}
/* Format specifiers for each column should be:
* MAX_PREFIX_LENGTH, 2*CLIENT_RANDOM_LENGTH, and 2*SECRET_LENGTH */
while (fscanf(file, "%31s %64s %96s", prefix, clientRandomHex, secretHex)
== 3) {
while (fgets(line, (int)sizeof(line), file) != NULL) {
/* RFC 9850 Section 1: ignore empty lines and lines whose first
* character is the octothorpe ('#') comment marker. */
if (line[0] == '#' || line[0] == '\n' || line[0] == '\r' ||
line[0] == '\0') {
continue;
}
/* Clear the field buffers so that a short field on one line cannot
* pick up stale bytes left by a previous line. */
XMEMSET(prefix, 0, sizeof(prefix));
XMEMSET(clientRandomHex, 0, sizeof(clientRandomHex));
XMEMSET(secretHex, 0, sizeof(secretHex));
/* RFC 9850 Section 2: silently ignore lines that do not conform to the
* "label SP random SP secret" format so that secrets can still be
* recovered from a corrupted file. */
if (sscanf(line, "%31s %64s %96s", prefix, clientRandomHex, secretHex)
!= 3) {
continue;
}
if (XSTRCMP(prefix, "CLIENT_RANDOM") == 0) {
type = SNIFFER_SECRET_TLS12_MASTER_SECRET;
@@ -7621,6 +7641,14 @@ static int parseKeyLogFile(const char* fileName, char* error)
continue;
}
/* The client random is always CLIENT_RANDOM_LENGTH bytes. Reject a
* line whose random field is not the exact expected length. The secret
* length is left variable because it depends on the negotiated hash. */
if (XSTRLEN(clientRandomHex) != (size_t)(2 * CLIENT_RANDOM_LENGTH)) {
fprintf(stderr, "malformed client random for prefix: %s\n", prefix);
continue;
}
hexToBin(clientRandomHex, clientRandom, CLIENT_RANDOM_LENGTH);
hexToBin(secretHex, secret, SECRET_LENGTH);
ret = addSecretNode(clientRandom, type, secret, error);
@@ -7629,6 +7657,7 @@ static int parseKeyLogFile(const char* fileName, char* error)
fclose(file);
ForceZero(secret, SECRET_LENGTH);
ForceZero(secretHex, sizeof(secretHex));
ForceZero(line, sizeof(line));
return ret;
}
}
@@ -7636,6 +7665,7 @@ static int parseKeyLogFile(const char* fileName, char* error)
ForceZero(secret, SECRET_LENGTH);
ForceZero(secretHex, sizeof(secretHex));
ForceZero(line, sizeof(line));
return 0;
}