Skip to content
Open
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
218 changes: 102 additions & 116 deletions src/com/lockboxlocal/entity/Model.java
Original file line number Diff line number Diff line change
@@ -1,95 +1,125 @@
package com.lockboxlocal.entity;

import javafx.util.Pair;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.sql.*;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.Base64;

public class Model {

Connection conn = null;
private Connection conn = null;

/**
* Retrieves and returns all boxes from the database.
* @return A list of the boxes' names.
*/
public ArrayList<String> getBoxes() {
// Encryption Constants
private static final String ALGO = "AES/GCM/NoPadding";
private static final int TAG_LENGTH_BIT = 128;
private static final int IV_LENGTH_BYTE = 12;

// Should move to env in producition app
private static final byte[] KEY_BYTES = "lockbox-localkey".getBytes();

ArrayList<String> result = new ArrayList<String>();
// ---- Secure GCM Encryption Methods ----

private String encrypt(String plainText) {
if (plainText == null) return null;
try {
// 1. Generate a unique IV for this specific encryption
byte[] iv = new byte[IV_LENGTH_BYTE];
new SecureRandom().nextBytes(iv);

Statement stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("select (boxName) from Boxes");
Cipher cipher = Cipher.getInstance(ALGO);
SecretKeySpec key = new SecretKeySpec(KEY_BYTES, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BIT, iv));

while(resultSet.next()) {
byte[] encryptedText = cipher.doFinal(plainText.getBytes("UTF-8"));
byte[] combined = ByteBuffer.allocate(iv.length + encryptedText.length)
.put(iv)
.put(encryptedText)
.array();

result.add(resultSet.getString("boxName"));
return Base64.getEncoder().encodeToString(combined);
} catch (Exception e) {
throw new RuntimeException("Encryption failed", e);
}
}

}
private String decrypt(String cipherText) {
if (cipherText == null) return null;
try {
byte[] combined = Base64.getDecoder().decode(cipherText);

resultSet.close();
stmt.close();

ByteBuffer bb = ByteBuffer.wrap(combined);
byte[] iv = new byte[IV_LENGTH_BYTE];
bb.get(iv);
byte[] actualCipher = new byte[bb.remaining()];
bb.get(actualCipher);

} catch(Exception e) {
e.printStackTrace();
Cipher cipher = Cipher.getInstance(ALGO);
SecretKeySpec key = new SecretKeySpec(KEY_BYTES, "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BIT, iv));

return new String(cipher.doFinal(actualCipher), "UTF-8");
} catch (Exception e) {
// If the key is wrong or data was tampered with, GCM throws an AEADBadTagException
System.err.println("Decryption failed: Integrity check failed or wrong key.");
return "DECRYPTION_ERROR";
}
}

/**
* Retrieves and returns all boxes from the database.
* @return A list of the boxes' names.
*/
public ArrayList<String> getBoxes() {
ArrayList<String> result = new ArrayList<>();
try (Statement stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("select (boxName) from Boxes")) {
while (resultSet.next()) {
result.add(resultSet.getString("boxName"));
}
} catch (Exception e) {
e.printStackTrace();
}
return result;

}

/**
* Removes a box with a given name from the database.
* @param boxName the box to delete from the database.
*/
public void deleteBox(String boxName) {

try {

PreparedStatement stmt = conn.prepareStatement("delete from Boxes where boxName = ?");
try (PreparedStatement stmt = conn.prepareStatement("delete from Boxes where boxName = ?")) {
stmt.setString(1, boxName);
stmt.executeUpdate();
stmt.close();

} catch (Exception e) {
e.printStackTrace();
}

}

/**
* Returns true if a box with a given name already
* exists in the database. Returns false otherwise.
*/
public boolean boxExists(String name) {

boolean result = false;

try {

PreparedStatement stmt = conn.prepareStatement("select * from Boxes where boxName = ?");
try (PreparedStatement stmt = conn.prepareStatement("select 1 from Boxes where boxName = ?")) {
stmt.setString(1, name);

ResultSet rset = stmt.executeQuery();

result = rset.next();

rset.close();
stmt.close();

} catch(Exception e) {
try (ResultSet rset = stmt.executeQuery()) {
return rset.next();
}
} catch (Exception e) {
e.printStackTrace();
return false;
}

return result;

}

/**
Expand All @@ -100,31 +130,20 @@ public boolean boxExists(String name) {
* @param relockDelay The relock delay of this lockbox.
*/
public void createBox(String name, String contents, int unlockDelay, int relockDelay) {

try {

PreparedStatement stmt = conn.prepareStatement("insert into Boxes " +
"(boxName, content, locked, relockTimestamp, unlockTimestamp, unlockDelay, relockDelay)" +
"values" +
"(?,?,?,?,?,?,?)");

try (PreparedStatement stmt = conn.prepareStatement("insert into Boxes " +
"(boxName, content, locked, relockTimestamp, unlockTimestamp, unlockDelay, relockDelay) " +
"values (?,?,?,?,?,?,?)")) {
stmt.setString(1, name);
stmt.setString(2, contents);
stmt.setString(2, encrypt(contents));
stmt.setInt(3, 1);
stmt.setInt(4, 0);
stmt.setNull(5, Types.INTEGER);

stmt.setInt(6, unlockDelay);
stmt.setInt(7, relockDelay);

stmt.executeUpdate();

stmt.close();

} catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}

}

/**
Expand All @@ -133,77 +152,47 @@ public void createBox(String name, String contents, int unlockDelay, int relockD
* @param name The name of the box to retrieve.
*/
public Lockbox getBox(String name) {

Lockbox result = null;

try {

PreparedStatement stmt = conn.prepareStatement("select * from Boxes where boxName = ?");
try (PreparedStatement stmt = conn.prepareStatement("select * from Boxes where boxName = ?")) {
stmt.setString(1, name);

ResultSet rset = stmt.executeQuery();

if(rset.next()) {

String tempName = rset.getString("boxName");
String contents = rset.getString("content");
int locked = rset.getInt("locked");
long relockTimestamp = rset.getLong("relockTimestamp");
Long unlockTimestamp = rset.getLong("unlockTimestamp");
long unlockDelay = rset.getLong("unlockDelay");
long relockDelay = rset.getLong("relockDelay");

result = new Lockbox(tempName, contents, locked, relockTimestamp, unlockTimestamp, unlockDelay, relockDelay);

try (ResultSet rset = stmt.executeQuery()) {
if (rset.next()) {
return new Lockbox(
rset.getString("boxName"),
decrypt(rset.getString("content")),
rset.getInt("locked"),
rset.getLong("relockTimestamp"),
rset.getLong("unlockTimestamp"),
rset.getLong("unlockDelay"),
rset.getLong("relockDelay")
);
}
}

rset.close();
stmt.close();

} catch (Exception e) {
e.printStackTrace();
}

return result;

return null;
}

/**
* Updates the data of a lockbox in the database.
* @param lockbox the lockbox to update.
*/
public void updateBox(Lockbox lockbox) {

try {

PreparedStatement stmt = conn.prepareStatement("update Boxes set " +
"content = ?," +
"locked = ?," +
"relockTimestamp = ?," +
"unlockTimestamp = ?" +
" where boxName = ?");

stmt.setString(1, lockbox.contents);
try (PreparedStatement stmt = conn.prepareStatement("update Boxes set " +
"content = ?, locked = ?, relockTimestamp = ?, unlockTimestamp = ? where boxName = ?")) {
stmt.setString(1, encrypt(lockbox.contents));
stmt.setInt(2, lockbox.locked);
stmt.setLong(3, lockbox.relockTimestamp);

if(lockbox.unlockTimestamp == null) {
stmt.setNull(4, Types.INTEGER);
} else {
stmt.setLong(4, lockbox.unlockTimestamp);
}

if (lockbox.unlockTimestamp == null) stmt.setNull(4, Types.INTEGER);
else stmt.setLong(4, lockbox.unlockTimestamp);
stmt.setString(5, lockbox.name);

stmt.executeUpdate();

} catch (Exception e) {
e.printStackTrace();
}

}

/**
/**
* Import the data from a .lbf file, inserting
* its data into the model's database.
* Returns the following pair:
Expand Down Expand Up @@ -240,7 +229,7 @@ public Pair<Long,Long> importBoxes(File importFile) {
"(?,?,?,?,?,?,?)");

stmt.setString(1, dataArr[0]);
stmt.setString(2, dataArr[1]);
stmt.setString(2, encrypt(dataArr[1]));
stmt.setInt(3, Integer.parseInt(dataArr[2]));
stmt.setInt(4, Integer.parseInt(dataArr[3]));
stmt.setInt(5, Integer.parseInt(dataArr[4]));
Expand Down Expand Up @@ -296,7 +285,7 @@ public boolean exportDB(String filePath) {
ArrayList<String> dataList = new ArrayList<String>();

dataList.add(rset.getString("boxName"));
dataList.add(rset.getString("content"));
dataList.add(decrypt(rset.getString("content")));
dataList.add(String.valueOf(rset.getInt("locked")));
dataList.add(String.valueOf(rset.getInt("relockTimestamp")));
dataList.add(String.valueOf(rset.getInt("unlockTimestamp")));
Expand Down Expand Up @@ -324,8 +313,6 @@ public boolean exportDB(String filePath) {

}



public Model() {

try {
Expand All @@ -351,5 +338,4 @@ public Model() {
}

}

}
}