Skip to content

Commit 750ab35

Browse files
krutonclaude
andcommitted
fix(security): reject all-zero ECDH shared secret
A server could send a degenerate EC point that causes the ECDH agreement to compute a zero shared secret, yielding completely predictable session keys. This check mirrors the existing guard in Curve25519KeyExchange and MlKemHybridKeyExchange. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 36449b2 commit 750ab35

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchange.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* ConnectBot SSH Library
3-
* Copyright 2025 Kenny Root
3+
* Copyright 2025-2026 Kenny Root
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -96,14 +96,21 @@ internal class EcdhKeyExchange(private val curveName: String) : KexAlgorithm {
9696
agreement.doPhase(serverPubKey, true)
9797
val rawSecret = agreement.generateSecret()
9898

99-
return encodeMpint(BigInteger(1, rawSecret).toByteArray())
99+
return computeSharedSecretFromRaw(rawSecret)
100100
} catch (e: SshException) {
101101
throw e
102102
} catch (e: Exception) {
103103
throw SshException("Invalid server ECDH public key: ${e.message}", e)
104104
}
105105
}
106106

107+
internal fun computeSharedSecretFromRaw(rawSecret: ByteArray): ByteArray {
108+
if (rawSecret.all { it == 0.toByte() }) {
109+
throw SshException("Invalid ECDH shared secret: all zeroes")
110+
}
111+
return encodeMpint(BigInteger(1, rawSecret).toByteArray())
112+
}
113+
107114
private fun encodeEcPoint(point: ECPoint): ByteArray {
108115
val x = point.affineX.toByteArray()
109116
val y = point.affineY.toByteArray()

sshlib/src/test/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchangeTest.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* ConnectBot SSH Library
3-
* Copyright 2025 Kenny Root
3+
* Copyright 2025-2026 Kenny Root
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -125,6 +125,15 @@ class EcdhKeyExchangeTest {
125125
}
126126
}
127127

128+
@Test
129+
fun `rejects all-zero shared secret`() {
130+
val ecdh = EcdhKeyExchange("nistp256")
131+
ecdh.generateClientKeys()
132+
assertFailsWith<SshException> {
133+
ecdh.computeSharedSecretFromRaw(ByteArray(32))
134+
}
135+
}
136+
128137
@Test
129138
fun `rejects unknown curve`() {
130139
assertFailsWith<SshException> {

0 commit comments

Comments
 (0)