Add various unit tests for crypto_get_uncompressed_pubkey
What changed, and why it matters
This commit only adds new automated tests for an existing cryptography helper function. It does not change the actual app code that runs on Ledger devices, so it cannot by itself introduce or fix a security bug. The commit message notes that some of the new tests check failure cases that will be addressed in a later commit.
No immediate action required for this commit. Review the subsequent commit that fixes the failure cases being tested here, as that is where any actual security relevance would lie.
Security signals we found
Tests added for invalid/unreachable public-key inputs to crypto_get_uncompressed_pubkey
Commit message states failure cases 'will be fixed in the next commit' — this commit is preparatory only
Evidence from the diff
The diff adds cmocka unit tests for crypto_get_uncompressed_pubkey in unit-tests/test_crypto.c. It covers valid compressed-key prefixes 0x02/0x03, in-place decompression, invalid prefix 0x04, a point not on the curve (x=5), and x values outside the secp256k1 field (x == p and x > p). No implementation code is modified. The commit explicitly states these failure cases ‘will be fixed in the next commit’, implying the current implementation may fail them, but this patch does not contain that fix.
Changed components
unit-tests/test_crypto.cInspect captured patch +98 / −0
### unit-tests/test_crypto.c
@@ -260,6 +260,98 @@ static void test_crypto_get_compressed_pubkey_invalid(void **state) {
assert_int_equal(ret, -1);
}
+/* ---------------------------------------------------------------- */
+/* crypto_get_uncompressed_pubkey */
+/* */
+/* Reuses the key pairs defined for crypto_get_compressed_pubkey. */
+/* ---------------------------------------------------------------- */
+
+static void test_crypto_get_uncompressed_pubkey_02(void **state) {
+ (void) state;
+
+ uint8_t key_in[33], key_out[65];
+ memcpy(key_in, compressed_key_02, 33);
+ int ret = crypto_get_uncompressed_pubkey(key_in, key_out);
+
+ assert_int_equal(ret, 0);
+
+ assert_memory_equal(key_out, uncompressed_key_02, 65);
+ assert_memory_equal(key_in, compressed_key_02, 33);
+}
+
+static void test_crypto_get_uncompressed_pubkey_03(void **state) {
+ (void) state;
+
+ uint8_t key_in[33], key_out[65];
+ memcpy(key_in, compressed_key_03, 33);
+ int ret = crypto_get_uncompressed_pubkey(key_in, key_out);
+
+ assert_int_equal(ret, 0);
+
+ assert_memory_equal(key_out, uncompressed_key_03, 65);
+ assert_memory_equal(key_in, compressed_key_03, 33);
+}
+
+// Test that it also works if out == compressed_key
+static void test_crypto_get_uncompressed_pubkey_in_place(void **state) {
+ (void) state;
+
+ uint8_t key_in_out[65];
+ memcpy(key_in_out, compressed_key_02, 33);
+ int ret = crypto_get_uncompressed_pubkey(key_in_out, key_in_out);
+
+ assert_int_equal(ret, 0);
+
+ assert_memory_equal(key_in_out, uncompressed_key_02, 65);
+}
+
+static void test_crypto_get_uncompressed_pubkey_invalid_prefix(void **state) {
+ (void) state;
+
+ uint8_t key_in[33], key_out[65];
+ memcpy(key_in, compressed_key_02, 33);
+ key_in[0] = 0x04; // only 0x02 and 0x03 are valid prefixes
+
+ assert_int_equal(crypto_get_uncompressed_pubkey(key_in, key_out), -1);
+}
+
+static void test_crypto_get_uncompressed_pubkey_not_on_curve(void **state) {
+ (void) state;
+
+ /* x = 5: with secp256k1's b=7, y^2 = 5^3 + 7 = 132 is not a quadratic
+ * residue mod p, so no point with this x exists. */
+ uint8_t key_in[33] = {0};
+ uint8_t key_out[65];
+ key_in[0] = 0x02;
+ key_in[32] = 5;
+
+ assert_int_equal(crypto_get_uncompressed_pubkey(key_in, key_out), -1);
+
+ /* Same x, with the other prefix. */
+ key_in[0] = 0x03;
+ assert_int_equal(crypto_get_uncompressed_pubkey(key_in, key_out), -1);
+}
+
+static void test_crypto_get_uncompressed_pubkey_x_not_in_field(void **state) {
+ (void) state;
+
+ /* x must be strictly smaller than the field order p. */
+ static const uint8_t p[32] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f,
+ };
+
+ uint8_t key_in[33], key_out[65];
+ key_in[0] = 0x02;
+
+ memcpy(key_in + 1, p, 32); // x == p
+ assert_int_equal(crypto_get_uncompressed_pubkey(key_in, key_out), -1);
+
+ memset(key_in + 1, 0xff, 32); // x > p
+ assert_int_equal(crypto_get_uncompressed_pubkey(key_in, key_out), -1);
+}
+
/* ---------------------------------------------------------------- */
/* crypto_get_checksum */
/* ---------------------------------------------------------------- */
@@ -862,6 +954,12 @@ int main(void) {
cmocka_unit_test(test_crypto_get_compressed_pubkey_03),
cmocka_unit_test(test_crypto_get_compressed_pubkey_in_place),
cmocka_unit_test(test_crypto_get_compressed_pubkey_invalid),
+ cmocka_unit_test(test_crypto_get_uncompressed_pubkey_02),
+ cmocka_unit_test(test_crypto_get_uncompressed_pubkey_03),
+ cmocka_unit_test(test_crypto_get_uncompressed_pubkey_in_place),
+ cmocka_unit_test(test_crypto_get_uncompressed_pubkey_invalid_prefix),
+ cmocka_unit_test(test_crypto_get_uncompressed_pubkey_not_on_curve),
+ cmocka_unit_test(test_crypto_get_uncompressed_pubkey_x_not_in_field),
cmocka_unit_test(test_crypto_get_checksum_empty),
cmocka_unit_test(test_crypto_get_checksum_hello),
cmocka_unit_test(test_crypto_get_compressed_pubkey_at_path_master),Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.