diff --git a/bcsop-cli/pom.xml b/bcsop-cli/pom.xml index 03173ba..d17fabe 100644 --- a/bcsop-cli/pom.xml +++ b/bcsop-cli/pom.xml @@ -10,14 +10,25 @@ bcsop-cli + jar 21 21 UTF-8 + 0.10.4 + bcsop-cli-native + org.pgpainless.BcSopCLI + 21.0.0 + + org.graalvm.nativeimage + svm + ${graal.version} + provided + org.pgpainless bcsop @@ -43,11 +54,51 @@ + + + + org.apache.maven.plugins + maven-compiler-plugin + + 3.11.0 + + + + info.picocli + picocli-codegen + 4.6.3 + + + + + + + org.apache.maven.plugins maven-assembly-plugin @@ -58,7 +109,7 @@ - org.pgpainless.BcSopCLI + ${mainClass} @@ -72,6 +123,144 @@ + + + + org.graalvm.nativeimage + native-image-maven-plugin + ${graal.version} + + ${imageName} + ${mainClass} + + + + + native-image + + package + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + generateGraalReflectionConfig + process-classes + + java + + + true + true + picocli.codegen.aot.graalvm.ReflectionConfigGenerator + + --output=bcsop-cli/target/classes/META-INF/native-image/${project.groupId}/${project.artifactId}/reflect-config.json + + sop.cli.picocli.SopCLI + + + + + + generateGraalDynamicProxyConfig + process-classes + + java + + + true + true + picocli.codegen.aot.graalvm.DynamicProxyConfigGenerator + + --output=bcsop-cli/target/classes/META-INF/native-image/${project.groupId}/${project.artifactId}/proxy-config.json + + sop.cli.picocli.SopCLI + + + + + + generateGraalResourceConfig + process-classes + + java + + + true + true + picocli.codegen.aot.graalvm.ResourceConfigGenerator + + --output=bcsop-cli/target/classes/META-INF/native-image/${project.groupId}/${project.artifactId}/resource-config.json + + sop.cli.picocli.SopCLI + + + + + + + + info.picocli + picocli-codegen + 4.6.3 + jar + + + - \ No newline at end of file + diff --git a/bcsop-cli/src/main/java/org/pgpainless/BcSopCLI.java b/bcsop-cli/src/main/java/org/pgpainless/BcSopCLI.java index 725d47d..bf21034 100644 --- a/bcsop-cli/src/main/java/org/pgpainless/BcSopCLI.java +++ b/bcsop-cli/src/main/java/org/pgpainless/BcSopCLI.java @@ -1,14 +1,27 @@ package org.pgpainless; import org.pgpainless.bouncycastle.sop.BouncyCastleSOP; +import picocli.CommandLine; +import sop.cli.picocli.SOPExceptionExitCodeMapper; +import sop.cli.picocli.SOPExecutionExceptionHandler; import sop.cli.picocli.SopCLI; -public class BcSopCLI { +public class BcSopCLI extends SopCLI { public static void main(String[] args) { SopCLI.EXECUTABLE_NAME = "bc-sop"; SopCLI.setSopInstance(new BouncyCastleSOP()); - int exitCode = SopCLI.execute(args); - System.exit(exitCode); + System.exit(run(args)); + } + + public static int run(String[] args) + { + CommandLine cmd = new CommandLine(SopCLI.class); + cmd.getSubcommands().get("generate-completion").getCommandSpec().usageMessage().hidden(true); + cmd.setCommandName("bcsop"); + cmd.setExecutionExceptionHandler(new SOPExecutionExceptionHandler()); + cmd.setExitCodeExceptionMapper(new SOPExceptionExitCodeMapper()); + cmd.setCaseInsensitiveEnumValuesAllowed(true); + return cmd.execute(args); } } \ No newline at end of file diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/AbstractBCOperation.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/AbstractBCOperation.java index 9a37617..026c7a3 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/AbstractBCOperation.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/AbstractBCOperation.java @@ -23,10 +23,10 @@ public abstract class AbstractBCOperation return new SessionKey((byte) sessionKey.getAlgorithm(), sessionKey.getKey()); } - protected List getVerifications(OpenPGPMessageInputStream.Result result) + protected List getVerifications(List signatures) { List verifications = new ArrayList<>(); - for (OpenPGPSignature.OpenPGPDocumentSignature sig : result.getSignatures()) + for (OpenPGPSignature.OpenPGPDocumentSignature sig : signatures) { if (sig.isValid()) { diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java index cdc1a16..28706ed 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java @@ -4,7 +4,6 @@ import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.util.io.Streams; import org.jetbrains.annotations.NotNull; import sop.Ready; -import sop.enums.ArmorLabel; import sop.exception.SOPGPException; import sop.operation.Armor; @@ -30,10 +29,4 @@ public class BCArmor } }; } - - @NotNull - @Override - public Armor label(@NotNull ArmorLabel armorLabel) throws SOPGPException.UnsupportedOption { - throw new SOPGPException.UnsupportedOption("Custom labels not supported."); - } } diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java index 7b65fad..4042899 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java @@ -26,8 +26,7 @@ public class BCDecrypt private Date notBefore = new Date(Long.MAX_VALUE); // end of time private Date notAfter = new Date(); // now - private final List encryptionKeys = new ArrayList<>(); - private final List encryptionKeyPassphrases = new ArrayList<>(); + private char[] keyPassword; private final OpenPGPMessageProcessor processor = new OpenPGPMessageProcessor(); @@ -35,7 +34,7 @@ public class BCDecrypt @NotNull @Override public ReadyWithResult ciphertext(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt, SOPGPException.KeyIsProtected, IOException { - return new ReadyWithResult() { + return new ReadyWithResult<>() { @Override public DecryptionResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException { try { @@ -45,7 +44,7 @@ public class BCDecrypt OpenPGPMessageInputStream.Result result = mIn.getResult(); return new DecryptionResult( getSessionKey(result), - getVerifications(result)); + getVerifications(result.getSignatures())); } catch (PGPException e) { throw new RuntimeException(e); } @@ -93,7 +92,7 @@ public class BCDecrypt @Override public Decrypt withKey(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { OpenPGPKey key = OpenPGPKey.fromInputStream(inputStream); - processor.addDecryptionKey(key); + processor.addDecryptionKey(key, keyPassword); return this; } @@ -101,7 +100,7 @@ public class BCDecrypt @Override public Decrypt withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { String passphrase = new String(bytes); - this.encryptionKeyPassphrases.add(passphrase); + this.keyPassword = passphrase.toCharArray(); return this; } } diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java index 87562cd..f046414 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java @@ -26,6 +26,7 @@ public class BCDetachedSign private final OpenPGPDetachedSignatureGenerator sigGen = new OpenPGPDetachedSignatureGenerator(); private boolean armored = true; + private char[] keyPassword = null; @NotNull @Override @@ -96,7 +97,7 @@ public class BCDetachedSign public DetachedSign key(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { try { - sigGen.addSigningKey(OpenPGPKey.fromInputStream(inputStream), null); + sigGen.addSigningKey(OpenPGPKey.fromInputStream(inputStream), keyPassword); } catch (InvalidSigningKeyException e) { @@ -111,6 +112,7 @@ public class BCDetachedSign @Override public DetachedSign withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { + keyPassword = new String(bytes).toCharArray(); return this; } } diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedVerify.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedVerify.java index e3d3a28..f838159 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedVerify.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedVerify.java @@ -56,16 +56,10 @@ public class BCDetachedVerify public List data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData { List signatures = processor.verify(inputStream); - List verifications = new ArrayList<>(); - for (OpenPGPSignature.OpenPGPDocumentSignature signature : signatures) + List verifications = getVerifications(signatures); + if (verifications.isEmpty()) { - if (signature.isValidAt(signature.getCreationTime())) - { - verifications.add(new Verification( - signature.getCreationTime(), - Hex.toHexString(signature.getIssuer().getKeyIdentifier().getFingerprint()), - Hex.toHexString(signature.getIssuerCertificate().getFingerprint()))); - } + throw new SOPGPException.NoSignature(); } return verifications; } diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java index f34cca8..44f3ea6 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java @@ -2,6 +2,7 @@ package org.pgpainless.bouncycastle.sop.operation; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; import org.bouncycastle.util.io.Streams; @@ -22,6 +23,7 @@ public class BCEncrypt implements Encrypt { private final OpenPGPMessageGenerator mGen; + private char[] keyPassword; public BCEncrypt() { @@ -45,12 +47,15 @@ public class BCEncrypt @NotNull @Override public Encrypt signWith(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, IOException { + OpenPGPKey key = OpenPGPKey.fromInputStream(inputStream); + mGen.addSigningKey(key, k -> keyPassword); return this; } @NotNull @Override public Encrypt withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { + this.keyPassword = new String(bytes).toCharArray(); return this; } diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java index 946b868..7d8a4e5 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java @@ -33,23 +33,33 @@ public class BCGenerateKey public void writeTo(@NotNull OutputStream outputStream) throws IOException { OpenPGPV6KeyGenerator generator = new BcOpenPGPV6KeyGenerator(new Date()); + OpenPGPKey key; try { - PGPSecretKeyRing keyRing = generator.ed25519x25519Key(userId, passphrase); - OpenPGPKey key = new OpenPGPKey(keyRing); - if (armor) + if (signOnly) { - outputStream.write(key.toAsciiArmoredString().getBytes(StandardCharsets.UTF_8)); + PGPSecretKeyRing keyRing = generator.signOnlyKey(passphrase); + key = new OpenPGPKey(keyRing); } else { - keyRing.encode(outputStream); + PGPSecretKeyRing keyRing = generator.ed25519x25519Key(userId, passphrase); + key = new OpenPGPKey(keyRing); } } catch (PGPException e) { throw new RuntimeException(e); } + + if (armor) + { + outputStream.write(key.toAsciiArmoredString().getBytes(StandardCharsets.UTF_8)); + } + else + { + key.getPGPKeyRing().encode(outputStream); + } } }; } diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java index a8f3892..3f187fe 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java @@ -17,9 +17,11 @@ import java.io.OutputStream; public class BCInlineSign extends AbstractBCOperation - implements InlineSign { + implements InlineSign +{ private final OpenPGPMessageGenerator mGen = new OpenPGPMessageGenerator(); + private char[] keyPassword; @NotNull @Override @@ -52,12 +54,13 @@ public class BCInlineSign @Override public InlineSign key(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { - mGen.addSigningKey(OpenPGPKey.fromInputStream(inputStream)); + mGen.addSigningKey(OpenPGPKey.fromInputStream(inputStream), k -> keyPassword); return this; } @Override public InlineSign withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { + this.keyPassword = new String(bytes).toCharArray(); return this; } } diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineVerify.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineVerify.java index a747bea..ecbe6af 100644 --- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineVerify.java +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineVerify.java @@ -26,7 +26,7 @@ public class BCInlineVerify @NotNull @Override public ReadyWithResult> data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData { - return new ReadyWithResult>() { + return new ReadyWithResult<>() { @Override public List writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException { try { @@ -34,7 +34,12 @@ public class BCInlineVerify Streams.pipeAll(mIn, outputStream); mIn.close(); OpenPGPMessageInputStream.Result result = mIn.getResult(); - return getVerifications(result); + List verifications = getVerifications(result.getSignatures()); + if (verifications.isEmpty()) + { + throw new SOPGPException.NoSignature(); + } + return verifications; } catch (PGPException e) { throw new RuntimeException(e); } diff --git a/pom.xml b/pom.xml index 38a5952..6ba8e59 100644 --- a/pom.xml +++ b/pom.xml @@ -48,12 +48,12 @@ org.pgpainless sop-java - 10.0.0 + 10.0.3-SNAPSHOT org.pgpainless sop-java-picocli - 10.0.0 + 10.0.3-SNAPSHOT