use HMACSHA512256
This commit is contained in:
parent
35dd21f3c1
commit
e141ff0049
|
@ -48,7 +48,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.tcpid</groupId>
|
<groupId>org.tcpid</groupId>
|
||||||
<artifactId>opretj</artifactId>
|
<artifactId>opretj</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.jopt-simple</groupId>
|
<groupId>net.sf.jopt-simple</groupId>
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class App {
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void displayHelp(PrintWriter out) {
|
private static void displayHelp(final PrintWriter out) {
|
||||||
out.write("Available Commands:\n");
|
out.write("Available Commands:\n");
|
||||||
out.write("\n");
|
out.write("\n");
|
||||||
out.write("help - this screen\n");
|
out.write("help - this screen\n");
|
||||||
|
@ -157,15 +157,6 @@ public class App {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(final String[] args) throws Exception {
|
public static void main(final String[] args) throws Exception {
|
||||||
MasterSigningKey sk = SK.getSubKey(1L);
|
|
||||||
|
|
||||||
System.err.println(SK.toString());
|
|
||||||
System.err.println(sk.toString());
|
|
||||||
sk = SK.getSubKey(2L);
|
|
||||||
System.err.println(sk.toString());
|
|
||||||
sk = SK.getSubKey(3L);
|
|
||||||
System.err.println(sk.toString());
|
|
||||||
System.exit(0);
|
|
||||||
final OptionParser parser = new OptionParser();
|
final OptionParser parser = new OptionParser();
|
||||||
final OptionSpec<NetworkEnum> net = parser.accepts("net", "The network to run the examples on")
|
final OptionSpec<NetworkEnum> net = parser.accepts("net", "The network to run the examples on")
|
||||||
.withRequiredArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.TEST);
|
.withRequiredArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.TEST);
|
||||||
|
@ -284,7 +275,7 @@ public class App {
|
||||||
kit.awaitTerminated();
|
kit.awaitTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean sendOPReturn(final OPRETWalletAppKit kit, PrintWriter output) {
|
private static boolean sendOPReturn(final OPRETWalletAppKit kit, final PrintWriter output) {
|
||||||
final OPRETWallet wallet = kit.opretwallet();
|
final OPRETWallet wallet = kit.opretwallet();
|
||||||
final NetworkParameters params = wallet.getNetworkParameters();
|
final NetworkParameters params = wallet.getNetworkParameters();
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,19 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.tcpid</groupId>
|
<groupId>org.tcpid</groupId>
|
||||||
<artifactId>opret-parent</artifactId>
|
<artifactId>opret-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>opretj</artifactId>
|
<artifactId>opretj</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>
|
||||||
|
|
||||||
|
|
18
opretj/src/main/java/org/tcpid/key/HMACSHA512256.java
Normal file
18
opretj/src/main/java/org/tcpid/key/HMACSHA512256.java
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package org.tcpid.key;
|
||||||
|
|
||||||
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
|
|
||||||
|
import org.libsodium.jni.Sodium;
|
||||||
|
import org.libsodium.jni.crypto.Util;
|
||||||
|
|
||||||
|
public class HMACSHA512256 {
|
||||||
|
public static final int HMACSHA512256_BYTES = sodium().crypto_auth_hmacsha512256_bytes();
|
||||||
|
public static final int HMACSHA512256_KEYBYTES = sodium().crypto_auth_hmacsha512256_keybytes();
|
||||||
|
|
||||||
|
public static byte[] of(final byte[] key, final byte[] msg) {
|
||||||
|
final byte[] hash = Util.zeros(HMACSHA512256_BYTES);
|
||||||
|
final byte[] hashkey = Util.prependZeros(HMACSHA512256_KEYBYTES, key);
|
||||||
|
Sodium.crypto_auth_hmacsha512256(hash, msg, msg.length, hashkey);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +1,22 @@
|
||||||
package org.tcpid.key;
|
package org.tcpid.key;
|
||||||
|
|
||||||
import static org.libsodium.jni.NaCl.sodium;
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.libsodium.jni.Sodium;
|
|
||||||
import org.libsodium.jni.crypto.Hash;
|
import org.libsodium.jni.crypto.Hash;
|
||||||
import org.libsodium.jni.crypto.Util;
|
|
||||||
import org.libsodium.jni.encoders.Encoder;
|
import org.libsodium.jni.encoders.Encoder;
|
||||||
|
|
||||||
import com.google.common.primitives.Longs;
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
public class MasterSigningKey extends SigningKey {
|
public class MasterSigningKey extends SigningKey {
|
||||||
|
private static final int HMACSHA512256_BYTES = sodium().crypto_auth_hmacsha512256_bytes();
|
||||||
|
private static final int HMACSHA512256_KEYBYTES = sodium().crypto_auth_hmacsha512256_keybytes();
|
||||||
|
public static final Hash HASH = new Hash();
|
||||||
private MasterSigningKey subkey;
|
private MasterSigningKey subkey;
|
||||||
private Long subkeyindex;
|
private Long subkeyindex;
|
||||||
public static final Hash HASH = new Hash();
|
|
||||||
private final ArrayList<Long> keyindex;
|
private final ArrayList<Long> keyindex;
|
||||||
|
|
||||||
public MasterSigningKey(final byte[] key, ArrayList<Long> keyindex) {
|
|
||||||
super(key);
|
|
||||||
subkey = null;
|
|
||||||
subkeyindex = 0L;
|
|
||||||
this.keyindex = new ArrayList<>(keyindex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MasterSigningKey(final byte[] key) {
|
public MasterSigningKey(final byte[] key) {
|
||||||
super(key);
|
super(key);
|
||||||
subkey = null;
|
subkey = null;
|
||||||
|
@ -32,30 +24,30 @@ public class MasterSigningKey extends SigningKey {
|
||||||
this.keyindex = new ArrayList<>();
|
this.keyindex = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MasterSigningKey getValidSubKey() {
|
public MasterSigningKey(final byte[] key, final ArrayList<Long> keyindex) {
|
||||||
if (subkey == null) {
|
super(key);
|
||||||
subkey = getSubKey(subkeyindex);
|
subkey = null;
|
||||||
}
|
subkeyindex = 0L;
|
||||||
return subkey;
|
this.keyindex = new ArrayList<>(keyindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MasterSigningKey getSubKey(Long offset) {
|
public MasterSigningKey getNextValidSubKey(final Long offset) {
|
||||||
System.err.println("getSubKey");
|
return getSubKey(subkeyindex + offset);
|
||||||
final byte[] la = Longs.toByteArray(offset);
|
}
|
||||||
final byte[] keynum = Util.prependZeros(16 - la.length, la);
|
|
||||||
System.err.println("getSubKey key=" + Encoder.HEX.encode(keynum));
|
public MasterSigningKey getSubKey(final Long offset) {
|
||||||
final byte[] appid = Util.prependZeros(14, "EC".getBytes());
|
final byte[] ob = Longs.toByteArray(offset);
|
||||||
final byte[] newseed = Util.zeros(SECRETKEY_BYTES);
|
final byte[] newseed = HMACSHA512256.of(seed, ob);
|
||||||
sodium();
|
|
||||||
Sodium.crypto_generichash_blake2b_salt_personal(newseed, newseed.length, null, 0, this.seed, this.seed.length,
|
|
||||||
keynum, appid);
|
|
||||||
final ArrayList<Long> ki = new ArrayList<>(this.keyindex);
|
final ArrayList<Long> ki = new ArrayList<>(this.keyindex);
|
||||||
ki.add(offset);
|
ki.add(offset);
|
||||||
return new MasterSigningKey(newseed, ki);
|
return new MasterSigningKey(newseed, ki);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MasterSigningKey getNextValidSubKey(Long offset) {
|
public MasterSigningKey getValidSubKey() {
|
||||||
return getSubKey(subkeyindex + offset);
|
if (subkey == null) {
|
||||||
|
subkey = getSubKey(subkeyindex);
|
||||||
|
}
|
||||||
|
return subkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void revokeSubKey() {
|
public void revokeSubKey() {
|
||||||
|
|
|
@ -10,6 +10,10 @@ public class MasterVerifyKey extends VerifyKey {
|
||||||
super(key);
|
super(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearSubKeys() {
|
||||||
|
subkeys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
public VerifyKey getValidSubKey() {
|
public VerifyKey getValidSubKey() {
|
||||||
return subkeys.getFirst();
|
return subkeys.getFirst();
|
||||||
}
|
}
|
||||||
|
@ -41,8 +45,4 @@ public class MasterVerifyKey extends VerifyKey {
|
||||||
|
|
||||||
subkeys.addLast(key);
|
subkeys.addLast(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearSubKeys() {
|
|
||||||
subkeys.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,6 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
private boolean revoked;
|
private boolean revoked;
|
||||||
private MasterVerifyKey masterkey;
|
private MasterVerifyKey masterkey;
|
||||||
|
|
||||||
public MasterVerifyKey getMasterkey() {
|
|
||||||
return masterkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMasterkey(MasterVerifyKey masterkey) {
|
|
||||||
this.masterkey = masterkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VerifyKey(final byte[] key) {
|
public VerifyKey(final byte[] key) {
|
||||||
Util.checkLength(key, PUBLICKEY_BYTES);
|
Util.checkLength(key, PUBLICKEY_BYTES);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
@ -83,6 +75,10 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
return Arrays.equals(key, ((VerifyKey) o).key);
|
return Arrays.equals(key, ((VerifyKey) o).key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MasterVerifyKey getMasterkey() {
|
||||||
|
return masterkey;
|
||||||
|
}
|
||||||
|
|
||||||
public PublicKey getPublicKey() {
|
public PublicKey getPublicKey() {
|
||||||
final byte[] pk = Util.zeros(PUBLICKEY_BYTES);
|
final byte[] pk = Util.zeros(PUBLICKEY_BYTES);
|
||||||
sodium();
|
sodium();
|
||||||
|
@ -98,6 +94,10 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
return revoked;
|
return revoked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMasterkey(final MasterVerifyKey masterkey) {
|
||||||
|
this.masterkey = masterkey;
|
||||||
|
}
|
||||||
|
|
||||||
public void setRevoked(final boolean revoked) {
|
public void setRevoked(final boolean revoked) {
|
||||||
if (this.revoked != true) {
|
if (this.revoked != true) {
|
||||||
this.revoked = revoked;
|
this.revoked = revoked;
|
||||||
|
|
|
@ -20,10 +20,13 @@ import org.bitcoinj.script.ScriptBuilder;
|
||||||
import org.bitcoinj.utils.ListenerRegistration;
|
import org.bitcoinj.utils.ListenerRegistration;
|
||||||
import org.bitcoinj.utils.Threading;
|
import org.bitcoinj.utils.Threading;
|
||||||
import org.libsodium.jni.crypto.Box;
|
import org.libsodium.jni.crypto.Box;
|
||||||
|
import org.libsodium.jni.crypto.Hash;
|
||||||
import org.libsodium.jni.keys.KeyPair;
|
import org.libsodium.jni.keys.KeyPair;
|
||||||
import org.libsodium.jni.keys.PublicKey;
|
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.key.HMACSHA512256;
|
||||||
|
import org.tcpid.key.MasterSigningKey;
|
||||||
import org.tcpid.key.SigningKey;
|
import org.tcpid.key.SigningKey;
|
||||||
import org.tcpid.key.VerifyKey;
|
import org.tcpid.key.VerifyKey;
|
||||||
|
|
||||||
|
@ -31,83 +34,11 @@ 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);
|
||||||
|
public static final Hash HASH = new Hash();
|
||||||
|
|
||||||
private static final List<Byte> OPRET_MAGIC = Bytes.asList(Utils.HEX.decode("ec0f"));
|
private static final List<Byte> OPRET_MAGIC = Bytes.asList(Utils.HEX.decode("ec0f"));
|
||||||
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
protected final Map<List<Byte>, List<VerifyKey>> verifyKeys = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
|
|
||||||
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
public static boolean checkKeyforRevoke(final VerifyKey k, final byte[] sig) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an event listener object. Methods on this object are called when
|
|
||||||
* scripts watched by this wallet change. The listener is executed by the
|
|
||||||
* given executor.
|
|
||||||
*/
|
|
||||||
public void addOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
|
||||||
// This is thread safe, so we don't need to take the lock.
|
|
||||||
opReturnChangeListeners.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
|
|
||||||
|
|
||||||
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(
|
|
||||||
Sha256Hash.of(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())).getBytes(), 0, SECRETKEY_BYTES));
|
|
||||||
|
|
||||||
final Box boxVK = new Box(VK, Epair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceVK = Arrays.copyOfRange(
|
|
||||||
Sha256Hash.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VK.toBytes())).getBytes(), 0,
|
|
||||||
NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] cipherMK = boxVK.decrypt(nonceVK, cipher);
|
|
||||||
|
|
||||||
final Box boxMK = new Box(MK, Epair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceMK = Arrays.copyOfRange(
|
|
||||||
Sha256Hash.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())).getBytes(), 0,
|
|
||||||
NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
|
|
||||||
|
|
||||||
return clear;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
|
|
||||||
final byte[] nonce) {
|
|
||||||
|
|
||||||
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(Sha256Hash
|
|
||||||
.of(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())).getBytes(),
|
|
||||||
0, SECRETKEY_BYTES));
|
|
||||||
|
|
||||||
final Box boxMK = new Box(Epair.getPublicKey(), MKpair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceMK = Arrays.copyOfRange(Sha256Hash
|
|
||||||
.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())).getBytes(),
|
|
||||||
0, NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
|
|
||||||
|
|
||||||
final Box boxVK = new Box(Epair.getPublicKey(), VKpair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceVK = Arrays.copyOfRange(Sha256Hash
|
|
||||||
.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())).getBytes(),
|
|
||||||
0, NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
|
|
||||||
|
|
||||||
return cipherVK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Script getRevokeScript(SigningKey key) {
|
|
||||||
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), key.getVerifyKey().toHash());
|
|
||||||
final byte[] sig = key.sign(revokemsg);
|
|
||||||
|
|
||||||
final Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec0f")).data(sig)
|
|
||||||
.data(key.getVerifyKey().getShortHash()).build();
|
|
||||||
return script;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean checkKeyforRevoke(VerifyKey k, final byte[] sig) {
|
|
||||||
logger.debug("CHECKING REVOKE PKHASH {} - SIG {}", Utils.HEX.encode(k.toHash()), Utils.HEX.encode(sig));
|
logger.debug("CHECKING REVOKE PKHASH {} - SIG {}", Utils.HEX.encode(k.toHash()), Utils.HEX.encode(sig));
|
||||||
|
|
||||||
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), k.toHash());
|
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), k.toHash());
|
||||||
|
@ -123,23 +54,88 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean cryptoSelfTest() {
|
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
|
||||||
final SigningKey sk = new SigningKey();
|
|
||||||
final VerifyKey vk = sk.getVerifyKey();
|
|
||||||
|
|
||||||
final SigningKey msk = new SigningKey();
|
final KeyPair Epair = new KeyPair(HMACSHA512256.of(MK.toBytes(), Bytes.concat(nonce, VK.toBytes())));
|
||||||
|
|
||||||
final KeyPair mkpair = msk.getKeyPair();
|
final Box boxVK = new Box(VK, Epair.getPrivateKey());
|
||||||
final KeyPair vkpair = sk.getKeyPair();
|
|
||||||
final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8);
|
final byte[] nonceVK = Arrays.copyOfRange(
|
||||||
final byte[] cipher = doubleEnc(mkpair, vkpair, "TEST".getBytes(), nonce);
|
HMACSHA512256.of(VK.toBytes(), Bytes.concat(nonce, Epair.getPrivateKey().toBytes())), 0, NONCE_BYTES);
|
||||||
// System.err.println("Cipher len: " + cipher.length);
|
|
||||||
try {
|
final byte[] cipherMK = boxVK.decrypt(nonceVK, cipher);
|
||||||
final byte[] chk = doubleDec(msk.getVerifyKey().getPublicKey(), vk.getPublicKey(), cipher, nonce);
|
|
||||||
return Arrays.equals(chk, "TEST".getBytes());
|
final Box boxMK = new Box(MK, Epair.getPrivateKey());
|
||||||
} catch (final Exception e) {
|
|
||||||
return false;
|
final byte[] nonceMK = Arrays.copyOfRange(
|
||||||
|
HMACSHA512256.of(MK.toBytes(), Bytes.concat(nonce, Epair.getPrivateKey().toBytes())), 0, NONCE_BYTES);
|
||||||
|
|
||||||
|
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
|
||||||
|
|
||||||
|
return clear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
|
||||||
|
final byte[] nonce) {
|
||||||
|
|
||||||
|
final KeyPair Epair = new KeyPair(HMACSHA512256.of(MKpair.getPublicKey().toBytes(),
|
||||||
|
Bytes.concat(nonce, VKpair.getPublicKey().toBytes())));
|
||||||
|
|
||||||
|
final Box boxMK = new Box(Epair.getPublicKey(), MKpair.getPrivateKey());
|
||||||
|
|
||||||
|
final byte[] nonceMK = Arrays.copyOfRange(
|
||||||
|
HMACSHA512256.of(MKpair.getPublicKey().toBytes(), Bytes.concat(nonce, Epair.getPrivateKey().toBytes())),
|
||||||
|
0, NONCE_BYTES);
|
||||||
|
|
||||||
|
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
|
||||||
|
|
||||||
|
final Box boxVK = new Box(Epair.getPublicKey(), VKpair.getPrivateKey());
|
||||||
|
|
||||||
|
final byte[] nonceVK = Arrays.copyOfRange(
|
||||||
|
HMACSHA512256.of(VKpair.getPublicKey().toBytes(), Bytes.concat(nonce, Epair.getPrivateKey().toBytes())),
|
||||||
|
0, NONCE_BYTES);
|
||||||
|
|
||||||
|
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
|
||||||
|
|
||||||
|
return cipherVK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Script getRevokeScript(final SigningKey key) {
|
||||||
|
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), key.getVerifyKey().toHash());
|
||||||
|
final byte[] sig = key.sign(revokemsg);
|
||||||
|
|
||||||
|
final Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec0f")).data(sig)
|
||||||
|
.data(key.getVerifyKey().getShortHash()).build();
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
protected final Map<List<Byte>, List<VerifyKey>> verifyKeys = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an event listener object. Methods on this object are called when
|
||||||
|
* scripts watched by this wallet change. The listener is executed by the
|
||||||
|
* given executor.
|
||||||
|
*/
|
||||||
|
public void addOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
||||||
|
// This is thread safe, so we don't need to take the lock.
|
||||||
|
opReturnChangeListeners.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addVerifyKey(final VerifyKey key, final long earliestTime) {
|
||||||
|
final List<Byte> hash = Bytes.asList(key.getShortHash());
|
||||||
|
if (!verifyKeys.containsKey(hash)) {
|
||||||
|
verifyKeys.put(hash, new ArrayList<VerifyKey>());
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyKeys.get(hash).add(key);
|
||||||
|
logger.debug("Adding pkhash {}", key.getShortHash());
|
||||||
|
addOPRET(key.getShortHash(), earliestTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkData(final OPRETTransaction t) {
|
private boolean checkData(final OPRETTransaction t) {
|
||||||
|
@ -172,6 +168,48 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
return handleRevoke(t);
|
return handleRevoke(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean cryptoSelfTest() {
|
||||||
|
if (NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES) {
|
||||||
|
logger.error("NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES: {} > {}", NONCE_BYTES,
|
||||||
|
HMACSHA512256.HMACSHA512256_BYTES);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: test assert(HMACSHA512_KEYBYTES == SECRETKEY_BYTES)
|
||||||
|
if (SECRETKEY_BYTES != HMACSHA512256.HMACSHA512256_BYTES) {
|
||||||
|
logger.error("SECRETKEY_BYTES != HMACSHA512256.HMACSHA512256_BYTES: {} > {}", SECRETKEY_BYTES,
|
||||||
|
HMACSHA512256.HMACSHA512256_BYTES);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
|
if (!Arrays.equals(Utils.HEX.decode("4071b2b3db7cc7aecd0b23608e96f44f08463ea0ee0a0c12f5fa21ff449deb55"),
|
||||||
|
msk.toBytes())) {
|
||||||
|
logger.error("MasterSigningKey(HASH.sha256('TESTSEED'.getBytes())) test failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Arrays.equals(Utils.HEX.decode("00cb0c8748318d27eab65159a2261c028d764c1154fc302b9b046aa2bbefab27"),
|
||||||
|
msk.getSubKey(1L).getSubKey(2L).getSubKey(3L).getSubKey(4L).toBytes())) {
|
||||||
|
logger.error("MasterSigningKey subkey derivation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SigningKey subkey = new SigningKey();
|
||||||
|
final VerifyKey vk = subkey.getVerifyKey();
|
||||||
|
|
||||||
|
final KeyPair mkpair = msk.getKeyPair();
|
||||||
|
final KeyPair vkpair = subkey.getKeyPair();
|
||||||
|
final byte[] nonce = Arrays.copyOfRange(HASH.sha256("TEST".getBytes()), 0, 8);
|
||||||
|
final byte[] cipher = doubleEnc(mkpair, vkpair, "TEST".getBytes(), nonce);
|
||||||
|
try {
|
||||||
|
final byte[] chk = doubleDec(msk.getVerifyKey().getPublicKey(), vk.getPublicKey(), cipher, nonce);
|
||||||
|
return Arrays.equals(chk, "TEST".getBytes());
|
||||||
|
} catch (final Exception e) {
|
||||||
|
logger.error("doubleEnc -> doubleDec failed!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean handleRevoke(final OPRETTransaction t) {
|
private boolean handleRevoke(final OPRETTransaction t) {
|
||||||
final List<Byte> pkhash = t.opretData.get(2);
|
final List<Byte> pkhash = t.opretData.get(2);
|
||||||
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
||||||
|
@ -201,15 +239,18 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
checkData(t);
|
checkData(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVerifyKey(final VerifyKey key, final long earliestTime) {
|
protected void queueOnOPRETRevoke(final VerifyKey key) {
|
||||||
final List<Byte> hash = Bytes.asList(key.getShortHash());
|
for (final ListenerRegistration<OPRETECEventListener> registration : opReturnChangeListeners) {
|
||||||
if (!verifyKeys.containsKey(hash)) {
|
registration.executor.execute(() -> registration.listener.onOPRETRevoke(key));
|
||||||
verifyKeys.put(hash, new ArrayList<VerifyKey>());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyKeys.get(hash).add(key);
|
/**
|
||||||
logger.debug("Adding pkhash {}", key.getShortHash());
|
* Removes the given event listener object. Returns true if the listener was
|
||||||
addOPRET(key.getShortHash(), earliestTime);
|
* removed, false if that listener was never added.
|
||||||
|
*/
|
||||||
|
public boolean removeOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
||||||
|
return ListenerRegistration.removeFromList(listener, opReturnChangeListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeVerifyKey(final VerifyKey key) {
|
public void removeVerifyKey(final VerifyKey key) {
|
||||||
|
@ -223,18 +264,4 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
|
|
||||||
removeOPRET(key.getShortHash());
|
removeOPRET(key.getShortHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void queueOnOPRETRevoke(VerifyKey key) {
|
|
||||||
for (final ListenerRegistration<OPRETECEventListener> registration : opReturnChangeListeners) {
|
|
||||||
registration.executor.execute(() -> registration.listener.onOPRETRevoke(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given event listener object. Returns true if the listener was
|
|
||||||
* removed, false if that listener was never added.
|
|
||||||
*/
|
|
||||||
public boolean removeOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
|
||||||
return ListenerRegistration.removeFromList(listener, opReturnChangeListeners);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ public interface OPRETHandlerInterface {
|
||||||
|
|
||||||
// void removeOPRET(byte[] magic);
|
// void removeOPRET(byte[] magic);
|
||||||
|
|
||||||
boolean removeOPRETChangeEventListener(OPRETChangeEventListener listener);
|
|
||||||
|
|
||||||
void pushTransaction(OPRETTransaction t);
|
void pushTransaction(OPRETTransaction t);
|
||||||
|
|
||||||
|
boolean removeOPRETChangeEventListener(OPRETChangeEventListener listener);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class OPRETTransaction implements Serializable {
|
||||||
return partialMerkleTree;
|
return partialMerkleTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPartialMerkleTree(PartialMerkleTree partialMerkleTree) {
|
public void setPartialMerkleTree(final PartialMerkleTree partialMerkleTree) {
|
||||||
this.partialMerkleTree = partialMerkleTree;
|
this.partialMerkleTree = partialMerkleTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -2,7 +2,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.tcpid</groupId>
|
<groupId>org.tcpid</groupId>
|
||||||
<artifactId>opret-parent</artifactId>
|
<artifactId>opret-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<modules>
|
<modules>
|
||||||
<module>opretj</module>
|
<module>opretj</module>
|
||||||
|
|
Loading…
Reference in a new issue