Commit
This commit is contained in:
		
					parent
					
						
							
								5a3e48d070
							
						
					
				
			
			
				commit
				
					
						286b9e3bdd
					
				
			
		
					 10 changed files with 255 additions and 152 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
plugins {
 | 
			
		||||
    kotlin("multiplatform") version "2.2.21"
 | 
			
		||||
    id("io.kotest") version "6.0.4"
 | 
			
		||||
    id("com.google.devtools.ksp") version "2.3.0"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +38,15 @@ kotlin {
 | 
			
		|||
        nativeMain.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-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
 | 
			
		||||
 | 
			
		||||
import dn42.m724.auth.pgp.PgpSignatureChecker
 | 
			
		||||
 | 
			
		||||
// This is actually a library, below code is just showcase
 | 
			
		||||
 | 
			
		||||
fun main() {
 | 
			
		||||
    print("Message to sign (single line): ")
 | 
			
		||||
    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")
 | 
			
		||||
    TODO("usage example or something here")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
package dn42.m724.auth
 | 
			
		||||
package dn42.m724.auth.checker
 | 
			
		||||
 | 
			
		||||
interface SignatureChecker {
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,47 @@
 | 
			
		|||
package dn42.m724.auth.pgp
 | 
			
		||||
package dn42.m724.auth.checker.pgp
 | 
			
		||||
 | 
			
		||||
import dn42.m724.auth.SignatureChecker
 | 
			
		||||
import gpgme.*
 | 
			
		||||
import kotlinx.cinterop.*
 | 
			
		||||
import dn42.m724.auth.checker.SignatureChecker
 | 
			
		||||
import gpgme.GPGME_PROTOCOL_OPENPGP
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@OptIn(ExperimentalForeignApi::class)
 | 
			
		||||
class PgpSignatureChecker : SignatureChecker {
 | 
			
		||||
    override fun verify(sourcePlainText: String, keyFingerprint: String, signature: String) {
 | 
			
		||||
        gpgme_check_version(null)?.let {
 | 
			
		||||
            println("GPGME version: ${it.toKString()}")
 | 
			
		||||
        } ?: throw RuntimeException("Failed to get GPGME version")
 | 
			
		||||
        gpgme_check_version(null) ?: throw RuntimeException("Failed to get GPGME version")
 | 
			
		||||
 | 
			
		||||
        withGpgContextMemScoped { ctx ->
 | 
			
		||||
            gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP.toUInt())
 | 
			
		||||
| 
						 | 
				
			
			@ -41,11 +72,10 @@ class PgpSignatureChecker : SignatureChecker {
 | 
			
		|||
 | 
			
		||||
        result.pointed.signatures?.let { initialPointer ->
 | 
			
		||||
            var signaturePointer: CPointer<_gpgme_signature>? = initialPointer
 | 
			
		||||
            println("pointer is null: ${signaturePointer == null}")
 | 
			
		||||
 | 
			
		||||
            while (signaturePointer != null) {
 | 
			
		||||
                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" }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +104,12 @@ class PgpSignatureChecker : SignatureChecker {
 | 
			
		|||
 | 
			
		||||
        try {
 | 
			
		||||
            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" }
 | 
			
		||||
 | 
			
		||||
            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" }
 | 
			
		||||
            }
 | 
			
		||||
        } 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