mirror of
https://codeberg.org/PGPainless/sop-java.git
synced 2024-11-22 07:12:04 +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
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSopVVersion(): String {
|
||||||
|
return executeForLines(commandList.plus("--sopv"))
|
||||||
|
}
|
||||||
|
|
||||||
override fun getSopSpecVersion(): String {
|
override fun getSopSpecVersion(): String {
|
||||||
return executeForLines(commandList.plus("--sop-spec"))
|
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 = ["--extended"]) var extended: Boolean = false
|
||||||
@Option(names = ["--backend"]) var backend: Boolean = false
|
@Option(names = ["--backend"]) var backend: Boolean = false
|
||||||
@Option(names = ["--sop-spec"]) var sopSpec: Boolean = false
|
@Option(names = ["--sop-spec"]) var sopSpec: Boolean = false
|
||||||
|
@Option(names = ["--sopv"]) var sopv: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
|
@ -47,5 +48,10 @@ class VersionCmd : AbstractSopCmd() {
|
||||||
println(version.getSopSpecVersion())
|
println(version.getSopSpecVersion())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exclusive!!.sopv) {
|
||||||
|
println(version.getSopVVersion())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
sop.name=sop
|
sop.name=sop
|
||||||
|
sopv.name=sopv
|
||||||
usage.header=Stateless OpenPGP Protocol
|
usage.header=Stateless OpenPGP Protocol
|
||||||
|
sopv.usage.header=Stateless OpenPGP Protocol - Signature Verification Interface Subset
|
||||||
locale=Locale for description texts
|
locale=Locale for description texts
|
||||||
|
|
||||||
# Generic
|
# Generic
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
sop.name=sop
|
sop.name=sop
|
||||||
|
sopv.name=sopv
|
||||||
usage.header=Stateless OpenPGP Protocol
|
usage.header=Stateless OpenPGP Protocol
|
||||||
|
sopv.usage.header=Stateless OpenPGP Protocol - Signature Verification Interface Subset
|
||||||
locale=Gebietsschema für Beschreibungstexte
|
locale=Gebietsschema für Beschreibungstexte
|
||||||
|
|
||||||
# Generic
|
# Generic
|
||||||
|
|
|
@ -9,16 +9,13 @@ import sop.operation.ChangeKeyPassword
|
||||||
import sop.operation.Dearmor
|
import sop.operation.Dearmor
|
||||||
import sop.operation.Decrypt
|
import sop.operation.Decrypt
|
||||||
import sop.operation.DetachedSign
|
import sop.operation.DetachedSign
|
||||||
import sop.operation.DetachedVerify
|
|
||||||
import sop.operation.Encrypt
|
import sop.operation.Encrypt
|
||||||
import sop.operation.ExtractCert
|
import sop.operation.ExtractCert
|
||||||
import sop.operation.GenerateKey
|
import sop.operation.GenerateKey
|
||||||
import sop.operation.InlineDetach
|
import sop.operation.InlineDetach
|
||||||
import sop.operation.InlineSign
|
import sop.operation.InlineSign
|
||||||
import sop.operation.InlineVerify
|
|
||||||
import sop.operation.ListProfiles
|
import sop.operation.ListProfiles
|
||||||
import sop.operation.RevokeKey
|
import sop.operation.RevokeKey
|
||||||
import sop.operation.Version
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stateless OpenPGP Interface. This class provides a stateless interface to various OpenPGP related
|
* 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
|
* intended for reuse. If you for example need to generate multiple keys, make a dedicated call to
|
||||||
* [generateKey] once per key generation.
|
* [generateKey] once per key generation.
|
||||||
*/
|
*/
|
||||||
interface SOP {
|
interface SOP : SOPV {
|
||||||
|
|
||||||
/** Get information about the implementations name and version. */
|
|
||||||
fun version(): Version
|
|
||||||
|
|
||||||
/** Generate a secret key. */
|
/** Generate a secret key. */
|
||||||
fun generateKey(): GenerateKey
|
fun generateKey(): GenerateKey
|
||||||
|
@ -53,24 +47,6 @@ interface SOP {
|
||||||
*/
|
*/
|
||||||
fun inlineSign(): InlineSign
|
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. */
|
/** Detach signatures from an inline signed message. */
|
||||||
fun inlineDetach(): InlineDetach
|
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
|
package sop.operation
|
||||||
|
|
||||||
|
import kotlin.jvm.Throws
|
||||||
|
import sop.exception.SOPGPException
|
||||||
|
|
||||||
interface Version {
|
interface Version {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,4 +100,11 @@ interface Version {
|
||||||
* @return remarks or null
|
* @return remarks or null
|
||||||
*/
|
*/
|
||||||
fun getSopSpecImplementationRemarks(): String?
|
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.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.opentest4j.TestAbortedException;
|
import org.opentest4j.TestAbortedException;
|
||||||
import sop.SOP;
|
import sop.SOP;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@ -72,4 +73,17 @@ public class VersionTest extends AbstractSOPTest {
|
||||||
int sopRevision = sop.version().getSopSpecRevisionNumber();
|
int sopRevision = sop.version().getSopSpecRevisionNumber();
|
||||||
assertTrue(sop.version().getSopSpecRevisionName().endsWith("" + sopRevision));
|
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