mirror of
https://github.com/openbsd/src.git
synced 2026-06-18 07:13:36 +02:00
Add experimental support for a composite post-quantum signature
scheme that combines ML-DSA 44 and Ed25519 using the construction specified in draft-ietf-lamps-pq-composite-sigs. There's also an early draft documenting use of the integration of this scheme into SSH as draft-miller-sshm-mldsa44-ed25519-composite-sigs This scheme is not enabled by default. To you use, you'll need to add it to HostKeyAlgorithms, PubkeyAcceptedAlgorithms, etc. Keys may be generated using "ssh-keygen -t mldsa44-ed25519". The ML-DSA implementation comes from libcrux. Thanks to Jonas Schneider-Bensch and Jonathan Protzenko for their work to make this available. Consensus is that it's time to get this in to allow people to experiment with it. feedback markus@ tb@ logan@ deraadt@
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# $OpenBSD: Makefile.inc,v 1.105 2026/02/06 23:31:29 dtucker Exp $
|
||||
# $OpenBSD: Makefile.inc,v 1.106 2026/06/14 03:59:34 djm Exp $
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
@@ -93,6 +93,8 @@ SRCS_KEY+= cipher-chachapoly.c
|
||||
.endif
|
||||
SRCS_KEY+= ssh-ed25519.c
|
||||
SRCS_KEY+= ssh-ed25519-sk.c
|
||||
SRCS_KEY+= libcrux-mlkem-mldsa.c
|
||||
SRCS_KEY+= ssh-mldsa-eddsa.c
|
||||
SRCS_KEY+= ssherr-libcrypto.c
|
||||
|
||||
.if (${OPENSSL:L} == "yes")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: authfd.c,v 1.141 2026/03/05 05:44:15 djm Exp $ */
|
||||
/* $OpenBSD: authfd.c,v 1.142 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@@ -607,6 +607,8 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
|
||||
#endif
|
||||
case KEY_ED25519:
|
||||
case KEY_ED25519_CERT:
|
||||
case KEY_MLDSA44_ED25519:
|
||||
case KEY_MLDSA44_ED25519_CERT:
|
||||
case KEY_ED25519_SK:
|
||||
case KEY_ED25519_SK_CERT:
|
||||
type = constrained ?
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: authfile.c,v 1.149 2026/02/14 00:18:34 jsg Exp $ */
|
||||
/* $OpenBSD: authfile.c,v 1.150 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
|
||||
*
|
||||
@@ -317,6 +317,7 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
|
||||
case KEY_ECDSA:
|
||||
#endif /* WITH_OPENSSL */
|
||||
case KEY_ED25519:
|
||||
case KEY_MLDSA44_ED25519:
|
||||
case KEY_UNSPEC:
|
||||
break;
|
||||
default:
|
||||
|
||||
+126
-1
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: crypto_api.h,v 1.10 2025/10/30 23:19:33 djm Exp $ */
|
||||
/* $OpenBSD: crypto_api.h,v 1.11 2026/06/14 03:59:34 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Assembled from generated headers and source files by Markus Friedl.
|
||||
@@ -54,6 +54,7 @@ crypto_hash_sha512(unsigned char *out, const unsigned char *in,
|
||||
|
||||
#define crypto_sign_ed25519_SECRETKEYBYTES 64U
|
||||
#define crypto_sign_ed25519_PUBLICKEYBYTES 32U
|
||||
#define crypto_sign_ed25519_SEEDBYTES 32U
|
||||
#define crypto_sign_ed25519_BYTES 64U
|
||||
|
||||
int crypto_sign_ed25519(unsigned char *, unsigned long long *,
|
||||
@@ -61,11 +62,15 @@ int crypto_sign_ed25519(unsigned char *, unsigned long long *,
|
||||
int crypto_sign_ed25519_open(unsigned char *, unsigned long long *,
|
||||
const unsigned char *, unsigned long long, const unsigned char *);
|
||||
int crypto_sign_ed25519_keypair(unsigned char *, unsigned char *);
|
||||
int crypto_sign_ed25519_keypair_from_seed(unsigned char *, unsigned char *,
|
||||
const unsigned char *);
|
||||
|
||||
#define crypto_kem_sntrup761_PUBLICKEYBYTES 1158
|
||||
#define crypto_kem_sntrup761_SECRETKEYBYTES 1763
|
||||
#define crypto_kem_sntrup761_CIPHERTEXTBYTES 1039
|
||||
#define crypto_kem_sntrup761_BYTES 32
|
||||
#define crypto_kem_mlkem768_ENCSEEDBYTES 32
|
||||
#define crypto_kem_mlkem768_KEYPAIRSEEDBYTES 64
|
||||
|
||||
int crypto_kem_sntrup761_enc(unsigned char *cstr, unsigned char *k,
|
||||
const unsigned char *pk);
|
||||
@@ -73,9 +78,129 @@ int crypto_kem_sntrup761_dec(unsigned char *k,
|
||||
const unsigned char *cstr, const unsigned char *sk);
|
||||
int crypto_kem_sntrup761_keypair(unsigned char *pk, unsigned char *sk);
|
||||
|
||||
/* ML-KEM-768 */
|
||||
#define crypto_kem_mlkem768_PUBLICKEYBYTES 1184
|
||||
#define crypto_kem_mlkem768_SECRETKEYBYTES 2400
|
||||
#define crypto_kem_mlkem768_CIPHERTEXTBYTES 1088
|
||||
#define crypto_kem_mlkem768_BYTES 32
|
||||
|
||||
/* Aliases for MLKEM768 */
|
||||
#define MLKEM768_PUBLICKEYBYTES crypto_kem_mlkem768_PUBLICKEYBYTES
|
||||
#define MLKEM768_SECRETKEYBYTES crypto_kem_mlkem768_SECRETKEYBYTES
|
||||
#define MLKEM768_CIPHERTEXTBYTES crypto_kem_mlkem768_CIPHERTEXTBYTES
|
||||
#define MLKEM768_BYTES crypto_kem_mlkem768_BYTES
|
||||
|
||||
int crypto_kem_mlkem768_keypair(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES]);
|
||||
int crypto_kem_mlkem768_enc(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
|
||||
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES]);
|
||||
int crypto_kem_mlkem768_dec(uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
|
||||
const uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
const uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES]);
|
||||
int crypto_kem_mlkem768_keypair_seeded(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES], const uint8_t seed[64]);
|
||||
int crypto_kem_mlkem768_enc_seeded(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
|
||||
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
const uint8_t seed[32]);
|
||||
|
||||
/* ML-DSA-44 */
|
||||
#define MLDSA44_PUBLICKEYBYTES 1312
|
||||
#define MLDSA44_SECRETKEYBYTES 2560
|
||||
#define MLDSA44_SIGBYTES 2420
|
||||
#define MLDSA44_SEEDBYTES 32
|
||||
|
||||
int crypto_sign_mldsa44_keypair(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA44_SECRETKEYBYTES]);
|
||||
int crypto_sign_mldsa44(uint8_t sig[MLDSA44_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA44_SECRETKEYBYTES]);
|
||||
int crypto_sign_mldsa44_verify(const uint8_t sig[MLDSA44_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA44_PUBLICKEYBYTES]);
|
||||
int crypto_sign_mldsa44_keypair_seeded(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA44_SECRETKEYBYTES], const uint8_t seed[MLDSA44_SEEDBYTES]);
|
||||
int crypto_sign_mldsa44_seeded(uint8_t sig[MLDSA44_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA44_SECRETKEYBYTES],
|
||||
const uint8_t seed[MLDSA44_SEEDBYTES]);
|
||||
|
||||
/* ML-DSA-65 */
|
||||
#define MLDSA65_PUBLICKEYBYTES 1952
|
||||
#define MLDSA65_SECRETKEYBYTES 4032
|
||||
#define MLDSA65_SIGBYTES 3309
|
||||
#define MLDSA65_SEEDBYTES 32
|
||||
|
||||
#if 0
|
||||
int crypto_sign_mldsa65_keypair(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA65_SECRETKEYBYTES]);
|
||||
int crypto_sign_mldsa65(uint8_t sig[MLDSA65_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA65_SECRETKEYBYTES]);
|
||||
int crypto_sign_mldsa65_verify(const uint8_t sig[MLDSA65_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA65_PUBLICKEYBYTES]);
|
||||
int crypto_sign_mldsa65_keypair_seeded(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA65_SECRETKEYBYTES], const uint8_t seed[MLDSA65_SEEDBYTES]);
|
||||
int crypto_sign_mldsa65_seeded(uint8_t sig[MLDSA65_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA65_SECRETKEYBYTES],
|
||||
const uint8_t seed[MLDSA65_SEEDBYTES]);
|
||||
#endif
|
||||
|
||||
/* ML-DSA-87 */
|
||||
#define MLDSA87_PUBLICKEYBYTES 2592
|
||||
#define MLDSA87_SECRETKEYBYTES 4896
|
||||
#define MLDSA87_SIGBYTES 4627
|
||||
#define MLDSA87_SEEDBYTES 32
|
||||
|
||||
int crypto_sign_mldsa87_keypair(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA87_SECRETKEYBYTES]);
|
||||
int crypto_sign_mldsa87(uint8_t sig[MLDSA87_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA87_SECRETKEYBYTES]);
|
||||
int crypto_sign_mldsa87_verify(const uint8_t sig[MLDSA87_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA87_PUBLICKEYBYTES]);
|
||||
int crypto_sign_mldsa87_keypair_seeded(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA87_SECRETKEYBYTES], const uint8_t seed[MLDSA87_SEEDBYTES]);
|
||||
int crypto_sign_mldsa87_seeded(uint8_t sig[MLDSA87_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA87_SECRETKEYBYTES],
|
||||
const uint8_t seed[MLDSA87_SEEDBYTES]);
|
||||
|
||||
/* MLDSA44-Ed25519-SHA512 */
|
||||
#define MLDSA44_ED25519_PK_SZ (MLDSA44_PUBLICKEYBYTES + crypto_sign_ed25519_PUBLICKEYBYTES)
|
||||
#define MLDSA44_ED25519_SK_SZ (MLDSA44_SEEDBYTES + crypto_sign_ed25519_SEEDBYTES)
|
||||
#define MLDSA44_ED25519_SIG_SZ (MLDSA44_SIGBYTES + crypto_sign_ed25519_BYTES)
|
||||
|
||||
int crypto_sign_mldsa44_ed25519_keygen(uint8_t pk[MLDSA44_ED25519_PK_SZ],
|
||||
uint8_t sk[MLDSA44_ED25519_SK_SZ]);
|
||||
int crypto_sign_mldsa44_ed25519_keygen_seeded(uint8_t pk[MLDSA44_ED25519_PK_SZ],
|
||||
uint8_t sk[MLDSA44_ED25519_SK_SZ],
|
||||
const uint8_t mldsa_seed[MLDSA44_SEEDBYTES],
|
||||
const uint8_t ed25519_seed[crypto_sign_ed25519_SEEDBYTES]);
|
||||
int crypto_sign_mldsa44_ed25519_sign(uint8_t sig[MLDSA44_ED25519_SIG_SZ],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA44_ED25519_SK_SZ]);
|
||||
int crypto_sign_mldsa44_ed25519_verify(const uint8_t sig[MLDSA44_ED25519_SIG_SZ],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA44_ED25519_PK_SZ]);
|
||||
|
||||
/* Utility */
|
||||
void sha3_256(uint8_t digest[32], const uint8_t *data, size_t len);
|
||||
void sha3_512(uint8_t digest[64], const uint8_t *data, size_t len);
|
||||
|
||||
#endif /* crypto_api_h */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ed25519-openssl.c,v 1.1 2025/10/30 20:49:10 djm Exp $ */
|
||||
/* $OpenBSD: ed25519-openssl.c,v 1.2 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2025 OpenSSH
|
||||
*
|
||||
@@ -91,6 +91,38 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_ed25519_keypair_from_seed(unsigned char *pk, unsigned char *sk,
|
||||
const unsigned char *seed)
|
||||
{
|
||||
EVP_PKEY *pkey = NULL;
|
||||
size_t pklen;
|
||||
int ret = -1;
|
||||
|
||||
if ((pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
|
||||
seed, SSH_ED25519_RAW_SECRET_KEY_LEN)) == NULL) {
|
||||
debug3_f("EVP_PKEY_new_raw_private_key failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Extract public key */
|
||||
pklen = crypto_sign_ed25519_PUBLICKEYBYTES;
|
||||
if (!EVP_PKEY_get_raw_public_key(pkey, pk, &pklen)) {
|
||||
debug3_f("EVP_PKEY_get_raw_public_key failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Build sk = seed || pk */
|
||||
memcpy(sk, seed, SSH_ED25519_RAW_SECRET_KEY_LEN);
|
||||
memcpy(sk + SSH_ED25519_RAW_SECRET_KEY_LEN, pk,
|
||||
crypto_sign_ed25519_PUBLICKEYBYTES);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
EVP_PKEY_free(pkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_ed25519(unsigned char *sm, unsigned long long *smlen,
|
||||
const unsigned char *m, unsigned long long mlen,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: kexmlkem768x25519.c,v 1.2 2024/10/27 02:06:59 djm Exp $ */
|
||||
/* $OpenBSD: kexmlkem768x25519.c,v 1.3 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2023 Markus Friedl. All rights reserved.
|
||||
*
|
||||
@@ -39,32 +39,30 @@
|
||||
#include "ssherr.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "libcrux_mlkem768_sha3.h"
|
||||
#include "crypto_api.h"
|
||||
|
||||
int
|
||||
kex_kem_mlkem768x25519_keypair(struct kex *kex)
|
||||
{
|
||||
struct sshbuf *buf = NULL;
|
||||
u_char rnd[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN], *cp = NULL;
|
||||
u_char *cp = NULL;
|
||||
size_t need;
|
||||
int r = SSH_ERR_INTERNAL_ERROR;
|
||||
struct libcrux_mlkem768_keypair keypair;
|
||||
|
||||
if ((buf = sshbuf_new()) == NULL)
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
need = crypto_kem_mlkem768_PUBLICKEYBYTES + CURVE25519_SIZE;
|
||||
need = MLKEM768_PUBLICKEYBYTES + CURVE25519_SIZE;
|
||||
if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
|
||||
goto out;
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
|
||||
memcpy(cp, keypair.pk.value, crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
memcpy(kex->mlkem768_client_key, keypair.sk.value,
|
||||
sizeof(kex->mlkem768_client_key));
|
||||
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,
|
||||
crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
MLKEM768_PUBLICKEYBYTES);
|
||||
#endif
|
||||
cp += crypto_kem_mlkem768_PUBLICKEYBYTES;
|
||||
cp += MLKEM768_PUBLICKEYBYTES;
|
||||
kexc25519_keygen(kex->c25519_client_key, cp);
|
||||
#ifdef DEBUG_KEXECDH
|
||||
dump_digest("client public key c25519:", cp, CURVE25519_SIZE);
|
||||
@@ -74,8 +72,6 @@ kex_kem_mlkem768x25519_keypair(struct kex *kex)
|
||||
kex->client_pub = buf;
|
||||
buf = NULL;
|
||||
out:
|
||||
explicit_bzero(&keypair, sizeof(keypair));
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
sshbuf_free(buf);
|
||||
return r;
|
||||
}
|
||||
@@ -88,20 +84,18 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
|
||||
struct sshbuf *server_blob = NULL;
|
||||
struct sshbuf *buf = NULL;
|
||||
const u_char *client_pub;
|
||||
u_char rnd[LIBCRUX_ML_KEM_ENC_PRNG_LEN];
|
||||
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;
|
||||
struct libcrux_mlkem768_enc_result enc;
|
||||
struct libcrux_mlkem768_pk mlkem_pub;
|
||||
|
||||
*server_blobp = NULL;
|
||||
*shared_secretp = NULL;
|
||||
memset(&mlkem_pub, 0, sizeof(mlkem_pub));
|
||||
|
||||
/* client_blob contains both KEM and ECDH client pubkeys */
|
||||
need = crypto_kem_mlkem768_PUBLICKEYBYTES + CURVE25519_SIZE;
|
||||
need = MLKEM768_PUBLICKEYBYTES + CURVE25519_SIZE;
|
||||
if (sshbuf_len(client_blob) != need) {
|
||||
r = SSH_ERR_SIGNATURE_INVALID;
|
||||
goto out;
|
||||
@@ -109,17 +103,11 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
|
||||
client_pub = sshbuf_ptr(client_blob);
|
||||
#ifdef DEBUG_KEXECDH
|
||||
dump_digest("client public key mlkem768:", client_pub,
|
||||
crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
MLKEM768_PUBLICKEYBYTES);
|
||||
dump_digest("client public key 25519:",
|
||||
client_pub + crypto_kem_mlkem768_PUBLICKEYBYTES,
|
||||
client_pub + MLKEM768_PUBLICKEYBYTES,
|
||||
CURVE25519_SIZE);
|
||||
#endif
|
||||
/* check public key validity */
|
||||
memcpy(mlkem_pub.value, client_pub, crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&mlkem_pub)) {
|
||||
r = SSH_ERR_SIGNATURE_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate buffer for concatenation of KEM key and ECDH shared key */
|
||||
/* the buffer will be hashed and the result is the shared secret */
|
||||
@@ -133,25 +121,27 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
|
||||
goto out;
|
||||
}
|
||||
/* generate and encrypt KEM key with client key */
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&mlkem_pub, rnd);
|
||||
if (crypto_kem_mlkem768_enc(ct, shared_secret, client_pub) != 0) {
|
||||
r = SSH_ERR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
/* generate ECDH key pair, store server pubkey after ciphertext */
|
||||
kexc25519_keygen(server_key, server_pub);
|
||||
if ((r = sshbuf_put(buf, enc.snd, sizeof(enc.snd))) != 0 ||
|
||||
(r = sshbuf_put(server_blob, enc.fst.value, sizeof(enc.fst.value))) != 0 ||
|
||||
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;
|
||||
/* append ECDH shared key */
|
||||
client_pub += crypto_kem_mlkem768_PUBLICKEYBYTES;
|
||||
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)
|
||||
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:",
|
||||
enc.fst.value, sizeof(enc.fst.value));
|
||||
dump_digest("server kem key:", enc.snd, sizeof(enc.snd));
|
||||
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
|
||||
@@ -172,8 +162,7 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
|
||||
out:
|
||||
explicit_bzero(hash, sizeof(hash));
|
||||
explicit_bzero(server_key, sizeof(server_key));
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
explicit_bzero(&enc, sizeof(enc));
|
||||
explicit_bzero(shared_secret, sizeof(shared_secret));
|
||||
sshbuf_free(server_blob);
|
||||
sshbuf_free(buf);
|
||||
return r;
|
||||
@@ -184,42 +173,37 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
|
||||
const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
|
||||
{
|
||||
struct sshbuf *buf = NULL;
|
||||
u_char mlkem_key[crypto_kem_mlkem768_BYTES];
|
||||
u_char shared_secret[MLKEM768_BYTES];
|
||||
const u_char *ciphertext, *server_pub;
|
||||
u_char hash[SSH_DIGEST_MAX_LENGTH];
|
||||
size_t need;
|
||||
int r;
|
||||
struct libcrux_mlkem768_sk mlkem_priv;
|
||||
struct libcrux_mlkem768_ciphertext mlkem_ciphertext;
|
||||
|
||||
*shared_secretp = NULL;
|
||||
memset(&mlkem_priv, 0, sizeof(mlkem_priv));
|
||||
memset(&mlkem_ciphertext, 0, sizeof(mlkem_ciphertext));
|
||||
|
||||
need = crypto_kem_mlkem768_CIPHERTEXTBYTES + CURVE25519_SIZE;
|
||||
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 + crypto_kem_mlkem768_CIPHERTEXTBYTES;
|
||||
server_pub = ciphertext + MLKEM768_CIPHERTEXTBYTES;
|
||||
/* hash concatenation of KEM key and ECDH shared key */
|
||||
if ((buf = sshbuf_new()) == NULL) {
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(mlkem_priv.value, kex->mlkem768_client_key,
|
||||
sizeof(kex->mlkem768_client_key));
|
||||
memcpy(mlkem_ciphertext.value, ciphertext,
|
||||
sizeof(mlkem_ciphertext.value));
|
||||
#ifdef DEBUG_KEXECDH
|
||||
dump_digest("server cipher text:", mlkem_ciphertext.value,
|
||||
sizeof(mlkem_ciphertext.value));
|
||||
dump_digest("server cipher text:", ciphertext,
|
||||
MLKEM768_CIPHERTEXTBYTES);
|
||||
dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE);
|
||||
#endif
|
||||
libcrux_ml_kem_mlkem768_portable_decapsulate(&mlkem_priv,
|
||||
&mlkem_ciphertext, mlkem_key);
|
||||
if ((r = sshbuf_put(buf, mlkem_key, sizeof(mlkem_key))) != 0)
|
||||
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)
|
||||
@@ -228,7 +212,7 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
|
||||
hash, sizeof(hash))) != 0)
|
||||
goto out;
|
||||
#ifdef DEBUG_KEXECDH
|
||||
dump_digest("client kem key:", mlkem_key, sizeof(mlkem_key));
|
||||
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
|
||||
@@ -245,9 +229,7 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
|
||||
buf = NULL;
|
||||
out:
|
||||
explicit_bzero(hash, sizeof(hash));
|
||||
explicit_bzero(&mlkem_priv, sizeof(mlkem_priv));
|
||||
explicit_bzero(&mlkem_ciphertext, sizeof(mlkem_ciphertext));
|
||||
explicit_bzero(mlkem_key, sizeof(mlkem_key));
|
||||
explicit_bzero(shared_secret, sizeof(shared_secret));
|
||||
sshbuf_free(buf);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
/* $OpenBSD: libcrux-mlkem-mldsa.c,v 1.1 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2026 Damien Miller <djm@mindrot.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "crypto_api.h"
|
||||
#include "libcrux_internal.h"
|
||||
|
||||
/* ML-KEM 768 */
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_keypair(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[crypto_kem_mlkem768_KEYPAIRSEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_kem_mlkem768_keypair_seeded(pk, sk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_keypair_seeded(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES],
|
||||
const uint8_t seed[crypto_kem_mlkem768_KEYPAIRSEEDBYTES])
|
||||
{
|
||||
libcrux_mlkem768_keypair keypair;
|
||||
libcrux_mlkem768_keypair_rnd rnd;
|
||||
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
|
||||
memcpy(pk, keypair.pk.data, crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
memcpy(sk, keypair.sk.data, crypto_kem_mlkem768_SECRETKEYBYTES);
|
||||
|
||||
explicit_bzero(&keypair, sizeof(keypair));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_enc(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
|
||||
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[crypto_kem_mlkem768_ENCSEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_kem_mlkem768_enc_seeded(ct, shared_secret, pk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_enc_seeded(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
|
||||
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
|
||||
const uint8_t seed[crypto_kem_mlkem768_ENCSEEDBYTES])
|
||||
{
|
||||
libcrux_mlkem768_enc_result enc;
|
||||
libcrux_mlkem768_pk pk_internal;
|
||||
libcrux_mlkem768_enc_rnd rnd;
|
||||
|
||||
memcpy(pk_internal.data, pk, crypto_kem_mlkem768_PUBLICKEYBYTES);
|
||||
if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&pk_internal))
|
||||
return -1;
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&pk_internal, rnd);
|
||||
memcpy(ct, enc.fst.data, crypto_kem_mlkem768_CIPHERTEXTBYTES);
|
||||
memcpy(shared_secret, enc.snd.data, crypto_kem_mlkem768_BYTES);
|
||||
|
||||
explicit_bzero(&enc, sizeof(enc));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_kem_mlkem768_dec(uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
|
||||
const uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
|
||||
const uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES])
|
||||
{
|
||||
libcrux_mlkem768_sk sk_internal;
|
||||
libcrux_mlkem768_ciphertext ct_internal;
|
||||
libcrux_mlkem768_dec_result shared_secret_internal;
|
||||
|
||||
memcpy(sk_internal.data, sk, crypto_kem_mlkem768_SECRETKEYBYTES);
|
||||
memcpy(ct_internal.data, ct, crypto_kem_mlkem768_CIPHERTEXTBYTES);
|
||||
shared_secret_internal = libcrux_ml_kem_mlkem768_portable_decapsulate(
|
||||
&sk_internal, &ct_internal);
|
||||
memcpy(shared_secret, shared_secret_internal.data,
|
||||
crypto_kem_mlkem768_BYTES);
|
||||
|
||||
explicit_bzero(&sk_internal, sizeof(sk_internal));
|
||||
explicit_bzero(&shared_secret_internal, sizeof(shared_secret_internal));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ML-DSA 44 */
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_keypair(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA44_SECRETKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[MLDSA44_SEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_sign_mldsa44_keypair_seeded(pk, sk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_keypair_seeded(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA44_SECRETKEYBYTES], const uint8_t seed[MLDSA44_SEEDBYTES])
|
||||
{
|
||||
libcrux_mldsa44_keypair_rnd rnd;
|
||||
libcrux_mldsa44_keypair keypair;
|
||||
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
keypair = libcrux_ml_dsa_ml_dsa_44_portable_generate_key_pair(rnd);
|
||||
memcpy(pk, keypair.verification_key.data, MLDSA44_PUBLICKEYBYTES);
|
||||
memcpy(sk, keypair.signing_key.data, MLDSA44_SECRETKEYBYTES);
|
||||
|
||||
explicit_bzero(&keypair, sizeof(keypair));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44(uint8_t sig[MLDSA44_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA44_SECRETKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[MLDSA44_SEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_sign_mldsa44_seeded(sig, msg, msglen, ctx, ctxlen, sk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_seeded(uint8_t sig[MLDSA44_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA44_SECRETKEYBYTES],
|
||||
const uint8_t seed[MLDSA44_SEEDBYTES])
|
||||
{
|
||||
libcrux_mldsa44_sign_rnd rnd;
|
||||
libcrux_mldsa44_sk sk_internal;
|
||||
libcrux_mldsa44_message message = { msg, msglen };
|
||||
libcrux_mldsa44_message context = { ctx, ctxlen };
|
||||
libcrux_mldsa44_sign_result res;
|
||||
int r = -1;
|
||||
|
||||
memcpy(sk_internal.data, sk, MLDSA44_SECRETKEYBYTES);
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
res = libcrux_ml_dsa_ml_dsa_44_portable_sign(&sk_internal,
|
||||
message, context, rnd);
|
||||
if (res.tag == LIBCRUX_RESULT_OK) {
|
||||
memcpy(sig, res.val.case_Ok.data, MLDSA44_SIGBYTES);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
explicit_bzero(&sk_internal, sizeof(sk_internal));
|
||||
explicit_bzero(&res, sizeof(res));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_verify(const uint8_t sig[MLDSA44_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA44_PUBLICKEYBYTES])
|
||||
{
|
||||
libcrux_mldsa44_pk pk_internal;
|
||||
libcrux_mldsa44_signature sig_internal;
|
||||
libcrux_mldsa44_message message = { msg, msglen };
|
||||
libcrux_mldsa44_message context = { ctx, ctxlen };
|
||||
libcrux_mldsa44_verify_result res;
|
||||
|
||||
memcpy(pk_internal.data, pk, MLDSA44_PUBLICKEYBYTES);
|
||||
memcpy(sig_internal.data, sig, MLDSA44_SIGBYTES);
|
||||
res = libcrux_ml_dsa_ml_dsa_44_portable_verify(&pk_internal,
|
||||
message, context, &sig_internal);
|
||||
|
||||
return (res.tag == LIBCRUX_RESULT_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
/* ML-DSA 65 */
|
||||
|
||||
#if 0
|
||||
int
|
||||
crypto_sign_mldsa65_keypair(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA65_SECRETKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[MLDSA65_SEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_sign_mldsa65_keypair_seeded(pk, sk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa65_keypair_seeded(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA65_SECRETKEYBYTES], const uint8_t seed[MLDSA65_SEEDBYTES])
|
||||
{
|
||||
libcrux_mldsa65_keypair_rnd rnd;
|
||||
libcrux_mldsa65_keypair keypair;
|
||||
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
keypair = libcrux_ml_dsa_ml_dsa_65_portable_generate_key_pair(rnd);
|
||||
memcpy(pk, keypair.verification_key.data, MLDSA65_PUBLICKEYBYTES);
|
||||
memcpy(sk, keypair.signing_key.data, MLDSA65_SECRETKEYBYTES);
|
||||
|
||||
explicit_bzero(&keypair, sizeof(keypair));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa65(uint8_t sig[MLDSA65_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA65_SECRETKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[MLDSA65_SEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_sign_mldsa65_seeded(sig, msg, msglen, ctx, ctxlen, sk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa65_seeded(uint8_t sig[MLDSA65_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA65_SECRETKEYBYTES],
|
||||
const uint8_t seed[MLDSA65_SEEDBYTES])
|
||||
{
|
||||
libcrux_mldsa65_sign_rnd rnd;
|
||||
libcrux_mldsa65_sk sk_internal;
|
||||
libcrux_mldsa65_message message = { msg, msglen };
|
||||
libcrux_mldsa65_message context = { ctx, ctxlen };
|
||||
libcrux_mldsa65_sign_result res;
|
||||
int r = -1;
|
||||
|
||||
memcpy(sk_internal.data, sk, MLDSA65_SECRETKEYBYTES);
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
res = libcrux_ml_dsa_ml_dsa_65_portable_sign(&sk_internal,
|
||||
message, context, rnd);
|
||||
if (res.tag == LIBCRUX_RESULT_OK) {
|
||||
memcpy(sig, res.val.case_Ok.data, MLDSA65_SIGBYTES);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
explicit_bzero(&sk_internal, sizeof(sk_internal));
|
||||
explicit_bzero(&res, sizeof(res));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa65_verify(const uint8_t sig[MLDSA65_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA65_PUBLICKEYBYTES])
|
||||
{
|
||||
libcrux_mldsa65_pk pk_internal;
|
||||
libcrux_mldsa65_signature sig_internal;
|
||||
libcrux_mldsa65_message message = { msg, msglen };
|
||||
libcrux_mldsa65_message context = { ctx, ctxlen };
|
||||
libcrux_mldsa65_verify_result res;
|
||||
|
||||
memcpy(pk_internal.data, pk, MLDSA65_PUBLICKEYBYTES);
|
||||
memcpy(sig_internal.data, sig, MLDSA65_SIGBYTES);
|
||||
res = libcrux_ml_dsa_ml_dsa_65_portable_verify(&pk_internal,
|
||||
message, context, &sig_internal);
|
||||
|
||||
return (res.tag == LIBCRUX_RESULT_OK) ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ML-DSA 87 */
|
||||
|
||||
#if 0
|
||||
int
|
||||
crypto_sign_mldsa87_keypair(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA87_SECRETKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[MLDSA87_SEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_sign_mldsa87_keypair_seeded(pk, sk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa87_keypair_seeded(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
|
||||
uint8_t sk[MLDSA87_SECRETKEYBYTES], const uint8_t seed[MLDSA87_SEEDBYTES])
|
||||
{
|
||||
libcrux_mldsa87_keypair_rnd rnd;
|
||||
libcrux_mldsa87_keypair keypair;
|
||||
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
keypair = libcrux_ml_dsa_ml_dsa_87_portable_generate_key_pair(rnd);
|
||||
memcpy(pk, keypair.verification_key.data, MLDSA87_PUBLICKEYBYTES);
|
||||
memcpy(sk, keypair.signing_key.data, MLDSA87_SECRETKEYBYTES);
|
||||
|
||||
explicit_bzero(&keypair, sizeof(keypair));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa87(uint8_t sig[MLDSA87_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA87_SECRETKEYBYTES])
|
||||
{
|
||||
uint8_t rnd[MLDSA87_SEEDBYTES];
|
||||
int r;
|
||||
|
||||
arc4random_buf(rnd, sizeof(rnd));
|
||||
r = crypto_sign_mldsa87_seeded(sig, msg, msglen, ctx, ctxlen, sk, rnd);
|
||||
explicit_bzero(rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa87_seeded(uint8_t sig[MLDSA87_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA87_SECRETKEYBYTES],
|
||||
const uint8_t seed[MLDSA87_SEEDBYTES])
|
||||
{
|
||||
libcrux_mldsa87_sign_rnd rnd;
|
||||
libcrux_mldsa87_sk sk_internal;
|
||||
libcrux_mldsa87_message message = { msg, msglen };
|
||||
libcrux_mldsa87_message context = { ctx, ctxlen };
|
||||
libcrux_mldsa87_sign_result res;
|
||||
int r = -1;
|
||||
|
||||
memcpy(sk_internal.data, sk, MLDSA87_SECRETKEYBYTES);
|
||||
memcpy(rnd.data, seed, sizeof(rnd.data));
|
||||
res = libcrux_ml_dsa_ml_dsa_87_portable_sign(&sk_internal,
|
||||
message, context, rnd);
|
||||
if (res.tag == LIBCRUX_RESULT_OK) {
|
||||
memcpy(sig, res.val.case_Ok.data, MLDSA87_SIGBYTES);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
explicit_bzero(&sk_internal, sizeof(sk_internal));
|
||||
explicit_bzero(&res, sizeof(res));
|
||||
explicit_bzero(&rnd, sizeof(rnd));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa87_verify(const uint8_t sig[MLDSA87_SIGBYTES],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA87_PUBLICKEYBYTES])
|
||||
{
|
||||
libcrux_mldsa87_pk pk_internal;
|
||||
libcrux_mldsa87_signature sig_internal;
|
||||
libcrux_mldsa87_message message = { msg, msglen };
|
||||
libcrux_mldsa87_message context = { ctx, ctxlen };
|
||||
libcrux_mldsa87_verify_result res;
|
||||
|
||||
memcpy(pk_internal.data, pk, MLDSA87_PUBLICKEYBYTES);
|
||||
memcpy(sig_internal.data, sig, MLDSA87_SIGBYTES);
|
||||
res = libcrux_ml_dsa_ml_dsa_87_portable_verify(&pk_internal,
|
||||
message, context, &sig_internal);
|
||||
|
||||
return (res.tag == LIBCRUX_RESULT_OK) ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
sha3_256(uint8_t digest[32], const uint8_t *data, size_t len)
|
||||
{
|
||||
Eurydice_borrow_slice_u8 input = { data, len };
|
||||
Eurydice_mut_borrow_slice_u8 output = { digest, 32 };
|
||||
libcrux_sha3_portable_sha256(output, input);
|
||||
}
|
||||
|
||||
void
|
||||
sha3_512(uint8_t digest[64], const uint8_t *data, size_t len)
|
||||
{
|
||||
Eurydice_borrow_slice_u8 input = { data, len };
|
||||
Eurydice_mut_borrow_slice_u8 output = { digest, 64 };
|
||||
libcrux_sha3_portable_sha512(output, input);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,219 +0,0 @@
|
||||
#!/bin/sh
|
||||
# $OpenBSD: mlkem768.sh,v 1.5 2025/11/13 05:13:06 djm Exp $
|
||||
# Placed in the Public Domain.
|
||||
#
|
||||
|
||||
#WANT_LIBCRUX_REVISION="origin/main"
|
||||
WANT_LIBCRUX_REVISION="core-models-v0.0.4"
|
||||
|
||||
BASE="libcrux/libcrux-ml-kem/extracts/c_header_only/generated"
|
||||
FILES="
|
||||
$BASE/eurydice_glue.h
|
||||
$BASE/libcrux_mlkem_core.h
|
||||
$BASE/libcrux_ct_ops.h
|
||||
$BASE/libcrux_sha3_portable.h
|
||||
$BASE/libcrux_mlkem768_portable.h
|
||||
"
|
||||
|
||||
START="$PWD"
|
||||
die() {
|
||||
echo "$@" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
set -xeuo pipefail
|
||||
test -d libcrux || git clone https://github.com/cryspen/libcrux
|
||||
cd libcrux
|
||||
test `git diff | wc -l` -ne 0 && die "tree has unstaged changes"
|
||||
git fetch
|
||||
git checkout -B extract 1>&2
|
||||
git reset --hard $WANT_LIBCRUX_REVISION 1>&2
|
||||
LIBCRUX_REVISION=`git rev-parse HEAD`
|
||||
set +x
|
||||
|
||||
cd $START
|
||||
(
|
||||
printf '/* $Open'; printf 'BSD$ */\n' # Sigh
|
||||
echo
|
||||
echo "/* Extracted from libcrux revision $LIBCRUX_REVISION */"
|
||||
echo
|
||||
echo '/*'
|
||||
cat libcrux/LICENSE-MIT | sed 's/^/ * /;s/ *$//'
|
||||
echo ' */'
|
||||
echo
|
||||
|
||||
LSHIFT="<<"
|
||||
cat << _EOF
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 2)
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
#define KRML_MUSTINLINE inline
|
||||
#define KRML_NOINLINE __attribute__((noinline, unused))
|
||||
#define KRML_HOST_EPRINTF(...)
|
||||
#define KRML_HOST_EXIT(x) fatal_f("internal error")
|
||||
|
||||
static inline void
|
||||
store64_le(uint8_t dst[8], uint64_t src)
|
||||
{
|
||||
dst[0] = src & 0xff;
|
||||
dst[1] = (src >> 8) & 0xff;
|
||||
dst[2] = (src >> 16) & 0xff;
|
||||
dst[3] = (src >> 24) & 0xff;
|
||||
dst[4] = (src >> 32) & 0xff;
|
||||
dst[5] = (src >> 40) & 0xff;
|
||||
dst[6] = (src >> 48) & 0xff;
|
||||
dst[7] = (src >> 56) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
store32_le(uint8_t dst[4], uint32_t src)
|
||||
{
|
||||
dst[0] = src & 0xff;
|
||||
dst[1] = (src >> 8) & 0xff;
|
||||
dst[2] = (src >> 16) & 0xff;
|
||||
dst[3] = (src >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
store32_be(uint8_t dst[4], uint32_t src)
|
||||
{
|
||||
dst[0] = (src >> 24) & 0xff;
|
||||
dst[1] = (src >> 16) & 0xff;
|
||||
dst[2] = (src >> 8) & 0xff;
|
||||
dst[3] = src & 0xff;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
load64_le(uint8_t src[8])
|
||||
{
|
||||
return (uint64_t)(src[0]) |
|
||||
((uint64_t)(src[1]) $LSHIFT 8) |
|
||||
((uint64_t)(src[2]) $LSHIFT 16) |
|
||||
((uint64_t)(src[3]) $LSHIFT 24) |
|
||||
((uint64_t)(src[4]) $LSHIFT 32) |
|
||||
((uint64_t)(src[5]) $LSHIFT 40) |
|
||||
((uint64_t)(src[6]) $LSHIFT 48) |
|
||||
((uint64_t)(src[7]) $LSHIFT 56);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
load32_le(uint8_t src[4])
|
||||
{
|
||||
return (uint32_t)(src[0]) |
|
||||
((uint32_t)(src[1]) $LSHIFT 8) |
|
||||
((uint32_t)(src[2]) $LSHIFT 16) |
|
||||
((uint32_t)(src[3]) $LSHIFT 24);
|
||||
}
|
||||
|
||||
#ifdef MISSING_BUILTIN_POPCOUNT
|
||||
static inline unsigned int
|
||||
__builtin_popcount(unsigned int num)
|
||||
{
|
||||
const int v[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
|
||||
return v[num & 0xf] + v[(num >> 4) & 0xf];
|
||||
}
|
||||
#endif
|
||||
|
||||
_EOF
|
||||
|
||||
for i in $FILES; do
|
||||
echo "/* from $i */"
|
||||
# Changes to all files:
|
||||
# - remove all includes, we inline everything required.
|
||||
# - cleanup whitespace
|
||||
sed -e "/#include/d" \
|
||||
-e 's/[ ]*$//' \
|
||||
$i | \
|
||||
case "$i" in
|
||||
*/eurydice_glue.h)
|
||||
# Replace endian function for consistency.
|
||||
perl -0777 -pe 's/(static inline void core_num__u32__to_be_bytes.*\n)([^}]*\n)/\1 store32_be(dst, src);\n/'
|
||||
;;
|
||||
# Default: pass through.
|
||||
*)
|
||||
cat
|
||||
;;
|
||||
esac
|
||||
echo
|
||||
done
|
||||
|
||||
echo
|
||||
echo '/* rename some types to be a bit more ergonomic */'
|
||||
echo '#define libcrux_mlkem768_keypair libcrux_ml_kem_mlkem768_MlKem768KeyPair_s'
|
||||
echo '#define libcrux_mlkem768_pk libcrux_ml_kem_types_MlKemPublicKey_30_s'
|
||||
echo '#define libcrux_mlkem768_sk libcrux_ml_kem_types_MlKemPrivateKey_d9_s'
|
||||
echo '#define libcrux_mlkem768_ciphertext libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s'
|
||||
echo '#define libcrux_mlkem768_enc_result tuple_c2_s'
|
||||
) > libcrux_mlkem768_sha3.h_new
|
||||
|
||||
# Do some checks on the resultant file
|
||||
|
||||
cat > libcrux_mlkem768_sha3_check.c << _EOF
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <err.h>
|
||||
#include "crypto_api.h"
|
||||
#define fatal_f(x) exit(1)
|
||||
#include "libcrux_mlkem768_sha3.h_new"
|
||||
int main(void) {
|
||||
struct libcrux_mlkem768_keypair keypair = {0};
|
||||
struct libcrux_mlkem768_pk pk = {0};
|
||||
struct libcrux_mlkem768_sk sk = {0};
|
||||
struct libcrux_mlkem768_ciphertext ct = {0};
|
||||
struct libcrux_mlkem768_enc_result enc_result = {0};
|
||||
uint8_t kp_seed[64] = {0}, enc_seed[32] = {0};
|
||||
uint8_t shared_key[crypto_kem_mlkem768_BYTES];
|
||||
|
||||
if (sizeof(keypair.pk.value) != crypto_kem_mlkem768_PUBLICKEYBYTES)
|
||||
errx(1, "keypair.pk bad");
|
||||
if (sizeof(keypair.sk.value) != crypto_kem_mlkem768_SECRETKEYBYTES)
|
||||
errx(1, "keypair.sk bad");
|
||||
if (sizeof(pk.value) != crypto_kem_mlkem768_PUBLICKEYBYTES)
|
||||
errx(1, "pk bad");
|
||||
if (sizeof(sk.value) != crypto_kem_mlkem768_SECRETKEYBYTES)
|
||||
errx(1, "sk bad");
|
||||
if (sizeof(ct.value) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
|
||||
errx(1, "ct bad");
|
||||
if (sizeof(enc_result.fst.value) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
|
||||
errx(1, "enc_result ct bad");
|
||||
if (sizeof(enc_result.snd) != crypto_kem_mlkem768_BYTES)
|
||||
errx(1, "enc_result shared key bad");
|
||||
|
||||
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(kp_seed);
|
||||
if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&keypair.pk))
|
||||
errx(1, "valid smoke failed");
|
||||
enc_result = libcrux_ml_kem_mlkem768_portable_encapsulate(&keypair.pk,
|
||||
enc_seed);
|
||||
libcrux_ml_kem_mlkem768_portable_decapsulate(&keypair.sk,
|
||||
&enc_result.fst, shared_key);
|
||||
if (memcmp(shared_key, enc_result.snd, sizeof(shared_key)) != 0)
|
||||
errx(1, "smoke failed");
|
||||
return 0;
|
||||
}
|
||||
_EOF
|
||||
cc -Wall -Wextra -Wno-unused-parameter -I . -o libcrux_mlkem768_sha3_check \
|
||||
libcrux_mlkem768_sha3_check.c
|
||||
./libcrux_mlkem768_sha3_check
|
||||
|
||||
# Extract PRNG inputs; there's no nice #defines for these
|
||||
key_pair_rng_len=`grep '^libcrux_ml_kem_mlkem768_portable_generate_key_pair.*randomness' libcrux_mlkem768_sha3.h_new | sed 's/.*randomness[[]//;s/\].*//'`
|
||||
enc_rng_len=`sed -e '/^static inline tuple_c2 libcrux_ml_kem_mlkem768_portable_encapsulate[(]$/,/[)] {$/!d' < libcrux_mlkem768_sha3.h_new | grep 'uint8_t randomness\[[0-9]*U\][)]' | sed 's/.*randomness\[\([0-9]*\)U\].*/\1/'`
|
||||
test -z "$key_pair_rng_len" && die "couldn't find size of libcrux_ml_kem_mlkem768_portable_kyber_generate_key_pair randomness argument"
|
||||
test -z "$enc_rng_len" && die "couldn't find size of libcrux_ml_kem_mlkem768_portable_kyber_encapsulate randomness argument"
|
||||
|
||||
(
|
||||
echo "/* defines for PRNG inputs */"
|
||||
echo "#define LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN $key_pair_rng_len"
|
||||
echo "#define LIBCRUX_ML_KEM_ENC_PRNG_LEN $enc_rng_len"
|
||||
) >> libcrux_mlkem768_sha3.h_new
|
||||
|
||||
mv libcrux_mlkem768_sha3.h_new libcrux_mlkem768_sha3.h
|
||||
rm libcrux_mlkem768_sha3_check libcrux_mlkem768_sha3_check.c
|
||||
echo 1>&2
|
||||
echo "libcrux_mlkem768_sha3.h OK" 1>&2
|
||||
|
||||
Executable
+341
@@ -0,0 +1,341 @@
|
||||
#!/bin/sh
|
||||
# $OpenBSD: mlkem_mldsa.sh,v 1.1 2026/06/14 03:59:34 djm Exp $
|
||||
# Placed in the Public Domain.
|
||||
#
|
||||
|
||||
WANT_LIBCRUX_REVISION="origin/jonas/combined-extraction-mldsa"
|
||||
|
||||
BASE="libcrux/combined_extraction/generated"
|
||||
FILES="
|
||||
$BASE/eurydice_glue.h
|
||||
$BASE/combined_core.h
|
||||
$BASE/libcrux_sha3_portable.h
|
||||
$BASE/libcrux_mlkem_core.h
|
||||
$BASE/libcrux_mldsa_core.h
|
||||
$BASE/libcrux_ct_ops.h
|
||||
$BASE/libcrux_mldsa_portable.h
|
||||
$BASE/libcrux_mldsa44_portable.h
|
||||
$BASE/libcrux_mldsa65_portable.h
|
||||
$BASE/libcrux_mldsa87_portable.h
|
||||
$BASE/libcrux_mlkem768_portable.h
|
||||
"
|
||||
|
||||
START="$PWD"
|
||||
die() {
|
||||
echo "$@" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
set -xeuo pipefail
|
||||
test -d libcrux || git clone https://github.com/cryspen/libcrux
|
||||
cd libcrux
|
||||
test `git diff | wc -l` -ne 0 && die "tree has unstaged changes"
|
||||
git fetch
|
||||
git checkout -B extract 1>&2
|
||||
git reset --hard $WANT_LIBCRUX_REVISION 1>&2
|
||||
LIBCRUX_REVISION=`git rev-parse HEAD`
|
||||
set +x
|
||||
|
||||
cd $START
|
||||
(
|
||||
printf '/* $Open'; printf 'BSD$ */\n' # Sigh
|
||||
echo
|
||||
echo "/* Extracted from libcrux revision $LIBCRUX_REVISION */"
|
||||
echo
|
||||
echo '/*'
|
||||
cat libcrux/LICENSE-MIT | sed 's/^/ * /;s/ *$//'
|
||||
echo ' */'
|
||||
echo
|
||||
|
||||
LSHIFT="<<"
|
||||
cat << _EOF
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 2)
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
#define KRML_MUSTINLINE inline
|
||||
#define KRML_NOINLINE __attribute__((noinline, unused))
|
||||
#define KRML_HOST_EPRINTF(...)
|
||||
#define KRML_HOST_EXIT(x) fatal_f("internal error")
|
||||
#define KRML_UNION_CONSTRUCTOR(T)
|
||||
|
||||
static inline void
|
||||
store64_le(uint8_t dst[8], uint64_t src)
|
||||
{
|
||||
dst[0] = src & 0xff;
|
||||
dst[1] = (src >> 8) & 0xff;
|
||||
dst[2] = (src >> 16) & 0xff;
|
||||
dst[3] = (src >> 24) & 0xff;
|
||||
dst[4] = (src >> 32) & 0xff;
|
||||
dst[5] = (src >> 40) & 0xff;
|
||||
dst[6] = (src >> 48) & 0xff;
|
||||
dst[7] = (src >> 56) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
store32_le(uint8_t dst[4], uint32_t src)
|
||||
{
|
||||
dst[0] = src & 0xff;
|
||||
dst[1] = (src >> 8) & 0xff;
|
||||
dst[2] = (src >> 16) & 0xff;
|
||||
dst[3] = (src >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
store16_le(uint8_t dst[2], uint16_t src)
|
||||
{
|
||||
dst[0] = src & 0xff;
|
||||
dst[1] = (src >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
store32_be(uint8_t dst[4], uint32_t src)
|
||||
{
|
||||
dst[0] = (src >> 24) & 0xff;
|
||||
dst[1] = (src >> 16) & 0xff;
|
||||
dst[2] = (src >> 8) & 0xff;
|
||||
dst[3] = src & 0xff;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
load64_le(uint8_t src[8])
|
||||
{
|
||||
return (uint64_t)(src[0]) |
|
||||
((uint64_t)(src[1]) $LSHIFT 8) |
|
||||
((uint64_t)(src[2]) $LSHIFT 16) |
|
||||
((uint64_t)(src[3]) $LSHIFT 24) |
|
||||
((uint64_t)(src[4]) $LSHIFT 32) |
|
||||
((uint64_t)(src[5]) $LSHIFT 40) |
|
||||
((uint64_t)(src[6]) $LSHIFT 48) |
|
||||
((uint64_t)(src[7]) $LSHIFT 56);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
load32_le(uint8_t src[4])
|
||||
{
|
||||
return (uint32_t)(src[0]) |
|
||||
((uint32_t)(src[1]) $LSHIFT 8) |
|
||||
((uint32_t)(src[2]) $LSHIFT 16) |
|
||||
((uint32_t)(src[3]) $LSHIFT 24);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
load16_le(uint8_t src[4])
|
||||
{
|
||||
return (uint16_t)(src[0]) |
|
||||
((uint16_t)(src[1]) $LSHIFT 8);
|
||||
}
|
||||
|
||||
#ifdef MISSING_BUILTIN_POPCOUNT
|
||||
static inline unsigned int
|
||||
__builtin_popcount(unsigned int num)
|
||||
{
|
||||
const int v[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
|
||||
return v[num & 0xf] + v[(num >> 4) & 0xf];
|
||||
}
|
||||
#endif
|
||||
|
||||
_EOF
|
||||
|
||||
for i in $FILES; do
|
||||
echo "/* from $i */"
|
||||
# Changes to all files:
|
||||
# - remove all includes, we inline everything required.
|
||||
# - cleanup whitespace
|
||||
# - convert C++-style constructors to C-style compound literals
|
||||
# - convert Result constructors to C initializers
|
||||
# - use anonymous unions to avoid "union U" redefinition errors
|
||||
sed -e "/#include/d" \
|
||||
-e 's/[ ]*$//' \
|
||||
$i | \
|
||||
case "$i" in
|
||||
*/eurydice_glue.h)
|
||||
# Replace endian function for consistency.
|
||||
perl -0777 -pe 's/(static inline void core_num__u32__to_be_bytes.*\n)([^}]*\n)/\1 store32_be(dst, src);\n/'
|
||||
;;
|
||||
# Default: pass through.
|
||||
*)
|
||||
cat
|
||||
;;
|
||||
esac | \
|
||||
perl -0777 -pe 's/ <<\n\s+\(uint32_t\)\(int32_t\)0//g'
|
||||
echo
|
||||
done
|
||||
|
||||
cat << _EOF
|
||||
|
||||
/* rename some types to be a bit more ergonomic */
|
||||
|
||||
/* ML-KEM 768 */
|
||||
typedef Eurydice_arr_c7 libcrux_mlkem768_keypair_rnd;
|
||||
typedef Eurydice_arr_ec libcrux_mlkem768_enc_rnd;
|
||||
typedef libcrux_ml_kem_mlkem768_MlKem768KeyPair libcrux_mlkem768_keypair;
|
||||
typedef Eurydice_arr_5f libcrux_mlkem768_pk;
|
||||
typedef Eurydice_arr_7d libcrux_mlkem768_sk;
|
||||
typedef Eurydice_arr_2b libcrux_mlkem768_ciphertext;
|
||||
typedef tuple_f4 libcrux_mlkem768_enc_result;
|
||||
typedef Eurydice_arr_ec libcrux_mlkem768_dec_result;
|
||||
/* ML-DSA 44 */
|
||||
typedef Eurydice_arr_ec libcrux_mldsa44_keypair_rnd;
|
||||
typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_44_MLDSA44KeyPair
|
||||
libcrux_mldsa44_keypair;
|
||||
typedef Eurydice_arr_10 libcrux_mldsa44_sk;
|
||||
typedef Eurydice_arr_02 libcrux_mldsa44_pk;
|
||||
typedef Eurydice_borrow_slice_u8 libcrux_mldsa44_message;
|
||||
typedef Eurydice_arr_ec libcrux_mldsa44_sign_rnd;
|
||||
typedef core_result_Result_48 libcrux_mldsa44_sign_result;
|
||||
typedef core_result_Result_41 libcrux_mldsa44_verify_result;
|
||||
typedef Eurydice_arr_85 libcrux_mldsa44_signature;
|
||||
/* ML-DSA 65 */
|
||||
typedef Eurydice_arr_ec libcrux_mldsa65_keypair_rnd;
|
||||
typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_65_MLDSA65KeyPair
|
||||
libcrux_mldsa65_keypair;
|
||||
typedef Eurydice_arr_24 libcrux_mldsa65_sk;
|
||||
typedef Eurydice_arr_29 libcrux_mldsa65_pk;
|
||||
typedef Eurydice_borrow_slice_u8 libcrux_mldsa65_message;
|
||||
typedef Eurydice_arr_ec libcrux_mldsa65_sign_rnd;
|
||||
typedef core_result_Result_8c libcrux_mldsa65_sign_result;
|
||||
typedef core_result_Result_41 libcrux_mldsa65_verify_result;
|
||||
typedef Eurydice_arr_0c libcrux_mldsa65_signature;
|
||||
/* ML-DSA 87 */
|
||||
typedef Eurydice_arr_ec libcrux_mldsa87_keypair_rnd;
|
||||
typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_87_MLDSA87KeyPair
|
||||
libcrux_mldsa87_keypair;
|
||||
typedef Eurydice_arr_e2 libcrux_mldsa87_sk;
|
||||
typedef Eurydice_arr_43 libcrux_mldsa87_pk;
|
||||
typedef Eurydice_borrow_slice_u8 libcrux_mldsa87_message;
|
||||
typedef Eurydice_arr_ec libcrux_mldsa87_sign_rnd;
|
||||
typedef core_result_Result_8b libcrux_mldsa87_sign_result;
|
||||
typedef core_result_Result_41 libcrux_mldsa87_verify_result;
|
||||
typedef Eurydice_arr_93 libcrux_mldsa87_signature;
|
||||
|
||||
#define LIBCRUX_RESULT_OK core_result_Ok
|
||||
|
||||
_EOF
|
||||
|
||||
) > libcrux_internal.h_new
|
||||
|
||||
# Do some checks on the resultant file
|
||||
|
||||
cat > libcrux_internal_check.c << _EOF
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <err.h>
|
||||
#include "crypto_api.h"
|
||||
#define fatal_f(x) exit(1)
|
||||
#include "libcrux_internal.h_new"
|
||||
|
||||
#define TEST_MLDSA(L) do { \
|
||||
libcrux_mldsa##L##_keypair_rnd kpseed##L = {0}; \
|
||||
libcrux_mldsa##L##_keypair kp##L = {0}; \
|
||||
libcrux_mldsa##L##_sk sk##L = {0}; \
|
||||
libcrux_mldsa##L##_pk pk##L = {0}; \
|
||||
const uint8_t msgdata##L[6] = { 0x4a, 0x75, 0x6c, 0x69, 0x75, 0x73 }; \
|
||||
libcrux_mldsa##L##_message msg##L = { \
|
||||
msgdata##L, sizeof(msgdata##L) \
|
||||
}; \
|
||||
const uint8_t ctxdata##L[4] = { 0x48, 0x75, 0x67, 0x6f }; \
|
||||
libcrux_mldsa##L##_message ctx##L = { \
|
||||
ctxdata##L, sizeof(ctxdata##L) \
|
||||
}; \
|
||||
libcrux_mldsa##L##_sign_rnd signseed##L = {0}; \
|
||||
libcrux_mldsa##L##_sign_result signresult##L = {0}; \
|
||||
libcrux_mldsa##L##_verify_result verifyresult##L = {0}; \
|
||||
libcrux_mldsa##L##_signature sig##L = {0}; \
|
||||
\
|
||||
if (sizeof(kpseed##L.data) != MLDSA##L##_SEEDBYTES) \
|
||||
errx(1, "libcrux_mldsa%d_keypair_rnd bad", L); \
|
||||
if (sizeof(kp##L.verification_key.data) != MLDSA##L##_PUBLICKEYBYTES) \
|
||||
errx(1, "libcrux_mldsa%d_keypair verification_key bad", L); \
|
||||
if (sizeof(kp##L.signing_key.data) != MLDSA##L##_SECRETKEYBYTES) \
|
||||
errx(1, "libcrux_mldsa%d_keypair signing_key bad", L); \
|
||||
if (sizeof(pk##L.data) != MLDSA##L##_PUBLICKEYBYTES) \
|
||||
errx(1, "libcrux_mldsa%d_pk bad", L); \
|
||||
if (sizeof(sk##L.data) != MLDSA##L##_SECRETKEYBYTES) \
|
||||
errx(1, "libcrux_mldsa%d_sk bad", L); \
|
||||
if (sizeof(signresult##L.val.case_Ok.data) != MLDSA##L##_SIGBYTES) \
|
||||
errx(1, "libcrux_mldsa%d_sign_result bad", L); \
|
||||
if (sizeof(sig##L.data) != MLDSA##L##_SIGBYTES) \
|
||||
errx(1, "libcrux_mldsa%d_signature bad", L); \
|
||||
\
|
||||
kp##L = libcrux_ml_dsa_ml_dsa_##L##_portable_generate_key_pair( \
|
||||
kpseed##L); \
|
||||
sk##L = kp##L.signing_key; \
|
||||
pk##L = kp##L.verification_key; \
|
||||
signresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_sign(&sk##L, \
|
||||
msg##L, ctx##L, signseed##L); \
|
||||
if (signresult##L.tag != LIBCRUX_RESULT_OK) \
|
||||
errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_sign failed", L); \
|
||||
sig##L = signresult##L.val.case_Ok; \
|
||||
verifyresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_verify(&pk##L, \
|
||||
msg##L, ctx##L, &sig##L); \
|
||||
if (verifyresult##L.tag != LIBCRUX_RESULT_OK) \
|
||||
errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_verify failed", L); \
|
||||
sig##L.data[10] ^= 0x10; /* corrupt the signature */ \
|
||||
verifyresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_verify(&pk##L, \
|
||||
msg##L, ctx##L, &sig##L); \
|
||||
if (verifyresult##L.tag == LIBCRUX_RESULT_OK) \
|
||||
errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_verify fail2", L); \
|
||||
} while (0)
|
||||
|
||||
int main(void) {
|
||||
libcrux_mlkem768_keypair keypair = {0};
|
||||
libcrux_mlkem768_pk pk = {0};
|
||||
libcrux_mlkem768_sk sk = {0};
|
||||
libcrux_mlkem768_ciphertext ct = {0};
|
||||
libcrux_mlkem768_enc_result enc_result = {0};
|
||||
libcrux_mlkem768_keypair_rnd kp_seed = {0};
|
||||
libcrux_mlkem768_enc_rnd enc_seed = {0};
|
||||
libcrux_mlkem768_dec_result shared_secret = {0};
|
||||
uint8_t shared_key[crypto_kem_mlkem768_BYTES];
|
||||
|
||||
if (sizeof(keypair.pk.data) != crypto_kem_mlkem768_PUBLICKEYBYTES)
|
||||
errx(1, "keypair.pk bad");
|
||||
if (sizeof(keypair.sk.data) != crypto_kem_mlkem768_SECRETKEYBYTES)
|
||||
errx(1, "keypair.sk bad");
|
||||
if (sizeof(pk.data) != crypto_kem_mlkem768_PUBLICKEYBYTES)
|
||||
errx(1, "pk bad");
|
||||
if (sizeof(sk.data) != crypto_kem_mlkem768_SECRETKEYBYTES)
|
||||
errx(1, "sk bad");
|
||||
if (sizeof(ct.data) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
|
||||
errx(1, "ct bad");
|
||||
if (sizeof(enc_result.fst.data) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
|
||||
errx(1, "enc_result ct bad");
|
||||
if (sizeof(enc_result.snd.data) != crypto_kem_mlkem768_BYTES)
|
||||
errx(1, "enc_result shared key bad");
|
||||
if (sizeof(kp_seed.data) != crypto_kem_mlkem768_KEYPAIRSEEDBYTES)
|
||||
errx(1, "keypair rnd bad");
|
||||
if (sizeof(enc_seed.data) != crypto_kem_mlkem768_ENCSEEDBYTES)
|
||||
errx(1, "keypair rnd bad");
|
||||
|
||||
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(kp_seed);
|
||||
if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&keypair.pk))
|
||||
errx(1, "valid smoke failed");
|
||||
enc_result = libcrux_ml_kem_mlkem768_portable_encapsulate(&keypair.pk,
|
||||
enc_seed);
|
||||
shared_secret = libcrux_ml_kem_mlkem768_portable_decapsulate(
|
||||
&keypair.sk, &enc_result.fst);
|
||||
memcpy(shared_key, shared_secret.data, sizeof(shared_key));
|
||||
if (memcmp(shared_key, enc_result.snd.data, sizeof(shared_key)) != 0)
|
||||
errx(1, "smoke failed");
|
||||
|
||||
TEST_MLDSA(44);
|
||||
TEST_MLDSA(65);
|
||||
TEST_MLDSA(87);
|
||||
|
||||
return 0;
|
||||
}
|
||||
_EOF
|
||||
cc -Wall -Wextra -Wno-unused-parameter -I . -o libcrux_internal_check \
|
||||
libcrux_internal_check.c
|
||||
./libcrux_internal_check
|
||||
|
||||
mv libcrux_internal.h_new libcrux_internal.h
|
||||
rm libcrux_internal_check libcrux_internal_check.c
|
||||
echo 1>&2
|
||||
echo "libcrux_internal.h OK" 1>&2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: pathnames.h,v 1.36 2025/08/29 03:50:38 djm Exp $ */
|
||||
/* $OpenBSD: pathnames.h,v 1.37 2026/06/14 03:59:34 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@@ -33,6 +33,7 @@
|
||||
#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key"
|
||||
#define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key"
|
||||
#define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key"
|
||||
#define _PATH_HOST_MLDSA44_ED25519_KEY_FILE SSHDIR "/ssh_host_mldsa44_ed25519_key"
|
||||
#define _PATH_DH_MODULI ETCDIR "/moduli"
|
||||
|
||||
#define _PATH_SSH_PROGRAM "/usr/bin/ssh"
|
||||
@@ -78,6 +79,7 @@
|
||||
#define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519"
|
||||
#define _PATH_SSH_CLIENT_ID_ECDSA_SK _PATH_SSH_USER_DIR "/id_ecdsa_sk"
|
||||
#define _PATH_SSH_CLIENT_ID_ED25519_SK _PATH_SSH_USER_DIR "/id_ed25519_sk"
|
||||
#define _PATH_SSH_CLIENT_ID_MLDSA44_ED25519 _PATH_SSH_USER_DIR "/id_mldsa44_ed25519"
|
||||
|
||||
/*
|
||||
* Configuration file in user's home directory. This file need not be
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: servconf.c,v 1.448 2026/05/31 13:12:07 djm Exp $ */
|
||||
/* $OpenBSD: servconf.c,v 1.449 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
* All rights reserved
|
||||
@@ -302,6 +302,8 @@ fill_default_server_options(ServerOptions *options)
|
||||
_PATH_HOST_ECDSA_KEY_FILE, 0);
|
||||
servconf_add_hostkey("[default]", 0, options,
|
||||
_PATH_HOST_ED25519_KEY_FILE, 0);
|
||||
servconf_add_hostkey("[default]", 0, options,
|
||||
_PATH_HOST_MLDSA44_ED25519_KEY_FILE, 0);
|
||||
}
|
||||
/* No certificates by default */
|
||||
if (options->num_ports == 0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssh-keygen.c,v 1.490 2026/03/03 09:57:25 dtucker Exp $ */
|
||||
/* $OpenBSD: ssh-keygen.c,v 1.491 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@@ -263,6 +263,10 @@ ask_filename(struct passwd *pw, const char *prompt)
|
||||
case KEY_ED25519_SK_CERT:
|
||||
name = _PATH_SSH_CLIENT_ID_ED25519_SK;
|
||||
break;
|
||||
case KEY_MLDSA44_ED25519:
|
||||
case KEY_MLDSA44_ED25519_CERT:
|
||||
name = _PATH_SSH_CLIENT_ID_MLDSA44_ED25519;
|
||||
break;
|
||||
default:
|
||||
fatal("bad key type");
|
||||
}
|
||||
@@ -995,6 +999,8 @@ do_gen_all_hostkeys(struct passwd *pw)
|
||||
{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
|
||||
#endif /* WITH_OPENSSL */
|
||||
{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
|
||||
{ "mldsa44-ed25519", "MLDSA44-ED25519",
|
||||
_PATH_HOST_MLDSA44_ED25519_KEY_FILE },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssh-keyscan.c,v 1.167 2025/08/29 03:50:38 djm Exp $ */
|
||||
/* $OpenBSD: ssh-keyscan.c,v 1.168 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
|
||||
*
|
||||
@@ -59,12 +59,13 @@ int ssh_port = SSH_DEFAULT_PORT;
|
||||
#define KT_ED25519 (1<<2)
|
||||
#define KT_ECDSA_SK (1<<4)
|
||||
#define KT_ED25519_SK (1<<5)
|
||||
#define KT_MLDSA44_ED25519 (1<<6)
|
||||
|
||||
#define KT_MIN KT_RSA
|
||||
#define KT_MAX KT_ED25519_SK
|
||||
#define KT_MAX KT_MLDSA44_ED25519
|
||||
|
||||
int get_cert = 0;
|
||||
int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK;
|
||||
int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK|KT_MLDSA44_ED25519;
|
||||
|
||||
int hash_hosts = 0; /* Hash hostname on output */
|
||||
|
||||
@@ -236,6 +237,11 @@ keygrab_ssh2(con *c)
|
||||
"ecdsa-sha2-nistp384,"
|
||||
"ecdsa-sha2-nistp521";
|
||||
break;
|
||||
case KT_MLDSA44_ED25519:
|
||||
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
|
||||
"ssh-mldsa44-ed25519-cert-v01@openssh.com" :
|
||||
"ssh-mldsa44-ed25519@openssh.com";
|
||||
break;
|
||||
case KT_ECDSA_SK:
|
||||
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
|
||||
"sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" :
|
||||
@@ -720,6 +726,9 @@ main(int argc, char **argv)
|
||||
case KEY_ECDSA_SK:
|
||||
get_keytypes |= KT_ECDSA_SK;
|
||||
break;
|
||||
case KEY_MLDSA44_ED25519:
|
||||
get_keytypes |= KT_MLDSA44_ED25519;
|
||||
break;
|
||||
case KEY_UNSPEC:
|
||||
default:
|
||||
fatal("Unknown key type \"%s\"", tname);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssh-keysign.c,v 1.80 2026/03/19 02:36:28 djm Exp $ */
|
||||
/* $OpenBSD: ssh-keysign.c,v 1.81 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2002 Markus Friedl. All rights reserved.
|
||||
*
|
||||
@@ -200,6 +200,7 @@ main(int argc, char **argv)
|
||||
key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY);
|
||||
key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY);
|
||||
key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY);
|
||||
key_fd[i++] = open(_PATH_HOST_MLDSA44_ED25519_KEY_FILE, O_RDONLY);
|
||||
|
||||
if ((pw = getpwuid(getuid())) == NULL)
|
||||
fatal("getpwuid failed");
|
||||
|
||||
@@ -0,0 +1,501 @@
|
||||
/* $OpenBSD: ssh-mldsa-eddsa.c,v 1.1 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2026 Damien Miller <djm@mindrot.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* draft-miller-sshm-mldsa44-ed25519-composite-sigs-00 */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "crypto_api.h"
|
||||
#include "sshbuf.h"
|
||||
#include "ssherr.h"
|
||||
#include "digest.h"
|
||||
#define SSHKEY_INTERNAL
|
||||
#include "sshkey.h"
|
||||
#include "log.h"
|
||||
|
||||
#define COMPOSITE_PREFIX "CompositeAlgorithmSignatures2025"
|
||||
#define COMPOSITE_LABEL "COMPSIG-MLDSA44-Ed25519-SHA512"
|
||||
#define SSH_MLDSA44_ED25519_ALG_NAME "ssh-mldsa44-ed25519@openssh.com"
|
||||
|
||||
/*
|
||||
* raw_* functions implement the draft-ietf-lamps-pq-composite-sigs-18
|
||||
* composite signature scheme. These are exposed (i.e. not static) so
|
||||
* we can test them separately in unittests/crypto.
|
||||
*/
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_ed25519_keygen(uint8_t pk[MLDSA44_ED25519_PK_SZ],
|
||||
uint8_t sk[MLDSA44_ED25519_SK_SZ])
|
||||
{
|
||||
uint8_t mldsa_seed[32], ed25519_seed[32];
|
||||
int r;
|
||||
|
||||
arc4random_buf(mldsa_seed, sizeof(mldsa_seed));
|
||||
arc4random_buf(ed25519_seed, sizeof(ed25519_seed));
|
||||
|
||||
r = crypto_sign_mldsa44_ed25519_keygen_seeded(pk, sk, mldsa_seed,
|
||||
ed25519_seed);
|
||||
explicit_bzero(mldsa_seed, sizeof(mldsa_seed));
|
||||
explicit_bzero(ed25519_seed, sizeof(ed25519_seed));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_ed25519_keygen_seeded(uint8_t pk[MLDSA44_ED25519_PK_SZ],
|
||||
uint8_t sk[MLDSA44_ED25519_SK_SZ], const uint8_t mldsa_seed[32],
|
||||
const uint8_t ed25519_seed[32])
|
||||
{
|
||||
uint8_t ed25519_pk[32], ed25519_sk[64];
|
||||
uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES];
|
||||
int ret = -1;
|
||||
|
||||
if (crypto_sign_mldsa44_keypair_seeded(pk, mldsa_sk, mldsa_seed) != 0)
|
||||
goto out;
|
||||
if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk,
|
||||
ed25519_seed) != 0)
|
||||
goto out;
|
||||
|
||||
/* Serialize PK: mldsaPK || ed25519PK */
|
||||
memcpy(pk + MLDSA44_PUBLICKEYBYTES, ed25519_pk, 32);
|
||||
|
||||
/* Serialize SK: mldsaSeed || ed25519Seed */
|
||||
memcpy(sk, mldsa_seed, 32);
|
||||
memcpy(sk + 32, ed25519_seed, 32);
|
||||
|
||||
/* success */
|
||||
ret = 0;
|
||||
out:
|
||||
explicit_bzero(mldsa_sk, sizeof(mldsa_sk));
|
||||
explicit_bzero(ed25519_sk, sizeof(ed25519_sk));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
construct_m_prime(uint8_t **m_primep, size_t *m_prime_lenp,
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen)
|
||||
{
|
||||
int r;
|
||||
uint8_t hash[64];
|
||||
struct sshbuf *m_prime;
|
||||
|
||||
*m_primep = NULL;
|
||||
*m_prime_lenp = 0;
|
||||
|
||||
if (ctxlen > 255)
|
||||
return SSH_ERR_INVALID_ARGUMENT;
|
||||
if ((r = ssh_digest_memory(SSH_DIGEST_SHA512, msg, msglen,
|
||||
hash, sizeof(hash))) != 0)
|
||||
return r;
|
||||
if ((m_prime = sshbuf_new()) == NULL)
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
if ((r = sshbuf_put(m_prime, COMPOSITE_PREFIX,
|
||||
sizeof(COMPOSITE_PREFIX) - 1)) != 0 ||
|
||||
(r = sshbuf_put(m_prime, COMPOSITE_LABEL,
|
||||
sizeof(COMPOSITE_LABEL) - 1)) != 0 ||
|
||||
(r = sshbuf_put_u8(m_prime, (uint8_t)ctxlen)) != 0 ||
|
||||
(r = sshbuf_put(m_prime, ctx, ctxlen)) != 0 ||
|
||||
(r = sshbuf_put(m_prime, hash, sizeof(hash))) != 0) {
|
||||
sshbuf_free(m_prime);
|
||||
return r;
|
||||
}
|
||||
if ((*m_primep = malloc(sshbuf_len(m_prime))) == NULL) {
|
||||
sshbuf_free(m_prime);
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
}
|
||||
memcpy(*m_primep, sshbuf_ptr(m_prime), sshbuf_len(m_prime));
|
||||
*m_prime_lenp = sshbuf_len(m_prime);
|
||||
/* success */
|
||||
sshbuf_free(m_prime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_ed25519_sign(uint8_t sig[MLDSA44_ED25519_SIG_SZ],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t sk[MLDSA44_ED25519_SK_SZ])
|
||||
{
|
||||
uint8_t *m_prime = NULL;
|
||||
size_t m_prime_len = 0;
|
||||
uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES];
|
||||
uint8_t mldsa_pk_dummy[MLDSA44_PUBLICKEYBYTES];
|
||||
uint8_t ed25519_pk[32], ed25519_sk[64];
|
||||
uint8_t mldsa_rnd[32];
|
||||
unsigned long long smlen;
|
||||
int r = -1;
|
||||
|
||||
if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen,
|
||||
ctx, ctxlen) != 0)
|
||||
return -1;
|
||||
|
||||
/* Expand ML-DSA key from seed */
|
||||
if (crypto_sign_mldsa44_keypair_seeded(mldsa_pk_dummy, mldsa_sk, sk) != 0)
|
||||
goto out;
|
||||
|
||||
/* Sign with ML-DSA */
|
||||
arc4random_buf(mldsa_rnd, sizeof(mldsa_rnd));
|
||||
if (crypto_sign_mldsa44_seeded(sig, m_prime, m_prime_len,
|
||||
(const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1,
|
||||
mldsa_sk, mldsa_rnd) != 0)
|
||||
goto out;
|
||||
|
||||
/* Expand Ed25519 key from seed */
|
||||
if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk,
|
||||
sk + 32) != 0)
|
||||
goto out;
|
||||
|
||||
/* Sign with Ed25519 */
|
||||
uint8_t *sm = malloc(m_prime_len + 64);
|
||||
if (sm == NULL)
|
||||
goto out;
|
||||
|
||||
if (crypto_sign_ed25519(sm, &smlen, m_prime, m_prime_len,
|
||||
ed25519_sk) != 0) {
|
||||
free(sm);
|
||||
goto out;
|
||||
}
|
||||
memcpy(sig + MLDSA44_SIGBYTES, sm, 64);
|
||||
free(sm);
|
||||
|
||||
r = 0;
|
||||
out:
|
||||
free(m_prime);
|
||||
explicit_bzero(mldsa_rnd, sizeof(mldsa_rnd));
|
||||
explicit_bzero(mldsa_sk, sizeof(mldsa_sk));
|
||||
explicit_bzero(ed25519_sk, sizeof(ed25519_sk));
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_sign_mldsa44_ed25519_verify(const uint8_t sig[MLDSA44_ED25519_SIG_SZ],
|
||||
const uint8_t *msg, size_t msglen,
|
||||
const uint8_t *ctx, size_t ctxlen,
|
||||
const uint8_t pk[MLDSA44_ED25519_PK_SZ])
|
||||
{
|
||||
uint8_t *m_prime = NULL;
|
||||
size_t m_prime_len = 0;
|
||||
uint8_t *sm = NULL, *m = NULL;
|
||||
unsigned long long smlen, mlen;
|
||||
int r = -1;
|
||||
|
||||
if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen,
|
||||
ctx, ctxlen) != 0)
|
||||
return -1;
|
||||
|
||||
/* Verify ML-DSA */
|
||||
if (crypto_sign_mldsa44_verify(sig, m_prime, m_prime_len,
|
||||
(const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1,
|
||||
pk) != 0)
|
||||
goto out;
|
||||
|
||||
/* Verify Ed25519 */
|
||||
smlen = m_prime_len + 64;
|
||||
mlen = smlen;
|
||||
if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL)
|
||||
goto out;
|
||||
memcpy(sm, sig + MLDSA44_SIGBYTES, 64);
|
||||
memcpy(sm + 64, m_prime, m_prime_len);
|
||||
|
||||
if (crypto_sign_ed25519_open(m, &mlen, sm, smlen,
|
||||
pk + MLDSA44_PUBLICKEYBYTES) != 0)
|
||||
goto out;
|
||||
if (mlen != m_prime_len)
|
||||
goto out;
|
||||
|
||||
r = 0;
|
||||
out:
|
||||
free(m_prime);
|
||||
free(sm);
|
||||
free(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* sshkey integration */
|
||||
|
||||
static void
|
||||
ssh_mldsa44_ed25519_cleanup(struct sshkey *k)
|
||||
{
|
||||
freezero(k->mldsa_ed25519_pk, MLDSA44_ED25519_PK_SZ);
|
||||
freezero(k->mldsa_ed25519_sk, MLDSA44_ED25519_SK_SZ);
|
||||
k->mldsa_ed25519_pk = NULL;
|
||||
k->mldsa_ed25519_sk = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_equal(const struct sshkey *a, const struct sshkey *b)
|
||||
{
|
||||
if (a->mldsa_ed25519_pk == NULL || b->mldsa_ed25519_pk == NULL)
|
||||
return 0;
|
||||
if (memcmp(a->mldsa_ed25519_pk, b->mldsa_ed25519_pk,
|
||||
MLDSA44_ED25519_PK_SZ) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b,
|
||||
enum sshkey_serialize_rep opts)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (key->mldsa_ed25519_pk == NULL)
|
||||
return SSH_ERR_INVALID_ARGUMENT;
|
||||
if ((r = sshbuf_put_string(b, key->mldsa_ed25519_pk,
|
||||
MLDSA44_ED25519_PK_SZ)) != 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b,
|
||||
enum sshkey_serialize_rep opts)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (key->mldsa_ed25519_sk == NULL)
|
||||
return SSH_ERR_INVALID_ARGUMENT;
|
||||
if (!sshkey_is_cert(key)) {
|
||||
if ((r = ssh_mldsa44_ed25519_serialize_public(key,
|
||||
b, opts)) != 0)
|
||||
return r;
|
||||
}
|
||||
if ((r = sshbuf_put_string(b, key->mldsa_ed25519_sk,
|
||||
MLDSA44_ED25519_SK_SZ)) != 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_deserialize_public(const char *ktype, struct sshbuf *b,
|
||||
struct sshkey *key)
|
||||
{
|
||||
u_char *pk = NULL;
|
||||
size_t len = 0;
|
||||
int r;
|
||||
|
||||
if ((r = sshbuf_get_string(b, &pk, &len)) != 0)
|
||||
return r;
|
||||
if (len != MLDSA44_ED25519_PK_SZ) {
|
||||
freezero(pk, len);
|
||||
return SSH_ERR_INVALID_FORMAT;
|
||||
}
|
||||
key->mldsa_ed25519_pk = pk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_deserialize_private(const char *ktype, struct sshbuf *b,
|
||||
struct sshkey *key)
|
||||
{
|
||||
int r;
|
||||
size_t sklen = 0;
|
||||
u_char *sk = NULL;
|
||||
|
||||
if (!sshkey_is_cert(key)) {
|
||||
if ((r = ssh_mldsa44_ed25519_deserialize_public(ktype,
|
||||
b, key)) != 0)
|
||||
return r;
|
||||
}
|
||||
if ((r = sshbuf_get_string(b, &sk, &sklen)) != 0)
|
||||
goto out;
|
||||
if (sklen != MLDSA44_ED25519_SK_SZ) {
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto out;
|
||||
}
|
||||
key->mldsa_ed25519_sk = sk;
|
||||
sk = NULL; /* transferred */
|
||||
r = 0;
|
||||
out:
|
||||
freezero(sk, sklen);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_generate(struct sshkey *k, int bits)
|
||||
{
|
||||
free(k->mldsa_ed25519_pk);
|
||||
free(k->mldsa_ed25519_sk);
|
||||
k->mldsa_ed25519_pk = NULL;
|
||||
k->mldsa_ed25519_sk = NULL;
|
||||
if ((k->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL ||
|
||||
(k->mldsa_ed25519_sk = malloc(MLDSA44_ED25519_SK_SZ)) == NULL) {
|
||||
free(k->mldsa_ed25519_pk);
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
}
|
||||
if (crypto_sign_mldsa44_ed25519_keygen(k->mldsa_ed25519_pk,
|
||||
k->mldsa_ed25519_sk) != 0) {
|
||||
free(k->mldsa_ed25519_pk);
|
||||
free(k->mldsa_ed25519_sk);
|
||||
return SSH_ERR_CRYPTO_ERROR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_copy_public(const struct sshkey *from, struct sshkey *to)
|
||||
{
|
||||
if (from->mldsa_ed25519_pk == NULL)
|
||||
return SSH_ERR_INVALID_ARGUMENT;
|
||||
if ((to->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL)
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
memcpy(to->mldsa_ed25519_pk, from->mldsa_ed25519_pk,
|
||||
MLDSA44_ED25519_PK_SZ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_sign(struct sshkey *key,
|
||||
u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
|
||||
const char *alg, const char *sk_provider, const char *sk_pin,
|
||||
u_int compat)
|
||||
{
|
||||
u_char sig[MLDSA44_ED25519_SIG_SZ];
|
||||
struct sshbuf *b = NULL;
|
||||
int r = SSH_ERR_INTERNAL_ERROR;
|
||||
|
||||
if (lenp != NULL)
|
||||
*lenp = 0;
|
||||
if (sigp != NULL)
|
||||
*sigp = NULL;
|
||||
|
||||
if (key == NULL ||
|
||||
sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 ||
|
||||
key->mldsa_ed25519_sk == NULL)
|
||||
return SSH_ERR_INVALID_ARGUMENT;
|
||||
|
||||
if (crypto_sign_mldsa44_ed25519_sign(sig, data, datalen, NULL, 0,
|
||||
key->mldsa_ed25519_sk) != 0) {
|
||||
r = SSH_ERR_CRYPTO_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((b = sshbuf_new()) == NULL) {
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
if ((r = sshbuf_put_cstring(b, SSH_MLDSA44_ED25519_ALG_NAME)) != 0 ||
|
||||
(r = sshbuf_put_string(b, sig, sizeof(sig))) != 0)
|
||||
goto out;
|
||||
|
||||
if (sigp != NULL) {
|
||||
if ((*sigp = malloc(sshbuf_len(b))) == NULL) {
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(*sigp, sshbuf_ptr(b), sshbuf_len(b));
|
||||
}
|
||||
if (lenp != NULL)
|
||||
*lenp = sshbuf_len(b);
|
||||
r = 0;
|
||||
out:
|
||||
sshbuf_free(b);
|
||||
explicit_bzero(sig, sizeof(sig));
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
ssh_mldsa44_ed25519_verify(const struct sshkey *key,
|
||||
const u_char *sig, size_t siglen, const u_char *data, size_t dlen,
|
||||
const char *alg, u_int compat, struct sshkey_sig_details **detailsp)
|
||||
{
|
||||
struct sshbuf *b = NULL;
|
||||
char *ktype = NULL;
|
||||
const u_char *sigblob;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
if (key == NULL ||
|
||||
sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 ||
|
||||
key->mldsa_ed25519_pk == NULL ||
|
||||
sig == NULL || siglen == 0)
|
||||
return SSH_ERR_INVALID_ARGUMENT;
|
||||
|
||||
if ((b = sshbuf_from(sig, siglen)) == NULL)
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
|
||||
(r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
|
||||
goto out;
|
||||
if (strcmp(SSH_MLDSA44_ED25519_ALG_NAME, ktype) != 0) {
|
||||
r = SSH_ERR_KEY_TYPE_MISMATCH;
|
||||
goto out;
|
||||
}
|
||||
if (sshbuf_len(b) != 0) {
|
||||
r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
|
||||
goto out;
|
||||
}
|
||||
if (len != MLDSA44_ED25519_SIG_SZ) {
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (crypto_sign_mldsa44_ed25519_verify(sigblob, data, dlen, NULL, 0,
|
||||
key->mldsa_ed25519_pk) != 0) {
|
||||
r = SSH_ERR_SIGNATURE_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
out:
|
||||
sshbuf_free(b);
|
||||
free(ktype);
|
||||
return r;
|
||||
}
|
||||
|
||||
const struct sshkey_impl_funcs sshkey_mldsa44_ed25519_funcs = {
|
||||
/* .size = */ NULL,
|
||||
/* .alloc = */ NULL,
|
||||
/* .cleanup = */ ssh_mldsa44_ed25519_cleanup,
|
||||
/* .equal = */ ssh_mldsa44_ed25519_equal,
|
||||
/* .ssh_serialize_public = */ ssh_mldsa44_ed25519_serialize_public,
|
||||
/* .ssh_deserialize_public = */ ssh_mldsa44_ed25519_deserialize_public,
|
||||
/* .ssh_serialize_private = */ ssh_mldsa44_ed25519_serialize_private,
|
||||
/* .ssh_deserialize_private = */ ssh_mldsa44_ed25519_deserialize_private,
|
||||
/* .generate = */ ssh_mldsa44_ed25519_generate,
|
||||
/* .copy_public = */ ssh_mldsa44_ed25519_copy_public,
|
||||
/* .sign = */ ssh_mldsa44_ed25519_sign,
|
||||
/* .verify = */ ssh_mldsa44_ed25519_verify,
|
||||
};
|
||||
|
||||
const struct sshkey_impl sshkey_mldsa44_ed25519_impl = {
|
||||
/* .name = */ "ssh-mldsa44-ed25519@openssh.com",
|
||||
/* .shortname = */ "MLDSA44-ED25519",
|
||||
/* .sigalg = */ NULL,
|
||||
/* .type = */ KEY_MLDSA44_ED25519,
|
||||
/* .nid = */ 0,
|
||||
/* .cert = */ 0,
|
||||
/* .sigonly = */ 0,
|
||||
/* .keybits = */ 256,
|
||||
/* .funcs = */ &sshkey_mldsa44_ed25519_funcs,
|
||||
};
|
||||
|
||||
const struct sshkey_impl sshkey_mldsa44_ed25519_cert_impl = {
|
||||
/* .name = */ "ssh-mldsa44-ed25519-cert-v01@openssh.com",
|
||||
/* .shortname = */ "MLDSA44-ED25519-CERT",
|
||||
/* .sigalg = */ NULL,
|
||||
/* .type = */ KEY_MLDSA44_ED25519_CERT,
|
||||
/* .nid = */ 0,
|
||||
/* .cert = */ 1,
|
||||
/* .sigonly = */ 0,
|
||||
/* .keybits = */ 256,
|
||||
/* .funcs = */ &sshkey_mldsa44_ed25519_funcs,
|
||||
};
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssh.c,v 1.632 2026/05/31 05:55:21 djm Exp $ */
|
||||
/* $OpenBSD: ssh.c,v 1.633 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@@ -1719,9 +1719,11 @@ main(int ac, char **av)
|
||||
L_CERT(_PATH_HOST_ECDSA_KEY_FILE, 0);
|
||||
L_CERT(_PATH_HOST_ED25519_KEY_FILE, 1);
|
||||
L_CERT(_PATH_HOST_RSA_KEY_FILE, 2);
|
||||
L_CERT(_PATH_HOST_MLDSA44_ED25519_KEY_FILE, 3);
|
||||
L_PUBKEY(_PATH_HOST_ECDSA_KEY_FILE, 4);
|
||||
L_PUBKEY(_PATH_HOST_ED25519_KEY_FILE, 5);
|
||||
L_PUBKEY(_PATH_HOST_RSA_KEY_FILE, 6);
|
||||
L_PUBKEY(_PATH_HOST_MLDSA44_ED25519_KEY_FILE, 7);
|
||||
if (loaded == 0)
|
||||
debug("HostbasedAuthentication enabled but no "
|
||||
"local public host keys could be loaded.");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshconnect.c,v 1.382 2026/02/16 00:45:41 dtucker Exp $ */
|
||||
/* $OpenBSD: sshconnect.c,v 1.383 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@@ -1604,6 +1604,7 @@ show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
|
||||
KEY_RSA,
|
||||
KEY_ECDSA,
|
||||
KEY_ED25519,
|
||||
KEY_MLDSA44_ED25519,
|
||||
-1
|
||||
};
|
||||
int i, ret = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshd-auth.c,v 1.15 2026/05/31 11:30:50 djm Exp $ */
|
||||
/* $OpenBSD: sshd-auth.c,v 1.16 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* SSH2 implementation:
|
||||
* Privilege Separation:
|
||||
@@ -236,6 +236,7 @@ list_hostkey_types(void)
|
||||
/* FALLTHROUGH */
|
||||
case KEY_ECDSA:
|
||||
case KEY_ED25519:
|
||||
case KEY_MLDSA44_ED25519:
|
||||
case KEY_ECDSA_SK:
|
||||
case KEY_ED25519_SK:
|
||||
append_hostkey_type(b, sshkey_ssh_name(key));
|
||||
@@ -255,6 +256,7 @@ list_hostkey_types(void)
|
||||
/* FALLTHROUGH */
|
||||
case KEY_ECDSA_CERT:
|
||||
case KEY_ED25519_CERT:
|
||||
case KEY_MLDSA44_ED25519_CERT:
|
||||
case KEY_ECDSA_SK_CERT:
|
||||
case KEY_ED25519_SK_CERT:
|
||||
append_hostkey_type(b, sshkey_ssh_name(key));
|
||||
@@ -279,6 +281,7 @@ get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
|
||||
case KEY_RSA_CERT:
|
||||
case KEY_ECDSA_CERT:
|
||||
case KEY_ED25519_CERT:
|
||||
case KEY_MLDSA44_ED25519_CERT:
|
||||
case KEY_ECDSA_SK_CERT:
|
||||
case KEY_ED25519_SK_CERT:
|
||||
key = host_certificates[i];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshd-session.c,v 1.23 2026/03/11 09:10:59 dtucker Exp $ */
|
||||
/* $OpenBSD: sshd-session.c,v 1.24 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* SSH2 implementation:
|
||||
* Privilege Separation:
|
||||
@@ -405,6 +405,7 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
|
||||
case KEY_ED25519_CERT:
|
||||
case KEY_ECDSA_SK_CERT:
|
||||
case KEY_ED25519_SK_CERT:
|
||||
case KEY_MLDSA44_ED25519_CERT:
|
||||
key = sensitive_data.host_certificates[i];
|
||||
break;
|
||||
default:
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshd.c,v 1.626 2026/02/09 21:21:39 dtucker Exp $ */
|
||||
/* $OpenBSD: sshd.c,v 1.627 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2002 Niels Provos. All rights reserved.
|
||||
@@ -1598,6 +1598,7 @@ main(int ac, char **av)
|
||||
case KEY_RSA:
|
||||
case KEY_ECDSA:
|
||||
case KEY_ED25519:
|
||||
case KEY_MLDSA44_ED25519:
|
||||
case KEY_ECDSA_SK:
|
||||
case KEY_ED25519_SK:
|
||||
if (have_agent || key != NULL)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssherr.c,v 1.11 2026/02/06 23:31:29 dtucker Exp $ */
|
||||
/* $OpenBSD: ssherr.c,v 1.12 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2011 Damien Miller
|
||||
*
|
||||
@@ -72,6 +72,8 @@ ssh_err(int n)
|
||||
case SSH_ERR_LIBCRYPTO_ERROR:
|
||||
msg = ssherr_libcrypto();
|
||||
return msg != NULL ? msg : "error in libcrypto";
|
||||
case SSH_ERR_INTERNAL_CRYPTO_ERROR:
|
||||
return "cryptographic operation failed";
|
||||
case SSH_ERR_UNEXPECTED_TRAILING_DATA:
|
||||
return "unexpected bytes remain after decoding";
|
||||
case SSH_ERR_SYSTEM_ERROR:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssherr.h,v 1.9 2026/02/06 23:31:29 dtucker Exp $ */
|
||||
/* $OpenBSD: ssherr.h,v 1.10 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2011 Damien Miller
|
||||
*
|
||||
@@ -82,6 +82,8 @@
|
||||
#define SSH_ERR_SIGN_ALG_UNSUPPORTED -58
|
||||
#define SSH_ERR_FEATURE_UNSUPPORTED -59
|
||||
#define SSH_ERR_DEVICE_NOT_FOUND -60
|
||||
#define SSH_ERR_CRYPTO_ERROR -61
|
||||
#define SSH_ERR_INTERNAL_CRYPTO_ERROR -62
|
||||
|
||||
/* Translate a numeric error code to a human-readable error string */
|
||||
const char *ssh_err(int n);
|
||||
|
||||
+10
-1
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshkey.c,v 1.161 2026/02/06 22:59:18 dtucker Exp $ */
|
||||
/* $OpenBSD: sshkey.c,v 1.162 2026/06/14 03:59:34 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
|
||||
@@ -87,6 +87,8 @@ extern const struct sshkey_impl sshkey_ed25519_impl;
|
||||
extern const struct sshkey_impl sshkey_ed25519_cert_impl;
|
||||
extern const struct sshkey_impl sshkey_ed25519_sk_impl;
|
||||
extern const struct sshkey_impl sshkey_ed25519_sk_cert_impl;
|
||||
extern const struct sshkey_impl sshkey_mldsa44_ed25519_impl;
|
||||
extern const struct sshkey_impl sshkey_mldsa44_ed25519_cert_impl;
|
||||
#ifdef WITH_OPENSSL
|
||||
extern const struct sshkey_impl sshkey_ecdsa_sk_impl;
|
||||
extern const struct sshkey_impl sshkey_ecdsa_sk_cert_impl;
|
||||
@@ -111,6 +113,8 @@ const struct sshkey_impl * const keyimpls[] = {
|
||||
&sshkey_ed25519_cert_impl,
|
||||
&sshkey_ed25519_sk_impl,
|
||||
&sshkey_ed25519_sk_cert_impl,
|
||||
&sshkey_mldsa44_ed25519_impl,
|
||||
&sshkey_mldsa44_ed25519_cert_impl,
|
||||
#ifdef WITH_OPENSSL
|
||||
&sshkey_ecdsa_nistp256_impl,
|
||||
&sshkey_ecdsa_nistp256_cert_impl,
|
||||
@@ -422,6 +426,8 @@ sshkey_type_plain(int type)
|
||||
return KEY_ECDSA_SK;
|
||||
case KEY_ED25519_CERT:
|
||||
return KEY_ED25519;
|
||||
case KEY_MLDSA44_ED25519_CERT:
|
||||
return KEY_MLDSA44_ED25519;
|
||||
case KEY_ED25519_SK_CERT:
|
||||
return KEY_ED25519_SK;
|
||||
default:
|
||||
@@ -442,6 +448,8 @@ sshkey_type_certified(int type)
|
||||
return KEY_ECDSA_SK_CERT;
|
||||
case KEY_ED25519:
|
||||
return KEY_ED25519_CERT;
|
||||
case KEY_MLDSA44_ED25519:
|
||||
return KEY_MLDSA44_ED25519_CERT;
|
||||
case KEY_ED25519_SK:
|
||||
return KEY_ED25519_SK_CERT;
|
||||
default:
|
||||
@@ -3342,6 +3350,7 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
|
||||
#ifdef WITH_OPENSSL
|
||||
case KEY_ECDSA_SK:
|
||||
#endif /* WITH_OPENSSL */
|
||||
case KEY_MLDSA44_ED25519:
|
||||
return sshkey_private_to_blob2(key, blob, passphrase,
|
||||
comment, openssh_format_cipher, openssh_format_rounds);
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshkey.h,v 1.73 2026/03/03 09:57:26 dtucker Exp $ */
|
||||
/* $OpenBSD: sshkey.h,v 1.74 2026/06/14 03:59:34 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||
@@ -61,6 +61,8 @@ enum sshkey_types {
|
||||
KEY_ECDSA_SK_CERT,
|
||||
KEY_ED25519_SK,
|
||||
KEY_ED25519_SK_CERT,
|
||||
KEY_MLDSA44_ED25519,
|
||||
KEY_MLDSA44_ED25519_CERT,
|
||||
KEY_UNSPEC
|
||||
};
|
||||
|
||||
@@ -118,6 +120,9 @@ struct sshkey {
|
||||
/* KEY_ED25519 and KEY_ED25519_SK */
|
||||
u_char *ed25519_sk;
|
||||
u_char *ed25519_pk;
|
||||
/* KEY_MLDSA44_ED25519 */
|
||||
u_char *mldsa_ed25519_sk;
|
||||
u_char *mldsa_ed25519_pk;
|
||||
/* KEY_ECDSA_SK and KEY_ED25519_SK */
|
||||
char *sk_application;
|
||||
uint8_t sk_flags;
|
||||
|
||||
Reference in New Issue
Block a user