Commit
This commit is contained in:
parent
5a3e48d070
commit
286b9e3bdd
10 changed files with 255 additions and 152 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "2.2.21"
|
kotlin("multiplatform") version "2.2.21"
|
||||||
|
id("io.kotest") version "6.0.4"
|
||||||
|
id("com.google.devtools.ksp") version "2.3.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
@ -36,7 +38,15 @@ kotlin {
|
||||||
nativeMain.dependencies {
|
nativeMain.dependencies {
|
||||||
}
|
}
|
||||||
nativeTest.dependencies {
|
nativeTest.dependencies {
|
||||||
|
implementation("io.kotest:kotest-framework-engine:6.0.4")
|
||||||
implementation("io.kotest:kotest-assertions-core:6.0.4")
|
implementation("io.kotest:kotest-assertions-core:6.0.4")
|
||||||
|
implementation("io.kotest:kotest-property:6.0.4")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note the documentation said "temporarily" so idk https://kotest.io/docs/framework/project-setup.html#re-running-tests
|
||||||
|
tasks.withType<Test>().configureEach {
|
||||||
|
logger.lifecycle("UP-TO-DATE check for $name is disabled, forcing it to run.")
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package dn42.m724.auth
|
|
||||||
|
|
||||||
sealed interface AuthenticationMethod {
|
|
||||||
data class Pgp(val fingerprint: String) : AuthenticationMethod {
|
|
||||||
init {
|
|
||||||
// TODO validate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Ssh(val type: KeyType, val fingerprint: String) : AuthenticationMethod {
|
|
||||||
init {
|
|
||||||
// TODO validate
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class KeyType {
|
|
||||||
Rsa,
|
|
||||||
Ed25519
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +1,5 @@
|
||||||
package dn42.m724.auth
|
package dn42.m724.auth
|
||||||
|
|
||||||
import dn42.m724.auth.pgp.PgpSignatureChecker
|
|
||||||
|
|
||||||
// This is actually a library, below code is just showcase
|
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
print("Message to sign (single line): ")
|
TODO("usage example or something here")
|
||||||
val message = readln()
|
|
||||||
|
|
||||||
print("Key fingerprint:")
|
|
||||||
val fingerprint = readln()
|
|
||||||
|
|
||||||
println("Please input signature below:")
|
|
||||||
val lines = mutableListOf<String>()
|
|
||||||
while (lines.lastOrNull() != "-----END PGP SIGNATURE-----") {
|
|
||||||
print(" ")
|
|
||||||
lines.add(readln())
|
|
||||||
}
|
|
||||||
|
|
||||||
val signature = lines.joinToString("\n")
|
|
||||||
|
|
||||||
val checker = PgpSignatureChecker()
|
|
||||||
checker.verify(message, fingerprint, signature)
|
|
||||||
println("Signature is valid")
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package dn42.m724.auth
|
package dn42.m724.auth.checker
|
||||||
|
|
||||||
interface SignatureChecker {
|
interface SignatureChecker {
|
||||||
/**
|
/**
|
||||||
|
|
@ -1,16 +1,47 @@
|
||||||
package dn42.m724.auth.pgp
|
package dn42.m724.auth.checker.pgp
|
||||||
|
|
||||||
import dn42.m724.auth.SignatureChecker
|
import dn42.m724.auth.checker.SignatureChecker
|
||||||
import gpgme.*
|
import gpgme.GPGME_PROTOCOL_OPENPGP
|
||||||
import kotlinx.cinterop.*
|
import gpgme.GPGME_SIG_STAT_GOOD
|
||||||
|
import gpgme.GPG_ERR_NO_ERROR
|
||||||
|
import gpgme.SEEK_SET
|
||||||
|
import gpgme._gpgme_op_verify_result
|
||||||
|
import gpgme._gpgme_signature
|
||||||
|
import gpgme.gpg_err_code
|
||||||
|
import gpgme.gpgme_check_version
|
||||||
|
import gpgme.gpgme_ctx_t
|
||||||
|
import gpgme.gpgme_ctx_tVar
|
||||||
|
import gpgme.gpgme_data_new
|
||||||
|
import gpgme.gpgme_data_new_from_mem
|
||||||
|
import gpgme.gpgme_data_read
|
||||||
|
import gpgme.gpgme_data_release
|
||||||
|
import gpgme.gpgme_data_seek
|
||||||
|
import gpgme.gpgme_data_tVar
|
||||||
|
import gpgme.gpgme_error_from_errno
|
||||||
|
import gpgme.gpgme_new
|
||||||
|
import gpgme.gpgme_op_verify
|
||||||
|
import gpgme.gpgme_op_verify_result
|
||||||
|
import gpgme.gpgme_release
|
||||||
|
import gpgme.gpgme_set_armor
|
||||||
|
import gpgme.gpgme_set_protocol
|
||||||
|
import gpgme.gpgme_strerror
|
||||||
|
import kotlinx.cinterop.ByteVar
|
||||||
|
import kotlinx.cinterop.CPointer
|
||||||
|
import kotlinx.cinterop.ExperimentalForeignApi
|
||||||
|
import kotlinx.cinterop.MemScope
|
||||||
|
import kotlinx.cinterop.alloc
|
||||||
|
import kotlinx.cinterop.allocArray
|
||||||
|
import kotlinx.cinterop.memScoped
|
||||||
|
import kotlinx.cinterop.pointed
|
||||||
|
import kotlinx.cinterop.ptr
|
||||||
|
import kotlinx.cinterop.toKString
|
||||||
|
import kotlinx.cinterop.value
|
||||||
import platform.posix.errno
|
import platform.posix.errno
|
||||||
|
|
||||||
@OptIn(ExperimentalForeignApi::class)
|
@OptIn(ExperimentalForeignApi::class)
|
||||||
class PgpSignatureChecker : SignatureChecker {
|
class PgpSignatureChecker : SignatureChecker {
|
||||||
override fun verify(sourcePlainText: String, keyFingerprint: String, signature: String) {
|
override fun verify(sourcePlainText: String, keyFingerprint: String, signature: String) {
|
||||||
gpgme_check_version(null)?.let {
|
gpgme_check_version(null) ?: throw RuntimeException("Failed to get GPGME version")
|
||||||
println("GPGME version: ${it.toKString()}")
|
|
||||||
} ?: throw RuntimeException("Failed to get GPGME version")
|
|
||||||
|
|
||||||
withGpgContextMemScoped { ctx ->
|
withGpgContextMemScoped { ctx ->
|
||||||
gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP.toUInt())
|
gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP.toUInt())
|
||||||
|
|
@ -41,11 +72,10 @@ class PgpSignatureChecker : SignatureChecker {
|
||||||
|
|
||||||
result.pointed.signatures?.let { initialPointer ->
|
result.pointed.signatures?.let { initialPointer ->
|
||||||
var signaturePointer: CPointer<_gpgme_signature>? = initialPointer
|
var signaturePointer: CPointer<_gpgme_signature>? = initialPointer
|
||||||
println("pointer is null: ${signaturePointer == null}")
|
|
||||||
|
|
||||||
while (signaturePointer != null) {
|
while (signaturePointer != null) {
|
||||||
val signature = signaturePointer.pointed
|
val signature = signaturePointer.pointed
|
||||||
println("Found signature with timestamp ${signature.timestamp} and fingerprint ${signature.fpr?.toKString()}")
|
//println("Found signature with timestamp ${signature.timestamp} and fingerprint ${signature.fpr?.toKString()}")
|
||||||
|
|
||||||
assertGpg(signature.status) { "Failed to check signature" }
|
assertGpg(signature.status) { "Failed to check signature" }
|
||||||
|
|
||||||
|
|
@ -74,7 +104,12 @@ class PgpSignatureChecker : SignatureChecker {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertGpg(
|
assertGpg(
|
||||||
gpgme_data_new_from_mem(sigData.ptr, signedMessage, signedMessage.length.toULong(), 1) // TODO idk if the 1 is good
|
gpgme_data_new_from_mem(
|
||||||
|
sigData.ptr,
|
||||||
|
signedMessage,
|
||||||
|
signedMessage.length.toULong(),
|
||||||
|
1
|
||||||
|
) // TODO idk if the 1 is good
|
||||||
) { "Failed to read signature" }
|
) { "Failed to read signature" }
|
||||||
|
|
||||||
assertGpg(
|
assertGpg(
|
||||||
|
|
@ -101,9 +136,6 @@ class PgpSignatureChecker : SignatureChecker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println(plainText.encodeToByteArray().toHexString())
|
|
||||||
println(sourcePlainText.encodeToByteArray().toHexString())
|
|
||||||
|
|
||||||
check(plainText.trim() == sourcePlainText.trim()) { "Plain text message doesn't match" }
|
check(plainText.trim() == sourcePlainText.trim()) { "Plain text message doesn't match" }
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package dn42.m724.auth.checker.ssh
|
||||||
|
|
||||||
|
import dn42.m724.auth.checker.SignatureChecker
|
||||||
|
|
||||||
|
class SshSignatureChecker : SignatureChecker {
|
||||||
|
override fun verify(sourcePlainText: String, keyFingerprint: String, signature: String) {
|
||||||
|
TODO("SSH signature verification not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package dn42.m724.auth.model
|
||||||
|
|
||||||
|
sealed interface AuthenticationMethod {
|
||||||
|
data class Pgp(val fingerprint: String) : AuthenticationMethod {
|
||||||
|
init {
|
||||||
|
// TODO validate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Ssh(val algorithm: Algorithm, val fingerprint: String) : AuthenticationMethod {
|
||||||
|
init {
|
||||||
|
// TODO validate
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Algorithm {
|
||||||
|
Rsa,
|
||||||
|
Ed25519
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromString(value: String): AuthenticationMethod {
|
||||||
|
val (keyType, fingerprint) = value.split(" ")
|
||||||
|
|
||||||
|
val authenticationMethod = if (keyType == "pgp-fingerprint") {
|
||||||
|
val bytes = fingerprint.hexToByteArray()
|
||||||
|
check(bytes.size == 20) { "Fingerprint is expected to be a 20-byte HEX-encoded string" }
|
||||||
|
|
||||||
|
Pgp(fingerprint)
|
||||||
|
} else if (keyType.startsWith("ssh-")) {
|
||||||
|
TODO("SSH key parsing not yet implemented")
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("Invalid signature key type: $keyType")
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticationMethod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
package dn42.m724.auth.checker.pgp
|
||||||
|
|
||||||
|
import dn42.m724.auth.checker.SignatureChecker
|
||||||
|
import io.kotest.assertions.throwables.shouldNotThrowAny
|
||||||
|
import io.kotest.assertions.throwables.shouldThrow
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.datatest.withData
|
||||||
|
|
||||||
|
private const val fingerprint = "2DE81640E41DD84A9ED9B8F8FD4D7C6652E72E7B"
|
||||||
|
private const val invalidFingerprint = "139F1460BC66A19A2F880D8D47BA020D8EBCC05E"
|
||||||
|
|
||||||
|
private val clearTextSignature = """
|
||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA512
|
||||||
|
|
||||||
|
simple message
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iJEEARYKADkWIQQt6BZA5B3YSp7ZuPj9TXxmUucuewUCaP4wchsUgAAAAAAEAA5t
|
||||||
|
YW51MiwyLjUrMS4xMSwyLDIACgkQ/U18ZlLnLnuM3AD+L0QBrn2pnGAemcJZDh+p
|
||||||
|
6oJnuTgeSMiMRkkbMgTOMFMBAPzhLKgyzx4YJcgrIinvZsgPowR9Pf0ryzxwQ5mo
|
||||||
|
qUwJ
|
||||||
|
=FYVj
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val detachedSignature = """
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iJEEABYKADkWIQQt6BZA5B3YSp7ZuPj9TXxmUucuewUCaP4wWxsUgAAAAAAEAA5t
|
||||||
|
YW51MiwyLjUrMS4xMSwyLDIACgkQ/U18ZlLnLnsaEgEA15uncZNWN6zV952vO6rG
|
||||||
|
mMIAG9X54X9Cfp5DEfG3hPcBAJypNGnlyHivk+ZKYDlKSZB2S53vKrA3q3J2yDqb
|
||||||
|
0SUF
|
||||||
|
=12FC
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
class PgpSignatureVerificationTest: FunSpec({
|
||||||
|
val checker: SignatureChecker = PgpSignatureChecker() // stateless (for now?)
|
||||||
|
|
||||||
|
val successfulCases = listOf(
|
||||||
|
TestCase(
|
||||||
|
description = "valid clear-text signature",
|
||||||
|
plainText = "simple message",
|
||||||
|
keyFingerprint = fingerprint,
|
||||||
|
signature = clearTextSignature
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
description = "valid detached signature",
|
||||||
|
plainText = "simple message",
|
||||||
|
keyFingerprint = fingerprint,
|
||||||
|
signature = detachedSignature
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
withData(
|
||||||
|
nameFn = { "verify should succeed for ${it.description}" },
|
||||||
|
successfulCases
|
||||||
|
) { (_, sourcePlainText, keyFingerprint, signature) ->
|
||||||
|
shouldNotThrowAny {
|
||||||
|
checker.verify(sourcePlainText, keyFingerprint, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val failureCases = listOf(
|
||||||
|
TestCase(
|
||||||
|
description = "not matching message (clear-text)",
|
||||||
|
plainText = "totally different",
|
||||||
|
keyFingerprint = fingerprint,
|
||||||
|
signature = clearTextSignature
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
description = "not matching message (detached)",
|
||||||
|
plainText = "totally different",
|
||||||
|
keyFingerprint = fingerprint,
|
||||||
|
signature = detachedSignature
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
description = "not matching fingerprint",
|
||||||
|
plainText = "simple message",
|
||||||
|
keyFingerprint = invalidFingerprint,
|
||||||
|
signature = detachedSignature
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
description = "blank signature",
|
||||||
|
plainText = "simple message",
|
||||||
|
keyFingerprint = fingerprint,
|
||||||
|
signature = ""
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
description = "blank fingerprint",
|
||||||
|
plainText = "simple message",
|
||||||
|
keyFingerprint = "",
|
||||||
|
signature = detachedSignature
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
description = "key ID instead of full fingerprint",
|
||||||
|
plainText = "simple message",
|
||||||
|
keyFingerprint = fingerprint.takeLast(16), // Assuming this is an invalid case
|
||||||
|
signature = detachedSignature
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
withData(
|
||||||
|
nameFn = { "verify should throw for ${it.description}" },
|
||||||
|
failureCases
|
||||||
|
) { (_, sourcePlainText, keyFingerprint, signature) ->
|
||||||
|
shouldThrow<Exception> { // TODO we should check the exception type when we start using exception types
|
||||||
|
checker.verify(sourcePlainText, keyFingerprint, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
private data class TestCase(
|
||||||
|
val description: String,
|
||||||
|
val plainText: String,
|
||||||
|
val keyFingerprint: String,
|
||||||
|
val signature: String
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package dn42.m724.auth.model
|
||||||
|
|
||||||
|
import io.kotest.assertions.throwables.shouldThrow
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.datatest.withData
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class AuthenticationMethodTest: FunSpec({
|
||||||
|
withData(
|
||||||
|
nameFn = { authenticationMethodString -> "fromString should throw for invalid input: $authenticationMethodString" },
|
||||||
|
"pgp-fingerprint 2DE81640E41D",
|
||||||
|
"pgp 2DE81640E41DD84A9ED9B8F8FD4D7C6652E72E7B",
|
||||||
|
"pgp-fingerprint 2DE81640E41DD84A9ED9B8F8FD4xveTes7C6652E72E7B",
|
||||||
|
"pgp-fingerprint"
|
||||||
|
) { authenticationMethodString ->
|
||||||
|
shouldThrow<Exception> {
|
||||||
|
AuthenticationMethod.fromString(authenticationMethodString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withData(
|
||||||
|
nameFn = { (authenticationMethodString, expected) -> "fromString should return $expected for input: $authenticationMethodString" },
|
||||||
|
Pair("pgp-fingerprint 2DE81640E41DD84A9ED9B8F8FD4D7C6652E72E7B", AuthenticationMethod.Pgp("2DE81640E41DD84A9ED9B8F8FD4D7C6652E72E7B")),
|
||||||
|
Pair("pgp-fingerprint 2DE81640E41DD84A9ED9B8F8FD4D7C6652E72E7B this is ignored", AuthenticationMethod.Pgp("2DE81640E41DD84A9ED9B8F8FD4D7C6652E72E7B"))
|
||||||
|
) { (authenticationMethodString, expected) ->
|
||||||
|
assertEquals(
|
||||||
|
expected = expected,
|
||||||
|
actual = AuthenticationMethod.fromString(authenticationMethodString)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
package dn42.m724.auth.pgp
|
|
||||||
|
|
||||||
import dn42.m724.auth.SignatureChecker
|
|
||||||
import io.kotest.assertions.throwables.shouldNotThrowAny
|
|
||||||
import io.kotest.assertions.throwables.shouldThrow
|
|
||||||
import kotlin.experimental.ExperimentalNativeApi
|
|
||||||
import kotlin.test.BeforeTest
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
private const val fingerprint = "2DE81640E41DD84A9ED9B8F8FD4D7C6652E72E7B"
|
|
||||||
private const val invalidFingerprint = "139F1460BC66A19A2F880D8D47BA020D8EBCC05E"
|
|
||||||
|
|
||||||
private val clearTextSignature = """
|
|
||||||
-----BEGIN PGP SIGNED MESSAGE-----
|
|
||||||
Hash: SHA512
|
|
||||||
|
|
||||||
simple message
|
|
||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
|
|
||||||
iJEEARYKADkWIQQt6BZA5B3YSp7ZuPj9TXxmUucuewUCaP4wchsUgAAAAAAEAA5t
|
|
||||||
YW51MiwyLjUrMS4xMSwyLDIACgkQ/U18ZlLnLnuM3AD+L0QBrn2pnGAemcJZDh+p
|
|
||||||
6oJnuTgeSMiMRkkbMgTOMFMBAPzhLKgyzx4YJcgrIinvZsgPowR9Pf0ryzxwQ5mo
|
|
||||||
qUwJ
|
|
||||||
=FYVj
|
|
||||||
-----END PGP SIGNATURE-----
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
val detachedSignature = """
|
|
||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
|
|
||||||
iJEEABYKADkWIQQt6BZA5B3YSp7ZuPj9TXxmUucuewUCaP4wWxsUgAAAAAAEAA5t
|
|
||||||
YW51MiwyLjUrMS4xMSwyLDIACgkQ/U18ZlLnLnsaEgEA15uncZNWN6zV952vO6rG
|
|
||||||
mMIAG9X54X9Cfp5DEfG3hPcBAJypNGnlyHivk+ZKYDlKSZB2S53vKrA3q3J2yDqb
|
|
||||||
0SUF
|
|
||||||
=12FC
|
|
||||||
-----END PGP SIGNATURE-----
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
class PgpSignatureVerificationTest {
|
|
||||||
private lateinit var checker: SignatureChecker
|
|
||||||
|
|
||||||
@BeforeTest
|
|
||||||
fun beforeTest() {
|
|
||||||
checker = PgpSignatureChecker()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `verify should succeed for valid signature`() {
|
|
||||||
shouldNotThrowAny {
|
|
||||||
checker.verify ("simple message", fingerprint, clearTextSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `verify should succeed for valid detached signature`() {
|
|
||||||
shouldNotThrowAny {
|
|
||||||
checker.verify("simple message", fingerprint, detachedSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `verify should fail for not matching message`() {
|
|
||||||
shouldThrow<Exception> { // "Plain text message doesn't match"
|
|
||||||
checker.verify("totally different", fingerprint, clearTextSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `verify should fail for not matching detached message`() {
|
|
||||||
shouldThrow<Exception> { // "Failed to check signature: Bad signature"
|
|
||||||
checker.verify("totally different", fingerprint, detachedSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `verify should fail for not matching fingerprint`() {
|
|
||||||
shouldThrow<Exception> { // Did not find any signature with matching key fingerprint
|
|
||||||
checker.verify("simple message", invalidFingerprint, detachedSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `verify should fail for empty input data`() {
|
|
||||||
shouldThrow<Exception> {
|
|
||||||
checker.verify("simple message", invalidFingerprint, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `verify should fail for empty fingerprint data`() {
|
|
||||||
shouldThrow<Exception> {
|
|
||||||
checker.verify("simple message", "", detachedSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue