Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 141 additions & 4 deletions apps/wolfsshd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include <wolfssl/wolfcrypt/wc_port.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssl/wolfcrypt/asn_public.h>

#ifdef WOLFSSL_FPKI
#include <wolfssl/wolfcrypt/asn.h>
Expand Down Expand Up @@ -147,20 +148,56 @@ struct WOLFSSHD_AUTH {
#endif

#ifndef MAX_LINE_SZ
/* Sized to hold the largest authorized_keys entry. */
/* sized for the largest authorized_keys entry, composite pubkeys
* included */
#ifndef WOLFSSH_NO_MLDSA
#ifndef WOLFSSH_NO_MLDSA87
#define MAX_LINE_SZ ((WC_MLDSA_87_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#if defined(WOLFSSH_CERTS)
/* x509v3-ssh-mldsa-87 size (pubkey+CA sig+DER, base64);
* COMPOSITE_MAX_TRAD_PUB_SZ is headroom for a future variant. */
#define MAX_LINE_SZ \
((WC_MLDSA_87_PUB_KEY_SIZE + WC_MLDSA_87_SIG_SIZE + \
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
#else
#define MAX_LINE_SZ \
((WC_MLDSA_87_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
2) / 3 * 4 + 640)
#endif
#elif !defined(WOLFSSH_NO_MLDSA65)
#define MAX_LINE_SZ ((WC_MLDSA_65_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#if defined(WOLFSSH_CERTS)
#define MAX_LINE_SZ \
((WC_MLDSA_65_PUB_KEY_SIZE + WC_MLDSA_65_SIG_SIZE + \
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
#else
#define MAX_LINE_SZ \
((WC_MLDSA_65_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
2) / 3 * 4 + 640)
#endif
#else
#define MAX_LINE_SZ ((WC_MLDSA_44_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#if defined(WOLFSSH_CERTS)
#define MAX_LINE_SZ \
((WC_MLDSA_44_PUB_KEY_SIZE + WC_MLDSA_44_SIG_SIZE + \
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
#else
#define MAX_LINE_SZ \
((WC_MLDSA_44_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
2) / 3 * 4 + 640)
#endif
#endif
#else
#define MAX_LINE_SZ 900
#endif
#endif

#ifdef WOLFSSHD_UNIT_TEST
/* Exposes MAX_LINE_SZ so tests can size worst-case lines without
* duplicating the formula above. */
word32 wolfsshd_test_MaxLineSz(void)
{
return (word32)MAX_LINE_SZ;
}
#endif

#if 0
/* this could potentially be useful in a deeply embedded future port */

Expand Down Expand Up @@ -257,6 +294,28 @@ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
#endif
#endif
#endif
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256)
"ssh-mldsa44-es256@wolfssl.com",
#endif
#if !defined(WOLFSSH_NO_MLDSA65) && \
!defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512)
"ssh-mldsa65-es256@wolfssl.com",
#endif
#if !defined(WOLFSSH_NO_MLDSA87) && \
!defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512)
"ssh-mldsa87-es384@wolfssl.com",
#endif
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \
!defined(NO_SHA512)
"ssh-mldsa44-ed25519@openssh.com",
#endif
#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \
!defined(NO_SHA512)
"ssh-mldsa65-ed25519@wolfssl.com",
#endif
#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448)
"ssh-mldsa87-ed448@wolfssl.com",
#endif
};
const int NUM_ALLOWED_TYPES =
(int)(sizeof(allowedTypes) / sizeof(allowedTypes[0]));
Expand Down Expand Up @@ -996,6 +1055,84 @@ static int SearchKeysFile(const char* keysFilePath, const byte* key,
return ret;
}

/* Detects OpenSSH vs ASN1/DER format of a raw host private key buffer.
*
* Uses wc_KeyPemToDer(), not wc_PemToDer(..., PRIVATEKEY_TYPE, ...): the
* latter also unwraps PKCS#8 via ToTraditional(), which mangles key types
* with no traditional DER form (e.g. ML-DSA).
*
* On a PEM buffer, *keyDer is a WMALLOC'd (heap, DYNTYPE_SSHD) buffer the
* caller must WS_FORCEZERO + WFREE; NULL if data was passed through as-is
* (raw DER or OpenSSH). privBuf/privBufSz are set to the buffer to actually
* load. Returns WOLFSSH_FORMAT_ASN1/WOLFSSH_FORMAT_OPENSSH, or negative on
* error. */
int wolfSSHD_DetectPrivKeyFormat(byte* data, word32 dataSz, void* heap,
byte** keyDer, byte** privBuf, word32* privBufSz)
{
int keyFormat = WOLFSSH_FORMAT_ASN1;
byte* der;
int derSz;

if (keyDer != NULL) {
*keyDer = NULL;
}
if (privBuf != NULL) {
*privBuf = NULL;
}
if (privBufSz != NULL) {
*privBufSz = 0;
}

if (data == NULL || dataSz == 0 || keyDer == NULL || privBuf == NULL ||
privBufSz == NULL) {
return WS_BAD_ARGUMENT;
}

der = (byte*)WMALLOC(dataSz, heap, DYNTYPE_SSHD);
if (der == NULL) {
return WS_MEMORY_E;
}

derSz = wc_KeyPemToDer(data, (int)dataSz, der, (int)dataSz, NULL);
if (derSz <= 0) {
WS_FORCEZERO(der, dataSz);
WFREE(der, heap, DYNTYPE_SSHD);

*privBuf = data;
*privBufSz = dataSz;

/* Strict prefix match for OpenSSH magic (35 bytes) */
if (*privBufSz >= 35 &&
WMEMCMP(*privBuf, "-----BEGIN OPENSSH PRIVATE KEY-----", 35) == 0) {
keyFormat = WOLFSSH_FORMAT_OPENSSH;
}
else if (*privBufSz >= sizeof("openssh-key-v1") &&
WMEMCMP(*privBuf, "openssh-key-v1",
sizeof("openssh-key-v1")) == 0) {
/* sizeof() includes the magic's trailing NUL */
keyFormat = WOLFSSH_FORMAT_OPENSSH;
}
else if (data[0] != 0x30) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to convert host private key from PEM.");
*privBuf = NULL;
*privBufSz = 0;
return WS_BAD_FILE_E;
}
}
else {
*keyDer = der;
*privBuf = der;
*privBufSz = (word32)derSz;
/* PEM-decoded result may still be an OpenSSH binary blob */
if (*privBufSz >= sizeof("openssh-key-v1") &&
WMEMCMP(*privBuf, "openssh-key-v1",
sizeof("openssh-key-v1")) == 0) {
keyFormat = WOLFSSH_FORMAT_OPENSSH;
}
}

return keyFormat;
}

WOLFSSHD_STATIC int SearchForPubKey(const char* path,
const char* authKeysFile, const char* user,
Expand Down
7 changes: 7 additions & 0 deletions apps/wolfsshd/auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ int wolfSSHD_GetHomeDirectory(WOLFSSHD_AUTH* auth, WOLFSSH* ssh, WCHAR* out, int
int wolfSSHD_OpenSecureFile(const char* path, WUID_T ownerUid,
int rejectReadable, void* heap, WFILE** out);

/* classifies a loaded host private key buffer as OpenSSH or ASN1/DER.
* *keyDer is a WMALLOC'd (heap, DYNTYPE_SSHD) buffer to WS_FORCEZERO +
* WFREE on a PEM decode, else NULL. */
int wolfSSHD_DetectPrivKeyFormat(byte* data, word32 dataSz, void* heap,
byte** keyDer, byte** privBuf, word32* privBufSz);

#ifdef WOLFSSHD_UNIT_TEST
#ifndef _WIN32
extern int (*wsshd_setregid_cb)(WGID_T, WGID_T);
Expand All @@ -118,6 +124,7 @@ int SearchForPubKey(const char* path, const char* authKeysFile,
const WS_UserAuthData_PublicKey* pubKeyCtx,
WUID_T uid, int strictModes);
#endif
word32 wolfsshd_test_MaxLineSz(void);
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
int CheckPasswordHashUnix(const char* input, char* stored);
#endif
Expand Down
14 changes: 14 additions & 0 deletions apps/wolfsshd/test/create_sshd_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ AuthorizedKeysFile $PWD/authorized_keys_test

EOF

cat <<EOF > sshd_config_test_mldsa
Port 22222
Protocol 2
LoginGraceTime 600
PermitRootLogin yes
PasswordAuthentication yes
PermitEmptyPasswords no
UsePrivilegeSeparation no
UseDNS no
HostKey $PWD/../../../keys/server-key-mldsa87es384
AuthorizedKeysFile $PWD/authorized_keys_test

EOF

cat <<EOF > sshd_config_test_x509
Port 22222
Protocol 2
Expand Down
10 changes: 10 additions & 0 deletions apps/wolfsshd/test/run_all_sshd_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ test_cases=(
"sshd_scp_fail.sh"
"sshd_term_close_test.sh"
"ssh_kex_algos.sh"
"sshd_mldsa_composite_test.sh"
Comment thread
stenslae marked this conversation as resolved.
)

# Set defaults
Expand Down Expand Up @@ -424,6 +425,15 @@ else
stop_wolfsshd
fi

# ML-DSA composite host key test. Runs when we control the local daemon.
# The client side uses an ECC key since we only test the host key here.
if [ "$USING_LOCAL_HOST" == 1 ]; then
start_wolfsshd "sshd_config_test_mldsa"
run_test "sshd_mldsa_composite_test.sh"
printf "Shutting down test wolfSSHd\n"
stop_wolfsshd
fi

# OpenSSH certificate user-auth test (self-contained: starts its own
# wolfSSHd; skips when not built with --enable-ossh-certs). Runs the suite
# against the wolfSSH example client and, for interop, the system OpenSSH
Expand Down
26 changes: 26 additions & 0 deletions apps/wolfsshd/test/sshd_mldsa_composite_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/sh

# sshd mldsa composite test

PWD=`pwd`
cd ../../..

TEST_CLIENT="./examples/client/client"
USER=`whoami`
PRIVATE_KEY="./keys/hansel-key-ecc.der"
PUBLIC_KEY="./keys/hansel-key-ecc.pub"

if [ -z "$1" ] || [ -z "$2" ]; then
echo "expecting host and port as arguments"
echo "./sshd_mldsa_composite_test.sh 127.0.0.1 22222"
exit 1
fi

set -e
echo "$TEST_CLIENT -c 'ls' -u $USER -i $PRIVATE_KEY -j $PUBLIC_KEY -h \"$1\" -p \"$2\""
$TEST_CLIENT -c 'ls' -u $USER -i $PRIVATE_KEY -j $PUBLIC_KEY -h "$1" -p "$2"

set +e

cd $PWD
exit 0
Loading
Loading