Skip to content
This repository has been archived by the owner on Jul 26, 2022. It is now read-only.

Commit

Permalink
Use custom action aware encryption converters
Browse files Browse the repository at this point in the history
Previously all transfer settings values were encrypted once they are
persisted independent from the location. Now only configs written to
disk are encrypted using a custom converter.

Changed default @Encrypted handling and removed persistence callbacks in
transfer settings.

Deprecated generic AbtractInitOperation#writeXmlObject. Every *TO
should use its own save method because the above case has shown that
persistence isn't generic in all cases.
  • Loading branch information
cr0 committed Nov 7, 2014
1 parent 9e24e5c commit e9d29f7
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 192 deletions.
65 changes: 27 additions & 38 deletions syncany-lib/src/main/java/org/syncany/config/to/ConfigTO.java
Expand Up @@ -17,21 +17,21 @@
*/
package org.syncany.config.to;

import javax.crypto.spec.SecretKeySpec;
import java.io.File;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.core.Commit;
import org.simpleframework.xml.core.Complete;
import org.simpleframework.xml.core.Persist;
import org.simpleframework.xml.convert.Convert;
import org.simpleframework.xml.convert.Registry;
import org.simpleframework.xml.convert.RegistryStrategy;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.strategy.Strategy;
import org.syncany.config.ConfigException;
import org.syncany.crypto.CipherParams;
import org.syncany.crypto.SaltedSecretKey;
import org.syncany.plugins.transfer.EncryptedTransferSettingsConverter;
import org.syncany.plugins.transfer.TransferSettings;
import org.syncany.util.StringUtil;
import org.syncany.util.SaltedSecretKeyConverter;

/**
* The config transfer object is used to create and load the local config
Expand All @@ -55,7 +55,7 @@ public class ConfigTO {
private String displayName;

@Element(name = "masterkey", required = false)
private String masterKeyEncoded;
@Convert(SaltedSecretKeyConverter.class)
private SaltedSecretKey masterKey;

@Element(name = "connection", required = true)
Expand All @@ -66,13 +66,32 @@ public class ConfigTO {

public static ConfigTO load(File file) throws ConfigException {
try {
return new Persister().read(ConfigTO.class, file);
Registry registry = new Registry();
Strategy strategy = new RegistryStrategy(registry);
registry.bind(SaltedSecretKey.class, new SaltedSecretKeyConverter());
registry.bind(String.class, new EncryptedTransferSettingsConverter());

return new Persister(strategy).read(ConfigTO.class, file);
}
catch (Exception ex) {
throw new ConfigException("Config file does not exist or is invalid: " + file, ex);
}
}

public void save(File file) throws ConfigException {
try {
Registry registry = new Registry();
Strategy strategy = new RegistryStrategy(registry);
registry.bind(SaltedSecretKey.class, new SaltedSecretKeyConverter());
registry.bind(String.class, new EncryptedTransferSettingsConverter(transferSettings.getClass()));

new Persister(strategy).write(this, file);
}
catch (Exception e) {
throw new ConfigException("Cannot write config to file " + file, e);
}
}

public String getMachineName() {
return machineName;
}
Expand Down Expand Up @@ -113,34 +132,4 @@ public void setCacheKeepBytes(Long cacheKeepBytes) {
this.cacheKeepBytes = cacheKeepBytes;
}

@Persist
public void prepare() {
if (masterKey != null) {
masterKeyEncoded = StringUtil.toHex(masterKey.getSalt()) + "/" + StringUtil.toHex(masterKey.getEncoded());
}
else {
masterKeyEncoded = null;
}
}

@Complete
public void release() {
masterKeyEncoded = null;
}

@Commit
public void commit() {
if (masterKeyEncoded != null && !"".equals(masterKeyEncoded)) {
String[] masterKeyEncodedParts = masterKeyEncoded.split("/");

byte[] saltBytes = StringUtil.fromHex(masterKeyEncodedParts[0]);
byte[] masterKeyBytes = StringUtil.fromHex(masterKeyEncodedParts[1]);

masterKey = new SaltedSecretKey(new SecretKeySpec(masterKeyBytes, CipherParams.MASTER_KEY_DERIVATION_FUNCTION), saltBytes);
}
else {
masterKey = null;
}
}

}
Expand Up @@ -17,7 +17,6 @@
*/
package org.syncany.config.to;

import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.util.Map;
import java.util.TreeMap;
Expand All @@ -27,14 +26,10 @@
import org.simpleframework.xml.Root;
import org.simpleframework.xml.convert.AnnotationStrategy;
import org.simpleframework.xml.convert.Convert;
import org.simpleframework.xml.convert.Converter;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.stream.InputNode;
import org.simpleframework.xml.stream.OutputNode;
import org.syncany.config.ConfigException;
import org.syncany.crypto.CipherParams;
import org.syncany.crypto.SaltedSecretKey;
import org.syncany.util.StringUtil;
import org.syncany.util.SaltedSecretKeyConverter;

/**
* The user config transfer object is a helper data structure that allows storing
Expand Down Expand Up @@ -96,18 +91,4 @@ public static void save(UserConfigTO userConfigTO, File file) throws ConfigExcep
throw new ConfigException("Cannot write user config to file " + file, e);
}
}

public static class SaltedSecretKeyConverter implements Converter<SaltedSecretKey> {
public SaltedSecretKey read(InputNode node) throws Exception {
byte[] saltBytes = StringUtil.fromHex(node.getAttribute("salt").getValue());
byte[] keyBytes = StringUtil.fromHex(node.getAttribute("key").getValue());

return new SaltedSecretKey(new SecretKeySpec(keyBytes, CipherParams.MASTER_KEY_DERIVATION_FUNCTION), saltBytes);
}

public void write(OutputNode node, SaltedSecretKey saltedSecretKey) {
node.setAttribute("salt", StringUtil.toHex(saltedSecretKey.getSalt()));
node.setAttribute("key", StringUtil.toHex(saltedSecretKey.getEncoded()));
}
}
}
Expand Up @@ -108,6 +108,9 @@ protected void deleteAppDirs(File localDir) throws IOException {
appDir.delete();
}

/**
* @deprecated Every *TO should define its own save operation
*/
protected void writeXmlFile(Object source, File file) throws IOException {
try {
Serializer serializer = new Persister();
Expand Down
Expand Up @@ -20,7 +20,6 @@
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand All @@ -43,27 +42,18 @@
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.simpleframework.xml.convert.Converter;
import org.simpleframework.xml.convert.Registry;
import org.simpleframework.xml.convert.RegistryStrategy;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.strategy.Strategy;
import org.simpleframework.xml.stream.Format;
import org.simpleframework.xml.stream.InputNode;
import org.simpleframework.xml.stream.OutputNode;
import org.syncany.crypto.CipherSpec;
import org.syncany.crypto.CipherSpecs;
import org.syncany.crypto.CipherUtil;
import org.syncany.crypto.SaltedSecretKey;
import org.syncany.plugins.Plugins;
import org.syncany.plugins.transfer.Encrypted;
import org.syncany.plugins.transfer.StorageException;
import org.syncany.plugins.transfer.TransferPlugin;
import org.syncany.plugins.transfer.TransferPluginUtil;
import org.syncany.plugins.transfer.TransferSettings;
import org.syncany.util.Base58;
import org.syncany.util.ReflectionUtil;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;

/**
Expand Down Expand Up @@ -317,11 +307,7 @@ private TransferSettings createTransferSettings(byte[] plaintextPluginSettingsBy
}

Class<? extends TransferSettings> pluginTransferSettingsClass = TransferPluginUtil.getTransferSettingsClass(plugin.getClass());

Registry registry = new Registry();
Strategy strategy = new RegistryStrategy(registry);
registry.bind(String.class, new EncryptedTransferSettingsConverter(pluginTransferSettingsClass));
TransferSettings transferSettings = new Persister(strategy).read(pluginTransferSettingsClass, pluginSettings);
TransferSettings transferSettings = new Persister().read(pluginTransferSettingsClass, pluginSettings);

logger.log(Level.INFO, "(Decrypted) link contains: " + pluginId + " -- " + pluginSettings);

Expand All @@ -339,52 +325,9 @@ private byte[] getPlaintextStorageXml() throws Exception {
plaintextOutputStream.write(transferSettings.getType().getBytes());

GZIPOutputStream plaintextGzipOutputStream = new GZIPOutputStream(plaintextOutputStream);

Registry registry = new Registry();
Strategy strategy = new RegistryStrategy(registry);
registry.bind(String.class, new EncryptedTransferSettingsConverter(transferSettings.getClass()));
new Persister(strategy, new Format(0)).write(transferSettings, plaintextGzipOutputStream);
new Persister(new Format(0)).write(transferSettings, plaintextGzipOutputStream);
plaintextGzipOutputStream.close();

return plaintextByteArrayOutputStream.toByteArray();
}

public static class EncryptedTransferSettingsConverter implements Converter<String> {

private Class<? extends TransferSettings> transferSettingsClass;
private List<String> encryptedFields;

public EncryptedTransferSettingsConverter(Class<? extends TransferSettings> transferSettingsClass) {
this.transferSettingsClass = transferSettingsClass;
encryptedFields = getEncryptedFields(transferSettingsClass);
}

@Override
public String read(InputNode node) throws Exception {
if (!encryptedFields.contains(node.getName())) {
return node.getValue();
}

return TransferSettings.encrypt(node.getValue());
}

@Override
public void write(OutputNode node, String raw) throws Exception {
if (!encryptedFields.contains(node.getName())) {
node.setValue(raw) ;
return;
}

node.setValue(TransferSettings.decrypt(raw));
}

private List<String> getEncryptedFields(Class<? extends TransferSettings> clazz) {
List<String> encryptedFields = Lists.newArrayList();
for(Field field : ReflectionUtil.getAllFieldsWithAnnotation(clazz, Encrypted.class)) {
encryptedFields.add(field.getName());
}
return encryptedFields;
}

}
}
Expand Up @@ -166,7 +166,8 @@ public ConnectOperationResult execute() throws Exception {

// Write file 'config.xml'
File configFile = new File(appDir, Config.FILE_CONFIG);
writeXmlFile(configTO, configFile);
configTO.save(configFile);
//writeXmlFile(configTO, configFile);

// Write file 'syncany'
File repoFile = new File(appDir, Config.FILE_REPO);
Expand Down Expand Up @@ -255,7 +256,7 @@ private ConfigTO createConfigTOFromLink(ConfigTO configTO, String link) throws S
try {
TransferSettings transferSettings = applicationLink.createTransferSettings(masterKey);
configTO.setTransferSettings(transferSettings);

retryPassword = false;
}
catch (CipherException e) {
Expand Down
Expand Up @@ -118,7 +118,8 @@ public InitOperationResult execute() throws Exception {
writeXmlFile(options.getRepoTO(), repoFile);
}

writeXmlFile(options.getConfigTO(), configFile);
options.getConfigTO().save(configFile);
//writeXmlFile(options.getConfigTO(), configFile);

// Make remote changes
logger.log(Level.INFO, "Uploading local repository");
Expand Down
@@ -0,0 +1,57 @@
package org.syncany.plugins.transfer;

import java.lang.reflect.Field;
import java.util.List;

import org.simpleframework.xml.convert.Converter;
import org.simpleframework.xml.stream.InputNode;
import org.simpleframework.xml.stream.OutputNode;
import org.syncany.util.ReflectionUtil;
import com.google.common.collect.Lists;

/**
* @author Christian Roth <christian.roth@port17.de>
*/
public class EncryptedTransferSettingsConverter implements Converter<String> {

private Class<? extends TransferSettings> transferSettingsClass;
private List<String> encryptedFields;

public EncryptedTransferSettingsConverter() {
}

public EncryptedTransferSettingsConverter(Class<? extends TransferSettings> transferSettingsClass) {
this.transferSettingsClass = transferSettingsClass;
encryptedFields = getEncryptedFields(transferSettingsClass);
}

@Override
public String read(InputNode node) throws Exception {
InputNode encryptedAttribute = node.getAttribute("encrypted");
if (encryptedAttribute != null && encryptedAttribute.getValue().equals(Boolean.TRUE.toString())) {
return TransferSettings.decrypt(node.getValue());
}

return node.getValue();
}

@Override
public void write(OutputNode node, String raw) throws Exception {
if (encryptedFields.contains(node.getName())) {
node.setValue(TransferSettings.encrypt(raw));
node.setAttribute("encrypted", Boolean.TRUE.toString());
return;
}

node.setValue(raw);
}

private List<String> getEncryptedFields(Class<? extends TransferSettings> clazz) {
List<String> encryptedFields = Lists.newArrayList();
for (Field field : ReflectionUtil.getAllFieldsWithAnnotation(clazz, Encrypted.class)) {
encryptedFields.add(field.getName());
}
return encryptedFields;
}

}

0 comments on commit e9d29f7

Please sign in to comment.