Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/fdp 1815 #12

Merged
merged 12 commits into from
Feb 1, 2024
2 changes: 2 additions & 0 deletions application/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependencies {
implementation(libs.bundles.data)
implementation(libs.logging)

implementation(libs.commonsCodec)

implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor")
implementation("jakarta.xml.bind:jakarta.xml.bind-api")

Expand Down
1 change: 1 addition & 0 deletions application/src/integrationTest/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ simulator:
# pskKey: coaps_secret_key
psk-identity: 867787050253370
psk-key: ABCDEFGHIJKLMNOP
psk-secret: "123456"
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class CaliforniumConfiguration(private val simulatorProperties: SimulatorPropert
.set(CoapConfig.COAP_PORT, simulatorProperties.uri.port)
.set(CoapConfig.COAP_SECURE_PORT, simulatorProperties.uri.port)
.set(DtlsConfig.DTLS_ROLE, DtlsRole.CLIENT_ONLY)
.set(DtlsConfig.DTLS_CIPHER_SUITES, listOf(CipherSuite.TLS_PSK_WITH_AES_256_CCM_8))
.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, false)
.set(DtlsConfig.DTLS_CIPHER_SUITES, listOf(CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

package org.gxf.crestdevicesimulator.configuration

import org.eclipse.californium.elements.config.Configuration
import org.gxf.crestdevicesimulator.simulator.data.entity.PreSharedKey
import org.gxf.crestdevicesimulator.simulator.data.repository.PskRepository
import org.springframework.context.annotation.Bean
Expand All @@ -19,7 +18,7 @@ class CoapClientConfiguration(private val simulatorProperties: SimulatorProperti
val savedKey = pskRepository.findById(simulatorProperties.pskIdentity)

if (savedKey.isEmpty) {
val initialPreSharedKey = PreSharedKey(simulatorProperties.pskIdentity, simulatorProperties.pskKey)
val initialPreSharedKey = PreSharedKey(simulatorProperties.pskIdentity, simulatorProperties.pskKey, simulatorProperties.pskSecret)
pskRepository.save(initialPreSharedKey)
store.key = simulatorProperties.pskKey
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class SimulatorProperties(
val uri: URI,
val pskIdentity: String,
val pskKey: String,
val pskSecret: String,
val messagePath: String,
val produceValidCbor: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class CoapClientService(
}
return coapClient
}

private fun createDtlsConnector(advancedSingleIdentityPskStore: AdvancedSingleIdentityPskStore): DTLSConnector {
val address = InetSocketAddress(0)
val dtlsBuilder = DtlsConnectorConfig.builder(configuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ import jakarta.persistence.Entity
import jakarta.persistence.Id

@Entity
class PreSharedKey(@Id val identity: String, var preSharedKey: String)
class PreSharedKey(@Id val identity: String, var preSharedKey: String, var secret: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Contributors to the GXF project
//
// SPDX-License-Identifier: Apache-2.0

package org.gxf.crestdevicesimulator.simulator.response

object PskExtractor {

/**
* Regex to split a valid PSK set command in 3 groups
* Group 0 containing everything
* Group 1 containing the next 16 chars after PSK: this is only the key
* Group 2 containing the next 64 chars after the key this is only the hash
*/
private val pskKeyHashSplitterRegex = "!PSK:([a-zA-Z0-9]{16})([a-zA-Z0-9]{64});PSK:[a-zA-Z0-9]{16}[a-zA-Z0-9]{64}SET".toRegex()

fun hasPskCommand(command: String) = pskKeyHashSplitterRegex.matches(command)

fun extractKeyFromCommand(command: String) = pskKeyHashSplitterRegex.findAll(command).first().groups[1]!!.value

fun extractHashFromCommand(command: String) = pskKeyHashSplitterRegex.findAll(command).first().groups[2]!!.value
Comment on lines +19 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather have one regex findAll instead of 2 but for the simulator this could be good enough

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,18 @@

package org.gxf.crestdevicesimulator.simulator.response

import io.github.oshai.kotlinlogging.KotlinLogging
import org.eclipse.californium.core.CoapResponse
import org.gxf.crestdevicesimulator.configuration.AdvancedSingleIdentityPskStore
import org.gxf.crestdevicesimulator.configuration.SimulatorProperties
import org.gxf.crestdevicesimulator.simulator.data.repository.PskRepository
import org.gxf.crestdevicesimulator.simulator.response.command.PskCommandHandler
import org.springframework.stereotype.Component

@Component
class ResponseHandler(private val simulatorProperties: SimulatorProperties,
private val pskRepository: PskRepository,
private val pskKeyExtractor: PskKeyExtractor,
private val pskStore: AdvancedSingleIdentityPskStore) {

private val logger = KotlinLogging.logger {}
class ResponseHandler(private val pskCommandHandler: PskCommandHandler) {

fun handleResponse(response: CoapResponse) {
val body = String(response.payload)

if (pskKeyExtractor.hasPskCommand(body)) {
val newPsk = pskKeyExtractor.extractKeyFromCommand(body)
handlePskChange(newPsk)
}
}

private fun handlePskChange(newPsk: String) {
val current = pskRepository.findById(simulatorProperties.pskIdentity)
if (current.isEmpty) {
logger.error { "No psk for identity: ${simulatorProperties.pskIdentity}" }
if (PskExtractor.hasPskCommand(body)) {
pskCommandHandler.handlePskChange(body)
}

logger.info { "Setting psk $newPsk for ${simulatorProperties.pskIdentity}" }

pskRepository.save(current.get().apply { preSharedKey = newPsk })
pskStore.key = newPsk
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Contributors to the GXF project
//
// SPDX-License-Identifier: Apache-2.0

package org.gxf.crestdevicesimulator.simulator.response.command

import io.github.oshai.kotlinlogging.KotlinLogging
import org.apache.commons.codec.digest.DigestUtils
import org.gxf.crestdevicesimulator.configuration.AdvancedSingleIdentityPskStore
import org.gxf.crestdevicesimulator.configuration.SimulatorProperties
import org.gxf.crestdevicesimulator.simulator.data.repository.PskRepository
import org.gxf.crestdevicesimulator.simulator.response.PskExtractor
import org.gxf.crestdevicesimulator.simulator.response.command.exception.InvalidPskHashException
import org.springframework.stereotype.Service

@Service
class PskCommandHandler(private val pskRepository: PskRepository,
private val simulatorProperties: SimulatorProperties,
private val pskStore: AdvancedSingleIdentityPskStore) {

private val logger = KotlinLogging.logger {}

fun handlePskChange(body: String) {
val newPsk = PskExtractor.extractKeyFromCommand(body)
val hash = PskExtractor.extractHashFromCommand(body)

val preSharedKeyOptional = pskRepository.findById(simulatorProperties.pskIdentity)

if (preSharedKeyOptional.isEmpty) {
logger.error { "No psk for identity: ${simulatorProperties.pskIdentity}" }
}

logger.info { "Validating hash for identity: ${simulatorProperties.pskIdentity}" }

val preSharedKey = preSharedKeyOptional.get()
val secret = preSharedKey.secret
val expectedHash = DigestUtils.sha256Hex("$secret$newPsk")

if (expectedHash != hash) {
throw InvalidPskHashException("PSK set Hash for Identity ${simulatorProperties.pskIdentity} did not match")
}

pskRepository.save(preSharedKey.apply { this.preSharedKey = newPsk })
pskStore.key = newPsk
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: Contributors to the GXF project
//
// SPDX-License-Identifier: Apache-2.0

package org.gxf.crestdevicesimulator.simulator.response.command.exception

class InvalidPskHashException(message: String) : Exception(message)
1 change: 1 addition & 0 deletions application/src/main/resources/application-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ simulator:
# pskKey: coaps_secret_key
psk-identity: 867787050253370
psk-key: ABCDEFGHIJKLMNOP
psk-secret: "123456"
8 changes: 8 additions & 0 deletions application/src/main/resources/db/migration/V2__secret.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- SPDX-FileCopyrightText: Contributors to the GXF project
--
-- SPDX-License-Identifier: Apache-2.0
-- No production data was set before this change so we can drop all existing data
delete from pre_shared_key;

alter table pre_shared_key
add column secret varchar(255) not null;
36 changes: 0 additions & 36 deletions application/src/test/kotlin/PreSharedKeyKeyExtractorTest.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.gxf.crestdevicesimulator.simulator

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper
import org.eclipse.californium.core.CoapClient
import org.eclipse.californium.core.CoapResponse
import org.eclipse.californium.core.coap.Request
import org.gxf.crestdevicesimulator.configuration.SimulatorProperties
import org.gxf.crestdevicesimulator.simulator.CborFactory
import org.gxf.crestdevicesimulator.simulator.Simulator
import org.gxf.crestdevicesimulator.simulator.coap.CoapClientService
import org.gxf.crestdevicesimulator.simulator.response.ResponseHandler
import org.junit.jupiter.api.Assertions.assertArrayEquals
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Contributors to the GXF project
//
// SPDX-License-Identifier: Apache-2.0
package org.gxf.crestdevicesimulator.simulator.response

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource


class PskExtractorTest {

companion object {
private const val testHash = "1234567890123456123456789012345612345678901234561234567890123456"

private const val validPskCommand = "!PSK:1234567891234567${testHash};PSK:1234567891234567${testHash}SET"
private const val validPskCommandWithKeyWordsInKey = "!PSK:PSKaSET1PSKd2SET${testHash};PSK:PSKaSET1PSKd2SET${testHash}SET"
private const val invalidKeySizePskCommand = "!PSK:1234${testHash};PSK:1234${testHash}SET"
private const val notPskCommand = "NoPskCommandInThisString"
}


@ParameterizedTest
@CsvSource(
"$validPskCommand, true",
"$validPskCommandWithKeyWordsInKey, true",
"$invalidKeySizePskCommand, false",
"$notPskCommand, false"
)
fun shouldReturnTrueWhenThereIsAPskCommandInString(pskCommand: String, isValid: Boolean) {
val result = PskExtractor.hasPskCommand(pskCommand)
assertThat(result).isEqualTo(isValid)
}

@ParameterizedTest
@CsvSource(
"$validPskCommand, 1234567891234567",
"$validPskCommandWithKeyWordsInKey, PSKaSET1PSKd2SET"
)
fun shouldReturnPskKeyFromValidPskCommand(pskCommand: String, expectedKey: String) {
val result = PskExtractor.extractKeyFromCommand(pskCommand)

assertThat(result).isEqualTo(expectedKey)
}

@ParameterizedTest
@CsvSource(
"$validPskCommand, $testHash",
"$validPskCommandWithKeyWordsInKey, $testHash"
)
fun shouldReturnHashFromValidPskCommand(pskCommand: String, expectedHash: String) {
val result = PskExtractor.extractHashFromCommand(pskCommand)

assertThat(result).isEqualTo(expectedHash)
}
}
Loading
Loading