F-14301, F-14302: keep no libc stdio copy of key material;

The keytools read and write private keys through fully buffered
stdio streams: for key files smaller than the stdio buffer, libc
copies the raw key bytes into its internal FILE buffer, and
fclose() frees that buffer without scrubbing it, so a copy of
every key survives the explicit zero_and_free()/wc_ForceZero()
wipes. Open every secret-material stream unbuffered with
setvbuf(f, NULL, _IONBF, 0) right after fopen():

- sign.c load_key(): the private key file. Also close the
  stream on the short-read failure path, which previously
  leaked it.
- sign.c make_header_ex(): the firmware encryption key/IV file.
- keygen.c: the five private-key write sites (RSA, ECC,
  Ed25519, Ed448, ML-DSA).
- lms_common.h / xmss_common.h: the LMS/XMSS private-state
  write, verify-read and read callbacks.

Public-key export streams are left buffered: not secret.
Host-side hardening only: whoever can read the tool heap can
read the key file too.
pull/913/head
Daniele Lacamera 2026-09-29 09:13:25 +02:00
parent 41b047ad49
commit b896caeea0
4 changed files with 28 additions and 0 deletions

View File

@ -607,6 +607,8 @@ static void keygen_rsa(const char *keyfile, int kbits, uint32_t id_mask,
exit_code = 4;
goto cleanup;
}
/* Unbuffered: keep no libc-owned copy of the private key. */
setvbuf(fpriv, NULL, _IONBF, 0);
fwrite(priv_der, privlen, 1, fpriv);
fclose(fpriv);
fpriv = NULL;
@ -687,6 +689,8 @@ static void keygen_ecc(const char *priv_fname, uint16_t ecc_key_size,
exit_code = 3;
goto cleanup;
}
/* Unbuffered: keep no libc-owned copy of the private key. */
setvbuf(fpriv, NULL, _IONBF, 0);
if (saveAsDer) {
/* save file as standard ASN.1 / DER */
@ -794,6 +798,8 @@ static void keygen_ed25519(const char *privkey, uint32_t id_mask)
exit_code = 3;
goto cleanup;
}
/* Unbuffered: keep no libc-owned copy of the private key. */
setvbuf(fpriv, NULL, _IONBF, 0);
fwrite(priv, 32, 1, fpriv);
fwrite(pub, 32, 1, fpriv);
fclose(fpriv);
@ -855,6 +861,8 @@ static void keygen_ed448(const char *privkey, uint32_t id_mask)
exit_code = 3;
goto cleanup;
}
/* Unbuffered: keep no libc-owned copy of the private key. */
setvbuf(fpriv, NULL, _IONBF, 0);
fwrite(priv, ED448_KEY_SIZE, 1, fpriv);
fwrite(pub, ED448_PUB_KEY_SIZE, 1, fpriv);
fclose(fpriv);
@ -1243,6 +1251,8 @@ static void keygen_ml_dsa(const char *priv_fname, uint32_t id_mask)
exit_code = 1;
goto cleanup;
}
/* Unbuffered: keep no libc-owned copy of the private key. */
setvbuf(fpriv, NULL, _IONBF, 0);
fwrite(priv, priv_len, 1, fpriv);
fwrite(pub, pub_len, 1, fpriv);

View File

@ -709,6 +709,8 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
printf("Open key file %s failed\n", key_file);
goto failure;
}
/* Unbuffered: keep no libc-owned copy of the private key bytes. */
setvbuf(f, NULL, _IONBF, 0);
fseek(f, 0, SEEK_END);
*key_buffer_sz = ftell(f);
fseek(f, 0, SEEK_SET);
@ -717,6 +719,8 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
io_sz = (int)fread(*key_buffer, 1, *key_buffer_sz, f);
if (io_sz != (int)*key_buffer_sz) {
printf("Key file read error!\n");
fclose(f);
f = NULL;
goto failure;
}
}
@ -2480,6 +2484,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
CMD.encrypt_key_file, strerror(errno));
goto failure;
}
/* Unbuffered: keep no libc-owned copy of the key/IV bytes. */
setvbuf(fek, NULL, _IONBF, 0);
ret = (int)fread(key, 1, keySz, fek);
if (ret != keySz) {
fprintf(stderr, "Error reading key from %s\n", CMD.encrypt_key_file);

View File

@ -52,6 +52,8 @@ static int lms_write_key(const byte * priv, word32 privSz, void * context)
return WC_LMS_RC_WRITE_FAIL;
}
}
/* Unbuffered: keep no libc-owned copy of the private state. */
setvbuf(file, NULL, _IONBF, 0);
n_write = fwrite(priv, 1, privSz, file);
@ -74,6 +76,8 @@ static int lms_write_key(const byte * priv, word32 privSz, void * context)
fprintf(stderr, "error: fopen(%s, \"r+\") failed.\n", filename);
return WC_LMS_RC_WRITE_FAIL;
}
/* Unbuffered: keep no libc-owned copy of the private state. */
setvbuf(file, NULL, _IONBF, 0);
XMEMSET(buff, 0, n_write);
@ -128,6 +132,8 @@ static int lms_read_key(byte * priv, word32 privSz, void * context)
fprintf(stderr, "error: fopen(%s, \"rb\") failed\n", filename);
return WC_LMS_RC_READ_FAIL;
}
/* Unbuffered: keep no libc-owned copy of the private state. */
setvbuf(file, NULL, _IONBF, 0);
n_read = fread(priv, 1, privSz, file);

View File

@ -52,6 +52,8 @@ static enum wc_XmssRc xmss_write_key(const byte * priv, word32 privSz, void * co
return WC_XMSS_RC_WRITE_FAIL;
}
}
/* Unbuffered: keep no libc-owned copy of the private state. */
setvbuf(file, NULL, _IONBF, 0);
n_write = fwrite(priv, 1, privSz, file);
@ -74,6 +76,8 @@ static enum wc_XmssRc xmss_write_key(const byte * priv, word32 privSz, void * co
fprintf(stderr, "error: fopen(%s, \"r+\") failed.\n", filename);
return WC_XMSS_RC_WRITE_FAIL;
}
/* Unbuffered: keep no libc-owned copy of the private state. */
setvbuf(file, NULL, _IONBF, 0);
buff = malloc(privSz);
if (buff == NULL) {
@ -130,6 +134,8 @@ static enum wc_XmssRc xmss_read_key(byte * priv, word32 privSz, void * context)
fprintf(stderr, "error: fopen(%s, \"rb\") failed\n", filename);
return WC_XMSS_RC_READ_FAIL;
}
/* Unbuffered: keep no libc-owned copy of the private state. */
setvbuf(file, NULL, _IONBF, 0);
n_read = fread(priv, 1, privSz, file);