Federico Mariani created CAMEL-26659:
----------------------------------------

             Summary: camel-ssh, camel-mina-sftp - prefer mlkem768x25519-sha256 
over the slower sntrup761 key exchange by default
                 Key: CAMEL-26659
                 URL: https://issues.apache.org/jira/browse/CAMEL-26659
             Project: Camel
          Issue Type: Improvement
          Components: camel-mina-sftp, camel-ssh
    Affects Versions: 4.18.5, 4.23.0, 4.22.2
            Reporter: Federico Mariani


h3. Problem
Apache MINA SSHD 2.20.0 (used by {{camel-ssh}} and {{camel-mina-sftp}}) 
proposes {{sntrup761x25519-sha512}} and its {{@openssh.com}} alias ahead of 
{{mlkem768x25519-sha256}}. The order is {{DEFAULT_KEX_PREFERENCE}} in sshd-core 
{{BaseBuilder.java}} lines 88-108, unchanged on MINA master.
* SSH picks the first algorithm in the client's list that the server also 
supports (RFC 4253, section 7.1). So against an OpenSSH 9.0 or later server, 
Camel negotiates sntrup761.
* That includes OpenSSH 10.0 and later. Since 10.0, OpenSSH itself uses 
{{mlkem768x25519-sha256}} by default (its 10.0 release notes; the 
{{KEX_SERVER_KEX}} list in {{myproposal.h}} starts with 
{{mlkem768x25519-sha256}}, then {{sntrup761x25519-sha512}}).

MINA runs both through the pure-Java Bouncy Castle code, and sntrup761 is much 
slower. Measured medians, Bouncy Castle 1.86, JDK 25, Apple M1 Pro:
* client key generation 36.6 ms;
* client decapsulation 22.2 ms;
* server encapsulation 7.6 ms.
So about 59 ms of CPU lands on the Camel side for each key exchange.

* *{{camel-ssh}}:* it opens and closes a session per exchange 
({{components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshHelper.java}}
 lines 92 and 212-219). So every exchange pays this cost.
** Bouncy Castle is a compile dependency (bcpg/bcpkix), so released versions 
already negotiate sntrup761.
* *{{camel-mina-sftp}}:* it builds its client with 
{{SshClient.setUpDefaultClient()}} ({{MinaSftpOperations.java}} line 248) and 
has the same default.
** Since CAMEL-26549 (main, 4.24.0) Bouncy Castle is on its runtime classpath, 
so from 4.24.0 every new connection pays the same cost.
** This was not measured for {{camel-mina-sftp}}.

h3. How to reproduce
Measured on main 7978db3d42dd with a test in {{camel-ssh}}:
* An embedded MINA {{SshServer}}, as in {{SshComponentTestSupport}}, with 
password authentication and an echo command.
* Routes 
{{from("direct:x").to("ssh://smx:smx@localhost:<port>?timeout=5000[&kex=...]")}}.
* 200 warm-up exchanges, then 300 measured.
* A session listener records the sessions and the negotiated key exchange.

||Endpoint||Negotiated key exchange||Median per exchange||
|default|sntrup761x25519-sha512|70.6 ms (p90 108 ms)|
|{{kex=mlkem768x25519-sha256}}|mlkem768x25519-sha256|3.8 ms|
|{{kex=curve25519-sha256}}|curve25519-sha256|2.6 ms|
|control: one reused session, one exec channel per command|-|0.5 ms|

* 50 exchanges gave 50 server sessions and 50 authentications.
* Client and server ran in one JVM, so the default row includes the 7.6 ms of 
server-side encapsulation. Against a native OpenSSH server, the Camel-side 
part, about 59 ms, would remain. That part was measured on Bouncy Castle 
directly, not against a real OpenSSH server.

h3. Proposed change
* When {{kex}} ({{camel-ssh}}) or {{keyExchangeProtocols}} 
({{camel-mina-sftp}}) is not set, move {{mlkem768x25519-sha256}} ahead of the 
two sntrup761 entries in the client's default key exchange factories.
** Keep the rest of MINA's order, and only reorder entries that are supported.
** For {{camel-ssh}}, in {{SshUtils.createAndStartClient}} (line 173). For 
{{camel-mina-sftp}}, next to {{configureKeyExchangeProtocols}} 
({{MinaSftpOperations.java}} line 328).
* *Interoperability:*
** A server that offers {{mlkem768x25519-sha256}} (OpenSSH 9.9 and later) 
negotiates it.
** OpenSSH 9.0 to 9.8 offers only {{[email protected]}}, so it 
negotiates that as today.
** Older or other servers fall back exactly as today.
** Both are hybrid post-quantum key exchanges, so an endpoint that negotiates 
one today still does. The new order matches the OpenSSH 10.0 client default.
* *Workaround available today:* set the order explicitly. The option replaces 
the whole proposal, so list the fallbacks too, for example:
{code}
ssh://host:22?username=admin&kex=mlkem768x25519-sha256,sntrup761x25519-sha512,[email protected],curve25519-sha256,ecdh-sha2-nistp256
{code}
** On {{camel-mina-sftp}} the option is {{keyExchangeProtocols}}.
* Optionally, propose the same order to Apache MINA SSHD (no upstream issue 
found).
* Reusing the {{camel-ssh}} session across exchanges is a separate, larger 
change and is not part of this issue.

h3. Impact / upgrade
* *What changes:* against servers that offer both algorithms, the negotiated 
key exchange changes. This needs an entry in 
{{camel-4x-upgrade-guide-4_24.adoc}}.
** The CAMEL-26549 entry there (around line 1455) and the docs say MINA 
negotiates sntrup761 "because it proposes that first". Update both: 
{{ssh-component.adoc}} "Post-Quantum Key Exchange" and 
{{mina-sftp-security.adoc}} line 204.
* Endpoints that set {{kex}} or {{keyExchangeProtocols}} are unaffected.
* If this lands before 4.24.0, {{camel-mina-sftp}} users never get the 
sntrup761 cost.
* *Affected versions (camel-ssh):* 4.18.5, 4.22.2 and 4.23.0 ship sshd 2.20.0 
with Bouncy Castle at compile scope.

h3. Related
* CAMEL-26549 (commit e413bf8987c8): Bouncy Castle at runtime for 
{{camel-mina-sftp}}/{{camel-ftp}}, and docs for the key exchange order.
* Apache MINA SSHD GH-606 (commit 38bb2c61): added the ML-KEM groups after 
sntrup761 in the default order.
* CAMEL-7277 and CAMEL-14063: the per-exchange session of {{camel-ssh}}.

_Claude Code on behalf of Federico Mariani_



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to