Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zmq secure connect implementation #641

Merged
merged 21 commits into from
Oct 3, 2018
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
56a07ca
zmq secure connection implement
AionJayT Sep 6, 2018
9fc903a
Merge branch 'native-check' into secure-zmq
AionJayT Sep 6, 2018
49c46a7
implement the curve key loading in the zmq ProtocolProcessor
AionJayT Sep 6, 2018
f994aea
more detail for the log and the config settings
AionJayT Sep 6, 2018
56998ef
Merge branch 'master' into secure-zmq
AionJayT Sep 7, 2018
91c64b1
fix zmq key loading error
AionJayT Sep 10, 2018
3d570d6
fix api secure connect config reset after kernel launch
AionJayT Sep 10, 2018
17fc73c
modify zmq key naming rule for the OS compatibility
AionJayT Sep 10, 2018
8067f6f
Merge branch 'master-pre-merge' into secure-zmq
AionJayT Sep 14, 2018
49c0ea7
add zmq_keystore into gitignore
AionJayT Sep 25, 2018
3f84a20
refactoring the getFiles method to a class
AionJayT Sep 25, 2018
1c75109
refactoring zmq secure connection for logging
AionJayT Sep 25, 2018
33c68bd
refactoring CfgApiZmq logging, ProtocolProcessor construct process an…
AionJayT Sep 25, 2018
0f14ba4
fix merge error
aionjay Oct 1, 2018
04672de
update api repo ref
aionjay Oct 1, 2018
415caea
move zmq key pair generate ouside of Cli. Will be checked after confi…
aionjay Oct 2, 2018
27caad4
undo format Cli and remove comment in Aion class
aionjay Oct 2, 2018
a51c944
fix pack issue
aionjay Oct 2, 2018
117a502
Merge branch 'master-pre-merge' into secure-zmq
aionjay Oct 3, 2018
699080a
fix module-info settings for modAionImpl
aionjay Oct 3, 2018
2208f82
update default config settings for the Java API secure connect
aionjay Oct 3, 2018
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/log/
/rt/
/web3/
/zmq_keystore/
/jars/

# ide
Expand Down
2 changes: 1 addition & 1 deletion aion_api
1 change: 1 addition & 0 deletions modAionBase/src/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
exports org.aion.base.util;
exports org.aion.base.vm;
exports org.aion.base.db;
exports org.aion.base.io;
exports org.aion.base;
}
46 changes: 46 additions & 0 deletions modAionBase/src/org/aion/base/io/File.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2017-2018 Aion foundation.
*
* This file is part of the aion network project.
*
* The aion network project is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or any later version.
*
* The aion network project is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the aion network project source files.
* If not, see <https://www.gnu.org/licenses/>.
*
* Contributors:
* Aion foundation.
*/

package org.aion.base.io;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class File {
public static List<java.io.File> getFiles(final Path path) {
if (path == null) {
System.out.println("getFiles null path input!");
return Collections.emptyList();
}

try {
java.io.File[] files = path.toFile().listFiles();
return files != null ? Arrays.asList(files) : Collections.emptyList();
} catch (UnsupportedOperationException | NullPointerException e) {
System.out.println("getFiles exception: " + e.toString());
return Collections.emptyList();
}
}
}
2 changes: 2 additions & 0 deletions modAionImpl/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@
<pathelement location="${dir.lib}/guava-25.1-jre.jar"/>
<pathelement location="${dir.lib}/libnsc.jar"/>
<pathelement location="${dir.lib}/slf4j-api-1.7.25.jar"/>
<pathelement location="${dir.lib}/libnzmq.jar"/>
<pathelement location="${dir.lib}/jsr305-3.0.2.jar"/>
</modulepath>
</javac>

Expand Down
108 changes: 96 additions & 12 deletions modAionImpl/src/org/aion/zero/impl/cli/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,43 @@

package org.aion.zero.impl.cli;

import static org.aion.mcf.account.Keystore.exist;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.io.BufferedReader;
import java.io.Console;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import javax.annotation.Nonnull;
import org.aion.base.util.Hex;
import org.aion.crypto.ECKey;
import org.aion.crypto.ECKeyFac;
import org.aion.mcf.account.Keystore;
import org.aion.mcf.config.Cfg;
import org.aion.mcf.config.CfgApiZmq;
import org.aion.mcf.config.CfgSsl;
import org.aion.zero.impl.Version;
import org.aion.zero.impl.config.CfgAion;
import org.aion.zero.impl.db.RecoveryUtils;
import org.zeromq.ZMQ;

import java.io.Console;

/**
* Command line interface.
Expand All @@ -52,6 +69,12 @@
*/
public class Cli {

private File keystoreDir = new File(
System.getProperty("user.dir") + File.separator + CfgSsl.SSL_KEYSTORE_DIR);

private File zmqkeyDir = new File(
System.getProperty("user.dir") + File.separator + CfgApiZmq.ZMQ_KEY_DIR);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the zmq_keystore should be added to .gitignore

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for Alexandra's suggestion

Also, can we have the value of System.getProperty() be passed into Cli's constructor instead of referring to it directly? The dependence on static methtod calls will make the class hard to test / modularize.

Copy link
Collaborator Author

@AionJayT AionJayT Sep 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aion-kelvin created an issue for tracking System.getProperty()
#652


private final String BASE_PATH = System.getProperty("user.dir");

private String BASE_PATH_WITH_NETWORK = BASE_PATH + "/config/" + CfgAion.getNetwork();
Expand All @@ -60,8 +83,6 @@ public class Cli {

private String dstGenesis = BASE_PATH_WITH_NETWORK + "/genesis.json";

File keystoreDir = new File(System.getProperty("user.dir") + File.separator + CfgSsl.SSL_KEYSTORE_DIR);

enum Network {
MAINNET, CONQUEST;

Expand Down Expand Up @@ -213,6 +234,17 @@ public int call(final String[] args, Cfg cfg, String path) {
return 1;
}
break;
case "-zs":
checkZmqKeystoreDir();
if (existZmqSecKeyFile()) {
System.out.println("Found existing secret key file. Please backup or remove it!");
return 1;
}

ZMQ.Curve.KeyPair kp = ZMQ.Curve.generateKeyPair();
genKeyFile(kp.publicKey, kp.secretKey);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on this implementation a user can generate multiple keys. Does having multiple keys have any advantage? Based on the key loading implementation I see a disadvantage in the fact that the chosen pair is relatively random. It may be preferable to overwrite the old key if the key generation is called a second time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlexandraRoatis

It may be preferable to overwrite the old key if the key generation is called a second time.

Alternatively, refuse to generate another one unless they manually remove the existing one, so they don't accidentally delete their private key and lose it forever. Or just prompt them before overwriting.

System.out.println("Generate ZmqKeyPairFinished!");
break;
case "-r":
if (args.length < 2) {
System.out.println("Starting database clean-up.");
Expand Down Expand Up @@ -448,15 +480,67 @@ else if (isValid(args[3])) {
return 1;
}

System.out.println("");
System.out.println();
} catch (Throwable e) {
System.out.println("");
System.out.println(e.toString());
return 1;
}

return 0;
}

private boolean existZmqSecKeyFile() {
List<File> files = org.aion.base.io.File.getFiles(zmqkeyDir.toPath());

for (File file : files) {
if (file.getName().contains("zmqCurveSeckey")) {
return true;
}
}

return false;
}

private void genKeyFile(@Nonnull final String publicKey, @Nonnull final String secretKey)
throws IOException {

DateFormat df = new SimpleDateFormat("yy-MM-dd'T'HH-mm-ss'Z'");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String iso_date = df.format(new Date(System.currentTimeMillis()));

String fileName = "UTC--" + iso_date + "--zmqCurvePubkey";
writeKeyToFile(fileName, publicKey);

fileName = "UTC--" + iso_date + "--zmqCurveSeckey";
writeKeyToFile(fileName, secretKey);
}

private void writeKeyToFile(@Nonnull final String fileName, @Nonnull final String key) throws IOException {
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-----");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);

Path p = Paths.get(zmqkeyDir.getPath());
Path keyFile = p.resolve(fileName);
if (!java.nio.file.Files.exists(keyFile)) {
keyFile = java.nio.file.Files.createFile(keyFile, attr);
}

FileOutputStream fos = new FileOutputStream(keyFile.toString());
fos.write(key.getBytes());
fos.close();
}

private void checkZmqKeystoreDir() {
if (!zmqkeyDir.isDirectory()) {
if (!zmqkeyDir.mkdir()) {
System.out.println("zmq keystore directory could not be created. " +
"Please check user permissions or create directory manually.");
System.exit(1);
}
System.out.println();
}
}

/**
* Print the CLI help info.
*/
Expand Down Expand Up @@ -586,7 +670,7 @@ private boolean listAccounts() {
* @return boolean
*/
private boolean exportPrivateKey(String address) {
if (!Keystore.exist(address)) {
if (!exist(address)) {
System.out.println("The account does not exist!");
return false;
}
Expand Down Expand Up @@ -786,7 +870,7 @@ private static boolean copyRecursively(File src, File target)
}
else {
try {
Files.copy(src, target);
com.google.common.io.Files.copy(src, target);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason this was removed from imports?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are 2 different Files classes using in this class

return true;
}
catch (IOException e) {
Expand Down
4 changes: 2 additions & 2 deletions modApiServer/src/org/aion/api/server/ApiAion.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public abstract class ApiAion extends Api {
// 2. underlying datastructure provides concurrency guarntees

// delegate concurrency to underlying object
protected static NrgOracle NRG_ORACLE;
private static NrgOracle NRG_ORACLE;
protected IAionChain ac; // assumption: blockchainImpl et al. provide concurrency guarantee

// using java.util.concurrent library objects
Expand Down Expand Up @@ -224,7 +224,7 @@ public AionBlock getBlock(long blkNr) {
}
}

public Map.Entry<AionBlock, BigInteger> getBlockWithTotalDifficulty(long blkNr) {
protected Map.Entry<AionBlock, BigInteger> getBlockWithTotalDifficulty(long blkNr) {
if (blkNr > 0) {
return ((AionBlockStore) this.ac.getBlockchain().getBlockStore())
.getChainBlockByNumberWithTotalDifficulty(blkNr);
Expand Down
15 changes: 7 additions & 8 deletions modApiServer/src/org/aion/api/server/zmq/HdlrZmq.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*******************************************************************************
/*
* Copyright (c) 2017-2018 Aion foundation.
*
* This file is part of the aion network project.
Expand All @@ -19,8 +19,7 @@
*
* Contributors:
* Aion foundation.
*
******************************************************************************/
*/

package org.aion.api.server.zmq;

Expand Down Expand Up @@ -70,7 +69,7 @@ public byte[] process(byte[] request, byte[] socketId) {
}
}

public void getTxWait() {
void getTxWait() {
TxWaitingMappingUpdate txWait = null;
try {
txWait = this.api.takeTxWait();
Expand Down Expand Up @@ -107,15 +106,15 @@ public Map<Long, Fltr> getFilter() {
return this.api.getFilter();
}

public BlockingQueue<TxPendingStatus> getTxStatusQueue() {
BlockingQueue<TxPendingStatus> getTxStatusQueue() {
return this.api.getPendingStatus();
}

public byte[] toRspMsg(byte[] msgHash, int txCode, String error) {
byte[] toRspMsg(byte[] msgHash, int txCode, String error) {
return ApiUtil.toReturnHeader(this.api.getApiVersion(), txCode, msgHash, error.getBytes());
}

public byte[] toRspMsg(byte[] msgHash, int txCode, String error, byte[] result) {
byte[] toRspMsg(byte[] msgHash, int txCode, String error, byte[] result) {
return ApiUtil.toReturnHeader(this.api.getApiVersion(), txCode, msgHash, error.getBytes(), result);
}

Expand All @@ -124,7 +123,7 @@ public byte[] process(byte[] request) {
return null;
}

public byte[] toRspEvtMsg(byte[] ecb) {
byte[] toRspEvtMsg(byte[] ecb) {
return ApiUtil.toReturnEvtHeader(this.api.getApiVersion(), ecb);
}

Expand Down
Loading