Use correct crypto curve for DH

This commit is contained in:
Harald Hoyer 2016-09-02 15:53:45 +02:00
parent f1c7da339c
commit 06556c673a
6 changed files with 323 additions and 164 deletions

View file

@ -7,7 +7,16 @@
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>opret-testapp</artifactId> <artifactId>opret-testapp</artifactId>
<repositories>
<repository>
<id>oss-sonatype</id>
<name>oss-sonatype</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies> <dependencies>
<dependency> <dependency>
@ -29,11 +38,12 @@
<version>1.0.13</version> <version>1.0.13</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.abstractj.kalium</groupId> <groupId>com.github.joshjdevl.libsodiumjni</groupId>
<artifactId>kalium</artifactId> <artifactId>libsodium-jni</artifactId>
<version>0.5.0</version> <version>1.0.2-SNAPSHOT</version>
</dependency> <type>jar</type>
</dependency>
<dependency> <dependency>
<groupId>org.tcpid</groupId> <groupId>org.tcpid</groupId>

View file

@ -1,34 +1,33 @@
package org.tcpid.opretj.testapp; package org.tcpid.opretj.testapp;
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN; import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
import static org.libsodium.jni.SodiumConstants.NONCE_BYTES;
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import org.abstractj.kalium.crypto.Box; import org.bitcoinj.core.Address;
import org.abstractj.kalium.crypto.Hash; import org.bitcoinj.core.AddressFormatException;
import org.abstractj.kalium.keys.KeyPair;
import org.abstractj.kalium.keys.PublicKey;
import org.abstractj.kalium.keys.SigningKey;
import org.abstractj.kalium.keys.VerifyKey;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.utils.Threading; import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.KeyChain.KeyPurpose;
import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet.SendResult; import org.libsodium.jni.crypto.Box;
import org.libsodium.jni.crypto.Hash;
import org.libsodium.jni.keys.KeyPair;
import org.libsodium.jni.keys.PublicKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.tcpid.opretj.OPRETECParser; import org.tcpid.opretj.OPRETECParser;
@ -45,40 +44,46 @@ import joptsimple.OptionSet;
import joptsimple.OptionSpec; import joptsimple.OptionSpec;
public class App { public class App {
private static final Logger logger = LoggerFactory.getLogger(App.class); public static final Hash HASH = new Hash();
public final static long OPRET_BIRTHDAY = 1471989600;
public static final long OPRET_BIRTHDAY = 1471989600; private final static Logger logger = LoggerFactory.getLogger(App.class);
static byte[] rawKey = Sha256Hash.hash("TESTSEED".getBytes()); private final static SigningKey SK = new SigningKey(HASH.sha256("TESTSEED".getBytes()));
static SigningKey sk = new SigningKey(rawKey); private final static VerifyKey VK = SK.getVerifyKey();
static VerifyKey v = sk.getVerifyKey(); private final static byte[] PKHASH = HASH.sha256(VK.toBytes());
private static byte[] pkhash = Sha256Hash.hash(v.toBytes()); private final static byte[] REVOKEMSG = Bytes.concat("Revoke ".getBytes(), PKHASH);
static byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), pkhash);
public static void checkKey(final byte[] pkhash, final byte[] sig) { public static void checkKey(final byte[] pkhash, final byte[] sig) {
logger.warn("CHECKING REVOKE PK {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig)); logger.warn("CHECKING REVOKE PKHASH {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
if (!Arrays.equals(App.pkhash, pkhash)) { if (!Arrays.equals(Arrays.copyOfRange(App.PKHASH, 0, 12), pkhash)) {
logger.warn("Unknown PK {}", Utils.HEX.encode(pkhash)); logger.warn("Unknown PK {}", Utils.HEX.encode(pkhash));
return; return;
} }
logger.warn("Using VerifyKey {}", v); logger.warn("Using VerifyKey {}", VK);
if (v.verify(revokemsg, sig)) { if (VK.verify(REVOKEMSG, sig)) {
logger.warn("REVOKED VerifyKey {}", v); logger.warn("REVOKED VerifyKey {}", VK);
} else { } else {
logger.warn("SIGNATURE does not match!"); logger.warn("SIGNATURE does not match!");
} }
} }
private static boolean cryptoSelfTest() { private static boolean cryptoSelfTest() {
final KeyPair MKpair = new KeyPair(); final SigningKey msk = new SigningKey();
final KeyPair VKpair = new KeyPair();
final KeyPair mkpair = msk.getKeyPair();
final KeyPair vkpair = SK.getKeyPair();
final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8); final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8);
final byte[] cipher = doubleEnc(MKpair, VKpair, "TEST".getBytes(), nonce); final byte[] cipher = doubleEnc(mkpair, vkpair, "TEST".getBytes(), nonce);
// System.err.println("Cipher len: " + cipher.length); // System.err.println("Cipher len: " + cipher.length);
final byte[] chk = doubleDec(MKpair.getPublicKey(), VKpair.getPublicKey(), cipher, nonce); try {
return Arrays.equals(chk, "TEST".getBytes()); final byte[] chk = doubleDec(msk.getVerifyKey().getPublicKey(), VK.getPublicKey(), cipher, nonce);
return Arrays.equals(chk, "TEST".getBytes());
} catch (final Exception e) {
return false;
}
} }
private static void displayBalance(final OPRETWalletAppKit kit, final PrintWriter out) { private static void displayBalance(final OPRETWalletAppKit kit, final PrintWriter out) {
@ -101,21 +106,21 @@ public class App {
} }
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) { private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
final Hash h = new Hash();
final KeyPair Epair = new KeyPair( final KeyPair Epair = new KeyPair(
Arrays.copyOfRange(h.blake2(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())), 0, 32)); Arrays.copyOfRange(HASH.sha256(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())), 0, SECRETKEY_BYTES));
final Box boxVK = new Box(VK.toBytes(), Epair.getPrivateKey().toBytes()); final Box boxVK = new Box(VK, Epair.getPrivateKey());
final byte[] nonceVK = Arrays
.copyOfRange(h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VK.toBytes())), 0, 24); final byte[] nonceVK = Arrays.copyOfRange(
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VK.toBytes())), 0, NONCE_BYTES);
final byte[] cipherMK = boxVK.decrypt(nonceVK, cipher); final byte[] cipherMK = boxVK.decrypt(nonceVK, cipher);
final Box boxMK = new Box(MK.toBytes(), Epair.getPrivateKey().toBytes()); final Box boxMK = new Box(MK, Epair.getPrivateKey());
final byte[] nonceMK = Arrays final byte[] nonceMK = Arrays.copyOfRange(
.copyOfRange(h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())), 0, 24); HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())), 0, NONCE_BYTES);
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK); final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
@ -124,22 +129,27 @@ public class App {
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear, private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
final byte[] nonce) { final byte[] nonce) {
final Hash h = new Hash();
final KeyPair Epair = new KeyPair(Arrays.copyOfRange( final KeyPair Epair = new KeyPair(Arrays.copyOfRange(
h.blake2(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())), 0, HASH.sha256(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
32)); SECRETKEY_BYTES));
final Box boxMK = new Box(Epair.getPublicKey(), MKpair.getPrivateKey());
final Box boxMK = new Box(Epair.getPublicKey().toBytes(), MKpair.getPrivateKey().toBytes());
final byte[] nonceMK = Arrays.copyOfRange( final byte[] nonceMK = Arrays.copyOfRange(
h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())), 0, 24); HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
NONCE_BYTES);
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear); final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
final Box boxVK = new Box(Epair.getPublicKey().toBytes(), VKpair.getPrivateKey().toBytes()); final Box boxVK = new Box(Epair.getPublicKey(), VKpair.getPrivateKey());
final byte[] nonceVK = Arrays.copyOfRange( final byte[] nonceVK = Arrays.copyOfRange(
h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())), 0, 24); HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())), 0,
NONCE_BYTES);
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK); final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
return cipherVK; return cipherVK;
} }
@ -167,7 +177,6 @@ public class App {
} }
private static void handleConsole(final OPRETWalletAppKit kit) throws IOException { private static void handleConsole(final OPRETWalletAppKit kit) throws IOException {
final String receiveStr = kit.wallet().freshReceiveAddress().toString();
final ConsoleReader reader = new ConsoleReader(); final ConsoleReader reader = new ConsoleReader();
final String[] cmds = { "help", "quit", "exit", "balance", "receive", "empty", "opret" }; final String[] cmds = { "help", "quit", "exit", "balance", "receive", "empty", "opret" };
reader.addCompleter(new StringsCompleter(cmds)); reader.addCompleter(new StringsCompleter(cmds));
@ -178,13 +187,17 @@ public class App {
displayBalance(kit, out); displayBalance(kit, out);
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
final String[] argv = line.split("\\s"); String[] argv = line.split("\\s");
if (argv.length == 0) { if (argv.length == 0) {
continue; continue;
} }
final String cmd = argv[0]; final String cmd = argv[0];
final String args[] = Arrays.copyOfRange(argv, 1, argv.length); if (cmd.isEmpty()) {
continue;
}
argv = Arrays.copyOfRange(argv, 1, argv.length);
switch (cmd.toLowerCase()) { switch (cmd.toLowerCase()) {
case "quit": case "quit":
@ -196,6 +209,8 @@ public class App {
displayBalance(kit, out); displayBalance(kit, out);
break; break;
case "receive": case "receive":
final String receiveStr = kit.wallet().freshReceiveAddress().toString();
out.write("send money to: " + receiveStr + "\n"); out.write("send money to: " + receiveStr + "\n");
try { try {
out.write(executeCommand("qrencode -t UTF8 -o - " + receiveStr)); out.write(executeCommand("qrencode -t UTF8 -o - " + receiveStr));
@ -205,14 +220,31 @@ public class App {
out.flush(); out.flush();
break; break;
case "empty": case "empty":
if (args.length != 1) { if (argv.length != 1) {
out.println("empty needs a receive address!"); out.println("'empty <address>' needs a valid receive address!");
continue; continue;
} }
out.println("'" + argv[0] + "'");
out.flush();
try {
final SendRequest request = SendRequest.emptyWallet(Address.fromBase58(kit.params(), argv[0]));
try {
kit.wallet().sendCoins(request);
} catch (final InsufficientMoneyException e) {
out.println(e.getLocalizedMessage());
out.flush();
}
} catch (final AddressFormatException e) {
out.println(e.getLocalizedMessage());
out.flush();
}
break; break;
case "opret": case "opret":
sendOPReturn(kit, out); sendOPReturn(kit, out);
break; break;
default:
out.println("Unknown command. Use 'help' to display available commands.");
break;
} }
} }
} }
@ -256,7 +288,7 @@ public class App {
earliestTime = Utils.currentTimeSeconds(); earliestTime = Utils.currentTimeSeconds();
} }
bs.addOPRET(pkhash, earliestTime); bs.addOPRET(Arrays.copyOfRange(App.PKHASH, 0, 12), earliestTime);
// bs.addOPRET(Sha256Hash.hash("test1".getBytes()), earliestTime); // bs.addOPRET(Sha256Hash.hash("test1".getBytes()), earliestTime);
// bs.addOPRET(Utils.HEX.decode("0f490dee643b01b06e0ea84c253a90050a3543cfb7c74319fb47b04afee5b872"), // bs.addOPRET(Utils.HEX.decode("0f490dee643b01b06e0ea84c253a90050a3543cfb7c74319fb47b04afee5b872"),
// earliestTime); // earliestTime);
@ -274,7 +306,10 @@ public class App {
if (params.getId().equals(NetworkParameters.ID_REGTEST)) { if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
kit.connectToLocalHost(); kit.connectToLocalHost();
} }
kit.setCheckpoints(App.class.getResourceAsStream("/" + params.getId() + ".checkpoints")); final InputStream is = App.class.getResourceAsStream("/" + params.getId() + ".checkpoints");
if (is != null) {
kit.setCheckpoints(is);
}
kit.startAsync(); kit.startAsync();
System.out.println("Please wait for the blockchain to be downloaded!"); System.out.println("Please wait for the blockchain to be downloaded!");
@ -292,23 +327,40 @@ public class App {
final OPRETWallet wallet = kit.opretwallet(); final OPRETWallet wallet = kit.opretwallet();
wallet.addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> { wallet.addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> {
System.out.println("-----> coins resceived: " + tx.getHashAsString()); final Coin c = tx.getValue(wallet1);
System.out.println("received: " + tx.getValue(wallet1));
if (c.isPositive()) {
System.out.println("-----> coins received: " + tx.getHashAsString());
System.out.println("received: " + c);
} else {
System.out.println("-----> coins sent: " + tx.getHashAsString());
System.out.println("sent: " + c.negate().toFriendlyString());
}
}); });
wallet.addCoinsSentEventListener(Threading.SAME_THREAD, /*
(wallet1, tx, prevBalance, newBalance) -> System.out.println("coins sent")); * wallet.addCoinsSentEventListener(Threading.SAME_THREAD, (wallet1, tx,
* prevBalance, newBalance) -> { final Coin c = tx.getValue(wallet1);
wallet.addKeyChainEventListener(Threading.SAME_THREAD, keys -> System.out.println("new key added")); *
* if (c.isPositive()) { System.out.println("-----> coins received: " +
wallet.addScriptChangeEventListener(Threading.SAME_THREAD, * tx.getHashAsString()); System.out.println("received: " + c); } else {
(wallet1, scripts, isAddingScripts) -> System.out.println("new script added")); * System.out.println("-----> coins sent: " + tx.getHashAsString());
* System.out.println("sent: " + c.negate().toFriendlyString()); }
wallet.addTransactionConfidenceEventListener(Threading.SAME_THREAD, (wallet1, tx) -> { *
System.out.println("-----> confidence changed: " + tx.getHashAsString()); * });
final TransactionConfidence confidence = tx.getConfidence(); *
System.out.println("new block depth: " + confidence.getDepthInBlocks()); * wallet.addKeyChainEventListener(Threading.SAME_THREAD, keys ->
}); * System.out.println("new key added"));
*
* wallet.addScriptChangeEventListener(Threading.SAME_THREAD, (wallet1,
* scripts, isAddingScripts) -> System.out.println("new script added"));
*
* wallet.addTransactionConfidenceEventListener(Threading.SAME_THREAD,
* (wallet1, tx) -> { System.out.println("-----> confidence changed: " +
* tx.getHashAsString()); final TransactionConfidence confidence =
* tx.getConfidence(); System.out.println("new block depth: " +
* confidence.getDepthInBlocks()); });
*/
// wallet.allowSpendingUnconfirmedTransactions(); // wallet.allowSpendingUnconfirmedTransactions();
handleConsole(kit); handleConsole(kit);
@ -322,44 +374,17 @@ public class App {
final OPRETWallet wallet = kit.opretwallet(); final OPRETWallet wallet = kit.opretwallet();
final NetworkParameters params = wallet.getNetworkParameters(); final NetworkParameters params = wallet.getNetworkParameters();
Transaction t = new Transaction(params); final Transaction t = new Transaction(params);
final byte[] sig = sk.sign(revokemsg); final byte[] sig = SK.sign(REVOKEMSG);
Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec1d")).data(Utils.HEX.decode("fe")) final Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec0f")).data(sig)
.data(pkhash).data(Arrays.copyOfRange(sig, 0, 32)).build(); .data(Arrays.copyOfRange(PKHASH, 0, 12)).build();
t.addOutput(Coin.ZERO, script); t.addOutput(Coin.ZERO, script);
t.addOutput(Transaction.DEFAULT_TX_FEE, wallet.freshAddress(KeyPurpose.CHANGE)); final SendRequest request = SendRequest.forTx(t);
SendRequest request = SendRequest.forTx(t);
request.ensureMinRequiredFee = true; request.ensureMinRequiredFee = true;
SendResult sr = null; request.shuffleOutputs = false;
try { try {
sr = wallet.sendCoins(request); wallet.sendCoins(request);
} catch (final InsufficientMoneyException e) {
output.println(e.getLocalizedMessage());
output.flush();
return false;
}
logger.debug("SendRequest {}", request);
script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec1d")).data(Utils.HEX.decode("ff"))
.data(pkhash).data(Arrays.copyOfRange(sig, 32, 64)).build();
t = new Transaction(params);
for (final TransactionOutput out : sr.tx.getOutputs()) {
if (out.getValue().compareTo(Transaction.DEFAULT_TX_FEE) == 0) {
logger.debug("Add Output: {} of value {}", out, out.getValue());
t.addInput(out);
}
}
t.addOutput(Coin.ZERO, script);
request = SendRequest.forTx(t);
request.ensureMinRequiredFee = true;
sr = null;
try {
sr = wallet.sendCoins(request);
} catch (final InsufficientMoneyException e) { } catch (final InsufficientMoneyException e) {
output.println(e.getLocalizedMessage()); output.println(e.getLocalizedMessage());
output.flush(); output.flush();

View file

@ -0,0 +1,94 @@
/**
* Copyright 2013 Bruno Oliveira, and individual contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.tcpid.opretj.testapp;
import static org.libsodium.jni.NaCl.sodium;
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
import org.libsodium.jni.crypto.Random;
import org.libsodium.jni.crypto.Util;
import org.libsodium.jni.encoders.Encoder;
import org.libsodium.jni.keys.KeyPair;
import org.libsodium.jni.keys.PrivateKey;
public class SigningKey {
private final byte[] seed;
private final byte[] secretKey;
private final VerifyKey verifyKey;
public SigningKey() {
this(new Random().randomBytes(SECRETKEY_BYTES));
}
public SigningKey(byte[] seed) {
Util.checkLength(seed, SECRETKEY_BYTES);
this.seed = seed;
this.secretKey = Util.zeros(SECRETKEY_BYTES * 2);
final byte[] publicKey = Util.zeros(PUBLICKEY_BYTES);
Util.isValid(sodium().crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
"Failed to generate a key pair");
this.verifyKey = new VerifyKey(publicKey);
}
public SigningKey(String seed, Encoder encoder) {
this(encoder.decode(seed));
}
public KeyPair getKeyPair() {
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
return new KeyPair(sk);
}
public PrivateKey getPrivateKey() {
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
return new PrivateKey(sk);
}
public VerifyKey getVerifyKey() {
return this.verifyKey;
}
public byte[] sign(byte[] message) {
byte[] signature = Util.prependZeros(SIGNATURE_BYTES, message);
final int[] bufferLen = new int[1];
sodium().crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
signature = Util.slice(signature, 0, SIGNATURE_BYTES);
return signature;
}
public String sign(String message, Encoder encoder) {
final byte[] signature = sign(encoder.decode(message));
return encoder.encode(signature);
}
public byte[] toBytes() {
return seed;
}
@Override
public String toString() {
return Encoder.HEX.encode(seed);
}
}

View file

@ -0,0 +1,68 @@
/**
* Copyright 2013 Bruno Oliveira, and individual contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.tcpid.opretj.testapp;
import static org.libsodium.jni.NaCl.sodium;
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
import org.libsodium.jni.crypto.Util;
import org.libsodium.jni.encoders.Encoder;
import org.libsodium.jni.keys.PublicKey;
public class VerifyKey {
private final byte[] key;
public VerifyKey(byte[] key) {
Util.checkLength(key, PUBLICKEY_BYTES);
this.key = key;
}
public VerifyKey(String key, Encoder encoder) {
this(encoder.decode(key));
}
public PublicKey getPublicKey() {
final byte[] pk = Util.zeros(PUBLICKEY_BYTES);
sodium().crypto_sign_ed25519_pk_to_curve25519(pk, this.key);
return new PublicKey(pk);
}
public byte[] toBytes() {
return key;
}
@Override
public String toString() {
return Encoder.HEX.encode(key);
}
public boolean verify(byte[] message, byte[] signature) {
Util.checkLength(signature, SIGNATURE_BYTES);
final byte[] sigAndMsg = Util.merge(signature, message);
final byte[] buffer = Util.zeros(sigAndMsg.length);
final int[] bufferLen = new int[1];
return Util.isValid(sodium().crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
"signature was forged or corrupted");
}
public boolean verify(String message, String signature, Encoder encoder) {
return verify(encoder.decode(message), encoder.decode(signature));
}
}

View file

@ -19,7 +19,7 @@ import com.google.common.primitives.Bytes;
public class OPRETECParser extends OPRETBaseHandler { public class OPRETECParser extends OPRETBaseHandler {
private static final Logger logger = LoggerFactory.getLogger(OPRETECParser.class); private static final Logger logger = LoggerFactory.getLogger(OPRETECParser.class);
private static final List<Byte> OPRET_MAGIC = Bytes.asList(Utils.HEX.decode("ec1d")); private static final List<Byte> OPRET_MAGIC = Bytes.asList(Utils.HEX.decode("ec0f"));
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = new HashMap<>(); protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = new HashMap<>();
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = new HashMap<>(); protected final Map<Sha256Hash, OPRETTransaction> transHashMap = new HashMap<>();
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
@ -31,59 +31,43 @@ public class OPRETECParser extends OPRETBaseHandler {
*/ */
public void addOPRETECRevokeEventListener(final OPRETECEventListener listener) { public void addOPRETECRevokeEventListener(final OPRETECEventListener listener) {
// This is thread safe, so we don't need to take the lock. // This is thread safe, so we don't need to take the lock.
opReturnChangeListeners opReturnChangeListeners.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
} }
private boolean checkData(final OPRETTransaction t1, final OPRETTransaction t2) { private boolean checkData(final OPRETTransaction t) {
final List<List<Byte>> opret_data = new ArrayList<>(t1.opretData); final List<List<Byte>> opret_data = new ArrayList<>(t.opretData);
opret_data.addAll(t2.opretData);
logger.debug("checking {}", opret_data); logger.debug("checking {}", opret_data);
if (opret_data.size() != 3) {
return false;
}
List<Byte> chunk; List<Byte> chunk;
chunk = opret_data.get(0); chunk = opret_data.get(0);
if (!chunk.equals(OPRET_MAGIC)) { if (!chunk.equals(OPRET_MAGIC)) {
logger.debug("0: != OPRET_MAGIC"); logger.debug("chunk 0: != OPRET_MAGIC");
return false; return false;
} }
chunk = opret_data.get(1); chunk = opret_data.get(1);
if (chunk.size() != 1) { if ((chunk.size() != 64)) {
logger.debug("1: size != 1"); logger.debug("chunk 1 size != 64, but {}", chunk.size());
return false; return false;
} }
if (chunk.get(0) == (byte) 0xFE) { chunk = opret_data.get(2);
if (opret_data.size() != 8) { if ((chunk.size() != 12)) {
logger.debug("FE: size != 8"); logger.debug("chunk 2 size!= 12 but {} ", chunk.size());
return false; return false;
}
chunk = opret_data.get(4);
if (!chunk.equals(OPRET_MAGIC)) {
logger.debug("FE 4 != OPRET_MAGIC");
return false;
}
if (!opret_data.get(2).equals(opret_data.get(6))) {
logger.debug("FE 2 != 6");
return false;
}
chunk = opret_data.get(5);
if ((chunk.size() != 1) || (chunk.get(0) != (byte) 0xFF)) {
logger.debug("FE 5 size!=1 or != FF");
return false;
}
return handleRevoke(t1, t2);
} else {
logger.debug("1: != 0xFE");
} }
return false; return handleRevoke(t);
} }
private boolean handleRevoke(final OPRETTransaction t1, final OPRETTransaction t2) { private boolean handleRevoke(final OPRETTransaction t) {
final byte[] pkhash = Bytes.toArray(t1.opretData.get(2)); final byte[] pkhash = Bytes.toArray(t.opretData.get(2));
final byte[] sig = Bytes.concat(Bytes.toArray(t1.opretData.get(3)), Bytes.toArray(t2.opretData.get(3))); final byte[] sig = Bytes.toArray(t.opretData.get(1));
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig)); logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
queueOnOPRETRevoke(pkhash, sig); queueOnOPRETRevoke(pkhash, sig);
@ -93,18 +77,7 @@ public class OPRETECParser extends OPRETBaseHandler {
@Override @Override
public void pushData(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash, public void pushData(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash,
final List<List<Byte>> opret_data) { final List<List<Byte>> opret_data) {
final OPRETTransaction optrans = new OPRETTransaction(blockHash, txHash, txPrevHash, opret_data); checkData(new OPRETTransaction(blockHash, txHash, opret_data));
logger.debug("pushData: {}", optrans);
for (final Sha256Hash t : txPrevHash) {
if (transHashMap.containsKey(t)) {
final OPRETTransaction opprev = transHashMap.get(t);
if (checkData(opprev, optrans)) {
transHashMap.remove(t);
return;
}
}
}
transHashMap.put(txHash, optrans);
} }
@Override @Override

View file

@ -2,7 +2,6 @@ package org.tcpid.opretj;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Set;
import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
@ -16,14 +15,11 @@ public class OPRETTransaction implements Serializable {
private static final long serialVersionUID = 4234625243756902517L; private static final long serialVersionUID = 4234625243756902517L;
public final Sha256Hash blockHash; public final Sha256Hash blockHash;
public final Sha256Hash txHash; public final Sha256Hash txHash;
public final Set<Sha256Hash> txPrevHash;
public final List<List<Byte>> opretData; public final List<List<Byte>> opretData;
public OPRETTransaction(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash, public OPRETTransaction(final Sha256Hash blockHash, final Sha256Hash txHash, final List<List<Byte>> opret_data) {
final List<List<Byte>> opret_data) {
this.blockHash = blockHash; this.blockHash = blockHash;
this.txHash = txHash; this.txHash = txHash;
this.txPrevHash = txPrevHash;
this.opretData = opret_data; this.opretData = opret_data;
} }
@ -32,13 +28,6 @@ public class OPRETTransaction implements Serializable {
final StringBuilder buf = new StringBuilder(); final StringBuilder buf = new StringBuilder();
buf.append("Received in Block: ").append(blockHash).append("\n"); buf.append("Received in Block: ").append(blockHash).append("\n");
buf.append("TX: ").append(txHash).append("\n"); buf.append("TX: ").append(txHash).append("\n");
buf.append("TxPrev: ");
for (final Sha256Hash d : txPrevHash) {
buf.append(d.toString());
buf.append(" ");
}
buf.append("\n");
buf.append("Data: "); buf.append("Data: ");
for (final List<Byte> d : opretData) { for (final List<Byte> d : opretData) {
buf.append(Utils.HEX.encode(Bytes.toArray(d))); buf.append(Utils.HEX.encode(Bytes.toArray(d)));