Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,15 @@ jobs:
name: jacoco-report
path: target/site/jacoco

- name: Start Tool
run: java -jar target/JavaCrypt.jar
- name: Start Tool (usage must exit with code 1)
run: |
set +e
java -jar target/JavaCrypt.jar
code=$?
if [ "$code" -ne 1 ]; then
echo "Expected exit code 1, got $code"
exit 1
fi

- name: Generate Keys
run: java -jar target/JavaCrypt.jar -genkeys priv.key pub.key
Expand Down
34 changes: 19 additions & 15 deletions src/main/java/javacrypt/MyCryptMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class MyCryptMain {
);

/**
* Displays the usage of the program and exits.
* Displays the usage of the program.
*/
private static void usage() {
String[] msg = {
Expand All @@ -51,9 +51,6 @@ private static void usage() {

// Output the strings
Arrays.stream(msg).forEach(System.err::println);

// Exit the program with an error code
System.exit(0);
}

/**
Expand All @@ -63,24 +60,30 @@ private static void usage() {
* @throws Exception If an error occurs.
*/
public static void main(String[] args) throws Exception {
int exitCode = execute(args);
if (exitCode != 0) {
System.exit(exitCode);
}
}

/**
* Runs the program without terminating the JVM.
*
* @param args The command-line arguments.
* @return The exit code: 0 on success, 1 if the usage was wrong.
* @throws Exception If an error occurs.
*/
static int execute(String[] args) throws Exception {
// Factory instance
RunnableKeyValueFactory runnableKeyValueFactory = new RunnableKeyValueFactory(MY_ARRAY.toArray(new String[0][]));

// Get the number of command-line arguments
int argSize = args.length;

if (argSize == 0) {
usage(); // Static method
if (args.length == 0 || !runnableKeyValueFactory.containsKey(args[0])) {
usage();
return 1;
}

// Get the control argument
String cmdKey = args[0];

// Check if the command exists
if (!runnableKeyValueFactory.containsKey(cmdKey)) {
usage();
}

// Perform the shift operation
List<String> optArgList = Arrays.asList(args).subList(1, args.length);

Expand All @@ -91,5 +94,6 @@ public static void main(String[] args) throws Exception {
myRun.run(optArgList);

System.out.println("End of the program.");
return 0;
}
}
4 changes: 1 addition & 3 deletions src/main/java/javacrypt/RunEncrypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ protected Cipher getCipher(Key key) {
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException ex) {
System.err.println("Exception: " + ex.getMessage());
System.exit(8);
return null; // Unreachable code, added to satisfy the compiler
throw new IllegalStateException("Cannot create cipher: " + ex.getMessage(), ex);
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/main/java/javacrypt/RunGenKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,12 @@ public void run(List<String> args) {
*
* @param key The key to write.
* @param file The file to write the key to.
* @throws IOException If the file cannot be written.
*/
private void writeKeyToFile(Key key, File file) {
private void writeKeyToFile(Key key, File file) throws IOException {
try (FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(key);
} catch (IOException e) {
System.err.println("Exception(writeKeyToFile): " + e.getMessage());
System.exit(2);
}
}

Expand Down
107 changes: 107 additions & 0 deletions src/test/java/javacrypt/ErrorPathTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package javacrypt;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class ErrorPathTest {

@TempDir
Path dir;

private final PrintStream originalErr = System.err;
private final ByteArrayOutputStream err = new ByteArrayOutputStream();

@BeforeEach
void captureErr() {
System.setErr(new PrintStream(err, true, StandardCharsets.UTF_8));
}

@AfterEach
void restoreErr() {
System.setErr(originalErr);
}

private String errText() {
return new String(err.toByteArray(), StandardCharsets.UTF_8);
}

@Test
void encryptWithMissingKeyFileReportsErrorAndWritesNothing() {
Path out = dir.resolve("out.dat");

new RunEncrypt().run(Arrays.asList(
dir.resolve("missing.key").toString(), dir.resolve("in.dat").toString(), out.toString()));

assertTrue(errText().contains("EXCEPTION: run"));
assertFalse(Files.exists(out));
}

@Test
void decryptWithMissingInputReportsError() {
Path priv = dir.resolve("priv.key");
new RunGenKeys().run(Arrays.asList(priv.toString(), dir.resolve("pub.key").toString()));

new RunDecrypt().run(Arrays.asList(
priv.toString(), dir.resolve("missing.dat").toString(), dir.resolve("out.dat").toString()));

assertTrue(errText().contains("EXCEPTION: run"));
}

@Test
void decryptWithWrongKeyFails() throws Exception {
Path priv1 = dir.resolve("priv1.key");
Path pub1 = dir.resolve("pub1.key");
Path priv2 = dir.resolve("priv2.key");
Path pub2 = dir.resolve("pub2.key");
new RunGenKeys().run(Arrays.asList(priv1.toString(), pub1.toString()));
new RunGenKeys().run(Arrays.asList(priv2.toString(), pub2.toString()));
Path in = dir.resolve("in.txt");
Path enc = dir.resolve("enc.dat");
Path dec = dir.resolve("dec.txt");
Files.write(in, "secret".getBytes(StandardCharsets.UTF_8));

new RunEncrypt().run(Arrays.asList(pub1.toString(), in.toString(), enc.toString()));
new RunDecrypt().run(Arrays.asList(priv2.toString(), enc.toString(), dec.toString()));

assertTrue(errText().contains("Exception"));
assertEquals(0, Files.exists(dec) ? Files.size(dec) : 0);
}

@Test
void genKeysToUnwritablePathReportsErrorWithoutExit() {
Path bad = dir.resolve("no-such-dir").resolve("priv.key");

new RunGenKeys().run(Arrays.asList(bad.toString(), dir.resolve("pub.key").toString()));

assertTrue(errText().contains("IOException"));
assertFalse(Files.exists(dir.resolve("pub.key")));
}

@Test
void copyWithMissingInputReportsError() {
new RunCopy().run(Arrays.asList(dir.resolve("missing").toString(), dir.resolve("out").toString()));

assertTrue(errText().contains("Exception"));
}

@Test
void genKeysDoesNotSupportCrypt() {
RunGenKeys genKeys = new RunGenKeys();

assertThrows(UnsupportedOperationException.class, () -> genKeys.crypt(new byte[0], null, null));
assertEquals(100, genKeys.getCryptoBufSize());
}
}
79 changes: 79 additions & 0 deletions src/test/java/javacrypt/MainTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package javacrypt;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class MainTest {

@TempDir
Path dir;

private final PrintStream originalErr = System.err;
private final ByteArrayOutputStream err = new ByteArrayOutputStream();

@BeforeEach
void captureErr() {
System.setErr(new PrintStream(err, true, StandardCharsets.UTF_8));
}

@AfterEach
void restoreErr() {
System.setErr(originalErr);
}

private String errText() {
return new String(err.toByteArray(), StandardCharsets.UTF_8);
}

@Test
void noArgumentsPrintsUsageAndFails() throws Exception {
assertEquals(1, MyCryptMain.execute(new String[0]));
assertTrue(errText().contains("Usage:"));
}

@Test
void unknownCommandPrintsUsageAndFails() throws Exception {
assertEquals(1, MyCryptMain.execute(new String[]{"-bogus"}));
assertTrue(errText().contains("Usage:"));
}

@Test
void fullCycleThroughMain() throws Exception {
Path priv = dir.resolve("priv.key");
Path pub = dir.resolve("pub.key");
Path in = dir.resolve("in.txt");
Path enc = dir.resolve("enc.dat");
Path dec = dir.resolve("dec.txt");
byte[] plain = "A text that is clearly longer than one hundred bytes so that several RSA blocks are needed for it. Yes."
.getBytes(StandardCharsets.UTF_8);
Files.write(in, plain);

assertEquals(0, MyCryptMain.execute(new String[]{"-genkeys", priv.toString(), pub.toString()}));
assertEquals(0, MyCryptMain.execute(new String[]{"-encrypt", pub.toString(), in.toString(), enc.toString()}));
assertEquals(0, MyCryptMain.execute(new String[]{"-decrypt", priv.toString(), enc.toString(), dec.toString()}));

assertArrayEquals(plain, Files.readAllBytes(dec));
}

@Test
void copyThroughMain() throws Exception {
Path in = dir.resolve("in.txt");
Path out = dir.resolve("out.txt");
Files.write(in, new byte[]{1, 2, 3});

assertEquals(0, MyCryptMain.execute(new String[]{"-copy", in.toString(), out.toString()}));

assertArrayEquals(new byte[]{1, 2, 3}, Files.readAllBytes(out));
}
}
Loading