Skip to content

Commit

Permalink
Initial implementation of the Lib Sodium enclave. (Consensys#29)
Browse files Browse the repository at this point in the history
* Initial implementation of the Lib Sodium enclave.
  • Loading branch information
rojotek authored Dec 17, 2017
1 parent a8011c3 commit 8ea8514
Show file tree
Hide file tree
Showing 16 changed files with 404 additions and 5 deletions.
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ cache:
- $HOME/.gradle/wrapper/
install: true
script:
- sudo apt-get install -y autoconf automake libtool make tar gcc-multilib libaio-dev
- sudo add-apt-repository -y ppa:elt/libsodium
- sudo apt-get update
- sudo apt-get install -y autoconf automake libtool make tar gcc-multilib libaio-dev libsodium-dev
- gradle build
- gradle test
- gradle test --stacktrace --info
notifications:
email: false
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ gradle build

Perhaps you might like using the ssh protocol to clone... in which case do `git clone git@github.com:ConsenSys/athena.git`

## libsodium

In order to be compatible with the original Haskell Constellation, the lib sodium library has been used to provide the encryption primitives.

In order to use this, you will first need to install lib sodium on your machine.

mac:
`brew install libsodium`

## Native transports

In order to make sure the http related communications are as optimised as possible, we use native transports with the
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies {
compile 'com.moandjiezana.toml:toml4j:0.7.2'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.0'
implementation 'org.mapdb:mapdb:3.0.5'

implementation group: 'com.muquit.libsodiumjna', name: 'libsodium-jna', version: '1.0.4'
testImplementation 'junit:junit:4.12'

// logger
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/net/consensys/athena/api/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ public interface Config {
*/
long port();

/**
* Path at which to locate the lib sodium shared library. Default:
*
* <ul>
* <li><b>Linux</b> /usr/local/lib/libsodium.so
* <li><b>Mac</b> /usr/local/lib/libsodium.dylib
* <li><b>Windows</b> C:/libsodium/libsodium.dll
* </ul>
*/
String libSodiumPath();

/**
* Directory to which paths to all other files referenced in the config are relative to.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@
* @see java.security.Key
* @see java.security.PrivateKey
*/
public interface CombinedKey {}
public interface CombinedKey {
byte[] getEncoded();
}
10 changes: 10 additions & 0 deletions src/main/java/net/consensys/athena/impl/config/MemoryConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class MemoryConfig implements Config {
private Optional<String[]> generateKeys = Optional.empty();
private Optional<Boolean> showVersion = Optional.empty();
private long verbosity;
private String libSodiumPath;

public void setUrl(URL url) {
this.url = url;
Expand Down Expand Up @@ -134,6 +135,10 @@ public void setVerbosity(long verbosity) {
this.verbosity = verbosity;
}

public void setLibSodiumPath(String libSodiumPath) {
this.libSodiumPath = libSodiumPath;
}

@Override
public URL url() {
return url;
Expand Down Expand Up @@ -258,4 +263,9 @@ public Optional<Boolean> showVersion() {
public long verbosity() {
return verbosity;
}

@Override
public String libSodiumPath() {
return libSodiumPath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import net.consensys.athena.api.config.Config;
import net.consensys.athena.api.config.ConfigException;
import net.consensys.athena.impl.enclave.sodium.LibSodiumSettings;

import java.io.File;
import java.io.InputStream;
Expand Down Expand Up @@ -45,6 +46,9 @@ public Config build(InputStream config) throws ConfigException {
memoryConfig.setSocket(new File(toml.getString("socket")));
}

memoryConfig.setLibSodiumPath(
toml.getString("libsodiumpath", LibSodiumSettings.defaultLibSodiumPath()));

try {
memoryConfig.setOtherNodes(convertListToURLArray(toml.getList("othernodes")));
} catch (ConfigException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package net.consensys.athena.impl.enclave.sodium;

import net.consensys.athena.api.config.Config;
import net.consensys.athena.api.enclave.CombinedKey;
import net.consensys.athena.api.enclave.Enclave;
import net.consensys.athena.api.enclave.EnclaveException;
import net.consensys.athena.api.enclave.EncryptedPayload;
import net.consensys.athena.api.enclave.HashAlgorithm;
import net.consensys.athena.api.enclave.KeyStore;
import net.consensys.athena.impl.enclave.SimpleEncryptedPayload;
import net.consensys.athena.impl.enclave.bouncycastle.Hasher;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;

import com.muquit.libsodiumjna.SodiumLibrary;
import com.muquit.libsodiumjna.exceptions.SodiumLibraryException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;

public class LibSodiumEnclave implements Enclave {
private static final Logger log = LogManager.getLogger();

static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}

private final Hasher hasher = new Hasher();
private KeyStore keyStore;

public LibSodiumEnclave(Config config, KeyStore keyStore) {
SodiumLibrary.setLibraryPath(config.libSodiumPath());

this.keyStore = keyStore;
}

@Override
public byte[] digest(HashAlgorithm algorithm, byte[] input) {
return hasher.digest(algorithm, input);
}

@Override
public EncryptedPayload encrypt(byte[] plaintext, PublicKey senderKey, PublicKey[] recipients) {
try {
PrivateKey senderPrivateKey = keyStore.getPrivateKey(senderKey);
if (senderPrivateKey == null) {
throw new EnclaveException("No PrivateKey found in keystore");
}
byte[] secretKey =
SodiumLibrary.randomBytes(SodiumLibrary.cryptoSecretBoxKeyBytes().intValue());
byte[] secretNonce = secretNonce();
byte[] cipherText = SodiumLibrary.cryptoSecretBoxEasy(plaintext, secretNonce, secretKey);

byte[] nonce = nonce();
CombinedKey[] combinedKeys = getCombinedKeys(recipients, senderPrivateKey, secretKey, nonce);
return new SimpleEncryptedPayload(senderKey, secretNonce, nonce, combinedKeys, cipherText);
} catch (SodiumLibraryException e) {
throw new EnclaveException(e);
}
}

@Override
public byte[] decrypt(EncryptedPayload ciphertextAndMetadata, PublicKey identity) {
try {
PrivateKey privateKey = keyStore.getPrivateKey(identity);
if (privateKey == null) {
throw new EnclaveException("No PrivateKey found in keystore");
}
CombinedKey key = ciphertextAndMetadata.getCombinedKeys()[0];
byte[] secretKey =
SodiumLibrary.cryptoBoxOpenEasy(
key.getEncoded(),
ciphertextAndMetadata.getCombinedKeyNonce(),
ciphertextAndMetadata.getSender().getEncoded(),
privateKey.getEncoded());
return SodiumLibrary.cryptoSecretBoxOpenEasy(
ciphertextAndMetadata.getCipherText(), ciphertextAndMetadata.getNonce(), secretKey);
} catch (SodiumLibraryException e) {
throw new EnclaveException(e);
}
}

private byte[] secretNonce() {
int secretNonceBytesLength = SodiumLibrary.cryptoSecretBoxNonceBytes().intValue();

return SodiumLibrary.randomBytes(secretNonceBytesLength);
}

private byte[] nonce() {
int nonceBytesLength = SodiumLibrary.cryptoBoxNonceBytes().intValue();
return SodiumLibrary.randomBytes(nonceBytesLength);
}

@NotNull
private CombinedKey[] getCombinedKeys(
PublicKey[] recipients, PrivateKey senderPrivateKey, byte[] secretKey, byte[] nonce)
throws SodiumLibraryException {
CombinedKey[] combinedKeys = new CombinedKey[recipients.length];
for (int i = 0; i < recipients.length; i++) {
PublicKey recipient = recipients[i];
byte[] encryptedKey =
SodiumLibrary.cryptoBoxEasy(
secretKey, nonce, recipient.getEncoded(), senderPrivateKey.getEncoded());
SodiumCombinedKey combinedKey = new SodiumCombinedKey(encryptedKey);
combinedKeys[i] = combinedKey;
}
return combinedKeys;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.consensys.athena.impl.enclave.sodium;

import java.io.File;

import com.sun.jna.Platform;

public class LibSodiumSettings {

public static String defaultLibSodiumPath() {
if (Platform.isMac()) {
return "/usr/local/lib/libsodium.dylib";
} else if (Platform.isWindows()) {
return "C:/libsodium/libsodium.dll";
} else if (new File("/usr/lib/x86_64-linux-gnu/libsodium.so").exists()) {
//Ubuntu trusty location.
return "/usr/lib/x86_64-linux-gnu/libsodium.so";
} else {
return "/usr/local/lib/libsodium.so";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.consensys.athena.impl.enclave.sodium;

import net.consensys.athena.api.enclave.CombinedKey;

public class SodiumCombinedKey implements CombinedKey {

byte[] key;

public SodiumCombinedKey(byte[] key) {
this.key = key;
}

@Override
public byte[] getEncoded() {
return key;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package net.consensys.athena.impl.enclave.sodium;

import net.consensys.athena.api.enclave.EnclaveException;
import net.consensys.athena.api.enclave.KeyStore;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;

import com.muquit.libsodiumjna.SodiumKeyPair;
import com.muquit.libsodiumjna.SodiumLibrary;
import com.muquit.libsodiumjna.exceptions.SodiumLibraryException;

public class SodiumMemoryKeyStore implements KeyStore {

Map<PublicKey, PrivateKey> store = new HashMap<>();

@Override
public PrivateKey getPrivateKey(PublicKey publicKey) {
return store.get(publicKey);
}

@Override
public PublicKey generateKeyPair() {
try {
SodiumKeyPair keyPair = SodiumLibrary.cryptoBoxKeyPair();
SodiumPrivateKey privateKey = new SodiumPrivateKey(keyPair.getPrivateKey());
SodiumPublicKey publicKey = new SodiumPublicKey(keyPair.getPublicKey());
store.put(publicKey, privateKey);
return publicKey;
} catch (SodiumLibraryException e) {
throw new EnclaveException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package net.consensys.athena.impl.enclave.sodium;

import java.security.PrivateKey;
import java.util.Arrays;

public class SodiumPrivateKey implements PrivateKey {

private byte[] privateKey;

public SodiumPrivateKey(byte[] privateKey) {

this.privateKey = privateKey;
}

@Override
public String getAlgorithm() {
return "sodium";
}

@Override
public String getFormat() {
return "raw";
}

@Override
public byte[] getEncoded() {
return privateKey;
}

@Override
public String toString() {
return "SodiumPrivateKey{" + "privateKey=" + Arrays.toString(privateKey) + '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.consensys.athena.impl.enclave.sodium;

import java.security.PublicKey;

public class SodiumPublicKey implements PublicKey {

private byte[] publicKey;

public SodiumPublicKey(byte[] publicKey) {
this.publicKey = publicKey;
}

@Override
public String getAlgorithm() {
return null;
}

@Override
public String getFormat() {
return null;
}

@Override
public byte[] getEncoded() {
return publicKey;
}
}
Loading

0 comments on commit 8ea8514

Please sign in to comment.