Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for X25519MLKEM768 #551

Merged
merged 4 commits into from
Nov 21, 2024
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
2 changes: 2 additions & 0 deletions include/picotls.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ extern "C" {
#define PTLS_GROUP_NAME_X25519 "x25519"
#define PTLS_GROUP_X448 30
#define PTLS_GROUP_NAME_X448 "x448"
#define PTLS_GROUP_X25519MLKEM768 4588
#define PTLS_GROUP_NAME_X25519MLKEM768 "X25519MLKEM768"

/* signature algorithms */
#define PTLS_SIGNATURE_RSA_PKCS1_SHA1 0x0201
Expand Down
7 changes: 7 additions & 0 deletions include/picotls/openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ extern ptls_key_exchange_algorithm_t ptls_openssl_x25519;
#define PTLS_OPENSSL_HAVE_X25519 0
#define PTLS_OPENSSL_HAS_X25519 0 /* deprecated; use HAVE_ */
#endif
#ifdef OPENSSL_IS_BORINGSSL
#define PTLS_OPENSSL_HAVE_X25519MLKEM768 1
extern ptls_key_exchange_algorithm_t ptls_openssl_x25519mlkem768;
#else
#define PTLS_OPENSSL_HAVE_X25519MLKEM768 0
#endif

/* when boringssl is used, existence of libdecrepit is assumed */
#if !defined(OPENSSL_NO_BF) || defined(OPENSSL_IS_BORINGSSL)
Expand All @@ -78,6 +84,7 @@ extern ptls_key_exchange_algorithm_t ptls_openssl_x25519;
#endif

extern ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges[];
extern ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges_all[];

extern ptls_cipher_algorithm_t ptls_openssl_aes128ecb;
extern ptls_cipher_algorithm_t ptls_openssl_aes128ctr;
Expand Down
152 changes: 152 additions & 0 deletions lib/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
#ifdef OPENSSL_IS_BORINGSSL
#include "./chacha20poly1305.h"
#endif
#if PTLS_OPENSSL_HAVE_X25519MLKEM768
#include <openssl/mlkem.h>
#endif
#ifdef PTLS_HAVE_AEGIS
#include "./libaegis.h"
#endif
Expand Down Expand Up @@ -706,6 +709,135 @@ static int evp_keyex_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iovec_t

#endif

#if PTLS_OPENSSL_HAVE_X25519MLKEM768

struct st_x25519mlkem768_context_t {
ptls_key_exchange_context_t super;
uint8_t pubkey[MLKEM768_PUBLIC_KEY_BYTES + X25519_PUBLIC_VALUE_LEN];
struct {
uint8_t x25519[X25519_PRIVATE_KEY_LEN];
struct MLKEM768_private_key mlkem;
} privkey;
};

static int x25519mlkem768_on_exchange(ptls_key_exchange_context_t **_ctx, int release, ptls_iovec_t *secret,
ptls_iovec_t ciphertext)
{
struct st_x25519mlkem768_context_t *ctx = (void *)*_ctx;
int ret;

if (secret == NULL) {
ret = 0;
goto Exit;
}

*secret = ptls_iovec_init(NULL, 0);

/* validate length */
if (ciphertext.len != MLKEM768_CIPHERTEXT_BYTES + X25519_PUBLIC_VALUE_LEN) {
ret = PTLS_ALERT_DECRYPT_ERROR;
goto Exit;
}

/* appsocate memory */
secret->len = MLKEM_SHARED_SECRET_BYTES + X25519_SHARED_KEY_LEN;
if ((secret->base = malloc(secret->len)) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}

/* run key exchange */
if (!MLKEM768_decap(secret->base, ciphertext.base, MLKEM768_CIPHERTEXT_BYTES, &ctx->privkey.mlkem) ||
!X25519(secret->base + MLKEM_SHARED_SECRET_BYTES, ctx->privkey.x25519, ciphertext.base + MLKEM768_CIPHERTEXT_BYTES)) {
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
goto Exit;
}
ret = 0;

Exit:
if (secret != NULL && ret != 0) {
free(secret->base);
*secret = ptls_iovec_init(NULL, 0);
}
if (release) {
ptls_clear_memory(&ctx->privkey, sizeof(ctx->privkey));
free(ctx);
*_ctx = NULL;
}
return ret;
}

static int x25519mlkem768_create(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx)
{
struct st_x25519mlkem768_context_t *ctx = NULL;

if ((ctx = malloc(sizeof(*ctx))) == NULL)
return PTLS_ERROR_NO_MEMORY;

ctx->super = (ptls_key_exchange_context_t){algo, ptls_iovec_init(ctx->pubkey, sizeof(ctx->pubkey)), x25519mlkem768_on_exchange};
MLKEM768_generate_key(ctx->pubkey, NULL, &ctx->privkey.mlkem);
X25519_keypair(ctx->pubkey + MLKEM768_PUBLIC_KEY_BYTES, ctx->privkey.x25519);

*_ctx = &ctx->super;
return 0;
}

static int x25519mlkem768_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *ciphertext, ptls_iovec_t *secret,
ptls_iovec_t peerkey)
{
struct {
CBS cbs;
struct MLKEM768_public_key key;
} mlkem_peer;
uint8_t x25519_privkey[X25519_PRIVATE_KEY_LEN];
int ret;

*ciphertext = ptls_iovec_init(NULL, 0);
*secret = ptls_iovec_init(NULL, 0);

/* validate input length */
if (peerkey.len != MLKEM768_PUBLIC_KEY_BYTES + X25519_PUBLIC_VALUE_LEN) {
ret = PTLS_ALERT_DECODE_ERROR;
goto Exit;
}

/* allocate memory */
ciphertext->len = MLKEM768_CIPHERTEXT_BYTES + X25519_PUBLIC_VALUE_LEN;
if ((ciphertext->base = malloc(ciphertext->len)) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
secret->len = MLKEM_SHARED_SECRET_BYTES + X25519_SHARED_KEY_LEN;
if ((secret->base = malloc(secret->len)) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}

/* run key exchange */
CBS_init(&mlkem_peer.cbs, peerkey.base, MLKEM768_PUBLIC_KEY_BYTES);
X25519_keypair(ciphertext->base + MLKEM768_CIPHERTEXT_BYTES, x25519_privkey);
if (!MLKEM768_parse_public_key(&mlkem_peer.key, &mlkem_peer.cbs) ||
!X25519(secret->base + MLKEM_SHARED_SECRET_BYTES, x25519_privkey, peerkey.base + MLKEM768_PUBLIC_KEY_BYTES)) {
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
goto Exit;
}
MLKEM768_encap(ciphertext->base, secret->base, &mlkem_peer.key);

ret = 0;

Exit:
if (ret != 0) {
free(ciphertext->base);
*ciphertext = ptls_iovec_init(NULL, 0);
free(secret->base);
*secret = ptls_iovec_init(NULL, 0);
}
ptls_clear_memory(&x25519_privkey, sizeof(x25519_privkey));
return ret;
}

#endif

int ptls_openssl_create_key_exchange(ptls_key_exchange_context_t **ctx, EVP_PKEY *pkey)
{
int ret, id;
Expand Down Expand Up @@ -2063,7 +2195,27 @@ ptls_key_exchange_algorithm_t ptls_openssl_x25519 = {.id = PTLS_GROUP_X25519,
.exchange = evp_keyex_exchange,
.data = NID_X25519};
#endif
#if PTLS_OPENSSL_HAVE_X25519MLKEM768
ptls_key_exchange_algorithm_t ptls_openssl_x25519mlkem768 = {.id = PTLS_GROUP_X25519MLKEM768,
.name = PTLS_GROUP_NAME_X25519MLKEM768,
.create = x25519mlkem768_create,
.exchange = x25519mlkem768_exchange};
#endif
ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges[] = {&ptls_openssl_secp256r1, NULL};
ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges_all[] = {
#if PTLS_OPENSSL_HAVE_X25519MLKEM768
&ptls_openssl_x25519mlkem768,
#endif
#if PTLS_OPENSSL_HAVE_SECP521R1
&ptls_openssl_secp521r1,
#endif
#if PTLS_OPENSSL_HAVE_SECP384R1
&ptls_openssl_secp384r1,
#endif
#if PTLS_OPENSSL_HAVE_X25519
&ptls_openssl_x25519,
#endif
&ptls_openssl_secp256r1, NULL};
ptls_cipher_algorithm_t ptls_openssl_aes128ecb = {
"AES128-ECB", PTLS_AES128_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct cipher_context_t),
aes128ecb_setup_crypto};
Expand Down
26 changes: 9 additions & 17 deletions t/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ static void usage(const char *cmd)
#endif
#if PTLS_OPENSSL_HAVE_X25519
", X25519"
#endif
#if PTLS_OPENSSL_HAVE_X25519MLKEM768
", X5519MLKEM768"
#endif
"\n"
"Supported signature algorithms: rsa, secp256r1"
Expand Down Expand Up @@ -545,29 +548,18 @@ int main(int argc, char **argv)
/* disable use of key exchanges entirely */
ctx.key_exchanges = NULL;
} else {
ptls_key_exchange_algorithm_t *algo = NULL;
#define MATCH(name) \
if (algo == NULL && strcasecmp(optarg, #name) == 0) \
algo = (&ptls_openssl_##name)
MATCH(secp256r1);
#if PTLS_OPENSSL_HAVE_SECP384R1
MATCH(secp384r1);
#endif
#if PTLS_OPENSSL_HAVE_SECP521R1
MATCH(secp521r1);
#endif
#if PTLS_OPENSSL_HAVE_X25519
MATCH(x25519);
#endif
#undef MATCH
if (algo == NULL) {
ptls_key_exchange_algorithm_t **named;
for (named = ptls_openssl_key_exchanges_all; *named != NULL; ++named)
if (strcasecmp((*named)->name, optarg) == 0)
break;
if (*named == NULL) {
fprintf(stderr, "could not find key exchange: %s\n", optarg);
return 1;
}
size_t i;
for (i = 0; key_exchanges[i] != NULL; ++i)
;
key_exchanges[i++] = algo;
key_exchanges[i++] = *named;
}
break;
case 'u':
Expand Down
4 changes: 4 additions & 0 deletions t/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ static void test_key_exchanges(void)
subtest("x25519-to-minicrypto", test_key_exchange, &ptls_openssl_x25519, &ptls_minicrypto_x25519);
subtest("x25519-from-minicrypto", test_key_exchange, &ptls_minicrypto_x25519, &ptls_openssl_x25519);
#endif

#if PTLS_OPENSSL_HAVE_X25519MLKEM768
subtest("x25519mlkem768", test_key_exchange, &ptls_openssl_x25519mlkem768, &ptls_openssl_x25519mlkem768);
#endif
}

static void test_sign_verify(EVP_PKEY *key, const ptls_openssl_signature_scheme_t *schemes)
Expand Down
Loading