From 65f53289d17b602dd4ac5bcb6e82240458b3c906 Mon Sep 17 00:00:00 2001 From: Aluminique <290900021+aluminique@users.noreply.github.com> Date: Mon, 26 Oct 2026 00:28:52 +0000 Subject: [PATCH] fix(crypto): decode EC points without BigInteger(int, byte[], int, int) Android added this constructor only in API 33. On Android 12 and older the ECDSA and NIST ECDH point decoding throws NoSuchMethodError; since it is an Error, updateHostKeys does not catch it and the app dies right after login on servers with an ECDSA host key. Copy the coordinate bytes and use BigInteger(int, byte[]). The length is checked just before, so the ranges and values are the same. --- CHANGELOG.md | 7 +++++++ .../kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchange.kt | 5 +++-- .../connectbot/sshlib/crypto/EcdsaSignatureAlgorithm.kt | 5 +++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cedebcef1..bda92745a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ Changes for library users since `0.6.3`. +### Fixed + +- ECDSA host keys and NIST ECDH key exchange no longer fail with + `NoSuchMethodError` on Android 12 and older: EC points are decoded without + `BigInteger(int, byte[], int, int)`, which Android has only since API 33. + The failure killed the app when the server sent its host keys after login. + ## [0.6.3][0.6.3] Changes for library users since `0.6.2`. diff --git a/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchange.kt b/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchange.kt index 5938cab52..4346a01cd 100644 --- a/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchange.kt +++ b/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchange.kt @@ -154,8 +154,9 @@ internal class EcdhKeyExchange(private val curveName: String) : KexAlgorithm { if (encoded.isEmpty() || encoded[0] != 0x04.toByte() || encoded.size != 1 + 2 * fieldSize) { throw SshException("Invalid EC point encoding") } - val x = BigInteger(1, encoded, 1, fieldSize) - val y = BigInteger(1, encoded, 1 + fieldSize, fieldSize) + // BigInteger(int, byte[], int, int) is missing before Android 13 (API 33). + val x = BigInteger(1, encoded.copyOfRange(1, 1 + fieldSize)) + val y = BigInteger(1, encoded.copyOfRange(1 + fieldSize, encoded.size)) val p = (ecParameterSpec.curve.field as ECFieldFp).p if (x >= p || y >= p || y.multiply(y).mod(p) != x.multiply(x).multiply(x).add(ecParameterSpec.curve.a.multiply(x)).add(ecParameterSpec.curve.b).mod(p) diff --git a/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdsaSignatureAlgorithm.kt b/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdsaSignatureAlgorithm.kt index 36892ad21..973a0e9c4 100644 --- a/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdsaSignatureAlgorithm.kt +++ b/sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdsaSignatureAlgorithm.kt @@ -69,8 +69,9 @@ internal object EcdsaSignatureAlgorithm : SshSignatureAlgorithm { if (encoded[0] != 0x04.toByte() || encoded.size != 1 + 2 * fieldSize) { throw SshException("Invalid EC point encoding") } - val x = BigInteger(1, encoded, 1, fieldSize) - val y = BigInteger(1, encoded, 1 + fieldSize, fieldSize) + // BigInteger(int, byte[], int, int) is missing before Android 13 (API 33). + val x = BigInteger(1, encoded.copyOfRange(1, 1 + fieldSize)) + val y = BigInteger(1, encoded.copyOfRange(1 + fieldSize, encoded.size)) return ECPoint(x, y) }