Skip to content

Commit 9b2fcbd

Browse files
authored
Support for DTLS session authentication context updates via outbound dgram (#63)
* Support for DTLS session authentication context updates via outbound datagram context * Address comments * Remove hacky ways of setrting context
1 parent f8aa937 commit 9b2fcbd

File tree

8 files changed

+55
-94
lines changed

8 files changed

+55
-94
lines changed

kotlin-mbedtls-netty/src/main/kotlin/org/opencoap/ssl/netty/DtlsChannelHandler.kt

+1-16
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import io.netty.channel.ChannelHandlerContext
2121
import io.netty.channel.ChannelPromise
2222
import io.netty.channel.socket.DatagramPacket
2323
import org.opencoap.ssl.SslConfig
24-
import org.opencoap.ssl.SslException
2524
import org.opencoap.ssl.transport.ByteBufferPacket
2625
import org.opencoap.ssl.transport.DtlsServer
2726
import org.opencoap.ssl.transport.DtlsSessionLifecycleCallbacks
@@ -92,23 +91,9 @@ class DtlsChannelHandler @JvmOverloads constructor(
9291
when (msg) {
9392
is DatagramPacketWithContext -> {
9493
write(msg, promise, ctx)
95-
if (msg.sessionContext.sessionExpirationHint) {
96-
promise.toCompletableFuture().thenAccept {
97-
dtlsServer.closeSession(msg.recipient())
98-
}
99-
}
94+
dtlsServer.handleOutboundDtlsSessionContext(msg.recipient(), msg.sessionContext, promise.toCompletableFuture())
10095
}
10196
is DatagramPacket -> write(msg, promise, ctx)
102-
is SessionAuthenticationContext -> {
103-
msg.map.forEach { (key, value) ->
104-
if (!dtlsServer.putSessionAuthenticationContext(msg.adr, key, value)) {
105-
promise.setFailure(SslException("Session does not exists"))
106-
}
107-
}
108-
if (!promise.isDone) {
109-
promise.setSuccess()
110-
}
111-
}
11297

11398
else -> ctx.write(msg, promise)
11499
}

kotlin-mbedtls-netty/src/main/kotlin/org/opencoap/ssl/netty/SessionAuthenticationContext.kt

-21
This file was deleted.

kotlin-mbedtls-netty/src/test/kotlin/org/opencoap/ssl/netty/EchoHandler.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class EchoHandler : ChannelInboundHandlerAdapter() {
3232
val authContext = (sessionContext.authenticationContext["AUTH"] ?: "")
3333
val dgramContent = dgram.content().toByteArray()
3434
val goToSleep = dgramContent.toString(Charset.defaultCharset()).endsWith(":sleep")
35+
val newAuthContext = dgramContent.toString(Charset.defaultCharset())
36+
.takeIf { it.startsWith("auth:") }
37+
?.substringAfter(":")
3538

3639
val reply = ctx.alloc().buffer(dgramContent.size + 20)
3740
reply.writeBytes(echoPrefix)
@@ -43,7 +46,10 @@ class EchoHandler : ChannelInboundHandlerAdapter() {
4346
reply,
4447
dgram.sender(),
4548
null,
46-
sessionContext.copy(sessionExpirationHint = goToSleep)
49+
sessionContext.copy(
50+
authenticationContext = newAuthContext?.let { mapOf("AUTH" to it) } ?: emptyMap(),
51+
sessionSuspensionHint = goToSleep
52+
)
4753
)
4854
)
4955
}

kotlin-mbedtls-netty/src/test/kotlin/org/opencoap/ssl/netty/NettyTest.kt

+3-10
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import org.opencoap.ssl.EmptyCidSupplier
3636
import org.opencoap.ssl.PskAuth
3737
import org.opencoap.ssl.RandomCidSupplier
3838
import org.opencoap.ssl.SslConfig
39-
import org.opencoap.ssl.SslException
4039
import org.opencoap.ssl.netty.NettyHelpers.createBootstrap
4140
import org.opencoap.ssl.transport.DtlsServer
4241
import org.opencoap.ssl.transport.HashMapSessionStore
@@ -173,15 +172,16 @@ class NettyTest {
173172
}
174173

175174
@Test
176-
fun `should forward authentication context`() {
175+
fun `should forward authentication context passed inside outbound datagram`() {
177176
// connect and handshake
178177
val client = NettyTransportAdapter.connect(clientConf, srvAddress).mapToString()
179178

180179
assertTrue(client.send("hi").await())
181180
assertEquals("ECHO:hi", client.receive(5.seconds).await())
182181

183182
// when
184-
srvChannel.writeAndFlush(SessionAuthenticationContext(client.localAddress(), mapOf("AUTH" to "007:"))).get()
183+
assertTrue(client.send("auth:007:").await())
184+
assertEquals("ECHO:auth:007:", client.receive(5.seconds).await())
185185

186186
// then
187187
assertTrue(client.send("hi").await())
@@ -190,13 +190,6 @@ class NettyTest {
190190
client.close()
191191
}
192192

193-
@Test
194-
fun `should fail to forward authentication context for non existing client`() {
195-
assertThatThrownBy {
196-
srvChannel.writeAndFlush(SessionAuthenticationContext(localAddress(1), mapOf("AUTH" to "007:"))).get()
197-
}.hasRootCause(SslException("Session does not exists"))
198-
}
199-
200193
@Test
201194
fun `server should load session from store`() {
202195
sessionStore.write(StoredSessionPair.cid, SessionWithContext(StoredSessionPair.srvSession, mapOf(), Instant.ofEpochSecond(123456789)))

kotlin-mbedtls/src/main/kotlin/org/opencoap/ssl/transport/DtlsServer.kt

+23-10
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,22 @@ class DtlsServer(
9494
return (sessions[peerAddress] as? DtlsSession)?.encrypt(plainPacket)
9595
}
9696

97-
fun putSessionAuthenticationContext(adr: InetSocketAddress, key: String, value: String?): Boolean {
98-
return when (val s = sessions[adr]) {
99-
is DtlsSession -> {
100-
if (value != null) {
101-
s.authenticationContext += (key to value)
102-
} else {
103-
s.authenticationContext -= key
97+
private fun updateSessionAuthenticationContext(adr: InetSocketAddress, authCtxUpdate: Map<String, String?>): Boolean {
98+
if (authCtxUpdate.isEmpty()) return true
99+
100+
return when (val s = sessions[adr] as? DtlsSession) {
101+
null -> false
102+
103+
else -> {
104+
authCtxUpdate.forEach { (key, value) ->
105+
if (value != null) {
106+
s.authenticationContext += (key to value)
107+
} else {
108+
s.authenticationContext -= key
109+
}
104110
}
105111
true
106112
}
107-
108-
else -> false
109113
}
110114
}
111115

@@ -118,13 +122,22 @@ class DtlsServer(
118122
}
119123
}
120124

121-
fun closeSession(addr: InetSocketAddress) {
125+
private fun closeSession(addr: InetSocketAddress) {
122126
sessions.remove(addr)?.apply {
123127
storeAndClose()
124128
logger.info("[{}] [CID:{}] DTLS session was stored", peerAddress, (this as? DtlsSession)?.sessionContext?.cid?.toHex() ?: "na")
125129
}
126130
}
127131

132+
fun handleOutboundDtlsSessionContext(adr: InetSocketAddress, ctx: DtlsSessionContext, writeFuture: CompletableFuture<Boolean>) {
133+
if (ctx.sessionSuspensionHint) {
134+
writeFuture.thenAccept {
135+
closeSession(adr)
136+
}
137+
}
138+
updateSessionAuthenticationContext(adr, ctx.authenticationContext)
139+
}
140+
128141
fun loadSession(sessBuf: SessionWithContext?, adr: InetSocketAddress, cid: ByteArray): Boolean {
129142
return try {
130143
if (sessBuf == null) {

kotlin-mbedtls/src/main/kotlin/org/opencoap/ssl/transport/DtlsServerTransport.kt

+3-12
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,11 @@ class DtlsServerTransport private constructor(
9797

9898
when {
9999
encPacket == null -> completedFuture(false)
100-
packet.sessionContext.sessionExpirationHint -> {
101-
transport.send(encPacket).thenApply { isSuccess ->
102-
if (isSuccess) {
103-
dtlsServer.closeSession(packet.peerAddress)
104-
}
105-
isSuccess
100+
else -> {
101+
transport.send(encPacket).also {
102+
dtlsServer.handleOutboundDtlsSessionContext(packet.peerAddress, packet.sessionContext, it)
106103
}
107104
}
108-
else -> transport.send(encPacket)
109105
}
110106
}.thenCompose(Function.identity())
111107

@@ -118,9 +114,4 @@ class DtlsServerTransport private constructor(
118114
}.get(30, TimeUnit.SECONDS)
119115
executor.shutdown()
120116
}
121-
122-
fun putSessionAuthenticationContext(adr: InetSocketAddress, key: String, value: String?): CompletableFuture<Boolean> =
123-
executor.supply {
124-
dtlsServer.putSessionAuthenticationContext(adr, key, value)
125-
}
126117
}

kotlin-mbedtls/src/main/kotlin/org/opencoap/ssl/transport/DtlsSessionContext.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ data class DtlsSessionContext @JvmOverloads constructor(
2525
val peerCertificateSubject: String? = null,
2626
val cid: ByteArray? = null,
2727
val sessionStartTimestamp: Instant? = null,
28-
val sessionExpirationHint: Boolean = false
28+
val sessionSuspensionHint: Boolean = false
2929
) {
3030
companion object {
3131
@JvmField
@@ -45,7 +45,7 @@ data class DtlsSessionContext @JvmOverloads constructor(
4545
if (!cid.contentEquals(other.cid)) return false
4646
} else if (other.cid != null) return false
4747
if (sessionStartTimestamp != other.sessionStartTimestamp) return false
48-
if (sessionExpirationHint != other.sessionExpirationHint) return false
48+
if (sessionSuspensionHint != other.sessionSuspensionHint) return false
4949

5050
return true
5151
}
@@ -55,7 +55,7 @@ data class DtlsSessionContext @JvmOverloads constructor(
5555
result = 31 * result + (peerCertificateSubject?.hashCode() ?: 0)
5656
result = 31 * result + (cid?.contentHashCode() ?: 0)
5757
result = 31 * result + (sessionStartTimestamp?.hashCode() ?: 0)
58-
result = 31 * result + (sessionExpirationHint.hashCode())
58+
result = 31 * result + (sessionSuspensionHint.hashCode())
5959
return result
6060
}
6161
}

kotlin-mbedtls/src/test/kotlin/org/opencoap/ssl/transport/DtlsServerTransportTest.kt

+15-21
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,13 @@ class DtlsServerTransportTest {
7070
if (msg == "error") {
7171
throw Exception("error")
7272
} else if (msg.startsWith("Authenticate:")) {
73-
server.putSessionAuthenticationContext(packet.peerAddress, "auth", msg.substring(12))
74-
server.send(Packet("OK".toByteBuffer(), packet.peerAddress))
73+
server.send(
74+
Packet(
75+
"OK".toByteBuffer(),
76+
packet.peerAddress,
77+
DtlsSessionContext(authenticationContext = mapOf("auth" to msg.substring(12)))
78+
)
79+
)
7580
} else {
7681
val ctx = (packet.sessionContext.authenticationContext["auth"] ?: "")
7782
server.send(packet.map { "$msg:resp$ctx".toByteBuffer() })
@@ -456,25 +461,14 @@ class DtlsServerTransportTest {
456461
}
457462

458463
@Test
459-
fun `should set and use session context`() {
460-
// given
461-
server = DtlsServerTransport.create(conf, sessionStore = sessionStore)
462-
val serverReceived = server.receive(1.seconds)
463-
// and, client connected
464+
fun `should set and use session context passed inside outbound datagram`() {
465+
server = DtlsServerTransport.create(conf, expireAfter = 100.millis, sessionStore = sessionStore, lifecycleCallbacks = sslLifecycleCallbacks).listen(echoHandler)
466+
// client connected
464467
val client = DtlsTransmitter.connect(server, clientConfig).await()
465-
client.send("hello!")
466-
assertEquals("hello!", serverReceived.await().buffer.decodeToString())
467-
468-
// when, session context is set
469-
assertTrue(server.putSessionAuthenticationContext(serverReceived.await().peerAddress, "auth", "id:dev-007").await())
470-
471-
// and, client sends messages
472-
client.send("msg1")
473-
client.send("msg2")
474-
475-
// then
476-
assertEquals(mapOf("auth" to "id:dev-007"), server.receive(1.seconds).await().sessionContext.authenticationContext)
477-
assertEquals(mapOf("auth" to "id:dev-007"), server.receive(1.seconds).await().sessionContext.authenticationContext)
468+
client.send("Authenticate:dev-007")
469+
assertEquals("OK", client.receiveString())
470+
client.send("hi")
471+
assertEquals("hi:resp:dev-007", client.receiveString())
478472

479473
client.close()
480474
}
@@ -491,7 +485,7 @@ class DtlsServerTransportTest {
491485
assertEquals("dupa", client.receive(1.seconds).await())
492486

493487
client.send("sleep")
494-
server.send(Packet("sleep".toByteBuffer(), serverReceived.await().peerAddress, sessionContext = DtlsSessionContext(sessionExpirationHint = true)))
488+
server.send(Packet("sleep".toByteBuffer(), serverReceived.await().peerAddress, sessionContext = DtlsSessionContext(sessionSuspensionHint = true)))
495489
assertEquals("sleep", client.receive(1.seconds).await())
496490

497491
await.atMost(5.seconds).untilAsserted {

0 commit comments

Comments
 (0)