Skip to content

Commit f9bc4f8

Browse files
beldmitclaude
andcommitted
Implement ML-DSA signature algorithms per draft-sfluhrer-ssh-mldsa-06
Add ssh-mldsa-44, ssh-mldsa-65, and ssh-mldsa-87 public key algorithms using FIPS 204 (ML-DSA) via OpenSSL 3.5+ EVP API. Includes certificate variants and FIPS mode support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 785ee73 commit f9bc4f8

12 files changed

Lines changed: 681 additions & 18 deletions

Makefile.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
103103
msg.o dns.o entropy.o gss-genr.o umac.o umac128.o \
104104
smult_curve25519_ref.o \
105105
poly1305.o chacha.o cipher-chachapoly.o cipher-chachapoly-libcrypto.o \
106-
ssh-ed25519.o digest-openssl.o digest-libc.o \
106+
ssh-ed25519.o ssh-mldsa.o digest-openssl.o digest-libc.o \
107107
hmac.o ed25519.o ed25519-openssl.o \
108108
kex.o kex-names.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
109109
kexgexc.o kexgexs.o \

authfd.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,14 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
605605
case KEY_ECDSA_CERT:
606606
case KEY_ECDSA_SK:
607607
case KEY_ECDSA_SK_CERT:
608+
#ifdef OPENSSL_HAS_MLDSA
609+
case KEY_MLDSA44:
610+
case KEY_MLDSA44_CERT:
611+
case KEY_MLDSA65:
612+
case KEY_MLDSA65_CERT:
613+
case KEY_MLDSA87:
614+
case KEY_MLDSA87_CERT:
615+
#endif
608616
#endif
609617
case KEY_ED25519:
610618
case KEY_ED25519_CERT:

configure.ac

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1515

16-
AC_INIT([OpenSSH], [Portable], [openssh-unix-dev@mindrot.org])
16+
AC_INIT([OpenSSH],[Portable],[openssh-unix-dev@mindrot.org])
1717
AC_CONFIG_MACRO_DIR([m4])
1818
AC_CONFIG_SRCDIR([ssh.c])
1919

@@ -845,29 +845,22 @@ int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
845845
AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
846846
[Prepend the address family to IP tunnel traffic])
847847
AC_MSG_CHECKING([if we have the Security Authorization Session API])
848-
AC_TRY_COMPILE([#include <Security/AuthSession.h>],
849-
[SessionCreate(0, 0);],
850-
[ac_cv_use_security_session_api="yes"
848+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <Security/AuthSession.h>]], [[SessionCreate(0, 0);]])],[ac_cv_use_security_session_api="yes"
851849
AC_DEFINE([USE_SECURITY_SESSION_API], [1],
852850
[platform has the Security Authorization Session API])
853851
LIBS="$LIBS -framework Security"
854-
AC_MSG_RESULT([yes])],
855-
[ac_cv_use_security_session_api="no"
852+
AC_MSG_RESULT([yes])],[ac_cv_use_security_session_api="no"
856853
AC_MSG_RESULT([no])])
857854
AC_MSG_CHECKING([if we have an in-memory credentials cache])
858-
AC_TRY_COMPILE(
859-
[#include <Kerberos/Kerberos.h>],
860-
[cc_context_t c;
861-
(void) cc_initialize (&c, 0, NULL, NULL);],
862-
[AC_DEFINE([USE_CCAPI], [1],
855+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <Kerberos/Kerberos.h>]], [[cc_context_t c;
856+
(void) cc_initialize (&c, 0, NULL, NULL);]])],[AC_DEFINE([USE_CCAPI], [1],
863857
[platform uses an in-memory credentials cache])
864858
LIBS="$LIBS -framework Security"
865859
AC_MSG_RESULT([yes])
866860
if test "x$ac_cv_use_security_session_api" = "xno"; then
867861
AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
868-
fi],
869-
[AC_MSG_RESULT([no])]
870-
)
862+
fi],[AC_MSG_RESULT([no])
863+
])
871864
m4_pattern_allow([AU_IPv])
872865
AC_CHECK_DECL([AU_IPv4], [],
873866
AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
@@ -3494,6 +3487,26 @@ if test "x$openssl" = "xyes" ; then
34943487
AC_MSG_RESULT([no])
34953488
]
34963489
)
3490+
3491+
AC_MSG_CHECKING([whether OpenSSL has ML-DSA support])
3492+
AC_LINK_IFELSE(
3493+
[AC_LANG_PROGRAM([[
3494+
#include <openssl/evp.h>
3495+
]], [[
3496+
EVP_PKEY_CTX *ctx;
3497+
ctx = EVP_PKEY_CTX_new_from_name(NULL, "ML-DSA-44", NULL);
3498+
if (ctx == NULL) return 1;
3499+
EVP_PKEY_CTX_free(ctx);
3500+
]])],
3501+
[
3502+
AC_MSG_RESULT([yes])
3503+
AC_DEFINE([OPENSSL_HAS_MLDSA], [1],
3504+
[libcrypto has ML-DSA support])
3505+
],
3506+
[
3507+
AC_MSG_RESULT([no])
3508+
]
3509+
)
34973510
fi
34983511

34993512
# PKCS11/U2F depend on OpenSSL and dlopen().

myproposal.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,13 @@
6060
"sk-ecdsa-sha2-nistp256@openssh.com," \
6161
"webauthn-sk-ecdsa-sha2-nistp256@openssh.com," \
6262
"rsa-sha2-512," \
63-
"rsa-sha2-256"
63+
"rsa-sha2-256," \
64+
"ssh-mldsa-44-cert-v01@openssh.com," \
65+
"ssh-mldsa-65-cert-v01@openssh.com," \
66+
"ssh-mldsa-87-cert-v01@openssh.com," \
67+
"ssh-mldsa-44," \
68+
"ssh-mldsa-65," \
69+
"ssh-mldsa-87"
6470

6571
#define KEX_FIPS_PK_ALG \
6672
"ecdsa-sha2-nistp256-cert-v01@openssh.com," \
@@ -72,7 +78,13 @@
7278
"ecdsa-sha2-nistp384," \
7379
"ecdsa-sha2-nistp521," \
7480
"rsa-sha2-512," \
75-
"rsa-sha2-256"
81+
"rsa-sha2-256," \
82+
"ssh-mldsa-44-cert-v01@openssh.com," \
83+
"ssh-mldsa-65-cert-v01@openssh.com," \
84+
"ssh-mldsa-87-cert-v01@openssh.com," \
85+
"ssh-mldsa-44," \
86+
"ssh-mldsa-65," \
87+
"ssh-mldsa-87"
7688

7789
#define KEX_SERVER_ENCRYPT \
7890
"chacha20-poly1305@openssh.com," \

pathnames.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key"
4040
#define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key"
4141
#define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key"
42+
#define _PATH_HOST_MLDSA44_KEY_FILE SSHDIR "/ssh_host_mldsa44_key"
43+
#define _PATH_HOST_MLDSA65_KEY_FILE SSHDIR "/ssh_host_mldsa65_key"
44+
#define _PATH_HOST_MLDSA87_KEY_FILE SSHDIR "/ssh_host_mldsa87_key"
4245
#define _PATH_DH_MODULI SSHDIR "/moduli"
4346
#define _PATH_SCP_KILL_SWITCH SSHDIR "/disable_scp"
4447

@@ -91,6 +94,9 @@
9194
#define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519"
9295
#define _PATH_SSH_CLIENT_ID_ECDSA_SK _PATH_SSH_USER_DIR "/id_ecdsa_sk"
9396
#define _PATH_SSH_CLIENT_ID_ED25519_SK _PATH_SSH_USER_DIR "/id_ed25519_sk"
97+
#define _PATH_SSH_CLIENT_ID_MLDSA44 _PATH_SSH_USER_DIR "/id_mldsa44"
98+
#define _PATH_SSH_CLIENT_ID_MLDSA65 _PATH_SSH_USER_DIR "/id_mldsa65"
99+
#define _PATH_SSH_CLIENT_ID_MLDSA87 _PATH_SSH_USER_DIR "/id_mldsa87"
94100

95101
/*
96102
* Configuration file in user's home directory. This file need not be

ssh-keygen.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,17 @@ ask_filename(struct passwd *pw, const char *prompt)
279279
case KEY_ED25519_SK_CERT:
280280
name = _PATH_SSH_CLIENT_ID_ED25519_SK;
281281
break;
282+
#ifdef OPENSSL_HAS_MLDSA
283+
case KEY_MLDSA44:
284+
name = _PATH_SSH_CLIENT_ID_MLDSA44;
285+
break;
286+
case KEY_MLDSA65:
287+
name = _PATH_SSH_CLIENT_ID_MLDSA65;
288+
break;
289+
case KEY_MLDSA87:
290+
name = _PATH_SSH_CLIENT_ID_MLDSA87;
291+
break;
292+
#endif
282293
default:
283294
fatal("bad key type");
284295
}
@@ -1024,6 +1035,11 @@ do_gen_all_hostkeys(struct passwd *pw)
10241035
#endif /* OPENSSL_HAS_ECC */
10251036
#endif /* WITH_OPENSSL */
10261037
{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
1038+
#ifdef OPENSSL_HAS_MLDSA
1039+
{ "mldsa44", "MLDSA44",_PATH_HOST_MLDSA44_KEY_FILE },
1040+
{ "mldsa65", "MLDSA65",_PATH_HOST_MLDSA65_KEY_FILE },
1041+
{ "mldsa87", "MLDSA87",_PATH_HOST_MLDSA87_KEY_FILE },
1042+
#endif
10271043
{ NULL, NULL, NULL }
10281044
};
10291045

ssh-keyscan.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,25 @@ int ssh_port = SSH_DEFAULT_PORT;
6565
#define KT_ED25519 (1<<2)
6666
#define KT_ECDSA_SK (1<<4)
6767
#define KT_ED25519_SK (1<<5)
68+
#ifdef OPENSSL_HAS_MLDSA
69+
#define KT_MLDSA44 (1<<6)
70+
#define KT_MLDSA65 (1<<7)
71+
#define KT_MLDSA87 (1<<8)
72+
#endif
6873

6974
#define KT_MIN KT_RSA
75+
#ifdef OPENSSL_HAS_MLDSA
76+
#define KT_MAX KT_MLDSA87
77+
#else
7078
#define KT_MAX KT_ED25519_SK
79+
#endif
7180

7281
int get_cert = 0;
73-
int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK;
82+
int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK
83+
#ifdef OPENSSL_HAS_MLDSA
84+
|KT_MLDSA44|KT_MLDSA65|KT_MLDSA87
85+
#endif
86+
;
7487

7588
int hash_hosts = 0; /* Hash hostname on output */
7689

@@ -276,6 +289,20 @@ keygrab_ssh2(con *c)
276289
"sk-ssh-ed25519-cert-v01@openssh.com" :
277290
"sk-ssh-ed25519@openssh.com";
278291
break;
292+
#ifdef OPENSSL_HAS_MLDSA
293+
case KT_MLDSA44:
294+
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
295+
"ssh-mldsa-44-cert-v01@openssh.com" : "ssh-mldsa-44";
296+
break;
297+
case KT_MLDSA65:
298+
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
299+
"ssh-mldsa-65-cert-v01@openssh.com" : "ssh-mldsa-65";
300+
break;
301+
case KT_MLDSA87:
302+
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
303+
"ssh-mldsa-87-cert-v01@openssh.com" : "ssh-mldsa-87";
304+
break;
305+
#endif
279306
default:
280307
fatal("unknown key type %d", c->c_keytype);
281308
break;
@@ -756,6 +783,17 @@ main(int argc, char **argv)
756783
case KEY_ECDSA_SK:
757784
get_keytypes |= KT_ECDSA_SK;
758785
break;
786+
#ifdef OPENSSL_HAS_MLDSA
787+
case KEY_MLDSA44:
788+
get_keytypes |= KT_MLDSA44;
789+
break;
790+
case KEY_MLDSA65:
791+
get_keytypes |= KT_MLDSA65;
792+
break;
793+
case KEY_MLDSA87:
794+
get_keytypes |= KT_MLDSA87;
795+
break;
796+
#endif
759797
case KEY_UNSPEC:
760798
default:
761799
fatal("Unknown key type \"%s\"", tname);

0 commit comments

Comments
 (0)