diff options
Diffstat (limited to 'src/main/java/pl/edu')
12 files changed, 520 insertions, 32 deletions
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/ByteSerializer.java b/src/main/java/pl/edu/mimuw/cloudatlas/ByteSerializer.java new file mode 100644 index 0000000..ee7a6f0 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/ByteSerializer.java @@ -0,0 +1,182 @@ +package pl.edu.mimuw.cloudatlas; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import pl.edu.mimuw.cloudatlas.agent.messages.*; +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; +import pl.edu.mimuw.cloudatlas.agent.modules.RecursiveScheduledTask; +import pl.edu.mimuw.cloudatlas.agent.modules.TimerScheduledTask; +import pl.edu.mimuw.cloudatlas.model.*; +import pl.edu.mimuw.cloudatlas.querysigner.QueryData; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.rmi.Remote; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.HashMap; +import java.util.LinkedHashMap; + +/** + * Serializes classes to and from byte arrays + */ +// TODO remove udupserializer +public class ByteSerializer { + private Kryo kryo; + + public ByteSerializer() { + kryo = new Kryo(); + kryo.setReferences(true); + kryo.setRegistrationRequired(true); + registerClasses(); + } + + private void registerClasses() { + + kryo.register(Inet4Address.class, new Serializer() { + + @Override + public void write(Kryo kryo, Output output, Object object) { + InetAddress ia = (InetAddress) object; + kryo.writeObject(output, ia.getAddress()); + } + + @Override + public Object read(Kryo kryo, Input input, Class type) { + try { + byte[] buf = kryo.readObject(input, byte[].class); + InetAddress addr = Inet4Address.getByAddress(buf); + return addr; + } catch (UnknownHostException e) { + System.out.println("Custom InetAddress read failed"); + e.printStackTrace(); + return null; + } + } + }); + + kryo.register(PathName.class, new Serializer() { + + @Override + public void write(Kryo kryo, Output output, Object object) { + PathName pn = (PathName) object; + kryo.writeObject(output, pn.getName()); + } + + @Override + public Object read(Kryo kryo, Input input, Class type) { + String addr = input.readString(); + return new PathName(addr); + } + }); + + kryo.register(ValueList.class, new Serializer() { + @Override + public void write(Kryo kryo, Output output, Object object) { + ValueList vl = (ValueList) object; + kryo.writeObject(output, ((TypeCollection) vl.getType()).getElementType()); + kryo.writeObject(output, vl.getValue()); + } + + @Override + public Object read(Kryo kryo, Input input, Class type) { + Type t = kryo.readObject(input, Type.class); + ArrayList list = kryo.readObject(input, ArrayList.class); + return new ValueList(list, t); + } + }); + + kryo.register(ValueSet.class, new Serializer() { + @Override + public void write(Kryo kryo, Output output, Object object) { + ValueSet vs = (ValueSet) object; + kryo.writeObject(output, ((TypeCollection) vs.getType()).getElementType()); + kryo.writeObject(output, vs.getValue()); + } + + @Override + public Object read(Kryo kryo, Input input, Class type) { + Type t = kryo.readObject(input, Type.class); + HashSet set = kryo.readObject(input, HashSet.class); + return new ValueSet(set, t); + } + }); + + // model + kryo.register(Value.class); + kryo.register(ValueBoolean.class); + kryo.register(ValueContact.class); + kryo.register(ValueDuration.class); + kryo.register(ValueInt.class); + kryo.register(ValueNull.class); + kryo.register(ValueQuery.class); + kryo.register(ValueSet.class); + kryo.register(ValueString.class); + kryo.register(ValueTime.class); + kryo.register(ValueUtils.class); + kryo.register(ZMI.class); + + kryo.register(Attribute.class); + kryo.register(AttributesMap.class); + kryo.register(AttributesUtil.class); + + kryo.register(Type.class); + kryo.register(TypeCollection.class); + kryo.register(TypePrimitive.class); + + // messages in chronological order so it's easier to keep track + kryo.register(AgentMessage.class); + kryo.register(AttributesMessage.class); + kryo.register(GetStateMessage.class); + kryo.register(HejkaMessage.class); + kryo.register(NoCoTamMessage.class); + kryo.register(QueryMessage.class); + kryo.register(QurnikMessage.class); + kryo.register(RemikMessage.class); + kryo.register(RemoveZMIMessage.class); + kryo.register(RequestStateMessage.class); + kryo.register(ResponseMessage.class); + kryo.register(RunQueriesMessage.class); + kryo.register(SetAttributeMessage.class); + kryo.register(StanikMessage.Type.class); + kryo.register(StanikMessage.class); + kryo.register(TimerSchedulerMessage.class); + kryo.register(UDUPMessage.class); + kryo.register(UpdateAttributesMessage.class); + kryo.register(UpdateQueriesMessage.class); + kryo.register(GossipGirlMessage.class); + kryo.register(GossipGirlMessage.Type.class); + kryo.register(RemoteGossipGirlMessage.class); + + // modules + kryo.register(TimerScheduledTask.class); + kryo.register(RecursiveScheduledTask.class); + + // other + kryo.register(byte[].class); + kryo.register(LinkedHashMap.class); + kryo.register(HashMap.class); + kryo.register(ModuleType.class); + kryo.register(QueryData.class); + } + + public Object deserialize(byte[] packetData, Class objClass) { + ByteArrayInputStream in = new ByteArrayInputStream(packetData); + Input kryoInput = new Input(in); + return kryo.readObject(kryoInput, objClass); + } + + public byte[] serialize(Object obj) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Output kryoOut = new Output(out); + kryo.writeObject(kryoOut, obj); + kryoOut.flush(); + kryoOut.close(); + return out.toByteArray(); + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java index d2e808a..90e7789 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java @@ -28,6 +28,8 @@ import pl.edu.mimuw.cloudatlas.model.Type; import pl.edu.mimuw.cloudatlas.model.TypePrimitive; import pl.edu.mimuw.cloudatlas.model.ZMI; import pl.edu.mimuw.cloudatlas.api.Api; +import pl.edu.mimuw.cloudatlas.querysigner.QueryData; +import pl.edu.mimuw.cloudatlas.querysigner.QueryUtils; public class ApiImplementation implements Api { ZMI root; @@ -60,16 +62,11 @@ public class ApiImplementation implements Api { } } - public void installQuery(String name, String queryCode) throws RemoteException { - Pattern queryNamePattern = Pattern.compile("&[a-zA-Z][\\w_]*"); - Matcher matcher = queryNamePattern.matcher(name); - if (!matcher.matches()) { - throw new RemoteException("Invalid query identifier"); - } + public void installQuery(String name, QueryData query) throws RemoteException { + QueryUtils.validateQueryName(name); try { - ValueQuery query = new ValueQuery(queryCode); Attribute attributeName = new Attribute(name); - installQueryInHierarchy(root, attributeName, query); + installQueryInHierarchy(root, attributeName, new ValueQuery(query)); executeAllQueries(root); } catch (Exception e) { throw new RemoteException("Failed to install query", e); @@ -85,7 +82,8 @@ public class ApiImplementation implements Api { } } - public void uninstallQuery(String queryName) throws RemoteException { + public void uninstallQuery(String queryName, QueryData query) throws RemoteException { + QueryUtils.validateQueryName(queryName); uninstallQueryInHierarchy(root, new Attribute(queryName)); } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/NewApiImplementation.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/NewApiImplementation.java index b293446..0bf4338 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/NewApiImplementation.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/NewApiImplementation.java @@ -22,6 +22,8 @@ import pl.edu.mimuw.cloudatlas.interpreter.Main; import pl.edu.mimuw.cloudatlas.interpreter.QueryResult; import pl.edu.mimuw.cloudatlas.model.*; import pl.edu.mimuw.cloudatlas.api.Api; +import pl.edu.mimuw.cloudatlas.querysigner.QueryData; +import pl.edu.mimuw.cloudatlas.querysigner.QueryUtils; public class NewApiImplementation implements Api { private EventBus eventBus; @@ -79,18 +81,13 @@ public class NewApiImplementation implements Api { } } - public void installQuery(String name, String queryCode) throws RemoteException { - Pattern queryNamePattern = Pattern.compile("&[a-zA-Z][\\w_]*"); - Matcher matcher = queryNamePattern.matcher(name); - if (!matcher.matches()) { - throw new RemoteException("Invalid query identifier"); - } + public void installQuery(String name, QueryData query) throws RemoteException { + QueryUtils.validateQueryName(name); try { - ValueQuery query = new ValueQuery(queryCode); Attribute attributeName = new Attribute(name); ValueTime timestamp = new ValueTime(System.currentTimeMillis()); Map<Attribute, Entry<ValueQuery, ValueTime>> queries = new HashMap(); - queries.put(attributeName, new SimpleImmutableEntry(query, timestamp)); + queries.put(attributeName, new SimpleImmutableEntry(new ValueQuery(query), timestamp)); UpdateQueriesMessage message = new UpdateQueriesMessage("", 0, queries); eventBus.addMessage(message); } catch (Exception e) { @@ -98,7 +95,8 @@ public class NewApiImplementation implements Api { } } - public void uninstallQuery(String queryName) throws RemoteException { + public void uninstallQuery(String queryName, QueryData query) throws RemoteException { + QueryUtils.validateQueryName(queryName); try { Attribute attributeName = new Attribute(queryName); ValueTime timestamp = new ValueTime(System.currentTimeMillis()); diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java b/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java index c62ee39..7cc629d 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java @@ -6,6 +6,8 @@ import java.rmi.RemoteException; import pl.edu.mimuw.cloudatlas.model.Value; import pl.edu.mimuw.cloudatlas.model.ValueContact; import pl.edu.mimuw.cloudatlas.model.AttributesMap; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; +import pl.edu.mimuw.cloudatlas.querysigner.QueryData; /** * @@ -27,9 +29,9 @@ public interface Api extends Remote { public AttributesMap getZoneAttributeValues(String zoneName) throws RemoteException; - public void installQuery(String queryName, String query) throws RemoteException; + public void installQuery(String queryName, QueryData query) throws RemoteException; - public void uninstallQuery(String queryName) throws RemoteException; + public void uninstallQuery(String queryName, QueryData query) throws RemoteException; public void setAttributeValue(String zoneName, String attributeName, Value value) throws RemoteException; diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/client/ClientController.java b/src/main/java/pl/edu/mimuw/cloudatlas/client/ClientController.java index 4019696..5f34fe9 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/client/ClientController.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/client/ClientController.java @@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.*; import org.springframework.stereotype.Controller; import pl.edu.mimuw.cloudatlas.api.Api; import pl.edu.mimuw.cloudatlas.model.*; +import pl.edu.mimuw.cloudatlas.querysigner.QueryData; +import pl.edu.mimuw.cloudatlas.querysignerapi.QuerySignerApi; import java.net.InetAddress; import java.rmi.registry.LocateRegistry; @@ -32,17 +34,21 @@ import java.util.*; @Controller public class ClientController { - private Api api; - + private Api agentApi; + private QuerySignerApi querySignerApi; private Map<ValueTime, AttributesMap> attributes; private String currentZoneName; private static final int MAX_ENTRIES = 10; ClientController() { try { - String hostname = System.getProperty("agent_hostname"); - Registry registry = LocateRegistry.getRegistry(hostname); - this.api = (Api) registry.lookup("Api"); + String agentHostname = System.getProperty("agent_hostname"); + Registry registry = LocateRegistry.getRegistry(agentHostname); + this.agentApi = (Api) registry.lookup("Api"); + + String querySignerHostname = System.getProperty("querysigner_hostname"); + Registry querySignerRegistry = LocateRegistry.getRegistry(querySignerHostname); + this.querySignerApi = (QuerySignerApi) querySignerRegistry.lookup("QuerySignerApi"); } catch (Exception e) { System.err.println("Client exception:"); e.printStackTrace(); @@ -74,7 +80,8 @@ public class ClientController { boolean success = true; try { - this.api.installQuery(queryObject.getName(), queryObject.getValue()); + QueryData query = this.querySignerApi.signInstallQuery(queryObject.getName(), queryObject.getValue()); + this.agentApi.installQuery(queryObject.getName(), query); } catch (Exception e) { success = false; System.err.println("Client exception:"); @@ -99,7 +106,8 @@ public class ClientController { boolean success = true; try { - this.api.uninstallQuery(queryObject.getName()); + QueryData query = querySignerApi.signUninstallQuery(queryObject.getName()); + this.agentApi.uninstallQuery(queryObject.getName(), query); } catch (Exception e) { success = false; System.err.println("Client exception:"); @@ -153,7 +161,7 @@ public class ClientController { try { contactObjects = parseContactsString(contactsObject); - this.api.setFallbackContacts(contactObjects); + this.agentApi.setFallbackContacts(contactObjects); } catch (Exception e) { success = false; System.err.println("Client exception:"); @@ -284,7 +292,7 @@ public class ClientController { try { attributeValue = parseAttributeValue(attributeObject); - api.setAttributeValue( + agentApi.setAttributeValue( attributeObject.getZoneName(), attributeObject.getAttributeName(), attributeValue); @@ -309,7 +317,7 @@ public class ClientController { String availableZonesString = ""; try { - availableZones = api.getZoneSet(); + availableZones = agentApi.getZoneSet(); availableZonesString = availableZones.toString().substring(1, availableZones.toString().length() - 1); } catch (Exception e) { success = false; @@ -336,7 +344,7 @@ public class ClientController { try { if (!this.currentZoneName.isEmpty()) { - attribData = api.getZoneAttributeValues(this.currentZoneName); + attribData = agentApi.getZoneAttributeValues(this.currentZoneName); currentTime = new ValueTime(System.currentTimeMillis()); this.attributes.put(currentTime, attribData); } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java index 6d233ea..ece50b5 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java @@ -6,6 +6,7 @@ import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.Program; import pl.edu.mimuw.cloudatlas.interpreter.query.parser; import pl.edu.mimuw.cloudatlas.interpreter.query.Yylex; import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.querysigner.QueryData; /** * A class that holds a CloudAtlas query. @@ -15,21 +16,45 @@ public class ValueQuery extends Value { private String code; // Parsed query private Program query; + // Query signature + private byte[] signature; + // Query signing timestamp + private long timestamp; + /** * Constructs a new <code>ValueQuery</code> object. * - * @param name the name of the query * @param query the code of the query */ public ValueQuery(String query) throws Exception { this.code = query; Yylex lex = new Yylex(new ByteArrayInputStream(query.getBytes())); this.query = (new parser(lex)).pProgram(); + this.signature = null; + this.timestamp = System.currentTimeMillis(); + } + + public ValueQuery(String query, byte[] querySignature) throws Exception { + this.code = query; + Yylex lex = new Yylex(new ByteArrayInputStream(query.getBytes())); + this.query = (new parser(lex)).pProgram(); + this.signature = querySignature; + this.timestamp = System.currentTimeMillis(); + } + + public ValueQuery(QueryData queryData) throws Exception { + this.code = queryData.getCode(); + Yylex lex = new Yylex(new ByteArrayInputStream(queryData.getCode().getBytes())); + this.query = (new parser(lex)).pProgram(); + this.signature = queryData.getSignature(); + this.timestamp = System.currentTimeMillis(); } private ValueQuery() { this.code = null; this.query = null; + this.signature = null; + this.timestamp = System.currentTimeMillis(); } public String getCode() { return code; } @@ -38,6 +63,12 @@ public class ValueQuery extends Value { return query; } + public byte[] getSignature() { return signature; } + + public long getTimestamp() { return timestamp; } + + public void setTimestamp(long timestamp) { this.timestamp = timestamp; } + @Override public Type getType() { return TypePrimitive.QUERY; diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/KeyUtils.java b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/KeyUtils.java new file mode 100644 index 0000000..7a543ba --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/KeyUtils.java @@ -0,0 +1,35 @@ +package pl.edu.mimuw.cloudatlas.querysigner; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +public class KeyUtils { + private final static String ENCRYPTION_ALGORITHM = "RSA"; + + public static PublicKey getPublicKey(String filename){ + try { + byte[] byteKey = Files.readAllBytes(Paths.get(filename)); + X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey); + KeyFactory kf = KeyFactory.getInstance(ENCRYPTION_ALGORITHM); + return kf.generatePublic(X509publicKey); + } catch(Exception e) { + e.printStackTrace(); + } + return null; + } + + public static PrivateKey getPrivateKey(String filename){ + try { + byte[] byteKey = Files.readAllBytes(Paths.get(filename)); + PKCS8EncodedKeySpec PKCS8privateKey = new PKCS8EncodedKeySpec(byteKey); + KeyFactory kf = KeyFactory.getInstance(ENCRYPTION_ALGORITHM); + return kf.generatePrivate(PKCS8privateKey); + } catch(Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QueryData.java b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QueryData.java new file mode 100644 index 0000000..7801a28 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QueryData.java @@ -0,0 +1,30 @@ +package pl.edu.mimuw.cloudatlas.querysigner; + +import java.io.Serializable; + +public class QueryData implements Serializable { + // Original source code + private String code; + // Query signature + private byte[] signature; + // Query signing timestamp + private long timestamp; + + public QueryData(String code, byte[] signature) { + this.code = code; + this.signature = signature; + this.timestamp = System.currentTimeMillis();; + } + + public String getCode() { + return code; + } + + public byte[] getSignature() { + return signature; + } + + public long getTimestamp() { + return timestamp; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QuerySigner.java b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QuerySigner.java new file mode 100644 index 0000000..735601d --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QuerySigner.java @@ -0,0 +1,44 @@ +package pl.edu.mimuw.cloudatlas.querysigner; + +import pl.edu.mimuw.cloudatlas.querysignerapi.QuerySignerApi; + +import java.io.IOException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; +import java.security.PrivateKey; +import java.security.PublicKey; + +public class QuerySigner { + public static class InvalidQueryException extends Exception { + InvalidQueryException() { + super("Query invalid"); + } + } + + private static QuerySignerApiImplementation initApi() throws IOException { + String publicKeyFile = System.getProperty("public_key_file"); + String privateKeyFile = System.getProperty("private_key_file"); + PublicKey publicKey = KeyUtils.getPublicKey(publicKeyFile); + PrivateKey privateKey = KeyUtils.getPrivateKey(privateKeyFile); + return new QuerySignerApiImplementation(publicKey, privateKey); + } + + public static void runRegistry() { + try { + QuerySignerApiImplementation api = initApi(); + QuerySignerApi apiStub = + (QuerySignerApi) UnicastRemoteObject.exportObject(api, 0); + Registry registry = LocateRegistry.getRegistry(); + registry.rebind("QuerySignerApi", apiStub); + System.out.println("QuerySigner: api bound"); + } catch (Exception e) { + System.err.println("QuerySigner registry initialization exception:"); + e.printStackTrace(); + } + } + + public static void main(String[] args) { + runRegistry(); + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QuerySignerApiImplementation.java b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QuerySignerApiImplementation.java new file mode 100644 index 0000000..32bc634 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QuerySignerApiImplementation.java @@ -0,0 +1,128 @@ +package pl.edu.mimuw.cloudatlas.querysigner; + +import pl.edu.mimuw.cloudatlas.ByteSerializer; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; +import pl.edu.mimuw.cloudatlas.querysignerapi.QuerySignerApi; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.rmi.RemoteException; +import java.security.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class QuerySignerApiImplementation implements QuerySignerApi { + private final static String ENCRYPTION_ALGORITHM = "RSA"; + private final static String DIGEST_ALGORITHM = "SHA-256"; + private PublicKey publicKey; + private PrivateKey privateKey; + private Map<String, ValueQuery> queries; + private Set<String> attribsSetByQueries; + private ByteSerializer byteSerializer; + + QuerySignerApiImplementation(PublicKey publicKey, PrivateKey privateKey) {// (byte[] serializedPublicKey, byte[] serializedPrivateKey) { + this.byteSerializer = new ByteSerializer(); +// this.publicKey = (PublicKey) byteSerializer.deserialize(serializedPublicKey, PublicKey.class); +// this.privateKey = (PrivateKey) byteSerializer.deserialize(serializedPrivateKey, PrivateKey.class); + this.publicKey = publicKey; + this.privateKey = privateKey; + this.queries = new HashMap<>(); + this.attribsSetByQueries = new HashSet<>(); + } + + private String byteArrayToString(byte[] arr, int offset, int len) { + StringBuffer sb = new StringBuffer(); + for (int i = offset, n = Math.min(arr.length, offset + len); i < n; ++i) { + String hex = Integer.toHexString(0xFF & arr[i]); + if (hex.length() < 2) { + sb.append('0'); + } + sb.append(hex); + } + return sb.toString(); + } + + private byte[] encryptQuery(byte[] query) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher signCipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); + signCipher.init(Cipher.ENCRYPT_MODE, privateKey); + byte[] encryptedBytes = signCipher.doFinal(query); + System.out.println( + "Bytes encrypted with " + ENCRYPTION_ALGORITHM + + ": " + byteArrayToString( + encryptedBytes, 0, encryptedBytes.length)); + return encryptedBytes; + } + + private byte[] decryptQuery(byte[] encryptedQuery) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException { + Cipher verifyCipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); + verifyCipher.init(Cipher.DECRYPT_MODE, publicKey); + byte[] decryptedBytes = verifyCipher.doFinal(encryptedQuery); + System.out.println( + "Bytes decrypted with " + ENCRYPTION_ALGORITHM + + ": " + byteArrayToString( + decryptedBytes, 0, decryptedBytes.length)); + return decryptedBytes; + } + + private byte[] cryptographicHash(byte[] serializedQuery) throws NoSuchAlgorithmException { + MessageDigest digestGenerator = + MessageDigest.getInstance(DIGEST_ALGORITHM); + byte[] digest = digestGenerator.digest(serializedQuery); + System.out.println( + DIGEST_ALGORITHM + " digest: " + + byteArrayToString( + digest, 0, digest.length)); + return digest; + } + + // TODO + private byte[] serializeQuery(String queryName, String queryCode) { + return byteSerializer.serialize(queryName + queryCode); + } + + @Override + public QueryData signInstallQuery(String queryName, String queryCode) throws RemoteException { + QueryUtils.validateQueryName(queryName); + try { + byte[] serializedQuery = serializeQuery(queryName, queryCode); + byte[] hashedQuery = cryptographicHash(serializedQuery); + byte[] querySignature = encryptQuery(hashedQuery); + return new QueryData(queryCode, querySignature); + } catch (Exception e) { + e.printStackTrace(); + throw new RemoteException(e.getLocalizedMessage()); + } + } + + @Override + public void validateInstallQuery(String queryName, QueryData query) throws RemoteException { + QueryUtils.validateQueryName(queryName); + try { + byte[] decryptedQuery = decryptQuery(query.getSignature()); + byte[] serializedQuery = serializeQuery(queryName, query.getCode()); + byte[] hashedSerializedQuery = cryptographicHash(serializedQuery); + if (hashedSerializedQuery != decryptedQuery) { + throw new QuerySigner.InvalidQueryException(); + } + } catch (NoSuchPaddingException | NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException | QuerySigner.InvalidQueryException e) { + e.printStackTrace(); + throw new RemoteException(e.getLocalizedMessage()); + } + } + + // TODO + @Override + public QueryData signUninstallQuery(String queryName) throws RemoteException { + return null; + } + + // TODO + @Override + public void validateUninstallQuery(String queryName, QueryData query) throws RemoteException { + + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QueryUtils.java b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QueryUtils.java new file mode 100644 index 0000000..6ec62f4 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/querysigner/QueryUtils.java @@ -0,0 +1,16 @@ +package pl.edu.mimuw.cloudatlas.querysigner; + +import java.rmi.RemoteException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class QueryUtils { + + public static void validateQueryName(String queryName) throws RemoteException { + Pattern queryNamePattern = Pattern.compile("&[a-zA-Z][\\w_]*"); + Matcher matcher = queryNamePattern.matcher(queryName); + if (!matcher.matches()) { + throw new RemoteException("Invalid query identifier"); + } + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/querysignerapi/QuerySignerApi.java b/src/main/java/pl/edu/mimuw/cloudatlas/querysignerapi/QuerySignerApi.java new file mode 100644 index 0000000..6b42d32 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/querysignerapi/QuerySignerApi.java @@ -0,0 +1,16 @@ +package pl.edu.mimuw.cloudatlas.querysignerapi; + +import pl.edu.mimuw.cloudatlas.querysigner.QueryData; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface QuerySignerApi extends Remote { + public QueryData signInstallQuery(String queryName, String queryCode) throws RemoteException; + + public QueryData signUninstallQuery(String queryName) throws RemoteException; + + public void validateInstallQuery(String queryName, QueryData query) throws RemoteException; + + public void validateUninstallQuery(String queryName, QueryData query) throws RemoteException; +} |