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
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,22 +451,30 @@ The wolfSSH client and server will automatically negotiate using Curve25519.
POST-QUANTUM
============

wolfSSH now supports the post-quantum algorithm ML-KEM (formerly known as
Kyber). It uses the ML-KEM-768 parameter set and is hybridized with ECDHE over
the P-256 ECC curve.
wolfSSH supports both post-quantum key exchange via ML-KEM (formerly known as
Kyber) and post-quantum signature verification via ML-DSA (formerly known as
Dilithium).

In order to use this key exchange you must build and install wolfSSL on your
system. Here is an example of an effective configuration:
* **ML-KEM**: Uses the ML-KEM-768 parameter set hybridized with ECDHE over the
P-256 ECC curve.
* **ML-DSA**: Supports ML-DSA-44, ML-DSA-65, and ML-DSA-87 parameter sets for
both server host keys and client public key authentication. When built with
certificate support, ML-DSA X.509 certificates (`x509v3-ssh-mldsa-44`,
`x509v3-ssh-mldsa-65`, and `x509v3-ssh-mldsa-87`) are also supported.

$ ./configure --enable-wolfssh --enable-mlkem
In order to use these algorithms you must build and install wolfSSL with
support for them. Here is an example of an effective configuration:

After that, simply configure and build wolfssh as usual:
$ ./configure --enable-wolfssh --enable-mlkem --enable-mldsa

After that, configure and build wolfSSH as usual:

$ ./configure
$ make all

The wolfSSH client and server will automatically negotiate using ML-KEM-768
hybridized with ECDHE over the P-256 ECC curve.
hybridized with ECDHE over the P-256 ECC curve and ML-DSA for host keys/client
public key authentication.

$ ./examples/echoserver/echoserver -f

Expand All @@ -476,7 +484,7 @@ On the client side, you will see the following output:

Server said: Hello, wolfSSH!

If you want to see interoperability with OpenQauntumSafe's fork of OpenSSH, you
If you want to see interoperability with OpenQuantumSafe's fork of OpenSSH, you
can build and execute the fork while the echoserver is running. Download the
release from here:

Expand All @@ -498,6 +506,7 @@ NOTE: when prompted, enter the password which is "upthehill".
You can type a line of text and when you press enter, the line will be echoed
back. Use CTRL-C to terminate the connection.


CERTIFICATE SUPPORT
===================

Expand Down
46 changes: 37 additions & 9 deletions apps/wolfsshd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,18 @@ struct WOLFSSHD_AUTH {
#endif

#ifndef MAX_LINE_SZ
#define MAX_LINE_SZ 900
/* Sized to hold the largest authorized_keys entry. */
#ifndef WOLFSSH_NO_MLDSA
#ifndef WOLFSSH_NO_MLDSA87
#define MAX_LINE_SZ ((WC_MLDSA_87_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#elif !defined(WOLFSSH_NO_MLDSA65)
#define MAX_LINE_SZ ((WC_MLDSA_65_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#else
#define MAX_LINE_SZ ((WC_MLDSA_44_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#endif
#else
#define MAX_LINE_SZ 900
#endif
Comment thread
stenslae marked this conversation as resolved.
#endif
#ifndef MAX_PATH_SZ
#define MAX_PATH_SZ 80
Expand Down Expand Up @@ -173,14 +184,7 @@ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
word32 keyCandSz = 0;
char* last = NULL;

enum {
#ifdef WOLFSSH_CERTS
NUM_ALLOWED_TYPES = 9
#else
NUM_ALLOWED_TYPES = 5
#endif
};
static const char* allowedTypes[NUM_ALLOWED_TYPES] = {
static const char* allowedTypes[] = {
"ssh-rsa",
"ssh-ed25519",
"ecdsa-sha2-nistp256",
Expand All @@ -192,7 +196,31 @@ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
"x509v3-ecdsa-sha2-nistp384",
"x509v3-ecdsa-sha2-nistp521",
#endif
#ifndef WOLFSSH_NO_MLDSA
#ifndef WOLFSSH_NO_MLDSA44
"ssh-mldsa-44",
#endif
#ifndef WOLFSSH_NO_MLDSA65
"ssh-mldsa-65",
#endif
#ifndef WOLFSSH_NO_MLDSA87
"ssh-mldsa-87",
#endif
#ifdef WOLFSSH_CERTS
#ifndef WOLFSSH_NO_MLDSA44
"x509v3-ssh-mldsa-44",
#endif
#ifndef WOLFSSH_NO_MLDSA65
"x509v3-ssh-mldsa-65",
#endif
#ifndef WOLFSSH_NO_MLDSA87
"x509v3-ssh-mldsa-87",
#endif
#endif
#endif
};
const int NUM_ALLOWED_TYPES =
(int)(sizeof(allowedTypes) / sizeof(allowedTypes[0]));
int typeOk = 0;
int i;

Expand Down
133 changes: 132 additions & 1 deletion examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,123 @@ static int load_key_ed25519(byte* buf, word32 bufSz)
#endif /* WOLFSSH_NO_ED25519 */


#ifndef WOLFSSH_NO_MLDSA44
static int load_key_mldsa44(byte* buf, word32 bufSz)
{
word32 sz = 0;
#ifndef NO_FILESYSTEM
sz = load_file("./keys/server-key-mldsa44.der", buf, &bufSz);
#else
Comment thread
stenslae marked this conversation as resolved.
(void)buf; (void)bufSz;
#endif
return sz;
}
#endif /* WOLFSSH_NO_MLDSA44 */

#ifndef WOLFSSH_NO_MLDSA65
static int load_key_mldsa65(byte* buf, word32 bufSz)
{
word32 sz = 0;
#ifndef NO_FILESYSTEM
sz = load_file("./keys/server-key-mldsa65.der", buf, &bufSz);
#else
(void)buf; (void)bufSz;
#endif
return sz;
}
#endif /* WOLFSSH_NO_MLDSA65 */

#ifndef WOLFSSH_NO_MLDSA87
static int load_key_mldsa87(byte* buf, word32 bufSz)
{
word32 sz = 0;
#ifndef NO_FILESYSTEM
sz = load_file("./keys/server-key-mldsa87.der", buf, &bufSz);
#else
(void)buf; (void)bufSz;
#endif
return sz;
}
#endif /* WOLFSSH_NO_MLDSA87 */


#ifndef WOLFSSH_NO_MLDSA
static int LoadMlDsaHostKeys(WOLFSSH_CTX* ctx, const char* keyList)
{
byte* mldsaBuf;
int loaded = 0;

mldsaBuf = (byte*)WMALLOC(MLDSA_MAX_BOTH_KEY_DER_SIZE, NULL, 0);
if (mldsaBuf == NULL) {
fprintf(stderr, "Couldn't allocate ML-DSA key load buffer.\n");
return -1;
}

#ifndef WOLFSSH_NO_MLDSA44
if (WSTRSTR(keyList, "mldsa-44") != NULL) {
int mldsaSz = load_key_mldsa44(mldsaBuf, MLDSA_MAX_BOTH_KEY_DER_SIZE);
if (mldsaSz <= 0) {
fprintf(stderr, "Couldn't load ML-DSA-44 key file.\n");
WFREE(mldsaBuf, NULL, 0);
return -1;
}
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, mldsaBuf, (word32)mldsaSz,
WOLFSSH_FORMAT_ASN1) < 0) {
fprintf(stderr, "Couldn't use ML-DSA-44 key buffer.\n");
WFREE(mldsaBuf, NULL, 0);
return -1;
}
loaded++;
}
#endif /* WOLFSSH_NO_MLDSA44 */

#ifndef WOLFSSH_NO_MLDSA65
if (WSTRSTR(keyList, "mldsa-65") != NULL) {
int mldsaSz = load_key_mldsa65(mldsaBuf, MLDSA_MAX_BOTH_KEY_DER_SIZE);
if (mldsaSz <= 0) {
fprintf(stderr, "Couldn't load ML-DSA-65 key file.\n");
WFREE(mldsaBuf, NULL, 0);
return -1;
}
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, mldsaBuf, (word32)mldsaSz,
WOLFSSH_FORMAT_ASN1) < 0) {
fprintf(stderr, "Couldn't use ML-DSA-65 key buffer.\n");
WFREE(mldsaBuf, NULL, 0);
return -1;
}
loaded++;
}
#endif /* WOLFSSH_NO_MLDSA65 */

#ifndef WOLFSSH_NO_MLDSA87
if (WSTRSTR(keyList, "mldsa-87") != NULL) {
int mldsaSz = load_key_mldsa87(mldsaBuf, MLDSA_MAX_BOTH_KEY_DER_SIZE);
if (mldsaSz <= 0) {
fprintf(stderr, "Couldn't load ML-DSA-87 key file.\n");
WFREE(mldsaBuf, NULL, 0);
return -1;
}
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, mldsaBuf, (word32)mldsaSz,
WOLFSSH_FORMAT_ASN1) < 0) {
fprintf(stderr, "Couldn't use ML-DSA-87 key buffer.\n");
WFREE(mldsaBuf, NULL, 0);
return -1;
}
loaded++;
}
#endif /* WOLFSSH_NO_MLDSA87 */

WFREE(mldsaBuf, NULL, 0);
if (loaded == 0) {
fprintf(stderr, "ML-DSA key list '%s' matched no supported level.\n",
keyList);
return -1;
}
return 0;
}
#endif /* WOLFSSH_NO_MLDSA */


typedef struct StrList {
const char* str;
struct StrList* next;
Expand Down Expand Up @@ -3108,7 +3225,8 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
if (tpmHostKeyPath != NULL) {
if (EchoserverInitTpmHostKey(ctx, tpmHostKeyPath,
ECHOSERVER_TPM_KEY_AUTH_DEFAULT) != 0) {
ES_ERROR("Couldn't load TPM host key from %s.\n", tpmHostKeyPath);
ES_ERROR("Couldn't load TPM host key from %s.\n",
tpmHostKeyPath);
}
loadDefaultHostKeys = 0;
}
Expand Down Expand Up @@ -3151,6 +3269,19 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
#endif /* WOLFSSH_NO_ED25519 */
}

/* Load only the ML-DSA levels requested in keyList; loading all levels
* unconditionally would force mldsa negotiation on non-mldsa tests. */
#ifndef WOLFSSH_NO_MLDSA
if (keyList != NULL && WSTRSTR(keyList, "mldsa") != NULL) {
if (LoadMlDsaHostKeys(ctx, keyList) != 0) {
#ifdef WOLFSSH_SMALL_STACK
WFREE(keyLoadBuf, NULL, 0);
#endif
ES_ERROR("Error loading ML-DSA host keys.\n");
}
}
#endif /* WOLFSSH_NO_MLDSA */

#ifndef NO_FILESYSTEM
if (userPubKey) {
byte* userBuf = NULL;
Expand Down
4 changes: 3 additions & 1 deletion keys/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ EXTRA_DIST+= \
keys/server-key.pem keys/fred-key.der keys/fred-key.pem \
keys/id_ecdsa keys/id_ecdsa.pub keys/id_rsa keys/id_rsa.pub \
keys/renewcerts.sh keys/renewcerts.cnf \
keys/server-key-ed25519.der keys/server-key-ed25519.pem
keys/server-key-ed25519.der keys/server-key-ed25519.pem \
keys/server-key-mldsa44.der keys/server-key-mldsa65.der \
keys/server-key-mldsa87.der

Binary file added keys/server-key-mldsa44.der
Binary file not shown.
Binary file added keys/server-key-mldsa65.der
Binary file not shown.
Binary file added keys/server-key-mldsa87.der
Binary file not shown.
Loading
Loading