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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user