move special EC parser and key handling to testapp
This commit is contained in:
parent
6b50ca4921
commit
765b452f7d
16 changed files with 149 additions and 203 deletions
opret-testapp
|
@ -1,4 +1,5 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/main/resources=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding/<project>=UTF-8
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -106,6 +106,15 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Unit tests plugin, to skip runing test add -Dmaven.test.skip -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<runOrder>alphabetical</runOrder>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
|
|
18
opret-testapp/src/main/java/org/tcpid/key/HMACSHA512256.java
Normal file
18
opret-testapp/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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package org.tcpid.key;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.libsodium.jni.crypto.Hash;
|
||||
import org.libsodium.jni.encoders.Encoder;
|
||||
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
public class MasterSigningKey extends SigningKey {
|
||||
public static final Hash HASH = new Hash();
|
||||
private MasterSigningKey subkey;
|
||||
private Long subkeyindex;
|
||||
private final ArrayList<Long> keyindex;
|
||||
|
||||
public MasterSigningKey(final byte[] key) {
|
||||
super(key);
|
||||
subkey = null;
|
||||
subkeyindex = 0L;
|
||||
this.keyindex = new ArrayList<>();
|
||||
}
|
||||
|
||||
public MasterSigningKey(final byte[] key, final ArrayList<Long> keyindex) {
|
||||
super(key);
|
||||
subkey = null;
|
||||
subkeyindex = 0L;
|
||||
this.keyindex = new ArrayList<>(keyindex);
|
||||
}
|
||||
|
||||
public MasterVerifyKey getMasterVerifyKey() {
|
||||
return new MasterVerifyKey(this.getVerifyKey().toBytes());
|
||||
}
|
||||
|
||||
public MasterSigningKey getNextValidSubKey(final Long offset) {
|
||||
return getSubKey(subkeyindex + offset);
|
||||
}
|
||||
|
||||
public MasterSigningKey getSubKey(final Long offset) {
|
||||
final byte[] ob = Longs.toByteArray(offset);
|
||||
final byte[] newseed = HMACSHA512256.of(seed, ob);
|
||||
final ArrayList<Long> ki = new ArrayList<>(this.keyindex);
|
||||
ki.add(offset);
|
||||
return new MasterSigningKey(newseed, ki);
|
||||
}
|
||||
|
||||
public MasterSigningKey getValidSubKey() {
|
||||
if (subkey == null) {
|
||||
subkey = getSubKey(subkeyindex);
|
||||
}
|
||||
return subkey;
|
||||
}
|
||||
|
||||
public void revokeSubKey() {
|
||||
subkey.setRevoked(true);
|
||||
subkeyindex++;
|
||||
subkey = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Index: " + this.keyindex.toString() + " " + Encoder.HEX.encode(seed);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package org.tcpid.key;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.tcpid.opretj.OPRETTransaction;
|
||||
|
||||
public class MasterVerifyKey extends VerifyKey {
|
||||
private final LinkedList<MasterVerifyKey> subkeys = new LinkedList<>();
|
||||
|
||||
public MasterVerifyKey(final byte[] key) {
|
||||
super(key);
|
||||
}
|
||||
|
||||
public void clearSubKeys() {
|
||||
subkeys.clear();
|
||||
}
|
||||
|
||||
public MasterVerifyKey getValidSubKey() {
|
||||
return subkeys.getFirst();
|
||||
}
|
||||
|
||||
public void revokeSubKey(final MasterVerifyKey key) {
|
||||
final int i = subkeys.indexOf(key);
|
||||
|
||||
if (i == -1) {
|
||||
throw new NoSuchElementException("No such subkey");
|
||||
}
|
||||
|
||||
subkeys.get(i).setRevoked(true);
|
||||
subkeys.remove(i);
|
||||
}
|
||||
|
||||
public void setFirstValidSubKey(final MasterVerifyKey key, final OPRETTransaction t1, final OPRETTransaction t2) {
|
||||
if (!subkeys.isEmpty()) {
|
||||
throw new IndexOutOfBoundsException("Subkey list is not empty");
|
||||
}
|
||||
|
||||
subkeys.addLast(key);
|
||||
}
|
||||
|
||||
public void setNextValidSubKey(final MasterVerifyKey after, final MasterVerifyKey key) {
|
||||
final VerifyKey l = subkeys.getLast();
|
||||
if (!l.equals(after)) {
|
||||
throw new NoSuchElementException("No such after key, or not last in list");
|
||||
}
|
||||
|
||||
subkeys.addLast(key);
|
||||
}
|
||||
}
|
125
opret-testapp/src/main/java/org/tcpid/key/SigningKey.java
Normal file
125
opret-testapp/src/main/java/org/tcpid/key/SigningKey.java
Normal file
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* 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.key;
|
||||
|
||||
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.Sodium;
|
||||
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 implements Comparable<SigningKey> {
|
||||
|
||||
protected final byte[] seed;
|
||||
|
||||
private final byte[] secretKey;
|
||||
private final VerifyKey verifyKey;
|
||||
private boolean revoked;
|
||||
|
||||
public SigningKey() {
|
||||
this(new Random().randomBytes(SECRETKEY_BYTES));
|
||||
}
|
||||
|
||||
public SigningKey(final byte[] seed) {
|
||||
Util.checkLength(seed, SECRETKEY_BYTES);
|
||||
this.seed = seed.clone();
|
||||
this.secretKey = Util.zeros(SECRETKEY_BYTES * 2);
|
||||
final byte[] publicKey = Util.zeros(PUBLICKEY_BYTES);
|
||||
sodium();
|
||||
Util.isValid(Sodium.crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
|
||||
"Failed to generate a key pair");
|
||||
|
||||
this.verifyKey = new VerifyKey(publicKey);
|
||||
}
|
||||
|
||||
public SigningKey(final String seed, final Encoder encoder) {
|
||||
this(encoder.decode(seed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final SigningKey other) {
|
||||
for (int i = SECRETKEY_BYTES - 1; i >= 0; i--) {
|
||||
final int thisByte = this.seed[i] & 0xff;
|
||||
final int otherByte = other.seed[i] & 0xff;
|
||||
if (thisByte > otherByte) {
|
||||
return 1;
|
||||
}
|
||||
if (thisByte < otherByte) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair() {
|
||||
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
||||
sodium();
|
||||
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();
|
||||
Sodium.crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
||||
return new PrivateKey(sk);
|
||||
}
|
||||
|
||||
public VerifyKey getVerifyKey() {
|
||||
return this.verifyKey;
|
||||
}
|
||||
|
||||
public boolean isRevoked() {
|
||||
return revoked;
|
||||
}
|
||||
|
||||
public void setRevoked(final boolean revoked) {
|
||||
if (this.revoked != true) {
|
||||
this.revoked = revoked;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] sign(final byte[] message) {
|
||||
byte[] signature = Util.prependZeros(SIGNATURE_BYTES, message);
|
||||
final int[] bufferLen = new int[1];
|
||||
sodium();
|
||||
Sodium.crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
|
||||
signature = Util.slice(signature, 0, SIGNATURE_BYTES);
|
||||
return signature;
|
||||
}
|
||||
|
||||
public String sign(final String message, final 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);
|
||||
}
|
||||
}
|
135
opret-testapp/src/main/java/org/tcpid/key/VerifyKey.java
Normal file
135
opret-testapp/src/main/java/org/tcpid/key/VerifyKey.java
Normal file
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* 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.key;
|
||||
|
||||
import static org.libsodium.jni.NaCl.sodium;
|
||||
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
||||
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.libsodium.jni.Sodium;
|
||||
import org.libsodium.jni.crypto.Hash;
|
||||
import org.libsodium.jni.crypto.Util;
|
||||
import org.libsodium.jni.encoders.Encoder;
|
||||
import org.libsodium.jni.keys.PublicKey;
|
||||
|
||||
public class VerifyKey implements Comparable<VerifyKey> {
|
||||
public static final Hash HASH = new Hash();
|
||||
|
||||
private final byte[] key;
|
||||
private final byte[] hash;
|
||||
private final byte[] shortHash;
|
||||
private boolean revoked;
|
||||
private MasterVerifyKey masterkey;
|
||||
|
||||
public VerifyKey(final byte[] key) {
|
||||
Util.checkLength(key, PUBLICKEY_BYTES);
|
||||
this.key = key;
|
||||
this.hash = HASH.sha256(key);
|
||||
this.shortHash = Arrays.copyOfRange(hash, 0, 12);
|
||||
this.revoked = false;
|
||||
}
|
||||
|
||||
public VerifyKey(final String key, final Encoder encoder) {
|
||||
this(encoder.decode(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final VerifyKey other) {
|
||||
for (int i = PUBLICKEY_BYTES - 1; i >= 0; i--) {
|
||||
final int thisByte = this.key[i] & 0xff;
|
||||
final int otherByte = other.key[i] & 0xff;
|
||||
if (thisByte > otherByte) {
|
||||
return 1;
|
||||
}
|
||||
if (thisByte < otherByte) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if ((o == null) || (getClass() != o.getClass())) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(key, ((VerifyKey) o).key);
|
||||
}
|
||||
|
||||
public MasterVerifyKey getMasterkey() {
|
||||
return masterkey;
|
||||
}
|
||||
|
||||
public PublicKey getPublicKey() {
|
||||
final byte[] pk = Util.zeros(PUBLICKEY_BYTES);
|
||||
sodium();
|
||||
Sodium.crypto_sign_ed25519_pk_to_curve25519(pk, this.key);
|
||||
return new PublicKey(pk);
|
||||
}
|
||||
|
||||
public byte[] getShortHash() {
|
||||
return this.shortHash;
|
||||
}
|
||||
|
||||
public boolean isRevoked() {
|
||||
return revoked;
|
||||
}
|
||||
|
||||
public void setMasterkey(final MasterVerifyKey masterkey) {
|
||||
this.masterkey = masterkey;
|
||||
}
|
||||
|
||||
public void setRevoked(final boolean revoked) {
|
||||
if (this.revoked != true) {
|
||||
this.revoked = revoked;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public byte[] toHash() {
|
||||
return this.hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Encoder.HEX.encode(key);
|
||||
}
|
||||
|
||||
public boolean verify(final byte[] message, final 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];
|
||||
|
||||
sodium();
|
||||
return Util.isValid(Sodium.crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
|
||||
"signature was forged or corrupted");
|
||||
}
|
||||
|
||||
public boolean verify(final String message, final String signature, final Encoder encoder) {
|
||||
return verify(encoder.decode(message), encoder.decode(signature));
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,6 @@ import org.libsodium.jni.crypto.Hash;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tcpid.key.MasterSigningKey;
|
||||
import org.tcpid.opretj.OPRETECParser;
|
||||
import org.tcpid.opretj.OPRETWallet;
|
||||
import org.tcpid.opretj.OPRETWalletAppKit;
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package org.tcpid.opretj.testapp;
|
||||
|
||||
import org.tcpid.key.MasterVerifyKey;
|
||||
|
||||
public interface OPRETECEventListener {
|
||||
void onOPRETRevoke(MasterVerifyKey key);
|
||||
}
|
|
@ -0,0 +1,429 @@
|
|||
package org.tcpid.opretj.testapp;
|
||||
|
||||
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
||||
import static org.libsodium.jni.NaCl.sodium;
|
||||
import static org.libsodium.jni.SodiumConstants.NONCE_BYTES;
|
||||
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.bitcoinj.core.PartialMerkleTree;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.utils.ListenerRegistration;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.libsodium.jni.Sodium;
|
||||
import org.libsodium.jni.crypto.Hash;
|
||||
import org.libsodium.jni.crypto.Util;
|
||||
import org.libsodium.jni.encoders.Encoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tcpid.key.HMACSHA512256;
|
||||
import org.tcpid.key.MasterSigningKey;
|
||||
import org.tcpid.key.MasterVerifyKey;
|
||||
import org.tcpid.key.SigningKey;
|
||||
import org.tcpid.key.VerifyKey;
|
||||
import org.tcpid.opretj.OPRETBaseHandler;
|
||||
import org.tcpid.opretj.OPRETTransaction;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
public class OPRETECParser extends OPRETBaseHandler {
|
||||
private static final Logger logger = LoggerFactory.getLogger(OPRETECParser.class);
|
||||
public static final Hash HASH = new Hash();
|
||||
|
||||
private static final List<Byte> OPRET_MAGIC_EC1C = Bytes.asList(Utils.HEX.decode("ec1c"));
|
||||
private static final List<Byte> OPRET_MAGIC_EC1D = Bytes.asList(Utils.HEX.decode("ec1d"));
|
||||
private static final List<Byte> OPRET_MAGIC_ECA1 = Bytes.asList(Utils.HEX.decode("eca1"));
|
||||
private static final List<Byte> OPRET_MAGIC_ECA2 = Bytes.asList(Utils.HEX.decode("eca2"));
|
||||
private static final List<Byte> OPRET_MAGIC_ECA3 = Bytes.asList(Utils.HEX.decode("eca3"));
|
||||
private static final List<Byte> OPRET_MAGIC_ECA4 = Bytes.asList(Utils.HEX.decode("eca4"));
|
||||
private static final List<Byte> OPRET_MAGIC_EC51 = Bytes.asList(Utils.HEX.decode("ec51"));
|
||||
private static final List<Byte> OPRET_MAGIC_EC52 = Bytes.asList(Utils.HEX.decode("ec52"));
|
||||
private static final List<Byte> OPRET_MAGIC_EC0F = Bytes.asList(Utils.HEX.decode("ec0f"));
|
||||
|
||||
public static boolean checkKeyforRevoke(final VerifyKey k, final byte[] 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());
|
||||
|
||||
logger.debug("Using VerifyKey {}", k);
|
||||
|
||||
if (k.verify(revokemsg, sig)) {
|
||||
logger.debug("REVOKED VerifyKey {}", k);
|
||||
return true;
|
||||
} else {
|
||||
logger.debug("SIGNATURE does not match!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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<OPRETTransaction>> transA1HashMap = Collections
|
||||
.synchronizedMap(new HashMap<>());
|
||||
protected final Map<List<Byte>, List<OPRETTransaction>> transA2HashMap = Collections
|
||||
.synchronizedMap(new HashMap<>());
|
||||
protected final Map<List<Byte>, List<OPRETTransaction>> transA3HashMap = Collections
|
||||
.synchronizedMap(new HashMap<>());
|
||||
protected final Map<List<Byte>, List<OPRETTransaction>> transA4HashMap = Collections
|
||||
.synchronizedMap(new HashMap<>());
|
||||
protected final Map<List<Byte>, List<OPRETTransaction>> trans51HashMap = Collections
|
||||
.synchronizedMap(new HashMap<>());
|
||||
protected final Map<List<Byte>, List<OPRETTransaction>> trans52HashMap = Collections
|
||||
.synchronizedMap(new HashMap<>());
|
||||
|
||||
protected final Map<List<Byte>, List<MasterVerifyKey>> 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 MasterVerifyKey key, final long earliestTime) {
|
||||
final List<Byte> hash = Bytes.asList(key.getShortHash());
|
||||
if (!verifyKeys.containsKey(hash)) {
|
||||
verifyKeys.put(hash, new ArrayList<MasterVerifyKey>());
|
||||
}
|
||||
|
||||
verifyKeys.get(hash).add(key);
|
||||
logger.debug("Adding pkhash {}", key.getShortHash());
|
||||
addOPRET(key.getShortHash(), earliestTime);
|
||||
}
|
||||
|
||||
public boolean cryptoSelfTest() {
|
||||
if (NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES) {
|
||||
logger.error("NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES: {} > {}", NONCE_BYTES,
|
||||
HMACSHA512256.HMACSHA512256_BYTES);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
final MasterSigningKey subkey = msk.getSubKey(1L).getSubKey(2L).getSubKey(3L).getSubKey(4L);
|
||||
if (!Arrays.equals(Utils.HEX.decode("00cb0c8748318d27eab65159a2261c028d764c1154fc302b9b046aa2bbefab27"),
|
||||
subkey.toBytes())) {
|
||||
logger.error("MasterSigningKey subkey derivation failed");
|
||||
return false;
|
||||
}
|
||||
final BigInteger biMSK = new BigInteger(1, msk.toBytes());
|
||||
BigInteger biSub = new BigInteger(1, subkey.toBytes());
|
||||
final BigInteger pow2_256 = new BigInteger("10000000000000000000000000000000000000000000000000000000000000000",
|
||||
16);
|
||||
final BigInteger biDiff = biSub.subtract(biMSK).mod(pow2_256);
|
||||
|
||||
logger.debug("{} = {}", Utils.HEX.encode(msk.toBytes()), biMSK.toString(16));
|
||||
|
||||
logger.debug("{} - {} = {}", Utils.HEX.encode(msk.toBytes()), Utils.HEX.encode(subkey.toBytes()),
|
||||
Utils.HEX.encode(biDiff.toByteArray()));
|
||||
|
||||
biSub = biMSK.add(biDiff).mod(pow2_256);
|
||||
final byte[] bisubb = biSub.toByteArray();
|
||||
logger.debug("{}", Utils.HEX.encode(Util.prependZeros(32 - bisubb.length, bisubb)));
|
||||
|
||||
if (!Arrays.equals(bisubb, subkey.toBytes())) {
|
||||
logger.error("MasterSigningKey subkey difference calculation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleAnnounce(final OPRETTransaction selfTx,
|
||||
final Map<List<Byte>, List<OPRETTransaction>> selfTransHashMap,
|
||||
final Map<List<Byte>, List<OPRETTransaction>> otherTransHashMap, final boolean isT1) {
|
||||
|
||||
final byte[] selfData = Bytes.toArray(selfTx.opretData.get(1));
|
||||
if (((selfData.length < 48) || (selfData.length > 64))) {
|
||||
logger.debug("invalid chunk1 size = {}", selfData.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<Byte> pkhash = selfTx.opretData.get(2);
|
||||
if ((pkhash.size() != 12)) {
|
||||
logger.debug("chunk 2 size != 12 but {} ", pkhash.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!verifyKeys.containsKey(pkhash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (otherTransHashMap.containsKey(pkhash)) {
|
||||
for (final OPRETTransaction otherTx : otherTransHashMap.get(pkhash)) {
|
||||
final byte[] otherData = Bytes.toArray(otherTx.opretData.get(1));
|
||||
final byte[] cipher = isT1
|
||||
? Bytes.concat(Arrays.copyOfRange(selfData, 0, 48), Arrays.copyOfRange(otherData, 0, 48))
|
||||
: Bytes.concat(Arrays.copyOfRange(otherData, 0, 48), Arrays.copyOfRange(selfData, 0, 48));
|
||||
final BigInteger selfNonce = (selfData.length == 48) ? BigInteger.ZERO
|
||||
: new BigInteger(1, Arrays.copyOfRange(selfData, 48, selfData.length));
|
||||
final BigInteger otherNonce = (otherData.length == 48) ? BigInteger.ZERO
|
||||
: new BigInteger(1, Arrays.copyOfRange(otherData, 48, otherData.length));
|
||||
|
||||
final BigInteger nonce = isT1 ? selfNonce.shiftLeft(16 * 8).or(otherNonce)
|
||||
: otherNonce.shiftLeft(16 * 8).or(selfNonce);
|
||||
|
||||
byte[] noncebytes = Util.prependZeros(32, nonce.toByteArray());
|
||||
noncebytes = Arrays.copyOfRange(noncebytes, noncebytes.length - 32, noncebytes.length);
|
||||
|
||||
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
||||
byte[] sharedkey, xornonce;
|
||||
sharedkey = HASH.sha256(HASH.sha256(Bytes.concat(k.toBytes(), noncebytes)));
|
||||
xornonce = Arrays.copyOfRange(HASH.sha256(Bytes.concat(sharedkey, noncebytes)), 0, 24);
|
||||
logger.debug("checking key {}", Encoder.HEX.encode(k.toBytes()));
|
||||
logger.debug("noncebytes {}", Encoder.HEX.encode(noncebytes));
|
||||
logger.debug("noncebytes len {}", noncebytes.length);
|
||||
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
||||
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||
sodium();
|
||||
final byte[] msg = Util.zeros(96);
|
||||
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
||||
final byte[] vk = Arrays.copyOfRange(msg, 0, 32);
|
||||
final byte[] sig = Arrays.copyOfRange(msg, 32, 96);
|
||||
try {
|
||||
logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk));
|
||||
k.verify(vk, sig);
|
||||
} catch (final RuntimeException e) {
|
||||
logger.debug("sig does not match");
|
||||
continue;
|
||||
}
|
||||
logger.debug("sig matches");
|
||||
|
||||
k.setFirstValidSubKey(new MasterVerifyKey(vk), selfTx, otherTx);
|
||||
otherTransHashMap.get(pkhash).remove(otherTx);
|
||||
if (otherTransHashMap.get(pkhash).isEmpty()) {
|
||||
otherTransHashMap.remove(pkhash);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no matching transaction found, save for later
|
||||
if (!selfTransHashMap.containsKey(pkhash)) {
|
||||
selfTransHashMap.put(pkhash, new ArrayList<OPRETTransaction>());
|
||||
}
|
||||
selfTransHashMap.get(pkhash).add(selfTx);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleEC0F(final OPRETTransaction t) {
|
||||
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
||||
if ((sig.length != 64)) {
|
||||
logger.debug("chunk 1 size != 64, but {}", sig.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<Byte> pkhash = t.opretData.get(2);
|
||||
if ((pkhash.size() != 12)) {
|
||||
logger.debug("chunk 2 size!= 12 but {} ", pkhash.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!verifyKeys.containsKey(pkhash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
||||
if (checkKeyforRevoke(k, sig)) {
|
||||
if (k.isRevoked()) {
|
||||
logger.debug("Duplicate REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()),
|
||||
Utils.HEX.encode(sig));
|
||||
} else {
|
||||
k.setRevoked(true);
|
||||
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig));
|
||||
}
|
||||
queueOnOPRETRevoke(k);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleEC1C(final OPRETTransaction t) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleEC1D(final OPRETTransaction t) {
|
||||
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
||||
if ((sig.length != 64)) {
|
||||
logger.debug("chunk 1 size != 64, but {}", sig.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<Byte> pkhash = t.opretData.get(2);
|
||||
if ((pkhash.size() != 12)) {
|
||||
logger.debug("chunk 2 size!= 12 but {} ", pkhash.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!verifyKeys.containsKey(pkhash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
||||
if (checkKeyforRevoke(k, sig)) {
|
||||
if (k.isRevoked()) {
|
||||
logger.debug("Duplicate REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()),
|
||||
Utils.HEX.encode(sig));
|
||||
} else {
|
||||
k.setRevoked(true);
|
||||
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig));
|
||||
}
|
||||
queueOnOPRETId(k);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleEC51(final OPRETTransaction t) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleEC52(final OPRETTransaction t) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleECA1(final OPRETTransaction t1) {
|
||||
logger.debug("handleECA1");
|
||||
return handleAnnounce(t1, transA1HashMap, transA2HashMap, true);
|
||||
}
|
||||
|
||||
private boolean handleECA2(final OPRETTransaction t2) {
|
||||
logger.debug("handleECA2");
|
||||
return handleAnnounce(t2, transA2HashMap, transA1HashMap, false);
|
||||
}
|
||||
|
||||
private boolean handleECA3(final OPRETTransaction t) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleECA4(final OPRETTransaction t) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushTransaction(final OPRETTransaction t) {
|
||||
logger.debug("checking {}", t.opretData);
|
||||
|
||||
if ((t.opretData.size() != 2) && (t.opretData.size() != 3) && (t.opretData.size() != 4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<Byte> chunk = t.opretData.get(0);
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_EC0F)) {
|
||||
return handleEC0F(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_EC1C)) {
|
||||
return handleEC1C(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_EC1D)) {
|
||||
return handleEC1D(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_ECA1)) {
|
||||
return handleECA1(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_ECA2)) {
|
||||
return handleECA2(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_ECA3)) {
|
||||
return handleECA3(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_ECA4)) {
|
||||
return handleECA4(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_EC51)) {
|
||||
return handleEC51(t);
|
||||
}
|
||||
|
||||
if (chunk.equals(OPRET_MAGIC_EC52)) {
|
||||
return handleEC52(t);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void queueOnOPRETId(final MasterVerifyKey k) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
protected void queueOnOPRETRevoke(final MasterVerifyKey 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);
|
||||
}
|
||||
|
||||
public void removeVerifyKey(final MasterVerifyKey key) {
|
||||
final List<Byte> hash = Bytes.asList(key.getShortHash());
|
||||
|
||||
if (!verifyKeys.containsKey(hash)) {
|
||||
return;
|
||||
}
|
||||
|
||||
verifyKeys.get(hash).remove(key);
|
||||
|
||||
removeOPRET(key.getShortHash());
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
<logger name="org.bitcoinj.core.AbstractBlockChain" level="error" />
|
||||
<logger name="org.bitcoinj.core.PeerSocketHandler" level="error" />
|
||||
<logger name="org.bitcoinj.net.ConnectionHandler" level="error" />
|
||||
<logger name="org.tcpid.opretj.OPRETECParser" level="debug" />
|
||||
<logger name="org.tcpid.opretj.testapp.OPRETECParser" level="debug" />
|
||||
<!--
|
||||
<logger name="eckey.OPRETSimpleLogger" level="debug" />
|
||||
<logger name="eckey.OPRETSimpleParser" level="debug" />
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package org.tcpid.opretj.testapp;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.libsodium.jni.NaCl.sodium;
|
||||
import static org.libsodium.jni.SodiumConstants.NONCE_BYTES;
|
||||
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.junit.Test;
|
||||
import org.libsodium.jni.Sodium;
|
||||
import org.libsodium.jni.crypto.Hash;
|
||||
import org.libsodium.jni.crypto.Util;
|
||||
import org.libsodium.jni.encoders.Encoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tcpid.key.HMACSHA512256;
|
||||
import org.tcpid.key.MasterSigningKey;
|
||||
import org.tcpid.key.MasterVerifyKey;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
public class TestCrypto {
|
||||
private final static Logger logger = LoggerFactory.getLogger(TestCrypto.class);
|
||||
private final static Hash HASH = new Hash();
|
||||
|
||||
@Test
|
||||
public void testDerive() {
|
||||
|
||||
assertTrue("NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES", NONCE_BYTES <= HMACSHA512256.HMACSHA512256_BYTES);
|
||||
assertEquals(SECRETKEY_BYTES, HMACSHA512256.HMACSHA512256_BYTES);
|
||||
|
||||
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||
|
||||
assertArrayEquals(Utils.HEX.decode("4071b2b3db7cc7aecd0b23608e96f44f08463ea0ee0a0c12f5fa21ff449deb55"),
|
||||
msk.toBytes());
|
||||
|
||||
final MasterSigningKey subkey = msk.getSubKey(1L).getSubKey(2L).getSubKey(3L).getSubKey(4L);
|
||||
assertArrayEquals(Utils.HEX.decode("00cb0c8748318d27eab65159a2261c028d764c1154fc302b9b046aa2bbefab27"),
|
||||
subkey.toBytes());
|
||||
|
||||
final BigInteger biMSK = new BigInteger(1, msk.toBytes());
|
||||
BigInteger biSub = new BigInteger(1, subkey.toBytes());
|
||||
final BigInteger pow2_256 = new BigInteger("10000000000000000000000000000000000000000000000000000000000000000",
|
||||
16);
|
||||
final BigInteger biDiff = biSub.subtract(biMSK).mod(pow2_256);
|
||||
|
||||
biSub = biMSK.add(biDiff).mod(pow2_256);
|
||||
final byte[] bisubb = biSub.toByteArray();
|
||||
|
||||
assertArrayEquals(bisubb, subkey.toBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSign() {
|
||||
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
||||
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), vk.toHash());
|
||||
final byte[] sig = msk.sign(revokemsg);
|
||||
assertTrue("Verification of signature failed.", vk.verify(revokemsg, sig));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignEnc() {
|
||||
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||
final MasterVerifyKey subkey = msk.getSubKey(1L).getMasterVerifyKey();
|
||||
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
||||
byte[] sig = msk.sign(subkey.toBytes());
|
||||
|
||||
logger.debug("using key {}", Encoder.HEX.encode(vk.toBytes()));
|
||||
final byte[] noncebytes = Util.zeros(32);
|
||||
final byte[] sharedkey = HASH.sha256(HASH.sha256(Bytes.concat(vk.toBytes(), noncebytes)));
|
||||
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(Bytes.concat(sharedkey, noncebytes)), 0, 24);
|
||||
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
||||
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||
|
||||
final byte[] cipher = Util.zeros(96);
|
||||
byte[] msg = Bytes.concat(subkey.toBytes(), sig);
|
||||
assertEquals(96, msg.length);
|
||||
|
||||
sodium();
|
||||
Sodium.crypto_stream_xsalsa20_xor(cipher, msg, 96, xornonce, sharedkey);
|
||||
assertEquals(96, cipher.length);
|
||||
logger.debug("Clear : {}", Encoder.HEX.encode(msg));
|
||||
logger.debug("Cipher: {}", Encoder.HEX.encode(cipher));
|
||||
msg = Util.zeros(96);
|
||||
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
||||
|
||||
final byte[] vkb = Arrays.copyOfRange(msg, 0, 32);
|
||||
sig = Arrays.copyOfRange(msg, 32, 96);
|
||||
logger.debug("vkb : {}", Encoder.HEX.encode(vkb));
|
||||
assertTrue("Verification of signature failed.", vk.verify(vkb, sig));
|
||||
assertArrayEquals(subkey.toBytes(), vkb);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignEncNoncebytes() {
|
||||
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
||||
byte[] sig = msk.sign(vk.toBytes());
|
||||
|
||||
logger.debug("nonce: using key {}", Encoder.HEX.encode(vk.toBytes()));
|
||||
final byte[] noncebytes = Encoder.HEX
|
||||
.decode("0000000000000000000000000000001100000000000000000000000000000000");
|
||||
final byte[] sharedkey = HASH.sha256(HASH.sha256(Bytes.concat(vk.toBytes(), noncebytes)));
|
||||
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(Bytes.concat(sharedkey, noncebytes)), 0, 24);
|
||||
logger.debug("nonce: nonce {}", Encoder.HEX.encode(xornonce));
|
||||
logger.debug("nonce: sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||
|
||||
final byte[] cipher = Util.zeros(96);
|
||||
byte[] msg = Bytes.concat(vk.toBytes(), sig);
|
||||
assertEquals(96, msg.length);
|
||||
|
||||
sodium();
|
||||
Sodium.crypto_stream_xsalsa20_xor(cipher, msg, 96, xornonce, sharedkey);
|
||||
assertEquals(96, cipher.length);
|
||||
logger.debug("nonce: Clear : {}", Encoder.HEX.encode(msg));
|
||||
logger.debug("nonce: Cipher: {}", Encoder.HEX.encode(cipher));
|
||||
msg = Util.zeros(96);
|
||||
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
||||
|
||||
final byte[] vkb = Arrays.copyOfRange(msg, 0, 32);
|
||||
sig = Arrays.copyOfRange(msg, 32, 96);
|
||||
logger.debug("nonce: vkb : {}", Encoder.HEX.encode(vkb));
|
||||
assertTrue("nonce: Verification of signature failed.", vk.verify(vkb, sig));
|
||||
assertArrayEquals(vk.toBytes(), vkb);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.tcpid.opretj.testapp;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.junit.Test;
|
||||
import org.libsodium.jni.encoders.Encoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tcpid.key.MasterVerifyKey;
|
||||
import org.tcpid.opretj.OPRETTransaction;
|
||||
import org.tcpid.opretj.testapp.OPRETECParser;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
public class TestECA1 {
|
||||
private final static Logger logger = LoggerFactory.getLogger(TestECA1.class);
|
||||
|
||||
/**
|
||||
* Test method for
|
||||
* {@link org.tcpid.opretj.testapp.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
|
||||
*/
|
||||
@Test
|
||||
public void testPushTransaction() {
|
||||
logger.debug("testPushTransaction");
|
||||
|
||||
final byte[] cipher = Encoder.HEX.decode(
|
||||
"a15b671a9890a6bd0b6ed9a50193a15283001ccd72e106198b32242a906c300e263fc31dbfdaad66c40fc9796db3a464ab4313a06bbcd88fc1d503110016114c1da8bdf6e58a82be18d33c1baa96e1a9fe9c6f939b6838b30972be2de53f12d0");
|
||||
final byte[] vkb = Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c");
|
||||
final MasterVerifyKey mvk = new MasterVerifyKey(vkb);
|
||||
|
||||
final byte[] vkbsha96 = Arrays.copyOfRange(Sha256Hash.of(vkb).getBytes(), 0, 12);
|
||||
final byte[] nullbyte = {};
|
||||
|
||||
List<List<Byte>> opret_data = new ArrayList<>();
|
||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
||||
opret_data.add(Bytes.asList(vkbsha96));
|
||||
final OPRETTransaction t1 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||
|
||||
opret_data = new ArrayList<>();
|
||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||
opret_data.add(Bytes.asList(vkbsha96));
|
||||
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||
|
||||
opret_data = new ArrayList<>();
|
||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
||||
opret_data.add(Bytes.asList(vkbsha96));
|
||||
final OPRETTransaction t3 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||
|
||||
opret_data = new ArrayList<>();
|
||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||
opret_data.add(Bytes.asList(vkbsha96));
|
||||
final OPRETTransaction t4 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||
|
||||
final OPRETECParser parser = new OPRETECParser();
|
||||
|
||||
parser.addVerifyKey(mvk, 0);
|
||||
|
||||
assertFalse(parser.pushTransaction(t2));
|
||||
assertFalse(parser.pushTransaction(t3));
|
||||
assertFalse(parser.pushTransaction(t4));
|
||||
assertTrue(parser.pushTransaction(t1));
|
||||
|
||||
mvk.clearSubKeys();
|
||||
|
||||
assertFalse(parser.pushTransaction(t1));
|
||||
assertFalse(parser.pushTransaction(t3));
|
||||
assertFalse(parser.pushTransaction(t4));
|
||||
assertTrue(parser.pushTransaction(t2));
|
||||
|
||||
mvk.clearSubKeys();
|
||||
|
||||
assertFalse(parser.pushTransaction(t1));
|
||||
assertFalse(parser.pushTransaction(t4));
|
||||
assertFalse(parser.pushTransaction(t3));
|
||||
assertTrue(parser.pushTransaction(t2));
|
||||
|
||||
mvk.clearSubKeys();
|
||||
|
||||
assertFalse(parser.pushTransaction(t2));
|
||||
assertFalse(parser.pushTransaction(t4));
|
||||
assertFalse(parser.pushTransaction(t3));
|
||||
assertTrue(parser.pushTransaction(t1));
|
||||
final MasterVerifyKey subkey = mvk.getValidSubKey();
|
||||
assertArrayEquals(subkey.toBytes(),
|
||||
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for
|
||||
* {@link org.tcpid.opretj.testapp.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
|
||||
*/
|
||||
@Test
|
||||
public void testPushTransactionWithNonce() {
|
||||
logger.debug("testPushTransactionWithNonce");
|
||||
final byte[] cipher = Encoder.HEX.decode(
|
||||
"24f99184d03a6ffa5826bd9300a7fb1cff264600f335b3c6042f15cb4d3d9019fa2a9c905cdf6f6c80178def845f0340e6d2e55a7dee433a5af984760adc23e187734e5e4e76aa22f3acab172262633139b6dcd11229fe2385661a70d6c206c0");
|
||||
final byte[] vkb = Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c");
|
||||
final MasterVerifyKey mvk = new MasterVerifyKey(vkb);
|
||||
|
||||
final byte[] vkbsha96 = Arrays.copyOfRange(Sha256Hash.of(vkb).getBytes(), 0, 12);
|
||||
final byte[] nullbyte = {};
|
||||
|
||||
List<List<Byte>> opret_data = new ArrayList<>();
|
||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
||||
final byte[] byte1f = { (byte) 0x11 };
|
||||
opret_data.add(Bytes.asList(Bytes.concat(Arrays.copyOfRange(cipher, 0, 48), byte1f)));
|
||||
opret_data.add(Bytes.asList(vkbsha96));
|
||||
final OPRETTransaction t1 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||
|
||||
opret_data = new ArrayList<>();
|
||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||
opret_data.add(Bytes.asList(vkbsha96));
|
||||
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||
|
||||
final OPRETECParser parser = new OPRETECParser();
|
||||
|
||||
parser.addVerifyKey(mvk, 0);
|
||||
|
||||
assertFalse(parser.pushTransaction(t2));
|
||||
assertTrue(parser.pushTransaction(t1));
|
||||
|
||||
mvk.clearSubKeys();
|
||||
|
||||
assertFalse(parser.pushTransaction(t1));
|
||||
assertTrue(parser.pushTransaction(t2));
|
||||
|
||||
mvk.clearSubKeys();
|
||||
|
||||
assertFalse(parser.pushTransaction(t1));
|
||||
assertTrue(parser.pushTransaction(t2));
|
||||
|
||||
mvk.clearSubKeys();
|
||||
|
||||
assertFalse(parser.pushTransaction(t2));
|
||||
assertTrue(parser.pushTransaction(t1));
|
||||
final MasterVerifyKey subkey = mvk.getValidSubKey();
|
||||
assertArrayEquals(subkey.toBytes(),
|
||||
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue