Fix in ECC point conversion

This commit is contained in:
Eric Blankenhorn
2026-05-08 16:53:17 -05:00
parent 01ba609f0d
commit 29f3b30651
2 changed files with 79 additions and 2 deletions
+20 -2
View File
@@ -1356,9 +1356,19 @@ WOLFSSL_EC_POINT* wolfSSL_EC_POINT_hex2point(const WOLFSSL_EC_GROUP *group,
}
key_sz = (wolfSSL_EC_GROUP_get_degree(group) + 7) / 8;
if (key_sz <= 0 || (size_t)key_sz > MAX_ECC_BYTES)
goto err;
if (hex[0] == '0' && hex[1] == '4') { /* uncompressed mode */
str_sz = (size_t)key_sz * 2;
/* The uncompressed encoding is exactly 2 + 4*key_sz hex chars
* ("04" prefix plus X and Y as 2*key_sz hex chars each). Reject
* any other length so XMEMCPY/BN_hex2bn cannot read past the end
* of the input and trailing garbage is not silently absorbed. */
if (XSTRLEN(hex + 2) != str_sz * 2)
goto err;
XMEMSET(strGx, 0x0, str_sz + 1);
XMEMCPY(strGx, hex + 2, str_sz);
@@ -1378,8 +1388,16 @@ WOLFSSL_EC_POINT* wolfSSL_EC_POINT_hex2point(const WOLFSSL_EC_GROUP *group,
}
else if (hex[0] == '0' && (hex[1] == '2' || hex[1] == '3')) {
size_t sz = XSTRLEN(hex + 2) / 2;
/* compressed mode */
octGx[0] = ECC_POINT_COMP_ODD;
/* The SEC 1 compressed encoding is exactly 1 + key_sz bytes, so
* the hex payload after the "02"/"03" prefix is exactly 2*key_sz
* hex chars. Require an exact match: rejecting sz > key_sz keeps
* hex_to_bytes() from writing past strGx, and rejecting sz <
* key_sz keeps wolfSSL_ECPoint_d2i() from reading uninitialized
* stack bytes for the X coordinate. */
if (sz != (size_t)key_sz)
goto err;
octGx[0] = (hex[1] == '2') ? ECC_POINT_COMP_EVEN
: ECC_POINT_COMP_ODD;
if (hex_to_bytes(hex + 2, octGx + 1, sz) != sz) {
goto err;
}
+59
View File
@@ -631,6 +631,65 @@ int test_wolfSSL_EC_POINT(void)
#endif
XFREE(hexStr, NULL, DYNAMIC_TYPE_ECC);
EC_POINT_free(get_point);
get_point = NULL;
/* Regression: oversized compressed-point hex must not overflow the stack
* buffer in wolfSSL_EC_POINT_hex2point(). The byte length decoded from
* the hex string must be bounded by the curve's ordinate size. */
{
char tooLongHex[2 + 600 + 1];
size_t i;
tooLongHex[0] = '0';
tooLongHex[1] = '3';
for (i = 2; i < sizeof(tooLongHex) - 1; i++)
tooLongHex[i] = 'A';
tooLongHex[sizeof(tooLongHex) - 1] = '\0';
ExpectNull(EC_POINT_hex2point(group, tooLongHex, NULL, ctx));
/* Same with the "02" (even Y) prefix. */
tooLongHex[1] = '2';
ExpectNull(EC_POINT_hex2point(group, tooLongHex, NULL, ctx));
/* Truncated uncompressed input: prefix "04" with too few hex chars
* to cover the curve's coordinates. Must return NULL without
* reading past the end of the input string. */
ExpectNull(EC_POINT_hex2point(group, "04AB", NULL, ctx));
/* Empty payload after a recognized prefix. */
ExpectNull(EC_POINT_hex2point(group, "03", NULL, ctx));
ExpectNull(EC_POINT_hex2point(group, "04", NULL, ctx));
/* Partially populated compressed input: must be rejected so that
* wolfSSL_ECPoint_d2i() does not consume uninitialized stack
* bytes as the X coordinate. */
ExpectNull(EC_POINT_hex2point(group, "03AB", NULL, ctx));
ExpectNull(EC_POINT_hex2point(group, "02ABCD", NULL, ctx));
}
#if defined(HAVE_COMP_KEY) && !defined(HAVE_SELFTEST)
/* Round-trip a compressed point with even Y ("02" prefix) to verify
* that the prefix-to-parity flag is honored in the compressed branch. */
{
EC_POINT* even_point = NULL;
EC_POINT* round_trip = NULL;
char* even_hex = NULL;
ExpectNotNull(even_point = EC_POINT_dup(Gxy, group));
ExpectIntEQ(EC_POINT_invert(group, even_point, ctx), 1);
ExpectNotNull(even_hex = EC_POINT_point2hex(group, even_point,
POINT_CONVERSION_COMPRESSED, ctx));
/* P-256 G has odd Y; inverting flips Y parity so prefix is "02". */
ExpectIntEQ(even_hex[1], '2');
ExpectNotNull(round_trip = EC_POINT_hex2point(group, even_hex, NULL,
ctx));
ExpectIntEQ(EC_POINT_cmp(group, even_point, round_trip, ctx), 0);
XFREE(even_hex, NULL, DYNAMIC_TYPE_ECC);
EC_POINT_free(round_trip);
EC_POINT_free(even_point);
}
#endif
#ifndef HAVE_SELFTEST
/* Test point to oct */