#include "includes.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <signal.h>
#include <endian.h>
#include "sshkey.h"
#include "kex.h"
#include "sshbuf.h"
#include "digest.h"
#include "ssherr.h"
#include "log.h"
#include "crypto_api.h"
#ifdef USE_MLKEM768X25519
int
kex_kem_mlkem768x25519_keypair(struct kex *kex)
{
struct sshbuf *buf = NULL;
u_char *cp = NULL;
size_t need;
int r = SSH_ERR_INTERNAL_ERROR;
if ((buf = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
need = MLKEM768_PUBLICKEYBYTES + CURVE25519_SIZE;
if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
goto out;
if (crypto_kem_mlkem768_keypair(cp, kex->mlkem768_client_key) != 0) {
r = SSH_ERR_INTERNAL_ERROR;
goto out;
}
#ifdef DEBUG_KEXECDH
dump_digest("client public key mlkem768:", cp,
MLKEM768_PUBLICKEYBYTES);
#endif
cp += MLKEM768_PUBLICKEYBYTES;
kexc25519_keygen(kex->c25519_client_key, cp);
#ifdef DEBUG_KEXECDH
dump_digest("client public key c25519:", cp, CURVE25519_SIZE);
#endif
r = 0;
kex->client_pub = buf;
buf = NULL;
out:
sshbuf_free(buf);
return r;
}
int
kex_kem_mlkem768x25519_enc(struct kex *kex,
const struct sshbuf *client_blob, struct sshbuf **server_blobp,
struct sshbuf **shared_secretp)
{
struct sshbuf *server_blob = NULL;
struct sshbuf *buf = NULL;
const u_char *client_pub;
u_char server_pub[CURVE25519_SIZE], server_key[CURVE25519_SIZE];
u_char hash[SSH_DIGEST_MAX_LENGTH];
u_char ct[MLKEM768_CIPHERTEXTBYTES];
u_char shared_secret[MLKEM768_BYTES];
size_t need;
int r = SSH_ERR_INTERNAL_ERROR;
*server_blobp = NULL;
*shared_secretp = NULL;
need = MLKEM768_PUBLICKEYBYTES + CURVE25519_SIZE;
if (sshbuf_len(client_blob) != need) {
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
client_pub = sshbuf_ptr(client_blob);
#ifdef DEBUG_KEXECDH
dump_digest("client public key mlkem768:", client_pub,
MLKEM768_PUBLICKEYBYTES);
dump_digest("client public key 25519:",
client_pub + MLKEM768_PUBLICKEYBYTES,
CURVE25519_SIZE);
#endif
if ((buf = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((server_blob = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (crypto_kem_mlkem768_enc(ct, shared_secret, client_pub) != 0) {
r = SSH_ERR_INTERNAL_ERROR;
goto out;
}
kexc25519_keygen(server_key, server_pub);
if ((r = sshbuf_put(buf, shared_secret, sizeof(shared_secret))) != 0 ||
(r = sshbuf_put(server_blob, ct, sizeof(ct))) != 0 ||
(r = sshbuf_put(server_blob, server_pub, sizeof(server_pub))) != 0)
goto out;
client_pub += MLKEM768_PUBLICKEYBYTES;
if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 1)) < 0)
goto out;
if ((r = ssh_digest_buffer(kex->hash_alg, buf,
hash, sizeof(hash))) != 0)
goto out;
#ifdef DEBUG_KEXECDH
dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE);
dump_digest("server cipher text:", ct, sizeof(ct));
dump_digest("server kem key:", shared_secret, sizeof(shared_secret));
dump_digest("concatenation of KEM key and ECDH shared key:",
sshbuf_ptr(buf), sshbuf_len(buf));
#endif
sshbuf_reset(buf);
if ((r = sshbuf_put_string(buf, hash,
ssh_digest_bytes(kex->hash_alg))) != 0)
goto out;
#ifdef DEBUG_KEXECDH
dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
#endif
r = 0;
*server_blobp = server_blob;
*shared_secretp = buf;
server_blob = NULL;
buf = NULL;
out:
explicit_bzero(hash, sizeof(hash));
explicit_bzero(server_key, sizeof(server_key));
explicit_bzero(shared_secret, sizeof(shared_secret));
sshbuf_free(server_blob);
sshbuf_free(buf);
return r;
}
int
kex_kem_mlkem768x25519_dec(struct kex *kex,
const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
{
struct sshbuf *buf = NULL;
u_char shared_secret[MLKEM768_BYTES];
const u_char *ciphertext, *server_pub;
u_char hash[SSH_DIGEST_MAX_LENGTH];
size_t need;
int r;
*shared_secretp = NULL;
need = MLKEM768_CIPHERTEXTBYTES + CURVE25519_SIZE;
if (sshbuf_len(server_blob) != need) {
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
ciphertext = sshbuf_ptr(server_blob);
server_pub = ciphertext + MLKEM768_CIPHERTEXTBYTES;
if ((buf = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
#ifdef DEBUG_KEXECDH
dump_digest("server cipher text:", ciphertext,
MLKEM768_CIPHERTEXTBYTES);
dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE);
#endif
if (crypto_kem_mlkem768_dec(shared_secret, ciphertext,
kex->mlkem768_client_key) != 0) {
r = SSH_ERR_INTERNAL_ERROR;
goto out;
}
if ((r = sshbuf_put(buf, shared_secret, sizeof(shared_secret))) != 0)
goto out;
if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub,
buf, 1)) < 0)
goto out;
if ((r = ssh_digest_buffer(kex->hash_alg, buf,
hash, sizeof(hash))) != 0)
goto out;
#ifdef DEBUG_KEXECDH
dump_digest("client kem key:", shared_secret, sizeof(shared_secret));
dump_digest("concatenation of KEM key and ECDH shared key:",
sshbuf_ptr(buf), sshbuf_len(buf));
#endif
sshbuf_reset(buf);
if ((r = sshbuf_put_string(buf, hash,
ssh_digest_bytes(kex->hash_alg))) != 0)
goto out;
#ifdef DEBUG_KEXECDH
dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
#endif
r = 0;
*shared_secretp = buf;
buf = NULL;
out:
explicit_bzero(hash, sizeof(hash));
explicit_bzero(shared_secret, sizeof(shared_secret));
sshbuf_free(buf);
return r;
}
#else
int
kex_kem_mlkem768x25519_keypair(struct kex *kex)
{
return SSH_ERR_SIGN_ALG_UNSUPPORTED;
}
int
kex_kem_mlkem768x25519_enc(struct kex *kex,
const struct sshbuf *client_blob, struct sshbuf **server_blobp,
struct sshbuf **shared_secretp)
{
return SSH_ERR_SIGN_ALG_UNSUPPORTED;
}
int
kex_kem_mlkem768x25519_dec(struct kex *kex,
const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
{
return SSH_ERR_SIGN_ALG_UNSUPPORTED;
}
#endif