mirror of
https://codeberg.org/PGPainless/sop-java.git
synced 2024-12-06 14:02:07 +01:00
Move signature verification operations to sopv interface subset
This commit is contained in:
parent
cd208c8942
commit
ed9b2f5fef
10 changed files with 224 additions and 25 deletions
53
external-sop/src/main/kotlin/sop/external/ExternalSOPV.kt
vendored
Normal file
53
external-sop/src/main/kotlin/sop/external/ExternalSOPV.kt
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.external
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.util.*
|
||||
import sop.SOPV
|
||||
import sop.external.ExternalSOP.TempDirProvider
|
||||
import sop.external.operation.DetachedVerifyExternal
|
||||
import sop.external.operation.InlineVerifyExternal
|
||||
import sop.external.operation.VersionExternal
|
||||
import sop.operation.DetachedVerify
|
||||
import sop.operation.InlineVerify
|
||||
import sop.operation.Version
|
||||
|
||||
/**
|
||||
* Implementation of the [SOPV] API subset using an external sopv/sop binary.
|
||||
*
|
||||
* Instantiate an [ExternalSOPV] object for the given binary and the given [TempDirProvider] using
|
||||
* empty environment variables.
|
||||
*
|
||||
* @param binaryName name / path of the sopv binary
|
||||
* @param tempDirProvider custom tempDirProvider
|
||||
*/
|
||||
class ExternalSOPV(
|
||||
private val binaryName: String,
|
||||
private val properties: Properties = Properties(),
|
||||
private val tempDirProvider: TempDirProvider = defaultTempDirProvider()
|
||||
) : SOPV {
|
||||
|
||||
override fun version(): Version = VersionExternal(binaryName, properties)
|
||||
|
||||
override fun detachedVerify(): DetachedVerify = DetachedVerifyExternal(binaryName, properties)
|
||||
|
||||
override fun inlineVerify(): InlineVerify =
|
||||
InlineVerifyExternal(binaryName, properties, tempDirProvider)
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Default implementation of the [TempDirProvider] which stores temporary files in the
|
||||
* systems temp dir ([Files.createTempDirectory]).
|
||||
*
|
||||
* @return default implementation
|
||||
*/
|
||||
@JvmStatic
|
||||
fun defaultTempDirProvider(): TempDirProvider {
|
||||
return TempDirProvider { Files.createTempDirectory("ext-sopv").toFile() }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,6 +68,10 @@ class VersionExternal(binary: String, environment: Properties) : Version {
|
|||
return null
|
||||
}
|
||||
|
||||
override fun getSopVVersion(): String {
|
||||
return executeForLines(commandList.plus("--sopv"))
|
||||
}
|
||||
|
||||
override fun getSopSpecVersion(): String {
|
||||
return executeForLines(commandList.plus("--sop-spec"))
|
||||
}
|
||||
|
|
98
sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopVCLI.kt
Normal file
98
sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopVCLI.kt
Normal file
|
@ -0,0 +1,98 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.cli.picocli
|
||||
|
||||
import java.util.*
|
||||
import kotlin.system.exitProcess
|
||||
import picocli.AutoComplete
|
||||
import picocli.CommandLine
|
||||
import sop.SOPV
|
||||
import sop.cli.picocli.commands.*
|
||||
import sop.exception.SOPGPException
|
||||
|
||||
@CommandLine.Command(
|
||||
name = "sopv",
|
||||
resourceBundle = "msg_sop",
|
||||
exitCodeOnInvalidInput = SOPGPException.UnsupportedSubcommand.EXIT_CODE,
|
||||
subcommands =
|
||||
[
|
||||
// Meta subcommands
|
||||
VersionCmd::class,
|
||||
// signature verification subcommands
|
||||
VerifyCmd::class,
|
||||
InlineVerifyCmd::class,
|
||||
// misc
|
||||
CommandLine.HelpCommand::class,
|
||||
AutoComplete.GenerateCompletion::class])
|
||||
class SopVCLI {
|
||||
|
||||
companion object {
|
||||
@JvmStatic private var sopvInstance: SOPV? = null
|
||||
|
||||
@JvmStatic
|
||||
fun getSopV(): SOPV =
|
||||
checkNotNull(sopvInstance) { cliMsg.getString("sop.error.runtime.no_backend_set") }
|
||||
|
||||
@JvmStatic
|
||||
fun setSopVInstance(sopv: SOPV?) {
|
||||
sopvInstance = sopv
|
||||
}
|
||||
|
||||
@JvmField var cliMsg: ResourceBundle = ResourceBundle.getBundle("msg_sop")
|
||||
|
||||
@JvmField var EXECUTABLE_NAME = "sopv"
|
||||
|
||||
@JvmField
|
||||
@CommandLine.Option(names = ["--stacktrace"], scope = CommandLine.ScopeType.INHERIT)
|
||||
var stacktrace = false
|
||||
|
||||
@JvmStatic
|
||||
fun main(vararg args: String) {
|
||||
val exitCode = execute(*args)
|
||||
if (exitCode != 0) {
|
||||
exitProcess(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun execute(vararg args: String): Int {
|
||||
// Set locale
|
||||
CommandLine(InitLocale()).parseArgs(*args)
|
||||
|
||||
// Re-set bundle with updated locale
|
||||
cliMsg = ResourceBundle.getBundle("msg_sop")
|
||||
|
||||
return CommandLine(SopVCLI::class.java)
|
||||
.apply {
|
||||
// explicitly set help command resource bundle
|
||||
subcommands["help"]?.setResourceBundle(ResourceBundle.getBundle("msg_help"))
|
||||
// Hide generate-completion command
|
||||
subcommands["generate-completion"]?.commandSpec?.usageMessage()?.hidden(true)
|
||||
// overwrite executable name
|
||||
commandName = EXECUTABLE_NAME
|
||||
// setup exception handling
|
||||
executionExceptionHandler = SOPExecutionExceptionHandler()
|
||||
exitCodeExceptionMapper = SOPExceptionExitCodeMapper()
|
||||
isCaseInsensitiveEnumValuesAllowed = true
|
||||
}
|
||||
.execute(*args)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Control the locale.
|
||||
*
|
||||
* @see <a href="https://picocli.info/#_controlling_the_locale">Picocli Readme</a>
|
||||
*/
|
||||
@CommandLine.Command
|
||||
class InitLocale {
|
||||
@CommandLine.Option(names = ["-l", "--locale"], descriptionKey = "sop.locale")
|
||||
fun setLocale(locale: String) = Locale.setDefault(Locale(locale))
|
||||
|
||||
@CommandLine.Unmatched
|
||||
var remainder: MutableList<String> =
|
||||
mutableListOf() // ignore any other parameters and options in the first parsing phase
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ class VersionCmd : AbstractSopCmd() {
|
|||
@Option(names = ["--extended"]) var extended: Boolean = false
|
||||
@Option(names = ["--backend"]) var backend: Boolean = false
|
||||
@Option(names = ["--sop-spec"]) var sopSpec: Boolean = false
|
||||
@Option(names = ["--sopv"]) var sopv: Boolean = false
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
|
@ -47,5 +48,10 @@ class VersionCmd : AbstractSopCmd() {
|
|||
println(version.getSopSpecVersion())
|
||||
return
|
||||
}
|
||||
|
||||
if (exclusive!!.sopv) {
|
||||
println(version.getSopVVersion())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
sop.name=sop
|
||||
sopv.name=sopv
|
||||
usage.header=Stateless OpenPGP Protocol
|
||||
sopv.usage.header=Stateless OpenPGP Protocol - Signature Verification Interface Subset
|
||||
locale=Locale for description texts
|
||||
|
||||
# Generic
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
sop.name=sop
|
||||
sopv.name=sopv
|
||||
usage.header=Stateless OpenPGP Protocol
|
||||
sopv.usage.header=Stateless OpenPGP Protocol - Signature Verification Interface Subset
|
||||
locale=Gebietsschema für Beschreibungstexte
|
||||
|
||||
# Generic
|
||||
|
|
|
@ -9,16 +9,13 @@ import sop.operation.ChangeKeyPassword
|
|||
import sop.operation.Dearmor
|
||||
import sop.operation.Decrypt
|
||||
import sop.operation.DetachedSign
|
||||
import sop.operation.DetachedVerify
|
||||
import sop.operation.Encrypt
|
||||
import sop.operation.ExtractCert
|
||||
import sop.operation.GenerateKey
|
||||
import sop.operation.InlineDetach
|
||||
import sop.operation.InlineSign
|
||||
import sop.operation.InlineVerify
|
||||
import sop.operation.ListProfiles
|
||||
import sop.operation.RevokeKey
|
||||
import sop.operation.Version
|
||||
|
||||
/**
|
||||
* Stateless OpenPGP Interface. This class provides a stateless interface to various OpenPGP related
|
||||
|
@ -26,10 +23,7 @@ import sop.operation.Version
|
|||
* intended for reuse. If you for example need to generate multiple keys, make a dedicated call to
|
||||
* [generateKey] once per key generation.
|
||||
*/
|
||||
interface SOP {
|
||||
|
||||
/** Get information about the implementations name and version. */
|
||||
fun version(): Version
|
||||
interface SOP : SOPV {
|
||||
|
||||
/** Generate a secret key. */
|
||||
fun generateKey(): GenerateKey
|
||||
|
@ -53,24 +47,6 @@ interface SOP {
|
|||
*/
|
||||
fun inlineSign(): InlineSign
|
||||
|
||||
/**
|
||||
* Verify detached signatures. If you need to verify an inline-signed message, use
|
||||
* [inlineVerify] instead.
|
||||
*/
|
||||
fun verify(): DetachedVerify = detachedVerify()
|
||||
|
||||
/**
|
||||
* Verify detached signatures. If you need to verify an inline-signed message, use
|
||||
* [inlineVerify] instead.
|
||||
*/
|
||||
fun detachedVerify(): DetachedVerify
|
||||
|
||||
/**
|
||||
* Verify signatures of an inline-signed message. If you need to verify detached signatures over
|
||||
* a message, use [detachedVerify] instead.
|
||||
*/
|
||||
fun inlineVerify(): InlineVerify
|
||||
|
||||
/** Detach signatures from an inline signed message. */
|
||||
fun inlineDetach(): InlineDetach
|
||||
|
||||
|
|
34
sop-java/src/main/kotlin/sop/SOPV.kt
Normal file
34
sop-java/src/main/kotlin/sop/SOPV.kt
Normal file
|
@ -0,0 +1,34 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop
|
||||
|
||||
import sop.operation.DetachedVerify
|
||||
import sop.operation.InlineVerify
|
||||
import sop.operation.Version
|
||||
|
||||
/** Subset of [SOP] implementing only OpenPGP signature verification. */
|
||||
interface SOPV {
|
||||
|
||||
/** Get information about the implementations name and version. */
|
||||
fun version(): Version
|
||||
|
||||
/**
|
||||
* Verify detached signatures. If you need to verify an inline-signed message, use
|
||||
* [inlineVerify] instead.
|
||||
*/
|
||||
fun verify(): DetachedVerify = detachedVerify()
|
||||
|
||||
/**
|
||||
* Verify detached signatures. If you need to verify an inline-signed message, use
|
||||
* [inlineVerify] instead.
|
||||
*/
|
||||
fun detachedVerify(): DetachedVerify
|
||||
|
||||
/**
|
||||
* Verify signatures of an inline-signed message. If you need to verify detached signatures over
|
||||
* a message, use [detachedVerify] instead.
|
||||
*/
|
||||
fun inlineVerify(): InlineVerify
|
||||
}
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
package sop.operation
|
||||
|
||||
import kotlin.jvm.Throws
|
||||
import sop.exception.SOPGPException
|
||||
|
||||
interface Version {
|
||||
|
||||
/**
|
||||
|
@ -97,4 +100,11 @@ interface Version {
|
|||
* @return remarks or null
|
||||
*/
|
||||
fun getSopSpecImplementationRemarks(): String?
|
||||
|
||||
/**
|
||||
* Return the single-line SEMVER version of the sopv interface subset it provides complete
|
||||
* coverage of. If the implementation does not provide complete coverage for any sopv interface,
|
||||
* this method throws an [SOPGPException.UnsupportedOption] instead.
|
||||
*/
|
||||
@Throws(SOPGPException.UnsupportedOption::class) fun getSopVVersion(): String
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.junit.jupiter.params.provider.Arguments;
|
|||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.opentest4j.TestAbortedException;
|
||||
import sop.SOP;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -72,4 +73,17 @@ public class VersionTest extends AbstractSOPTest {
|
|||
int sopRevision = sop.version().getSopSpecRevisionNumber();
|
||||
assertTrue(sop.version().getSopSpecRevisionName().endsWith("" + sopRevision));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideInstances")
|
||||
public void sopVVersionTest(SOP sop) {
|
||||
try {
|
||||
sop.version().getSopVVersion();
|
||||
} catch (SOPGPException.UnsupportedOption e) {
|
||||
throw new TestAbortedException(
|
||||
"Implementation does (gracefully) not provide coverage for any sopv interface version.");
|
||||
} catch (RuntimeException e) {
|
||||
throw new TestAbortedException("Implementation does not provide coverage for any sopv interface version.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue