Split integrations and export into ext module
feat: move integrations from lib to ext feat: move content export from lib to ext feat: rename affected packages for separate module distribution chore: update Gradle module wiring chore: adjust JPMS descriptors and dependencies docs: update module structure documentation
This commit is contained in:
@@ -1,112 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2026, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package zeroecho.sdk.content.export;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class Base64StreamTest {
|
||||
|
||||
@Test
|
||||
void testEncodedOutputFrom4KBInput() throws Exception {
|
||||
System.out.println("testEncodedOutputFrom4KBInput");
|
||||
|
||||
byte[] inputBytes = new byte[4096];
|
||||
new SecureRandom().nextBytes(inputBytes); // random 4KB data
|
||||
|
||||
byte[] inputEncoded = Base64.getEncoder().encode(inputBytes);
|
||||
|
||||
System.out.println("...encoded length should be: " + inputEncoded.length);
|
||||
|
||||
int lineLength = 76;
|
||||
|
||||
String[] prefixes = { "echo ", null };
|
||||
byte[][] lineSeparators = { " >> file.tmp\r\n".getBytes(StandardCharsets.US_ASCII),
|
||||
"\n".getBytes(StandardCharsets.US_ASCII), null };
|
||||
|
||||
for (String prefix : prefixes) {
|
||||
for (byte[] lineSep : lineSeparators) {
|
||||
System.out.printf("...***** testing with prefix=%s and lineSep=%s%n",
|
||||
prefix == null ? "null" : "\"" + prefix + "\"",
|
||||
lineSep == null ? "null" : "\"" + new String(lineSep, StandardCharsets.US_ASCII) + "\"");
|
||||
|
||||
try (InputStream base64Stream = new Base64Stream(new ByteArrayInputStream(inputBytes),
|
||||
prefix == null ? null : prefix.getBytes(StandardCharsets.US_ASCII), lineLength, lineSep)) {
|
||||
|
||||
String encodedOutput = new String(base64Stream.readAllBytes(), StandardCharsets.US_ASCII);
|
||||
System.out.println(encodedOutput);
|
||||
|
||||
String[] lines = encodedOutput.split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
int space = lines[i].indexOf(' ');
|
||||
if (space > 0) {
|
||||
lines[i] = lines[i].split(" ")[prefix == null ? 0 : 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (lineSep != null) {
|
||||
for (String line : lines) {
|
||||
assertTrue(line.length() <= lineLength, "Line exceeds max length: " + line.length());
|
||||
}
|
||||
}
|
||||
|
||||
String joined = String.join("", lines);
|
||||
|
||||
System.out.println("...lines: " + lines.length);
|
||||
System.out.println("......encoded length: " + joined.length());
|
||||
|
||||
if (lineSep == null && prefix != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] decoded = Base64.getDecoder().decode(joined);
|
||||
|
||||
System.out.println("...decoded length: " + decoded.length);
|
||||
|
||||
assertArrayEquals(inputBytes, decoded, "Decoded content does not match original input");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("...ok");
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2026, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package zeroecho.sdk.integrations.covert;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TextualCodecTest {
|
||||
|
||||
@Test
|
||||
void testGenerate100CharacterText() {
|
||||
System.out.println("testGenerate100CharacterText");
|
||||
|
||||
TextualCodec.Generator generator = TextualCodec.Generator.EN;
|
||||
String result = generator.getText(100);
|
||||
|
||||
System.out.println("...generated text: " + result);
|
||||
|
||||
assertNotNull(result, "Generated text should not be null");
|
||||
assertEquals(100, result.length(), "Generated text should have 100 characters");
|
||||
|
||||
// Ensure all characters are from the expected alphabet
|
||||
for (char c : result.toCharArray()) {
|
||||
assertTrue(TextualCodec.Generator.ENGLISH.containsKey(c),
|
||||
"Generated character '" + c + "' is not part of the English frequency table");
|
||||
}
|
||||
|
||||
// Optional: Analyze distribution for debugging
|
||||
Map<Character, Integer> histogram = new HashMap<>();
|
||||
for (char c : result.toCharArray()) {
|
||||
histogram.merge(c, 1, Integer::sum);
|
||||
}
|
||||
|
||||
System.out.println("...character distribution: " + histogram);
|
||||
System.out.println("...ok");
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2026, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package zeroecho.sdk.integrations.covert.jpeg;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import conflux.Ctx;
|
||||
import conflux.CtxInterface;
|
||||
import zeroecho.core.CryptoAlgorithms;
|
||||
import zeroecho.core.alg.aes.AesKeyGenSpec;
|
||||
import zeroecho.core.alg.aes.AesSpec;
|
||||
import zeroecho.sdk.builders.alg.AesDataContentBuilder;
|
||||
import zeroecho.sdk.builders.core.DataContentChainBuilder;
|
||||
import zeroecho.sdk.builders.core.PlainBytesBuilder;
|
||||
import zeroecho.sdk.content.api.DataContent;
|
||||
|
||||
class JpegExifIntegrationTest {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
private static byte[] readAll(InputStream in) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
in.transferTo(out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptEmbedExtractDecrypt() throws Exception {
|
||||
System.out.println("testEncryptEmbedExtractDecrypt");
|
||||
|
||||
String inputText = "Hello, this is a secret message to embed in JPEG.";
|
||||
|
||||
inputText = inputText + inputText;
|
||||
inputText = inputText + inputText;
|
||||
inputText = inputText + inputText;
|
||||
|
||||
final byte[] inputBytes = inputText.getBytes(StandardCharsets.UTF_8);
|
||||
final byte[] aad = { 1, 2, 3 };
|
||||
|
||||
System.out.println("...inputBytes.length=" + inputBytes.length);
|
||||
|
||||
// AES encryption setup
|
||||
/*
|
||||
* CryptoAlgorithm aes = CryptoAlgorithms.require("AES"); SecretKey key =
|
||||
* aes.symmetricKeyBuilder(AesKeyGenSpec.class).generateSecret(AesKeyGenSpec.
|
||||
* aes256()); AesSpec spec =
|
||||
* AesSpec.builder().mode(Mode.GCM).tagLenBits(128).header(null).build();
|
||||
* EncryptionContext enc = CryptoAlgorithms.create("AES", KeyUsage.ENCRYPT, key,
|
||||
* spec); CtxInterface session = Ctx.INSTANCE.getContext("aes-ctx-" +
|
||||
* System.nanoTime()); session.put(ConfluxKeys.aad("AES"), aad); ((ContextAware)
|
||||
* enc).setContext(session);
|
||||
*/
|
||||
SecretKey key = CryptoAlgorithms.require("AES").symmetricKeyBuilder(AesKeyGenSpec.class)
|
||||
.generateSecret(AesKeyGenSpec.aes256());
|
||||
CtxInterface session = Ctx.INSTANCE.getContext("aes-ctx-" + System.nanoTime());
|
||||
|
||||
byte[] encryptedBytes;
|
||||
DataContent dccb = DataContentChainBuilder.encrypt()
|
||||
// input
|
||||
.add(PlainBytesBuilder.builder().bytes(inputBytes))
|
||||
// encryption
|
||||
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded())
|
||||
// using general AES/GCM/128 without specified header
|
||||
.spec(AesSpec.gcm128(null))
|
||||
// but let the builder add the default header for storing AAD and IV
|
||||
.withHeader().withAad(aad).context(session))
|
||||
// and create the pipeline
|
||||
.build();
|
||||
try (InputStream in = dccb.getStream()) {
|
||||
encryptedBytes = readAll(in);
|
||||
}
|
||||
|
||||
System.out.println("...encryptedBytes.length=" + encryptedBytes.length);
|
||||
System.out.println("...-> " + Arrays.toString(encryptedBytes));
|
||||
|
||||
// Prepare JPEG test image
|
||||
Path jpegOriginal = getResourcePath("test.jpg");
|
||||
Path jpegEmbedded = tempDir.resolve("stego.jpg");
|
||||
|
||||
// Embed payload
|
||||
try (InputStream payloadInput = new ByteArrayInputStream(encryptedBytes);
|
||||
OutputStream jpegOutput = Files.newOutputStream(jpegEmbedded)) {
|
||||
|
||||
JpegExifEmbedder embedder = new JpegExifEmbedder();
|
||||
embedder.setSlots(Slot.defaults());
|
||||
int embed_size = embedder.embed(jpegOriginal, payloadInput, jpegOutput);
|
||||
System.out.println("...embeddedStream.length=" + embed_size);
|
||||
}
|
||||
|
||||
// Extract payload
|
||||
ByteArrayOutputStream extractedEncrypted = new ByteArrayOutputStream();
|
||||
JpegExifEmbedder embedder = new JpegExifEmbedder();
|
||||
embedder.setSlots(Slot.defaults());
|
||||
embedder.extract(jpegEmbedded, extractedEncrypted);
|
||||
|
||||
byte[] extractedEncryptedBytes = extractedEncrypted.toByteArray();
|
||||
System.out.println("...extractedEncryptedBytes.length=" + extractedEncryptedBytes.length);
|
||||
System.out.println("...-> " + Arrays.toString(extractedEncryptedBytes));
|
||||
|
||||
// Decrypt
|
||||
dccb = DataContentChainBuilder.decrypt()
|
||||
// input
|
||||
.add(PlainBytesBuilder.builder().bytes(extractedEncryptedBytes))
|
||||
// encryption
|
||||
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded()).spec(AesSpec.gcm128(null))
|
||||
// let us use the default header for AAD and IV
|
||||
.withHeader().withAad(aad).context(session))
|
||||
// and create the pipeline
|
||||
.build();
|
||||
byte[] pt1;
|
||||
try (InputStream in = dccb.getStream()) {
|
||||
pt1 = readAll(in);
|
||||
}
|
||||
/*
|
||||
* AesSpec spec =
|
||||
* AesSpec.builder().mode(Mode.GCM).tagLenBits(128).header(null).build();
|
||||
* EncryptionContext dec1 = CryptoAlgorithms.create("AES", KeyUsage.DECRYPT,
|
||||
* key, spec); ((ContextAware) dec1).setContext(session); // same IV/AAD in ctx
|
||||
* byte[] pt1 = readAll(dec1.attach(new
|
||||
* ByteArrayInputStream(extractedEncryptedBytes))); dec1.close();
|
||||
*/
|
||||
String decrypted = new String(pt1, StandardCharsets.UTF_8);
|
||||
|
||||
assertEquals(inputText, decrypted);
|
||||
System.out.println("...ok");
|
||||
}
|
||||
|
||||
private Path getResourcePath(String resource) throws URISyntaxException {
|
||||
URL url = getClass().getClassLoader().getResource(resource);
|
||||
if (url == null) {
|
||||
throw new IllegalArgumentException("Missing resource: " + resource);
|
||||
}
|
||||
return Paths.get(url.toURI());
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2026, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package zeroecho.sdk.integrations.stegano;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LSBSteganographyMethodTest {
|
||||
|
||||
@Test
|
||||
void testEmbedAndExtract() throws Exception {
|
||||
SteganographyMethod method = new LSBSteganographyMethod();
|
||||
String message = "Hello from LSB!";
|
||||
InputStream imageIn = getClass().getResourceAsStream("/test.jpg");
|
||||
InputStream msgIn = new ByteArrayInputStream(message.getBytes());
|
||||
|
||||
// Embed
|
||||
InputStream embedded = method.embed(imageIn, ImageFormat.PNG, msgIn);
|
||||
|
||||
// Extract
|
||||
InputStream extracted = method.extract(embedded);
|
||||
String result = new String(extracted.readAllBytes());
|
||||
|
||||
assertEquals(message, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMetadata() {
|
||||
SteganographyMethod method = new LSBSteganographyMethod();
|
||||
StegoMetadata meta = method.getMetadata();
|
||||
|
||||
assertEquals("LSB", meta.name());
|
||||
assertTrue(meta.fullName().contains("Least Significant"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createSample() throws Exception {
|
||||
// for verification purposes
|
||||
SteganographyMethod method = new LSBSteganographyMethod();
|
||||
String message = "Hello from LSB!";
|
||||
InputStream imageIn = getClass().getResourceAsStream("/test.jpg");
|
||||
InputStream msgIn = new ByteArrayInputStream(message.getBytes());
|
||||
|
||||
// Embed
|
||||
InputStream embedded = method.embed(imageIn, ImageFormat.PNG, msgIn);
|
||||
|
||||
embedded.transferTo(Files.newOutputStream(Paths.get("/tmp/lsb-test.png"), StandardOpenOption.CREATE));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user