Skip to content
Merged
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
36 changes: 36 additions & 0 deletions .github/workflows/os-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,39 @@ jobs:
set -o pipefail
LD_LIBRARY_PATH=${{ github.workspace }}/build-dir/lib ./tests/unit.test | tee unit-test.log
grep "ScpSendCallback_ExactFitBuffer: SUCCESS" unit-test.log

build_wolfssh_no_rsa_ecc:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest ]
name: Build and test wolfssh without RSA or ECC
runs-on: ${{ matrix.os }}
timeout-minutes: 16
steps:
# Not the cached --enable-all wolfSSL: this job needs one with no RSA
# and no ECC. Ed25519 is then the only signing algorithm wolfSSH has,
# and wolfssh/internal.h wants WOLFSSL_ED25519_STREAMING_VERIFY with
# it, so --enable-ed25519-stream is required, not optional. The ssh
# client app needs Base64_Encode_NoNl(), which nothing else in this
# short list pulls in, hence --enable-base64encode.
- name: Checkout, build, and install wolfssl
uses: wolfSSL/actions-build-autotools-project@v1
with:
repository: wolfssl/wolfssl
ref: master
path: wolfssl
configure: >-
--enable-ssh --enable-ed25519 --enable-ed25519-stream
--enable-curve25519 --enable-aesctr --enable-base64encode
--disable-rsa --disable-ecc --disable-dsa --disable-mldsa
check: false
install: true

- name: Checkout, build, and test wolfssh
uses: wolfSSL/actions-build-autotools-project@v1
with:
repository: wolfssl/wolfssh
path: wolfssh
configure: --enable-keyboard-interactive --enable-sftp --enable-scp --enable-sshclient LDFLAGS="-L${{ github.workspace }}/build-dir/lib" CPPFLAGS="-I${{ github.workspace }}/build-dir/include"
check: true
60 changes: 53 additions & 7 deletions examples/client/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ static const unsigned int hanselPrivateRsaSz = 1191;
#endif


#ifndef WOLFSSH_NO_ECC
#ifndef WOLFSSH_NO_ECDSA
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
static const char* hanselPublicEcc =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA"
Expand Down Expand Up @@ -275,6 +275,23 @@ static const unsigned int hanselPrivateEccSz = 223;
#endif
#endif

/* The pair in keys/hansel-key-ed25519.*, the only built-in user key left
* when both RSA and ECDSA are compiled out. */
#if defined(WOLFSSH_NO_RSA) && defined(WOLFSSH_NO_ECDSA) && \
!defined(WOLFSSH_NO_ED25519)
static const char* hanselPublicEd25519 =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHTSoBZIJBO2V0Jb2OWyMWNbkD"
"d6ReDfKxnrAPlbPuCe hansel";
static const byte hanselPrivateEd25519[] = {
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
0x04, 0x22, 0x04, 0x20, 0x28, 0xc6, 0xe9, 0xd8, 0x37, 0x4d, 0x0c, 0x52,
0x7e, 0x5f, 0xb3, 0x4c, 0x81, 0xe8, 0x68, 0xee, 0xc9, 0x7c, 0xad, 0x00,
0xad, 0xa0, 0xe3, 0xe2, 0x13, 0x06, 0x55, 0xf1, 0x17, 0xf1, 0x0a, 0xf0
};
static const unsigned int hanselPrivateEd25519Sz =
(unsigned int)sizeof(hanselPrivateEd25519);
#endif


#if defined(WOLFSSH_CERTS)

Expand Down Expand Up @@ -476,6 +493,13 @@ int ClientUserAuth(byte authType,
if (authType == WOLFSSH_USERAUTH_PUBLICKEY) {
WS_UserAuthData_PublicKey* pk = &authData->sf.publicKey;

if (userPublicKeyType == NULL || userPublicKeySz == 0) {
/* Nothing to sign with. SendUserAuthRequest() turns this
* into WS_FATAL_ERROR rather than putting an untyped
* publickey request on the wire. */
return WOLFSSH_USERAUTH_FAILURE;
}

pk->publicKeyType = userPublicKeyType;
pk->publicKeyTypeSz = userPublicKeyTypeSz;
pk->publicKey = userPublicKey;
Expand Down Expand Up @@ -1008,16 +1032,25 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc,
(void)tpmKeyAuth; /* Not used */

if (privKeyName == NULL) {
#if defined(WOLFSSH_NO_RSA) && defined(WOLFSSH_NO_ECC)
#if defined(WOLFSSH_NO_RSA) && defined(WOLFSSH_NO_ECDSA)
(void)userEcc;
#ifndef WOLFSSH_NO_ED25519
userPrivateKeySz = sizeof(userPrivateKeyBuf);
ret = wolfSSH_ReadKey_buffer(hanselPrivateEd25519,
hanselPrivateEd25519Sz, WOLFSSH_FORMAT_ASN1,
&userPrivateKey, &userPrivateKeySz,
&userPrivateKeyType, &userPrivateKeyTypeSz, heap);
isPrivate = 1;
#else
/* No built-in key to load. Leave the client to authenticate
* some other way rather than failing here. */
userPrivateKeySz = 0;
userPrivateKeyType = NULL;
(void)userEcc;
(void)heap;
#endif
#else
if (userEcc) {
#ifndef WOLFSSH_NO_ECC
#ifndef WOLFSSH_NO_ECDSA
userPrivateKeySz = sizeof(userPrivateKeyBuf);
ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz,
WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz,
Expand Down Expand Up @@ -1080,19 +1113,32 @@ int ClientUsePubKey(const char* pubKeyName, int userEcc, void* heap)
int ret = 0;

if (pubKeyName == NULL) {
#if defined(WOLFSSH_NO_RSA) && defined(WOLFSSH_NO_ECC)
#if defined(WOLFSSH_NO_RSA) && defined(WOLFSSH_NO_ECDSA)
(void)userEcc;
#ifndef WOLFSSH_NO_ED25519
{
byte* p = userPublicKey;

userPublicKeySz = sizeof(userPublicKeyBuf);
ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEd25519,
(word32)strlen(hanselPublicEd25519), WOLFSSH_FORMAT_SSH,
&p, &userPublicKeySz,
&userPublicKeyType, &userPublicKeyTypeSz, heap);
isPrivate = 1;
}
#else
/* No built-in key to load. Leave the client to authenticate
* some other way rather than failing here. */
userPublicKeySz = 0;
userPublicKeyType = NULL;
(void)userEcc;
(void)heap;
#endif
#else
byte* p = userPublicKey;
userPublicKeySz = sizeof(userPublicKeyBuf);

if (userEcc) {
#ifndef WOLFSSH_NO_ECC
#ifndef WOLFSSH_NO_ECDSA
ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc,
(word32)strlen(hanselPublicEcc), WOLFSSH_FORMAT_SSH,
&p, &userPublicKeySz,
Expand Down
35 changes: 30 additions & 5 deletions examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -2284,6 +2284,7 @@ static int load_file(const char* fileName, byte* buf, word32* bufSz)
}
#endif /* NO_FILESYSTEM */

#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
#ifdef WOLFSSH_NO_ECDSA_SHA2_NISTP256
#define ECC_PATH "./keys/server-key-ecc-521.der"
#else
Expand Down Expand Up @@ -2319,6 +2320,7 @@ static int load_key(byte isEcc, byte* buf, word32 bufSz)

return sz;
}
#endif /* !WOLFSSH_NO_RSA || !WOLFSSH_NO_ECDSA */

#ifndef WOLFSSH_NO_ED25519
/* returns buffer size on success */
Expand Down Expand Up @@ -2659,7 +2661,7 @@ static const char samplePasswordBuffer[] =
"jack:fetchapail\n";


#ifndef WOLFSSH_NO_ECC
#ifndef WOLFSSH_NO_ECDSA
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
static const char samplePublicKeyEccBuffer[] =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA"
Expand Down Expand Up @@ -2703,6 +2705,17 @@ static const char samplePublicKeyEccBuffer[] =
#endif /* WOLFSSH_TPM */
#endif /* WOLFSSH_NO_RSA */

/* Ed25519 is the only signing algorithm left when neither RSA nor ECDSA
* is compiled in, so the server needs sample keys of its own. */
#if defined(WOLFSSH_NO_RSA) && defined(WOLFSSH_NO_ECDSA) && \
!defined(WOLFSSH_NO_ED25519)
static const char samplePublicKeyEd25519Buffer[] =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHTSoBZIJBO2V0Jb2OWyMWNbkD"
"d6ReDfKxnrAPlbPuCe hansel\n"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFD8Bwir++gzNJmif9ooAZdaRi"
"sFZjlp9XU2seaec7/m gretel\n";
#endif

#ifdef WOLFSSH_ALLOW_USERAUTH_NONE

static const char sampleNoneBuffer[] =
Expand Down Expand Up @@ -3882,12 +3895,14 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
userEcc = 1;
peerEcc = 1;
#endif
#ifdef WOLFSSH_NO_ECC
/* If wolfCrypt isn't built with ECC, force ECC off. */
#ifdef WOLFSSH_NO_ECDSA
/* If wolfCrypt isn't built with ECDSA, force ECC off. */
userEcc = 0;
peerEcc = 0;
#endif
(void)userEcc;
/* Only load_key() reads it, and that is RSA/ECDSA only. */
(void)peerEcc;

if (wolfSSH_Init() != WS_SUCCESS) {
ES_ERROR("Couldn't initialize wolfSSH.\n");
Expand Down Expand Up @@ -4130,6 +4145,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
#endif

if (loadDefaultHostKeys) {
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
bufSz = load_key(peerEcc, keyLoadBuf, bufSz);
if (bufSz == 0) {
#ifdef WOLFSSH_SMALL_STACK
Expand All @@ -4147,7 +4163,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
ES_ERROR("Couldn't use first key buffer.\n");
}

#if !defined(WOLFSSH_NO_RSA) && !defined(WOLFSSH_NO_ECC)
#if !defined(WOLFSSH_NO_RSA) && !defined(WOLFSSH_NO_ECDSA)
peerEcc = !peerEcc;
bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ;

Expand All @@ -4168,6 +4184,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
ES_ERROR("Couldn't use second key buffer.\n");
}
#endif
#endif /* !WOLFSSH_NO_RSA || !WOLFSSH_NO_ECDSA */

#ifndef WOLFSSH_NO_ED25519
bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ;
Expand Down Expand Up @@ -4300,8 +4317,15 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
keyLoadBuf[bufSz] = 0;
LoadPasswordBuffer(keyLoadBuf, bufSz, &pwMapList);

#if defined(WOLFSSH_NO_RSA) && defined(WOLFSSH_NO_ECDSA)
/* Ed25519 is the only sample key left, so -e has nothing to pick
* between and is ignored. */
#ifndef WOLFSSH_NO_ED25519
bufName = samplePublicKeyEd25519Buffer;
#endif
#else
if (userEcc) {
#ifndef WOLFSSH_NO_ECC
#ifndef WOLFSSH_NO_ECDSA
bufName = samplePublicKeyEccBuffer;
#endif
}
Expand All @@ -4314,6 +4338,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
#endif
#endif
}
#endif
if (bufName != NULL) {
bufSz = (word32)WSTRLEN(bufName);
WMEMCPY(keyLoadBuf, bufName, bufSz);
Expand Down
Binary file added keys/gretel-key-ed25519.der
Binary file not shown.
3 changes: 3 additions & 0 deletions keys/gretel-key-ed25519.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIEHo0C0B8ZM9j3mX6D3LLQLp+wHhvXG6eriviX8uBDhc
-----END PRIVATE KEY-----
1 change: 1 addition & 0 deletions keys/gretel-key-ed25519.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFD8Bwir++gzNJmif9ooAZdaRisFZjlp9XU2seaec7/m gretel
Binary file added keys/hansel-key-ed25519.der
Binary file not shown.
3 changes: 3 additions & 0 deletions keys/hansel-key-ed25519.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEICjG6dg3TQxSfl+zTIHoaO7JfK0AraDj4hMGVfEX8Qrw
-----END PRIVATE KEY-----
1 change: 1 addition & 0 deletions keys/hansel-key-ed25519.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHTSoBZIJBO2V0Jb2OWyMWNbkDd6ReDfKxnrAPlbPuCe hansel
2 changes: 2 additions & 0 deletions keys/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ EXTRA_DIST+= \
keys/gretel-key-ecc-384.der keys/gretel-key-ecc-384.pem keys/gretel-key-ecc-384.pub \
keys/gretel-key-ecc-521.der keys/gretel-key-ecc-521.pem keys/gretel-key-ecc-521.pub \
keys/gretel-key-rsa.der keys/gretel-key-rsa.pem keys/gretel-key-rsa.pub \
keys/hansel-key-ed25519.der keys/hansel-key-ed25519.pem keys/hansel-key-ed25519.pub \
keys/gretel-key-ed25519.der keys/gretel-key-ed25519.pem keys/gretel-key-ed25519.pub \
keys/pubkeys-ecc.txt keys/pubkeys-ecc-384.txt keys/pubkeys-ecc-521.txt \
keys/pubkeys-rsa.txt keys/passwd.txt keys/ca-cert-ecc.der \
keys/ca-cert-ecc.pem keys/ca-key-ecc.der keys/ca-key-ecc.pem \
Expand Down
63 changes: 56 additions & 7 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -6400,7 +6400,10 @@ int wcPrimeForId(byte id)
return ECC_CURVE_INVALID;
}
}
#endif /* !WOLFSSH_NO_ECDSA || !WOLFSSH_NO_ECDH */


#ifndef WOLFSSH_NO_ECDSA
static INLINE const char *PrimeNameForId(byte id)
{
switch (id) {
Expand Down Expand Up @@ -9228,10 +9231,14 @@ static int ValidateKexDhGexGroup(const byte* primeGroup, word32 primeGroupSz,
}
}

/* Safe prime check: q = (p - 1) / 2 must also be prime. */
/* Safe prime check: q = (p - 1) / 2 must also be prime. mp_rshb() rather
* than mp_div_2(): the latter is an ECC-only entry point in SP math, and
* q is positive here, so the shift is the same operation. */
if (ret == WS_SUCCESS) {
if (mp_sub_d(&p, 1, &q) != MP_OKAY || mp_div_2(&q, &q) != MP_OKAY)
if (mp_sub_d(&p, 1, &q) != MP_OKAY)
ret = WS_CRYPTO_FAILED;
else
mp_rshb(&q, 1);
}
if (ret == WS_SUCCESS) {
isPrime = MP_NO;
Expand Down Expand Up @@ -11644,6 +11651,9 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
}
}

/* Only the RSA and ECDSA arms above read the digest size;
* with both compiled out the switch is just the default. */
WOLFSSH_UNUSED(digestSz);
WS_FORCEZERO(digest, sizeof(digest));
}

Expand Down Expand Up @@ -15732,6 +15742,8 @@ static int SendKexGetSigningKey(WOLFSSH* ssh,


heap = ssh->ctx->heap;
/* Only the RSA, ECDSA and ML-DSA arms allocate; Ed25519 does not. */
WOLFSSH_UNUSED(heap);

#ifdef WOLFSSH_TPM
ssh->handshake->useTpm = ssh->ctx->privateKey[keyIdx].isTpm;
Expand Down Expand Up @@ -16049,6 +16061,7 @@ static int SendKexGetSigningKey(WOLFSSH* ssh,
sigKeyBlock_ptr->sk.ecc.qSz);
}
break;
#endif /* WOLFSSH_NO_ECDSA */

#ifndef WOLFSSH_NO_ED25519
case ID_ED25519:
Expand Down Expand Up @@ -16117,8 +16130,7 @@ static int SendKexGetSigningKey(WOLFSSH* ssh,
sigKeyBlock_ptr->sk.ed.q,
sigKeyBlock_ptr->sk.ed.qSz);
break;
#endif
#endif
#endif /* WOLFSSH_NO_ED25519 */

#ifndef WOLFSSH_NO_MLDSA
#ifdef WOLFSSH_CERTS
Expand Down Expand Up @@ -21285,9 +21297,46 @@ static int PrepareUserAuthRequestEd25519(WOLFSSH* ssh, word32* payloadSz,
else
#endif
{
ret = GetOpenSshKey(keySig,
authData->sf.publicKey.privateKey,
authData->sf.publicKey.privateKeySz, &idx);
int derRet;

/* As in the RSA and ECDSA paths, try DER first and fall back to
* the OpenSSH container. Only a decode failure falls back; a
* derive failure keeps its own error. */
derRet = wc_Ed25519PrivateKeyDecode(
authData->sf.publicKey.privateKey, &idx,
&keySig->ks.ed25519.key,
authData->sf.publicKey.privateKeySz);

if (derRet != 0) {
idx = 0;
ret = GetOpenSshKey(keySig,
authData->sf.publicKey.privateKey,
authData->sf.publicKey.privateKeySz, &idx);
}
else {
ret = WS_SUCCESS;

if (!keySig->ks.ed25519.key.pubKeySet) {
#ifdef HAVE_ED25519_MAKE_KEY
/* Priv-only DER: derive the public key from the seed,
* the way SendKexGetSigningKey() does for a host key. */
byte q[ED25519_PUB_KEY_SIZE];

ret = wc_ed25519_make_public(&keySig->ks.ed25519.key,
q, (word32)sizeof(q));
if (ret == 0) {
/* trusted=1: q came from this key's own scalar. */
ret = wc_ed25519_import_public_ex(q,
ED25519_PUB_KEY_SIZE,
&keySig->ks.ed25519.key, 1);
}
#else
/* Nothing to derive it with; reject here rather than
* failing inside wc_ed25519_sign_msg(). */
ret = WS_KEY_FORMAT_E;
#endif /* HAVE_ED25519_MAKE_KEY */
}
}
}
}

Expand Down
Loading
Loading