Skip to content

Commit 65fdad2

Browse files
shaunchokshiejohnstown
authored andcommitted
wolfsshd: load PKCS#8 PEM host keys (e.g. ML-DSA)
wolfsshd could not load a host key stored as a PKCS#8 "-----BEGIN PRIVATE KEY-----" PEM file (the form emitted for ML-DSA keys, and by `openssl genpkey`). SetupCTX() pre-converted the file with wc_PemToDer(..., PRIVATEKEY_TYPE, ...), which only recognizes the classic "RSA/EC PRIVATE KEY" headers. On a PKCS#8 body that call returns success but produces a malformed DER (leading 0x04 rather than a 0x30 SEQUENCE); the bytes were then passed to wolfSSH_CTX_UsePrivateKey_buffer() as WOLFSSH_FORMAT_ASN1, which rejected them with WS_BAD_FILETYPE_E. Detect PEM vs DER by content and decode PEM with wc_KeyPemToDer(), which handles PKCS#1, SEC1 and PKCS#8 private-key bodies. DER keys are unchanged. Tested (master + wolfSSL master): an ML-DSA-65 PEM host key now loads (wolfSSH_CTX_UsePrivateKey_buffer ret = 0, was WS_BAD_FILETYPE_E); ML-DSA-65 DER and ECDSA SEC1 PEM host keys continue to load (no regression). Also harden the host-key load path per review: reject an empty (0-byte) key file explicitly instead of taking the PEM branch into WMALLOC(0), and zeroize the raw file buffer (not just the decoded DER) before freeing so private-key material does not linger in the heap.
1 parent e393bcb commit 65fdad2

1 file changed

Lines changed: 54 additions & 13 deletions

File tree

apps/wolfsshd/wolfsshd.c

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,8 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
349349
byte** banner)
350350
{
351351
int ret = WS_SUCCESS;
352-
DerBuffer* der = NULL;
353-
byte* privBuf;
354-
word32 privBufSz;
352+
byte* privBuf = NULL;
353+
word32 privBufSz = 0;
355354
void* heap = NULL;
356355

357356
if (ctx == NULL) {
@@ -412,28 +411,70 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
412411
}
413412

414413
if (ret == WS_SUCCESS) {
415-
if (wc_PemToDer(data, dataSz, PRIVATEKEY_TYPE, &der, NULL,
416-
NULL, NULL) != 0) {
417-
wolfSSH_Log(WS_LOG_DEBUG, "[SSHD] Failed to convert host "
418-
"private key from PEM. Assuming key in DER "
419-
"format.");
414+
/* Host keys may be PEM or DER. Detect by content: a DER key is
415+
* an ASN.1 SEQUENCE (leading 0x30); anything else is treated as
416+
* PEM text and decoded with wc_KeyPemToDer(), which handles
417+
* PKCS#1, SEC1 and PKCS#8 "PRIVATE KEY" bodies.
418+
*
419+
* The previous code used wc_PemToDer(..., PRIVATEKEY_TYPE, ...),
420+
* which only recognizes the classic "RSA/EC PRIVATE KEY" PEM
421+
* headers. On a PKCS#8 body (how ML-DSA host keys are emitted)
422+
* it returns *success* but yields a malformed body (leading
423+
* 0x04, not a 0x30 SEQUENCE), which
424+
* wolfSSH_CTX_UsePrivateKey_buffer() then rejects with
425+
* WS_BAD_FILETYPE_E, so ML-DSA PEM host keys could not load. */
426+
byte* keyDer = NULL;
427+
428+
if (dataSz == 0) {
429+
/* An empty (0-byte) file passes the NULL check above but
430+
* carries no key material. Handle it explicitly as a file
431+
* error instead of falling into the PEM path, where
432+
* WMALLOC(0) is implementation-defined (may return NULL and
433+
* be misreported as WS_MEMORY_E). */
434+
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Host key file is empty.");
435+
ret = WS_BAD_FILE_E;
436+
}
437+
else if (data[0] == 0x30) {
420438
privBuf = data;
421439
privBufSz = dataSz;
422440
}
423441
else {
424-
privBuf = der->buffer;
425-
privBufSz = der->length;
442+
keyDer = (byte*)WMALLOC(dataSz, heap, DYNTYPE_SSHD);
443+
if (keyDer == NULL) {
444+
ret = WS_MEMORY_E;
445+
}
446+
else {
447+
int keyDerSz = wc_KeyPemToDer(data, dataSz, keyDer,
448+
(int)dataSz, NULL);
449+
if (keyDerSz <= 0) {
450+
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to convert "
451+
"host private key from PEM.");
452+
ret = WS_BAD_FILE_E;
453+
}
454+
else {
455+
privBuf = keyDer;
456+
privBufSz = (word32)keyDerSz;
457+
}
458+
}
426459
}
427460

428-
if (wolfSSH_CTX_UsePrivateKey_buffer(*ctx, privBuf, privBufSz,
429-
WOLFSSH_FORMAT_ASN1) < 0) {
461+
if (ret == WS_SUCCESS
462+
&& wolfSSH_CTX_UsePrivateKey_buffer(*ctx, privBuf,
463+
privBufSz, WOLFSSH_FORMAT_ASN1) < 0) {
430464
wolfSSH_Log(WS_LOG_ERROR,
431465
"[SSHD] Failed to use host private key.");
432466
ret = WS_BAD_ARGUMENT;
433467
}
434468

469+
if (keyDer != NULL) {
470+
WS_FORCEZERO(keyDer, dataSz);
471+
WFREE(keyDer, heap, DYNTYPE_SSHD);
472+
}
473+
/* data held the raw private key — the DER bytes, or the PEM
474+
* text decoded into keyDer above. Zeroize before freeing so key
475+
* material does not linger in the heap after use. */
476+
WS_FORCEZERO(data, dataSz);
435477
freeBufferFromFile(data, heap);
436-
wc_FreeDer(&der);
437478
}
438479
}
439480
}

0 commit comments

Comments
 (0)