Skip to content

Commit afff1f0

Browse files
hyperfinitismJuergenReppSIT
authored andcommitted
refactor(tpm2_getekcertificate): harden Intel EK cert parsing
Extract a reusable get_json_field() helper to look up named fields in JSON strings, and use it to replace fragile ad-hoc parsing: - Check for "pubhash" field presence instead of matching a raw prefix string ("{\"pubhash"), making detection resilient to whitespace and field ordering variations. - Replace base64_decode() with convert_base64url_to_base64(): extract the "certificate" field via get_json_field(), then perform Base64URL to Base64 conversion separately. No new external dependencies (e.g. json-c) are introduced. Signed-off-by: Takuma IMAMURA <209989118+hyperfinitism@users.noreply.github.com>
1 parent 2467d6f commit afff1f0

1 file changed

Lines changed: 89 additions & 30 deletions

File tree

tools/tpm2_getekcertificate.c

Lines changed: 89 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "tpm2_nv_util.h"
2222
#include "tpm2_tool.h"
2323
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
24-
#include <openssl/core_names.h>
24+
#include <openssl/core_names.h>
2525
#endif
2626

2727

@@ -1073,7 +1073,7 @@ static tool_rc get_nv_ek_certificate(ESYS_CONTEXT *ectx) {
10731073

10741074
tool_rc rc = tool_rc_success;
10751075

1076-
for (i = 0; i < ARRAY_LEN(ek_index_maps); i++) {
1076+
for (i = 0; i < ARRAY_LEN(ek_index_maps); i++) {
10771077
if (ek_index_maps[i].found) {
10781078
rc = nv_read(ectx, ek_index_maps[i].index);
10791079
if (rc != tool_rc_success) {
@@ -1165,33 +1165,89 @@ static tool_rc process_input(ESYS_CONTEXT *ectx) {
11651165
return print_intel_ek_certificate_warning();
11661166
}
11671167

1168-
static char *base64_decode(char **split, unsigned int cert_length) {
1168+
static const char *get_json_field(const char *cert_buffer, const char *field_name) {
11691169

1170-
*split += strlen("certficate\" : ");
1171-
char *final_string = NULL;
1172-
int outlen;
1173-
CURL *curl = curl_easy_init();
1174-
if (curl) {
1175-
char *output = curl_easy_unescape(curl, *split, cert_length, &outlen);
1176-
if (output) {
1177-
final_string = strdup(output);
1178-
curl_free(output);
1179-
}
1170+
const char *field_ptr = strstr(cert_buffer, field_name);
1171+
if (!field_ptr) {
1172+
return NULL;
11801173
}
1181-
curl_easy_cleanup(curl);
1182-
curl_global_cleanup();
1174+
field_ptr += strlen(field_name);
1175+
1176+
// Skip spaces after field name
1177+
while (field_ptr && *field_ptr == ' ')
1178+
++field_ptr;
1179+
1180+
// Expect colon after field name
1181+
if (*field_ptr != ':')
1182+
return NULL;
1183+
++field_ptr;
1184+
1185+
// Skip spaces after colon
1186+
while (field_ptr && *field_ptr == ' ')
1187+
++field_ptr;
1188+
1189+
// Expect double quote after colon
1190+
if (*field_ptr != '"')
1191+
return NULL;
1192+
++field_ptr;
1193+
1194+
return field_ptr;
1195+
}
11831196

1184-
if(final_string) {
1185-
size_t i;
1186-
for (i = 0; i < strlen(final_string); i++) {
1187-
final_string[i] = final_string[i] == '-' ? '+' : final_string[i];
1188-
final_string[i] = final_string[i] == '_' ? '/' : final_string[i];
1189-
final_string[i] = final_string[i] == '"' ? '\0' : final_string[i];
1190-
final_string[i] = final_string[i] == '}' ? '\0' : final_string[i];
1197+
static int hex_digit(unsigned char c) {
1198+
if (c >= '0' && c <= '9') return c - '0';
1199+
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
1200+
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
1201+
return -1;
1202+
}
1203+
1204+
static char *convert_base64url_to_base64(const char *s) {
1205+
1206+
size_t len = strlen(s);
1207+
char *out = malloc(len + 1);
1208+
if (!out) {
1209+
return NULL;
1210+
}
1211+
1212+
size_t i = 0;
1213+
size_t j = 0;
1214+
while (i < len) {
1215+
switch (s[i]) {
1216+
case '-':
1217+
out[j] = '+';
1218+
i++;
1219+
break;
1220+
case '_':
1221+
out[j] = '/';
1222+
i++;
1223+
break;
1224+
case '"':
1225+
case '}':
1226+
out[j] = '\0';
1227+
return out;
1228+
case '%':
1229+
if (i + 2 < len) {
1230+
int hi = hex_digit(s[i + 1]);
1231+
int lo = hex_digit(s[i + 2]);
1232+
if (hi >= 0 && lo >= 0) {
1233+
out[j] = (char)((hi << 4) | lo);
1234+
i += 3;
1235+
break;
1236+
}
1237+
}
1238+
out[j] = s[i];
1239+
i++;
1240+
break;
1241+
default:
1242+
out[j] = s[i];
1243+
i++;
1244+
break;
11911245
}
1246+
j++;
11921247
}
11931248

1194-
return final_string;
1249+
out[j] = '\0';
1250+
return out;
11951251
}
11961252

11971253
#define PEM_BEGIN_CERT_LINE "\n-----BEGIN CERTIFICATE-----\n"
@@ -1207,8 +1263,10 @@ static tool_rc process_output(void) {
12071263
bool is_intel_cert = ctx.manufacturer == VENDOR_INTEL;
12081264

12091265
if (!is_intel_cert && ctx.web_cert_buffer) {
1210-
is_intel_cert = !(strncmp((const char *)ctx.web_cert_buffer,
1211-
"{\"pubhash", strlen("{\"pubhash")));
1266+
/*
1267+
* Heuristics: If the cert contains the "pubhash" field, it is likely an Intel cert.
1268+
*/
1269+
is_intel_cert = (bool)get_json_field((const char *)ctx.web_cert_buffer, "\"pubhash\"");
12121270
}
12131271

12141272
/*
@@ -1223,12 +1281,12 @@ static tool_rc process_output(void) {
12231281
* Base 64: https://tools.ietf.org/html/rfc4648#section-5 to PEM
12241282
*/
12251283
if (ctx.web_cert_buffer && is_intel_cert && !ctx.is_cert_raw) {
1226-
char *split = strstr((char *)ctx.web_cert_buffer, "certificate");
1227-
if (!split) {
1284+
const char *cert_field = get_json_field((const char *)ctx.web_cert_buffer, "\"certificate\"");
1285+
if (!cert_field) {
12281286
LOG_ERR("Unexpected EK cert response: missing \"certificate\" field");
12291287
return tool_rc_general_error;
12301288
}
1231-
char *copy_buffer = base64_decode(&split, ctx.web_cert_buffer_size);
1289+
char *copy_buffer = convert_base64url_to_base64(cert_field);
12321290
if (!copy_buffer) {
12331291
LOG_ERR("Failed to decode EK certificate data");
12341292
return tool_rc_general_error;
@@ -1253,6 +1311,7 @@ static tool_rc process_output(void) {
12531311
copy_buffer);
12541312
strcpy((char *)ctx.web_cert_buffer + strlen(PEM_BEGIN_CERT_LINE) +
12551313
strlen(copy_buffer), PEM_END_CERT_LINE);
1314+
ctx.web_cert_buffer_size = strlen((char *)ctx.web_cert_buffer);
12561315
free(copy_buffer);
12571316
}
12581317

@@ -1312,7 +1371,7 @@ static tool_rc process_output(void) {
13121371
LOG_WARN("Ignoring the additional output file since only %zu certificates found on NV",
13131372
ctx.nv_cert_count);
13141373
}
1315-
1374+
13161375
return tool_rc_success;
13171376
}
13181377

@@ -1379,7 +1438,7 @@ static bool on_option(char key, char *value) {
13791438
if (!value || !value[0]) {
13801439
LOG_ERR("No encoding given.");
13811440
return false;
1382-
}
1441+
}
13831442
switch (value[0]) {
13841443
case 'a':
13851444
ctx.encoding = ENC_AMD;

0 commit comments

Comments
 (0)