Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
version: 2
registries:
maven-artifactory:
type: maven-repository
url: https://maven.pkg.github.com/osgp/
username: IoTFDP
password: ${{secrets.IOTFDP_GITHUB_TOKEN}}
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

The secret reference uses incorrect syntax. GitHub Actions secrets should be referenced as ${{ secrets.SECRET_NAME }} (with spaces around the braces), not ${{secrets.SECRET_NAME}}.

Suggested change
password: ${{secrets.IOTFDP_GITHUB_TOKEN}}
password: ${{ secrets.IOTFDP_GITHUB_TOKEN }}

Copilot uses AI. Check for mistakes.
updates:
- package-ecosystem: "gradle"
directory: "/"
open-pull-requests-limit: 10
registries: "*"
schedule:
interval: "daily"
groups:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
dependency-graph: generate-and-submit
- name: Build binaries
run: ./gradlew.bat build createReleaseDistributable packageReleaseMsi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish binaries
uses: actions/upload-artifact@v4
with:
Expand Down
21 changes: 21 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,24 @@ tasks.withType<KotlinCompile> {
dependsOn(tasks.named("updateGitHooks"))
compilerOptions { freeCompilerArgs.addAll("-Xjsr305=strict") }
}

subprojects {
repositories {
google {
mavenContent {
includeGroupAndSubgroups("androidx")
includeGroupAndSubgroups("com.android")
includeGroupAndSubgroups("com.google")
}
}
mavenCentral()
maven {
name = "GXFGithubPackages"
url = uri("https://maven.pkg.github.com/osgp/*")
credentials {
username = project.findProperty("github.username") as String? ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("github.token") as String? ?: System.getenv("GITHUB_TOKEN")
}
}
}
}
1 change: 1 addition & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ kotlin {
implementation(libs.ktor)
implementation(libs.kotlinSerializationJson)
implementation(libs.protobufJavaUtil)
implementation(libs.oslpMessageSigning)
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions composeApp/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# --- Keep proto classes ---
-keep class com.google.** { *; }
-keep class org.opensmartgridplatform.oslp.** { *; }
-keep class com.gxf.utilities.oslp.message.signing.** { *; }

# --- General kotlinx.serialization support ---
-keep class kotlinx.serialization.** { *; }
Expand All @@ -38,3 +39,30 @@
-dontwarn kotlin.Metadata
-dontwarn io.ktor.**
-dontnote io.ktor.**

# Suppress warnings for logback and SLF4J
-dontwarn ch.qos.logback.**
-dontwarn org.slf4j.**
-dontwarn kotlinx.coroutines.slf4j.**

# Suppress warnings for Android and Robolectric (if not targeting Android)
-dontwarn android.os.**
-dontwarn org.robolectric.**

# Suppress warnings for Google App Engine
-dontwarn com.google.appengine.**
-dontwarn com.google.apphosting.**

# Suppress warnings for libcore (Android internals)
-dontwarn libcore.io.**

# Suppress warnings for missing LogbackLoggerWrapper methods
-dontwarn io.github.oshai.kotlinlogging.logback.internal.LogbackLoggerWrapper

# Suppress warnings for missing LogbackLogEvent methods
-dontwarn io.github.oshai.kotlinlogging.logback.internal.LogbackLogEvent


-dontwarn com.oracle.svm.core.annotate.Substitute
-dontwarn com.oracle.svm.core.annotate.TargetElement
-dontwarn com.oracle.svm.core.annotate.TargetClass
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import org.lfenergy.gxf.oslp.models.MainViewModel
import org.lfenergy.gxf.oslp.service.DeviceStateService
import org.lfenergy.gxf.oslp.service.RequestService
import org.lfenergy.gxf.oslp.sockets.ServerSocket
import org.lfenergy.gxf.oslp.util.SigningUtil
import org.lfenergy.gxf.oslp.util.OslpSigningUtil

fun main() = application {
val requestService = remember { RequestService() }
Expand All @@ -41,9 +41,7 @@ fun main() = application {
onContinue = {
val config = ApplicationConfiguration.update(configurationViewModel)

isConfigured = SigningUtil.initializeKeys()

if (isConfigured) {
if (OslpSigningUtil.getInstance().oslpKeyProvider.initializeKeys()) {
config.storeConfiguration()
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

This change removes the assignment to isConfigured variable. The variable isConfigured is likely used elsewhere in the code, and this change may break the configuration flow logic.

Suggested change
config.storeConfiguration()
config.storeConfiguration()
isConfigured = true

Copilot uses AI. Check for mistakes.
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import org.lfenergy.gxf.oslp.domain.Envelope
import org.lfenergy.gxf.oslp.exception.InvalidJsonException
import org.lfenergy.gxf.oslp.sockets.ClientSocket
import org.lfenergy.gxf.oslp.util.Logger
import org.lfenergy.gxf.oslp.util.SigningUtil
import org.lfenergy.gxf.oslp.util.OslpSigningUtil
import org.lfenergy.gxf.oslp.util.toByteArray
import org.opensmartgridplatform.oslp.Oslp
import org.opensmartgridplatform.oslp.Oslp.LightValue
Expand Down Expand Up @@ -134,9 +134,10 @@ class RequestService() {
val messageBytes = payload.toByteArray()

val signature =
SigningUtil.createSignature(
sequenceNumber.toByteArray(2) + deviceId + lengthIndicator.toByteArray(2) + messageBytes
)
OslpSigningUtil.getInstance()
.createSignature(
sequenceNumber.toByteArray(2) + deviceId + lengthIndicator.toByteArray(2) + messageBytes
)

val request = Envelope(signature, sequenceNumber, deviceId, lengthIndicator, messageBytes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package org.lfenergy.gxf.oslp.sockets.receive

import org.lfenergy.gxf.oslp.domain.Envelope
import org.lfenergy.gxf.oslp.util.Logger
import org.lfenergy.gxf.oslp.util.SigningUtil
import org.lfenergy.gxf.oslp.util.OslpSigningUtil
import org.lfenergy.gxf.oslp.util.toByteArray
import org.opensmartgridplatform.oslp.Oslp.Message

Expand All @@ -26,10 +26,11 @@ abstract class ReceiveStrategy {
private fun validateSignature(requestEnvelope: Envelope): Boolean {
val verified =
with(requestEnvelope) {
SigningUtil.verifySignature(
sequenceNumber.toByteArray(2) + deviceId + lengthIndicator.toByteArray(2) + messageBytes,
securityKey,
)
OslpSigningUtil.getInstance()
.verifySignature(
sequenceNumber.toByteArray(2) + deviceId + lengthIndicator.toByteArray(2) + messageBytes,
securityKey,
)
}

if (!verified) {
Expand All @@ -41,12 +42,13 @@ abstract class ReceiveStrategy {

private fun createResponseEnvelope(requestEnvelope: Envelope, responsePayload: ByteArray): Envelope {
val securityKey =
SigningUtil.createSignature(
requestEnvelope.sequenceNumber.toByteArray(2) +
requestEnvelope.deviceId +
responsePayload.size.toByteArray(2) +
responsePayload
)
OslpSigningUtil.getInstance()
.createSignature(
requestEnvelope.sequenceNumber.toByteArray(2) +
requestEnvelope.deviceId +
responsePayload.size.toByteArray(2) +
responsePayload
)

return Envelope(
securityKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
// SPDX-License-Identifier: Apache-2.0
package org.lfenergy.gxf.oslp.util

import com.gxf.utilities.oslp.message.signing.KeyProvider
import java.io.File
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.PublicKey
import java.security.SecureRandom
import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.swing.JOptionPane
import org.lfenergy.gxf.oslp.models.ApplicationConfiguration

object SigningUtil {
private const val SECURITY_PROVIDER = "SunEC"
private const val SECURITY_ALGORITHM = "SHA256withECDSA"
private const val SECURITY_KEYTYPE = "EC"
class OslpKeyProvider : KeyProvider {
companion object {
const val SECURITY_PROVIDER = "SunEC"
private const val SECURITY_KEYTYPE = "EC"
}

private lateinit var publicKey: PublicKey
private lateinit var privateKey: PrivateKey
Expand All @@ -39,26 +39,12 @@ object SigningUtil {
return true
}

fun createSignature(message: ByteArray): ByteArray =
Signature.getInstance(SECURITY_ALGORITHM, SECURITY_PROVIDER)
.apply {
initSign(privateKey, SecureRandom())
update(message)
}
.sign()

fun verifySignature(message: ByteArray, securityKey: ByteArray): Boolean {
val builder =
Signature.getInstance(SECURITY_ALGORITHM, SECURITY_PROVIDER).apply {
initVerify(publicKey)
update(message)
}

// Truncation needed for some signature types, including the used SHA256withECDSA
val len = securityKey[1] + 2 and 0xff
val truncatedKey = securityKey.copyOf(len)
override fun getPublicKey(): PublicKey {
return publicKey
}

return builder.verify(truncatedKey)
override fun getPrivateKey(): PrivateKey {
return privateKey
}

private fun showErrorDialog(message: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright Contributors to the GXF project
//
// SPDX-License-Identifier: Apache-2.0
package org.lfenergy.gxf.oslp.util

import com.gxf.utilities.oslp.message.signing.SigningUtil
import com.gxf.utilities.oslp.message.signing.configuration.SigningProperties

class OslpSigningUtil(signingProperties: SigningProperties, val oslpKeyProvider: OslpKeyProvider) :
SigningUtil(signingProperties, oslpKeyProvider) {

companion object {
private var instance: OslpSigningUtil? = null
private const val SECURITY_ALGORITHM = "SHA256withECDSA"

fun getInstance(): OslpSigningUtil =
instance
?: OslpSigningUtil(
SigningProperties(OslpKeyProvider.SECURITY_PROVIDER, SECURITY_ALGORITHM),
OslpKeyProvider(),
)
.also { instance = it }
}
}
6 changes: 4 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
androidx-lifecycle = "2.9.4"
compose-multiplatform = "1.9.0"
gradleWrapperUpgrade = "0.12"
gxfUtils = "2.6.2"
kotlin = "2.2.20"
kotlinSerializationJson = "1.9.0"
kotlinx-coroutines = "1.10.2"
ktor = "3.3.0"
protobuf = "4.32.1"
protobufJavaUtil = "4.32.1"
protobufPlugin = "0.9.5"
spotless = "7.2.1"
kotlinSerializationJson = "1.9.0"
protobufJavaUtil = "4.32.1"

[libraries]
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
Expand All @@ -20,6 +21,7 @@ protobufKotlin = { group = "com.google.protobuf", name = "protobuf-kotlin", vers
protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
kotlinSerializationJson = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinSerializationJson" }
protobufJavaUtil = { group = "com.google.protobuf", name = "protobuf-java-util", version.ref = "protobufJavaUtil" }
oslpMessageSigning = { group = "com.gxf.utilities", name = "oslp-message-signing", version.ref = "gxfUtils" }

[plugins]
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
Expand Down
27 changes: 0 additions & 27 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,6 @@ rootProject.name = "oslp-test-tool"

enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

pluginManagement {
repositories {
google {
mavenContent {
includeGroupAndSubgroups("androidx")
includeGroupAndSubgroups("com.android")
includeGroupAndSubgroups("com.google")
}
}
mavenCentral()
gradlePluginPortal()
}
}

dependencyResolutionManagement {
repositories {
google {
mavenContent {
includeGroupAndSubgroups("androidx")
includeGroupAndSubgroups("com.android")
includeGroupAndSubgroups("com.google")
}
}
mavenCentral()
}
}

include(":composeApp")

include(":protobuf")
Loading