Skip to content

Commit fc2eaa4

Browse files
committed
test(transport): cover TLS hook trust manager on JVM
1 parent cb5d764 commit fc2eaa4

1 file changed

Lines changed: 96 additions & 0 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (c) 2026 Meshtastic LLC
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package org.meshtastic.mqtt.transport.tcp
18+
19+
import io.ktor.network.tls.TLSConfigBuilder
20+
import java.security.cert.X509Certificate
21+
import javax.net.ssl.X509TrustManager
22+
import kotlin.test.Test
23+
import kotlin.test.assertNotNull
24+
import kotlin.test.assertSame
25+
26+
/**
27+
* JVM-only checks that the caller's TLS hook mutates the same [TLSConfigBuilder] ktor
28+
* consumes, using `trustManager` — a property that exists only on the JVM and Android
29+
* actuals of [TLSConfigBuilder], and so cannot be asserted from `commonTest`.
30+
*
31+
* On the JVM, `configurePlatformTrust` is a no-op, so a hook-installed trust manager should
32+
* survive `applyMqttTls` untouched. On Android it is deliberately *not* preserved by
33+
* identity — it gets wrapped in a hostname-aware delegate. That wrapping needs the
34+
* `X509TrustManagerExtensions` framework class and is therefore out of unit-test scope.
35+
*/
36+
class TcpTransportTrustManagerTest {
37+
private object FakeTrustManager : X509TrustManager {
38+
override fun checkClientTrusted(
39+
chain: Array<out X509Certificate>,
40+
authType: String,
41+
) = Unit
42+
43+
override fun checkServerTrusted(
44+
chain: Array<out X509Certificate>,
45+
authType: String,
46+
) = Unit
47+
48+
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
49+
}
50+
51+
@Test
52+
fun hookInstalledTrustManagerSurvivesApplyMqttTls() {
53+
val builder = TLSConfigBuilder()
54+
builder.applyMqttTls("broker.example.com") { trustManager = FakeTrustManager }
55+
assertSame(FakeTrustManager, builder.trustManager)
56+
}
57+
58+
@Test
59+
fun hookInstalledTrustManagerSurvivesForIpLiteralHost() {
60+
// SNI is suppressed for IP literals, but the hook must still be applied — the
61+
// earlier SNI/trust coupling bug (Meshtastic-Android #5894) was exactly this
62+
// class of mistake.
63+
val builder = TLSConfigBuilder()
64+
builder.applyMqttTls("192.168.1.50") { trustManager = FakeTrustManager }
65+
assertSame(FakeTrustManager, builder.trustManager)
66+
}
67+
68+
@Test
69+
fun hookWriteToTrustManagerTakesEffect() {
70+
// The lambda runs against the live builder and its write survives the rest of
71+
// applyMqttTls. This does not assert the hook/platform-trust ordering — on the JVM
72+
// configurePlatformTrust is a no-op, so that ordering is not observable here.
73+
var ranWithBuilder = false
74+
val builder = TLSConfigBuilder()
75+
builder.applyMqttTls("broker.example.com") {
76+
ranWithBuilder = true
77+
trustManager = FakeTrustManager
78+
}
79+
assertNotNull(builder.trustManager)
80+
assertSame(FakeTrustManager, builder.trustManager)
81+
kotlin.test.assertTrue(ranWithBuilder)
82+
}
83+
84+
@Test
85+
fun factoryDoesNotInvokeHookAtCreateTime() {
86+
// The hook belongs to the handshake, not to transport construction.
87+
var invocations = 0
88+
val factory = TcpTransportFactory { invocations++ }
89+
// create() must not run the hook — that happens during the handshake.
90+
factory.create(
91+
org.meshtastic.mqtt.MqttEndpoint
92+
.Tcp("broker.example.com", 8883, tls = true),
93+
)
94+
kotlin.test.assertEquals(0, invocations)
95+
}
96+
}

0 commit comments

Comments
 (0)