diff --git a/README.md b/README.md index 23f9158e..f09d2df9 100644 --- a/README.md +++ b/README.md @@ -402,9 +402,9 @@ behavior, give the echoserver the command line option `-f`. POST-QUANTUM ============ -wolfSSH now supports the post-quantum algorithm SABER. It uses the NIST +wolfSSH now supports the post-quantum algorithm Kyber. It uses the NIST submission's Level 1 parameter set implemented by liboqs via an integration -with wolfSSH. +with wolfSSH. It is hybridized with ECDHE over the P-256 ECC curve. In order be able to use liboqs, you must have it built and installed on your system. We support the 0.7.0 release of liboqs. You can download it from the @@ -422,13 +422,13 @@ Once unpacked, this would be sufficient: $ sudo make install -In order to enable support for SABER Level1 in wolfSSH, use the `--with-liboqs` -build option during configuration: +In order to enable support for Kyber Level1 hybridized with ECDHE over the P-256 +ECC curve in wolfSSH, use the `--with-liboqs` build option during configuration: $ ./configure --with-liboqs -The wolfSSH client and server will automatically negotiate using SABER Level1 -if this feature is enabled. +The wolfSSH client and server will automatically negotiate using Kyber Level1 +hybridized with ECDHE over the P-256 ECC curve if this feature is enabled. $ ./examples/echoserver/echoserver -f @@ -450,7 +450,7 @@ The following is sufficient for build and execution: $ cd openssh-OQS-OpenSSH-snapshot-2021-08/ $ ./configure --with-liboqs-dir=/usr/local $ make all - $ ./ssh -o"KexAlgorithms +saber-lightsaber-sha256" \ + $ ./ssh -o"KexAlgorithms +ecdh-sha2-nistp256-kyber-512-sha256" \ -o"PubkeyAcceptedAlgorithms +ssh-rsa" \ -o"HostkeyAlgorithms +ssh-rsa" \ jill@localhost -p 22222 diff --git a/src/internal.c b/src/internal.c index 068798f5..8187ba8f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -107,9 +107,9 @@ Flags: WOLFSSH_NO_ECDSA_SHA2_NISTP521 Set when ECC or SHA2-512 are disabled. Set to disable use of ECDSA server authentication with prime NISTP521. - WOLFSSH_NO_SABER_LEVEL1_SHA256 - Set when there is no liboqs integration. Set to disable use of post-quantum - SABER Level 1 KEM. + WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + Set when there is no liboqs integration. Set to disable use of ECDHE with + prime NISTP256 hybridized with post-quantum Kyber Level1 KEM. WOLFSSH_NO_AES_CBC Set when AES or AES-CBC are disabled. Set to disable use of AES-CBC encryption. @@ -1098,9 +1098,9 @@ static const NameIdPair NameIdMap[] = { #ifndef WOLFSSH_NO_DH_GEX_SHA256 { ID_DH_GROUP14_SHA256, "diffie-hellman-group14-sha256" }, #endif -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - /* We use lightsaber here to achieve interop with OQS's fork. */ - { ID_SABER_LEVEL1_SHA256, "saber-lightsaber-sha256" }, +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + /* 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" }, #endif /* Public Key IDs */ #ifndef WOLFSSH_NO_SSH_RSA_SHA1 @@ -2092,8 +2092,8 @@ static const word32 cannedKeyAlgoEcc521Sz = sizeof(cannedKeyAlgoEcc521); static const byte cannedKexAlgo[] = { -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - ID_SABER_LEVEL1_SHA256, +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256, #endif #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521 ID_ECDH_SHA2_NISTP521, @@ -2279,8 +2279,8 @@ static INLINE enum wc_HashType HashForId(byte id) case ID_ECDSA_SHA2_NISTP256: return WC_HASH_TYPE_SHA256; #endif -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - case ID_SABER_LEVEL1_SHA256: +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: return WC_HASH_TYPE_SHA256; #endif @@ -2313,6 +2313,10 @@ static INLINE enum wc_HashType HashForId(byte id) static INLINE int wcPrimeForId(byte id) { switch (id) { +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: + return ECC_SECP256R1; +#endif #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256 case ID_ECDH_SHA2_NISTP256: return ECC_SECP256R1; @@ -3275,8 +3279,8 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) * issues with re-key */ ssh->kSz = MAX_KEX_KEY_SZ; if (!ssh->handshake->useEcc -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - && !ssh->handshake->useSaber +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + && !ssh->handshake->useEccKyber #endif ) { #ifndef WOLFSSH_NO_DH @@ -3329,33 +3333,55 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) ret = WS_INVALID_ALGO_ID; #endif } -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - else if (ssh->handshake->useSaber) { - /* This is a KEM. In this case, I need to decapsulate the - * ciphertext. */ - OQS_KEM* kem = NULL; - ret = 0; +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + else if (ssh->handshake->useEccKyber) { + /* This is a a hybrid of ECDHE and a post-quantum KEM. In this + * case, I need to generated the ECC shared secret and + * decapsulate the ciphertext of the post-quantum KEM. */ + OQS_KEM* kem = OQS_KEM_new(OQS_KEM_alg_kyber_512); + if (kem == NULL) { + ret = WS_INVALID_ALGO_ID; + } + ret = wc_ecc_init(key_ptr); +#ifdef HAVE_WC_ECC_SET_RNG + if (ret == WS_SUCCESS) + ret = wc_ecc_set_rng(key_ptr, ssh->rng); +#endif if (ret == 0) { - kem = OQS_KEM_new(OQS_KEM_alg_saber_lightsaber); - if (kem == NULL) { - ret = WS_INVALID_ALGO_ID; - } + ret = wc_ecc_import_x963(f, fSz - kem->length_ciphertext, + key_ptr); } if (ret == 0) { - if (fSz != kem->length_ciphertext) { - ret = WS_BUFFER_E; - } + #ifdef PRIVATE_KEY_UNLOCK + PRIVATE_KEY_UNLOCK(); + #endif + ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc, + key_ptr, ssh->k, &ssh->kSz); + #ifdef PRIVATE_KEY_LOCK + PRIVATE_KEY_LOCK(); + #endif } + wc_ecc_free(key_ptr); + wc_ecc_free(&ssh->handshake->privKey.ecc); - if (OQS_KEM_decaps(kem, ssh->k, f, ssh->handshake->x) + if (OQS_KEM_decaps(kem, ssh->k + ssh->kSz, + f + fSz - kem->length_ciphertext, ssh->handshake->x) != OQS_SUCCESS) { ret = WS_ERROR; } if (ret == 0) { - ssh->kSz = kem->length_shared_secret; + ssh->kSz += kem->length_shared_secret; + } else { + ssh->kSz = 0; + WLOG(WS_LOG_ERROR, + "Generate ECC-kyber (decap) shared secret failed, %d", ret); + } + + if (kem != NULL) { + OQS_KEM_free(kem); } } #endif @@ -6683,8 +6709,8 @@ static const char cannedKeyAlgoEcc521Names[] = "ecdsa-sha2-nistp521"; #endif static const char cannedKexAlgoNames[] = -#if !defined(WOLFSSH_NO_SABER_LEVEL1_SHA256) - "saber-lightsaber-sha256," +#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256) + "ecdh-sha2-nistp256-kyber-512-sha256," #endif #if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) "ecdh-sha2-nistp521," @@ -6712,7 +6738,7 @@ static const char cannedKexAlgoNames[] = defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ - defined(WOLFSSH_NO_SABER_LEVEL1_256) + defined(WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256) #warning "You need at least one key exchange algorithm." #endif @@ -6897,9 +6923,10 @@ struct wolfSSH_sigKeyBlockFull { } sk; }; -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - /* Size of SABER Level1 ciphertext. */ - #define KEX_F_SIZE 736 +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + /* Size of Kyber public key (bigger than ciphertext) and some extra for the + * ECC hybrid component. */ + #define KEX_F_SIZE 1024 #else #define KEX_F_SIZE (256 + 1) #endif @@ -6927,8 +6954,8 @@ int SendKexDhReply(WOLFSSH* ssh) word32 fSz = KEX_F_SIZE; word32 sigSz = KEX_SIG_SIZE; byte useEcc = 0; -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - byte useSaber = 0; +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + byte useEccKyber = 0; #endif byte fPad = 0; byte kPad = 0; @@ -7032,9 +7059,9 @@ int SendKexDhReply(WOLFSSH* ssh) msgId = MSGID_KEXDH_REPLY; break; #endif -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - case ID_SABER_LEVEL1_SHA256: - useSaber = 1; /* Only support level 1 for now. */ +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: + useEccKyber = 1; /* Only support level 1 for now. */ msgId = MSGID_KEXKEM_REPLY; break; #endif @@ -7332,8 +7359,8 @@ int SendKexDhReply(WOLFSSH* ssh) /* Or make the server's ECDH private value, and the shared secret K. */ if (ret == 0) { if (!useEcc -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - && !useSaber +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + && !useEccKyber #endif ) { #ifndef WOLFSSH_NO_DH @@ -7445,36 +7472,108 @@ int SendKexDhReply(WOLFSSH* ssh) #endif #endif /* !defined(WOLFSSH_NO_ECDH) */ } -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - else if (useSaber) { - /* This is a KEM. In this case, I need to encapsulate the - * shared secret. */ +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + else if (useEccKyber) { + /* 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 + * the shared secret, use the post-quantum public key to + * generate and encapsulate the shared secret and send the + * ciphertext. */ OQS_KEM* kem = NULL; + int primeId; ret = 0; + #ifdef WOLFSSH_SMALL_STACK + ecc_key *pubKey = NULL, *privKey = NULL; + pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap, + DYNTYPE_PUBKEY); + privKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap, + DYNTYPE_PRIVKEY); + if (pubKey == NULL || privKey == NULL) { + ret = WS_MEMORY_E; + } + #else + ecc_key pubKey[1]; + ecc_key privKey[1]; + #endif if (ret == 0) { - kem = OQS_KEM_new(OQS_KEM_alg_saber_lightsaber); + kem = OQS_KEM_new(OQS_KEM_alg_kyber_512); if (kem == NULL) { ret = WS_INVALID_ALGO_ID; } } + primeId = wcPrimeForId(ssh->handshake->kexId); + if (primeId == ECC_CURVE_INVALID) + ret = WS_INVALID_PRIME_CURVE; + + if (ret == 0) + ret = wc_ecc_init_ex(pubKey, heap, INVALID_DEVID); + if (ret == 0) + ret = wc_ecc_init_ex(privKey, heap, INVALID_DEVID); +#ifdef HAVE_WC_ECC_SET_RNG + if (ret == 0) + ret = wc_ecc_set_rng(privKey, ssh->rng); +#endif + + if (ret == 0) + ret = wc_ecc_import_x963_ex(ssh->handshake->e, + ssh->handshake->eSz - kem->length_public_key, + pubKey, primeId); + + if (ret == 0) + ret = wc_ecc_make_key_ex(ssh->rng, + wc_ecc_get_curve_size_from_id(primeId), + privKey, primeId); if (ret == 0) { - if (ssh->handshake->eSz != kem->length_public_key) { - ret = WS_PUBKEY_REJECTED_E; - } + #ifdef PRIVATE_KEY_UNLOCK + PRIVATE_KEY_UNLOCK(); + #endif + ret = wc_ecc_export_x963(privKey, f_ptr, &fSz); + #ifdef PRIVATE_KEY_LOCK + PRIVATE_KEY_LOCK(); + #endif } + if (ret == 0) { + #ifdef PRIVATE_KEY_UNLOCK + PRIVATE_KEY_UNLOCK(); + #endif + ret = wc_ecc_shared_secret(privKey, pubKey, + ssh->k, &ssh->kSz); + #ifdef PRIVATE_KEY_LOCK + PRIVATE_KEY_LOCK(); + #endif + } + wc_ecc_free(privKey); + wc_ecc_free(pubKey); + #ifdef WOLFSSH_SMALL_STACK + WFREE(pubKey, heap, DYNTYPE_PUBKEY); + WFREE(privKey, heap, DYNTYPE_PRIVKEY); + pubKey = NULL; + privKey = NULL; + #endif if (ret == 0) { - if (OQS_KEM_encaps(kem, f_ptr, ssh->k, ssh->handshake->e) + 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) { - ssh->kSz = kem->length_shared_secret; - fSz = kem->length_ciphertext; + 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) { + OQS_KEM_free(kem); } } #endif @@ -7485,12 +7584,7 @@ int SendKexDhReply(WOLFSSH* ssh) } /* Hash in the server's DH f-value. */ - /* Do not want leading zero's removed for SABER. */ - if (ret == 0 -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - && !useSaber -#endif - ) { + if (ret == 0) { ret = CreateMpint(f_ptr, &fSz, &fPad); } if (ret == 0) { @@ -8055,10 +8149,10 @@ int SendKexDhInit(WOLFSSH* ssh) msgId = MSGID_KEXECDH_INIT; break; #endif -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - case ID_SABER_LEVEL1_SHA256: +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + case ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256: /* Only support level 1 for now. */ - ssh->handshake->useSaber = 1; + ssh->handshake->useEccKyber = 1; msgId = MSGID_KEXKEM_INIT; break; #endif @@ -8070,8 +8164,8 @@ int SendKexDhInit(WOLFSSH* ssh) if (ret == WS_SUCCESS) { if (!ssh->handshake->useEcc -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - && !ssh->handshake->useSaber +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + && !ssh->handshake->useEccKyber #endif ) { #ifndef WOLFSSH_NO_DH @@ -8088,7 +8182,7 @@ int SendKexDhInit(WOLFSSH* ssh) e, &eSz); #endif } - else if (ssh->handshake->useEcc) { + else if (ssh->handshake->useEcc || ssh->handshake->useEccKyber) { #if !defined(WOLFSSH_NO_ECDH) ecc_key* privKey = &ssh->handshake->privKey.ecc; int primeId = wcPrimeForId(ssh->handshake->kexId); @@ -8120,41 +8214,41 @@ int SendKexDhInit(WOLFSSH* ssh) ret = WS_INVALID_ALGO_ID; #endif /* !defined(WOLFSSH_NO_ECDH) */ } -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - else if (ssh->handshake->useSaber) { + else { + ret = WS_INVALID_ALGO_ID; + } + +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + if (ssh->handshake->useEccKyber) { OQS_KEM* kem = NULL; ret = 0; - kem = OQS_KEM_new(OQS_KEM_alg_saber_lightsaber); + kem = OQS_KEM_new(OQS_KEM_alg_kyber_512); if (kem == NULL) { ret = WS_INVALID_ALGO_ID; } if (ret == 0) { - if (OQS_KEM_keypair(kem, e, ssh->handshake->x) + if (OQS_KEM_keypair(kem, e + eSz, ssh->handshake->x) != OQS_SUCCESS) { /* This should never happen */ ret = WS_ERROR; } - eSz = kem->length_public_key; + eSz += kem->length_public_key; ssh->handshake->xSz = kem->length_secret_key; } + + if (kem != NULL) { + OQS_KEM_free(kem); + } } #endif - else { - ret = WS_INVALID_ALGO_ID; - } if (ret == 0) ret = WS_SUCCESS; } - /* Do not want leading zero's removed for SABER. */ - if (ret == WS_SUCCESS -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - && !ssh->handshake->useSaber -#endif - ) { + if (ret == WS_SUCCESS) { ret = CreateMpint(e, &eSz, &ePad); } diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 325d12e9..259c312a 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -133,8 +133,8 @@ extern "C" { #define WOLFSSH_NO_ECDH_SHA2_ED25519 #endif #if !defined(WOLFSSH_HAVE_LIBOQS) || defined(NO_SHA256) - #undef WOLFSSH_NO_SABER_LEVEL1_SHA256 - #define WOLFSSH_NO_SABER_LEVEL1_SHA256 + #undef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + #define WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 #endif #if defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \ @@ -144,7 +144,7 @@ extern "C" { defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ defined(WOLFSSH_NO_ECDH_SHA2_ED25519) && \ - defined(WOLFSSH_NO_SABER_LEVEL1_SHA256) + defined(WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256) #error "You need at least one key agreement algorithm." #endif @@ -271,8 +271,8 @@ enum { ID_ECDH_SHA2_ED25519, ID_ECDH_SHA2_ED25519_LIBSSH, ID_DH_GROUP14_SHA256, -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - ID_SABER_LEVEL1_SHA256, +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + ID_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256, #endif /* Public Key IDs */ @@ -350,9 +350,9 @@ enum { #define WOLFSSH_DEFAULT_GEXDH_MAX 8192 #endif #ifndef MAX_KEX_KEY_SZ - #ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - /* Private key size of SABER Level1. Biggest artifact. */ - #define MAX_KEX_KEY_SZ 1568 + #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + /* Private key size of Kyber Level1. Biggest artifact. */ + #define MAX_KEX_KEY_SZ 1632 #else /* This is based on the 8192-bit DH key that is the max size. */ #define MAX_KEX_KEY_SZ (WOLFSSH_DEFAULT_GEXDH_MAX / 8) @@ -417,8 +417,8 @@ struct WOLFSSH_CTX { byte* privateKey; /* Owned by CTX */ word32 privateKeySz; byte useEcc; /* Depends on the private key */ -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - byte useSaber:1; /* Depends on the private key */ +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + byte useEccKyber; /* Depends on the private key */ #endif word32 highwaterMark; const char* banner; @@ -483,8 +483,8 @@ typedef struct HandshakeInfo { #endif byte useEcc; -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 - byte useSaber:1; +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 + byte useEccKyber; #endif union { @@ -909,13 +909,13 @@ enum WS_MessageIds { MSGID_KEXDH_INIT = 30, MSGID_KEXECDH_INIT = 30, -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 MSGID_KEXKEM_INIT = 30, #endif MSGID_KEXDH_REPLY = 31, MSGID_KEXECDH_REPLY = 31, -#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 +#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 MSGID_KEXKEM_REPLY = 31, #endif