add ECA3 and ECA4

This commit is contained in:
Harald Hoyer 2016-09-14 14:37:11 +02:00
parent f68d85cea0
commit 1bd005fb5e
8 changed files with 450 additions and 173 deletions

View file

@ -60,6 +60,7 @@ A MK is stored along with the key birthday, which is the date of the first appea
### MVK announce subkey VK 0xECA[1,2] - A-nnounce ### MVK announce subkey VK 0xECA[1,2] - A-nnounce
nonce[0:32] = nonce[0:16] | nonce[16:32] nonce[0:32] = nonce[0:16] | nonce[16:32]
data chunks are prepended with zeros, if its length is smaller than 16. data chunks are prepended with zeros, if its length is smaller than 16.
E.g. E.g.
@ -74,14 +75,16 @@ If nonce is missing completely, then
is assumed. is assumed.
A nonce **must** be used only once. Either only one VK_pub is announced per MVK ever and nonce is missing, A nonce **must** be used only once. Either only one VK_pub is announced per MVK ever and nonce is missing,
or for every MVK announcement, the nonce has to be *unique* or *true random* bytes. or for every MVK announcement, the nonce has to be **unique** or **true random** bytes.
```
sharedkey = sha256(sha256(MVK_pub | nonce)) sharedkey = sha256(sha256(MVK_pub | nonce))
xornonce[24] = sha256(sharedkey | nonce)[0:24] xornonce[24] = sha256(sharedkey | nonce)[0:24]
sig[64] = crypto_sign(VK_pub, MKV) sig[64] = crypto_sign(VK_pub, MKV)
msg[96] = VK_pub || sig msg[96] = VK_pub || sig
cipher[96] = crypto_stream_xor(msg, xornonce, sharedkey) cipher[96] = crypto_stream_xor(msg, xornonce, sharedkey)
```
clients may flush T1, if T2 does not follow in the next 20 blocks clients may flush T1, if T2 does not follow in the next 20 blocks
clients may flush T2, if T1 does not follow in the next 20 blocks clients may flush T2, if T1 does not follow in the next 20 blocks
@ -94,12 +97,14 @@ clients may flush T2, if T1 does not follow in the next 20 blocks
| Size | 1 | 3 | 49 | 13 | | Size | 1 | 3 | 49 | 13 |
### MVK announce next subkey VK_n+1 0xECA[3,4] - A-nnounce ### MVK announce next subkey VK_n+1 0xECA[3,4] - A-nnounce
```
sharedkey = sha256(sha256(VK_n_pub)) sharedkey = sha256(sha256(VK_n_pub))
nonce[24] = sha256(sharedkey)[0:24] nonce[24] = sha256(sharedkey)[0:24]
sig[64] = crypto_sign(VK_n+1_pub, MKV) sig[64] = crypto_sign(VK_n+1_pub, MKV)
msg[96] = VK_n+1_pub || sig msg[96] = VK_n+1_pub || sig
cipher[96] = crypto_stream_xor(msg, nonce, sharedkey) cipher[96] = crypto_stream_xor(msg, nonce, sharedkey)
```
clients may flush T1, if T2 does not follow in the next 20 blocks clients may flush T1, if T2 does not follow in the next 20 blocks
clients may flush T2, if T1 does not follow in the next 20 blocks clients may flush T2, if T1 does not follow in the next 20 blocks
@ -112,8 +117,10 @@ clients may flush T2, if T1 does not follow in the next 20 blocks
| Size | 1 | 3 | 49 | 13 | 13 | | Size | 1 | 3 | 49 | 13 | 13 |
### Public Doc or other key OK sign 0xEC5[1,2] ### Public Doc or other key OK sign 0xEC5[1,2]
```
sign[64] = Sign_Key('Sign ' || sha256(Doc/OK)) sign[64] = Sign_Key('Sign ' || sha256(Doc/OK))
data = optional data (max 2*19 bytes) data = optional data (max 2*19 bytes)
```
clients may flush T1, if T2 does not follow in the next 20 blocks clients may flush T1, if T2 does not follow in the next 20 blocks
clients may flush T2, if T1 does not follow in the next 20 blocks clients may flush T2, if T1 does not follow in the next 20 blocks

View file

@ -1,10 +1,14 @@
package org.tcpid.key; package org.tcpid.key;
import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import org.tcpid.opretj.OPRETTransaction; import org.tcpid.opretj.OPRETTransaction;
import com.google.common.primitives.Bytes;
public class MasterVerifyKey extends VerifyKey { public class MasterVerifyKey extends VerifyKey {
private final LinkedList<MasterVerifyKey> subkeys = new LinkedList<>(); private final LinkedList<MasterVerifyKey> subkeys = new LinkedList<>();
@ -16,6 +20,15 @@ public class MasterVerifyKey extends VerifyKey {
subkeys.clear(); subkeys.clear();
} }
public MasterVerifyKey getSubKeybyHash(List<Byte> subpkhash) {
for (final MasterVerifyKey k : subkeys) {
if (Arrays.equals(k.getShortHash(), Bytes.toArray(subpkhash))) {
return k;
}
}
return null;
}
public MasterVerifyKey getValidSubKey() { public MasterVerifyKey getValidSubKey() {
return subkeys.getFirst(); return subkeys.getFirst();
} }
@ -39,8 +52,9 @@ public class MasterVerifyKey extends VerifyKey {
subkeys.addLast(key); subkeys.addLast(key);
} }
public void setNextValidSubKey(final MasterVerifyKey after, final MasterVerifyKey key) { public void setNextValidSubKey(final MasterVerifyKey after, final MasterVerifyKey key, OPRETTransaction t1,
final VerifyKey l = subkeys.getLast(); OPRETTransaction t2) {
final MasterVerifyKey l = subkeys.getLast();
if (!l.equals(after)) { if (!l.equals(after)) {
throw new NoSuchElementException("No such after key, or not last in list"); throw new NoSuchElementException("No such after key, or not last in list");
} }

View file

@ -124,8 +124,13 @@ public class VerifyKey implements Comparable<VerifyKey> {
final int[] bufferLen = new int[1]; final int[] bufferLen = new int[1];
sodium(); sodium();
return Util.isValid(Sodium.crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key), try {
Util.isValid(Sodium.crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
"signature was forged or corrupted"); "signature was forged or corrupted");
return true;
} catch (final Exception e) {
return false;
}
} }
public boolean verify(final String message, final String signature, final Encoder encoder) { public boolean verify(final String message, final String signature, final Encoder encoder) {

View file

@ -168,7 +168,7 @@ public class OPRETECParser extends OPRETBaseHandler {
private boolean handleAnnounce(final OPRETTransaction selfTx, private boolean handleAnnounce(final OPRETTransaction selfTx,
final Map<List<Byte>, List<OPRETTransaction>> selfTransHashMap, final Map<List<Byte>, List<OPRETTransaction>> selfTransHashMap,
final Map<List<Byte>, List<OPRETTransaction>> otherTransHashMap, final boolean isT1) { final Map<List<Byte>, List<OPRETTransaction>> otherTransHashMap, final boolean isT1) {
logger.debug("handleAnnounce");
final byte[] selfData = Bytes.toArray(selfTx.opretData.get(1)); final byte[] selfData = Bytes.toArray(selfTx.opretData.get(1));
if (((selfData.length < 48) || (selfData.length > 64))) { if (((selfData.length < 48) || (selfData.length > 64))) {
logger.debug("invalid chunk1 size = {}", selfData.length); logger.debug("invalid chunk1 size = {}", selfData.length);
@ -176,7 +176,7 @@ public class OPRETECParser extends OPRETBaseHandler {
} }
final List<Byte> pkhash = selfTx.opretData.get(2); final List<Byte> pkhash = selfTx.opretData.get(2);
if ((pkhash.size() != 12)) { if (pkhash.size() != 12) {
logger.debug("chunk 2 size != 12 but {} ", pkhash.size()); logger.debug("chunk 2 size != 12 but {} ", pkhash.size());
return false; return false;
} }
@ -216,16 +216,16 @@ public class OPRETECParser extends OPRETBaseHandler {
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey); Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
final byte[] vk = Arrays.copyOfRange(msg, 0, 32); final byte[] vk = Arrays.copyOfRange(msg, 0, 32);
final byte[] sig = Arrays.copyOfRange(msg, 32, 96); final byte[] sig = Arrays.copyOfRange(msg, 32, 96);
try {
logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk)); logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk));
k.verify(vk, sig);
} catch (final RuntimeException e) { if (!k.verify(vk, sig)) {
logger.debug("sig does not match"); logger.debug("sig does not match");
continue; continue;
} }
logger.debug("sig matches"); logger.debug("sig matches");
k.setFirstValidSubKey(new MasterVerifyKey(vk), selfTx, otherTx); k.setFirstValidSubKey(new MasterVerifyKey(vk), isT1 ? selfTx : otherTx, isT1 ? otherTx : selfTx);
otherTransHashMap.get(pkhash).remove(otherTx); otherTransHashMap.get(pkhash).remove(otherTx);
if (otherTransHashMap.get(pkhash).isEmpty()) { if (otherTransHashMap.get(pkhash).isEmpty()) {
otherTransHashMap.remove(pkhash); otherTransHashMap.remove(pkhash);
@ -337,13 +337,91 @@ public class OPRETECParser extends OPRETBaseHandler {
return handleAnnounce(t2, transA2HashMap, transA1HashMap, false); return handleAnnounce(t2, transA2HashMap, transA1HashMap, false);
} }
private boolean handleECA3(final OPRETTransaction t) { private boolean handleECA3(final OPRETTransaction t1) {
// TODO Auto-generated method stub logger.debug("handleECA3");
return handleNextKey(t1, transA3HashMap, transA4HashMap, true);
}
private boolean handleECA4(final OPRETTransaction t2) {
logger.debug("handleECA4");
return handleNextKey(t2, transA4HashMap, transA3HashMap, false);
}
private boolean handleNextKey(final OPRETTransaction selfTx,
final Map<List<Byte>, List<OPRETTransaction>> selfTransHashMap,
final Map<List<Byte>, List<OPRETTransaction>> otherTransHashMap, final boolean isT1) {
logger.debug("handleNextKey");
final byte[] selfData = Bytes.toArray(selfTx.opretData.get(1));
if (selfData.length != 48) {
logger.debug("invalid chunk1 size = {}", selfData.length);
return false; return false;
} }
private boolean handleECA4(final OPRETTransaction t) { final List<Byte> pkhash = selfTx.opretData.get(2);
// TODO Auto-generated method stub if (pkhash.size() != 12) {
logger.debug("chunk 2 size != 12 but {} ", pkhash.size());
return false;
}
final List<Byte> subpkhash = selfTx.opretData.get(3);
if (subpkhash.size() != 12) {
logger.debug("chunk 2 size != 12 but {} ", subpkhash.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(selfData, otherData) : Bytes.concat(otherData, selfData);
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
final MasterVerifyKey vk_n = k.getSubKeybyHash(subpkhash);
if (vk_n == null) {
continue;
}
final byte[] sharedkey = HASH.sha256(vk_n.toHash());
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(sharedkey), 0, 24);
logger.debug("checking key {}", Encoder.HEX.encode(k.toBytes()));
logger.debug("checking subkey {}", Encoder.HEX.encode(vk_n.toBytes()));
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);
logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk));
if (!k.verify(vk, sig)) {
logger.debug("sig does not match");
continue;
}
logger.debug("sig matches");
k.setNextValidSubKey(vk_n, new MasterVerifyKey(vk), isT1 ? selfTx : otherTx,
isT1 ? otherTx : selfTx);
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; return false;
} }
@ -352,6 +430,7 @@ public class OPRETECParser extends OPRETBaseHandler {
logger.debug("checking {}", t.opretData); logger.debug("checking {}", t.opretData);
if ((t.opretData.size() != 2) && (t.opretData.size() != 3) && (t.opretData.size() != 4)) { if ((t.opretData.size() != 2) && (t.opretData.size() != 3) && (t.opretData.size() != 4)) {
logger.debug("Wrong chunk count");
return false; return false;
} }

View file

@ -15,6 +15,7 @@
<logger name="org.bitcoinj.core.PeerSocketHandler" level="error" /> <logger name="org.bitcoinj.core.PeerSocketHandler" level="error" />
<logger name="org.bitcoinj.net.ConnectionHandler" level="error" /> <logger name="org.bitcoinj.net.ConnectionHandler" level="error" />
<logger name="org.tcpid.opret.OPRETECParser" level="debug" /> <logger name="org.tcpid.opret.OPRETECParser" level="debug" />
<logger name="org.tcpid.opret.TestCrypto" level="debug" />
<!-- <!--
<logger name="eckey.OPRETSimpleLogger" level="debug" /> <logger name="eckey.OPRETSimpleLogger" level="debug" />
<logger name="eckey.OPRETSimpleParser" level="debug" /> <logger name="eckey.OPRETSimpleParser" level="debug" />

View file

@ -30,7 +30,7 @@ public class TestCrypto {
@Test @Test
public void testDerive() { public void testDerive() {
logger.debug("testDerive");
assertTrue("NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES", NONCE_BYTES <= HMACSHA512256.HMACSHA512256_BYTES); assertTrue("NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES", NONCE_BYTES <= HMACSHA512256.HMACSHA512256_BYTES);
assertEquals(SECRETKEY_BYTES, HMACSHA512256.HMACSHA512256_BYTES); assertEquals(SECRETKEY_BYTES, HMACSHA512256.HMACSHA512256_BYTES);
@ -57,6 +57,7 @@ public class TestCrypto {
@Test @Test
public void testSign() { public void testSign() {
logger.debug("testSign");
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes())); final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
final MasterVerifyKey vk = msk.getMasterVerifyKey(); final MasterVerifyKey vk = msk.getMasterVerifyKey();
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), vk.toHash()); final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), vk.toHash());
@ -66,6 +67,7 @@ public class TestCrypto {
@Test @Test
public void testSignEnc() { public void testSignEnc() {
logger.debug("testSignEnc");
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes())); final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
final MasterVerifyKey subkey = msk.getSubKey(1L).getMasterVerifyKey(); final MasterVerifyKey subkey = msk.getSubKey(1L).getMasterVerifyKey();
final MasterVerifyKey vk = msk.getMasterVerifyKey(); final MasterVerifyKey vk = msk.getMasterVerifyKey();
@ -97,8 +99,46 @@ public class TestCrypto {
assertArrayEquals(subkey.toBytes(), vkb); assertArrayEquals(subkey.toBytes(), vkb);
} }
@Test
public void testSignEncNext() {
logger.debug("testSignEncNext");
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
final MasterVerifyKey mvk = msk.getMasterVerifyKey();
final MasterVerifyKey subkey1 = msk.getSubKey(1L).getMasterVerifyKey();
final MasterVerifyKey subkey2 = msk.getSubKey(2L).getMasterVerifyKey();
byte[] sig = msk.sign(subkey2.toBytes());
logger.debug("using key {}", Encoder.HEX.encode(subkey1.toBytes()));
final byte[] sharedkey = HASH.sha256(HASH.sha256(subkey1.toBytes()));
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(sharedkey), 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(subkey2.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.", mvk.verify(vkb, sig));
assertArrayEquals(subkey2.toBytes(), vkb);
}
@Test @Test
public void testSignEncNoncebytes() { public void testSignEncNoncebytes() {
logger.debug("testSignEncNoncebytes");
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes())); final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
final MasterVerifyKey vk = msk.getMasterVerifyKey(); final MasterVerifyKey vk = msk.getMasterVerifyKey();
byte[] sig = msk.sign(vk.toBytes()); byte[] sig = msk.sign(vk.toBytes());

View file

@ -1,155 +0,0 @@
/**
*
*/
package org.tcpid.opret;
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.opret.OPRETECParser;
import org.tcpid.opretj.OPRETTransaction;
import com.google.common.primitives.Bytes;
public class TestECA1 {
private final static Logger logger = LoggerFactory.getLogger(TestECA1.class);
/**
* Test method for
* {@link org.tcpid.opret.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.opret.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"));
}
}

View file

@ -0,0 +1,286 @@
/**
*
*/
package org.tcpid.opret;
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.After;
import org.junit.Before;
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 com.google.common.primitives.Bytes;
public class TestPushTransaction {
private Logger logger;
@Before
public void setUp() throws Exception {
logger = LoggerFactory.getLogger(TestPushTransaction.class);
}
@After
public void tearDown() throws Exception {
}
/**
* Test method for
* {@link org.tcpid.opret.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
*/
@Test
public void testPushTransactionECA12() {
logger.debug("testPushTransactionECA12");
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);
// create t3 and t4 to test if the parser handles garbage
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));
MasterVerifyKey subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
mvk.clearSubKeys();
assertFalse(parser.pushTransaction(t1));
assertFalse(parser.pushTransaction(t3));
assertFalse(parser.pushTransaction(t4));
assertTrue(parser.pushTransaction(t2));
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
mvk.clearSubKeys();
assertFalse(parser.pushTransaction(t1));
assertFalse(parser.pushTransaction(t4));
assertFalse(parser.pushTransaction(t3));
assertTrue(parser.pushTransaction(t2));
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
mvk.clearSubKeys();
assertFalse(parser.pushTransaction(t2));
assertFalse(parser.pushTransaction(t4));
assertFalse(parser.pushTransaction(t3));
assertTrue(parser.pushTransaction(t1));
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
}
/**
* Test method for
* {@link org.tcpid.opret.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
*/
@Test
public void testPushTransactionECA12WithNonce() {
logger.debug("testPushTransactionECA12WithNonce");
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));
MasterVerifyKey subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
mvk.clearSubKeys();
assertFalse(parser.pushTransaction(t1));
assertTrue(parser.pushTransaction(t2));
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
mvk.clearSubKeys();
assertFalse(parser.pushTransaction(t1));
assertTrue(parser.pushTransaction(t2));
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
mvk.clearSubKeys();
assertFalse(parser.pushTransaction(t2));
assertTrue(parser.pushTransaction(t1));
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
}
/**
* Test method for
* {@link org.tcpid.opret.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
*/
@Test
public void testPushTransactionECA34() {
logger.debug("testPushTransactionECA34");
final byte[] cipher = Encoder.HEX.decode(
"6c44299f0b00638ca3e8a42d29082fb8f007eaff491472f9329c950b9181bfece5b7bf749b9b941dd92816c62b89f4673848ffe435576ee4e39d2e5f26041daff667c25dee1116a3f9f223de565465cafb2fdf3422c8c905666c268f7244850c");
final MasterVerifyKey mvk = new MasterVerifyKey(
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
final MasterVerifyKey firstsub = new MasterVerifyKey(
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
final byte[] nullbyte = {};
List<List<Byte>> opret_data = new ArrayList<>();
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca3")));
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
opret_data.add(Bytes.asList(mvk.getShortHash()));
opret_data.add(Bytes.asList(firstsub.getShortHash()));
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("eca4")));
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
opret_data.add(Bytes.asList(mvk.getShortHash()));
opret_data.add(Bytes.asList(firstsub.getShortHash()));
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
// create t3 and t4 to test if the parser handles garbage
opret_data = new ArrayList<>();
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca4")));
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
opret_data.add(Bytes.asList(mvk.getShortHash()));
opret_data.add(Bytes.asList(firstsub.getShortHash()));
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("eca3")));
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
opret_data.add(Bytes.asList(mvk.getShortHash()));
opret_data.add(Bytes.asList(firstsub.getShortHash()));
final OPRETTransaction t4 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
final OPRETECParser parser = new OPRETECParser();
parser.addVerifyKey(mvk, 0);
parser.addVerifyKey(firstsub, 0);
assertFalse(parser.pushTransaction(t2));
assertFalse(parser.pushTransaction(t3));
assertFalse(parser.pushTransaction(t4));
assertTrue(parser.pushTransaction(t1));
mvk.revokeSubKey(firstsub);
MasterVerifyKey subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
mvk.clearSubKeys();
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
assertFalse(parser.pushTransaction(t1));
assertFalse(parser.pushTransaction(t3));
assertFalse(parser.pushTransaction(t4));
assertTrue(parser.pushTransaction(t2));
mvk.revokeSubKey(firstsub);
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
mvk.clearSubKeys();
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
assertFalse(parser.pushTransaction(t1));
assertFalse(parser.pushTransaction(t4));
assertFalse(parser.pushTransaction(t3));
assertTrue(parser.pushTransaction(t2));
mvk.revokeSubKey(firstsub);
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
mvk.clearSubKeys();
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
assertFalse(parser.pushTransaction(t2));
assertFalse(parser.pushTransaction(t4));
assertFalse(parser.pushTransaction(t3));
assertTrue(parser.pushTransaction(t1));
mvk.revokeSubKey(firstsub);
subkey = mvk.getValidSubKey();
assertArrayEquals(subkey.toBytes(),
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
}
}