diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/SignImpl.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/SignImpl.java index 068903d7..b3d18043 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/SignImpl.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/SignImpl.java @@ -28,6 +28,7 @@ import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.util.ArmoredOutputStreamFactory; import sop.MicAlg; import sop.ReadyWithResult; +import sop.SigningResult; import sop.enums.SignAs; import sop.exception.SOPGPException; import sop.operation.Sign; @@ -69,7 +70,7 @@ public class SignImpl implements Sign { } @Override - public ReadyWithResult data(InputStream data) throws IOException { + public ReadyWithResult data(InputStream data) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { EncryptionStream signingStream = PGPainless.encryptAndOrSign() @@ -77,9 +78,9 @@ public class SignImpl implements Sign { .withOptions(ProducerOptions.sign(signingOptions) .setAsciiArmor(armor)); - return new ReadyWithResult() { + return new ReadyWithResult() { @Override - public MicAlg writeTo(OutputStream outputStream) throws IOException { + public SigningResult writeTo(OutputStream outputStream) throws IOException { if (signingStream.isClosed()) { throw new IllegalStateException("EncryptionStream is already closed."); @@ -106,7 +107,9 @@ public class SignImpl implements Sign { out.close(); outputStream.close(); // armor out does not close underlying stream - return micAlgFromSignatures(signatures); + return SigningResult.builder() + .setMicAlg(micAlgFromSignatures(signatures)) + .build(); } }; 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 index 735e9664..250679fd 100644 --- 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 @@ -15,6 +15,7 @@ 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; @@ -93,9 +94,10 @@ public class SignCmd implements Runnable { } try { - ReadyWithResult ready = sign.data(System.in); - MicAlg micAlg = ready.writeTo(System.out); + ReadyWithResult ready = sign.data(System.in); + SigningResult result = ready.writeTo(System.out); + MicAlg micAlg = result.getMicAlg(); if (micAlgOut != null) { // Write micalg out micAlgOut.createNewFile(); 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 index c5c6e201..ce0ce54a 100644 --- 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 @@ -19,9 +19,9 @@ import java.io.OutputStream; import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import sop.MicAlg; import sop.ReadyWithResult; import sop.SOP; +import sop.SigningResult; import sop.cli.picocli.SopCLI; import sop.exception.SOPGPException; import sop.operation.Sign; @@ -34,10 +34,10 @@ public class SignCmdTest { @BeforeEach public void mockComponents() throws IOException, SOPGPException.ExpectedText { sign = mock(Sign.class); - when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult() { + when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult() { @Override - public MicAlg writeTo(OutputStream outputStream) { - return MicAlg.fromHashAlgorithmId(10); + public SigningResult writeTo(OutputStream outputStream) { + return SigningResult.builder().build(); } }); @@ -110,9 +110,9 @@ public class SignCmdTest { @Test @ExpectSystemExitWithStatus(1) public void data_ioExceptionCausesExit1() throws IOException, SOPGPException.ExpectedText { - when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult() { + when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult() { @Override - public MicAlg writeTo(OutputStream outputStream) throws IOException { + public SigningResult writeTo(OutputStream outputStream) throws IOException { throw new IOException(); } }); diff --git a/sop-java/src/main/java/sop/SigningResult.java b/sop-java/src/main/java/sop/SigningResult.java new file mode 100644 index 00000000..2cb142dc --- /dev/null +++ b/sop-java/src/main/java/sop/SigningResult.java @@ -0,0 +1,50 @@ +// 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/operation/Sign.java b/sop-java/src/main/java/sop/operation/Sign.java index 75f4e5a8..be518cde 100644 --- a/sop-java/src/main/java/sop/operation/Sign.java +++ b/sop-java/src/main/java/sop/operation/Sign.java @@ -8,8 +8,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import sop.MicAlg; import sop.ReadyWithResult; +import sop.SigningResult; import sop.enums.SignAs; import sop.exception.SOPGPException; @@ -55,7 +55,7 @@ public interface Sign { * @param data input stream containing data * @return ready */ - ReadyWithResult data(InputStream data) throws IOException, SOPGPException.ExpectedText; + ReadyWithResult data(InputStream data) throws IOException, SOPGPException.ExpectedText; /** * Signs data. @@ -63,7 +63,7 @@ public interface Sign { * @param data byte array containing data * @return ready */ - default ReadyWithResult data(byte[] data) throws IOException, SOPGPException.ExpectedText { + default ReadyWithResult data(byte[] data) throws IOException, SOPGPException.ExpectedText { return data(new ByteArrayInputStream(data)); } }