diff --git a/build.gradle b/build.gradle index 277f1a8b..af0936e1 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,12 @@ allprojects { apply plugin: 'jacoco' apply plugin: 'checkstyle' + // Only generate jar for submodules + // https://stackoverflow.com/a/25445035 + jar { + onlyIf { !sourceSets.main.allSource.files.isEmpty() } + } + // For non-sop modules, enable android api compatibility check if (it.name.equals('pgpainless-core') || it.name.equals('sop-java') || it.name.equals('pgpainless-sop')) { // animalsniffer @@ -68,6 +74,7 @@ allprojects { logbackVersion = '1.2.9' junitVersion = '5.8.2' picocliVersion = '4.6.2' + sopJavaVersion = '1.1.0' rootConfigDir = new File(rootDir, 'config') gitCommit = getGitCommit() isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI')) diff --git a/pgpainless-cli/build.gradle b/pgpainless-cli/build.gradle index eeacf5fa..9db15a5b 100644 --- a/pgpainless-cli/build.gradle +++ b/pgpainless-cli/build.gradle @@ -40,7 +40,7 @@ dependencies { implementation "ch.qos.logback:logback-classic:$logbackVersion" implementation(project(":pgpainless-sop")) - implementation(project(":sop-java-picocli")) + implementation "org.pgpainless:sop-java-picocli:$sopJavaVersion" implementation "info.picocli:picocli:$picocliVersion" diff --git a/pgpainless-sop/build.gradle b/pgpainless-sop/build.gradle index 5ce6a7c6..87e893b2 100644 --- a/pgpainless-sop/build.gradle +++ b/pgpainless-sop/build.gradle @@ -20,7 +20,7 @@ dependencies { testImplementation "ch.qos.logback:logback-classic:$logbackVersion" implementation(project(":pgpainless-core")) - implementation(project(":sop-java")) + implementation "org.pgpainless:sop-java:$sopJavaVersion" } test { diff --git a/settings.gradle b/settings.gradle index 7ebc7ad4..aea19392 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,8 +5,6 @@ rootProject.name = 'PGPainless' include 'pgpainless-core', - 'sop-java', 'pgpainless-sop', - 'sop-java-picocli', 'pgpainless-cli' diff --git a/sop-java-picocli/README.md b/sop-java-picocli/README.md index f76c9295..3c9234af 100644 --- a/sop-java-picocli/README.md +++ b/sop-java-picocli/README.md @@ -1,34 +1 @@ - -# SOP-Java-Picocli - -Implementation of the [Stateless OpenPGP Command Line Interface](https://tools.ietf.org/html/draft-dkg-openpgp-stateless-cli-01) specification. -This terminal application allows generation of OpenPGP keys, extraction of public key certificates, -armoring and de-armoring of data, as well as - of course - encryption/decryption of messages and creation/verification of signatures. - -## Install a SOP backend - -This module comes without a SOP backend, so in order to function you need to extend it with an implementation of the interfaces defined in `sop-java`. -An implementation using PGPainless can be found in the module `pgpainless-sop`, but it is of course possible to provide your -own implementation. - -Just install your SOP backend by calling -```java -// static method call prior to execution of the main method -SopCLI.setSopInstance(yourSopImpl); -``` - -## Usage - -To get an overview of available commands of the application, execute -```shell -java -jar sop-java-picocli-XXX.jar help -``` - -If you just want to get started encrypting messages, see the module `pgpainless-cli` which initializes -`sop-java-picocli` with `pgpainless-sop`, so you can get started right away without the need to manually wire stuff up. - -Enjoy! \ No newline at end of file +# [MOVED](https://github.com/pgpainless/sop-java/tree/master/sop-java-picocli) \ No newline at end of file diff --git a/sop-java-picocli/build.gradle b/sop-java-picocli/build.gradle deleted file mode 100644 index 81183a09..00000000 --- a/sop-java-picocli/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -plugins { - id 'application' -} - -dependencies { - testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" - - // https://todd.ginsberg.com/post/testing-system-exit/ - testImplementation 'com.ginsberg:junit5-system-exit:1.1.1' - testImplementation 'org.mockito:mockito-core:4.2.0' - - implementation(project(":sop-java")) - implementation "info.picocli:picocli:$picocliVersion" - - // https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305 - implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2' -} - -mainClassName = 'sop.cli.picocli.SopCLI' - -jar { - manifest { - attributes 'Main-Class': "$mainClassName" - } - - from { - configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } { - exclude "META-INF/*.SF" - exclude "META-INF/*.DSA" - exclude "META-INF/*.RSA" - } -} - diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/DateParser.java b/sop-java-picocli/src/main/java/sop/cli/picocli/DateParser.java deleted file mode 100644 index d2e2188e..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/DateParser.java +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import java.util.Date; - -import sop.util.UTCUtil; - -public class DateParser { - - public static final Date BEGINNING_OF_TIME = new Date(0); - public static final Date END_OF_TIME = new Date(8640000000000000L); - - public static Date parseNotAfter(String notAfter) { - Date date = notAfter.equals("now") ? new Date() : notAfter.equals("-") ? END_OF_TIME : UTCUtil.parseUTCDate(notAfter); - if (date == null) { - Print.errln("Invalid date string supplied as value of --not-after."); - System.exit(1); - } - return date; - } - - public static Date parseNotBefore(String notBefore) { - Date date = notBefore.equals("now") ? new Date() : notBefore.equals("-") ? BEGINNING_OF_TIME : UTCUtil.parseUTCDate(notBefore); - if (date == null) { - Print.errln("Invalid date string supplied as value of --not-before."); - System.exit(1); - } - return date; - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/FileUtil.java b/sop-java-picocli/src/main/java/sop/cli/picocli/FileUtil.java deleted file mode 100644 index cd92e6db..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/FileUtil.java +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; - -import sop.exception.SOPGPException; - -public class FileUtil { - - private static final String ERROR_AMBIGUOUS = "File name '%s' is ambiguous. File with the same name exists on the filesystem."; - private static final String ERROR_ENV_FOUND = "Environment variable '%s' not set."; - private static final String ERROR_OUTPUT_EXISTS = "Output file '%s' already exists."; - private static final String ERROR_INPUT_NOT_EXIST = "File '%s' does not exist."; - private static final String ERROR_CANNOT_CREATE_FILE = "Output file '%s' cannot be created: %s"; - - public static final String PRFX_ENV = "@ENV:"; - public static final String PRFX_FD = "@FD:"; - - private static EnvironmentVariableResolver envResolver = System::getenv; - - public static void setEnvironmentVariableResolver(EnvironmentVariableResolver envResolver) { - if (envResolver == null) { - throw new NullPointerException("Variable envResolver cannot be null."); - } - FileUtil.envResolver = envResolver; - } - - public interface EnvironmentVariableResolver { - /** - * Resolve the value of the given environment variable. - * Return null if the variable is not present. - * - * @param name name of the variable - * @return variable value or null - */ - String resolveEnvironmentVariable(String name); - } - - public static File getFile(String fileName) { - if (fileName == null) { - throw new NullPointerException("File name cannot be null."); - } - - if (fileName.startsWith(PRFX_ENV)) { - - if (new File(fileName).exists()) { - throw new SOPGPException.AmbiguousInput(String.format(ERROR_AMBIGUOUS, fileName)); - } - - String envName = fileName.substring(PRFX_ENV.length()); - String envValue = envResolver.resolveEnvironmentVariable(envName); - if (envValue == null) { - throw new IllegalArgumentException(String.format(ERROR_ENV_FOUND, envName)); - } - return new File(envValue); - } else if (fileName.startsWith(PRFX_FD)) { - - if (new File(fileName).exists()) { - throw new SOPGPException.AmbiguousInput(String.format(ERROR_AMBIGUOUS, fileName)); - } - - throw new IllegalArgumentException("File descriptors not supported."); - } - - return new File(fileName); - } - - public static FileInputStream getFileInputStream(String fileName) { - File file = getFile(fileName); - try { - FileInputStream inputStream = new FileInputStream(file); - return inputStream; - } catch (FileNotFoundException e) { - throw new SOPGPException.MissingInput(String.format(ERROR_INPUT_NOT_EXIST, fileName), e); - } - } - - public static File createNewFileOrThrow(File file) throws IOException { - if (file == null) { - throw new NullPointerException("File cannot be null."); - } - - try { - if (!file.createNewFile()) { - throw new SOPGPException.OutputExists(String.format(ERROR_OUTPUT_EXISTS, file.getAbsolutePath())); - } - } catch (IOException e) { - throw new IOException(String.format(ERROR_CANNOT_CREATE_FILE, file.getAbsolutePath(), e.getMessage())); - } - return file; - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/Print.java b/sop-java-picocli/src/main/java/sop/cli/picocli/Print.java deleted file mode 100644 index d6474e1d..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/Print.java +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -public class Print { - - public static void errln(String string) { - // CHECKSTYLE:OFF - System.err.println(string); - // CHECKSTYLE:ON - } - - public static void trace(Throwable e) { - // CHECKSTYLE:OFF - e.printStackTrace(); - // CHECKSTYLE:ON - } - - public static void outln(String string) { - // CHECKSTYLE:OFF - System.out.println(string); - // CHECKSTYLE:ON - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/SOPExceptionExitCodeMapper.java b/sop-java-picocli/src/main/java/sop/cli/picocli/SOPExceptionExitCodeMapper.java deleted file mode 100644 index 8b38af32..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/SOPExceptionExitCodeMapper.java +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import picocli.CommandLine; -import sop.exception.SOPGPException; - -public class SOPExceptionExitCodeMapper implements CommandLine.IExitCodeExceptionMapper { - - @Override - public int getExitCode(Throwable exception) { - if (exception instanceof SOPGPException) { - return ((SOPGPException) exception).getExitCode(); - } - if (exception instanceof CommandLine.UnmatchedArgumentException) { - CommandLine.UnmatchedArgumentException ex = (CommandLine.UnmatchedArgumentException) exception; - // Unmatched option of subcommand (eg. `generate-key -k`) - if (ex.isUnknownOption()) { - return SOPGPException.UnsupportedOption.EXIT_CODE; - } - // Unmatched subcommand - return SOPGPException.UnsupportedSubcommand.EXIT_CODE; - } - // Invalid option (eg. `--label Invalid`) - if (exception instanceof CommandLine.ParameterException) { - return SOPGPException.UnsupportedOption.EXIT_CODE; - } - - // Others, like IOException etc. - return 1; - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/SOPExecutionExceptionHandler.java b/sop-java-picocli/src/main/java/sop/cli/picocli/SOPExecutionExceptionHandler.java deleted file mode 100644 index bbd8b976..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/SOPExecutionExceptionHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import picocli.CommandLine; - -public class SOPExecutionExceptionHandler implements CommandLine.IExecutionExceptionHandler { - - @Override - public int handleExecutionException(Exception ex, CommandLine commandLine, CommandLine.ParseResult parseResult) { - int exitCode = commandLine.getExitCodeExceptionMapper() != null ? - commandLine.getExitCodeExceptionMapper().getExitCode(ex) : - commandLine.getCommandSpec().exitCodeOnExecutionException(); - CommandLine.Help.ColorScheme colorScheme = commandLine.getColorScheme(); - // CHECKSTYLE:OFF - if (ex.getMessage() != null) { - commandLine.getErr().println(colorScheme.errorText(ex.getMessage())); - } - ex.printStackTrace(commandLine.getErr()); - // CHECKSTYLE:ON - - return exitCode; - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/SopCLI.java b/sop-java-picocli/src/main/java/sop/cli/picocli/SopCLI.java deleted file mode 100644 index bc0ae3dd..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/SopCLI.java +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import picocli.CommandLine; -import sop.SOP; -import sop.cli.picocli.commands.ArmorCmd; -import sop.cli.picocli.commands.DearmorCmd; -import sop.cli.picocli.commands.DecryptCmd; -import sop.cli.picocli.commands.DetachInbandSignatureAndMessageCmd; -import sop.cli.picocli.commands.EncryptCmd; -import sop.cli.picocli.commands.ExtractCertCmd; -import sop.cli.picocli.commands.GenerateKeyCmd; -import sop.cli.picocli.commands.SignCmd; -import sop.cli.picocli.commands.VerifyCmd; -import sop.cli.picocli.commands.VersionCmd; - -@CommandLine.Command( - exitCodeOnInvalidInput = 69, - subcommands = { - CommandLine.HelpCommand.class, - ArmorCmd.class, - DearmorCmd.class, - DecryptCmd.class, - DetachInbandSignatureAndMessageCmd.class, - EncryptCmd.class, - ExtractCertCmd.class, - GenerateKeyCmd.class, - SignCmd.class, - VerifyCmd.class, - VersionCmd.class - } -) -public class SopCLI { - // Singleton - static SOP SOP_INSTANCE; - - public static String EXECUTABLE_NAME = "sop"; - - public static void main(String[] args) { - int exitCode = execute(args); - if (exitCode != 0) { - System.exit(exitCode); - } - } - - public static int execute(String[] args) { - return new CommandLine(SopCLI.class) - .setCommandName(EXECUTABLE_NAME) - .setExecutionExceptionHandler(new SOPExecutionExceptionHandler()) - .setExitCodeExceptionMapper(new SOPExceptionExitCodeMapper()) - .setCaseInsensitiveEnumValuesAllowed(true) - .execute(args); - } - - public static SOP getSop() { - if (SOP_INSTANCE == null) { - throw new IllegalStateException("No SOP backend set."); - } - return SOP_INSTANCE; - } - - public static void setSopInstance(SOP instance) { - SOP_INSTANCE = instance; - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ArmorCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ArmorCmd.java deleted file mode 100644 index a015a688..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ArmorCmd.java +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.IOException; - -import picocli.CommandLine; -import sop.Ready; -import sop.cli.picocli.Print; -import sop.cli.picocli.SopCLI; -import sop.enums.ArmorLabel; -import sop.exception.SOPGPException; -import sop.operation.Armor; - -@CommandLine.Command(name = "armor", - description = "Add ASCII Armor to standard input", - exitCodeOnInvalidInput = SOPGPException.UnsupportedOption.EXIT_CODE) -public class ArmorCmd implements Runnable { - - @CommandLine.Option(names = {"--label"}, description = "Label to be used in the header and tail of the armoring.", paramLabel = "{auto|sig|key|cert|message}") - ArmorLabel label; - - @Override - public void run() { - Armor armor = SopCLI.getSop().armor(); - if (armor == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'armor' not implemented."); - } - - if (label != null) { - try { - armor.label(label); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - Print.errln("Armor labels not supported."); - System.exit(unsupportedOption.getExitCode()); - } - } - - try { - Ready ready = armor.data(System.in); - ready.writeTo(System.out); - } catch (SOPGPException.BadData badData) { - Print.errln("Bad data."); - Print.trace(badData); - System.exit(badData.getExitCode()); - } catch (IOException e) { - Print.errln("IO Error."); - Print.trace(e); - System.exit(1); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DearmorCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DearmorCmd.java deleted file mode 100644 index 343b1135..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DearmorCmd.java +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.IOException; - -import picocli.CommandLine; -import sop.cli.picocli.Print; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Dearmor; - -@CommandLine.Command(name = "dearmor", - description = "Remove ASCII Armor from standard input", - exitCodeOnInvalidInput = SOPGPException.UnsupportedOption.EXIT_CODE) -public class DearmorCmd implements Runnable { - - @Override - public void run() { - Dearmor dearmor = SopCLI.getSop().dearmor(); - if (dearmor == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'dearmor' not implemented."); - } - - try { - SopCLI.getSop() - .dearmor() - .data(System.in) - .writeTo(System.out); - } catch (SOPGPException.BadData e) { - Print.errln("Bad data."); - Print.trace(e); - System.exit(e.getExitCode()); - } catch (IOException e) { - Print.errln("IO Error."); - Print.trace(e); - System.exit(1); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DecryptCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DecryptCmd.java deleted file mode 100644 index 8fc4650b..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DecryptCmd.java +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.regex.Pattern; - -import picocli.CommandLine; -import sop.DecryptionResult; -import sop.ReadyWithResult; -import sop.SessionKey; -import sop.Verification; -import sop.cli.picocli.DateParser; -import sop.cli.picocli.FileUtil; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Decrypt; -import sop.util.HexUtil; - -@CommandLine.Command(name = "decrypt", - description = "Decrypt a message from standard input", - exitCodeOnInvalidInput = SOPGPException.UnsupportedOption.EXIT_CODE) -public class DecryptCmd implements Runnable { - - private static final String SESSION_KEY_OUT = "--session-key-out"; - private static final String VERIFY_OUT = "--verify-out"; - - private static final String ERROR_UNSUPPORTED_OPTION = "Option '%s' is not supported."; - private static final String ERROR_FILE_NOT_EXIST = "File '%s' does not exist."; - private static final String ERROR_OUTPUT_OF_OPTION_EXISTS = "Target %s of option %s already exists."; - - @CommandLine.Option( - names = {SESSION_KEY_OUT}, - description = "Can be used to learn the session key on successful decryption", - paramLabel = "SESSIONKEY") - File sessionKeyOut; - - @CommandLine.Option( - names = {"--with-session-key"}, - description = "Enables decryption of the \"CIPHERTEXT\" using the session key directly against the \"SEIPD\" packet", - paramLabel = "SESSIONKEY") - List withSessionKey = new ArrayList<>(); - - @CommandLine.Option( - names = {"--with-password"}, - description = "Enables decryption based on any \"SKESK\" packets in the \"CIPHERTEXT\"", - paramLabel = "PASSWORD") - List withPassword = new ArrayList<>(); - - @CommandLine.Option(names = {VERIFY_OUT}, - description = "Produces signature verification status to the designated file", - paramLabel = "VERIFICATIONS") - File verifyOut; - - @CommandLine.Option(names = {"--verify-with"}, - description = "Certificates whose signatures would be acceptable for signatures over this message", - paramLabel = "CERT") - List certs = new ArrayList<>(); - - @CommandLine.Option(names = {"--not-before"}, - description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" + - "Reject signatures with a creation date not in range.\n" + - "Defaults to beginning of time (\"-\").", - paramLabel = "DATE") - String notBefore = "-"; - - @CommandLine.Option(names = {"--not-after"}, - description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" + - "Reject signatures with a creation date not in range.\n" + - "Defaults to current system time (\"now\").\n" + - "Accepts special value \"-\" for end of time.", - paramLabel = "DATE") - String notAfter = "now"; - - @CommandLine.Parameters(index = "0..*", - description = "Secret keys to attempt decryption with", - paramLabel = "KEY") - List keys = new ArrayList<>(); - - @Override - public void run() { - throwIfOutputExists(verifyOut, VERIFY_OUT); - throwIfOutputExists(sessionKeyOut, SESSION_KEY_OUT); - - Decrypt decrypt = SopCLI.getSop().decrypt(); - if (decrypt == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'decrypt' not implemented."); - } - - setNotAfter(notAfter, decrypt); - setNotBefore(notBefore, decrypt); - setWithPasswords(withPassword, decrypt); - setWithSessionKeys(withSessionKey, decrypt); - setVerifyWith(certs, decrypt); - setDecryptWith(keys, decrypt); - - if (verifyOut != null && certs.isEmpty()) { - String errorMessage = "Option %s is requested, but no option %s was provided."; - throw new SOPGPException.IncompleteVerification(String.format(errorMessage, VERIFY_OUT, "--verify-with")); - } - - try { - ReadyWithResult ready = decrypt.ciphertext(System.in); - DecryptionResult result = ready.writeTo(System.out); - writeSessionKeyOut(result); - writeVerifyOut(result); - } catch (SOPGPException.BadData badData) { - throw new SOPGPException.BadData("No valid OpenPGP message found on Standard Input.", badData); - } catch (IOException ioException) { - throw new RuntimeException(ioException); - } - } - - private void throwIfOutputExists(File outputFile, String optionName) { - if (outputFile == null) { - return; - } - - if (outputFile.exists()) { - throw new SOPGPException.OutputExists(String.format(ERROR_OUTPUT_OF_OPTION_EXISTS, outputFile.getAbsolutePath(), optionName)); - } - } - - private void writeVerifyOut(DecryptionResult result) throws IOException { - if (verifyOut != null) { - FileUtil.createNewFileOrThrow(verifyOut); - try (FileOutputStream outputStream = new FileOutputStream(verifyOut)) { - PrintWriter writer = new PrintWriter(outputStream); - for (Verification verification : result.getVerifications()) { - // CHECKSTYLE:OFF - writer.println(verification.toString()); - // CHECKSTYLE:ON - } - writer.flush(); - } - } - } - - private void writeSessionKeyOut(DecryptionResult result) throws IOException { - if (sessionKeyOut != null) { - FileUtil.createNewFileOrThrow(sessionKeyOut); - - try (FileOutputStream outputStream = new FileOutputStream(sessionKeyOut)) { - if (!result.getSessionKey().isPresent()) { - throw new SOPGPException.UnsupportedOption("Session key not extracted. Possibly the feature --session-key-out is not supported."); - } else { - SessionKey sessionKey = result.getSessionKey().get(); - outputStream.write(sessionKey.getAlgorithm()); - outputStream.write(sessionKey.getKey()); - } - } - } - } - - private void setDecryptWith(List keys, Decrypt decrypt) { - for (File key : keys) { - try (FileInputStream keyIn = new FileInputStream(key)) { - decrypt.withKey(keyIn); - } catch (SOPGPException.KeyIsProtected keyIsProtected) { - throw new SOPGPException.KeyIsProtected("Key in file " + key.getAbsolutePath() + " is password protected.", keyIsProtected); - } catch (SOPGPException.BadData badData) { - throw new SOPGPException.BadData("File " + key.getAbsolutePath() + " does not contain a private key.", badData); - } catch (FileNotFoundException e) { - throw new SOPGPException.MissingInput(String.format(ERROR_FILE_NOT_EXIST, key.getAbsolutePath()), e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - private void setVerifyWith(List certs, Decrypt decrypt) { - for (File cert : certs) { - try (FileInputStream certIn = new FileInputStream(cert)) { - decrypt.verifyWithCert(certIn); - } catch (FileNotFoundException e) { - throw new SOPGPException.MissingInput(String.format(ERROR_FILE_NOT_EXIST, cert.getAbsolutePath()), e); - } catch (SOPGPException.BadData badData) { - throw new SOPGPException.BadData("File " + cert.getAbsolutePath() + " does not contain a valid certificate.", badData); - } catch (IOException ioException) { - throw new RuntimeException(ioException); - } - } - } - - private void setWithSessionKeys(List withSessionKey, Decrypt decrypt) { - Pattern sessionKeyPattern = Pattern.compile("^\\d+:[0-9A-F]+$"); - for (String sessionKey : withSessionKey) { - if (!sessionKeyPattern.matcher(sessionKey).matches()) { - throw new IllegalArgumentException("Session keys are expected in the format 'ALGONUM:HEXKEY'."); - } - String[] split = sessionKey.split(":"); - byte algorithm = (byte) Integer.parseInt(split[0]); - byte[] key = HexUtil.hexToBytes(split[1]); - - try { - decrypt.withSessionKey(new SessionKey(algorithm, key)); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - throw new SOPGPException.UnsupportedOption(String.format(ERROR_UNSUPPORTED_OPTION, "--with-session-key"), unsupportedOption); - } - } - } - - private void setWithPasswords(List withPassword, Decrypt decrypt) { - for (String password : withPassword) { - try { - decrypt.withPassword(password); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - throw new SOPGPException.UnsupportedOption(String.format(ERROR_UNSUPPORTED_OPTION, "--with-password"), unsupportedOption); - } - } - } - - private void setNotAfter(String notAfter, Decrypt decrypt) { - Date notAfterDate = DateParser.parseNotAfter(notAfter); - try { - decrypt.verifyNotAfter(notAfterDate); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - throw new SOPGPException.UnsupportedOption(String.format(ERROR_UNSUPPORTED_OPTION, "--not-after"), unsupportedOption); - } - } - - private void setNotBefore(String notBefore, Decrypt decrypt) { - Date notBeforeDate = DateParser.parseNotBefore(notBefore); - try { - decrypt.verifyNotBefore(notBeforeDate); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - throw new SOPGPException.UnsupportedOption(String.format(ERROR_UNSUPPORTED_OPTION, "--not-before"), unsupportedOption); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DetachInbandSignatureAndMessageCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DetachInbandSignatureAndMessageCmd.java deleted file mode 100644 index f5c71a2a..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/DetachInbandSignatureAndMessageCmd.java +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - -import picocli.CommandLine; -import sop.Signatures; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.DetachInbandSignatureAndMessage; - -@CommandLine.Command(name = "detach-inband-signature-and-message", - description = "Split a clearsigned message", - exitCodeOnInvalidInput = SOPGPException.UnsupportedOption.EXIT_CODE) -public class DetachInbandSignatureAndMessageCmd implements Runnable { - - @CommandLine.Option( - names = {"--signatures-out"}, - description = "Destination to which a detached signatures block will be written", - paramLabel = "SIGNATURES") - File signaturesOut; - - @CommandLine.Option(names = "--no-armor", - description = "ASCII armor the output", - negatable = true) - boolean armor = true; - - @Override - public void run() { - DetachInbandSignatureAndMessage detach = SopCLI.getSop().detachInbandSignatureAndMessage(); - if (detach == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'detach-inband-signature-and-message' not implemented."); - } - - if (signaturesOut == null) { - throw new SOPGPException.MissingArg("--signatures-out is required."); - } - - if (!armor) { - detach.noArmor(); - } - - try { - Signatures signatures = detach - .message(System.in).writeTo(System.out); - if (!signaturesOut.createNewFile()) { - throw new SOPGPException.OutputExists("Destination of --signatures-out already exists."); - } - signatures.writeTo(new FileOutputStream(signaturesOut)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/EncryptCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/EncryptCmd.java deleted file mode 100644 index 0634240b..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/EncryptCmd.java +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import picocli.CommandLine; -import sop.Ready; -import sop.cli.picocli.SopCLI; -import sop.enums.EncryptAs; -import sop.exception.SOPGPException; -import sop.operation.Encrypt; - -@CommandLine.Command(name = "encrypt", - description = "Encrypt a message from standard input", - exitCodeOnInvalidInput = 37) -public class EncryptCmd implements Runnable { - - @CommandLine.Option(names = "--no-armor", - description = "ASCII armor the output", - negatable = true) - boolean armor = true; - - @CommandLine.Option(names = {"--as"}, - description = "Type of the input data. Defaults to 'binary'", - paramLabel = "{binary|text|mime}") - EncryptAs type; - - @CommandLine.Option(names = "--with-password", - description = "Encrypt the message with a password", - paramLabel = "PASSWORD") - List withPassword = new ArrayList<>(); - - @CommandLine.Option(names = "--sign-with", - description = "Sign the output with a private key", - paramLabel = "KEY") - List signWith = new ArrayList<>(); - - @CommandLine.Parameters(description = "Certificates the message gets encrypted to", - index = "0..*", - paramLabel = "CERTS") - List certs = new ArrayList<>(); - - @Override - public void run() { - Encrypt encrypt = SopCLI.getSop().encrypt(); - if (encrypt == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'encrypt' not implemented."); - } - - if (type != null) { - try { - encrypt.mode(type); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - throw new SOPGPException.UnsupportedOption("Unsupported option '--as'.", unsupportedOption); - } - } - - if (withPassword.isEmpty() && certs.isEmpty()) { - throw new SOPGPException.MissingArg("At least one password or cert file required for encryption."); - } - - for (String password : withPassword) { - try { - encrypt.withPassword(password); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - throw new SOPGPException.UnsupportedOption("Unsupported option '--with-password'.", unsupportedOption); - } - } - - for (File keyFile : signWith) { - try (FileInputStream keyIn = new FileInputStream(keyFile)) { - encrypt.signWith(keyIn); - } catch (FileNotFoundException e) { - throw new SOPGPException.MissingInput("Key file " + keyFile.getAbsolutePath() + " not found.", e); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (SOPGPException.KeyIsProtected keyIsProtected) { - throw new SOPGPException.KeyIsProtected("Key from " + keyFile.getAbsolutePath() + " is password protected.", keyIsProtected); - } catch (SOPGPException.UnsupportedAsymmetricAlgo unsupportedAsymmetricAlgo) { - throw new SOPGPException.UnsupportedAsymmetricAlgo("Key from " + keyFile.getAbsolutePath() + " has unsupported asymmetric algorithm.", unsupportedAsymmetricAlgo); - } catch (SOPGPException.KeyCannotSign keyCannotSign) { - throw new SOPGPException.KeyCannotSign("Key from " + keyFile.getAbsolutePath() + " cannot sign.", keyCannotSign); - } catch (SOPGPException.BadData badData) { - throw new SOPGPException.BadData("Key file " + keyFile.getAbsolutePath() + " does not contain a valid OpenPGP private key.", badData); - } - } - - for (File certFile : certs) { - try (FileInputStream certIn = new FileInputStream(certFile)) { - encrypt.withCert(certIn); - } catch (FileNotFoundException e) { - throw new SOPGPException.MissingInput("Certificate file " + certFile.getAbsolutePath() + " not found.", e); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (SOPGPException.UnsupportedAsymmetricAlgo unsupportedAsymmetricAlgo) { - throw new SOPGPException.UnsupportedAsymmetricAlgo("Certificate from " + certFile.getAbsolutePath() + " has unsupported asymmetric algorithm.", unsupportedAsymmetricAlgo); - } catch (SOPGPException.CertCannotEncrypt certCannotEncrypt) { - throw new SOPGPException.CertCannotEncrypt("Certificate from " + certFile.getAbsolutePath() + " is not capable of encryption.", certCannotEncrypt); - } catch (SOPGPException.BadData badData) { - throw new SOPGPException.BadData("Certificate file " + certFile.getAbsolutePath() + " does not contain a valid OpenPGP certificate.", badData); - } - } - - if (!armor) { - encrypt.noArmor(); - } - - try { - Ready ready = encrypt.plaintext(System.in); - ready.writeTo(System.out); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ExtractCertCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ExtractCertCmd.java deleted file mode 100644 index f4559339..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ExtractCertCmd.java +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.IOException; - -import picocli.CommandLine; -import sop.Ready; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.ExtractCert; - -@CommandLine.Command(name = "extract-cert", - description = "Extract a public key certificate from a secret key from standard input", - exitCodeOnInvalidInput = 37) -public class ExtractCertCmd implements Runnable { - - @CommandLine.Option(names = "--no-armor", - description = "ASCII armor the output", - negatable = true) - boolean armor = true; - - @Override - public void run() { - ExtractCert extractCert = SopCLI.getSop().extractCert(); - if (extractCert == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'extract-cert' not implemented."); - } - - if (!armor) { - extractCert.noArmor(); - } - - try { - Ready ready = extractCert.key(System.in); - ready.writeTo(System.out); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (SOPGPException.BadData badData) { - throw new SOPGPException.BadData("Standard Input does not contain valid OpenPGP private key material.", badData); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/GenerateKeyCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/GenerateKeyCmd.java deleted file mode 100644 index 28bde279..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/GenerateKeyCmd.java +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import picocli.CommandLine; -import sop.Ready; -import sop.cli.picocli.Print; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.GenerateKey; - -@CommandLine.Command(name = "generate-key", - description = "Generate a secret key", - exitCodeOnInvalidInput = 37) -public class GenerateKeyCmd implements Runnable { - - @CommandLine.Option(names = "--no-armor", - description = "ASCII armor the output", - negatable = true) - boolean armor = true; - - @CommandLine.Parameters(description = "User-ID, eg. \"Alice \"") - List userId = new ArrayList<>(); - - @Override - public void run() { - GenerateKey generateKey = SopCLI.getSop().generateKey(); - if (generateKey == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'generate-key' not implemented."); - } - - for (String userId : userId) { - generateKey.userId(userId); - } - - if (!armor) { - generateKey.noArmor(); - } - - try { - Ready ready = generateKey.generate(); - ready.writeTo(System.out); - } catch (SOPGPException.MissingArg missingArg) { - Print.errln("Missing argument."); - Print.trace(missingArg); - System.exit(missingArg.getExitCode()); - } catch (SOPGPException.UnsupportedAsymmetricAlgo unsupportedAsymmetricAlgo) { - Print.errln("Unsupported asymmetric algorithm."); - Print.trace(unsupportedAsymmetricAlgo); - System.exit(unsupportedAsymmetricAlgo.getExitCode()); - } catch (IOException e) { - Print.errln("IO Error."); - Print.trace(e); - System.exit(1); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/SignCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/SignCmd.java deleted file mode 100644 index 7574923e..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/SignCmd.java +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import picocli.CommandLine; -import sop.MicAlg; -import sop.ReadyWithResult; -import sop.SigningResult; -import sop.cli.picocli.Print; -import sop.cli.picocli.SopCLI; -import sop.enums.SignAs; -import sop.exception.SOPGPException; -import sop.operation.Sign; - -@CommandLine.Command(name = "sign", - description = "Create a detached signature on the data from standard input", - exitCodeOnInvalidInput = 37) -public class SignCmd implements Runnable { - - @CommandLine.Option(names = "--no-armor", - description = "ASCII armor the output", - negatable = true) - boolean armor = true; - - @CommandLine.Option(names = "--as", description = "Defaults to 'binary'. If '--as=text' and the input data is not valid UTF-8, sign fails with return code 53.", - paramLabel = "{binary|text}") - SignAs type; - - @CommandLine.Parameters(description = "Secret keys used for signing", - paramLabel = "KEYS") - List secretKeyFile = new ArrayList<>(); - - @CommandLine.Option(names = "--micalg-out", description = "Emits the digest algorithm used to the specified file in a way that can be used to populate the micalg parameter for the PGP/MIME Content-Type (RFC3156)", - paramLabel = "MICALG") - File micAlgOut; - - @Override - public void run() { - Sign sign = SopCLI.getSop().sign(); - if (sign == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'sign' not implemented."); - } - - if (type != null) { - try { - sign.mode(type); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - Print.errln("Unsupported option '--as'"); - Print.trace(unsupportedOption); - System.exit(unsupportedOption.getExitCode()); - } - } - - if (micAlgOut != null && micAlgOut.exists()) { - throw new SOPGPException.OutputExists(String.format("Target %s of option %s already exists.", micAlgOut.getAbsolutePath(), "--micalg-out")); - } - - if (secretKeyFile.isEmpty()) { - Print.errln("Missing required parameter 'KEYS'."); - System.exit(19); - } - - for (File keyFile : secretKeyFile) { - try (FileInputStream keyIn = new FileInputStream(keyFile)) { - sign.key(keyIn); - } catch (FileNotFoundException e) { - Print.errln("File " + keyFile.getAbsolutePath() + " does not exist."); - Print.trace(e); - System.exit(1); - } catch (IOException e) { - Print.errln("Cannot access file " + keyFile.getAbsolutePath()); - Print.trace(e); - System.exit(1); - } catch (SOPGPException.KeyIsProtected e) { - Print.errln("Key " + keyFile.getName() + " is password protected."); - Print.trace(e); - System.exit(1); - } catch (SOPGPException.BadData badData) { - Print.errln("Bad data in key file " + keyFile.getAbsolutePath() + ":"); - Print.trace(badData); - System.exit(badData.getExitCode()); - } - } - - if (!armor) { - sign.noArmor(); - } - - try { - ReadyWithResult ready = sign.data(System.in); - SigningResult result = ready.writeTo(System.out); - - MicAlg micAlg = result.getMicAlg(); - if (micAlgOut != null) { - // Write micalg out - micAlgOut.createNewFile(); - FileOutputStream micAlgOutStream = new FileOutputStream(micAlgOut); - micAlg.writeTo(micAlgOutStream); - micAlgOutStream.close(); - } - } catch (IOException e) { - Print.errln("IO Error."); - Print.trace(e); - System.exit(1); - } catch (SOPGPException.ExpectedText expectedText) { - Print.errln("Expected text input, but got binary data."); - Print.trace(expectedText); - System.exit(expectedText.getExitCode()); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/VerifyCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/VerifyCmd.java deleted file mode 100644 index 2702b4b9..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/VerifyCmd.java +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import picocli.CommandLine; -import sop.Verification; -import sop.cli.picocli.DateParser; -import sop.cli.picocli.Print; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Verify; - -@CommandLine.Command(name = "verify", - description = "Verify a detached signature over the data from standard input", - exitCodeOnInvalidInput = 37) -public class VerifyCmd implements Runnable { - - @CommandLine.Parameters(index = "0", - description = "Detached signature", - paramLabel = "SIGNATURE") - File signature; - - @CommandLine.Parameters(index = "1..*", - arity = "1..*", - description = "Public key certificates", - paramLabel = "CERT") - List certificates = new ArrayList<>(); - - @CommandLine.Option(names = {"--not-before"}, - description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" + - "Reject signatures with a creation date not in range.\n" + - "Defaults to beginning of time (\"-\").", - paramLabel = "DATE") - String notBefore = "-"; - - @CommandLine.Option(names = {"--not-after"}, - description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" + - "Reject signatures with a creation date not in range.\n" + - "Defaults to current system time (\"now\").\n" + - "Accepts special value \"-\" for end of time.", - paramLabel = "DATE") - String notAfter = "now"; - - @Override - public void run() { - Verify verify = SopCLI.getSop().verify(); - if (verify == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'verify' not implemented."); - } - - if (notAfter != null) { - try { - verify.notAfter(DateParser.parseNotAfter(notAfter)); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - Print.errln("Unsupported option '--not-after'."); - Print.trace(unsupportedOption); - System.exit(unsupportedOption.getExitCode()); - } - } - if (notBefore != null) { - try { - verify.notBefore(DateParser.parseNotBefore(notBefore)); - } catch (SOPGPException.UnsupportedOption unsupportedOption) { - Print.errln("Unsupported option '--not-before'."); - Print.trace(unsupportedOption); - System.exit(unsupportedOption.getExitCode()); - } - } - - for (File certFile : certificates) { - try (FileInputStream certIn = new FileInputStream(certFile)) { - verify.cert(certIn); - } catch (FileNotFoundException fileNotFoundException) { - Print.errln("Certificate file " + certFile.getAbsolutePath() + " not found."); - - Print.trace(fileNotFoundException); - System.exit(1); - } catch (IOException ioException) { - Print.errln("IO Error."); - Print.trace(ioException); - System.exit(1); - } catch (SOPGPException.BadData badData) { - Print.errln("Certificate file " + certFile.getAbsolutePath() + " appears to not contain a valid OpenPGP certificate."); - Print.trace(badData); - System.exit(badData.getExitCode()); - } - } - - if (signature != null) { - try (FileInputStream sigIn = new FileInputStream(signature)) { - verify.signatures(sigIn); - } catch (FileNotFoundException e) { - Print.errln("Signature file " + signature.getAbsolutePath() + " does not exist."); - Print.trace(e); - System.exit(1); - } catch (IOException e) { - Print.errln("IO Error."); - Print.trace(e); - System.exit(1); - } catch (SOPGPException.BadData badData) { - Print.errln("File " + signature.getAbsolutePath() + " does not contain a valid OpenPGP signature."); - Print.trace(badData); - System.exit(badData.getExitCode()); - } - } - - List verifications = null; - try { - verifications = verify.data(System.in); - } catch (SOPGPException.NoSignature e) { - Print.errln("No verifiable signature found."); - Print.trace(e); - System.exit(e.getExitCode()); - } catch (IOException ioException) { - Print.errln("IO Error."); - Print.trace(ioException); - System.exit(1); - } catch (SOPGPException.BadData badData) { - Print.errln("Standard Input appears not to contain a valid OpenPGP message."); - Print.trace(badData); - System.exit(badData.getExitCode()); - } - for (Verification verification : verifications) { - Print.outln(verification.toString()); - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/VersionCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/VersionCmd.java deleted file mode 100644 index 4a319192..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/VersionCmd.java +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import picocli.CommandLine; -import sop.cli.picocli.Print; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Version; - -@CommandLine.Command(name = "version", description = "Display version information about the tool", - exitCodeOnInvalidInput = 37) -public class VersionCmd implements Runnable { - - @CommandLine.ArgGroup() - Exclusive exclusive; - - static class Exclusive { - @CommandLine.Option(names = "--extended", description = "Print an extended version string.") - boolean extended; - - @CommandLine.Option(names = "--backend", description = "Print information about the cryptographic backend.") - boolean backend; - } - - - - @Override - public void run() { - Version version = SopCLI.getSop().version(); - if (version == null) { - throw new SOPGPException.UnsupportedSubcommand("Command 'version' not implemented."); - } - - if (exclusive == null) { - Print.outln(version.getName() + " " + version.getVersion()); - return; - } - - if (exclusive.extended) { - Print.outln(version.getExtendedVersion()); - return; - } - - if (exclusive.backend) { - Print.outln(version.getBackendVersion()); - return; - } - } -} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/package-info.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/package-info.java deleted file mode 100644 index fc6aefda..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Subcommands of the PGPainless SOP. - */ -package sop.cli.picocli.commands; diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/package-info.java b/sop-java-picocli/src/main/java/sop/cli/picocli/package-info.java deleted file mode 100644 index 83f426d6..00000000 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Implementation of the Stateless OpenPGP Command Line Interface using Picocli. - */ -package sop.cli.picocli; diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/DateParserTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/DateParserTest.java deleted file mode 100644 index 5c7def50..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/DateParserTest.java +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.Date; - -import org.junit.jupiter.api.Test; -import sop.util.UTCUtil; - -public class DateParserTest { - - @Test - public void parseNotAfterDashReturnsEndOfTime() { - assertEquals(DateParser.END_OF_TIME, DateParser.parseNotAfter("-")); - } - - @Test - public void parseNotBeforeDashReturnsBeginningOfTime() { - assertEquals(DateParser.BEGINNING_OF_TIME, DateParser.parseNotBefore("-")); - } - - @Test - public void parseNotAfterNowReturnsNow() { - assertEquals(new Date().getTime(), DateParser.parseNotAfter("now").getTime(), 1000); - } - - @Test - public void parseNotBeforeNowReturnsNow() { - assertEquals(new Date().getTime(), DateParser.parseNotBefore("now").getTime(), 1000); - } - - @Test - public void parseNotAfterTimestamp() { - String timestamp = "2019-10-24T23:48:29Z"; - Date date = DateParser.parseNotAfter(timestamp); - assertEquals(timestamp, UTCUtil.formatUTCDate(date)); - } - - @Test - public void parseNotBeforeTimestamp() { - String timestamp = "2019-10-29T18:36:45Z"; - Date date = DateParser.parseNotBefore(timestamp); - assertEquals(timestamp, UTCUtil.formatUTCDate(date)); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/FileUtilTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/FileUtilTest.java deleted file mode 100644 index eeb4589d..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/FileUtilTest.java +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import sop.exception.SOPGPException; - -public class FileUtilTest { - - @BeforeAll - public static void setup() { - FileUtil.setEnvironmentVariableResolver(new FileUtil.EnvironmentVariableResolver() { - @Override - public String resolveEnvironmentVariable(String name) { - if (name.equals("test123")) { - return "test321"; - } - return null; - } - }); - } - - @Test - public void getFile_ThrowsForNull() { - assertThrows(NullPointerException.class, () -> FileUtil.getFile(null)); - } - - @Test - public void getFile_prfxEnvAlreadyExists() throws IOException { - File tempFile = new File("@ENV:test"); - tempFile.createNewFile(); - tempFile.deleteOnExit(); - - assertThrows(SOPGPException.AmbiguousInput.class, () -> FileUtil.getFile("@ENV:test")); - } - - @Test - public void getFile_EnvironmentVariable() { - File file = FileUtil.getFile("@ENV:test123"); - assertEquals("test321", file.getName()); - } - - @Test - public void getFile_nonExistentEnvVariable() { - assertThrows(IllegalArgumentException.class, () -> FileUtil.getFile("@ENV:INVALID")); - } - - @Test - public void getFile_prfxFdAlreadyExists() throws IOException { - File tempFile = new File("@FD:1"); - tempFile.createNewFile(); - tempFile.deleteOnExit(); - - assertThrows(SOPGPException.AmbiguousInput.class, () -> FileUtil.getFile("@FD:1")); - } - - @Test - public void getFile_prfxFdNotSupported() { - assertThrows(IllegalArgumentException.class, () -> FileUtil.getFile("@FD:2")); - } - - @Test - public void createNewFileOrThrow_throwsForNull() { - assertThrows(NullPointerException.class, () -> FileUtil.createNewFileOrThrow(null)); - } - - @Test - public void createNewFileOrThrow_success() throws IOException { - File dir = Files.createTempDirectory("test").toFile(); - dir.deleteOnExit(); - File file = new File(dir, "file"); - - assertFalse(file.exists()); - FileUtil.createNewFileOrThrow(file); - assertTrue(file.exists()); - } - - @Test - public void createNewFileOrThrow_alreadyExists() throws IOException { - File dir = Files.createTempDirectory("test").toFile(); - dir.deleteOnExit(); - File file = new File(dir, "file"); - - FileUtil.createNewFileOrThrow(file); - assertTrue(file.exists()); - assertThrows(SOPGPException.OutputExists.class, () -> FileUtil.createNewFileOrThrow(file)); - } - - @Test - public void getFileInputStream_success() throws IOException { - File dir = Files.createTempDirectory("test").toFile(); - dir.deleteOnExit(); - File file = new File(dir, "file"); - - FileUtil.createNewFileOrThrow(file); - FileInputStream inputStream = FileUtil.getFileInputStream(file.getAbsolutePath()); - assertNotNull(inputStream); - } - - @Test - public void getFileInputStream_fileNotFound() throws IOException { - File dir = Files.createTempDirectory("test").toFile(); - dir.deleteOnExit(); - File file = new File(dir, "file"); - - assertThrows(SOPGPException.MissingInput.class, - () -> FileUtil.getFileInputStream(file.getAbsolutePath())); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/SOPTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/SOPTest.java deleted file mode 100644 index 6360a779..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/SOPTest.java +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.Test; -import sop.SOP; -import sop.operation.Armor; -import sop.operation.Dearmor; -import sop.operation.Decrypt; -import sop.operation.DetachInbandSignatureAndMessage; -import sop.operation.Encrypt; -import sop.operation.ExtractCert; -import sop.operation.GenerateKey; -import sop.operation.Sign; -import sop.operation.Verify; -import sop.operation.Version; - -public class SOPTest { - - @Test - @ExpectSystemExitWithStatus(69) - public void assertExitOnInvalidSubcommand() { - SOP sop = mock(SOP.class); - SopCLI.setSopInstance(sop); - - SopCLI.main(new String[] {"invalid"}); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void assertThrowsIfNoSOPBackendSet() { - SopCLI.SOP_INSTANCE = null; - // At this point, no SOP backend is set, so an InvalidStateException triggers exit(1) - SopCLI.main(new String[] {"armor"}); - } - - @Test - public void UnsupportedSubcommandsTest() { - SOP nullCommandSOP = new SOP() { - @Override - public Version version() { - return null; - } - - @Override - public GenerateKey generateKey() { - return null; - } - - @Override - public ExtractCert extractCert() { - return null; - } - - @Override - public Sign sign() { - return null; - } - - @Override - public Verify verify() { - return null; - } - - @Override - public Encrypt encrypt() { - return null; - } - - @Override - public Decrypt decrypt() { - return null; - } - - @Override - public Armor armor() { - return null; - } - - @Override - public Dearmor dearmor() { - return null; - } - - @Override - public DetachInbandSignatureAndMessage detachInbandSignatureAndMessage() { - return null; - } - }; - SopCLI.setSopInstance(nullCommandSOP); - - List commands = new ArrayList<>(); - commands.add(new String[] {"armor"}); - commands.add(new String[] {"dearmor"}); - commands.add(new String[] {"decrypt"}); - commands.add(new String[] {"detach-inband-signature-and-message"}); - commands.add(new String[] {"encrypt"}); - commands.add(new String[] {"extract-cert"}); - commands.add(new String[] {"generate-key"}); - commands.add(new String[] {"sign"}); - commands.add(new String[] {"verify", "signature.asc", "cert.asc"}); - commands.add(new String[] {"version"}); - - for (String[] command : commands) { - int exit = SopCLI.execute(command); - assertEquals(69, exit, "Unexpected exit code for non-implemented command " + Arrays.toString(command) + ": " + exit); - } - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/ArmorCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/ArmorCmdTest.java deleted file mode 100644 index 01aaa9a5..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/ArmorCmdTest.java +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import com.ginsberg.junit.exit.FailOnSystemExit; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import sop.Ready; -import sop.SOP; -import sop.cli.picocli.SopCLI; -import sop.enums.ArmorLabel; -import sop.exception.SOPGPException; -import sop.operation.Armor; - -public class ArmorCmdTest { - - private Armor armor; - private SOP sop; - - @BeforeEach - public void mockComponents() throws SOPGPException.BadData { - armor = mock(Armor.class); - sop = mock(SOP.class); - when(sop.armor()).thenReturn(armor); - when(armor.data((InputStream) any())).thenReturn(nopReady()); - - SopCLI.setSopInstance(sop); - } - - @Test - public void assertLabelIsNotCalledByDefault() throws SOPGPException.UnsupportedOption { - SopCLI.main(new String[] {"armor"}); - verify(armor, never()).label(any()); - } - - @Test - public void assertLabelIsCalledWhenFlaggedWithArgument() throws SOPGPException.UnsupportedOption { - for (ArmorLabel label : ArmorLabel.values()) { - SopCLI.main(new String[] {"armor", "--label", label.name()}); - verify(armor, times(1)).label(label); - } - } - - @Test - public void assertDataIsAlwaysCalled() throws SOPGPException.BadData { - SopCLI.main(new String[] {"armor"}); - verify(armor, times(1)).data((InputStream) any()); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void assertThrowsForInvalidLabel() { - SopCLI.main(new String[] {"armor", "--label", "Invalid"}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void ifLabelsUnsupportedExit37() throws SOPGPException.UnsupportedOption { - when(armor.label(any())).thenThrow(new SOPGPException.UnsupportedOption("Custom Armor labels are not supported.")); - - SopCLI.main(new String[] {"armor", "--label", "Sig"}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void ifBadDataExit41() throws SOPGPException.BadData { - when(armor.data((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - - SopCLI.main(new String[] {"armor"}); - } - - @Test - @FailOnSystemExit - public void ifNoErrorsNoExit() { - when(sop.armor()).thenReturn(armor); - - SopCLI.main(new String[] {"armor"}); - } - - private static Ready nopReady() { - return new Ready() { - @Override - public void writeTo(OutputStream outputStream) { - } - }; - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/DearmorCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/DearmorCmdTest.java deleted file mode 100644 index aaad201b..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/DearmorCmdTest.java +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import sop.Ready; -import sop.SOP; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Dearmor; - -public class DearmorCmdTest { - - private SOP sop; - private Dearmor dearmor; - - @BeforeEach - public void mockComponents() throws IOException, SOPGPException.BadData { - sop = mock(SOP.class); - dearmor = mock(Dearmor.class); - when(dearmor.data((InputStream) any())).thenReturn(nopReady()); - when(sop.dearmor()).thenReturn(dearmor); - - SopCLI.setSopInstance(sop); - } - - private static Ready nopReady() { - return new Ready() { - @Override - public void writeTo(OutputStream outputStream) { - } - }; - } - - @Test - public void assertDataIsCalled() throws IOException, SOPGPException.BadData { - SopCLI.main(new String[] {"dearmor"}); - verify(dearmor, times(1)).data((InputStream) any()); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void assertBadDataCausesExit41() throws IOException, SOPGPException.BadData { - when(dearmor.data((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException("invalid armor"))); - SopCLI.main(new String[] {"dearmor"}); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/DecryptCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/DecryptCmdTest.java deleted file mode 100644 index 9e1c35ba..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/DecryptCmdTest.java +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.Date; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatcher; -import org.mockito.ArgumentMatchers; -import sop.DecryptionResult; -import sop.ReadyWithResult; -import sop.SOP; -import sop.SessionKey; -import sop.Verification; -import sop.cli.picocli.DateParser; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Decrypt; -import sop.util.HexUtil; -import sop.util.UTCUtil; - -public class DecryptCmdTest { - - private Decrypt decrypt; - - @BeforeEach - public void mockComponents() throws SOPGPException.UnsupportedOption, SOPGPException.MissingArg, SOPGPException.BadData, SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.PasswordNotHumanReadable, SOPGPException.CannotDecrypt { - SOP sop = mock(SOP.class); - decrypt = mock(Decrypt.class); - - when(decrypt.verifyNotAfter(any())).thenReturn(decrypt); - when(decrypt.verifyNotBefore(any())).thenReturn(decrypt); - when(decrypt.withPassword(any())).thenReturn(decrypt); - when(decrypt.withSessionKey(any())).thenReturn(decrypt); - when(decrypt.withKey((InputStream) any())).thenReturn(decrypt); - when(decrypt.ciphertext((InputStream) any())).thenReturn(nopReadyWithResult()); - - when(sop.decrypt()).thenReturn(decrypt); - - SopCLI.setSopInstance(sop); - } - - private static ReadyWithResult nopReadyWithResult() { - return new ReadyWithResult() { - @Override - public DecryptionResult writeTo(OutputStream outputStream) { - return new DecryptionResult(null, Collections.emptyList()); - } - }; - } - - @Test - @ExpectSystemExitWithStatus(19) - public void missingArgumentsExceptionCausesExit19() throws SOPGPException.MissingArg, SOPGPException.BadData, SOPGPException.CannotDecrypt { - when(decrypt.ciphertext((InputStream) any())).thenThrow(new SOPGPException.MissingArg("Missing arguments.")); - SopCLI.main(new String[] {"decrypt"}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void badDataExceptionCausesExit41() throws SOPGPException.MissingArg, SOPGPException.BadData, SOPGPException.CannotDecrypt { - when(decrypt.ciphertext((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - SopCLI.main(new String[] {"decrypt"}); - } - - @Test - @ExpectSystemExitWithStatus(31) - public void assertNotHumanReadablePasswordCausesExit31() throws SOPGPException.PasswordNotHumanReadable, - SOPGPException.UnsupportedOption { - when(decrypt.withPassword(any())).thenThrow(new SOPGPException.PasswordNotHumanReadable()); - SopCLI.main(new String[] {"decrypt", "--with-password", "pretendThisIsNotReadable"}); - } - - @Test - public void assertWithPasswordPassesPasswordDown() throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { - SopCLI.main(new String[] {"decrypt", "--with-password", "orange"}); - verify(decrypt, times(1)).withPassword("orange"); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void assertUnsupportedWithPasswordCausesExit37() throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { - when(decrypt.withPassword(any())).thenThrow(new SOPGPException.UnsupportedOption("Decrypting with password not supported.")); - SopCLI.main(new String[] {"decrypt", "--with-password", "swordfish"}); - } - - @Test - public void assertDefaultTimeRangesAreUsedIfNotOverwritten() throws SOPGPException.UnsupportedOption { - Date now = new Date(); - SopCLI.main(new String[] {"decrypt"}); - verify(decrypt, times(1)).verifyNotBefore(DateParser.BEGINNING_OF_TIME); - verify(decrypt, times(1)).verifyNotAfter( - ArgumentMatchers.argThat(argument -> { - // allow 1-second difference - return Math.abs(now.getTime() - argument.getTime()) <= 1000; - })); - } - - @Test - public void assertVerifyNotAfterAndBeforeDashResultsInMaxTimeRange() throws SOPGPException.UnsupportedOption { - SopCLI.main(new String[] {"decrypt", "--not-before", "-", "--not-after", "-"}); - verify(decrypt, times(1)).verifyNotBefore(DateParser.BEGINNING_OF_TIME); - verify(decrypt, times(1)).verifyNotAfter(DateParser.END_OF_TIME); - } - - @Test - public void assertVerifyNotAfterAndBeforeNowResultsInMinTimeRange() throws SOPGPException.UnsupportedOption { - Date now = new Date(); - ArgumentMatcher isMaxOneSecOff = argument -> { - // Allow less than 1-second difference - return Math.abs(now.getTime() - argument.getTime()) <= 1000; - }; - - SopCLI.main(new String[] {"decrypt", "--not-before", "now", "--not-after", "now"}); - verify(decrypt, times(1)).verifyNotAfter(ArgumentMatchers.argThat(isMaxOneSecOff)); - verify(decrypt, times(1)).verifyNotBefore(ArgumentMatchers.argThat(isMaxOneSecOff)); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void assertMalformedDateInNotBeforeCausesExit1() { - // ParserException causes exit(1) - SopCLI.main(new String[] {"decrypt", "--not-before", "invalid"}); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void assertMalformedDateInNotAfterCausesExit1() { - // ParserException causes exit(1) - SopCLI.main(new String[] {"decrypt", "--not-after", "invalid"}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void assertUnsupportedNotAfterCausesExit37() throws SOPGPException.UnsupportedOption { - when(decrypt.verifyNotAfter(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting upper signature date boundary not supported.")); - SopCLI.main(new String[] {"decrypt", "--not-after", "now"}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void assertUnsupportedNotBeforeCausesExit37() throws SOPGPException.UnsupportedOption { - when(decrypt.verifyNotBefore(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting lower signature date boundary not supported.")); - SopCLI.main(new String[] {"decrypt", "--not-before", "now"}); - } - - @Test - @ExpectSystemExitWithStatus(59) - public void assertExistingSessionKeyOutFileCausesExit59() throws IOException { - File tempFile = File.createTempFile("existing-session-key-", ".tmp"); - tempFile.deleteOnExit(); - SopCLI.main(new String[] {"decrypt", "--session-key-out", tempFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void assertWhenSessionKeyCannotBeExtractedExit37() throws IOException { - Path tempDir = Files.createTempDirectory("session-key-out-dir"); - File tempFile = new File(tempDir.toFile(), "session-key"); - tempFile.deleteOnExit(); - SopCLI.main(new String[] {"decrypt", "--session-key-out", tempFile.getAbsolutePath()}); - } - - @Test - public void assertSessionKeyIsProperlyWrittenToSessionKeyFile() throws SOPGPException.CannotDecrypt, SOPGPException.MissingArg, SOPGPException.BadData, IOException { - byte[] key = "C7CBDAF42537776F12509B5168793C26B93294E5ABDFA73224FB0177123E9137".getBytes(StandardCharsets.UTF_8); - when(decrypt.ciphertext((InputStream) any())).thenReturn(new ReadyWithResult() { - @Override - public DecryptionResult writeTo(OutputStream outputStream) { - return new DecryptionResult( - new SessionKey((byte) 9, key), - Collections.emptyList() - ); - } - }); - Path tempDir = Files.createTempDirectory("session-key-out-dir"); - File tempFile = new File(tempDir.toFile(), "session-key"); - tempFile.deleteOnExit(); - SopCLI.main(new String[] {"decrypt", "--session-key-out", tempFile.getAbsolutePath()}); - - ByteArrayOutputStream bytesInFile = new ByteArrayOutputStream(); - try (FileInputStream fileIn = new FileInputStream(tempFile)) { - byte[] buf = new byte[32]; - int read = fileIn.read(buf); - while (read != -1) { - bytesInFile.write(buf, 0, read); - read = fileIn.read(buf); - } - } - - byte[] algAndKey = new byte[key.length + 1]; - algAndKey[0] = (byte) 9; - System.arraycopy(key, 0, algAndKey, 1, key.length); - assertArrayEquals(algAndKey, bytesInFile.toByteArray()); - } - - @Test - @ExpectSystemExitWithStatus(29) - public void assertUnableToDecryptExceptionResultsInExit29() throws SOPGPException.CannotDecrypt, SOPGPException.MissingArg, SOPGPException.BadData { - when(decrypt.ciphertext((InputStream) any())).thenThrow(new SOPGPException.CannotDecrypt()); - SopCLI.main(new String[] {"decrypt"}); - } - - @Test - @ExpectSystemExitWithStatus(3) - public void assertNoSignatureExceptionCausesExit3() throws SOPGPException.CannotDecrypt, SOPGPException.MissingArg, SOPGPException.BadData { - when(decrypt.ciphertext((InputStream) any())).thenReturn(new ReadyWithResult() { - @Override - public DecryptionResult writeTo(OutputStream outputStream) throws SOPGPException.NoSignature { - throw new SOPGPException.NoSignature(); - } - }); - SopCLI.main(new String[] {"decrypt"}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void badDataInVerifyWithCausesExit41() throws IOException, SOPGPException.BadData { - when(decrypt.verifyWithCert((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - File tempFile = File.createTempFile("verify-with-", ".tmp"); - SopCLI.main(new String[] {"decrypt", "--verify-with", tempFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(61) - public void unexistentCertFileCausesExit61() { - SopCLI.main(new String[] {"decrypt", "--verify-with", "invalid"}); - } - - @Test - @ExpectSystemExitWithStatus(59) - public void existingVerifyOutCausesExit59() throws IOException { - File certFile = File.createTempFile("existing-verify-out-cert", ".asc"); - File existingVerifyOut = File.createTempFile("existing-verify-out", ".tmp"); - - SopCLI.main(new String[] {"decrypt", "--verify-out", existingVerifyOut.getAbsolutePath(), "--verify-with", certFile.getAbsolutePath()}); - } - - @Test - public void verifyOutIsProperlyWritten() throws IOException, SOPGPException.CannotDecrypt, SOPGPException.MissingArg, SOPGPException.BadData { - File certFile = File.createTempFile("verify-out-cert", ".asc"); - File verifyOut = new File(certFile.getParent(), "verify-out.txt"); - if (verifyOut.exists()) { - verifyOut.delete(); - } - verifyOut.deleteOnExit(); - Date date = UTCUtil.parseUTCDate("2021-07-11T20:58:23Z"); - when(decrypt.ciphertext((InputStream) any())).thenReturn(new ReadyWithResult() { - @Override - public DecryptionResult writeTo(OutputStream outputStream) { - return new DecryptionResult(null, Collections.singletonList( - new Verification( - date, - "1B66A707819A920925BC6777C3E0AFC0B2DFF862", - "C8CD564EBF8D7BBA90611D8D071773658BF6BF86")) - ); - } - }); - - SopCLI.main(new String[] {"decrypt", "--verify-out", verifyOut.getAbsolutePath(), "--verify-with", certFile.getAbsolutePath()}); - try (BufferedReader reader = new BufferedReader(new FileReader(verifyOut))) { - String line = reader.readLine(); - assertEquals("2021-07-11T20:58:23Z 1B66A707819A920925BC6777C3E0AFC0B2DFF862 C8CD564EBF8D7BBA90611D8D071773658BF6BF86", line); - } - } - - @Test - public void assertWithSessionKeyIsPassedDown() throws SOPGPException.UnsupportedOption { - SessionKey key1 = new SessionKey((byte) 9, HexUtil.hexToBytes("C7CBDAF42537776F12509B5168793C26B93294E5ABDFA73224FB0177123E9137")); - SessionKey key2 = new SessionKey((byte) 9, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD")); - SopCLI.main(new String[] {"decrypt", - "--with-session-key", "9:C7CBDAF42537776F12509B5168793C26B93294E5ABDFA73224FB0177123E9137", - "--with-session-key", "9:FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"}); - verify(decrypt).withSessionKey(key1); - verify(decrypt).withSessionKey(key2); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void assertMalformedSessionKeysResultInExit1() { - SopCLI.main(new String[] {"decrypt", - "--with-session-key", "C7CBDAF42537776F12509B5168793C26B93294E5ABDFA73224FB0177123E9137"}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void assertBadDataInKeysResultsInExit41() throws SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, IOException { - when(decrypt.withKey((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - File tempKeyFile = File.createTempFile("key-", ".tmp"); - SopCLI.main(new String[] {"decrypt", tempKeyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(61) - public void assertKeyFileNotFoundCausesExit61() { - SopCLI.main(new String[] {"decrypt", "nonexistent-key"}); - } - - @Test - @ExpectSystemExitWithStatus(67) - public void assertProtectedKeyCausesExit67() throws IOException, SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData { - when(decrypt.withKey((InputStream) any())).thenThrow(new SOPGPException.KeyIsProtected()); - File tempKeyFile = File.createTempFile("key-", ".tmp"); - SopCLI.main(new String[] {"decrypt", tempKeyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(13) - public void assertUnsupportedAlgorithmExceptionCausesExit13() throws SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, IOException { - when(decrypt.withKey((InputStream) any())).thenThrow(new SOPGPException.UnsupportedAsymmetricAlgo("Unsupported asymmetric algorithm.", new IOException())); - File tempKeyFile = File.createTempFile("key-", ".tmp"); - SopCLI.main(new String[] {"decrypt", tempKeyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(23) - public void verifyOutWithoutVerifyWithCausesExit23() { - SopCLI.main(new String[] {"decrypt", "--verify-out", "out.file"}); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/EncryptCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/EncryptCmdTest.java deleted file mode 100644 index 91f0a1e7..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/EncryptCmdTest.java +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import sop.Ready; -import sop.SOP; -import sop.cli.picocli.SopCLI; -import sop.enums.EncryptAs; -import sop.exception.SOPGPException; -import sop.operation.Encrypt; - -public class EncryptCmdTest { - - Encrypt encrypt; - - @BeforeEach - public void mockComponents() throws IOException { - encrypt = mock(Encrypt.class); - when(encrypt.plaintext((InputStream) any())).thenReturn(new Ready() { - @Override - public void writeTo(OutputStream outputStream) { - - } - }); - - SOP sop = mock(SOP.class); - when(sop.encrypt()).thenReturn(encrypt); - - SopCLI.setSopInstance(sop); - } - - @Test - @ExpectSystemExitWithStatus(19) - public void missingBothPasswordAndCertFileCauseExit19() { - SopCLI.main(new String[] {"encrypt", "--no-armor"}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void as_unsupportedEncryptAsCausesExit37() throws SOPGPException.UnsupportedOption { - when(encrypt.mode(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting encryption mode not supported.")); - - SopCLI.main(new String[] {"encrypt", "--as", "Binary"}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void as_invalidModeOptionCausesExit37() { - SopCLI.main(new String[] {"encrypt", "--as", "invalid"}); - } - - @Test - public void as_modeIsPassedDown() throws SOPGPException.UnsupportedOption { - for (EncryptAs mode : EncryptAs.values()) { - SopCLI.main(new String[] {"encrypt", "--as", mode.name(), "--with-password", "0rbit"}); - verify(encrypt, times(1)).mode(mode); - } - } - - @Test - @ExpectSystemExitWithStatus(31) - public void withPassword_notHumanReadablePasswordCausesExit31() throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { - when(encrypt.withPassword("pretendThisIsNotReadable")).thenThrow(new SOPGPException.PasswordNotHumanReadable()); - - SopCLI.main(new String[] {"encrypt", "--with-password", "pretendThisIsNotReadable"}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void withPassword_unsupportedWithPasswordCausesExit37() throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { - when(encrypt.withPassword(any())).thenThrow(new SOPGPException.UnsupportedOption("Encrypting with password not supported.")); - - SopCLI.main(new String[] {"encrypt", "--with-password", "orange"}); - } - - @Test - public void signWith_multipleTimesGetPassedDown() throws IOException, SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.KeyCannotSign, SOPGPException.BadData { - File keyFile1 = File.createTempFile("sign-with-1-", ".asc"); - File keyFile2 = File.createTempFile("sign-with-2-", ".asc"); - - SopCLI.main(new String[] {"encrypt", "--with-password", "password", "--sign-with", keyFile1.getAbsolutePath(), "--sign-with", keyFile2.getAbsolutePath()}); - verify(encrypt, times(2)).signWith((InputStream) any()); - } - - @Test - @ExpectSystemExitWithStatus(61) - public void signWith_nonExistentKeyFileCausesExit61() { - SopCLI.main(new String[] {"encrypt", "--with-password", "admin", "--sign-with", "nonExistent.asc"}); - } - - @Test - @ExpectSystemExitWithStatus(67) - public void signWith_keyIsProtectedCausesExit67() throws SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.KeyCannotSign, SOPGPException.BadData, IOException { - when(encrypt.signWith((InputStream) any())).thenThrow(new SOPGPException.KeyIsProtected()); - File keyFile = File.createTempFile("sign-with", ".asc"); - SopCLI.main(new String[] {"encrypt", "--sign-with", keyFile.getAbsolutePath(), "--with-password", "starship"}); - } - - @Test - @ExpectSystemExitWithStatus(13) - public void signWith_unsupportedAsymmetricAlgoCausesExit13() throws SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.KeyCannotSign, SOPGPException.BadData, IOException { - when(encrypt.signWith((InputStream) any())).thenThrow(new SOPGPException.UnsupportedAsymmetricAlgo("Unsupported asymmetric algorithm.", new Exception())); - File keyFile = File.createTempFile("sign-with", ".asc"); - SopCLI.main(new String[] {"encrypt", "--with-password", "123456", "--sign-with", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(79) - public void signWith_certCannotSignCausesExit1() throws IOException, SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.KeyCannotSign, SOPGPException.BadData { - when(encrypt.signWith((InputStream) any())).thenThrow(new SOPGPException.KeyCannotSign()); - File keyFile = File.createTempFile("sign-with", ".asc"); - SopCLI.main(new String[] {"encrypt", "--with-password", "dragon", "--sign-with", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void signWith_badDataCausesExit41() throws SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.KeyCannotSign, SOPGPException.BadData, IOException { - when(encrypt.signWith((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - File keyFile = File.createTempFile("sign-with", ".asc"); - SopCLI.main(new String[] {"encrypt", "--with-password", "orange", "--sign-with", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(61) - public void cert_nonExistentCertFileCausesExit61() { - SopCLI.main(new String[] {"encrypt", "invalid.asc"}); - } - - @Test - @ExpectSystemExitWithStatus(13) - public void cert_unsupportedAsymmetricAlgorithmCausesExit13() throws IOException, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.CertCannotEncrypt, SOPGPException.BadData { - when(encrypt.withCert((InputStream) any())).thenThrow(new SOPGPException.UnsupportedAsymmetricAlgo("Unsupported asymmetric algorithm.", new Exception())); - File certFile = File.createTempFile("cert", ".asc"); - SopCLI.main(new String[] {"encrypt", certFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(17) - public void cert_certCannotEncryptCausesExit17() throws IOException, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.CertCannotEncrypt, SOPGPException.BadData { - when(encrypt.withCert((InputStream) any())).thenThrow(new SOPGPException.CertCannotEncrypt("Certificate cannot encrypt.", new Exception())); - File certFile = File.createTempFile("cert", ".asc"); - SopCLI.main(new String[] {"encrypt", certFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void cert_badDataCausesExit41() throws IOException, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.CertCannotEncrypt, SOPGPException.BadData { - when(encrypt.withCert((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - File certFile = File.createTempFile("cert", ".asc"); - SopCLI.main(new String[] {"encrypt", certFile.getAbsolutePath()}); - } - - @Test - public void noArmor_notCalledByDefault() { - SopCLI.main(new String[] {"encrypt", "--with-password", "clownfish"}); - verify(encrypt, never()).noArmor(); - } - - @Test - public void noArmor_callGetsPassedDown() { - SopCLI.main(new String[] {"encrypt", "--with-password", "monkey", "--no-armor"}); - verify(encrypt, times(1)).noArmor(); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void writeTo_ioExceptionCausesExit1() throws IOException { - when(encrypt.plaintext((InputStream) any())).thenReturn(new Ready() { - @Override - public void writeTo(OutputStream outputStream) throws IOException { - throw new IOException(); - } - }); - - SopCLI.main(new String[] {"encrypt", "--with-password", "wildcat"}); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/ExtractCertCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/ExtractCertCmdTest.java deleted file mode 100644 index 382fe300..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/ExtractCertCmdTest.java +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import sop.Ready; -import sop.SOP; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.ExtractCert; - -public class ExtractCertCmdTest { - - ExtractCert extractCert; - - @BeforeEach - public void mockComponents() throws IOException, SOPGPException.BadData { - extractCert = mock(ExtractCert.class); - when(extractCert.key((InputStream) any())).thenReturn(new Ready() { - @Override - public void writeTo(OutputStream outputStream) { - } - }); - - SOP sop = mock(SOP.class); - when(sop.extractCert()).thenReturn(extractCert); - - SopCLI.setSopInstance(sop); - } - - @Test - public void noArmor_notCalledByDefault() { - SopCLI.main(new String[] {"extract-cert"}); - verify(extractCert, never()).noArmor(); - } - - @Test - public void noArmor_passedDown() { - SopCLI.main(new String[] {"extract-cert", "--no-armor"}); - verify(extractCert, times(1)).noArmor(); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void key_ioExceptionCausesExit1() throws IOException, SOPGPException.BadData { - when(extractCert.key((InputStream) any())).thenReturn(new Ready() { - @Override - public void writeTo(OutputStream outputStream) throws IOException { - throw new IOException(); - } - }); - SopCLI.main(new String[] {"extract-cert"}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void key_badDataCausesExit41() throws IOException, SOPGPException.BadData { - when(extractCert.key((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - SopCLI.main(new String[] {"extract-cert"}); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/GenerateKeyCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/GenerateKeyCmdTest.java deleted file mode 100644 index 643cf363..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/GenerateKeyCmdTest.java +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.OutputStream; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; -import sop.Ready; -import sop.SOP; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.GenerateKey; - -public class GenerateKeyCmdTest { - - GenerateKey generateKey; - - @BeforeEach - public void mockComponents() throws SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.MissingArg, IOException { - generateKey = mock(GenerateKey.class); - when(generateKey.generate()).thenReturn(new Ready() { - @Override - public void writeTo(OutputStream outputStream) { - - } - }); - - SOP sop = mock(SOP.class); - when(sop.generateKey()).thenReturn(generateKey); - - SopCLI.setSopInstance(sop); - } - - @Test - public void noArmor_notCalledByDefault() { - SopCLI.main(new String[] {"generate-key", "Alice"}); - verify(generateKey, never()).noArmor(); - } - - @Test - public void noArmor_passedDown() { - SopCLI.main(new String[] {"generate-key", "--no-armor", "Alice"}); - verify(generateKey, times(1)).noArmor(); - } - - @Test - public void userId_multipleUserIdsPassedDownInProperOrder() { - SopCLI.main(new String[] {"generate-key", "Alice ", "Bob "}); - - InOrder inOrder = Mockito.inOrder(generateKey); - inOrder.verify(generateKey).userId("Alice "); - inOrder.verify(generateKey).userId("Bob "); - - verify(generateKey, times(2)).userId(any()); - } - - @Test - @ExpectSystemExitWithStatus(19) - public void missingArgumentCausesExit19() throws SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.MissingArg, IOException { - // TODO: RFC4880-bis and the current Stateless OpenPGP CLI spec allow keys to have no user-ids, - // so we might want to change this test in the future. - when(generateKey.generate()).thenThrow(new SOPGPException.MissingArg("Missing user-id.")); - SopCLI.main(new String[] {"generate-key"}); - } - - @Test - @ExpectSystemExitWithStatus(13) - public void unsupportedAsymmetricAlgorithmCausesExit13() throws SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.MissingArg, IOException { - when(generateKey.generate()).thenThrow(new SOPGPException.UnsupportedAsymmetricAlgo("Unsupported asymmetric algorithm.", new Exception())); - SopCLI.main(new String[] {"generate-key", "Alice"}); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void ioExceptionCausesExit1() throws SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.MissingArg, IOException { - when(generateKey.generate()).thenReturn(new Ready() { - @Override - public void writeTo(OutputStream outputStream) throws IOException { - throw new IOException(); - } - }); - SopCLI.main(new String[] {"generate-key", "Alice"}); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/SignCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/SignCmdTest.java deleted file mode 100644 index ce0ce54a..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/SignCmdTest.java +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import sop.ReadyWithResult; -import sop.SOP; -import sop.SigningResult; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Sign; - -public class SignCmdTest { - - Sign sign; - File keyFile; - - @BeforeEach - public void mockComponents() throws IOException, SOPGPException.ExpectedText { - sign = mock(Sign.class); - when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult() { - @Override - public SigningResult writeTo(OutputStream outputStream) { - return SigningResult.builder().build(); - } - }); - - SOP sop = mock(SOP.class); - when(sop.sign()).thenReturn(sign); - - SopCLI.setSopInstance(sop); - - keyFile = File.createTempFile("sign-", ".asc"); - } - - @Test - public void as_optionsAreCaseInsensitive() { - SopCLI.main(new String[] {"sign", "--as", "Binary", keyFile.getAbsolutePath()}); - SopCLI.main(new String[] {"sign", "--as", "binary", keyFile.getAbsolutePath()}); - SopCLI.main(new String[] {"sign", "--as", "BINARY", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void as_invalidOptionCausesExit37() { - SopCLI.main(new String[] {"sign", "--as", "Invalid", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void as_unsupportedOptionCausesExit37() throws SOPGPException.UnsupportedOption { - when(sign.mode(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting signing mode not supported.")); - SopCLI.main(new String[] {"sign", "--as", "binary", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void key_nonExistentKeyFileCausesExit1() { - SopCLI.main(new String[] {"sign", "invalid.asc"}); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void key_keyIsProtectedCausesExit1() throws SOPGPException.KeyIsProtected, IOException, SOPGPException.BadData { - when(sign.key((InputStream) any())).thenThrow(new SOPGPException.KeyIsProtected()); - SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void key_badDataCausesExit41() throws SOPGPException.KeyIsProtected, IOException, SOPGPException.BadData { - when(sign.key((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(19) - public void key_missingKeyFileCausesExit19() { - SopCLI.main(new String[] {"sign"}); - } - - @Test - public void noArmor_notCalledByDefault() { - SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()}); - verify(sign, never()).noArmor(); - } - - @Test - public void noArmor_passedDown() { - SopCLI.main(new String[] {"sign", "--no-armor", keyFile.getAbsolutePath()}); - verify(sign, times(1)).noArmor(); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void data_ioExceptionCausesExit1() throws IOException, SOPGPException.ExpectedText { - when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult() { - @Override - public SigningResult writeTo(OutputStream outputStream) throws IOException { - throw new IOException(); - } - }); - SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(53) - public void data_expectedTextExceptionCausesExit53() throws IOException, SOPGPException.ExpectedText { - when(sign.data((InputStream) any())).thenThrow(new SOPGPException.ExpectedText()); - SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()}); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/VerifyCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/VerifyCmdTest.java deleted file mode 100644 index 028d2451..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/VerifyCmdTest.java +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatchers; -import sop.SOP; -import sop.Verification; -import sop.cli.picocli.DateParser; -import sop.cli.picocli.SopCLI; -import sop.exception.SOPGPException; -import sop.operation.Verify; -import sop.util.UTCUtil; - -public class VerifyCmdTest { - - Verify verify; - File signature; - File cert; - - PrintStream originalSout; - - @BeforeEach - public void prepare() throws SOPGPException.UnsupportedOption, SOPGPException.BadData, SOPGPException.NoSignature, IOException { - originalSout = System.out; - - verify = mock(Verify.class); - when(verify.notBefore(any())).thenReturn(verify); - when(verify.notAfter(any())).thenReturn(verify); - when(verify.cert((InputStream) any())).thenReturn(verify); - when(verify.signatures((InputStream) any())).thenReturn(verify); - when(verify.data((InputStream) any())).thenReturn( - Collections.singletonList( - new Verification( - UTCUtil.parseUTCDate("2019-10-29T18:36:45Z"), - "EB85BB5FA33A75E15E944E63F231550C4F47E38E", - "EB85BB5FA33A75E15E944E63F231550C4F47E38E") - ) - ); - - SOP sop = mock(SOP.class); - when(sop.verify()).thenReturn(verify); - - SopCLI.setSopInstance(sop); - - signature = File.createTempFile("signature-", ".asc"); - cert = File.createTempFile("cert-", ".asc"); - } - - @AfterEach - public void restoreSout() { - System.setOut(originalSout); - } - - @Test - public void notAfter_passedDown() throws SOPGPException.UnsupportedOption { - Date date = UTCUtil.parseUTCDate("2019-10-29T18:36:45Z"); - SopCLI.main(new String[] {"verify", "--not-after", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()}); - verify(verify, times(1)).notAfter(date); - } - - @Test - public void notAfter_now() throws SOPGPException.UnsupportedOption { - Date now = new Date(); - SopCLI.main(new String[] {"verify", "--not-after", "now", signature.getAbsolutePath(), cert.getAbsolutePath()}); - verify(verify, times(1)).notAfter(dateMatcher(now)); - } - - @Test - public void notAfter_dashCountsAsEndOfTime() throws SOPGPException.UnsupportedOption { - SopCLI.main(new String[] {"verify", "--not-after", "-", signature.getAbsolutePath(), cert.getAbsolutePath()}); - verify(verify, times(1)).notAfter(DateParser.END_OF_TIME); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void notAfter_unsupportedOptionCausesExit37() throws SOPGPException.UnsupportedOption { - when(verify.notAfter(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting upper signature date boundary not supported.")); - SopCLI.main(new String[] {"verify", "--not-after", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()}); - } - - @Test - public void notBefore_passedDown() throws SOPGPException.UnsupportedOption { - Date date = UTCUtil.parseUTCDate("2019-10-29T18:36:45Z"); - SopCLI.main(new String[] {"verify", "--not-before", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()}); - verify(verify, times(1)).notBefore(date); - } - - @Test - public void notBefore_now() throws SOPGPException.UnsupportedOption { - Date now = new Date(); - SopCLI.main(new String[] {"verify", "--not-before", "now", signature.getAbsolutePath(), cert.getAbsolutePath()}); - verify(verify, times(1)).notBefore(dateMatcher(now)); - } - - @Test - public void notBefore_dashCountsAsBeginningOfTime() throws SOPGPException.UnsupportedOption { - SopCLI.main(new String[] {"verify", "--not-before", "-", signature.getAbsolutePath(), cert.getAbsolutePath()}); - verify(verify, times(1)).notBefore(DateParser.BEGINNING_OF_TIME); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void notBefore_unsupportedOptionCausesExit37() throws SOPGPException.UnsupportedOption { - when(verify.notBefore(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting lower signature date boundary not supported.")); - SopCLI.main(new String[] {"verify", "--not-before", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()}); - } - - @Test - public void notBeforeAndNotAfterAreCalledWithDefaultValues() throws SOPGPException.UnsupportedOption { - SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()}); - verify(verify, times(1)).notAfter(dateMatcher(new Date())); - verify(verify, times(1)).notBefore(DateParser.BEGINNING_OF_TIME); - } - - private static Date dateMatcher(Date date) { - return ArgumentMatchers.argThat(argument -> Math.abs(argument.getTime() - date.getTime()) < 1000); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void cert_fileNotFoundCausesExit1() { - SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), "invalid.asc"}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void cert_badDataCausesExit41() throws SOPGPException.BadData { - when(verify.cert((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(1) - public void signature_fileNotFoundCausesExit1() { - SopCLI.main(new String[] {"verify", "invalid.sig", cert.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void signature_badDataCausesExit41() throws SOPGPException.BadData { - when(verify.signatures((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(3) - public void data_noSignaturesCausesExit3() throws SOPGPException.NoSignature, IOException, SOPGPException.BadData { - when(verify.data((InputStream) any())).thenThrow(new SOPGPException.NoSignature()); - SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()}); - } - - @Test - @ExpectSystemExitWithStatus(41) - public void data_badDataCausesExit41() throws SOPGPException.NoSignature, IOException, SOPGPException.BadData { - when(verify.data((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException())); - SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()}); - } - - @Test - public void resultIsPrintedProperly() throws SOPGPException.NoSignature, IOException, SOPGPException.BadData { - when(verify.data((InputStream) any())).thenReturn(Arrays.asList( - new Verification(UTCUtil.parseUTCDate("2019-10-29T18:36:45Z"), - "EB85BB5FA33A75E15E944E63F231550C4F47E38E", - "EB85BB5FA33A75E15E944E63F231550C4F47E38E"), - new Verification(UTCUtil.parseUTCDate("2019-10-24T23:48:29Z"), - "C90E6D36200A1B922A1509E77618196529AE5FF8", - "C4BC2DDB38CCE96485EBE9C2F20691179038E5C6") - )); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - System.setOut(new PrintStream(out)); - - SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()}); - - System.setOut(originalSout); - - String expected = "2019-10-29T18:36:45Z EB85BB5FA33A75E15E944E63F231550C4F47E38E EB85BB5FA33A75E15E944E63F231550C4F47E38E\n" + - "2019-10-24T23:48:29Z C90E6D36200A1B922A1509E77618196529AE5FF8 C4BC2DDB38CCE96485EBE9C2F20691179038E5C6\n"; - - assertEquals(expected, out.toString()); - } -} diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/VersionCmdTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/commands/VersionCmdTest.java deleted file mode 100644 index 98ea58e2..00000000 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/commands/VersionCmdTest.java +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.cli.picocli.commands; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import sop.SOP; -import sop.cli.picocli.SopCLI; -import sop.operation.Version; - -public class VersionCmdTest { - - private Version version; - - @BeforeEach - public void mockComponents() { - SOP sop = mock(SOP.class); - version = mock(Version.class); - when(version.getName()).thenReturn("MockSop"); - when(version.getVersion()).thenReturn("1.0"); - when(sop.version()).thenReturn(version); - - SopCLI.setSopInstance(sop); - } - - @Test - public void assertVersionCommandWorks() { - SopCLI.main(new String[] {"version"}); - verify(version, times(1)).getVersion(); - verify(version, times(1)).getName(); - } - - @Test - @ExpectSystemExitWithStatus(37) - public void assertInvalidOptionResultsInExit37() { - SopCLI.main(new String[] {"version", "--invalid"}); - } -} diff --git a/sop-java/README.md b/sop-java/README.md index 452576c6..e10261b6 100644 --- a/sop-java/README.md +++ b/sop-java/README.md @@ -1,80 +1 @@ - - -# SOP-Java - -[![Spec Revision: 3](https://img.shields.io/badge/Spec%20Revision-3-blue)](https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-03) -[![Maven Central](https://badgen.net/maven/v/maven-central/org.pgpainless/sop-java)](https://search.maven.org/artifact/org.pgpainless/sop-java) -[![JavaDoc](https://badgen.net/badge/javadoc/yes/green)](https://pgpainless.org/releases/latest/javadoc/sop/SOP.html) -[![REUSE status](https://api.reuse.software/badge/github.com/pgpainless/pgpainless)](https://api.reuse.software/info/github.com/pgpainless/pgpainless) - -Stateless OpenPGP Protocol for Java. - -This module contains interfaces that model the API described by the -[Stateless OpenPGP Command Line Interface](https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-03) specification. - -This module is not a command line application! For that, see `sop-java-picocli`. - -## Usage Examples - -The API defined by `sop-java` is super straight forward: -```java -SOP sop = ... // e.g. new org.pgpainless.sop.SOPImpl(); - -// Generate an OpenPGP key -byte[] key = sop.generateKey() - .userId("Alice ") - .generate() - .getBytes(); - -// Extract the certificate (public key) -byte[] cert = sop.extractCert() - .key(key) - .getBytes(); - -// Encrypt a message -byte[] message = ... -byte[] encrypted = sop.encrypt() - .withCert(cert) - .signWith(key) - .plaintext(message) - .getBytes(); - -// Decrypt a message -ByteArrayAndResult messageAndVerifications = sop.decrypt() - .verifyWith(cert) - .withKey(key) - .ciphertext(encrypted) - .toByteArrayAndResult(); -byte[] decrypted = messageAndVerifications.getBytes(); -// Signature Verifications -DecryptionResult messageInfo = messageAndVerifications.getResult(); -List signatureVerifications = messageInfo.getVerifications(); -``` - -Furthermore, the API is capable of signing messages and verifying unencrypted signed data, as well as adding and removing ASCII armor. - -### Limitations -As per the spec, sop-java does not (yet) deal with encrypted OpenPGP keys. - -## Why should I use this? - -If you need to use OpenPGP functionality like encrypting/decrypting messages, or creating/verifying -signatures inside your application, you probably don't want to start from scratch and instead reuse some library. - -Instead of locking yourselves in by depending hard on that one library, you can simply depend on the interfaces from -`sop-java` and plug in a library (such as `pgpainless-sop`) that implements said interfaces. - -That way you don't make yourself dependent from a single OpenPGP library and stay flexible. -Should another library emerge, that better suits your needs (and implements `sop-java`), you can easily switch -by swapping out the dependency with minimal changes to your code. - -## Why should I *implement* this? - -Did you create an [OpenPGP](https://datatracker.ietf.org/doc/html/rfc4880) implementation that can be used in the Java ecosystem? -By implementing the `sop-java` interface, you can turn your library into a command line interface (see `sop-java-picocli`). -This allows you to plug your library into the [OpenPGP interoperability test suite](https://tests.sequoia-pgp.org/) -of the [Sequoia-PGP](https://sequoia-pgp.org/) project. +# [MOVED](https://github.com/pgpainless/sop-java/tree/master/sop-java) \ No newline at end of file diff --git a/sop-java/build.gradle b/sop-java/build.gradle deleted file mode 100644 index c2e2f1fb..00000000 --- a/sop-java/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -plugins { - id 'java' -} - -group 'org.pgpainless' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/sop-java/src/main/java/sop/ByteArrayAndResult.java b/sop-java/src/main/java/sop/ByteArrayAndResult.java deleted file mode 100644 index fd2b39a7..00000000 --- a/sop-java/src/main/java/sop/ByteArrayAndResult.java +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -/** - * Tuple of a byte array and associated result object. - * @param type of result - */ -public class ByteArrayAndResult { - - private final byte[] bytes; - private final T result; - - public ByteArrayAndResult(byte[] bytes, T result) { - this.bytes = bytes; - this.result = result; - } - - /** - * Return the byte array part. - * - * @return bytes - */ - public byte[] getBytes() { - return bytes; - } - - /** - * Return the result part. - * - * @return result - */ - public T getResult() { - return result; - } - - /** - * Return the byte array part as an {@link InputStream}. - * - * @return input stream - */ - public InputStream getInputStream() { - return new ByteArrayInputStream(getBytes()); - } -} diff --git a/sop-java/src/main/java/sop/DecryptionResult.java b/sop-java/src/main/java/sop/DecryptionResult.java deleted file mode 100644 index 4f0e1ab2..00000000 --- a/sop-java/src/main/java/sop/DecryptionResult.java +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import sop.util.Optional; - -public class DecryptionResult { - - private final Optional sessionKey; - private final List verifications; - - public DecryptionResult(SessionKey sessionKey, List verifications) { - this.sessionKey = Optional.ofNullable(sessionKey); - this.verifications = Collections.unmodifiableList(verifications); - } - - public Optional getSessionKey() { - return sessionKey; - } - - public List getVerifications() { - return new ArrayList<>(verifications); - } -} diff --git a/sop-java/src/main/java/sop/MicAlg.java b/sop-java/src/main/java/sop/MicAlg.java deleted file mode 100644 index 5bee7875..00000000 --- a/sop-java/src/main/java/sop/MicAlg.java +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.io.OutputStream; -import java.io.PrintWriter; - -public class MicAlg { - - private final String micAlg; - - public MicAlg(String micAlg) { - if (micAlg == null) { - throw new IllegalArgumentException("MicAlg String cannot be null."); - } - this.micAlg = micAlg; - } - - public static MicAlg empty() { - return new MicAlg(""); - } - - public static MicAlg fromHashAlgorithmId(int id) { - switch (id) { - case 1: - return new MicAlg("pgp-md5"); - case 2: - return new MicAlg("pgp-sha1"); - case 3: - return new MicAlg("pgp-ripemd160"); - case 8: - return new MicAlg("pgp-sha256"); - case 9: - return new MicAlg("pgp-sha384"); - case 10: - return new MicAlg("pgp-sha512"); - case 11: - return new MicAlg("pgp-sha224"); - default: - throw new IllegalArgumentException("Unsupported hash algorithm ID: " + id); - } - } - - public String getMicAlg() { - return micAlg; - } - - public void writeTo(OutputStream outputStream) { - PrintWriter pw = new PrintWriter(outputStream); - pw.write(getMicAlg()); - pw.close(); - } -} diff --git a/sop-java/src/main/java/sop/Ready.java b/sop-java/src/main/java/sop/Ready.java deleted file mode 100644 index 71ab26ec..00000000 --- a/sop-java/src/main/java/sop/Ready.java +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public abstract class Ready { - - /** - * Write the data to the provided output stream. - * - * @param outputStream output stream - * @throws IOException in case of an IO error - */ - public abstract void writeTo(OutputStream outputStream) throws IOException; - - /** - * Return the data as a byte array by writing it to a {@link ByteArrayOutputStream} first and then returning - * the array. - * - * @return data as byte array - * @throws IOException in case of an IO error - */ - public byte[] getBytes() throws IOException { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - writeTo(bytes); - return bytes.toByteArray(); - } - - /** - * Return an input stream containing the data. - * - * @return input stream - * @throws IOException in case of an IO error - */ - public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(getBytes()); - } -} diff --git a/sop-java/src/main/java/sop/ReadyWithResult.java b/sop-java/src/main/java/sop/ReadyWithResult.java deleted file mode 100644 index 9feeddae..00000000 --- a/sop-java/src/main/java/sop/ReadyWithResult.java +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import sop.exception.SOPGPException; - -public abstract class ReadyWithResult { - - /** - * Write the data e.g. decrypted plaintext to the provided output stream and return the result of the - * processing operation. - * - * @param outputStream output stream - * @return result, eg. signatures - * - * @throws IOException in case of an IO error - * @throws SOPGPException.NoSignature if there are no valid signatures found - */ - public abstract T writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature; - - /** - * Return the data as a {@link ByteArrayAndResult}. - * Calling {@link ByteArrayAndResult#getBytes()} will give you access to the data as byte array, while - * {@link ByteArrayAndResult#getResult()} will grant access to the appended result. - * - * @return byte array and result - * @throws IOException in case of an IO error - * @throws SOPGPException.NoSignature if there are no valid signatures found - */ - public ByteArrayAndResult toByteArrayAndResult() throws IOException, SOPGPException.NoSignature { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - T result = writeTo(bytes); - return new ByteArrayAndResult<>(bytes.toByteArray(), result); - } -} diff --git a/sop-java/src/main/java/sop/SOP.java b/sop-java/src/main/java/sop/SOP.java deleted file mode 100644 index 2c2ccf16..00000000 --- a/sop-java/src/main/java/sop/SOP.java +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import sop.operation.Armor; -import sop.operation.Dearmor; -import sop.operation.Decrypt; -import sop.operation.DetachInbandSignatureAndMessage; -import sop.operation.Encrypt; -import sop.operation.ExtractCert; -import sop.operation.GenerateKey; -import sop.operation.Sign; -import sop.operation.Verify; -import sop.operation.Version; - -/** - * Stateless OpenPGP Interface. - */ -public interface SOP { - - /** - * Get information about the implementations name and version. - * - * @return version - */ - Version version(); - - /** - * Generate a secret key. - * Customize the operation using the builder {@link GenerateKey}. - * - * @return builder instance - */ - GenerateKey generateKey(); - - /** - * Extract a certificate (public key) from a secret key. - * Customize the operation using the builder {@link ExtractCert}. - * - * @return builder instance - */ - ExtractCert extractCert(); - - /** - * Create detached signatures. - * Customize the operation using the builder {@link Sign}. - * - * @return builder instance - */ - Sign sign(); - - /** - * Verify detached signatures. - * Customize the operation using the builder {@link Verify}. - * - * @return builder instance - */ - Verify verify(); - - /** - * Encrypt a message. - * Customize the operation using the builder {@link Encrypt}. - * - * @return builder instance - */ - Encrypt encrypt(); - - /** - * Decrypt a message. - * Customize the operation using the builder {@link Decrypt}. - * - * @return builder instance - */ - Decrypt decrypt(); - - /** - * Convert binary OpenPGP data to ASCII. - * Customize the operation using the builder {@link Armor}. - * - * @return builder instance - */ - Armor armor(); - - /** - * Converts ASCII armored OpenPGP data to binary. - * Customize the operation using the builder {@link Dearmor}. - * - * @return builder instance - */ - Dearmor dearmor(); - - DetachInbandSignatureAndMessage detachInbandSignatureAndMessage(); -} diff --git a/sop-java/src/main/java/sop/SessionKey.java b/sop-java/src/main/java/sop/SessionKey.java deleted file mode 100644 index 2adcec4d..00000000 --- a/sop-java/src/main/java/sop/SessionKey.java +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.util.Arrays; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import sop.util.HexUtil; - -public class SessionKey { - - private static final Pattern PATTERN = Pattern.compile("^(\\d):([0-9a-fA-F]+)$"); - - private final byte algorithm; - private final byte[] sessionKey; - - public SessionKey(byte algorithm, byte[] sessionKey) { - this.algorithm = algorithm; - this.sessionKey = sessionKey; - } - - /** - * Return the symmetric algorithm octet. - * - * @return algorithm id - */ - public byte getAlgorithm() { - return algorithm; - } - - /** - * Return the session key. - * - * @return session key - */ - public byte[] getKey() { - return sessionKey; - } - - @Override - public int hashCode() { - return getAlgorithm() * 17 + Arrays.hashCode(getKey()); - } - - @Override - public boolean equals(Object other) { - if (other == null) { - return false; - } - if (this == other) { - return true; - } - if (!(other instanceof SessionKey)) { - return false; - } - - SessionKey otherKey = (SessionKey) other; - return getAlgorithm() == otherKey.getAlgorithm() && Arrays.equals(getKey(), otherKey.getKey()); - } - - public static SessionKey fromString(String string) { - Matcher matcher = PATTERN.matcher(string); - if (!matcher.matches()) { - throw new IllegalArgumentException("Provided session key does not match expected format."); - } - byte algorithm = Byte.parseByte(matcher.group(1)); - String key = matcher.group(2); - - return new SessionKey(algorithm, HexUtil.hexToBytes(key)); - } - - @Override - public String toString() { - return "" + (int) getAlgorithm() + ':' + HexUtil.bytesToHex(sessionKey); - } -} diff --git a/sop-java/src/main/java/sop/Signatures.java b/sop-java/src/main/java/sop/Signatures.java deleted file mode 100644 index dd3f000d..00000000 --- a/sop-java/src/main/java/sop/Signatures.java +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.io.IOException; -import java.io.OutputStream; - -public abstract class Signatures extends Ready { - - /** - * Write OpenPGP signatures to the provided output stream. - * - * @param signatureOutputStream output stream - * @throws IOException in case of an IO error - */ - @Override - public abstract void writeTo(OutputStream signatureOutputStream) throws IOException; - -} diff --git a/sop-java/src/main/java/sop/SigningResult.java b/sop-java/src/main/java/sop/SigningResult.java deleted file mode 100644 index 2cb142dc..00000000 --- a/sop-java/src/main/java/sop/SigningResult.java +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -/** - * This class contains various information about a signed message. - */ -public final class SigningResult { - - private final MicAlg micAlg; - - private SigningResult(MicAlg micAlg) { - this.micAlg = micAlg; - } - - /** - * Return a string identifying the digest mechanism used to create the signed message. - * This is useful for setting the micalg= parameter for the multipart/signed - * content type of a PGP/MIME object as described in section 5 of [RFC3156]. - * - * If more than one signature was generated and different digest mechanisms were used, - * the value of the micalg object is an empty string. - * - * @return micalg - */ - public MicAlg getMicAlg() { - return micAlg; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private MicAlg micAlg; - - public Builder setMicAlg(MicAlg micAlg) { - this.micAlg = micAlg; - return this; - } - - public SigningResult build() { - SigningResult signingResult = new SigningResult(micAlg); - return signingResult; - } - } -} diff --git a/sop-java/src/main/java/sop/Verification.java b/sop-java/src/main/java/sop/Verification.java deleted file mode 100644 index 2047c3d4..00000000 --- a/sop-java/src/main/java/sop/Verification.java +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop; - -import java.util.Date; - -import sop.util.UTCUtil; - -public class Verification { - - private final Date creationTime; - private final String signingKeyFingerprint; - private final String signingCertFingerprint; - - public Verification(Date creationTime, String signingKeyFingerprint, String signingCertFingerprint) { - this.creationTime = creationTime; - this.signingKeyFingerprint = signingKeyFingerprint; - this.signingCertFingerprint = signingCertFingerprint; - } - - /** - * Return the signatures' creation time. - * - * @return signature creation time - */ - public Date getCreationTime() { - return creationTime; - } - - /** - * Return the fingerprint of the signing (sub)key. - * - * @return signing key fingerprint - */ - public String getSigningKeyFingerprint() { - return signingKeyFingerprint; - } - - /** - * Return the fingerprint fo the signing certificate. - * - * @return signing certificate fingerprint - */ - public String getSigningCertFingerprint() { - return signingCertFingerprint; - } - - @Override - public String toString() { - return UTCUtil.formatUTCDate(getCreationTime()) + - ' ' + - getSigningKeyFingerprint() + - ' ' + - getSigningCertFingerprint(); - } -} diff --git a/sop-java/src/main/java/sop/enums/ArmorLabel.java b/sop-java/src/main/java/sop/enums/ArmorLabel.java deleted file mode 100644 index aeaa6f9b..00000000 --- a/sop-java/src/main/java/sop/enums/ArmorLabel.java +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.enums; - -public enum ArmorLabel { - Auto, - Sig, - Key, - Cert, - Message -} diff --git a/sop-java/src/main/java/sop/enums/EncryptAs.java b/sop-java/src/main/java/sop/enums/EncryptAs.java deleted file mode 100644 index 2de6792b..00000000 --- a/sop-java/src/main/java/sop/enums/EncryptAs.java +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.enums; - -public enum EncryptAs { - Binary, - Text, - MIME -} diff --git a/sop-java/src/main/java/sop/enums/SignAs.java b/sop-java/src/main/java/sop/enums/SignAs.java deleted file mode 100644 index fcd79f4d..00000000 --- a/sop-java/src/main/java/sop/enums/SignAs.java +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.enums; - -public enum SignAs { - Binary, - Text -} diff --git a/sop-java/src/main/java/sop/enums/package-info.java b/sop-java/src/main/java/sop/enums/package-info.java deleted file mode 100644 index 67148d3e..00000000 --- a/sop-java/src/main/java/sop/enums/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Stateless OpenPGP Interface for Java. - * Enumerations. - */ -package sop.enums; diff --git a/sop-java/src/main/java/sop/exception/SOPGPException.java b/sop-java/src/main/java/sop/exception/SOPGPException.java deleted file mode 100644 index 6b844f59..00000000 --- a/sop-java/src/main/java/sop/exception/SOPGPException.java +++ /dev/null @@ -1,316 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.exception; - -public abstract class SOPGPException extends RuntimeException { - - public SOPGPException() { - super(); - } - - public SOPGPException(String message) { - super(message); - } - - public SOPGPException(Throwable e) { - super(e); - } - - public SOPGPException(String message, Throwable cause) { - super(message, cause); - } - - public abstract int getExitCode(); - - /** - * No acceptable signatures found (sop verify). - */ - public static class NoSignature extends SOPGPException { - - public static final int EXIT_CODE = 3; - - public NoSignature() { - super("No verifiable signature found."); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Asymmetric algorithm unsupported (sop encrypt). - */ - public static class UnsupportedAsymmetricAlgo extends SOPGPException { - - public static final int EXIT_CODE = 13; - - public UnsupportedAsymmetricAlgo(String message, Throwable e) { - super(message, e); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Certificate not encryption capable (e,g, expired, revoked, unacceptable usage). - */ - public static class CertCannotEncrypt extends SOPGPException { - public static final int EXIT_CODE = 17; - - public CertCannotEncrypt(String message, Throwable cause) { - super(message, cause); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Missing required argument. - */ - public static class MissingArg extends SOPGPException { - - public static final int EXIT_CODE = 19; - - public MissingArg(String s) { - super(s); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Incomplete verification instructions (sop decrypt). - */ - public static class IncompleteVerification extends SOPGPException { - - public static final int EXIT_CODE = 23; - - public IncompleteVerification(String message) { - super(message); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Unable to decrypt (sop decrypt). - */ - public static class CannotDecrypt extends SOPGPException { - - public static final int EXIT_CODE = 29; - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Non-UTF-8 or otherwise unreliable password (sop encrypt). - */ - public static class PasswordNotHumanReadable extends SOPGPException { - - public static final int EXIT_CODE = 31; - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Unsupported option. - */ - public static class UnsupportedOption extends SOPGPException { - - public static final int EXIT_CODE = 37; - - public UnsupportedOption(String message) { - super(message); - } - - public UnsupportedOption(String message, Throwable cause) { - super(message, cause); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Invalid data type (no secret key where KEYS expected, etc.). - */ - public static class BadData extends SOPGPException { - - public static final int EXIT_CODE = 41; - - public BadData(Throwable e) { - super(e); - } - - public BadData(String message, BadData badData) { - super(message, badData); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Non-Text input where text expected. - */ - public static class ExpectedText extends SOPGPException { - - public static final int EXIT_CODE = 53; - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Output file already exists. - */ - public static class OutputExists extends SOPGPException { - - public static final int EXIT_CODE = 59; - - public OutputExists(String message) { - super(message); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Input file does not exist. - */ - public static class MissingInput extends SOPGPException { - - public static final int EXIT_CODE = 61; - - public MissingInput(String message, Throwable cause) { - super(message, cause); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * A KEYS input is protected (locked) with a password, and sop cannot unlock it. - */ - public static class KeyIsProtected extends SOPGPException { - - public static final int EXIT_CODE = 67; - - public KeyIsProtected() { - super(); - } - - public KeyIsProtected(String message, Throwable cause) { - super(message, cause); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Unsupported subcommand. - */ - public static class UnsupportedSubcommand extends SOPGPException { - - public static final int EXIT_CODE = 69; - - public UnsupportedSubcommand(String message) { - super(message); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * An indirect parameter is a special designator (it starts with @), but sop does not know how to handle the prefix. - */ - public static class UnsupportedSpecialPrefix extends SOPGPException { - - public static final int EXIT_CODE = 71; - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * A indirect input parameter is a special designator (it starts with @), - * and a filename matching the designator is actually present. - */ - public static class AmbiguousInput extends SOPGPException { - - public static final int EXIT_CODE = 73; - - public AmbiguousInput(String message) { - super(message); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } - - /** - * Key not signature-capable (e.g. expired, revoked, unacceptable usage flags) - * (sop sign and sop encrypt with --sign-with). - */ - public static class KeyCannotSign extends SOPGPException { - - public static final int EXIT_CODE = 79; - - public KeyCannotSign() { - super(); - } - - public KeyCannotSign(String s, KeyCannotSign keyCannotSign) { - super(s, keyCannotSign); - } - - @Override - public int getExitCode() { - return EXIT_CODE; - } - } -} diff --git a/sop-java/src/main/java/sop/exception/package-info.java b/sop-java/src/main/java/sop/exception/package-info.java deleted file mode 100644 index 4abc562b..00000000 --- a/sop-java/src/main/java/sop/exception/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Stateless OpenPGP Interface for Java. - * Exception classes. - */ -package sop.exception; diff --git a/sop-java/src/main/java/sop/operation/Armor.java b/sop-java/src/main/java/sop/operation/Armor.java deleted file mode 100644 index dea3257a..00000000 --- a/sop-java/src/main/java/sop/operation/Armor.java +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import sop.Ready; -import sop.enums.ArmorLabel; -import sop.exception.SOPGPException; - -public interface Armor { - - /** - * Overrides automatic detection of label. - * - * @param label armor label - * @return builder instance - */ - Armor label(ArmorLabel label) throws SOPGPException.UnsupportedOption; - - /** - * Armor the provided data. - * - * @param data input stream of unarmored OpenPGP data - * @return armored data - */ - Ready data(InputStream data) throws SOPGPException.BadData; - - /** - * Armor the provided data. - * - * @param data unarmored OpenPGP data - * @return armored data - */ - default Ready data(byte[] data) throws SOPGPException.BadData { - return data(new ByteArrayInputStream(data)); - } -} diff --git a/sop-java/src/main/java/sop/operation/Dearmor.java b/sop-java/src/main/java/sop/operation/Dearmor.java deleted file mode 100644 index 35eceb56..00000000 --- a/sop-java/src/main/java/sop/operation/Dearmor.java +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import sop.Ready; -import sop.exception.SOPGPException; - -public interface Dearmor { - - /** - * Dearmor armored OpenPGP data. - * - * @param data armored OpenPGP data - * @return input stream of unarmored data - */ - Ready data(InputStream data) throws SOPGPException.BadData, IOException; - - /** - * Dearmor armored OpenPGP data. - * - * @param data armored OpenPGP data - * @return input stream of unarmored data - */ - default Ready data(byte[] data) throws SOPGPException.BadData, IOException { - return data(new ByteArrayInputStream(data)); - } -} diff --git a/sop-java/src/main/java/sop/operation/Decrypt.java b/sop-java/src/main/java/sop/operation/Decrypt.java deleted file mode 100644 index 0811ac2d..00000000 --- a/sop-java/src/main/java/sop/operation/Decrypt.java +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Date; - -import sop.DecryptionResult; -import sop.ReadyWithResult; -import sop.SessionKey; -import sop.exception.SOPGPException; - -public interface Decrypt { - - /** - * Makes the SOP consider signatures before this date invalid. - * - * @param timestamp timestamp - * @return builder instance - */ - Decrypt verifyNotBefore(Date timestamp) - throws SOPGPException.UnsupportedOption; - - /** - * Makes the SOP consider signatures after this date invalid. - * - * @param timestamp timestamp - * @return builder instance - */ - Decrypt verifyNotAfter(Date timestamp) - throws SOPGPException.UnsupportedOption; - - /** - * Adds one or more verification cert. - * - * @param cert input stream containing the cert(s) - * @return builder instance - */ - Decrypt verifyWithCert(InputStream cert) - throws SOPGPException.BadData, - IOException; - - /** - * Adds one or more verification cert. - * - * @param cert byte array containing the cert(s) - * @return builder instance - */ - default Decrypt verifyWithCert(byte[] cert) - throws SOPGPException.BadData, IOException { - return verifyWithCert(new ByteArrayInputStream(cert)); - } - - /** - * Tries to decrypt with the given session key. - * - * @param sessionKey session key - * @return builder instance - */ - Decrypt withSessionKey(SessionKey sessionKey) - throws SOPGPException.UnsupportedOption; - - /** - * Tries to decrypt with the given password. - * - * @param password password - * @return builder instance - */ - Decrypt withPassword(String password) - throws SOPGPException.PasswordNotHumanReadable, - SOPGPException.UnsupportedOption; - - /** - * Adds one or more decryption key. - * - * @param key input stream containing the key(s) - * @return builder instance - */ - Decrypt withKey(InputStream key) - throws SOPGPException.KeyIsProtected, - SOPGPException.BadData, - SOPGPException.UnsupportedAsymmetricAlgo; - - /** - * Adds one or more decryption key. - * - * @param key byte array containing the key(s) - * @return builder instance - */ - default Decrypt withKey(byte[] key) - throws SOPGPException.KeyIsProtected, - SOPGPException.BadData, - SOPGPException.UnsupportedAsymmetricAlgo { - return withKey(new ByteArrayInputStream(key)); - } - - /** - * Decrypts the given ciphertext, returning verification results and plaintext. - * @param ciphertext ciphertext - * @return ready with result - */ - ReadyWithResult ciphertext(InputStream ciphertext) - throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt; - - /** - * Decrypts the given ciphertext, returning verification results and plaintext. - * @param ciphertext ciphertext - * @return ready with result - */ - default ReadyWithResult ciphertext(byte[] ciphertext) - throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt { - return ciphertext(new ByteArrayInputStream(ciphertext)); - } -} diff --git a/sop-java/src/main/java/sop/operation/DetachInbandSignatureAndMessage.java b/sop-java/src/main/java/sop/operation/DetachInbandSignatureAndMessage.java deleted file mode 100644 index 9e22258c..00000000 --- a/sop-java/src/main/java/sop/operation/DetachInbandSignatureAndMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import sop.ReadyWithResult; -import sop.Signatures; - -/** - * Split cleartext signed messages up into data and signatures. - */ -public interface DetachInbandSignatureAndMessage { - - /** - * Do not wrap the signatures in ASCII armor. - * @return builder - */ - DetachInbandSignatureAndMessage noArmor(); - - /** - * Detach the provided cleartext signed message from its signatures. - * - * @param messageInputStream input stream containing the signed message - * @return result containing the detached message - * @throws IOException in case of an IO error - */ - ReadyWithResult message(InputStream messageInputStream) throws IOException; - - /** - * Detach the provided cleartext signed message from its signatures. - * - * @param message byte array containing the signed message - * @return result containing the detached message - * @throws IOException in case of an IO error - */ - default ReadyWithResult message(byte[] message) throws IOException { - return message(new ByteArrayInputStream(message)); - } -} diff --git a/sop-java/src/main/java/sop/operation/Encrypt.java b/sop-java/src/main/java/sop/operation/Encrypt.java deleted file mode 100644 index 784c07a0..00000000 --- a/sop-java/src/main/java/sop/operation/Encrypt.java +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import sop.Ready; -import sop.enums.EncryptAs; -import sop.exception.SOPGPException; - -public interface Encrypt { - - /** - * Disable ASCII armor encoding. - * - * @return builder instance - */ - Encrypt noArmor(); - - /** - * Sets encryption mode. - * - * @param mode mode - * @return builder instance - */ - Encrypt mode(EncryptAs mode) - throws SOPGPException.UnsupportedOption; - - /** - * Adds the signer key. - * - * @param key input stream containing the encoded signer key - * @return builder instance - */ - Encrypt signWith(InputStream key) - throws SOPGPException.KeyIsProtected, - SOPGPException.KeyCannotSign, - SOPGPException.UnsupportedAsymmetricAlgo, - SOPGPException.BadData; - - /** - * Adds the signer key. - * - * @param key byte array containing the encoded signer key - * @return builder instance - */ - default Encrypt signWith(byte[] key) - throws SOPGPException.KeyIsProtected, - SOPGPException.KeyCannotSign, - SOPGPException.UnsupportedAsymmetricAlgo, - SOPGPException.BadData { - return signWith(new ByteArrayInputStream(key)); - } - - /** - * Encrypt with the given password. - * - * @param password password - * @return builder instance - */ - Encrypt withPassword(String password) - throws SOPGPException.PasswordNotHumanReadable, - SOPGPException.UnsupportedOption; - - /** - * Encrypt with the given cert. - * - * @param cert input stream containing the encoded cert. - * @return builder instance - */ - Encrypt withCert(InputStream cert) - throws SOPGPException.CertCannotEncrypt, - SOPGPException.UnsupportedAsymmetricAlgo, - SOPGPException.BadData; - - /** - * Encrypt with the given cert. - * - * @param cert byte array containing the encoded cert. - * @return builder instance - */ - default Encrypt withCert(byte[] cert) - throws SOPGPException.CertCannotEncrypt, - SOPGPException.UnsupportedAsymmetricAlgo, - SOPGPException.BadData { - return withCert(new ByteArrayInputStream(cert)); - } - - /** - * Encrypt the given data yielding the ciphertext. - * @param plaintext plaintext - * @return input stream containing the ciphertext - */ - Ready plaintext(InputStream plaintext) - throws IOException; - - /** - * Encrypt the given data yielding the ciphertext. - * @param plaintext plaintext - * @return input stream containing the ciphertext - */ - default Ready plaintext(byte[] plaintext) throws IOException { - return plaintext(new ByteArrayInputStream(plaintext)); - } -} diff --git a/sop-java/src/main/java/sop/operation/ExtractCert.java b/sop-java/src/main/java/sop/operation/ExtractCert.java deleted file mode 100644 index 32491111..00000000 --- a/sop-java/src/main/java/sop/operation/ExtractCert.java +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import sop.Ready; -import sop.exception.SOPGPException; - -public interface ExtractCert { - - /** - * Disable ASCII armor encoding. - * - * @return builder instance - */ - ExtractCert noArmor(); - - /** - * Extract the cert(s) from the provided key(s). - * - * @param keyInputStream input stream containing the encoding of one or more OpenPGP keys - * @return result containing the encoding of the keys certs - */ - Ready key(InputStream keyInputStream) throws IOException, SOPGPException.BadData; - - /** - * Extract the cert(s) from the provided key(s). - * - * @param key byte array containing the encoding of one or more OpenPGP key - * @return result containing the encoding of the keys certs - */ - default Ready key(byte[] key) throws IOException, SOPGPException.BadData { - return key(new ByteArrayInputStream(key)); - } -} diff --git a/sop-java/src/main/java/sop/operation/GenerateKey.java b/sop-java/src/main/java/sop/operation/GenerateKey.java deleted file mode 100644 index c652e84a..00000000 --- a/sop-java/src/main/java/sop/operation/GenerateKey.java +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.IOException; -import java.io.InputStream; - -import sop.Ready; -import sop.exception.SOPGPException; - -public interface GenerateKey { - - /** - * Disable ASCII armor encoding. - * - * @return builder instance - */ - GenerateKey noArmor(); - - /** - * Adds a user-id. - * - * @param userId user-id - * @return builder instance - */ - GenerateKey userId(String userId); - - /** - * Generate the OpenPGP key and return it encoded as an {@link InputStream}. - * - * @return key - */ - Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo, IOException; -} diff --git a/sop-java/src/main/java/sop/operation/Sign.java b/sop-java/src/main/java/sop/operation/Sign.java deleted file mode 100644 index be518cde..00000000 --- a/sop-java/src/main/java/sop/operation/Sign.java +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import sop.ReadyWithResult; -import sop.SigningResult; -import sop.enums.SignAs; -import sop.exception.SOPGPException; - -public interface Sign { - - /** - * Disable ASCII armor encoding. - * - * @return builder instance - */ - Sign noArmor(); - - /** - * Sets the signature mode. - * Note: This method has to be called before {@link #key(InputStream)} is called. - * - * @param mode signature mode - * @return builder instance - */ - Sign mode(SignAs mode) throws SOPGPException.UnsupportedOption; - - /** - * Add one or more signing keys. - * - * @param key input stream containing encoded keys - * @return builder instance - */ - Sign key(InputStream key) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException; - - /** - * Add one or more signing keys. - * - * @param key byte array containing encoded keys - * @return builder instance - */ - default Sign key(byte[] key) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException { - return key(new ByteArrayInputStream(key)); - } - - /** - * Signs data. - * - * @param data input stream containing data - * @return ready - */ - ReadyWithResult data(InputStream data) throws IOException, SOPGPException.ExpectedText; - - /** - * Signs data. - * - * @param data byte array containing data - * @return ready - */ - default ReadyWithResult data(byte[] data) throws IOException, SOPGPException.ExpectedText { - return data(new ByteArrayInputStream(data)); - } -} diff --git a/sop-java/src/main/java/sop/operation/Verify.java b/sop-java/src/main/java/sop/operation/Verify.java deleted file mode 100644 index 1bf9fe09..00000000 --- a/sop-java/src/main/java/sop/operation/Verify.java +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.Date; - -import sop.exception.SOPGPException; - -public interface Verify extends VerifySignatures { - - /** - * Makes the SOP implementation consider signatures before this date invalid. - * - * @param timestamp timestamp - * @return builder instance - */ - Verify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption; - - /** - * Makes the SOP implementation consider signatures after this date invalid. - * - * @param timestamp timestamp - * @return builder instance - */ - Verify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption; - - /** - * Add one or more verification cert. - * - * @param cert input stream containing the encoded certs - * @return builder instance - */ - Verify cert(InputStream cert) throws SOPGPException.BadData; - - /** - * Add one or more verification cert. - * - * @param cert byte array containing the encoded certs - * @return builder instance - */ - default Verify cert(byte[] cert) throws SOPGPException.BadData { - return cert(new ByteArrayInputStream(cert)); - } - - /** - * Provides the signatures. - * @param signatures input stream containing encoded, detached signatures. - * - * @return builder instance - */ - VerifySignatures signatures(InputStream signatures) throws SOPGPException.BadData; - - /** - * Provides the signatures. - * @param signatures byte array containing encoded, detached signatures. - * - * @return builder instance - */ - default VerifySignatures signatures(byte[] signatures) throws SOPGPException.BadData { - return signatures(new ByteArrayInputStream(signatures)); - } - -} diff --git a/sop-java/src/main/java/sop/operation/VerifySignatures.java b/sop-java/src/main/java/sop/operation/VerifySignatures.java deleted file mode 100644 index d41a8edd..00000000 --- a/sop-java/src/main/java/sop/operation/VerifySignatures.java +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - -import sop.Verification; -import sop.exception.SOPGPException; - -public interface VerifySignatures { - - /** - * Provide the signed data (without signatures). - * - * @param data signed data - * @return list of signature verifications - * @throws IOException in case of an IO error - * @throws SOPGPException.NoSignature when no signature is found - * @throws SOPGPException.BadData when the data is invalid OpenPGP data - */ - List data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData; - - /** - * Provide the signed data (without signatures). - * - * @param data signed data - * @return list of signature verifications - * @throws IOException in case of an IO error - * @throws SOPGPException.NoSignature when no signature is found - * @throws SOPGPException.BadData when the data is invalid OpenPGP data - */ - default List data(byte[] data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData { - return data(new ByteArrayInputStream(data)); - } -} diff --git a/sop-java/src/main/java/sop/operation/Version.java b/sop-java/src/main/java/sop/operation/Version.java deleted file mode 100644 index 0b50993f..00000000 --- a/sop-java/src/main/java/sop/operation/Version.java +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.operation; - -public interface Version { - - /** - * Return the implementations name. - * e.g. "SOP", - * - * @return implementation name - */ - String getName(); - - /** - * Return the implementations short version string. - * e.g. "1.0" - * - * @return version string - */ - String getVersion(); - - /** - * Return version information about the used OpenPGP backend. - * e.g. "Bouncycastle 1.70" - * - * @return backend version string - */ - String getBackendVersion(); - - /** - * Return an extended version string containing multiple lines of version information. - * The first line MUST match the information produced by {@link #getName()} and {@link #getVersion()}, but the rest of the text - * has no defined structure. - * Example: - *
-     *     "SOP 1.0
-     *     Awesome PGP!
-     *     Using Bouncycastle 1.70
-     *     LibFoo 1.2.2
-     *     See https://pgp.example.org/sop/ for more information"
-     * 
- * - * @return extended version string - */ - String getExtendedVersion(); -} diff --git a/sop-java/src/main/java/sop/operation/package-info.java b/sop-java/src/main/java/sop/operation/package-info.java deleted file mode 100644 index dde4d5bb..00000000 --- a/sop-java/src/main/java/sop/operation/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Stateless OpenPGP Interface for Java. - * Different cryptographic operations. - */ -package sop.operation; diff --git a/sop-java/src/main/java/sop/package-info.java b/sop-java/src/main/java/sop/package-info.java deleted file mode 100644 index 5ad4f528..00000000 --- a/sop-java/src/main/java/sop/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Stateless OpenPGP Interface for Java. - */ -package sop; diff --git a/sop-java/src/main/java/sop/util/HexUtil.java b/sop-java/src/main/java/sop/util/HexUtil.java deleted file mode 100644 index 9b88f53d..00000000 --- a/sop-java/src/main/java/sop/util/HexUtil.java +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021 Paul Schaub, @maybeWeCouldStealAVan, @Dave L. -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -public class HexUtil { - - private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); - - /** - * Encode a byte array to a hex string. - * - * @see - * How to convert a byte array to a hex string in Java? - * @param bytes bytes - * @return hex encoding - */ - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = HEX_ARRAY[v >>> 4]; - hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; - } - return new String(hexChars); - } - - /** - * Decode a hex string into a byte array. - * - * @see - * Convert a string representation of a hex dump to a byte array using Java? - * @param s hex string - * @return decoded byte array - */ - public static byte[] hexToBytes(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i + 1), 16)); - } - return data; - } -} diff --git a/sop-java/src/main/java/sop/util/Optional.java b/sop-java/src/main/java/sop/util/Optional.java deleted file mode 100644 index 00eb2012..00000000 --- a/sop-java/src/main/java/sop/util/Optional.java +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -/** - * Backport of java.util.Optional for older Android versions. - * - * @param item type - */ -public class Optional { - - private final T item; - - public Optional() { - this(null); - } - - public Optional(T item) { - this.item = item; - } - - public static Optional of(T item) { - if (item == null) { - throw new NullPointerException("Item cannot be null."); - } - return new Optional<>(item); - } - - public static Optional ofNullable(T item) { - return new Optional<>(item); - } - - public static Optional ofEmpty() { - return new Optional<>(null); - } - - public T get() { - return item; - } - - public boolean isPresent() { - return item != null; - } - - public boolean isEmpty() { - return item == null; - } -} diff --git a/sop-java/src/main/java/sop/util/ProxyOutputStream.java b/sop-java/src/main/java/sop/util/ProxyOutputStream.java deleted file mode 100644 index 0559e8f4..00000000 --- a/sop-java/src/main/java/sop/util/ProxyOutputStream.java +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * {@link OutputStream} that buffers data being written into it, until its underlying output stream is being replaced. - * At that point, first all the buffered data is being written to the underlying stream, followed by any successive - * data that may get written to the {@link ProxyOutputStream}. - * - * This class is useful if we need to provide an {@link OutputStream} at one point in time when the final - * target output stream is not yet known. - */ -public class ProxyOutputStream extends OutputStream { - - private final ByteArrayOutputStream buffer; - private OutputStream swapped; - - public ProxyOutputStream() { - this.buffer = new ByteArrayOutputStream(); - } - - public synchronized void replaceOutputStream(OutputStream underlying) throws IOException { - if (underlying == null) { - throw new NullPointerException("Underlying OutputStream cannot be null."); - } - this.swapped = underlying; - - byte[] bufferBytes = buffer.toByteArray(); - swapped.write(bufferBytes); - } - - @Override - public synchronized void write(byte[] b) throws IOException { - if (swapped == null) { - buffer.write(b); - } else { - swapped.write(b); - } - } - - @Override - public synchronized void write(byte[] b, int off, int len) throws IOException { - if (swapped == null) { - buffer.write(b, off, len); - } else { - swapped.write(b, off, len); - } - } - - @Override - public synchronized void flush() throws IOException { - buffer.flush(); - if (swapped != null) { - swapped.flush(); - } - } - - @Override - public synchronized void close() throws IOException { - buffer.close(); - if (swapped != null) { - swapped.close(); - } - } - - @Override - public synchronized void write(int i) throws IOException { - if (swapped == null) { - buffer.write(i); - } else { - swapped.write(i); - } - } -} diff --git a/sop-java/src/main/java/sop/util/UTCUtil.java b/sop-java/src/main/java/sop/util/UTCUtil.java deleted file mode 100644 index 8ef7e773..00000000 --- a/sop-java/src/main/java/sop/util/UTCUtil.java +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; - -/** - * Utility class to parse and format dates as ISO-8601 UTC timestamps. - */ -public class UTCUtil { - - public static final SimpleDateFormat UTC_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - public static final SimpleDateFormat[] UTC_PARSERS = new SimpleDateFormat[] { - UTC_FORMATTER, - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"), - new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"), - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'") - }; - - static { - for (SimpleDateFormat f : UTC_PARSERS) { - f.setTimeZone(TimeZone.getTimeZone("UTC")); - } - } - /** - * Parse an ISO-8601 UTC timestamp from a string. - * - * @param dateString string - * @return date - */ - public static Date parseUTCDate(String dateString) { - for (SimpleDateFormat parser : UTC_PARSERS) { - try { - return parser.parse(dateString); - } catch (ParseException e) { - // Try next parser - } - } - return null; - } - - /** - * Format a date as ISO-8601 UTC timestamp. - * - * @param date date - * @return timestamp string - */ - public static String formatUTCDate(Date date) { - return UTC_FORMATTER.format(date); - } -} diff --git a/sop-java/src/main/java/sop/util/package-info.java b/sop-java/src/main/java/sop/util/package-info.java deleted file mode 100644 index 3dd9fc19..00000000 --- a/sop-java/src/main/java/sop/util/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Utility classes. - */ -package sop.util; diff --git a/sop-java/src/test/java/sop/util/ByteArrayAndResultTest.java b/sop-java/src/test/java/sop/util/ByteArrayAndResultTest.java deleted file mode 100644 index 8ae1859f..00000000 --- a/sop-java/src/test/java/sop/util/ByteArrayAndResultTest.java +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; -import sop.ByteArrayAndResult; -import sop.Verification; - -public class ByteArrayAndResultTest { - - @Test - public void testCreationAndGetters() { - byte[] bytes = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); - List result = Collections.singletonList( - new Verification(UTCUtil.parseUTCDate("2019-10-24T23:48:29Z"), - "C90E6D36200A1B922A1509E77618196529AE5FF8", - "C4BC2DDB38CCE96485EBE9C2F20691179038E5C6") - ); - ByteArrayAndResult> bytesAndResult = new ByteArrayAndResult<>(bytes, result); - - assertArrayEquals(bytes, bytesAndResult.getBytes()); - assertEquals(result, bytesAndResult.getResult()); - } -} diff --git a/sop-java/src/test/java/sop/util/HexUtilTest.java b/sop-java/src/test/java/sop/util/HexUtilTest.java deleted file mode 100644 index 54fc21de..00000000 --- a/sop-java/src/test/java/sop/util/HexUtilTest.java +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.nio.charset.Charset; - -import org.junit.jupiter.api.Test; - -/** - * Test using some test vectors from RFC4648. - * - * @see RFC-4648 §10: Test Vectors - */ -public class HexUtilTest { - - @SuppressWarnings("CharsetObjectCanBeUsed") - private static final Charset ASCII = Charset.forName("US-ASCII"); - - @Test - public void emptyHexEncodeTest() { - assertHexEquals("", ""); - } - - @Test - public void encodeF() { - assertHexEquals("66", "f"); - } - - @Test - public void encodeFo() { - assertHexEquals("666F", "fo"); - } - - @Test - public void encodeFoo() { - assertHexEquals("666F6F", "foo"); - } - - @Test - public void encodeFoob() { - assertHexEquals("666F6F62", "foob"); - } - - @Test - public void encodeFooba() { - assertHexEquals("666F6F6261", "fooba"); - } - - @Test - public void encodeFoobar() { - assertHexEquals("666F6F626172", "foobar"); - } - - private void assertHexEquals(String hex, String ascii) { - assertEquals(hex, HexUtil.bytesToHex(ascii.getBytes(ASCII))); - assertArrayEquals(ascii.getBytes(ASCII), HexUtil.hexToBytes(hex)); - } -} diff --git a/sop-java/src/test/java/sop/util/MicAlgTest.java b/sop-java/src/test/java/sop/util/MicAlgTest.java deleted file mode 100644 index f720c85b..00000000 --- a/sop-java/src/test/java/sop/util/MicAlgTest.java +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; -import sop.MicAlg; - -public class MicAlgTest { - - @Test - public void constructorNullArgThrows() { - assertThrows(IllegalArgumentException.class, () -> new MicAlg(null)); - } - - @Test - public void emptyMicAlgIsEmptyString() { - MicAlg empty = MicAlg.empty(); - assertNotNull(empty.getMicAlg()); - assertTrue(empty.getMicAlg().isEmpty()); - } - - @Test - public void fromInvalidAlgorithmIdThrows() { - assertThrows(IllegalArgumentException.class, () -> MicAlg.fromHashAlgorithmId(-1)); - } - - @Test - public void fromHashAlgorithmIdsKnownAlgsMatch() { - Map knownAlgorithmMicalgs = new HashMap<>(); - knownAlgorithmMicalgs.put(1, "pgp-md5"); - knownAlgorithmMicalgs.put(2, "pgp-sha1"); - knownAlgorithmMicalgs.put(3, "pgp-ripemd160"); - knownAlgorithmMicalgs.put(8, "pgp-sha256"); - knownAlgorithmMicalgs.put(9, "pgp-sha384"); - knownAlgorithmMicalgs.put(10, "pgp-sha512"); - knownAlgorithmMicalgs.put(11, "pgp-sha224"); - - for (Integer id : knownAlgorithmMicalgs.keySet()) { - MicAlg micAlg = MicAlg.fromHashAlgorithmId(id); - assertEquals(knownAlgorithmMicalgs.get(id), micAlg.getMicAlg()); - } - } -} diff --git a/sop-java/src/test/java/sop/util/OptionalTest.java b/sop-java/src/test/java/sop/util/OptionalTest.java deleted file mode 100644 index 45900b73..00000000 --- a/sop-java/src/test/java/sop/util/OptionalTest.java +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -public class OptionalTest { - - @Test - public void testEmpty() { - Optional optional = new Optional<>(); - assertEmpty(optional); - } - - @Test - public void testArg() { - String string = "foo"; - Optional optional = new Optional<>(string); - assertFalse(optional.isEmpty()); - assertTrue(optional.isPresent()); - assertEquals(string, optional.get()); - } - - @Test - public void testOfEmpty() { - Optional optional = Optional.ofEmpty(); - assertEmpty(optional); - } - - @Test - public void testNullArg() { - Optional optional = new Optional<>(null); - assertEmpty(optional); - } - - @Test - public void testOfWithNullArgThrows() { - assertThrows(NullPointerException.class, () -> Optional.of(null)); - } - - @Test - public void testOf() { - String string = "Hello, World!"; - Optional optional = Optional.of(string); - assertFalse(optional.isEmpty()); - assertTrue(optional.isPresent()); - assertEquals(string, optional.get()); - } - - @Test - public void testOfNullableWithNull() { - Optional optional = Optional.ofNullable(null); - assertEmpty(optional); - } - - @Test - public void testOfNullableWithArg() { - Optional optional = Optional.ofNullable("bar"); - assertEquals("bar", optional.get()); - assertFalse(optional.isEmpty()); - assertTrue(optional.isPresent()); - } - - private void assertEmpty(Optional optional) { - assertTrue(optional.isEmpty()); - assertFalse(optional.isPresent()); - - assertNull(optional.get()); - } -} diff --git a/sop-java/src/test/java/sop/util/ProxyOutputStreamTest.java b/sop-java/src/test/java/sop/util/ProxyOutputStreamTest.java deleted file mode 100644 index 9d99fd4f..00000000 --- a/sop-java/src/test/java/sop/util/ProxyOutputStreamTest.java +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import org.junit.jupiter.api.Test; - -public class ProxyOutputStreamTest { - - @Test - public void replaceOutputStreamThrowsNPEForNull() { - ProxyOutputStream proxy = new ProxyOutputStream(); - assertThrows(NullPointerException.class, () -> proxy.replaceOutputStream(null)); - } - - @Test - public void testSwappingStreamPreservesWrittenBytes() throws IOException { - byte[] firstSection = "Foo\nBar\n".getBytes(StandardCharsets.UTF_8); - byte[] secondSection = "Baz\n".getBytes(StandardCharsets.UTF_8); - - ProxyOutputStream proxy = new ProxyOutputStream(); - proxy.write(firstSection); - - ByteArrayOutputStream swappedStream = new ByteArrayOutputStream(); - proxy.replaceOutputStream(swappedStream); - - proxy.write(secondSection); - proxy.close(); - - assertEquals("Foo\nBar\nBaz\n", swappedStream.toString()); - } -} diff --git a/sop-java/src/test/java/sop/util/ReadyTest.java b/sop-java/src/test/java/sop/util/ReadyTest.java deleted file mode 100644 index 07fa0903..00000000 --- a/sop-java/src/test/java/sop/util/ReadyTest.java +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; - -import org.junit.jupiter.api.Test; -import sop.Ready; - -public class ReadyTest { - - @Test - public void readyTest() throws IOException { - byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); - Ready ready = new Ready() { - @Override - public void writeTo(OutputStream outputStream) throws IOException { - outputStream.write(data); - } - }; - - assertArrayEquals(data, ready.getBytes()); - } -} diff --git a/sop-java/src/test/java/sop/util/ReadyWithResultTest.java b/sop-java/src/test/java/sop/util/ReadyWithResultTest.java deleted file mode 100644 index 97841fa8..00000000 --- a/sop-java/src/test/java/sop/util/ReadyWithResultTest.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; -import sop.ByteArrayAndResult; -import sop.ReadyWithResult; -import sop.Verification; -import sop.exception.SOPGPException; - -public class ReadyWithResultTest { - - @Test - public void testReadyWithResult() throws SOPGPException.NoSignature, IOException { - byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); - List result = Collections.singletonList( - new Verification(UTCUtil.parseUTCDate("2019-10-24T23:48:29Z"), - "C90E6D36200A1B922A1509E77618196529AE5FF8", - "C4BC2DDB38CCE96485EBE9C2F20691179038E5C6") - ); - ReadyWithResult> readyWithResult = new ReadyWithResult>() { - @Override - public List writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature { - outputStream.write(data); - return result; - } - }; - - ByteArrayAndResult> bytesAndResult = readyWithResult.toByteArrayAndResult(); - assertArrayEquals(data, bytesAndResult.getBytes()); - assertEquals(result, bytesAndResult.getResult()); - } -} diff --git a/sop-java/src/test/java/sop/util/SessionKeyTest.java b/sop-java/src/test/java/sop/util/SessionKeyTest.java deleted file mode 100644 index 2891d0d4..00000000 --- a/sop-java/src/test/java/sop/util/SessionKeyTest.java +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import org.junit.jupiter.api.Test; -import sop.SessionKey; - -public class SessionKeyTest { - - @Test - public void fromStringTest() { - String string = "9:FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"; - SessionKey sessionKey = SessionKey.fromString(string); - assertEquals(string, sessionKey.toString()); - } - - @Test - public void toStringTest() { - SessionKey sessionKey = new SessionKey((byte) 9, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD")); - assertEquals("9:FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD", sessionKey.toString()); - } - - @Test - public void equalsTest() { - SessionKey s1 = new SessionKey((byte) 9, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD")); - SessionKey s2 = new SessionKey((byte) 9, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD")); - SessionKey s3 = new SessionKey((byte) 4, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD")); - SessionKey s4 = new SessionKey((byte) 9, HexUtil.hexToBytes("19125CD57392BAB7037C7078359FCA4BEAF687F4025CBF9F7BCD8059CACC14FB")); - SessionKey s5 = new SessionKey((byte) 4, HexUtil.hexToBytes("19125CD57392BAB7037C7078359FCA4BEAF687F4025CBF9F7BCD8059CACC14FB")); - - assertEquals(s1, s1); - assertEquals(s1, s2); - assertEquals(s1.hashCode(), s2.hashCode()); - assertNotEquals(s1, s3); - assertNotEquals(s1.hashCode(), s3.hashCode()); - assertNotEquals(s1, s4); - assertNotEquals(s1.hashCode(), s4.hashCode()); - assertNotEquals(s4, s5); - assertNotEquals(s4.hashCode(), s5.hashCode()); - assertNotEquals(s1, null); - assertNotEquals(s1, "FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"); - } - - @Test - public void fromString_missingAlgorithmIdThrows() { - String missingAlgorithId = "FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"; - assertThrows(IllegalArgumentException.class, () -> SessionKey.fromString(missingAlgorithId)); - } - - @Test - public void fromString_wrongDivider() { - String semicolonDivider = "9;FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"; - assertThrows(IllegalArgumentException.class, () -> SessionKey.fromString(semicolonDivider)); - } -} diff --git a/sop-java/src/test/java/sop/util/SigningResultTest.java b/sop-java/src/test/java/sop/util/SigningResultTest.java deleted file mode 100644 index 0d35cdc1..00000000 --- a/sop-java/src/test/java/sop/util/SigningResultTest.java +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; -import sop.MicAlg; -import sop.SigningResult; - -public class SigningResultTest { - - @Test - public void basicBuilderTest() { - SigningResult result = SigningResult.builder() - .setMicAlg(MicAlg.fromHashAlgorithmId(10)) - .build(); - - assertEquals("pgp-sha512", result.getMicAlg().getMicAlg()); - } -} diff --git a/sop-java/src/test/java/sop/util/UTCUtilTest.java b/sop-java/src/test/java/sop/util/UTCUtilTest.java deleted file mode 100644 index 18de8176..00000000 --- a/sop-java/src/test/java/sop/util/UTCUtilTest.java +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.util.Date; - -import org.junit.jupiter.api.Test; - -/** - * Test parsing some date examples from the stateless OpenPGP CLI spec. - * - * @see OpenPGP Stateless CLI §4.1. Date - */ -public class UTCUtilTest { - - @Test - public void parseExample1() { - String timestamp = "2019-10-29T12:11:04+00:00"; - Date date = UTCUtil.parseUTCDate(timestamp); - assertEquals("2019-10-29T12:11:04Z", UTCUtil.formatUTCDate(date)); - } - - @Test - public void parseExample2() { - String timestamp = "2019-10-24T23:48:29Z"; - Date date = UTCUtil.parseUTCDate(timestamp); - assertEquals("2019-10-24T23:48:29Z", UTCUtil.formatUTCDate(date)); - } - - @Test - public void parseExample3() { - String timestamp = "20191029T121104Z"; - Date date = UTCUtil.parseUTCDate(timestamp); - assertEquals("2019-10-29T12:11:04Z", UTCUtil.formatUTCDate(date)); - } - - @Test - public void invalidDateReturnsNull() { - String invalidTimestamp = "foobar"; - Date expectNull = UTCUtil.parseUTCDate(invalidTimestamp); - assertNull(expectNull); - } -}