ecc_p256-kyber_level interop with OQS OpenSSH

The implementation now complies with the following draft:
https://www.ietf.org/id/draft-kampanakis-curdle-ssh-pq-ke-01.html

We implement the method as defined by the following name:
ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org
pull/510/head
Anthony Hu 2023-02-28 15:32:42 -05:00
parent 54df2e99b8
commit 9b96f58442
4 changed files with 238 additions and 148 deletions

View File

@ -459,7 +459,7 @@ The following is sufficient for build and execution:
$ cd openssh-OQS-OpenSSH-snapshot-2021-08/ $ cd openssh-OQS-OpenSSH-snapshot-2021-08/
$ ./configure --with-liboqs-dir=/usr/local $ ./configure --with-liboqs-dir=/usr/local
$ make all $ make all
$ ./ssh -o"KexAlgorithms +ecdh-nistp256-kyber-512-sha256" \ $ ./ssh -o"KexAlgorithms=ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org" \
-o"PubkeyAcceptedAlgorithms +ssh-rsa" \ -o"PubkeyAcceptedAlgorithms +ssh-rsa" \
-o"HostkeyAlgorithms +ssh-rsa" \ -o"HostkeyAlgorithms +ssh-rsa" \
jill@localhost -p 22222 jill@localhost -p 22222

View File

@ -113,7 +113,7 @@ Flags:
WOLFSSH_NO_ECDSA_SHA2_NISTP521 WOLFSSH_NO_ECDSA_SHA2_NISTP521
Set when ECC or SHA2-512 are disabled. Set to disable use of ECDSA server Set when ECC or SHA2-512 are disabled. Set to disable use of ECDSA server
authentication with prime NISTP521. authentication with prime NISTP521.
WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
Set when there is no liboqs integration. Set to disable use of ECDHE with Set when there is no liboqs integration. Set to disable use of ECDHE with
prime NISTP256 hybridized with post-quantum Kyber Level1 KEM. prime NISTP256 hybridized with post-quantum Kyber Level1 KEM.
WOLFSSH_NO_AES_CBC WOLFSSH_NO_AES_CBC
@ -1194,7 +1194,8 @@ int GenerateKey(byte hashId, byte keyId,
byte* key, word32 keySz, byte* key, word32 keySz,
const byte* k, word32 kSz, const byte* k, word32 kSz,
const byte* h, word32 hSz, const byte* h, word32 hSz,
const byte* sessionId, word32 sessionIdSz) const byte* sessionId, word32 sessionIdSz,
byte doKeyPad)
{ {
word32 blocks, remainder; word32 blocks, remainder;
wc_HashAlg hash; wc_HashAlg hash;
@ -1220,7 +1221,10 @@ int GenerateKey(byte hashId, byte keyId,
return WS_BAD_ARGUMENT; return WS_BAD_ARGUMENT;
} }
if (k[0] & 0x80) kPad = 1; /* Data can be define as string and mpint. (see Section 5 of RFC4251).
* This padding is required in the case of an mpint, but not in the case of
* a string. */
if (doKeyPad && (k[0] & 0x80)) kPad = 1;
c32toa(kSz + kPad, kSzFlat); c32toa(kSz + kPad, kSzFlat);
blocks = keySz / digestSz; blocks = keySz / digestSz;
@ -1304,7 +1308,7 @@ int GenerateKey(byte hashId, byte keyId,
} }
static int GenerateKeys(WOLFSSH* ssh, byte hashId) static int GenerateKeys(WOLFSSH* ssh, byte hashId, byte doKeyPad)
{ {
Keys* cK = NULL; Keys* cK = NULL;
Keys* sK = NULL; Keys* sK = NULL;
@ -1327,36 +1331,37 @@ static int GenerateKeys(WOLFSSH* ssh, byte hashId)
ret = GenerateKey(hashId, 'A', ret = GenerateKey(hashId, 'A',
cK->iv, cK->ivSz, cK->iv, cK->ivSz,
ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz,
ssh->sessionId, ssh->sessionIdSz); ssh->sessionId, ssh->sessionIdSz, doKeyPad);
if (ret == WS_SUCCESS) if (ret == WS_SUCCESS)
ret = GenerateKey(hashId, 'B', ret = GenerateKey(hashId, 'B',
sK->iv, sK->ivSz, sK->iv, sK->ivSz,
ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz,
ssh->sessionId, ssh->sessionIdSz); ssh->sessionId, ssh->sessionIdSz, doKeyPad);
if (ret == WS_SUCCESS) if (ret == WS_SUCCESS)
ret = GenerateKey(hashId, 'C', ret = GenerateKey(hashId, 'C',
cK->encKey, cK->encKeySz, cK->encKey, cK->encKeySz,
ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz,
ssh->sessionId, ssh->sessionIdSz); ssh->sessionId, ssh->sessionIdSz, doKeyPad);
if (ret == WS_SUCCESS) if (ret == WS_SUCCESS)
ret = GenerateKey(hashId, 'D', ret = GenerateKey(hashId, 'D',
sK->encKey, sK->encKeySz, sK->encKey, sK->encKeySz,
ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz,
ssh->sessionId, ssh->sessionIdSz); ssh->sessionId, ssh->sessionIdSz, doKeyPad);
if (ret == WS_SUCCESS) { if (ret == WS_SUCCESS) {
if (!ssh->handshake->aeadMode) { if (!ssh->handshake->aeadMode) {
ret = GenerateKey(hashId, 'E', ret = GenerateKey(hashId, 'E',
cK->macKey, cK->macKeySz, cK->macKey, cK->macKeySz,
ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz,
ssh->sessionId, ssh->sessionIdSz); ssh->sessionId, ssh->sessionIdSz, doKeyPad);
if (ret == WS_SUCCESS) { if (ret == WS_SUCCESS) {
ret = GenerateKey(hashId, 'F', ret = GenerateKey(hashId, 'F',
sK->macKey, sK->macKeySz, sK->macKey, sK->macKeySz,
ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz,
ssh->sessionId, ssh->sessionIdSz); ssh->sessionId, ssh->sessionIdSz, doKeyPad);
} }
} }
} }
#ifdef SHOW_SECRETS #ifdef SHOW_SECRETS
if (ret == WS_SUCCESS) { if (ret == WS_SUCCESS) {
printf("\n** Showing Secrets **\nK:\n"); printf("\n** Showing Secrets **\nK:\n");
@ -1448,9 +1453,10 @@ static const NameIdPair NameIdMap[] = {
#ifndef WOLFSSH_NO_DH_GEX_SHA256 #ifndef WOLFSSH_NO_DH_GEX_SHA256
{ ID_DH_GROUP14_SHA256, "diffie-hellman-group14-sha256" }, { ID_DH_GROUP14_SHA256, "diffie-hellman-group14-sha256" },
#endif #endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
/* We use kyber-512 here to achieve interop with OQS's fork. */ /* We use kyber-512 here to achieve interop with OQS's fork. */
{ ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256, "ecdh-sha2-nistp256-kyber-512-sha256" }, { ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
"ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org" },
#endif #endif
/* Public Key IDs */ /* Public Key IDs */
#ifndef WOLFSSH_NO_SSH_RSA_SHA1 #ifndef WOLFSSH_NO_SSH_RSA_SHA1
@ -2454,8 +2460,8 @@ static const byte cannedKeyAlgoClient[] = {
}; };
static const byte cannedKexAlgo[] = { static const byte cannedKexAlgo[] = {
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256, ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
#endif #endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
ID_ECDH_SHA2_NISTP521, ID_ECDH_SHA2_NISTP521,
@ -2647,8 +2653,8 @@ static INLINE enum wc_HashType HashForId(byte id)
#endif #endif
return WC_HASH_TYPE_SHA256; return WC_HASH_TYPE_SHA256;
#endif #endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
return WC_HASH_TYPE_SHA256; return WC_HASH_TYPE_SHA256;
#endif #endif
@ -2687,8 +2693,8 @@ static INLINE enum wc_HashType HashForId(byte id)
static INLINE int wcPrimeForId(byte id) static INLINE int wcPrimeForId(byte id)
{ {
switch (id) { switch (id) {
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
return ECC_SECP256R1; return ECC_SECP256R1;
#endif #endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
@ -3711,6 +3717,10 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
ecc_key key_s; ecc_key key_s;
#endif #endif
#endif #endif
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
byte sharedSecretHashSz = 0;
byte *sharedSecretHash = NULL;
#endif
WLOG(WS_LOG_DEBUG, "Entering DoKexDhReply()"); WLOG(WS_LOG_DEBUG, "Entering DoKexDhReply()");
@ -3939,7 +3949,7 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
* issues with re-key */ * issues with re-key */
ssh->kSz = MAX_KEX_KEY_SZ; ssh->kSz = MAX_KEX_KEY_SZ;
if (!ssh->handshake->useEcc if (!ssh->handshake->useEcc
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !ssh->handshake->useEccKyber && !ssh->handshake->useEccKyber
#endif #endif
) { ) {
@ -3985,7 +3995,7 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
ret = WS_INVALID_ALGO_ID; ret = WS_INVALID_ALGO_ID;
#endif #endif
} }
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
else if (ssh->handshake->useEccKyber) { else if (ssh->handshake->useEccKyber) {
/* This is a a hybrid of ECDHE and a post-quantum KEM. In this /* This is a a hybrid of ECDHE and a post-quantum KEM. In this
* case, I need to generated the ECC shared secret and * case, I need to generated the ECC shared secret and
@ -3995,33 +4005,38 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
ret = WS_INVALID_ALGO_ID; ret = WS_INVALID_ALGO_ID;
} }
if (fSz <= (word32)kem->length_ciphertext) { if ((ret == 0) && (fSz <= (word32)kem->length_ciphertext)) {
ret = WS_BUFFER_E; ret = WS_BUFFER_E;
} }
ret = wc_ecc_init(key_ptr); if (ret == 0) {
ret = wc_ecc_init(key_ptr);
}
#ifdef HAVE_WC_ECC_SET_RNG #ifdef HAVE_WC_ECC_SET_RNG
if (ret == 0) if (ret == 0) {
ret = wc_ecc_set_rng(key_ptr, ssh->rng); ret = wc_ecc_set_rng(key_ptr, ssh->rng);
}
#endif #endif
if (ret == 0) { if (ret == 0) {
ret = wc_ecc_import_x963(f, fSz - ret = wc_ecc_import_x963(f + kem->length_ciphertext,
(word32)kem->length_ciphertext, key_ptr); fSz - (word32)kem->length_ciphertext, key_ptr);
} }
if (ret == 0) { if (ret == 0) {
PRIVATE_KEY_UNLOCK(); PRIVATE_KEY_UNLOCK();
ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc, ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
key_ptr, ssh->k, &ssh->kSz); key_ptr, ssh->k + kem->length_shared_secret,
&ssh->kSz);
PRIVATE_KEY_LOCK(); PRIVATE_KEY_LOCK();
} }
wc_ecc_free(key_ptr); wc_ecc_free(key_ptr);
wc_ecc_free(&ssh->handshake->privKey.ecc); wc_ecc_free(&ssh->handshake->privKey.ecc);
if (OQS_KEM_decaps(kem, ssh->k + ssh->kSz, if (ret == 0) {
f + fSz - kem->length_ciphertext, ssh->handshake->x) if (OQS_KEM_decaps(kem, ssh->k, f, ssh->handshake->x)
!= OQS_SUCCESS) { != OQS_SUCCESS) {
ret = WS_ERROR; ret = WS_ERROR;
}
} }
if (ret == 0) { if (ret == 0) {
@ -4036,31 +4051,60 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
if (kem != NULL) { if (kem != NULL) {
OQS_KEM_free(kem); OQS_KEM_free(kem);
} }
/* Replace the concatenated shared secrets with the hash. That
* will become the new shared secret. */
if (ret == 0) {
sharedSecretHashSz = wc_HashGetDigestSize(enmhashId);
sharedSecretHash = (byte *)WMALLOC(sharedSecretHashSz,
ssh->ctx->heap,
DYNTYPE_PRIVKEY);
if (sharedSecretHash == NULL) {
ret = WS_MEMORY_E;
}
}
if (ret == 0) {
ret = wc_Hash(enmhashId, ssh->k, ssh->kSz, sharedSecretHash,
sharedSecretHashSz);
}
if (ret == 0) {
XMEMCPY(ssh->k, sharedSecretHash, sharedSecretHashSz);
ssh->kSz = sharedSecretHashSz;
}
} }
#endif #endif /* !WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
else { else {
ret = WS_INVALID_ALGO_ID; ret = WS_INVALID_ALGO_ID;
} }
} }
if (ret == 0)
ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
/* Hash in the shared secret K. */ /* Hash in the shared secret K. */
if (ret == 0
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !ssh->handshake->useEccKyber
#endif
) {
ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
}
if (ret == 0) { if (ret == 0) {
c32toa(ssh->kSz + kPad, scratchLen); c32toa(ssh->kSz + kPad, scratchLen);
ret = HashUpdate(&ssh->handshake->hash, enmhashId, ret = HashUpdate(&ssh->handshake->hash, enmhashId,
scratchLen, LENGTH_SZ); scratchLen, LENGTH_SZ);
} }
if (ret == 0) {
if (kPad) { if ((ret == 0) && (kPad)) {
scratchLen[0] = 0; scratchLen[0] = 0;
ret = HashUpdate(&ssh->handshake->hash, ret = HashUpdate(&ssh->handshake->hash,
enmhashId, scratchLen, 1); enmhashId, scratchLen, 1);
}
} }
if (ret == 0)
if (ret == 0) {
ret = HashUpdate(&ssh->handshake->hash, enmhashId, ret = HashUpdate(&ssh->handshake->hash, enmhashId,
ssh->k, ssh->kSz); ssh->k, ssh->kSz);
}
/* Save the exchange hash value H, and session ID. */ /* Save the exchange hash value H, and session ID. */
if (ret == 0) { if (ret == 0) {
@ -4195,8 +4239,13 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
} }
} }
if (ret == WS_SUCCESS) if (ret == WS_SUCCESS) {
ret = GenerateKeys(ssh, enmhashId); int useKeyPadding = 1;
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
useKeyPadding = !ssh->handshake->useEccKyber;
#endif
ret = GenerateKeys(ssh, enmhashId, useKeyPadding);
}
if (ret == WS_SUCCESS) if (ret == WS_SUCCESS)
ret = SendNewKeys(ssh); ret = SendNewKeys(ssh);
@ -7801,8 +7850,8 @@ static const char cannedMacAlgoNames[] =
#endif #endif
static const char cannedKexAlgoNames[] = static const char cannedKexAlgoNames[] =
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256) #if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
"ecdh-sha2-nistp256-kyber-512-sha256," "ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org,"
#endif #endif
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) #if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521)
"ecdh-sha2-nistp521," "ecdh-sha2-nistp521,"
@ -7830,7 +7879,7 @@ static const char cannedKexAlgoNames[] =
defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \ defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \
defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \
defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \
defined(WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256) defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
#warning "You need at least one key exchange algorithm." #warning "You need at least one key exchange algorithm."
#endif #endif
@ -8040,7 +8089,7 @@ struct wolfSSH_sigKeyBlockFull {
} sk; } sk;
}; };
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
/* Size of Kyber public key (bigger than ciphertext) and some extra for the /* Size of Kyber public key (bigger than ciphertext) and some extra for the
* ECC hybrid component. */ * ECC hybrid component. */
#define KEX_F_SIZE 1024 #define KEX_F_SIZE 1024
@ -8508,8 +8557,10 @@ int SendKexDhReply(WOLFSSH* ssh)
word32 fSz = KEX_F_SIZE; word32 fSz = KEX_F_SIZE;
word32 sigSz = KEX_SIG_SIZE; word32 sigSz = KEX_SIG_SIZE;
byte useEcc = 0; byte useEcc = 0;
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
byte useEccKyber = 0; byte useEccKyber = 0;
byte sharedSecretHashSz = 0;
byte *sharedSecretHash = NULL;
#endif #endif
byte fPad = 0; byte fPad = 0;
byte kPad = 0; byte kPad = 0;
@ -8614,8 +8665,8 @@ int SendKexDhReply(WOLFSSH* ssh)
msgId = MSGID_KEXDH_REPLY; msgId = MSGID_KEXDH_REPLY;
break; break;
#endif #endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
useEccKyber = 1; /* Only support level 1 for now. */ useEccKyber = 1; /* Only support level 1 for now. */
msgId = MSGID_KEXKEM_REPLY; msgId = MSGID_KEXKEM_REPLY;
break; break;
@ -8655,7 +8706,7 @@ int SendKexDhReply(WOLFSSH* ssh)
/* Or make the server's ECDH private value, and the shared secret K. */ /* Or make the server's ECDH private value, and the shared secret K. */
if (ret == 0) { if (ret == 0) {
if (!useEcc if (!useEcc
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !useEccKyber && !useEccKyber
#endif #endif
) { ) {
@ -8756,7 +8807,7 @@ int SendKexDhReply(WOLFSSH* ssh)
#endif #endif
#endif /* !defined(WOLFSSH_NO_ECDH) */ #endif /* !defined(WOLFSSH_NO_ECDH) */
} }
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
else if (useEccKyber) { else if (useEccKyber) {
/* This is a hybrid KEM. In this case, I need to generate my ECC /* This is a hybrid KEM. In this case, I need to generate my ECC
* keypair, send the public one, use the private one to generate * keypair, send the public one, use the private one to generate
@ -8780,8 +8831,14 @@ int SendKexDhReply(WOLFSSH* ssh)
ecc_key privKey[1]; ecc_key privKey[1];
#endif #endif
XMEMSET(pubKey, 0, sizeof(*pubKey)); if (ret == 0) {
XMEMSET(privKey, 0, sizeof(*privKey)); XMEMSET(pubKey, 0, sizeof(*pubKey));
XMEMSET(privKey, 0, sizeof(*privKey));
primeId = wcPrimeForId(ssh->handshake->kexId);
if (primeId == ECC_CURVE_INVALID)
ret = WS_INVALID_PRIME_CURVE;
}
if (ret == 0) { if (ret == 0) {
kem = OQS_KEM_new(OQS_KEM_alg_kyber_512); kem = OQS_KEM_new(OQS_KEM_alg_kyber_512);
@ -8790,46 +8847,67 @@ int SendKexDhReply(WOLFSSH* ssh)
} }
} }
if (ssh->handshake->eSz <= (word32)kem->length_public_key) { if ((ret == 0) &&
(ssh->handshake->eSz <= (word32)kem->length_public_key)) {
ret = WS_BUFFER_E; ret = WS_BUFFER_E;
} }
if (ret == 0) { if (ret == 0) {
primeId = wcPrimeForId(ssh->handshake->kexId); if (OQS_KEM_encaps(kem, f_ptr, ssh->k,
if (primeId == ECC_CURVE_INVALID) ssh->handshake->e) != OQS_SUCCESS) {
ret = WS_INVALID_PRIME_CURVE; ret = WS_PUBKEY_REJECTED_E;
}
} }
if (ret == 0) if (ret == 0) {
fSz -= kem->length_ciphertext;
ssh->kSz -= kem->length_shared_secret;
}
else {
fSz = 0;
ssh->kSz = 0;
WLOG(WS_LOG_ERROR,
"Generate ECC-kyber (encap) shared secret failed, %d",
ret);
}
if (ret == 0) {
ret = wc_ecc_init_ex(pubKey, heap, INVALID_DEVID); ret = wc_ecc_init_ex(pubKey, heap, INVALID_DEVID);
if (ret == 0) }
if (ret == 0) {
ret = wc_ecc_init_ex(privKey, heap, INVALID_DEVID); ret = wc_ecc_init_ex(privKey, heap, INVALID_DEVID);
}
#ifdef HAVE_WC_ECC_SET_RNG #ifdef HAVE_WC_ECC_SET_RNG
if (ret == 0) if (ret == 0) {
ret = wc_ecc_set_rng(privKey, ssh->rng); ret = wc_ecc_set_rng(privKey, ssh->rng);
}
#endif #endif
if (ret == 0) {
if (ret == 0) ret = wc_ecc_import_x963_ex(
ret = wc_ecc_import_x963_ex(ssh->handshake->e, ssh->handshake->e + kem->length_public_key,
ssh->handshake->eSz - ssh->handshake->eSz - (word32)kem->length_public_key,
(word32)kem->length_public_key, pubKey, primeId);
pubKey, primeId); }
if (ret == 0) {
if (ret == 0)
ret = wc_ecc_make_key_ex(ssh->rng, ret = wc_ecc_make_key_ex(ssh->rng,
wc_ecc_get_curve_size_from_id(primeId), wc_ecc_get_curve_size_from_id(primeId),
privKey, primeId); privKey, primeId);
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
ret = wc_ecc_export_x963(privKey, f_ptr, &fSz);
PRIVATE_KEY_LOCK();
} }
if (ret == 0) { if (ret == 0) {
PRIVATE_KEY_UNLOCK(); PRIVATE_KEY_UNLOCK();
ret = wc_ecc_shared_secret(privKey, pubKey, ret = wc_ecc_export_x963(privKey,
ssh->k, &ssh->kSz); f_ptr + kem->length_ciphertext, &fSz);
fSz += kem->length_ciphertext;
PRIVATE_KEY_LOCK(); PRIVATE_KEY_LOCK();
} }
if (ret == 0) {
word32 tmp_kSz = ssh->kSz;
PRIVATE_KEY_UNLOCK();
ret = wc_ecc_shared_secret(privKey, pubKey,
ssh->k + kem->length_shared_secret, &tmp_kSz);
PRIVATE_KEY_LOCK();
ssh->kSz = kem->length_shared_secret + tmp_kSz;
}
wc_ecc_free(privKey); wc_ecc_free(privKey);
wc_ecc_free(pubKey); wc_ecc_free(pubKey);
#ifdef WOLFSSH_SMALL_STACK #ifdef WOLFSSH_SMALL_STACK
@ -8838,32 +8916,34 @@ int SendKexDhReply(WOLFSSH* ssh)
pubKey = NULL; pubKey = NULL;
privKey = NULL; privKey = NULL;
#endif #endif
if (ret == 0) {
if (OQS_KEM_encaps(kem, f_ptr + fSz, ssh->k + ssh->kSz,
ssh->handshake->e + ssh->handshake->eSz
- kem->length_public_key)
!= OQS_SUCCESS) {
ret = WS_PUBKEY_REJECTED_E;
}
}
if (ret == 0) {
fSz += kem->length_ciphertext;
ssh->kSz += kem->length_shared_secret;
} else {
fSz = 0;
ssh->kSz = 0;
WLOG(WS_LOG_ERROR,
"Generate ECC-kyber (encap) shared secret failed, %d",
ret);
}
if (kem != NULL) { if (kem != NULL) {
OQS_KEM_free(kem); OQS_KEM_free(kem);
kem = NULL;
} }
/* Replace the concatenated shared secrets with the hash. That
* will become the new shared secret.*/
if (ret == 0) {
sharedSecretHashSz = wc_HashGetDigestSize(enmhashId);
sharedSecretHash = (byte *)WMALLOC(sharedSecretHashSz, heap,
DYNTYPE_PRIVKEY);
if (sharedSecretHash == NULL) {
ret = WS_MEMORY_E;
}
}
if (ret == 0) {
ret = wc_Hash(enmhashId, ssh->k, ssh->kSz, sharedSecretHash,
sharedSecretHashSz);
}
if (ret == 0) {
XMEMCPY(ssh->k, sharedSecretHash, sharedSecretHashSz);
ssh->kSz = sharedSecretHashSz;
}
WFREE(sharedSecretHash, heap, DYNTYPE_PRIVKEY);
sharedSecretHash = NULL;
} }
#endif #endif /* !WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
else { else {
/* This should never happen */ /* This should never happen */
ret = WS_ERROR; ret = WS_ERROR;
@ -8871,47 +8951,47 @@ int SendKexDhReply(WOLFSSH* ssh)
} }
/* Hash in the server's DH f-value. */ /* Hash in the server's DH f-value. */
if (ret == 0) { if (ret == 0
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !useEccKyber
#endif
) {
ret = CreateMpint(f_ptr, &fSz, &fPad); ret = CreateMpint(f_ptr, &fSz, &fPad);
} }
if (ret == 0) { if (ret == 0) {
c32toa(fSz + fPad, scratchLen); c32toa(fSz + fPad, scratchLen);
ret = HashUpdate(&ssh->handshake->hash, enmhashId, ret = HashUpdate(&ssh->handshake->hash, enmhashId, scratchLen,
scratchLen, LENGTH_SZ); LENGTH_SZ);
}
if ((ret == 0) && (fPad)) {
scratchLen[0] = 0;
ret = HashUpdate(&ssh->handshake->hash, enmhashId, scratchLen, 1);
} }
if (ret == 0) { if (ret == 0) {
if (fPad) { ret = HashUpdate(&ssh->handshake->hash, enmhashId, f_ptr, fSz);
scratchLen[0] = 0;
ret = HashUpdate(&ssh->handshake->hash,
enmhashId, scratchLen, 1);
}
}
if (ret == 0) {
ret = HashUpdate(&ssh->handshake->hash,
enmhashId, f_ptr, fSz);
} }
/* Hash in the shared secret K. */ /* Hash in the shared secret K. */
if (ret == 0) { if (ret == 0
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !useEccKyber
#endif
) {
ret = CreateMpint(ssh->k, &ssh->kSz, &kPad); ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
} }
if (ret == 0) { if (ret == 0) {
c32toa(ssh->kSz + kPad, scratchLen); c32toa(ssh->kSz + kPad, scratchLen);
ret = HashUpdate(&ssh->handshake->hash, enmhashId, ret = HashUpdate(&ssh->handshake->hash, enmhashId,
scratchLen, LENGTH_SZ); scratchLen, LENGTH_SZ);
} }
if (ret == 0) { if ((ret == 0) && (kPad)) {
if (kPad) { scratchLen[0] = 0;
scratchLen[0] = 0; ret = HashUpdate(&ssh->handshake->hash,
ret = HashUpdate(&ssh->handshake->hash, enmhashId, scratchLen, 1);
enmhashId, scratchLen, 1);
}
} }
if (ret == 0) { if (ret == 0) {
ret = HashUpdate(&ssh->handshake->hash, enmhashId, ret = HashUpdate(&ssh->handshake->hash, enmhashId,
ssh->k, ssh->kSz); ssh->k, ssh->kSz);
} }
/* Save the exchange hash value H, and session ID. */ /* Save the exchange hash value H, and session ID. */
@ -8921,7 +9001,6 @@ int SendKexDhReply(WOLFSSH* ssh)
wc_HashFree(&ssh->handshake->hash, enmhashId); wc_HashFree(&ssh->handshake->hash, enmhashId);
ssh->handshake->hashId = WC_HASH_TYPE_NONE; ssh->handshake->hashId = WC_HASH_TYPE_NONE;
} }
if (ret == 0) { if (ret == 0) {
ssh->hSz = wc_HashGetDigestSize(enmhashId); ssh->hSz = wc_HashGetDigestSize(enmhashId);
if (ssh->sessionIdSz == 0) { if (ssh->sessionIdSz == 0) {
@ -8929,9 +9008,9 @@ int SendKexDhReply(WOLFSSH* ssh)
ssh->sessionIdSz = ssh->hSz; ssh->sessionIdSz = ssh->hSz;
} }
} }
if (ret != WS_SUCCESS) {
if (ret != WS_SUCCESS)
ret = WS_CRYPTO_FAILED; ret = WS_CRYPTO_FAILED;
}
} }
/* Sign h with the server's private key. */ /* Sign h with the server's private key. */
@ -9072,8 +9151,13 @@ int SendKexDhReply(WOLFSSH* ssh)
} }
} }
if (ret == WS_SUCCESS) if (ret == WS_SUCCESS) {
ret = GenerateKeys(ssh, enmhashId); int doKeyPadding = 1;
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
doKeyPadding = !useEccKyber;
#endif
ret = GenerateKeys(ssh, enmhashId, doKeyPadding);
}
/* Get the buffer, copy the packet data, once f is laid into the buffer, /* Get the buffer, copy the packet data, once f is laid into the buffer,
* add it to the hash and then add K. */ * add it to the hash and then add K. */
@ -9482,8 +9566,8 @@ int SendKexDhInit(WOLFSSH* ssh)
msgId = MSGID_KEXECDH_INIT; msgId = MSGID_KEXECDH_INIT;
break; break;
#endif #endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
/* Only support level 1 for now. */ /* Only support level 1 for now. */
ssh->handshake->useEccKyber = 1; ssh->handshake->useEccKyber = 1;
msgId = MSGID_KEXKEM_INIT; msgId = MSGID_KEXKEM_INIT;
@ -9497,7 +9581,7 @@ int SendKexDhInit(WOLFSSH* ssh)
if (ret == WS_SUCCESS) { if (ret == WS_SUCCESS) {
if (!ssh->handshake->useEcc if (!ssh->handshake->useEcc
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !ssh->handshake->useEccKyber && !ssh->handshake->useEccKyber
#endif #endif
) { ) {
@ -9516,7 +9600,7 @@ int SendKexDhInit(WOLFSSH* ssh)
#endif #endif
} }
else if (ssh->handshake->useEcc else if (ssh->handshake->useEcc
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
|| ssh->handshake->useEccKyber || ssh->handshake->useEccKyber
#endif #endif
) { ) {
@ -9551,7 +9635,7 @@ int SendKexDhInit(WOLFSSH* ssh)
ret = WS_INVALID_ALGO_ID; ret = WS_INVALID_ALGO_ID;
} }
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
if (ssh->handshake->useEccKyber) { if (ssh->handshake->useEccKyber) {
OQS_KEM* kem = NULL; OQS_KEM* kem = NULL;
ret = 0; ret = 0;
@ -9561,27 +9645,32 @@ int SendKexDhInit(WOLFSSH* ssh)
ret = WS_INVALID_ALGO_ID; ret = WS_INVALID_ALGO_ID;
} }
/* Move ecc to the back. Note that this assumes the PQ public key
* is bigger than the ECC public key. */
if (ret == 0) { if (ret == 0) {
if (OQS_KEM_keypair(kem, e + eSz, ssh->handshake->x) XMEMCPY(e + kem->length_public_key, e, eSz);
!= OQS_SUCCESS) { if (OQS_KEM_keypair(kem, e, ssh->handshake->x) != OQS_SUCCESS) {
/* This should never happen */ /* This should never happen */
ret = WS_ERROR; ret = WS_ERROR;
} }
eSz += kem->length_public_key; eSz += kem->length_public_key;
ssh->handshake->xSz = (word32)kem->length_secret_key; ssh->handshake->xSz = (word32)kem->length_secret_key;
} }
if (kem != NULL) { if (kem != NULL) {
OQS_KEM_free(kem); OQS_KEM_free(kem);
} }
} }
#endif #endif /* ! WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
if (ret == 0) {
if (ret == 0)
ret = WS_SUCCESS; ret = WS_SUCCESS;
}
} }
if (ret == WS_SUCCESS) { if (ret == WS_SUCCESS
#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
&& !ssh->handshake->useEccKyber
#endif
) {
ret = CreateMpint(e, &eSz, &ePad); ret = CreateMpint(e, &eSz, &ePad);
} }

View File

@ -1735,9 +1735,10 @@ int wolfSSH_KDF(byte hashId, byte keyId,
const byte* h, word32 hSz, const byte* h, word32 hSz,
const byte* sessionId, word32 sessionIdSz) const byte* sessionId, word32 sessionIdSz)
{ {
int doKeyPadding = 1;
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_KDF()"); WLOG(WS_LOG_DEBUG, "Entering wolfSSH_KDF()");
return GenerateKey(hashId, keyId, key, keySz, k, kSz, h, hSz, return GenerateKey(hashId, keyId, key, keySz, k, kSz, h, hSz,
sessionId, sessionIdSz); sessionId, sessionIdSz, doKeyPadding);
} }

View File

@ -141,8 +141,8 @@ extern "C" {
#define WOLFSSH_NO_ECDH_SHA2_ED25519 #define WOLFSSH_NO_ECDH_SHA2_ED25519
#endif #endif
#if !defined(WOLFSSH_HAVE_LIBOQS) || defined(NO_SHA256) #if !defined(WOLFSSH_HAVE_LIBOQS) || defined(NO_SHA256)
#undef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #undef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
#define WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #define WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
#endif #endif
#if defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \ #if defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \
@ -152,7 +152,7 @@ extern "C" {
defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \
defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \
defined(WOLFSSH_NO_ECDH_SHA2_ED25519) && \ defined(WOLFSSH_NO_ECDH_SHA2_ED25519) && \
defined(WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256) defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
#error "You need at least one key agreement algorithm." #error "You need at least one key agreement algorithm."
#endif #endif
@ -285,8 +285,8 @@ enum {
ID_ECDH_SHA2_ED25519, ID_ECDH_SHA2_ED25519,
ID_ECDH_SHA2_ED25519_LIBSSH, ID_ECDH_SHA2_ED25519_LIBSSH,
ID_DH_GROUP14_SHA256, ID_DH_GROUP14_SHA256,
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256, ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
#endif #endif
/* Public Key IDs */ /* Public Key IDs */
@ -369,7 +369,7 @@ enum {
#define WOLFSSH_DEFAULT_GEXDH_MAX 8192 #define WOLFSSH_DEFAULT_GEXDH_MAX 8192
#endif #endif
#ifndef MAX_KEX_KEY_SZ #ifndef MAX_KEX_KEY_SZ
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
/* Private key size of Kyber Level1. Biggest artifact. */ /* Private key size of Kyber Level1. Biggest artifact. */
#define MAX_KEX_KEY_SZ 1632 #define MAX_KEX_KEY_SZ 1632
#else #else
@ -513,7 +513,7 @@ typedef struct HandshakeInfo {
#endif #endif
byte useEcc; byte useEcc;
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
byte useEccKyber; byte useEccKyber;
#endif #endif
@ -863,7 +863,7 @@ WOLFSSH_LOCAL int SendChannelTerminalRequest(WOLFSSH* ssh);
WOLFSSH_LOCAL int SendChannelAgentRequest(WOLFSSH* ssh); WOLFSSH_LOCAL int SendChannelAgentRequest(WOLFSSH* ssh);
WOLFSSH_LOCAL int SendChannelSuccess(WOLFSSH*, word32, int); WOLFSSH_LOCAL int SendChannelSuccess(WOLFSSH*, word32, int);
WOLFSSH_LOCAL int GenerateKey(byte, byte, byte*, word32, const byte*, word32, WOLFSSH_LOCAL int GenerateKey(byte, byte, byte*, word32, const byte*, word32,
const byte*, word32, const byte*, word32); const byte*, word32, const byte*, word32, byte doKeyPad);
enum AcceptStates { enum AcceptStates {
@ -958,13 +958,13 @@ enum WS_MessageIds {
MSGID_KEXDH_INIT = 30, MSGID_KEXDH_INIT = 30,
MSGID_KEXECDH_INIT = 30, MSGID_KEXECDH_INIT = 30,
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
MSGID_KEXKEM_INIT = 30, MSGID_KEXKEM_INIT = 30,
#endif #endif
MSGID_KEXDH_REPLY = 31, MSGID_KEXDH_REPLY = 31,
MSGID_KEXECDH_REPLY = 31, MSGID_KEXECDH_REPLY = 31,
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
MSGID_KEXKEM_REPLY = 31, MSGID_KEXKEM_REPLY = 31,
#endif #endif