Skip to content

Commit df4055b

Browse files
committed
Apply new ed25519s module to stdlib module
1 parent 710efba commit df4055b

File tree

14 files changed

+243
-312
lines changed

14 files changed

+243
-312
lines changed

build.gradle.kts

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ apiValidation {
108108
"love.forte.simbot.annotations.InternalSimbotAPI",
109109
"love.forte.simbot.qguild.QGInternalApi",
110110
"love.forte.simbot.component.qguild.ExperimentalQGApi",
111-
"love.forte.simbot.qguild.ExperimentalQGMediaApi"
111+
"love.forte.simbot.qguild.ExperimentalQGMediaApi",
112+
"love.forte.simbot.qguild.ed25519.annotations.InternalEd25519Api"
112113
),
113114
)
114115

buildSrc/src/main/kotlin/JvmConfig.kt

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024. ForteScarlet.
2+
* Copyright (c) 2024-2025. ForteScarlet.
33
*
44
* This file is part of simbot-component-qq-guild.
55
*
@@ -21,13 +21,11 @@ import org.gradle.api.tasks.compile.JavaCompile
2121
import org.gradle.kotlin.dsl.assign
2222
import org.gradle.kotlin.dsl.get
2323
import org.gradle.kotlin.dsl.getByName
24-
import org.gradle.kotlin.dsl.withType
2524
import org.gradle.process.CommandLineArgumentProvider
2625
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
2726
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2827
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
2928
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
30-
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
3129
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
3230

3331

@@ -52,25 +50,23 @@ inline fun KotlinJvmTarget.configJava(crossinline block: KotlinJvmTarget.() -> U
5250
}
5351

5452

55-
fun KotlinTopLevelExtension.configJavaToolchain(jdkVersion: Int) {
56-
jvmToolchain(jdkVersion)
57-
}
58-
5953
inline fun KotlinMultiplatformExtension.configKotlinJvm(
6054
jdkVersion: Int = JVMConstants.KT_JVM_TARGET_VALUE,
6155
crossinline block: KotlinJvmTarget.() -> Unit = {}
6256
) {
63-
configJavaToolchain(jdkVersion)
57+
jvmToolchain(jdkVersion)
6458
jvm {
6559
configJava(block)
60+
compilerOptions {
61+
}
6662
}
6763
}
6864

6965
inline fun KotlinJvmProjectExtension.configKotlinJvm(
7066
jdkVersion: Int = JVMConstants.KT_JVM_TARGET_VALUE,
7167
crossinline block: KotlinJvmProjectExtension.() -> Unit = {}
7268
) {
73-
configJavaToolchain(jdkVersion)
69+
jvmToolchain(jdkVersion)
7470
compilerOptions {
7571
javaParameters = true
7672
jvmTarget.set(JvmTarget.fromTarget(jdkVersion.toString()))
@@ -80,16 +76,23 @@ inline fun KotlinJvmProjectExtension.configKotlinJvm(
8076
block()
8177
}
8278

79+
/**
80+
* 要放在 `kotlin {}` 下面
81+
*/
8382
inline fun Project.configJavaCompileWithModule(
8483
moduleName: String? = null,
8584
jvmVersion: String = JVMConstants.KT_JVM_TARGET,
8685
crossinline block: JavaCompile.() -> Unit = {}
8786
) {
88-
tasks.withType<JavaCompile> {
87+
tasks.named("compileJava", JavaCompile::class.java) {
8988
options.encoding = "UTF-8"
9089
sourceCompatibility = jvmVersion
9190
targetCompatibility = jvmVersion
9291

92+
println("$this sourceSets[\"main\"]: ${sourceSets["main"]}")
93+
println("$this sourceSets[\"main\"].output: ${sourceSets["main"].output}")
94+
println("$this sourceSets[\"main\"].output.asPath: ${sourceSets["main"].output.asPath}")
95+
9396
if (moduleName != null) {
9497
options.compilerArgumentProviders.add(CommandLineArgumentProvider {
9598
// Provide compiled Kotlin classes to javac – needed for Java/Kotlin mixed sources to work

simbot-component-qq-guild-api/build.gradle.kts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023-2024. ForteScarlet.
2+
* Copyright (c) 2023-2025. ForteScarlet.
33
*
44
* This file is part of simbot-component-qq-guild.
55
*
@@ -34,8 +34,6 @@ plugins {
3434

3535
setup(P.ComponentQQGuild)
3636

37-
configJavaCompileWithModule("simbot.component.qqguild.api")
38-
//apply(plugin = "qq-guild-dokka-partial-configure")
3937
apply(plugin = "qq-guild-multiplatform-maven-publish")
4038

4139
//configJsTestTasks()
@@ -133,3 +131,5 @@ tasks.withType<DokkaTaskPartial>().configureEach {
133131
suppressGeneratedFiles.set(false)
134132
}
135133
}
134+
135+
configJavaCompileWithModule("simbot.component.qqguild.api")

simbot-component-qq-guild-core/build.gradle.kts

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ plugins {
3030

3131
setup(P.ComponentQQGuild)
3232

33-
configJavaCompileWithModule("simbot.component.qqguild.core")
3433
apply(plugin = "qq-guild-multiplatform-maven-publish")
3534

3635
configJsTestTasks()
@@ -98,3 +97,5 @@ kotlin {
9897
}
9998
}
10099
}
100+
101+
configJavaCompileWithModule("simbot.component.qqguild.core")
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
# internal ed25519 module
22

33
一个内部使用的用于提供对 ed25519 签名的支持的模块。
4-
5-
JVM 中优先尝试通过 `Security.getProvider("BC")` 检测是否存在 bouncycastle,如果存在则使用它的 `Ed25519` 类库。
4+
通过 `ed25519KeyPairGenerator()` 获取一个当前平台支持的 `Ed25519KeyPairGenerator` 实例。
65

76
```Kotlin
8-
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
9-
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters
7+
val generator = ed25519KeyPairGenerator()
8+
val (privateKey, publicKey) = generator.generate(seed)
9+
```
10+
11+
非 JVM 平台中,均使用 [libsodium bindings](https://github.com/ionspin/kotlin-multiplatform-libsodium)
12+
作为实现。
13+
14+
JVM 中优先尝试检测是否存在以下类:
15+
16+
- `org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters`
17+
- `org.bouncycastle.crypto.params.Ed25519PublicKeyParameters`
18+
- `org.bouncycastle.crypto.signers.Ed25519Signer`
1019

11-
val seedBytes = seed.toByteArray()
12-
val privateKeyParams = Ed25519PrivateKeyParameters(seedBytes, 0)
13-
val publicKeyParams = privateKeyParams.generatePublicKey()
20+
如果存在,则使用这些类进行签名操作,也就是使用 [BouncyCastle](https://www.bouncycastle.org/)
21+
(编译时使用的是 `org.bouncycastle:bcprov-jdk18on:1.80`)
22+
进行密钥的生成或签名/验签。
23+
24+
当 BouncyCastle 的相关类存在时,使用 `ed25519KeyPairGenerator().generate(seed)` 相当于:
25+
26+
```Kotlin
27+
val (privateKey, publicKey) = BouncyCastleEd25519KeyPair(seed)
1428
```
29+
30+
如果 BouncyCastle 的相关类不存在,则使用 [ed25519-java](https://github.com/str4d/ed25519-java)
31+
作为实现库。此库默认作为 `implementation` 加载于 classpath 中。
32+
相比于 BouncyCastle (≈8,480 KB ≈8.48 MB),它轻量得多 (≈62 KB)。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public abstract interface annotation class love/forte/simbot/qguild/ed25519/annotations/InternalEd25519Api : java/lang/annotation/Annotation {
2+
}
3+

simbot-component-qq-guild-internal-ed25519/build.gradle.kts

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ plugins {
2828

2929
setup(P.ComponentQQGuild)
3030

31-
configJavaCompileWithModule("simbot.component.qqguild.internal.ed25519")
3231
apply(plugin = "qq-guild-multiplatform-maven-publish")
3332

3433
configJsTestTasks()
@@ -91,3 +90,6 @@ kotlin {
9190
}
9291
}
9392
}
93+
94+
configJavaCompileWithModule("simbot.component.qqguild.internal.ed25519s")
95+

simbot-component-qq-guild-internal-ed25519/src/commonMain/kotlin/love/forte/simbot/qguild/ed25519/Ed25519s.kt

+5
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ public expect fun ed25519KeyPairGenerator(): Ed25519KeyPairGenerator
6161
public interface Ed25519KeyPair {
6262
public val privateKey: Ed25519PrivateKey
6363
public val publicKey: Ed25519PublicKey
64+
65+
@InternalEd25519Api
66+
public companion object {
67+
public const val CRYPTO_SIGN_BYTES: Int = 64
68+
}
6469
}
6570

6671
@InternalEd25519Api
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module simbot.component.qqguild.internal.ed25519s {
2+
requires kotlin.stdlib;
3+
requires static org.bouncycastle.provider;
4+
requires simbot.logger;
5+
requires net.i2p.crypto.eddsa;
6+
7+
exports love.forte.simbot.qguild.ed25519 to
8+
simbot.component.qqguild.stdlib,
9+
simbot.component.qqguild.core;
10+
exports love.forte.simbot.qguild.ed25519.annotations;
11+
}

simbot-component-qq-guild-internal-ed25519/src/jvmMain/kotlin/love/forte/simbot/qguild/ed25519/Ed25519s.jvm.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public actual fun ed25519KeyPairGenerator(): Ed25519KeyPairGenerator {
4040
return if (bouncyCastleAccessible) {
4141
BouncyCastleEd25519KeyPairGenerator
4242
} else {
43-
TODO()
43+
EddsaEd25519KeyPairGenerator
4444
}
4545
}
4646

@@ -65,13 +65,11 @@ private val bouncyCastleAccessible: Boolean by lazy {
6565
}
6666
}.also {
6767
if (!it) {
68-
// TODO log debug
6968
val msg = "BouncyCastle is inaccessible because of the classes {} are not found in classpath. " +
7069
"`ed25519-java` will be used instead."
7170

7271
ed25519sLogger.info(msg, bouncyCastleRequiredClasses.asList())
7372
ed25519sLogger.debug(msg, bouncyCastleRequiredClasses.asList(), bouncyCastleInaccessible)
74-
7573
}
7674

7775
}

simbot-component-qq-guild-stdlib/build.gradle.kts

+2-12
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ plugins {
3030

3131
setup(P.ComponentQQGuild)
3232

33-
configJavaCompileWithModule("simbot.component.qqguild.stdlib")
3433
apply(plugin = "qq-guild-multiplatform-maven-publish")
3534

3635
configJsTestTasks()
@@ -71,17 +70,12 @@ kotlin {
7170
api(libs.ktor.client.contentNegotiation)
7271
api(libs.ktor.serialization.kotlinxJson)
7372
api(libs.ktor.client.ws)
74-
75-
// https://github.com/ionspin/kotlin-multiplatform-libsodium
76-
implementation("com.ionspin.kotlin:multiplatform-crypto-libsodium-bindings:0.9.2")
7773
}
7874

7975
commonTest.dependencies {
8076
implementation(kotlin("test"))
8177
implementation(libs.kotlinx.coroutines.test)
8278
implementation(libs.ktor.client.mock)
83-
// https://github.com/diglol/crypto
84-
// implementation("com.diglol.crypto:pkc:0.2.0")
8579
}
8680

8781
jvmTest.dependencies {
@@ -90,12 +84,6 @@ kotlin {
9084
implementation(libs.log4j.core)
9185
implementation(libs.log4j.slf4j2)
9286
implementation(libs.bouncycastle.bcprov.jdk18on)
93-
// implementation("dev.whyoleg.cryptography:cryptography-core:0.4.0")
94-
// implementation(kotlincrypto.core.digest)
95-
// implementation(kotlincrypto.core.mac)
96-
// implementation(kotlincrypto.core.xof)
97-
// implementation(kotlincrypto.macs.hmac.sha1)
98-
// implementation(kotlincrypto.macs.kmac)
9987
}
10088

10189
jsMain.dependencies {
@@ -107,3 +95,5 @@ kotlin {
10795
}
10896
}
10997
}
98+
99+
configJavaCompileWithModule("simbot.component.qqguild.stdlib")

simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/internal/BotImpl.kt

+14-33
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022-2024. ForteScarlet.
2+
* Copyright (c) 2022-2025. ForteScarlet.
33
*
44
* This file is part of simbot-component-qq-guild.
55
*
@@ -17,7 +17,6 @@
1717

1818
package love.forte.simbot.qguild.stdlib.internal
1919

20-
import com.ionspin.kotlin.crypto.signature.crypto_sign_BYTES
2120
import io.ktor.client.*
2221
import io.ktor.client.plugins.*
2322
import io.ktor.client.plugins.contentnegotiation.*
@@ -50,6 +49,10 @@ import love.forte.simbot.qguild.api.app.AppAccessToken
5049
import love.forte.simbot.qguild.api.app.GetAppAccessTokenApi
5150
import love.forte.simbot.qguild.api.requestData
5251
import love.forte.simbot.qguild.api.user.GetBotInfoApi
52+
import love.forte.simbot.qguild.ed25519.Ed25519KeyPair
53+
import love.forte.simbot.qguild.ed25519.Signature
54+
import love.forte.simbot.qguild.ed25519.annotations.InternalEd25519Api
55+
import love.forte.simbot.qguild.ed25519.paddingEd25519Seed
5356
import love.forte.simbot.qguild.event.*
5457
import love.forte.simbot.qguild.model.User
5558
import love.forte.simbot.qguild.stdlib.*
@@ -65,7 +68,7 @@ import kotlin.time.Duration.Companion.seconds
6568
* implementation for [Bot].
6669
* @author ForteScarlet
6770
*/
68-
@OptIn(ExperimentalSimbotCollectionApi::class)
71+
@OptIn(ExperimentalSimbotCollectionApi::class, InternalEd25519Api::class)
6972
internal class BotImpl(
7073
override val ticket: Bot.Ticket,
7174
override val configuration: BotConfiguration,
@@ -106,7 +109,7 @@ internal class BotImpl(
106109
}
107110
}
108111

109-
private val ed25519KeyPair: Ed25519Keypair by lazy {
112+
private val ed25519KeyPair: Ed25519KeyPair by lazy {
110113
genEd25519Keypair(ticket.secret.paddingEd25519Seed().toByteArray())
111114
}
112115

@@ -411,7 +414,7 @@ internal class BotImpl(
411414
}
412415
}
413416

414-
@OptIn(ExperimentalStdlibApi::class)
417+
@OptIn(ExperimentalStdlibApi::class, InternalEd25519Api::class)
415418
override suspend fun emitEvent(
416419
payload: String,
417420
options: EmitEventOptions?,
@@ -427,9 +430,9 @@ internal class BotImpl(
427430

428431
val signatureBytes = signature.hexToByteArray()
429432

430-
check(crypto_sign_BYTES == signatureBytes.size) {
433+
check(Ed25519KeyPair.CRYPTO_SIGN_BYTES == signatureBytes.size) {
431434
"Invalid signature hex size, " +
432-
"expect ${crypto_sign_BYTES}, " +
435+
"expect ${Ed25519KeyPair.CRYPTO_SIGN_BYTES}, " +
433436
"actual ${signatureBytes.size}"
434437
}
435438

@@ -443,7 +446,7 @@ internal class BotImpl(
443446

444447
val msg = "$signatureTimestamp$payload"
445448

446-
check(ed25519PublicKey.verify(signature = signatureBytes, message = msg.toByteArray())) {
449+
check(ed25519PublicKey.verify(signature = Signature(signatureBytes), data = msg.toByteArray())) {
447450
"Ed25519 verify failed"
448451
}
449452
}
@@ -454,9 +457,11 @@ internal class BotImpl(
454457

455458
val signature = ed25519PrivateKey.sign(msg.toByteArray())
456459

460+
signature.data
461+
457462
val verified = Signal.CallbackVerify.Verified(
458463
plainToken,
459-
signature.signatureBytes().toHexString()
464+
signature.data.toHexString()
460465
)
461466

462467
return EmitResult.Verified(verified)
@@ -625,27 +630,3 @@ internal suspend fun BotImpl.emitEvent(dispatch: Signal.Dispatch, raw: String) {
625630
}
626631
}
627632
}
628-
629-
630-
/**
631-
* Repeat to `length` == 32
632-
*/
633-
internal fun String.paddingEd25519Seed(): String {
634-
return when {
635-
length == 32 || isEmpty() -> this
636-
length > 32 -> substring(32)
637-
else -> {
638-
buildString(32) {
639-
append(this@paddingEd25519Seed)
640-
while (length < 32) {
641-
append(
642-
this@paddingEd25519Seed,
643-
0,
644-
kotlin.math.min(32 - length, this@paddingEd25519Seed.length)
645-
)
646-
}
647-
}
648-
649-
}
650-
}
651-
}

0 commit comments

Comments
 (0)