diff --git a/bundles/org.eclipse.passage.lic.bc/src/org/eclipse/passage/lic/internal/bc/BcDecodedStream.java b/bundles/org.eclipse.passage.lic.bc/src/org/eclipse/passage/lic/internal/bc/BcDecodedStream.java index bf5b102da..a7c72a8a9 100644 --- a/bundles/org.eclipse.passage.lic.bc/src/org/eclipse/passage/lic/internal/bc/BcDecodedStream.java +++ b/bundles/org.eclipse.passage.lic.bc/src/org/eclipse/passage/lic/internal/bc/BcDecodedStream.java @@ -63,7 +63,6 @@ void produce(InputStream publicKeyRing, DigestExpectation digest) throws Licensi signature.init(new JcaPGPContentVerifierBuilderProvider(), decodeKey); writeVerifiedDecodedOutput(literal, signature, factory); } - } catch (Exception e) { throw new LicensingException( // String.format(BcMessages.getString("BcStreamCodec_deconde_error"), product), //$NON-NLS-1$ , diff --git a/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/BcStreamCodecTest.java b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/BcStreamCodecTest.java new file mode 100644 index 000000000..210d91b49 --- /dev/null +++ b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/BcStreamCodecTest.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.bc.tests; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.passage.lic.internal.api.LicensedProduct; +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.base.BaseLicensedProduct; +import org.eclipse.passage.lic.internal.base.io.PassageFileExtension; +import org.eclipse.passage.lic.internal.bc.BcStreamCodec; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; + +@SuppressWarnings("restriction") +abstract class BcStreamCodecTest { + + @Rule + public final TemporaryFolder root = new TemporaryFolder(); + + protected final void assertFileExists(Path file) { + assertTrue(Files.exists(file)); + assertTrue(Files.isRegularFile(file)); + } + + protected final PairInfo pair(String user, String pass) throws IOException { + return pair((pub, secret) -> new PairKeys(pub, secret), user, pass); + } + + protected final PairInfo pair(ThrowingCtor ctor, String user, String pass) throws IOException { + Path pub = new TmpFile(root).keyFile(new PassageFileExtension.PublicKey()); + Path secret = new TmpFile(root).keyFile(new PassageFileExtension.PrivateKey()); + BcStreamCodec codec = new BcStreamCodec(this::product); + try { + codec.createKeyPair(pub, secret, user, pass); + } catch (LicensingException e) { + fail("PGP key pair generation on valid data is not supposed to fail"); //$NON-NLS-1$ + } + return ctor.create(pub, secret); + } + + protected final InputStream anInput() { + return new ByteArrayInputStream(new byte[0]); + } + + protected final OutputStream anOutput() { + return new ByteArrayOutputStream(); + } + + protected final LicensedProduct product() { + return new BaseLicensedProduct("bc-stream-codec-test-product", "2.4.21"); //$NON-NLS-1$//$NON-NLS-2$ + } +} diff --git a/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/KeyPairGenerationTest.java b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/KeyPairGenerationTest.java index a0eb1e4ca..1d840f813 100644 --- a/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/KeyPairGenerationTest.java +++ b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/KeyPairGenerationTest.java @@ -21,26 +21,18 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; -import org.eclipse.passage.lic.internal.api.LicensedProduct; import org.eclipse.passage.lic.internal.api.LicensingException; import org.eclipse.passage.lic.internal.api.io.DigestExpectation; -import org.eclipse.passage.lic.internal.base.BaseLicensedProduct; import org.eclipse.passage.lic.internal.base.io.FileContent; import org.eclipse.passage.lic.internal.base.io.PassageFileExtension; import org.eclipse.passage.lic.internal.bc.BcStreamCodec; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; @SuppressWarnings("restriction") -public final class KeyPairGenerationTest { - - @Rule - public TemporaryFolder root = new TemporaryFolder(); +public final class KeyPairGenerationTest extends BcStreamCodecTest { @Test public void generationSucceeds() throws IOException { @@ -78,16 +70,16 @@ public void subsequentPairDiffer() throws IOException { @Test public void generatedPairIsFunctional() throws IOException { // given - Path victim = new TmpFile(root).fileWithContent(); + Path origin = new TmpFile(root).fileWithContent(); Path encoded = new TmpFile(root).file(".txt"); //$NON-NLS-1$ Path decoded = new TmpFile(root).file(".txt"); //$NON-NLS-1$ String user = "fake.user"; //$NON-NLS-1$ String pass = "some$pass#val&1"; //$NON-NLS-1$ - // when: first: encode - PairInfo pair = pair((pub, secret) -> new PairKeys(pub, secret), user, pass); + // when: first: create pair + PairInfo pair = pair(user, pass); try (// - InputStream target = new FileInputStream(victim.toFile()); // + InputStream target = new FileInputStream(origin.toFile()); // OutputStream destination = new FileOutputStream(encoded.toFile()); // InputStream key = new FileInputStream(pair.secondInfo().toFile())) { new BcStreamCodec(this::product).encode(target, destination, key, user, pass); @@ -108,7 +100,7 @@ public void generatedPairIsFunctional() throws IOException { // then assertTrue(Objects.deepEquals(// - new FileContent(victim).get(), // + new FileContent(origin).get(), // new FileContent(decoded).get())); } @@ -181,28 +173,4 @@ private PairInfo pair(ThrowingCtor ctor) throws IOException { return pair(ctor, "test-user", "test-pass-word");//$NON-NLS-1$//$NON-NLS-2$ } - private PairInfo pair(ThrowingCtor ctor, String user, String pass) throws IOException { - // given: - Path pub = new TmpFile(root).keyFile(new PassageFileExtension.PublicKey()); - Path secret = new TmpFile(root).keyFile(new PassageFileExtension.PrivateKey()); - BcStreamCodec codec = new BcStreamCodec(this::product); - try { - // when - codec.createKeyPair(pub, secret, user, pass); - } catch (LicensingException e) { - // then - fail("PGP key pair generation on valid data is not supposed to fail"); //$NON-NLS-1$ - } - return ctor.create(pub, secret); - } - - private void assertFileExists(Path file) { - assertTrue(Files.exists(file)); - assertTrue(Files.isRegularFile(file)); - } - - private LicensedProduct product() { - return new BaseLicensedProduct("keygen-test-product", "1.0.18"); //$NON-NLS-1$//$NON-NLS-2$ - } - } diff --git a/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/StreamDecodingTest.java b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/StreamDecodingTest.java new file mode 100644 index 000000000..29c28ad92 --- /dev/null +++ b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/StreamDecodingTest.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.bc.tests; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.function.Function; + +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.api.io.DigestExpectation; +import org.eclipse.passage.lic.internal.base.io.BaseDigestExpectation; +import org.eclipse.passage.lic.internal.base.io.FileContent; +import org.eclipse.passage.lic.internal.bc.BcDigest; +import org.eclipse.passage.lic.internal.bc.BcStreamCodec; +import org.junit.Test; + +@SuppressWarnings("restriction") +public final class StreamDecodingTest extends BcStreamCodecTest { + + @Test + public void decodingIsFunctional() throws IOException { + Path[] actors; + try { + actors = decode(); + } catch (LicensingException e) { + fail("Decoding for valid data is not supposed to fail"); //$NON-NLS-1$ + throw new RuntimeException();// unreachable + } + Path origin = actors[0]; + Path encoded = actors[1]; + Path decoded = actors[2]; + assertTrue(Files.size(decoded) > 0); + assertFalse(Objects.deepEquals(// + new FileContent(encoded).get(), // + new FileContent(decoded).get())); + assertTrue(Objects.deepEquals(// + new FileContent(origin).get(), // + new FileContent(decoded).get())); + } + + /** + * Decoding with a foreign key must fail even if it is not demanded to be + * assessed by digest expectation. + */ + @Test + public void properKeyIsRequired() throws IOException { + try { + decodeWithKey(PairInfo::secondInfo); + } catch (LicensingException e) { + return; + } + fail("Decoding with incorrect key must fail"); //$NON-NLS-1$ + } + + @Test + public void validDigestDoesNotFailDecoding() throws IOException { + try { + decodeWithDigest(key -> new BaseDigestExpectation(new BcDigest(key).get())); + } catch (LicensingException e) { + fail("Decoding for valid data is not supposed to fail"); //$NON-NLS-1$ + } + } + + @Test + public void invalidDigestFailsDecoding() throws IOException { + try { + decodeWithDigest(key -> new BaseDigestExpectation(new byte[0])); + } catch (LicensingException e) { + assertTrue(e.getMessage().contains("digest")); //$NON-NLS-1$ + return; + } + fail("Unsatisfiable digest expectation must stop decoding"); //$NON-NLS-1$ + } + + private Path encoded(Path origin, Path privateKey, String user, String pass) throws IOException { + Path encoded = new TmpFile(root).file(".txt"); //$NON-NLS-1$ + try (// + InputStream source = new FileInputStream(origin.toFile()); + OutputStream destination = new FileOutputStream(encoded.toFile()); + InputStream key = new FileInputStream(privateKey.toFile())) { + new BcStreamCodec(this::product).encode(source, destination, key, user, pass); + } catch (LicensingException e) { + fail("Failed to encode a file: check corresponding tests set"); //$NON-NLS-1$ + } + return encoded; + } + + @Test(expected = NullPointerException.class) + public void sourceIsMandatory() throws IOException { + try (OutputStream output = anOutput(); InputStream key = anInput()) { + decodeNull(null, output, key, new DigestExpectation.None()); + } + } + + @Test(expected = NullPointerException.class) + public void targetIsMandatory() throws IOException { + try (InputStream input = anInput(); InputStream key = anInput()) { + decodeNull(input, null, key, new DigestExpectation.None()); + } + } + + @Test(expected = NullPointerException.class) + public void keyIsMandatory() throws IOException { + try (InputStream input = anInput(); OutputStream output = anOutput()) { + decodeNull(input, output, null, new DigestExpectation.None()); + } + } + + @Test(expected = NullPointerException.class) + public void digestIsMandatory() throws IOException { + try (InputStream input = anInput(); OutputStream output = anOutput(); InputStream key = anInput()) { + decodeNull(input, output, key, null); + } + } + + private void decodeNull(InputStream input, OutputStream output, InputStream key, DigestExpectation digest) { + try { + new BcStreamCodec(this::product).decode(input, output, key, digest); + } catch (LicensingException e) { + fail("Incorrect incoming data are not intended to cause any decoding activity"); //$NON-NLS-1$ + } + } + + private Path[] decode() throws IOException, LicensingException { + return decode(pair -> pair.firstInfo(), key -> new DigestExpectation.None()); + } + + private Path[] decodeWithDigest(ThrowingDigestSupplier digest) throws IOException, LicensingException { + return decode(pair -> pair.firstInfo(), digest); + } + + private Path[] decodeWithKey(Function, Path> publicKey) throws IOException, LicensingException { + return decode(publicKey, key -> new DigestExpectation.None()); + } + + private Path[] decode(Function, Path> publicKey, ThrowingDigestSupplier digest) + throws IOException, LicensingException { + // given + String user = "Suer"; //$NON-NLS-1$ + String pass = "Vyer"; //$NON-NLS-1$ + PairInfo pair = pair(user, pass); + Path origin = new TmpFile(root).fileWithContent(); + Path encoded = encoded(origin, pair.secondInfo(), user, pass); + Path decoded = new TmpFile(root).file(".txt"); //$NON-NLS-1$ + assumeTrue(Files.size(decoded) == 0); + + // when + try (// + InputStream source = new FileInputStream(encoded.toFile()); + OutputStream destination = new FileOutputStream(decoded.toFile()); + InputStream key = new FileInputStream(publicKey.apply(pair).toFile())) { + new BcStreamCodec(this::product).decode(source, destination, key, digest.forKey(pair.firstInfo())); + } + return new Path[] { origin, encoded, decoded }; + } + + private interface ThrowingDigestSupplier { + DigestExpectation forKey(Path key) throws IOException; + } + +} diff --git a/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/StreamEncodingTest.java b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/StreamEncodingTest.java index ca5e05c22..d3370daab 100644 --- a/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/StreamEncodingTest.java +++ b/tests/org.eclipse.passage.lic.bc.tests/src/org/eclipse/passage/lic/internal/bc/tests/StreamEncodingTest.java @@ -17,8 +17,6 @@ import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -28,21 +26,14 @@ import java.nio.file.Path; import java.util.Objects; -import org.eclipse.passage.lic.internal.api.LicensedProduct; import org.eclipse.passage.lic.internal.api.LicensingException; -import org.eclipse.passage.lic.internal.base.BaseLicensedProduct; import org.eclipse.passage.lic.internal.base.io.FileContent; import org.eclipse.passage.lic.internal.base.io.PassageFileExtension; import org.eclipse.passage.lic.internal.bc.BcStreamCodec; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; @SuppressWarnings("restriction") -public final class StreamEncodingTest { - - @Rule - public TemporaryFolder root = new TemporaryFolder(); +public final class StreamEncodingTest extends BcStreamCodecTest { @Test public void encodingIsFunctional() throws IOException { @@ -71,7 +62,7 @@ public void encodingIsFunctional() throws IOException { } /** - * Here we do the encoding using the random char sequence as an encryption key. + * Here we do the encoding using a random char sequence as an encryption key. * This supposed to constantly fail as only properly generated keys are * acceptable for encoding purpose. */ @@ -135,15 +126,6 @@ private Path privateKey(String user, String pass) throws IOException { fail("Failed to getenate key pair: check corresponging tests set"); //$NON-NLS-1$ } return key; - - } - - private InputStream anInput() { - return new ByteArrayInputStream(new byte[0]); - } - - private OutputStream anOutput() { - return new ByteArrayOutputStream(); } private void encodeNull(InputStream input, OutputStream output, InputStream key) { @@ -154,12 +136,8 @@ private void encodeNull(InputStream input, OutputStream output, InputStream key, try { new BcStreamCodec(this::product).encode(input, output, key, user, pass); } catch (LicensingException e) { - fail("Incorrect incoming data are not intended to use any encoding activity"); //$NON-NLS-1$ + fail("Incorrect incoming data are not intended to cause any encoding activity"); //$NON-NLS-1$ } } - private LicensedProduct product() { - return new BaseLicensedProduct("encoding-test-product", "2.4.21"); //$NON-NLS-1$//$NON-NLS-2$ - } - }