Skip to content

Commit

Permalink
Merge pull request #12 from OSGP/feature/FDP-1815
Browse files Browse the repository at this point in the history
Feature/fdp 1815
  • Loading branch information
LucianoFavoroso authored Feb 1, 2024
2 parents fd7b498 + 9417ae3 commit a844709
Show file tree
Hide file tree
Showing 19 changed files with 234 additions and 86 deletions.
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
}

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

0 comments on commit a844709

Please sign in to comment.