Skip to content

Commit

Permalink
Merge pull request #660 from anhu/Curve25519
Browse files Browse the repository at this point in the history
Add Curve25519 KEX support.
  • Loading branch information
ejohnstown authored Mar 14, 2024
2 parents b798f63 + 3610e2b commit 84bc8b1
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 5 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,25 @@ behavior, give the echoserver the command line option `-f`.

$ ./examples/echoserver/echoserver -f

CURVE25519
==========

wolfSSH now supports Curve25519 for key exchange. To enable this support simply
compile wolfSSL with support for wolfssh and Curve25519.

$ cd wolfssl
$ ./configure --enable-wolfssh --enable-curve25519

After building and installing wolfSSL, you can simply configure with no options.

$ cd wolfssh
$ ./configure

The wolfSSH client and server will automatically negotiate using Curve25519.

$ ./examples/echoserver/echoserver -f

$ ./examples/client/client -u jill -P upthehill

POST-QUANTUM
============
Expand Down
161 changes: 157 additions & 4 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@
algorithms off.
WOLFSSH_KEY_QUANTITY_REQ
Number of keys required to be in an OpenSSH-style key wrapper.
WOLFSSH_NO_CURVE25519_SHA256
Set when Curve25519 or SHA2-256 are disabled in wolfSSL. Set to disable use
of Curve25519 key exchange.
*/

static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv"
Expand Down Expand Up @@ -571,6 +574,9 @@ static const char cannedKexAlgoNames[] =
#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
"[email protected],"
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
"curve25519-sha256,"
#endif
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521)
"ecdh-sha2-nistp521,"
#endif
Expand Down Expand Up @@ -2148,6 +2154,10 @@ static const NameIdPair NameIdMap[] = {
/* We use kyber-512 here to achieve interop with OQS's fork. */
{ ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256, TYPE_KEX,
"[email protected]" },
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
/* See RFC 8731 */
{ ID_CURVE25519_SHA256, TYPE_KEX, "curve25519-sha256" },
#endif
{ ID_EXTINFO_S, TYPE_OTHER, "ext-info-s" },
{ ID_EXTINFO_C, TYPE_OTHER, "ext-info-c" },
Expand Down Expand Up @@ -3374,6 +3384,10 @@ static INLINE enum wc_HashType HashForId(byte id)
case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
return WC_HASH_TYPE_SHA256;
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
case ID_CURVE25519_SHA256:
return WC_HASH_TYPE_SHA256;
#endif
#ifndef WOLFSSH_NO_RSA_SHA2_256
case ID_RSA_SHA2_256:
return WC_HASH_TYPE_SHA256;
Expand Down Expand Up @@ -3439,6 +3453,10 @@ static INLINE int wcPrimeForId(byte id)
case ID_ECDSA_SHA2_NISTP384:
return ECC_SECP384R1;
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
case ID_CURVE25519_SHA256:
return ECC_X25519;
#endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
case ID_ECDH_SHA2_NISTP521:
return ECC_SECP521R1;
Expand Down Expand Up @@ -4668,6 +4686,9 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
if (!ssh->handshake->useEcc
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !ssh->handshake->useEccKyber
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
&& !ssh->handshake->useCurve25519
#endif
) {
#ifndef WOLFSSH_NO_DH
Expand Down Expand Up @@ -4712,6 +4733,37 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
ret = WS_INVALID_ALGO_ID;
#endif
}
#ifndef WOLFSSH_NO_CURVE25519_SHA256
else if (ssh->handshake->useCurve25519) {
curve25519_key pub;
ret = wc_curve25519_init(&pub);

if (ret == 0)
ret = wc_curve25519_check_public(f, fSz,
EC25519_LITTLE_ENDIAN);

if (ret == 0) {
ret = wc_curve25519_import_public_ex(f, fSz, &pub,
EC25519_LITTLE_ENDIAN);
}

if (ret == 0) {
PRIVATE_KEY_UNLOCK();
ret = wc_curve25519_shared_secret_ex(
&ssh->handshake->privKey.curve25519, &pub,
ssh->k, &ssh->kSz, EC25519_LITTLE_ENDIAN);
PRIVATE_KEY_LOCK();
}

wc_curve25519_free(&pub);
wc_curve25519_free(&ssh->handshake->privKey.curve25519);

if (ret != 0) {
WLOG(WS_LOG_ERROR,
"Gen curve25519 shared secret failed, %d", ret);
}
}
#endif /* !WOLFSSH_NO_CURVE25519_SHA256 */
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
else if (ssh->handshake->useEccKyber) {
/* This is a a hybrid of ECDHE and a post-quantum KEM. In this
Expand Down Expand Up @@ -4954,8 +5006,8 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)

if (ret == WS_SUCCESS) {
int useKeyPadding = 1;
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
useKeyPadding = !ssh->handshake->useEccKyber;
#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
doKeyPadding = !ssh->handshake->useEccKyber;
#endif
ret = GenerateKeys(ssh, hashId, useKeyPadding);
}
Expand Down Expand Up @@ -9775,6 +9827,9 @@ int SendKexDhReply(WOLFSSH* ssh)
byte useEccKyber = 0;
byte sharedSecretHashSz = 0;
byte *sharedSecretHash = NULL;
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
byte useCurve25519 = 0;
#endif
byte fPad = 0;
byte kPad = 0;
Expand Down Expand Up @@ -9858,6 +9913,12 @@ int SendKexDhReply(WOLFSSH* ssh)
msgId = MSGID_KEXDH_REPLY;
break;
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
case ID_CURVE25519_SHA256:
useCurve25519 = 1;
msgId = MSGID_KEXDH_REPLY;
break;
#endif
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
useEccKyber = 1; /* Only support level 1 for now. */
Expand Down Expand Up @@ -9901,6 +9962,9 @@ int SendKexDhReply(WOLFSSH* ssh)
if (!useEcc
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !useEccKyber
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
&& !useCurve25519
#endif
) {
#ifndef WOLFSSH_NO_DH
Expand Down Expand Up @@ -10016,6 +10080,62 @@ int SendKexDhReply(WOLFSSH* ssh)
#endif
#endif /* !defined(WOLFSSH_NO_ECDH) */
}
#ifndef WOLFSSH_NO_CURVE25519_SHA256
if (useCurve25519) {
#ifdef WOLFSSH_SMALL_STACK
curve25519_key *pubKey = NULL, *privKey = NULL;
pubKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key),
heap, DYNTYPE_PUBKEY);
privKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key),
heap, DYNTYPE_PRIVKEY);
if (pubKey == NULL || privKey == NULL) {
ret = WS_MEMORY_E;
}
#else
curve25519_key pubKey[1], privKey[1];
#endif

if (ret == 0)
ret = wc_curve25519_init_ex(pubKey, heap,
INVALID_DEVID);
if (ret == 0)
ret = wc_curve25519_init_ex(privKey, heap,
INVALID_DEVID);
if (ret == 0)
ret = wc_curve25519_check_public(ssh->handshake->e,
ssh->handshake->eSz, EC25519_LITTLE_ENDIAN);
if (ret == 0)
ret = wc_curve25519_import_public_ex(
ssh->handshake->e, ssh->handshake->eSz,
pubKey, EC25519_LITTLE_ENDIAN);

if (ret == 0)
ret = wc_curve25519_make_key(ssh->rng,
CURVE25519_KEYSIZE, privKey);

if (ret == 0) {
PRIVATE_KEY_UNLOCK();
ret = wc_curve25519_export_public_ex(privKey,
f_ptr, &fSz, EC25519_LITTLE_ENDIAN);
PRIVATE_KEY_LOCK();
}

if (ret == 0) {
PRIVATE_KEY_UNLOCK();
ret = wc_curve25519_shared_secret_ex(privKey, pubKey,
ssh->k, &ssh->kSz, EC25519_LITTLE_ENDIAN);
PRIVATE_KEY_LOCK();
}
wc_curve25519_free(privKey);
wc_curve25519_free(pubKey);
#ifdef WOLFSSH_SMALL_STACK
WFREE(pubKey, heap, DYNTYPE_PUBKEY);
WFREE(privKey, heap, DYNTYPE_PRIVKEY);
pubKey = NULL;
privKey = NULL;
#endif
}
#endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
else if (useEccKyber) {
/* This is a hybrid KEM. In this case, I need to generate my ECC
Expand Down Expand Up @@ -10163,6 +10283,9 @@ int SendKexDhReply(WOLFSSH* ssh)
if (ret == 0
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !useEccKyber
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
&& !useCurve25519
#endif
) {
ret = CreateMpint(f_ptr, &fSz, &fPad);
Expand Down Expand Up @@ -10380,8 +10503,8 @@ int SendKexDhReply(WOLFSSH* ssh)

if (ret == WS_SUCCESS) {
int doKeyPadding = 1;
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
doKeyPadding = !useEccKyber;
#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
doKeyPadding = !ssh->handshake->useEccKyber;
#endif
ret = GenerateKeys(ssh, hashId, doKeyPadding);
}
Expand Down Expand Up @@ -10795,6 +10918,12 @@ int SendKexDhInit(WOLFSSH* ssh)
msgId = MSGID_KEXECDH_INIT;
break;
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
case ID_CURVE25519_SHA256:
ssh->handshake->useCurve25519 = 1;
msgId = MSGID_KEXECDH_INIT;
break;
#endif
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
/* Only support level 1 for now. */
Expand All @@ -10813,6 +10942,9 @@ int SendKexDhInit(WOLFSSH* ssh)
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !ssh->handshake->useEccKyber
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
&& !ssh->handshake->useCurve25519
#endif
) {
#ifndef WOLFSSH_NO_DH
DhKey* privKey = &ssh->handshake->privKey.dh;
Expand All @@ -10828,6 +10960,23 @@ int SendKexDhInit(WOLFSSH* ssh)
e, &eSz);
#endif
}
#ifndef WOLFSSH_NO_CURVE25519_SHA256
else if (ssh->handshake->useCurve25519) {
curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
if (ret == 0)
ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
INVALID_DEVID);
if (ret == 0)
ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE,
privKey);
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
ret = wc_curve25519_export_public_ex(privKey, e, &eSz,
EC25519_LITTLE_ENDIAN);
PRIVATE_KEY_LOCK();
}
}
#endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */
else if (ssh->handshake->useEcc
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
|| ssh->handshake->useEccKyber
Expand Down Expand Up @@ -10899,6 +11048,10 @@ int SendKexDhInit(WOLFSSH* ssh)
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !ssh->handshake->useEccKyber
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
&& !ssh->handshake->useCurve25519
#endif

) {
ret = CreateMpint(e, &eSz, &ePad);
}
Expand Down
17 changes: 16 additions & 1 deletion wolfssh/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <wolfssl/wolfcrypt/dh.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfssl/wolfcrypt/rsa.h>
#include <wolfssl/wolfcrypt/curve25519.h>
#ifdef WOLFSSH_SCP
#include <wolfssh/wolfscp.h>
#endif
Expand Down Expand Up @@ -160,6 +161,10 @@ extern "C" {
#undef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
#define WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
#endif
#if !defined(HAVE_CURVE25519) || defined(NO_SHA256)
#undef WOLFSSH_NO_CURVE25519_SHA256
#define WOLFSSH_NO_CURVE25519_SHA256
#endif

#if defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \
defined(WOLFSSH_NO_DH_GROUP14_SHA1) && \
Expand All @@ -168,7 +173,8 @@ extern "C" {
defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \
defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \
defined(WOLFSSH_NO_ECDH_SHA2_ED25519) && \
defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256) && \
defined(WOLFSSH_NO_CURVE25519_SHA256)
#error "You need at least one key agreement algorithm."
#endif

Expand Down Expand Up @@ -307,6 +313,9 @@ enum {
ID_DH_GROUP14_SHA256,
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
ID_CURVE25519_SHA256,
#endif
ID_EXTINFO_S, /* Pseudo-KEX to indicate server extensions. */
ID_EXTINFO_C, /* Pseudo-KEX to indicate client extensions. */
Expand Down Expand Up @@ -578,13 +587,19 @@ typedef struct HandshakeInfo {
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
byte useEccKyber;
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
byte useCurve25519;
#endif

union {
#ifndef WOLFSSH_NO_DH
DhKey dh;
#endif
#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH)
ecc_key ecc;
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
curve25519_key curve25519;
#endif
} privKey;
} HandshakeInfo;
Expand Down

0 comments on commit 84bc8b1

Please sign in to comment.