feat(core/prodtest): Update to new noise lib version + add card certificitate authenticity check.
What changed, and why it matters
This commit updates a factory-testing tool (prodtest) used with Trezor hardware wallets. It swaps a randomly generated test key for a fixed, hard-coded test keypair and adds a check that the public key reported by an NFC backup card matches the public key in the card's certificate. The change is in a production-test utility, not normal user firmware, and the hard-coded key appears to be a test fixture rather than a live secret. The added certificate check is a security-hardening step, but the commit is partial: it references a new noise_xxpsk3.c source file and a new library API without showing the implementation.
Treat this as a low-risk hardening change in a factory-test tool, but verify that the hard-coded keypair is documented as a test-only fixture and is not used in production firmware or live card authentication. Review the full implementation of noise_xxpsk3.c and the updated noise library to confirm the certificate parsing and public-key extraction are robust. No urgent user action is indicated.
Security signals we found
Hard-coded cryptographic test keypair in production-test code
New Noise protocol source file (noise_xxpsk3.c) added to the cryptographic build
Added public-key-vs-certificate comparison to verify card identity during NFC backup testing
Commit is partial: new library functions and source file contents are not shown in the diff
Evidence from the diff
In core/embed/crypto/build.rs, the build now compiles both noise_kk1.c and the new noise_xxpsk3.c. In prodtest_nfc_backup.c, the previous code filled static_private_key with RNG output; the patch replaces it with a constant 32-byte private key and a matching constant 32-byte public key. It also updates the call to noise_xxpsk3_initiator_init to pass the public key explicitly. After receiving the card’s response, it now extracts card_public_key from noise_xxpsk3_initiator_handle_response1 and compares it to cert.public_key parsed from the X.509 certificate, failing if they differ. The commit message frames this as adding a card certificate authenticity check and moving to a new noise library version.
Changed components
core/embed/crypto/build.rscore/embed/projects/prodtest/cmd/prodtest_nfc_backup.ccore/embed/crypto/noise/noise_xxpsk3.c (referenced but not shown)Inspect captured patch +27 / −8
### core/embed/crypto/build.rs
@@ -252,7 +252,11 @@ fn add_aes_gcm(lib: &mut CLibrary, attrs: &CompileAttrs) -> Result<()> {
}
fn add_noise(lib: &mut CLibrary, attrs: &CompileAttrs) -> Result<()> {
- lib.add_sources_in_dir_with_attrs(CRYPTO_PATH, ["noise_kk1.c"], Some(attrs.clone()));
+ lib.add_sources_in_dir_with_attrs(
+ CRYPTO_PATH,
+ ["noise_kk1.c", "noise_xxpsk3.c"],
+ Some(attrs.clone()),
+ );
Ok(())
}
### core/embed/projects/prodtest/cmd/prodtest_nfc_backup.c
@@ -647,11 +647,19 @@ static ts_t nfc_backup_noise(cli_t *cli, uint8_t (*psk)[32]) {
TSH_CHECK_ARG(*psk != NULL);
bool noise_status = false;
- // Generate static private key for initiator
- uint8_t static_private_key[NOISE_XXPSK3_DHLEN] = {0};
- rng_fill_buffer(static_private_key, sizeof(static_private_key));
-
- noise_status = noise_xxpsk3_initiator_init(&intr, *psk, static_private_key);
+ // Test static keypair for Noise XXPSK3 handshake.
+ uint8_t static_private_key[NOISE_XXPSK3_DHLEN] = {
+ 0x43, 0xa1, 0x7e, 0x8a, 0xad, 0x8b, 0xf5, 0xb0, 0x26, 0x12, 0xfe,
+ 0x6d, 0xeb, 0x77, 0xcd, 0xc0, 0x84, 0x59, 0xad, 0x05, 0xf4, 0xd6,
+ 0xb7, 0x32, 0xc5, 0xb4, 0xa2, 0xe1, 0xbf, 0xec, 0x99, 0x7b};
+
+ uint8_t static_public_key[NOISE_XXPSK3_DHLEN] = {
+ 0x8a, 0xd7, 0x10, 0xc4, 0xcd, 0xa6, 0x35, 0xf7, 0x3f, 0x06, 0x04,
+ 0x99, 0x4f, 0x79, 0xbd, 0x19, 0xe9, 0xba, 0xfa, 0x10, 0x9c, 0xef,
+ 0xe4, 0x22, 0xdd, 0x60, 0x86, 0x63, 0xc2, 0xe1, 0xa4, 0x58};
+
+ noise_status = noise_xxpsk3_initiator_init(&intr, *psk, static_private_key,
+ static_public_key);
TSH_CHECK(noise_status, TS_EINVAL);
uint8_t request[256] = {0};
@@ -670,18 +678,25 @@ static ts_t nfc_backup_noise(cli_t *cli, uint8_t (*psk)[32]) {
status = nfc_transceive(&cmd, &rsp);
TSH_CHECK_OK(status);
+ uint8_t card_public_key[NOISE_XXPSK3_DHLEN] = {0};
+
uint8_t certificate[512] = {0};
size_t certificate_size = 0;
noise_status = noise_xxpsk3_initiator_handle_response1(
- &intr, rsp.data, rsp.data_len - 2, certificate, sizeof(certificate),
- &certificate_size);
+ &intr, rsp.data, rsp.data_len - 2, card_public_key, certificate,
+ sizeof(certificate), &certificate_size);
TSH_CHECK(noise_status, TS_EINVAL);
nfc_backup_certificate_t cert = {0};
parse_x509_certificate(certificate, certificate_size, &cert);
print_certificate(cli, &cert);
+ if (memcmp(cert.public_key, card_public_key, NOISE_XXPSK3_DHLEN) != 0) {
+ cli_trace(cli, "Card public key does not match certificate public key.");
+ TSH_CHECK(false, TS_EINVAL);
+ }
+
noise_status = noise_xxpsk3_initiator_create_request2(
&intr, NULL, 0, request, sizeof(request), &request_size);
TSH_CHECK(noise_status, TS_EINVAL);Why this scored 22/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.