2 Commits

Author SHA1 Message Date
276ac91eb4 chore: replace apache-cli deprecated methods
Signed-off-by: Leo Galambos <lg@hq.egothor.org>
2025-12-27 16:55:28 +01:00
e82e0e57fb Merge hybrid cryptography support (KEX, derived keys, signatures)
This merge introduces the sdk.hybrid package with support for:
- hybrid key exchange (classic + post-quantum),
- hybrid-derived key injection for encryption and MAC builders,
- hybrid signature composition.

The implementation is additive at the SDK layer and does not modify
core cryptographic contracts.
2025-12-26 21:08:31 +01:00
7 changed files with 98 additions and 94 deletions

View File

@@ -132,17 +132,17 @@ public final class CovertCommand {
* @throws ParseException if the arguments are invalid or incomplete
*/
public static int main(String[] args, Options options) throws ParseException {
final Option EMBED_OPTION = Option.builder().longOpt("embed").desc("Embed a payload into a JPEG").build();
final Option EXTRACT_OPTION = Option.builder().longOpt("extract").desc("Extract a payload from a JPEG").build();
final Option EMBED_OPTION = Option.builder().longOpt("embed").desc("Embed a payload into a JPEG").get();
final Option EXTRACT_OPTION = Option.builder().longOpt("extract").desc("Extract a payload from a JPEG").get();
final Option JPEG_OPTION = Option.builder().longOpt("jpeg").hasArg().argName("input.jpg")
.desc("Input JPEG file").required().build();
.desc("Input JPEG file").required().get();
final Option PAYLOAD_OPTION = Option.builder().longOpt("payload").hasArg().argName("payload.dat")
.desc("Binary payload file to embed").build();
.desc("Binary payload file to embed").get();
final Option OUTPUT_OPTION = Option.builder().longOpt("output").hasArg().argName("outputFile")
.desc("Output JPEG or payload file").required().build();
.desc("Output JPEG or payload file").required().get();
final Option SLOTS_OPTION = Option.builder().longOpt("slots").hasArgs().valueSeparator(';')
.argName("slot1;slot2;...")
.desc("Custom EXIF slots (e.g. Exif.UserComment:4096;Exif.Custom/tag=700,ascii,64,exif:1024)").build();
.desc("Custom EXIF slots (e.g. Exif.UserComment:4096;Exif.Custom/tag=700,ascii,64,exif:1024)").get();
OptionGroup modeGroup = new OptionGroup();
modeGroup.addOption(EMBED_OPTION);

View File

@@ -128,71 +128,71 @@ public final class Guard {
// ---- operation selection
final Option OPT_ENCRYPT = Option.builder("e").longOpt("encrypt").hasArg().argName("in-file")
.desc("Encrypt the given file").build();
.desc("Encrypt the given file").get();
final Option OPT_DECRYPT = Option.builder("d").longOpt("decrypt").hasArg().argName("in-file")
.desc("Decrypt the given file").build();
.desc("Decrypt the given file").get();
final OptionGroup OP = new OptionGroup().addOption(OPT_ENCRYPT).addOption(OPT_DECRYPT);
OP.setRequired(true);
// ---- common I/O
final Option OPT_OUT = Option.builder("o").longOpt("output").hasArg().argName("out-file")
.desc("Output file (default: <in>.enc for encrypt, <in>.dec for decrypt)").build();
.desc("Output file (default: <in>.enc for encrypt, <in>.dec for decrypt)").get();
final Option OPT_KEYRING = Option.builder().longOpt("keyring").hasArg().argName("keyring.txt")
.desc("KeyringStore file for aliases (required when aliases are used)").build();
.desc("KeyringStore file for aliases (required when aliases are used)").get();
// ---- payload selection and parameters
final Option OPT_ALG = Option.builder().longOpt("alg").hasArg().argName("name")
.desc("Payload: aes-gcm | aes-ctr | aes-cbc-pkcs7 | aes-cbc-nopad | chacha-aead | chacha-stream "
+ "(default: aes-gcm)")
.build();
.get();
final Option OPT_AAD_HEX = Option.builder("a").longOpt("aad-hex").hasArg().argName("hex")
.desc("Additional authenticated data as hex (AEAD modes)").build();
.desc("Additional authenticated data as hex (AEAD modes)").get();
final Option OPT_TAG_BITS = Option.builder().longOpt("tag-bits").hasArg().argName("96..128")
.desc("AES-GCM tag length in bits (default 128)").build();
.desc("AES-GCM tag length in bits (default 128)").get();
final Option OPT_NONCE_HEX = Option.builder().longOpt("nonce-hex").hasArg().argName("hex")
.desc("ChaCha nonce (12-byte hex)").build();
.desc("ChaCha nonce (12-byte hex)").get();
final Option OPT_INIT_CTR = Option.builder().longOpt("init-ctr").hasArg().argName("int")
.desc("ChaCha stream initial counter (default 1)").build();
.desc("ChaCha stream initial counter (default 1)").get();
final Option OPT_CTR = Option.builder().longOpt("ctr").hasArg().argName("int")
.desc("ChaCha stream counter override (propagated via context)").build();
.desc("ChaCha stream counter override (propagated via context)").get();
final Option OPT_NO_HDR = Option.builder().longOpt("no-header")
.desc("Do not write or expect a symmetric header").build();
.desc("Do not write or expect a symmetric header").get();
// ---- envelope parameters
final Option OPT_CEK_BYTES = Option.builder().longOpt("cek-bytes").hasArg().argName("len")
.desc("Payload key (CEK) length in bytes (default 32)").build();
.desc("Payload key (CEK) length in bytes (default 32)").get();
final Option OPT_MAX_RECIPS = Option.builder().longOpt("max-recipients").hasArg().argName("n")
.desc("Max recipients in the envelope header (default 64)").build();
.desc("Max recipients in the envelope header (default 64)").get();
final Option OPT_MAX_ENTRY = Option.builder().longOpt("max-entry-len").hasArg().argName("bytes")
.desc("Max single recipient-entry length (default 1048576)").build();
.desc("Max single recipient-entry length (default 1048576)").get();
final Option OPT_NO_SHUFFLE = Option.builder().longOpt("no-shuffle")
.desc("Disable shuffling of recipients (enabled by default)").build();
.desc("Disable shuffling of recipients (enabled by default)").get();
// ---- recipients (real)
final Option OPT_TO_ALIAS = Option.builder().longOpt("to-alias").hasArg().argName("alias")
.desc("Add recipient by alias from keyring (repeatable)").build();
.desc("Add recipient by alias from keyring (repeatable)").get();
final Option OPT_TO_PSW = Option.builder().longOpt("to-psw").hasArg().argName("password")
.desc("Add password recipient (repeatable)").build();
.desc("Add password recipient (repeatable)").get();
final Option OPT_PSW_ITER = Option.builder().longOpt("to-iter").hasArg().argName("n")
.desc("PBKDF2 iterations for password recipients (default 200000)").build();
.desc("PBKDF2 iterations for password recipients (default 200000)").get();
final Option OPT_PSW_SALT = Option.builder().longOpt("to-salt-len").hasArg().argName("bytes")
.desc("PBKDF2 salt length for password recipients (default 16)").build();
.desc("PBKDF2 salt length for password recipients (default 16)").get();
final Option OPT_PSW_KEK = Option.builder().longOpt("to-kek-bytes").hasArg().argName("bytes")
.desc("Derived KEK length for password recipients (default 32)").build();
.desc("Derived KEK length for password recipients (default 32)").get();
// ---- decoys (all types)
final Option OPT_DECOY_ALIAS = Option.builder().longOpt("decoy-alias").hasArg().argName("alias")
.desc("Add a decoy recipient from keyring (repeatable)").build();
.desc("Add a decoy recipient from keyring (repeatable)").get();
final Option OPT_DECOY_PSW = Option.builder().longOpt("decoy-psw").hasArg().argName("password")
.desc("Add a decoy password recipient (repeatable)").build();
.desc("Add a decoy password recipient (repeatable)").get();
final Option OPT_DECOY_PSW_RAND = Option.builder().longOpt("decoy-psw-rand").hasArg().argName("n")
.desc("Add N random decoy password recipients").build();
.desc("Add N random decoy password recipients").get();
// ---- unlock (decrypt)
final Option OPT_PRIV_ALIAS = Option.builder().longOpt("priv-alias").hasArg().argName("alias")
.desc("Unlock with private key from keyring").build();
.desc("Unlock with private key from keyring").get();
final Option OPT_PASSWORD = Option.builder("p").longOpt("password").hasArg().argName("password")
.desc("Unlock with password").build();
.desc("Unlock with password").get();
options.addOptionGroup(OP);
options.addOption(OPT_OUT);

View File

@@ -113,91 +113,91 @@ public final class Kem { // NOPMD
/** Encrypt mode: -e|--encrypt &lt;input&gt; */
public static final Option OPT_ENCRYPT = Option.builder("e").longOpt("encrypt").hasArg().argName("input")
.desc("Encrypt the input file").build();
.desc("Encrypt the input file").get();
/** Decrypt mode: -d|--decrypt &lt;input&gt; */
public static final Option OPT_DECRYPT = Option.builder("d").longOpt("decrypt").hasArg().argName("input")
.desc("Decrypt the input file").build();
.desc("Decrypt the input file").get();
/** Output path: -o|--output &lt;file&gt; */
public static final Option OPT_OUTPUT = Option.builder("o").longOpt("output").hasArg().argName("file")
.desc("Output file path (default: &lt;input&gt;.enc for encrypt, &lt;input&gt;.dec for decrypt)").build();
.desc("Output file path (default: &lt;input&gt;.enc for encrypt, &lt;input&gt;.dec for decrypt)").get();
/** Keyring path: -K|--keyring &lt;file&gt; */
public static final Option OPT_KEYRING = Option.builder("K").longOpt("keyring").hasArg().argName("file")
.desc("Path to KeyringStore file").build();
.desc("Path to KeyringStore file").get();
/** Recipient public alias (encrypt): --pub &lt;alias&gt; */
public static final Option OPT_PUB = Option.builder().longOpt("pub").hasArg().argName("alias")
.desc("Recipient public key alias (encryption)").build();
.desc("Recipient public key alias (encryption)").get();
/** Recipient private alias (decrypt): --priv &lt;alias&gt; */
public static final Option OPT_PRIV = Option.builder().longOpt("priv").hasArg().argName("alias")
.desc("Recipient private key alias (decryption)").build();
.desc("Recipient private key alias (decryption)").get();
/** KEM id: --kem &lt;id&gt; */
public static final Option OPT_KEM = Option.builder().longOpt("kem").hasArg().argName("id")
.desc("KEM algorithm id (see --list-kems)").build();
.desc("KEM algorithm id (see --list-kems)").get();
/** Discovery: --list-kems */
public static final Option OPT_LIST_KEMS = Option.builder().longOpt("list-kems")
.desc("List KEM algorithms that support ENCAPSULATE and DECAPSULATE and exit").build();
.desc("List KEM algorithms that support ENCAPSULATE and DECAPSULATE and exit").get();
/** Payload switch: --aes */
public static final Option OPT_AES = Option.builder().longOpt("aes")
.desc("Use AES payload (select mode via --aes-cipher)").build();
.desc("Use AES payload (select mode via --aes-cipher)").get();
/** Payload switch: --chacha */
public static final Option OPT_CHACHA = Option.builder().longOpt("chacha")
.desc("Use ChaCha payload (AEAD if --aad is provided, otherwise stream)").build();
.desc("Use ChaCha payload (AEAD if --aad is provided, otherwise stream)").get();
/** AAD (hex): --aad &lt;hex&gt; */
public static final Option OPT_AAD = Option.builder().longOpt("aad").hasArg().argName("hex")
.desc("Additional Authenticated Data (hex)").build();
.desc("Additional Authenticated Data (hex)").get();
/** Header toggle: --header */
public static final Option OPT_HEADER = Option.builder().longOpt("header")
.desc("Write/read a compact symmetric header (IV/AAD/params) when supported").build();
.desc("Write/read a compact symmetric header (IV/AAD/params) when supported").get();
/** HKDF: --hkdf [infoHex] */
public static final Option OPT_HKDF = Option.builder().longOpt("hkdf").optionalArg(true).hasArg().argName("infoHex")
.desc("Use HKDF-SHA256 for KEM secret; optional info as hex (default internal info)").build();
.desc("Use HKDF-SHA256 for KEM secret; optional info as hex (default internal info)").get();
/** Direct secret: --direct */
public static final Option OPT_DIRECT = Option.builder().longOpt("direct")
.desc("Use the raw KEM shared secret directly (disable HKDF)").build();
.desc("Use the raw KEM shared secret directly (disable HKDF)").get();
/** Derived key bytes: --key-bytes &lt;int&gt; */
public static final Option OPT_KEY_BYTES = Option.builder().longOpt("key-bytes").hasArg().argName("int")
.type(Number.class).desc("Derived symmetric key length in bytes (default 32)").build();
.type(Number.class).desc("Derived symmetric key length in bytes (default 32)").get();
/** Max KEM ciphertext len: --max-kem-ct &lt;int&gt; */
public static final Option OPT_MAX_KEM_CT = Option.builder().longOpt("max-kem-ct").hasArg().argName("int")
.type(Number.class).desc("Maximum accepted KEM ciphertext length in bytes (default 65536)").build();
.type(Number.class).desc("Maximum accepted KEM ciphertext length in bytes (default 65536)").get();
/** AES mode: --aes-cipher gcm|ctr|cbc */
public static final Option OPT_AES_CIPHER = Option.builder().longOpt("aes-cipher").hasArg().argName("gcm|ctr|cbc")
.desc("AES cipher variant for payload (default gcm)").build();
.desc("AES cipher variant for payload (default gcm)").get();
/** AES IV: --aes-iv &lt;hex&gt; */
public static final Option OPT_AES_IV = Option.builder().longOpt("aes-iv").hasArg().argName("hex")
.desc("AES IV/nonce (hex)").build();
.desc("AES IV/nonce (hex)").get();
/** AES tag bits: --aes-tag-bits &lt;int&gt; */
public static final Option OPT_AES_TAG_BITS = Option.builder().longOpt("aes-tag-bits").hasArg().argName("int")
.type(Number.class).desc("AES-GCM authentication tag length in bits (default 128)").build();
.type(Number.class).desc("AES-GCM authentication tag length in bits (default 128)").get();
/** ChaCha nonce: --chacha-nonce &lt;hex&gt; */
public static final Option OPT_CHACHA_NONCE = Option.builder().longOpt("chacha-nonce").hasArg().argName("hex")
.desc("ChaCha nonce (hex, usually 12 bytes)").build();
.desc("ChaCha nonce (hex, usually 12 bytes)").get();
/** ChaCha counter value: --chacha-counter &lt;int&gt; */
public static final Option OPT_CHACHA_COUNTER = Option.builder().longOpt("chacha-counter").hasArg().argName("int")
.type(Number.class).desc("ChaCha counter value for stream mode (integer)").build();
.type(Number.class).desc("ChaCha counter value for stream mode (integer)").get();
/** ChaCha initial counter: --chacha-initial &lt;int&gt; */
public static final Option OPT_CHACHA_INITIAL = Option.builder().longOpt("chacha-initial").hasArg().argName("int")
.type(Number.class).desc("ChaCha initial counter (integer)").build();
.type(Number.class).desc("ChaCha initial counter (integer)").get();
private Kem() {
// no instances

View File

@@ -167,49 +167,49 @@ public final class KeyStoreManagement { // NOPMD
// ---------------------------------------------------------------------
private static final Option KEYSTORE_OPTION = Option.builder("k").longOpt("keystore").hasArg().argName("file")
.desc("Path to keyring store").build();
.desc("Path to keyring store").get();
private static final Option LIST_ALGORITHMS_OPTION = Option.builder().longOpt("list-algorithms")
.desc("List catalog algorithms with symmetric/asymmetric support").build();
.desc("List catalog algorithms with symmetric/asymmetric support").get();
private static final Option LIST_ALIASES_OPTION = Option.builder().longOpt("list-aliases")
.desc("List aliases present in the keyring").build();
.desc("List aliases present in the keyring").get();
private static final Option GENERATE_OPTION = Option.builder().longOpt("generate")
.desc("Generate a keypair or a secret").build();
.desc("Generate a keypair or a secret").get();
private static final Option ALG_OPTION = Option.builder().longOpt("alg").hasArg().argName("id")
.desc("Algorithm id (e.g., RSA, Ed25519, AES, Frodo)").build();
.desc("Algorithm id (e.g., RSA, Ed25519, AES, Frodo)").get();
private static final Option ALIAS_OPTION = Option.builder().longOpt("alias").hasArg().argName("name")
.desc("Alias base; for asymmetric, two entries will be written").build();
.desc("Alias base; for asymmetric, two entries will be written").get();
private static final Option KIND_OPTION = Option.builder().longOpt("kind").hasArg().argName("sym|asym")
.desc("Force symmetric or asymmetric when algorithm supports both").build();
.desc("Force symmetric or asymmetric when algorithm supports both").get();
private static final Option PUB_SUFFIX_OPTION = Option.builder().longOpt("pub-suffix").hasArg().argName("sfx")
.desc("Suffix for public alias (default .pub)").build();
.desc("Suffix for public alias (default .pub)").get();
private static final Option PRV_SUFFIX_OPTION = Option.builder().longOpt("prv-suffix").hasArg().argName("sfx")
.desc("Suffix for private alias (default .prv)").build();
.desc("Suffix for private alias (default .prv)").get();
private static final Option OVERWRITE_OPTION = Option.builder().longOpt("overwrite")
.desc("Overwrite existing aliases on conflict").build();
.desc("Overwrite existing aliases on conflict").get();
private static final Option EXPORT_OPTION = Option.builder().longOpt("export")
.desc("Export selected aliases as a versioned text snippet").build();
.desc("Export selected aliases as a versioned text snippet").get();
private static final Option IMPORT_OPTION = Option.builder().longOpt("import")
.desc("Import a versioned text snippet into the keyring").build();
.desc("Import a versioned text snippet into the keyring").get();
private static final Option ALIASES_OPTION = Option.builder().longOpt("aliases").hasArg().argName("a,b,c")
.desc("Comma-separated aliases to export; empty means all").build();
.desc("Comma-separated aliases to export; empty means all").get();
private static final Option OUTFILE_OPTION = Option.builder().longOpt("out").hasArg().argName("file|-")
.desc("Output file for export (default '-' for stdout)").build();
.desc("Output file for export (default '-' for stdout)").get();
private static final Option INFILE_OPTION = Option.builder().longOpt("in").hasArg().argName("file|-")
.desc("Input file for import (default '-' for stdin)").build();
.desc("Input file for import (default '-' for stdin)").get();
/** Prevents instantiation. */
private KeyStoreManagement() {

View File

@@ -48,10 +48,10 @@ import java.util.Objects;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.help.HelpFormatter;
import zeroecho.core.alg.digest.DigestSpec;
import zeroecho.core.err.VerificationException;
@@ -103,28 +103,28 @@ public final class Tag { // NOPMD
// ---- All options as constants
private static final Option TYPE_OPT = Option.builder().longOpt("type").hasArg().argName("signature|digest")
.desc("tag primitive type").build();
.desc("tag primitive type").get();
private static final Option MODE_OPT = Option.builder().longOpt("mode").hasArg().argName("produce|verify")
.desc("operation mode").build();
.desc("operation mode").get();
private static final Option ALG_OPT = Option.builder().longOpt("alg").hasArg().argName("id")
.desc("algorithm id (signature: Ed25519/Ed448/ECDSA/RSA; digest: SHA-256/.../SHAKE256:N)").build();
.desc("algorithm id (signature: Ed25519/Ed448/ECDSA/RSA; digest: SHA-256/.../SHAKE256:N)").get();
private static final Option KS_OPT = Option.builder().longOpt("ks").hasArg().argName("file")
.desc("keyring file (required for signature)").build();
.desc("keyring file (required for signature)").get();
private static final Option PRIV_OPT = Option.builder().longOpt("priv").hasArg().argName("alias")
.desc("private key alias (signature + produce)").build();
.desc("private key alias (signature + produce)").get();
private static final Option PUB_OPT = Option.builder().longOpt("pub").hasArg().argName("alias")
.desc("public key alias (signature + verify)").build();
.desc("public key alias (signature + verify)").get();
private static final Option IN_OPT = Option.builder().longOpt("in").hasArg().argName("file|-")
.desc("input file or - for STDIN").build();
.desc("input file or - for STDIN").get();
private static final Option OUT_OPT = Option.builder().longOpt("out").hasArg().argName("file|-")
.desc("output file or - for STDOUT").build();
.desc("output file or - for STDOUT").get();
// ---- Allowed values and defaults (no enums)
private static final String TYPE_SIGNATURE = "signature";
@@ -172,7 +172,7 @@ public final class Tag { // NOPMD
if (!has(cli, TYPE_OPT) || !has(cli, MODE_OPT) || !has(cli, ALG_OPT) || !has(cli, IN_OPT)
|| !has(cli, OUT_OPT)) {
new HelpFormatter().printHelp("zeroecho -T [options]", opts);
HelpFormatter.builder().get().printHelp("zeroecho -T [options]", "", opts, "", false);
return 2;
}

View File

@@ -41,12 +41,12 @@ import java.util.logging.Logger;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.help.HelpFormatter;
import zeroecho.sdk.util.BouncyCastleActivator;
@@ -98,8 +98,9 @@ public final class ZeroEcho {
* prints the help message.
*
* @param args command-line arguments passed to the program
* @throws IOException If the output could not be written
*/
public static void main(final String[] args) {
public static void main(final String[] args) throws IOException {
final int errorCode = mainProcess(args);
if (errorCode == 0) {
@@ -116,15 +117,16 @@ public final class ZeroEcho {
*
* @param args command-line arguments passed to the program
* @return error-code
* @throws IOException If the output could not be written
*/
public static int mainProcess(final String... args) {
final Option KEM_OPTION = Option.builder("E").longOpt("kem").desc("KEM encryption/decryption").build();
public static int mainProcess(final String... args) throws IOException {
final Option KEM_OPTION = Option.builder("E").longOpt("kem").desc("KEM encryption/decryption").get();
final Option GUARD_OPTION = Option.builder("G").longOpt("guard")
.desc("multi-recipient encryption/decryption (keys+passwords), AES/ChaCha").build();
final Option KEYSTORE_OPTION = Option.builder("K").longOpt("ksm").desc("key store management").build();
final Option COVERT_OPTION = Option.builder("C").longOpt("covert").desc("covert channel processing").build();
.desc("multi-recipient encryption/decryption (keys+passwords), AES/ChaCha").get();
final Option KEYSTORE_OPTION = Option.builder("K").longOpt("ksm").desc("key store management").get();
final Option COVERT_OPTION = Option.builder("C").longOpt("covert").desc("covert channel processing").get();
final Option TAG_OPTION = Option.builder("T").longOpt("tag")
.desc("tag subcommand (signature/digest; produce/verify)").build();
.desc("tag subcommand (signature/digest; produce/verify)").get();
final OptionGroup OPERATION_GROUP = new OptionGroup();
OPERATION_GROUP.addOption(GUARD_OPTION);
@@ -186,12 +188,12 @@ public final class ZeroEcho {
* @param options The {@link Options} instance defining the available
* command-line options.
* @return always {@code 1}
* @throws IOException If the output could not be written
*/
private static int help(final Options options) {
private static int help(final Options options) throws IOException {
// automatically generate the help statement
final HelpFormatter formatter = new HelpFormatter();
formatter.setWidth(80);
formatter.printHelp(ZeroEcho.class.getName(), options);
final HelpFormatter formatter = HelpFormatter.builder().get();
formatter.printHelp(ZeroEcho.class.getName(), "", options, "", false);
return 1;
}

View File

@@ -36,12 +36,14 @@ package zeroecho;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import org.junit.jupiter.api.Test;
class ZeroEchoTest {
@Test
void testAsymetricOptionWithoutParamsReturnsOne() {
void testAsymetricOptionWithoutParamsReturnsOne() throws IOException {
System.out.println("testAsymetricOptionWithoutParamsReturnsOne");
int result = ZeroEcho.mainProcess(new String[] { "-A" });
assertEquals(1, result, "Asymetric option without parameters should return 1 (error/help)");
@@ -49,7 +51,7 @@ class ZeroEchoTest {
}
@Test
void testAesPswOptionWithoutParamsReturnsOne() {
void testAesPswOptionWithoutParamsReturnsOne() throws IOException {
System.out.println("testAesPswOptionWithoutParamsReturnsOne");
int result = ZeroEcho.mainProcess(new String[] { "-P" });
assertEquals(1, result, "AES-PSW option without parameters should return 1 (error/help)");
@@ -57,7 +59,7 @@ class ZeroEchoTest {
}
@Test
void testNoOptionReturnsOne() {
void testNoOptionReturnsOne() throws IOException {
System.out.println("testNoOptionReturnsOne");
int result = ZeroEcho.mainProcess(new String[] {});
assertEquals(1, result, "No options should return 1 (error/help)");
@@ -65,7 +67,7 @@ class ZeroEchoTest {
}
@Test
void testInvalidOptionReturnsOne() {
void testInvalidOptionReturnsOne() throws IOException {
System.out.println("testInvalidOptionReturnsOne");
int result = ZeroEcho.mainProcess(new String[] { "-X" });
assertEquals(1, result, "Invalid option should return 1 (error/help)");