Skip to content

Commit

Permalink
added non-native decryption
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Dec 9, 2024
1 parent da9d717 commit 23670de
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,30 @@ class NativeGenerator(
encodedProperties.forEach { (key, secret) ->
typeSpec = typeSpec.addFunction(
FunSpec.builder(key)
.addAnnotation(Utils.optInAnnotation(C.experimentalForeignApi, C.experimentalNativeApi))
.addModifiers(KModifier.ACTUAL)
.addParameter("key", String::class)
.addParameter(
ParameterSpec.builder(
name = "config",
LambdaTypeName.get(
receiver = Utils.sekretConfig.nestedClass("Builder"),
returnType = Unit::class.asTypeName()
)
).build()
)
.returns(String::class.asTypeName().copy(nullable = true))
.addStatement("return $key(key, %T.Builder().apply(config).build())", Utils.sekretConfig)
.build()
).addFunction(
FunSpec.builder(key)
.addModifiers(KModifier.ACTUAL)
.addParameter("key", String::class)
.addParameter(
ParameterSpec.builder(
name = "config",
Utils.sekretConfig
).build()
)
.returns(String::class.asClassName().copy(nullable = true))
.addStatement("val obfuscatedSecret = intArrayOf(%L)", secret)
.addStatement("return %M(obfuscatedSecret, key)", JNI.getNativeValue(JNI_PACKAGE_NAME))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ class NativeJNIGenerator(

encodedProperties.forEach { (key, secret) ->
spec.addFunction(
FunSpec.builder(key)
FunSpec.builder("${key}Decrypted")
.addAnnotation(Utils.optInAnnotation(C.experimentalForeignApi, C.experimentalNativeApi))
.addAnnotation(
AnnotationSpec.builder(C.cname)
.addMember("%S", "Java_${Utils.packageNameCSave(settings.packageName)}_Sekret_$key")
.addMember("%S", "Java_${Utils.packageNameCSave(settings.packageName)}_Sekret_${key}Decrypted")
.build()
)
.addParameter("env", C.pointer.parameterizedBy(JNI.jniEnvVar(JNI_PACKAGE_NAME)))
Expand All @@ -40,6 +40,24 @@ class NativeJNIGenerator(
JNI.getExtensionNativeValue(JNI_PACKAGE_NAME)
)
.build()
).addFunction(
FunSpec.builder("_${key}Encrypted")
.addAnnotation(Utils.optInAnnotation(C.experimentalForeignApi, C.experimentalNativeApi))
.addAnnotation(
AnnotationSpec.builder(C.cname)
.addMember("%S", "Java_${Utils.packageNameCSave(settings.packageName)}_Sekret__${key}Encrypted")
.build()
)
.addParameter("env", C.pointer.parameterizedBy(JNI.jniEnvVar(JNI_PACKAGE_NAME)))
.addParameter("clazz", JNI.libraryJObject(JNI_PACKAGE_NAME).copy(nullable = true))
.returns(JNI.libraryJIntArray(JNI_PACKAGE_NAME).copy(nullable = true))
.addStatement("val obfuscatedSecret = intArrayOf(%L)", secret)
.addStatement(
"val target = env.%M(obfuscatedSecret.size) ?: return null",
JNI.newIntArray(JNI_PACKAGE_NAME)
)
.addStatement("return env.%M(target, obfuscatedSecret)", JNI.fillTarget(JNI_PACKAGE_NAME))
.build()
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dev.datlag.sekret.gradle.generator.nonNative
import com.squareup.kotlinpoet.*
import dev.datlag.sekret.gradle.EncodedProperty
import dev.datlag.sekret.gradle.generator.SekretGenerator
import dev.datlag.sekret.gradle.helper.Utils
import dev.datlag.sekret.gradle.keys
import java.io.File

Expand All @@ -22,7 +23,31 @@ class CommonGenerator(
FunSpec.builder(key)
.addModifiers(KModifier.EXPECT)
.addParameter("key", String::class)
.returns(String::class.asClassName().copy(nullable = true))
.addParameter(
ParameterSpec.builder(
name = "config",
LambdaTypeName.get(
receiver = Utils.sekretConfig.nestedClass("Builder"),
returnType = Unit::class.asTypeName()
)
).build()
)
.returns(String::class.asTypeName().copy(nullable = true))
.build()
).addFunction(
FunSpec.builder(key)
.addModifiers(KModifier.EXPECT)
.addParameter("key", String::class)
.addParameter(
ParameterSpec.builder(
name = "config",
Utils.sekretConfig
).defaultValue(
"%T()",
Utils.sekretConfig
).build()
)
.returns(String::class.asTypeName().copy(nullable = true))
.build()
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dev.datlag.sekret.gradle.generator.nonNative

import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.MemberName.Companion.member
import dev.datlag.sekret.gradle.EncodedProperty
import dev.datlag.sekret.gradle.generator.SekretGenerator
import dev.datlag.sekret.gradle.helper.Utils
import dev.datlag.sekret.gradle.keys
import java.io.File

Expand All @@ -20,9 +22,64 @@ class JNIGenerator(
typeSpec = typeSpec.addFunction(
FunSpec.builder(key)
.addAnnotation(JvmStatic::class)
.addModifiers(KModifier.ACTUAL, KModifier.EXTERNAL)
.addModifiers(KModifier.ACTUAL)
.addParameter("key", String::class)
.returns(String::class.asClassName().copy(nullable = true))
.addParameter(
ParameterSpec.builder(
name = "config",
LambdaTypeName.get(
receiver = Utils.sekretConfig.nestedClass("Builder"),
returnType = Unit::class.asTypeName()
)
).build()
)
.returns(String::class.asTypeName().copy(nullable = true))
.addStatement("return $key(key, %T.Builder().apply(config).build())", Utils.sekretConfig)
.build()
).addFunction(
FunSpec.builder(key)
.addAnnotation(JvmStatic::class)
.addModifiers(KModifier.ACTUAL)
.addParameter("key", String::class)
.addParameter("config", Utils.sekretConfig)
.returns(String::class.asTypeName().copy(nullable = true))
.beginControlFlow("return if (config.jni.decryptDirectly)")
.addStatement("${key}Decrypted(key)")
.nextControlFlow("else")
.addStatement("${key}Encrypted()?.decrypt(key) ?:")
.beginControlFlow("if (config.jni.fallbackToDirectDecryption)")
.addStatement("${key}Decrypted(key)")
.nextControlFlow("else")
.addStatement("null")
.endControlFlow()
.endControlFlow()
.build()
).addFunction(
FunSpec.builder(key)
.addAnnotation(JvmStatic::class)
.addParameter("key", String::class)
.returns(String::class.asTypeName().copy(nullable = true))
.addStatement("return $key(key, %T())", Utils.sekretConfig)
.build()
).addFunction(
FunSpec.builder("${key}Decrypted")
.addAnnotation(JvmStatic::class)
.addModifiers(KModifier.PRIVATE, KModifier.EXTERNAL)
.addParameter("key", String::class)
.returns(String::class.asTypeName().copy(nullable = true))
.build()
).addFunction(
FunSpec.builder("_${key}Encrypted")
.addAnnotation(JvmStatic::class)
.addModifiers(KModifier.PRIVATE, KModifier.EXTERNAL)
.returns(IntArray::class.asTypeName().copy(nullable = true))
.build()
).addFunction(
FunSpec.builder("${key}Encrypted")
.addAnnotation(JvmStatic::class)
.addModifiers(KModifier.PRIVATE)
.returns(Utils.encryptedSecret.copy(nullable = true))
.addStatement("return _${key}Encrypted()?.let(%L)", Utils.encryptedSecret.member("invoke"))
.build()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dev.datlag.sekret.gradle.EncodedProperty
import dev.datlag.sekret.gradle.generator.SekretGenerator
import dev.datlag.sekret.gradle.generator.SekretGenerator.JNI_PACKAGE_NAME
import dev.datlag.sekret.gradle.helper.JNI
import dev.datlag.sekret.gradle.helper.Utils
import java.io.File

class JSGenerator(
Expand All @@ -22,6 +23,28 @@ class JSGenerator(
FunSpec.builder(key)
.addModifiers(KModifier.ACTUAL)
.addParameter("key", String::class)
.addParameter(
ParameterSpec.builder(
name = "config",
LambdaTypeName.get(
receiver = Utils.sekretConfig.nestedClass("Builder"),
returnType = Unit::class.asTypeName()
)
).build()
)
.returns(String::class.asTypeName().copy(nullable = true))
.addStatement("return $key(key, %T.Builder().apply(config).build())", Utils.sekretConfig)
.build()
).addFunction(
FunSpec.builder(key)
.addModifiers(KModifier.ACTUAL)
.addParameter("key", String::class)
.addParameter(
ParameterSpec.builder(
name = "config",
Utils.sekretConfig
).build()
)
.returns(String::class.asClassName().copy(nullable = true))
.addStatement("val obfuscatedSecret = intArrayOf(%L)", secret)
.addStatement("return %M(obfuscatedSecret, key)", JNI.getNativeValue(JNI_PACKAGE_NAME))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object JNI {

private const val LIBRARY_J_STRING = "jString"
private const val LIBRARY_J_OBJECT = "jObject"
private const val LIBRARY_J_IntArray = "jIntArray"

private const val LIBRARY_SEKRET_HELPER = "SekretHelper"

Expand All @@ -24,9 +25,12 @@ object JNI {

fun getNativeValue(packageName: String) = MemberName(sekretHelper(packageName), "getNativeValue")
fun getExtensionNativeValue(packageName: String) = MemberName(packageName, "getNativeValue")
fun newIntArray(packageName: String) = MemberName(packageName, "newIntArray")
fun fillTarget(packageName: String) = MemberName(packageName, "fill")

fun libraryJString(packageName: String) = ClassName(packageName, LIBRARY_J_STRING)
fun libraryJObject(packageName: String) = ClassName(packageName, LIBRARY_J_OBJECT)
fun libraryJIntArray(packageName: String) = ClassName(packageName, LIBRARY_J_IntArray)

sealed class Error(private val msg: String) {
object NewString : Error("Could not find NewString method in JNI")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import java.util.*

object Utils {

val encryptedSecret = ClassName("dev.datlag.sekret", "EncryptedSecret")
val sekretConfig = ClassName("dev.datlag.sekret", "SekretConfig")

fun packageNameToFolderStructure(packageName: String): String {
return packageName.replace(".", File.pathSeparator?.ifBlank { null } ?: "/")
}
Expand Down
2 changes: 2 additions & 0 deletions sekret-lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
signing
alias(libs.plugins.vanniktech.publish)
alias(libs.plugins.osdetector)
alias(libs.plugins.serialization)
}

val artifact = VersionCatalog.artifactName()
Expand Down Expand Up @@ -106,6 +107,7 @@ kotlin {
sourceSets {
commonMain.dependencies {
api(project(":sekret-annotations"))
implementation(libs.serialization)
}

val jniNativeMain by creating {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.datlag.sekret

class EncryptedSecret private constructor(
private val data: IntArray
) {
fun decrypt(key: String): String {
return SekretHelper.getNativeValue(data, key)
}

companion object {
operator fun invoke(data: IntArray?): EncryptedSecret? {
return if (data == null || data.isEmpty()) {
null
} else {
EncryptedSecret(data)
}
}
}
}
46 changes: 46 additions & 0 deletions sekret-lib/src/commonMain/kotlin/dev/datlag/sekret/SekretConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.datlag.sekret

import kotlinx.serialization.Serializable

/**
* Configure Sekret decryption behavior.
*
* @param jni specify JNI related configuration (Android and Desktop JVM)
*/
@Serializable
data class SekretConfig(
val jni: JNI = JNI()
) {
/**
* Configure decryption behavior on JNI.
*
* @param decryptDirectly if true decrypts directly in native code, might expose keys to JNI tracing.
* @param fallbackToDirectDecryption if true decrypts in native code if non-native decryption fails.
* See [decryptDirectly]
*/
@Serializable
data class JNI(
val decryptDirectly: Boolean = false,
val fallbackToDirectDecryption: Boolean = true,
) {
class Builder {
var decryptDirectly: Boolean = false
var fallbackToDirectDecryption: Boolean = true

fun build(): JNI = JNI(decryptDirectly, fallbackToDirectDecryption)
}
}

class Builder {
var jni: JNI = JNI()
private set

fun jni(block: JNI.Builder.() -> Unit) = apply {
jni = JNI.Builder().apply(block).build()
}

fun build(): SekretConfig = SekretConfig(jni)
}
}

fun SekretConfig(block: SekretConfig.Builder.() -> Unit) = SekretConfig.Builder().apply(block).build()

0 comments on commit 23670de

Please sign in to comment.