
eu.clarussecure.dataoperations.encryption.EncryptionModule Maven / Gradle / Ivy
The newest version!
package eu.clarussecure.dataoperations.encryption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import org.postgis.PGbox2d;
import org.postgis.Point;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import eu.clarussecure.dataoperations.AttributeNamesUtilities;
import eu.clarussecure.dataoperations.Criteria;
import eu.clarussecure.dataoperations.DataOperation;
import eu.clarussecure.dataoperations.DataOperationCommand;
import eu.clarussecure.dataoperations.DataOperationResult;
import eu.clarussecure.dataoperations.geometry.GeometryBuilder;
import eu.clarussecure.dataoperations.geometry.ProjectedCRS;
public class EncryptionModule implements DataOperation {
// This string is a flag to identify attributes that are not covered in the
// security policy
// It is used actively by the HEAD function.
protected static final String TO_BE_FILTERED_FLAG = "NOT_COVERED";
// Data extracted from the security policy
// NOTE: key set of attributeTypes CAN HAVE wildcards to match more than one
// attribute
protected Map attributeTypes = new HashMap<>(); // qualifName->type
protected Map dataTypes = new HashMap<>(); // qualifName->data
// type
protected Map typesProtection = new HashMap<>(); // type->protectionModule
protected Map typesDataIDs = new HashMap<>(); // type->idKey
protected KeyStore keyStore = KeyStore.getInstance();
// Mapping to determine where to store each qualified name
protected int cloudsNumber;
// protected Map attributeClouds = new HashMap<>();
public EncryptionModule(Document policy) {
// TODO - Extract the number of "endpoints" (aka Clouds) from the
// policy.
// At this point this number WILL BE HARD CODED!!!
this.cloudsNumber = 1;
// First, get the types of each attribute and build the map
NodeList nodes = policy.getElementsByTagName("attribute");
for (int i = 0; i < nodes.getLength(); i++) {
// Get the node and the list of its attributes
Node node = nodes.item(i);
NamedNodeMap attributes = node.getAttributes();
// Extract the required attributes
String attributeName = attributes.getNamedItem("name").getNodeValue();
String attributeType = attributes.getNamedItem("attribute_type").getNodeValue();
String dataType = attributes.getNamedItem("data_type").getNodeValue();
// Add the information to the map
this.attributeTypes.put(attributeName, attributeType);
this.dataTypes.put(attributeName, dataType);
}
// Second , get the protection of each attribute type and their idKeys
nodes = policy.getElementsByTagName("attribute_type");
for (int i = 0; i < nodes.getLength(); i++) {
// Get the node and the list of its attributes
Node node = nodes.item(i);
NamedNodeMap attributes = node.getAttributes();
// Extract the reuqired attributes
String attributeType = attributes.getNamedItem("type").getNodeValue();
String typeProtection = attributes.getNamedItem("protection").getNodeValue();
// Add the information to the map
this.typesProtection.put(attributeType, typeProtection);
// Get the idKey only if the protection module is "encryption" or
// "simple"
if (typeProtection.equals("encryption") || typeProtection.equals("simple")) {
String dataID = attributes.getNamedItem("id_key").getNodeValue();
this.typesDataIDs.put(attributeType, dataID);
}
}
// FIXME - Should the policy specify in which cloud to store the
// encrypted data?
// If so, this information should be available in the "attribute_type"
// tag
// so the "mapping" showing where to store each attribute should be
// built here.
/*
* Example:
*
* ...
*
*/
// At the moment, the mapping will be done assuming the encrypted
// attributes go to the first cloud
// this.forEach(qualifiedName -> this.attributeClouds.put(qualifiedName,
// 0));
}
@Override
protected void finalize() throws Throwable {
super.finalize();
this.keyStore.deleteInstance();
}
@Override
public List get(String[] attributeNames, Criteria[] criteria) {
// IMPORTANT REMARK:
// Since the encryption is not homomorphic, all the data must be
// retrieved
// The selection of the rows will be done in the outboud GET, after
// decrypting the data
Map attributesMapping = this.buildAttributesMapping(attributeNames,
notCoveredAttribute -> notCoveredAttribute, unprotectedAttrib -> unprotectedAttrib);
// First, Generate the ORDERED list of the protected attributeNames
List protectedAttributes = new ArrayList<>();
Stream.of(attributeNames)
.forEach(attributeName -> protectedAttributes.add(attributesMapping.get(attributeName)));
// Second, process the Criteria to transform the requested
// AttributeNames to the protected ones
if (criteria != null) {
Stream.of(criteria).forEach(criterion -> {
// Determine if the column is encrypted of not
String protectedAttribute = attributesMapping.get(criterion.getAttributeName());
if (!criterion.getAttributeName().equals(protectedAttribute)) {
// The protected and unprotected Attribute Names do not
// match
// This implies the criteria operates over an encrypted
// column
// // First, modify the operator to use a String comparator
// criterion.setOperator("s=");
// Second, encrypt the treshold
String protectedThreshold = "";
try {
// Find which "protectionRule" (in the keyset of attributeTypes) matches the given attribute name
String matchedProtection = null;
for(String protectionRule : this.attributeTypes.keySet()){
Pattern p = Pattern.compile(AttributeNamesUtilities.escapeRegex(protectionRule));
if(p.matcher(criterion.getAttributeName()).matches()){
matchedProtection = protectionRule;
}
}
// If none matches, ignore this attribute => it is not convered by the Policy
if(matchedProtection == null)
return;
// Obtain the dataID
String dataID = this.typesDataIDs.get(this.attributeTypes.get(matchedProtection));
// Get the prpteciton type of this attribute
String protection = this.typesProtection
.get(this.attributeTypes.get(matchedProtection));
// Encrypt only if the protection type is "encryption"
// or "simple"
if (protection.equals("encryption") || protection.equals("simple")) {
byte[] bytesAttribEnc;
// Initialize the Secret Key and the Init Vector of
// the Cipher
IvParameterSpec iv = new IvParameterSpec(this.keyStore.retrieveInitVector(dataID));
SecretKey sk = this.keyStore.retrieveKey(dataID);
if (this.dataTypes.get(matchedProtection).equals("geometric_object")) {
String value = criterion.getValue();
if (criterion.getOperator().equals("area")) {
String[] area = value.split(",");
value = String.format("SRID=%s;BOX(%s %s, %s %s)", area[4].trim(), area[0].trim(),
area[1].trim(), area[2].trim(), area[3].trim());
}
GeometryBuilder builder = new GeometryBuilder();
Object geom = builder.decode(value);
if (geom != null) {
if (geom instanceof Point) {
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, sk, iv);
// NOTE - To correctly encrypt, just cipher coordinates
Point point = (Point) geom;
double maxX, maxY;
int srid = point.getSrid();
if (srid != 0) {
ProjectedCRS crs = ProjectedCRS.resolve(srid);
maxX = crs.getAxis("x").getMax();
maxY = crs.getAxis("y").getMax();
} else {
maxX = Double.MAX_VALUE;
maxY = Double.MAX_VALUE;
}
point.x = encryptDouble(cipher, point.x, maxX);
point.y = encryptDouble(cipher, point.y, maxY);
} else if (geom instanceof PGbox2d) {
PGbox2d box = (PGbox2d) geom;
int srid = box.getLLB().getSrid();
if (srid != 0) {
ProjectedCRS crs = ProjectedCRS.resolve(srid);
box.getLLB().x = crs.getAxis("x").getMin();
box.getLLB().y = crs.getAxis("y").getMin();
box.getURT().x = crs.getAxis("x").getMax();
box.getURT().y = crs.getAxis("y").getMax();
} else {
box.getLLB().x = -Double.MAX_VALUE;
box.getLLB().y = -Double.MAX_VALUE;
box.getURT().x = Double.MAX_VALUE;
box.getURT().y = Double.MAX_VALUE;
}
if (criterion.getOperator().equals("area")) {
value = box.getLLB().x + ", " + box.getLLB().y + ", " + box.getURT().x + ", " + box.getURT().y + ", " + srid;
}
}
if (!criterion.getOperator().equals("area")) {
value = builder.encode(geom);
}
}
protectedThreshold = value;
} else {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sk, iv);
// NOTE - To correctly encrypt, First cipher, THEN
// Base64 encode
String value = criterion.getValue() != null ? criterion.getValue() : "clarus_null";
bytesAttribEnc = cipher.doFinal(value.getBytes());
protectedThreshold = Base64.getEncoder().encodeToString(bytesAttribEnc);
}
} else {
// Otherwise, just let the value pass in plain text
protectedThreshold = criterion.getValue();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
criterion.setValue(protectedThreshold);
// Third, substitute the involved attribute name with its
// protected one
criterion.setAttributeName(attributesMapping.get(criterion.getAttributeName()));
}
});
}
// Third, create the Comman object
DataOperationCommand command = new EncryptionCommand(attributeNames,
protectedAttributes.toArray(new String[attributeNames.length]), null, attributesMapping, criteria);
List commands = new ArrayList<>();
commands.add(command);
return commands;
}
@Override
public List get(List promise, List contents) {
// Iterate over all the given commands
List commands = new ArrayList<>();
int rowCount = 0;
for (int n = 0; n < promise.size(); n++) {
DataOperationCommand com = promise.get(n);
String[][] content = contents.get(n);
String[] plainAttributeNames = new String[com.getProtectedAttributeNames().length];
List plainContents = new ArrayList<>();
Map mapAttributes = new HashMap<>();
Base64.Decoder decoder = Base64.getDecoder();
// Second, decipher the attribute names
try {
// First, decipher the attribute Names and map them to the
// origial ones
for (int i = 0; i < com.getProtectedAttributeNames().length; i++) {
/*
* // Get the proteciton type of this attribute String
* protection =
* this.typesProtection.get(this.attributeTypes.get(
* matchedProtection));
*
* // Decrypt only if the protection type is "encryption" or
* "simple" if (protection.equals("encryption") ||
* protection.equals("simple")) { // Obtain the dataID
* String dataID =
* this.typesDataIDs.get(this.attributeTypes.get(
* matchedProtection));
*
* // Initialize the Secret Key and the Init Vector of the
* Cipher IvParameterSpec iv = new
* IvParameterSpec(this.keyStore.retrieveInitVector(dataID))
* ; SecretKey sk = this.keyStore.retrieveKey(dataID);
*
* // Initialize the required instances of Ciphers Cipher
* cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
* cipher.init(Cipher.DECRYPT_MODE, sk, iv); // NOTE - To
* correctly decrypt, first Base64 decode, THEN decipher
* byte[] bytesEncAttributeName = cipher
* .doFinal(decoder.decode(com.getProtectedAttributeNames()[
* i])); plainAttributeNames[i] = new
* String(bytesEncAttributeName); } else {
* plainAttributeNames[i] =
* com.getProtectedAttributeNames()[i]; }
*/
if (com.getProtectedAttributeNames()[i].endsWith("_enc")) {
plainAttributeNames[i] = com.getProtectedAttributeNames()[i].substring(0,
com.getProtectedAttributeNames()[i].length() - 4);
} else {
plainAttributeNames[i] = com.getProtectedAttributeNames()[i];
}
mapAttributes.put(com.getProtectedAttributeNames()[i], plainAttributeNames[i]);
}
// Second, decipher the contents
for (int i = 0; i < content.length; i++) {
String[] row = new String[plainAttributeNames.length]; // Reconstructed
// row
for (int j = 0; j < plainAttributeNames.length; j++) {
// Find which "protectionRule" (in the keyset of
// attributeTypes) matches the given attribute name
String matchedProtection = null;
for (String protectionRule : this.attributeTypes.keySet()) {
Pattern p = Pattern.compile(AttributeNamesUtilities.escapeRegex(protectionRule));
if (p.matcher(plainAttributeNames[j]).matches()) {
matchedProtection = protectionRule;
}
}
// If none matches, ignore this attribute => it is not
// convered by the Policy
if (matchedProtection == null)
continue;
String plainValue;
// Get the proteciton type of this attribute
String protection = this.typesProtection.get(this.attributeTypes.get(matchedProtection));
// Decrypt only if the protection type is "encryption"
// or "simple"
if (protection.equals("encryption") || protection.equals("simple")) {
// Get the dataID
String dataID = this.typesDataIDs.get(this.attributeTypes.get(matchedProtection));
// Get the Key, Initialization Vector and initialize
// the Cipher
SecretKey key = this.keyStore.retrieveKey(dataID);
IvParameterSpec iv = new IvParameterSpec(this.keyStore.retrieveInitVector(dataID));
if (this.dataTypes.get(matchedProtection).equals("geometric_object")) {
String value = content[i][j];
GeometryBuilder builder = new GeometryBuilder();
Object geom = builder.decode(value);
if (geom != null) {
if (geom instanceof Point) {
// Initialize the required instances of Ciphers
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
// NOTE - To correctly encrypt, just cipher coordinates
Point point = (Point) geom;
double maxX, maxY;
int srid = point.getSrid();
if (srid == 0) {
srid = Arrays.stream(com.getCriteria())
.filter(c -> c.getOperator().equals("area")).findFirst()
.map(Criteria::getValue).map(v -> v.split(",")).map(tk -> tk[4])
.map(String::trim).map(Integer::parseInt).orElse(0);
}
if (srid != 0) {
ProjectedCRS crs = ProjectedCRS.resolve(srid);
maxX = crs.getAxis("x").getMax();
maxY = crs.getAxis("y").getMax();
} else {
maxX = Double.MAX_VALUE;
maxY = Double.MAX_VALUE;
}
point.x = decryptDouble(cipher, point.x, maxX);
point.y = decryptDouble(cipher, point.y, maxY);
} else if (geom instanceof PGbox2d) {
PGbox2d box = (PGbox2d) geom;
int srid = box.getLLB().getSrid();
if (srid == 0) {
srid = Arrays.stream(com.getCriteria())
.filter(c -> c.getOperator().equals("area")).findFirst()
.map(Criteria::getValue).map(v -> v.split(",")).map(tk -> tk[4])
.map(String::trim).map(Integer::parseInt).orElse(0);
}
if (srid != 0) {
ProjectedCRS crs = ProjectedCRS.resolve(srid);
box.getLLB().x = crs.getAxis("x").getMin();
box.getLLB().y = crs.getAxis("y").getMin();
box.getURT().x = crs.getAxis("x").getMax();
box.getURT().y = crs.getAxis("y").getMax();
} else {
box.getLLB().x = -Double.MAX_VALUE;
box.getLLB().y = -Double.MAX_VALUE;
box.getURT().x = Double.MAX_VALUE;
box.getURT().y = Double.MAX_VALUE;
}
}
value = builder.encode(geom);
}
plainValue = value;
} else {
// Initialize the required instances of Ciphers
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
// Decipher the value
// NOTE - To correctly decrypt, first Base64
// decode, THEN decipher
byte[] bytesDecContent = cipher.doFinal(decoder.decode(content[i][j].getBytes()));
plainValue = new String(bytesDecContent);
if ("clarus_null".equals(plainValue)) {
plainValue = null;
}
}
} else {
// Simply copy the content
plainValue = content[i][j];
}
// Add the computed value (deciphered or not to the row
row[j] = plainValue;
}
// Add the row to the final result
plainContents.add(row);
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
// Encapsulate the output
DataOperationResult command = new EncryptionResult(plainAttributeNames,
plainContents.toArray(new String[rowCount][plainAttributeNames.length]));
commands.add(command);
}
return commands;
}
@Override
public List post(String[] attributeNames, String[][] contents) {
String[][] encContents = new String[contents.length][attributeNames.length];
Base64.Encoder encoder = Base64.getEncoder();
// Create the mapping between the given Attribute Names and the protected ones.
// This method uses the "buildAttributesMapping" function, letting the not covered and unprotected attributes pass
// (i.e. not marking them since this mapping WILL NOT be filtered later)
Map attributesMapping = this.buildAttributesMapping(attributeNames, notCoveredAttrib -> notCoveredAttrib, unprotectedAttrib -> unprotectedAttrib);
try {
byte[] bytesContentEnc;
// Second, obfuscate the contents
for (int i = 0; i < contents.length; i++) {
for (int j = 0; j < attributeNames.length; j++) {
// Get the prpteciton type of this attribute
// Find which "protectionRule" (in the keyset of attributeTypes) matches the given attribute name
String matchedProtection = null;
for(String protectionRule : this.attributeTypes.keySet()){
Pattern p = Pattern.compile(AttributeNamesUtilities.escapeRegex(protectionRule));
if(p.matcher(attributeNames[j]).matches()){
matchedProtection = protectionRule;
}
}
// If none matches, ignore this attribute => it is not convered by the Policy
if(matchedProtection == null)
continue;
String protection = this.typesProtection.get(this.attributeTypes.get(matchedProtection));
// Encrypt only if the protection type is "encryption" or "simple"
if (protection.equals("encryption") || protection.equals("simple")) {
// Get the dataID
String dataID = this.typesDataIDs.get(this.attributeTypes.get(matchedProtection));
// Get the Key, Initialization Vector and initialize the Cipher
SecretKey key = this.keyStore.retrieveKey(dataID);
IvParameterSpec iv = new IvParameterSpec(this.keyStore.retrieveInitVector(dataID));
if (this.dataTypes.get(matchedProtection).equals("geometric_object")) {
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
String value = contents[i][j];
GeometryBuilder builder = new GeometryBuilder();
Object geom = builder.decode(value);
if (geom != null) {
// NOTE - To correctly encrypt, just cipher coordinates
if (geom instanceof Point) {
Point point = (Point) geom;
double maxX, maxY;
int srid = point.getSrid();
if (srid != 0) {
ProjectedCRS crs = ProjectedCRS.resolve(srid);
maxX = crs.getAxis("x").getMax();
maxY = crs.getAxis("y").getMax();
} else {
maxX = Double.MAX_VALUE;
maxY = Double.MAX_VALUE;
}
point.x = encryptDouble(cipher, point.x, maxX);
point.y = encryptDouble(cipher, point.y, maxY);
}
value = builder.encode(geom);
}
encContents[i][j] = value;
} else {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
// NOTE - To correctly encrypt, First cipher, THEN Base64 encode
String content = contents[i][j] != null ? contents[i][j] : "clarus_null";
bytesContentEnc = cipher.doFinal(content.getBytes());
encContents[i][j] = encoder.encodeToString(bytesContentEnc);
}
} else {
// Simply copy the content
encContents[i][j] = contents[i][j];
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
// Generate the ORDERED list of the protected attributeNames
List protectedAttributes = new ArrayList<>();
Stream.of(attributeNames)
.forEach(attributeName -> protectedAttributes.add(attributesMapping.get(attributeName)));
// Encapsulate the output
DataOperationCommand command = new EncryptionCommand(attributeNames,
protectedAttributes.toArray(new String[attributeNames.length]), encContents, attributesMapping,
null);
List commands = new ArrayList<>();
commands.add(command);
return commands;
}
private double encryptDouble(Cipher cipher, double value, double maxValue)
throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
long rawBits = Double.doubleToRawLongBits(value); // e.g. clear: 0xa3412345678abcde
// separate encryption of the exponent and of the significand to
// preserve validity regarding the range of valid values
long sign = rawBits & 0x8000000000000000L; // e.g. clear: 0x8000000000000000
long exponent = rawBits & 0x7ff0000000000000L; // e.g. clear: 0x2340000000000000
long significand = rawBits & 0x000fffffffffffffL; // e.g. clear: 0x00012345678abcde
// encrypt significand using the provided cipher
significand = significand << 4; // e.g. clear: 0x0012345678abcde0
byte[] bytesContent = toByteArray(significand);
bytesContent = swapByteArray(bytesContent); // e.g. clear: 0xe0cdab7856341200
bytesContent[0] = (byte) ((bytesContent[0] & 0xff) >>> 4); // e.g. clear: 0x0ecdab7856341200
significand = toLong(bytesContent);
significand = significand >>> 8; // e.g. clear: 0x000ecdab78563412
bytesContent = toByteArray(significand);
byte[] bytesContentEnc = new byte[bytesContent.length];
bytesContentEnc[0] = bytesContent[0]; // e.g. encrypted: 0x0000000000000000
bytesContentEnc[1] = bytesContent[1]; // e.g. encrypted: 0x000e000000000000
cipher.doFinal(bytesContent, 2, bytesContent.length - 2, bytesContentEnc, 2);
significand = toLong(bytesContentEnc); // e.g. encrypted: 0x000e(cdab78563412)
// encrypt the exponent using XOR cipher
short expo = (short) (exponent >>> 52); // e.g. clear: 0x0234
long rawBitsMax = Double.doubleToRawLongBits(maxValue); // e.g. clear: 0xc1731940863d70a4
long exponentMax = rawBitsMax & 0x7ff0000000000000L; // e.g. clear: 0xc170000000000000
short expoMax = (short) (exponentMax >>> 52); // e.g. clear: 0x0c17
int highestLeadingBit = 32 - Integer.numberOfLeadingZeros(expoMax) - 1; // e.g. 10
short lowestBitsMask = (short) ((1 << highestLeadingBit) - 1); // e.g. 0x03ff
short highestBitsMask = (short) ~lowestBitsMask; // e.g. 0xfc00
short xorMask = (short) (expoMax & lowestBitsMask); // e.g. 0x0017
expo = (short) ((expo & highestBitsMask) // e.g. encrypted: 0x02(23)
| ((expo & lowestBitsMask) ^ xorMask));
exponent = (long) expo << 52; // e.g. encrypted: 0x2(23)0000000000000
rawBits = sign | exponent | significand; // e.g. encrypted: 0xa(23)e(cdab78563412)
value = Double.longBitsToDouble(rawBits);
return value;
}
private double decryptDouble(Cipher cipher, double value, double maxValue)
throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
long rawBits = Double.doubleToRawLongBits(value); // e.g. encrypted: 0xa(23)e(cdab78563412)
// separate decryption of the exponent and of the significand to
// preserve validity regarding the range of valid values
long sign = rawBits & 0x8000000000000000L; // e.g. encrypted: 0x8000000000000000
long exponent = rawBits & 0x7ff0000000000000L; // e.g. encrypted: 0x2(23)0000000000000
long significand = rawBits & 0x000fffffffffffffL; // e.g. encrypted: 0x000e(cdab78563412)
// decrypt the significand using the provided cipher
byte[] bytesContent = toByteArray(significand);
byte[] bytesDecContent = new byte[bytesContent.length];
bytesDecContent[0] = bytesContent[0]; // e.g. clear: 0x0000000000000000
bytesDecContent[1] = bytesContent[1]; // e.g. clear: 0x000e000000000000
cipher.doFinal(bytesContent, 2, bytesContent.length - 2, bytesDecContent, 2);
significand = toLong(bytesDecContent); // e.g. clear: 0x000ecdab78563412
significand = significand << 8; // e.g. clear: 0x0ecdab7856341200
bytesDecContent = toByteArray(significand);
bytesDecContent[0] = (byte) (bytesDecContent[0] << 4); // e.g. clear: 0xe0cdab7856341200
bytesDecContent = swapByteArray(bytesDecContent); // e.g. clear: 0x0012345678abcde0
significand = toLong(bytesDecContent);
significand = significand >>> 4; // e.g. clear: 0x00012345678abcde
// decrypt the exponent using XOR cipher
short expo = (short) (exponent >>> 52); // e.g. encrypted: 0x02(23)
long rawBitsMax = Double.doubleToRawLongBits(maxValue); // e.g. clear: 0xc1731940863d70a4
long exponentMax = rawBitsMax & 0x7ff0000000000000L; // e.g. clear: 0xc170000000000000
short expoMax = (short) (exponentMax >>> 52); // e.g. clear: 0x0c17
int highestLeadingBit = 32 - Integer.numberOfLeadingZeros(expoMax) - 1; // e.g. 10
short lowestBitsMask = (short) ((1 << highestLeadingBit) - 1); // e.g. 0x03ff
short highestBitsMask = (short) ~lowestBitsMask; // e.g. 0xfc00
short xorMask = (short) (expoMax & lowestBitsMask); // e.g. 0x0017
expo = (short) ((expo & highestBitsMask) // e.g. encrypted: 0x0234
| ((expo & lowestBitsMask) ^ xorMask));
exponent = (long) expo << 52; // e.g. clear: 0x2340000000000000
rawBits = sign | exponent | significand; // e.g. clear: 0xa3412345678abcde
value = Double.longBitsToDouble(rawBits);
return value;
}
private byte[] swapByteArray(byte[] value) {
for (int i = 0; i < value.length / 2; i++) {
byte b = value[i];
value[i] = value[value.length - i - 1];
value[value.length - i - 1] = b;
}
return value;
}
private byte[] toByteArray(long value) {
return new byte[] { (byte) (value >>> 56), (byte) (value >>> 48), (byte) (value >>> 40), (byte) (value >>> 32),
(byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value };
}
private long toLong(byte[] value) {
return ((long) value[0] & 0xff) << 56 | ((long) value[1] & 0xff) << 48 | ((long) value[2] & 0xff) << 40
| ((long) value[3] & 0xff) << 32 | ((long) value[4] & 0xff) << 24 | ((long) value[5] & 0xff) << 16
| ((long) value[6] & 0xff) << 8 | (long) value[7] & 0xff;
}
@Override
public List put(String[] attributeNames, Criteria[] criteria, String[][] contents) {
// This module does not use PUT method
return null;
}
@Override
public List delete(String[] attributeNames, Criteria[] criteria) {
Map attributesMapping = this.buildAttributesMapping(attributeNames,
notCoveredAttribute -> notCoveredAttribute, unprotectedAttrib -> unprotectedAttrib);
// First, Generate the ORDERED list of the protected attributeNames
List protectedAttributes = new ArrayList<>();
Stream.of(attributeNames)
.forEach(attributeName -> protectedAttributes.add(attributesMapping.get(attributeName)));
// Second, process the Criteria to transform the requested
// AttributeNames to the protected ones
if (criteria != null) {
Stream.of(criteria).forEach(criterion -> {
// Determine if the column is encrypted of not
String protectedAttribute = attributesMapping.get(criterion.getAttributeName());
if (!criterion.getAttributeName().equals(protectedAttribute)) {
// The protected and unprotected Attribute Names do not
// match
// This implies the criteria operates over an encrypted
// column
// Encrypt the treshold
String protectedThreshold = "";
try {
// Find which "protectionRule" (in the keyset of attributeTypes) matches the given attribute name
String matchedProtection = null;
for(String protectionRule : this.attributeTypes.keySet()){
Pattern p = Pattern.compile(AttributeNamesUtilities.escapeRegex(protectionRule));
if(p.matcher(criterion.getAttributeName()).matches()){
matchedProtection = protectionRule;
}
}
// If none matches, ignore this attribute => it is not convered by the Policy
if(matchedProtection == null)
return;
// Obtain the dataID
String dataID = this.typesDataIDs.get(this.attributeTypes.get(matchedProtection));
// Get the prpteciton type of this attribute
String protection = this.typesProtection
.get(this.attributeTypes.get(matchedProtection));
// Encrypt only if the protection type is "encryption"
// or "simple"
if (protection.equals("encryption") || protection.equals("simple")) {
byte[] bytesAttribEnc;
// Initialize the Secret Key and the Init Vector of
// the Cipher
IvParameterSpec iv = new IvParameterSpec(this.keyStore.retrieveInitVector(dataID));
SecretKey sk = this.keyStore.retrieveKey(dataID);
if (this.dataTypes.get(matchedProtection).equals("geometric_object")) {
String value = criterion.getValue();
if (criterion.getOperator().equals("area")) {
String[] area = value.split(",");
value = String.format("SRID=%s;BOX(%s %s, %s %s)", area[4].trim(), area[0].trim(),
area[1].trim(), area[2].trim(), area[3].trim());
}
GeometryBuilder builder = new GeometryBuilder();
Object geom = builder.decode(value);
if (geom != null) {
if (geom instanceof Point) {
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, sk, iv);
// NOTE - To correctly encrypt, just cipher coordinates
Point point = (Point) geom;
double maxX, maxY;
int srid = point.getSrid();
if (srid != 0) {
ProjectedCRS crs = ProjectedCRS.resolve(srid);
maxX = crs.getAxis("x").getMax();
maxY = crs.getAxis("y").getMax();
} else {
maxX = Double.MAX_VALUE;
maxY = Double.MAX_VALUE;
}
point.x = encryptDouble(cipher, point.x, maxX);
point.y = encryptDouble(cipher, point.y, maxY);
} else if (geom instanceof PGbox2d) {
PGbox2d box = (PGbox2d) geom;
int srid = box.getLLB().getSrid();
if (srid != 0) {
ProjectedCRS crs = ProjectedCRS.resolve(srid);
box.getLLB().x = crs.getAxis("x").getMin();
box.getLLB().y = crs.getAxis("y").getMin();
box.getURT().x = crs.getAxis("x").getMax();
box.getURT().y = crs.getAxis("y").getMax();
} else {
box.getLLB().x = -Double.MAX_VALUE;
box.getLLB().y = -Double.MAX_VALUE;
box.getURT().x = Double.MAX_VALUE;
box.getURT().y = Double.MAX_VALUE;
}
if (criterion.getOperator().equals("area")) {
value = box.getLLB().x + ", " + box.getLLB().y + ", " + box.getURT().x + ", " + box.getURT().y + ", " + srid;
}
}
if (!criterion.getOperator().equals("area")) {
value = builder.encode(geom);
}
}
protectedThreshold = value;
} else {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sk, iv);
// NOTE - To correctly encrypt, First cipher, THEN
// Base64 encode
String value = criterion.getValue() != null ? criterion.getValue() : "clarus_null";
bytesAttribEnc = cipher.doFinal(value.getBytes());
protectedThreshold = Base64.getEncoder().encodeToString(bytesAttribEnc);
}
} else {
// Otherwise, just let the value pass in plain text
protectedThreshold = criterion.getValue();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
criterion.setValue(protectedThreshold);
// Third, substitute the involved attribute name with its
// protected one
criterion.setAttributeName(attributesMapping.get(criterion.getAttributeName()));
}
});
}
// Third, create the Comman object
DataOperationCommand command = new EncryptionCommand(attributeNames,
protectedAttributes.toArray(new String[attributeNames.length]), null, attributesMapping, criteria);
List commands = new ArrayList<>();
commands.add(command);
return commands;
}
@Override
public List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy