Skip to content

Commit 84bc8b1

Browse files
authored
Merge pull request #660 from anhu/Curve25519
Add Curve25519 KEX support.
2 parents b798f63 + 3610e2b commit 84bc8b1

File tree

3 files changed

+192
-5
lines changed

3 files changed

+192
-5
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,25 @@ behavior, give the echoserver the command line option `-f`.
401401

402402
$ ./examples/echoserver/echoserver -f
403403

404+
CURVE25519
405+
==========
406+
407+
wolfSSH now supports Curve25519 for key exchange. To enable this support simply
408+
compile wolfSSL with support for wolfssh and Curve25519.
409+
410+
$ cd wolfssl
411+
$ ./configure --enable-wolfssh --enable-curve25519
412+
413+
After building and installing wolfSSL, you can simply configure with no options.
414+
415+
$ cd wolfssh
416+
$ ./configure
417+
418+
The wolfSSH client and server will automatically negotiate using Curve25519.
419+
420+
$ ./examples/echoserver/echoserver -f
421+
422+
$ ./examples/client/client -u jill -P upthehill
404423

405424
POST-QUANTUM
406425
============

src/internal.c

Lines changed: 157 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@
148148
algorithms off.
149149
WOLFSSH_KEY_QUANTITY_REQ
150150
Number of keys required to be in an OpenSSH-style key wrapper.
151+
WOLFSSH_NO_CURVE25519_SHA256
152+
Set when Curve25519 or SHA2-256 are disabled in wolfSSL. Set to disable use
153+
of Curve25519 key exchange.
151154
*/
152155

153156
static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv"
@@ -571,6 +574,9 @@ static const char cannedKexAlgoNames[] =
571574
#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
572575
"ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org,"
573576
#endif
577+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
578+
"curve25519-sha256,"
579+
#endif
574580
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521)
575581
"ecdh-sha2-nistp521,"
576582
#endif
@@ -2148,6 +2154,10 @@ static const NameIdPair NameIdMap[] = {
21482154
/* We use kyber-512 here to achieve interop with OQS's fork. */
21492155
{ ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256, TYPE_KEX,
21502156
"ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org" },
2157+
#endif
2158+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
2159+
/* See RFC 8731 */
2160+
{ ID_CURVE25519_SHA256, TYPE_KEX, "curve25519-sha256" },
21512161
#endif
21522162
{ ID_EXTINFO_S, TYPE_OTHER, "ext-info-s" },
21532163
{ ID_EXTINFO_C, TYPE_OTHER, "ext-info-c" },
@@ -3374,6 +3384,10 @@ static INLINE enum wc_HashType HashForId(byte id)
33743384
case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
33753385
return WC_HASH_TYPE_SHA256;
33763386
#endif
3387+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
3388+
case ID_CURVE25519_SHA256:
3389+
return WC_HASH_TYPE_SHA256;
3390+
#endif
33773391
#ifndef WOLFSSH_NO_RSA_SHA2_256
33783392
case ID_RSA_SHA2_256:
33793393
return WC_HASH_TYPE_SHA256;
@@ -3439,6 +3453,10 @@ static INLINE int wcPrimeForId(byte id)
34393453
case ID_ECDSA_SHA2_NISTP384:
34403454
return ECC_SECP384R1;
34413455
#endif
3456+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
3457+
case ID_CURVE25519_SHA256:
3458+
return ECC_X25519;
3459+
#endif
34423460
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
34433461
case ID_ECDH_SHA2_NISTP521:
34443462
return ECC_SECP521R1;
@@ -4668,6 +4686,9 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
46684686
if (!ssh->handshake->useEcc
46694687
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
46704688
&& !ssh->handshake->useEccKyber
4689+
#endif
4690+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
4691+
&& !ssh->handshake->useCurve25519
46714692
#endif
46724693
) {
46734694
#ifndef WOLFSSH_NO_DH
@@ -4712,6 +4733,37 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
47124733
ret = WS_INVALID_ALGO_ID;
47134734
#endif
47144735
}
4736+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
4737+
else if (ssh->handshake->useCurve25519) {
4738+
curve25519_key pub;
4739+
ret = wc_curve25519_init(&pub);
4740+
4741+
if (ret == 0)
4742+
ret = wc_curve25519_check_public(f, fSz,
4743+
EC25519_LITTLE_ENDIAN);
4744+
4745+
if (ret == 0) {
4746+
ret = wc_curve25519_import_public_ex(f, fSz, &pub,
4747+
EC25519_LITTLE_ENDIAN);
4748+
}
4749+
4750+
if (ret == 0) {
4751+
PRIVATE_KEY_UNLOCK();
4752+
ret = wc_curve25519_shared_secret_ex(
4753+
&ssh->handshake->privKey.curve25519, &pub,
4754+
ssh->k, &ssh->kSz, EC25519_LITTLE_ENDIAN);
4755+
PRIVATE_KEY_LOCK();
4756+
}
4757+
4758+
wc_curve25519_free(&pub);
4759+
wc_curve25519_free(&ssh->handshake->privKey.curve25519);
4760+
4761+
if (ret != 0) {
4762+
WLOG(WS_LOG_ERROR,
4763+
"Gen curve25519 shared secret failed, %d", ret);
4764+
}
4765+
}
4766+
#endif /* !WOLFSSH_NO_CURVE25519_SHA256 */
47154767
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
47164768
else if (ssh->handshake->useEccKyber) {
47174769
/* This is a a hybrid of ECDHE and a post-quantum KEM. In this
@@ -4954,8 +5006,8 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
49545006

49555007
if (ret == WS_SUCCESS) {
49565008
int useKeyPadding = 1;
4957-
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
4958-
useKeyPadding = !ssh->handshake->useEccKyber;
5009+
#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
5010+
doKeyPadding = !ssh->handshake->useEccKyber;
49595011
#endif
49605012
ret = GenerateKeys(ssh, hashId, useKeyPadding);
49615013
}
@@ -9775,6 +9827,9 @@ int SendKexDhReply(WOLFSSH* ssh)
97759827
byte useEccKyber = 0;
97769828
byte sharedSecretHashSz = 0;
97779829
byte *sharedSecretHash = NULL;
9830+
#endif
9831+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
9832+
byte useCurve25519 = 0;
97789833
#endif
97799834
byte fPad = 0;
97809835
byte kPad = 0;
@@ -9858,6 +9913,12 @@ int SendKexDhReply(WOLFSSH* ssh)
98589913
msgId = MSGID_KEXDH_REPLY;
98599914
break;
98609915
#endif
9916+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
9917+
case ID_CURVE25519_SHA256:
9918+
useCurve25519 = 1;
9919+
msgId = MSGID_KEXDH_REPLY;
9920+
break;
9921+
#endif
98619922
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
98629923
case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
98639924
useEccKyber = 1; /* Only support level 1 for now. */
@@ -9901,6 +9962,9 @@ int SendKexDhReply(WOLFSSH* ssh)
99019962
if (!useEcc
99029963
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
99039964
&& !useEccKyber
9965+
#endif
9966+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
9967+
&& !useCurve25519
99049968
#endif
99059969
) {
99069970
#ifndef WOLFSSH_NO_DH
@@ -10016,6 +10080,62 @@ int SendKexDhReply(WOLFSSH* ssh)
1001610080
#endif
1001710081
#endif /* !defined(WOLFSSH_NO_ECDH) */
1001810082
}
10083+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
10084+
if (useCurve25519) {
10085+
#ifdef WOLFSSH_SMALL_STACK
10086+
curve25519_key *pubKey = NULL, *privKey = NULL;
10087+
pubKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key),
10088+
heap, DYNTYPE_PUBKEY);
10089+
privKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key),
10090+
heap, DYNTYPE_PRIVKEY);
10091+
if (pubKey == NULL || privKey == NULL) {
10092+
ret = WS_MEMORY_E;
10093+
}
10094+
#else
10095+
curve25519_key pubKey[1], privKey[1];
10096+
#endif
10097+
10098+
if (ret == 0)
10099+
ret = wc_curve25519_init_ex(pubKey, heap,
10100+
INVALID_DEVID);
10101+
if (ret == 0)
10102+
ret = wc_curve25519_init_ex(privKey, heap,
10103+
INVALID_DEVID);
10104+
if (ret == 0)
10105+
ret = wc_curve25519_check_public(ssh->handshake->e,
10106+
ssh->handshake->eSz, EC25519_LITTLE_ENDIAN);
10107+
if (ret == 0)
10108+
ret = wc_curve25519_import_public_ex(
10109+
ssh->handshake->e, ssh->handshake->eSz,
10110+
pubKey, EC25519_LITTLE_ENDIAN);
10111+
10112+
if (ret == 0)
10113+
ret = wc_curve25519_make_key(ssh->rng,
10114+
CURVE25519_KEYSIZE, privKey);
10115+
10116+
if (ret == 0) {
10117+
PRIVATE_KEY_UNLOCK();
10118+
ret = wc_curve25519_export_public_ex(privKey,
10119+
f_ptr, &fSz, EC25519_LITTLE_ENDIAN);
10120+
PRIVATE_KEY_LOCK();
10121+
}
10122+
10123+
if (ret == 0) {
10124+
PRIVATE_KEY_UNLOCK();
10125+
ret = wc_curve25519_shared_secret_ex(privKey, pubKey,
10126+
ssh->k, &ssh->kSz, EC25519_LITTLE_ENDIAN);
10127+
PRIVATE_KEY_LOCK();
10128+
}
10129+
wc_curve25519_free(privKey);
10130+
wc_curve25519_free(pubKey);
10131+
#ifdef WOLFSSH_SMALL_STACK
10132+
WFREE(pubKey, heap, DYNTYPE_PUBKEY);
10133+
WFREE(privKey, heap, DYNTYPE_PRIVKEY);
10134+
pubKey = NULL;
10135+
privKey = NULL;
10136+
#endif
10137+
}
10138+
#endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */
1001910139
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
1002010140
else if (useEccKyber) {
1002110141
/* This is a hybrid KEM. In this case, I need to generate my ECC
@@ -10163,6 +10283,9 @@ int SendKexDhReply(WOLFSSH* ssh)
1016310283
if (ret == 0
1016410284
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
1016510285
&& !useEccKyber
10286+
#endif
10287+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
10288+
&& !useCurve25519
1016610289
#endif
1016710290
) {
1016810291
ret = CreateMpint(f_ptr, &fSz, &fPad);
@@ -10380,8 +10503,8 @@ int SendKexDhReply(WOLFSSH* ssh)
1038010503

1038110504
if (ret == WS_SUCCESS) {
1038210505
int doKeyPadding = 1;
10383-
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
10384-
doKeyPadding = !useEccKyber;
10506+
#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
10507+
doKeyPadding = !ssh->handshake->useEccKyber;
1038510508
#endif
1038610509
ret = GenerateKeys(ssh, hashId, doKeyPadding);
1038710510
}
@@ -10795,6 +10918,12 @@ int SendKexDhInit(WOLFSSH* ssh)
1079510918
msgId = MSGID_KEXECDH_INIT;
1079610919
break;
1079710920
#endif
10921+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
10922+
case ID_CURVE25519_SHA256:
10923+
ssh->handshake->useCurve25519 = 1;
10924+
msgId = MSGID_KEXECDH_INIT;
10925+
break;
10926+
#endif
1079810927
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
1079910928
case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
1080010929
/* Only support level 1 for now. */
@@ -10813,6 +10942,9 @@ int SendKexDhInit(WOLFSSH* ssh)
1081310942
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
1081410943
&& !ssh->handshake->useEccKyber
1081510944
#endif
10945+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
10946+
&& !ssh->handshake->useCurve25519
10947+
#endif
1081610948
) {
1081710949
#ifndef WOLFSSH_NO_DH
1081810950
DhKey* privKey = &ssh->handshake->privKey.dh;
@@ -10828,6 +10960,23 @@ int SendKexDhInit(WOLFSSH* ssh)
1082810960
e, &eSz);
1082910961
#endif
1083010962
}
10963+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
10964+
else if (ssh->handshake->useCurve25519) {
10965+
curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
10966+
if (ret == 0)
10967+
ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
10968+
INVALID_DEVID);
10969+
if (ret == 0)
10970+
ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE,
10971+
privKey);
10972+
if (ret == 0) {
10973+
PRIVATE_KEY_UNLOCK();
10974+
ret = wc_curve25519_export_public_ex(privKey, e, &eSz,
10975+
EC25519_LITTLE_ENDIAN);
10976+
PRIVATE_KEY_LOCK();
10977+
}
10978+
}
10979+
#endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */
1083110980
else if (ssh->handshake->useEcc
1083210981
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
1083310982
|| ssh->handshake->useEccKyber
@@ -10899,6 +11048,10 @@ int SendKexDhInit(WOLFSSH* ssh)
1089911048
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
1090011049
&& !ssh->handshake->useEccKyber
1090111050
#endif
11051+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
11052+
&& !ssh->handshake->useCurve25519
11053+
#endif
11054+
1090211055
) {
1090311056
ret = CreateMpint(e, &eSz, &ePad);
1090411057
}

wolfssh/internal.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <wolfssl/wolfcrypt/dh.h>
3838
#include <wolfssl/wolfcrypt/ecc.h>
3939
#include <wolfssl/wolfcrypt/rsa.h>
40+
#include <wolfssl/wolfcrypt/curve25519.h>
4041
#ifdef WOLFSSH_SCP
4142
#include <wolfssh/wolfscp.h>
4243
#endif
@@ -160,6 +161,10 @@ extern "C" {
160161
#undef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
161162
#define WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
162163
#endif
164+
#if !defined(HAVE_CURVE25519) || defined(NO_SHA256)
165+
#undef WOLFSSH_NO_CURVE25519_SHA256
166+
#define WOLFSSH_NO_CURVE25519_SHA256
167+
#endif
163168

164169
#if defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \
165170
defined(WOLFSSH_NO_DH_GROUP14_SHA1) && \
@@ -168,7 +173,8 @@ extern "C" {
168173
defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \
169174
defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \
170175
defined(WOLFSSH_NO_ECDH_SHA2_ED25519) && \
171-
defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
176+
defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256) && \
177+
defined(WOLFSSH_NO_CURVE25519_SHA256)
172178
#error "You need at least one key agreement algorithm."
173179
#endif
174180

@@ -307,6 +313,9 @@ enum {
307313
ID_DH_GROUP14_SHA256,
308314
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
309315
ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
316+
#endif
317+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
318+
ID_CURVE25519_SHA256,
310319
#endif
311320
ID_EXTINFO_S, /* Pseudo-KEX to indicate server extensions. */
312321
ID_EXTINFO_C, /* Pseudo-KEX to indicate client extensions. */
@@ -578,13 +587,19 @@ typedef struct HandshakeInfo {
578587
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
579588
byte useEccKyber;
580589
#endif
590+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
591+
byte useCurve25519;
592+
#endif
581593

582594
union {
583595
#ifndef WOLFSSH_NO_DH
584596
DhKey dh;
585597
#endif
586598
#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH)
587599
ecc_key ecc;
600+
#endif
601+
#ifndef WOLFSSH_NO_CURVE25519_SHA256
602+
curve25519_key curve25519;
588603
#endif
589604
} privKey;
590605
} HandshakeInfo;

0 commit comments

Comments
 (0)