From fc91f7366c39ea43193fcc5bcbd66f983e796990 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 2 Nov 2017 16:30:25 -0500 Subject: [PATCH 1/4] Experimentally changing the nonce type to BigInteger --- .../cy/agorise/graphenej/objects/Memo.java | 44 ++++++++++++------- .../cy/agorise/graphenej/TransactionTest.java | 3 +- .../agorise/graphenej/objects/MemoTest.java | 18 ++++---- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java b/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java index f94a901..6472852 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java @@ -11,6 +11,7 @@ import org.bitcoinj.core.ECKey; import org.spongycastle.math.ec.ECPoint; import java.lang.reflect.Type; +import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -36,7 +37,7 @@ public class Memo implements ByteSerializable, JsonSerializable { private Address from; private Address to; - private long nonce; + private BigInteger nonce; private byte[] message; private String plaintextMessage; @@ -67,7 +68,7 @@ public class Memo implements ByteSerializable, JsonSerializable { * @param nonce: Nonce used in the encryption. * @param message: Message in ciphertext. */ - public Memo(Address from, Address to, long nonce, byte[] message){ + public Memo(Address from, Address to, BigInteger nonce, byte[] message){ this.from = from; this.to = to; this.nonce = nonce; @@ -90,7 +91,7 @@ public class Memo implements ByteSerializable, JsonSerializable { return this.to; } - public long getNonce(){ + public BigInteger getNonce(){ return this.nonce; } @@ -113,15 +114,17 @@ public class Memo implements ByteSerializable, JsonSerializable { * @param message: Plaintext message. * @return: The encrypted version of the message. */ - public static byte[] encryptMessage(ECKey privateKey, PublicKey publicKey, long nonce, String message){ + public static byte[] encryptMessage(ECKey privateKey, PublicKey publicKey, BigInteger nonce, String message){ byte[] encrypted = null; try { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); // Getting nonce bytes - String stringNonce = String.format("%d", nonce); - byte[] nonceBytes = Arrays.copyOfRange(Util.hexlify(stringNonce), 0, stringNonce.length()); + byte[] paddedNonceBytes = new byte[8]; + byte[] originalNonceBytes = nonce.toByteArray(); + System.arraycopy(originalNonceBytes, 0, paddedNonceBytes, 8 - originalNonceBytes.length, originalNonceBytes.length); +// byte[] nonceBytes = nonce.toByteArray(); // Getting shared secret byte[] secret = publicKey.getKey().getPubKeyPoint().multiply(privateKey.getPrivKey()).normalize().getXCoord().getEncoded(); @@ -129,7 +132,7 @@ public class Memo implements ByteSerializable, JsonSerializable { // SHA-512 of shared secret byte[] ss = sha512.digest(secret); - byte[] seed = Bytes.concat(nonceBytes, Util.hexlify(Util.bytesToHex(ss))); + byte[] seed = Bytes.concat(paddedNonceBytes, Util.hexlify(Util.bytesToHex(ss))); // Calculating checksum byte[] sha256Msg = sha256.digest(message.getBytes()); @@ -154,7 +157,7 @@ public class Memo implements ByteSerializable, JsonSerializable { * @param message: Plaintext message. * @return: The encrypted version of the message. */ - public static byte[] encryptMessage(ECKey privateKey, Address destinationAddress, long nonce, String message){ + public static byte[] encryptMessage(ECKey privateKey, Address destinationAddress, BigInteger nonce, String message){ return encryptMessage(privateKey, destinationAddress.getPublicKey(), nonce, message); } @@ -168,15 +171,19 @@ public class Memo implements ByteSerializable, JsonSerializable { * @return: The plaintext version of the enrcrypted message. * @throws ChecksumException */ - public static String decryptMessage(ECKey privateKey, PublicKey publicKey, long nonce, byte[] message) throws ChecksumException { + public static String decryptMessage(ECKey privateKey, PublicKey publicKey, BigInteger nonce, byte[] message) throws ChecksumException { String plaintext = ""; try { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); // Getting nonce bytes - String stringNonce = String.format("%d", nonce); - byte[] nonceBytes = Arrays.copyOfRange(Util.hexlify(stringNonce), 0, stringNonce.length()); + byte[] paddedNonceBytes = new byte[8]; + byte[] originalNonceBytes = nonce.toByteArray(); + System.arraycopy(originalNonceBytes, 0, paddedNonceBytes, 8 - originalNonceBytes.length, originalNonceBytes.length); + System.out.println("Nonce Bytes length......: "+originalNonceBytes.length); + System.out.println("Templated Bytes.........: "+Util.bytesToHex(originalNonceBytes)); + System.out.println("Nonce bytes.............: "+Util.bytesToHex(paddedNonceBytes)); // Getting shared secret byte[] secret = publicKey.getKey().getPubKeyPoint().multiply(privateKey.getPrivKey()).normalize().getXCoord().getEncoded(); @@ -184,7 +191,8 @@ public class Memo implements ByteSerializable, JsonSerializable { // SHA-512 of shared secret byte[] ss = sha512.digest(secret); - byte[] seed = Bytes.concat(nonceBytes, Util.hexlify(Util.bytesToHex(ss))); + byte[] seed = Bytes.concat(paddedNonceBytes, Util.hexlify(Util.bytesToHex(ss))); + System.out.println("seed: "+Util.bytesToHex(seed)); // Calculating checksum byte[] sha256Msg = sha256.digest(message); @@ -215,7 +223,7 @@ public class Memo implements ByteSerializable, JsonSerializable { * @return: The plaintext version of the enrcrypted message. * @throws ChecksumException */ - public static String decryptMessage(ECKey privateKey, Address sourceAddress, long nonce, byte[] message) throws ChecksumException { + public static String decryptMessage(ECKey privateKey, Address sourceAddress, BigInteger nonce, byte[] message) throws ChecksumException { return decryptMessage(privateKey, sourceAddress.getPublicKey(), nonce, message); } @@ -236,7 +244,7 @@ public class Memo implements ByteSerializable, JsonSerializable { new byte[]{(byte) this.message.length}, this.message); } else { - byte[] nonceBytes = Util.revertLong(nonce); + byte[] nonceBytes = Util.revertBytes(nonce.toByteArray()); ECPoint senderPoint = ECKey.compressPoint(from.getPublicKey().getKey().getPubKeyPoint()); PublicKey senderPublicKey = new PublicKey(ECKey.fromPublicOnly(senderPoint)); @@ -288,7 +296,13 @@ public class Memo implements ByteSerializable, JsonSerializable { JsonObject jsonObject = json.getAsJsonObject(); String fromAddress = jsonObject.get(KEY_FROM).getAsString(); String toAddress = jsonObject.get(KEY_TO).getAsString(); - long nonce = Long.parseLong(jsonObject.get(KEY_NONCE).getAsString(), 16); + BigInteger nonce; + System.out.println("Trying to deserialize memo with nonce: <"+jsonObject.get(KEY_NONCE).getAsString()+">"); + try{ + nonce = new BigInteger(jsonObject.get(KEY_NONCE).getAsString(), 10); + }catch(NumberFormatException e){ + nonce = new BigInteger(jsonObject.get(KEY_NONCE).getAsString(), 16); + } String msg = jsonObject.get(KEY_MESSAGE).getAsString(); Memo memo = null; try{ diff --git a/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java b/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java index 1a176c3..a844699 100644 --- a/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java +++ b/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java @@ -11,6 +11,7 @@ import org.junit.Before; import org.junit.Test; import java.io.IOException; +import java.math.BigInteger; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; @@ -143,7 +144,7 @@ public class TransactionTest { PublicKey to2 = new PublicKey(ECKey.fromPublicOnly(new BrainKey(BILTHON_16_BRAIN_KEY, 0).getPublicKey())); // Creating memo - long nonce = 1; + BigInteger nonce = BigInteger.ONE; byte[] encryptedMessage = Memo.encryptMessage(sourcePrivateKey, to1, nonce, "another message"); Memo memo = new Memo(new Address(ECKey.fromPublicOnly(sourcePrivateKey.getPubKey())), new Address(to1.getKey()), nonce, encryptedMessage); diff --git a/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java b/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java index a8a3562..c22172f 100644 --- a/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java +++ b/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java @@ -10,6 +10,8 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import java.math.BigInteger; + import cy.agorise.graphenej.Address; import cy.agorise.graphenej.TestAccounts; import cy.agorise.graphenej.PublicKey; @@ -36,10 +38,10 @@ public class MemoTest { private String longerMessage = "testing now longer string with some special charaters é ç o ú á í Í mMno!!"; private byte[] shortEncryptedMessage = Util.hexToBytes("93c398e05f2a36a535f82880032a062d"); - private long shortEncryptedMessageNonce = 386471255144360L; + private BigInteger shortEncryptedMessageNonce = new BigInteger("386471255144360"); private byte[] longerEncryptedMessage = Util.hexToBytes("8ba8f5ed85ad9f7675bd30408a28d6f6ba138476d1e995dd61c01f0041ab25911e04d93fe4ce30e4f6c9a5134cceb67d653e140aa542da19ce2fc646bcde46e088da06a9327eaac79ffe8bc9d71d586195c04bb023995f18e66c9f9e5c6b0d7c"); - private long longEncryptedMessageNonce = 386469162162343L; + private BigInteger longEncryptedMessageNonce = new BigInteger("386469162162343"); @Before public void setUp() throws Exception { @@ -92,7 +94,7 @@ public class MemoTest { @Test public void shouldEncryptAndDecryptShortMessage(){ try { - long nonce = 1; + BigInteger nonce = BigInteger.ONE; byte[] encrypted = Memo.encryptMessage(sourcePrivate, destinationAddress, nonce, shortMessage); String decrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, encrypted); System.out.println("Short Decrypted Message: " + decrypted); @@ -105,7 +107,7 @@ public class MemoTest { @Test public void shouldEncryptAndDecryptLongerMessage(){ try{ - long nonce = 1; + BigInteger nonce = BigInteger.ONE; byte[] longEncrypted = Memo.encryptMessage(sourcePrivate, destinationAddress, nonce, longerMessage); String longDecrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, longEncrypted); System.out.println("Long Decrypted Message: " + longDecrypted); @@ -138,20 +140,20 @@ public class MemoTest { @Test public void shouldBeByteSerializable(){ String byteReference = "01029392096400eafe5f5ce7e2ab74134c3422fc49e5853bdeb298fb096258e26f6303d1fb8c7421db64d46fba7e36f428854ca06eff65698b293f37c7ffaa54e2c2b20100000000000000104ccbca3750fd2e531441de02b23fe6c7"; - byte[] encrypted = Memo.encryptMessage(sourcePrivate, destinationAddress, 1, shortMessage); - Memo memo = new Memo(sourceAddress, destinationAddress, 1, encrypted); + byte[] encrypted = Memo.encryptMessage(sourcePrivate, destinationAddress, BigInteger.ONE, shortMessage); + Memo memo = new Memo(sourceAddress, destinationAddress, BigInteger.ONE, encrypted); byte[] memoBytes = memo.toBytes(); assertEquals("Memo instance should generate a valid byte array", byteReference, Util.bytesToHex(memoBytes)); } @Test public void shouldDeserializeFromString(){ - String jsonMemo = "{\"from\": \"BTS6nB7gw1EawYXRofLvuivLsboVmh2inXroQgSQqYfAc5Bamk4Vq\",\"to\": \"BTS4xAQGg2ePLeDGZvQFpsh9CjMhQvRnVkPp6jPoE6neVPotRfZX9\",\"nonce\": \"15f2d8ee4ec23\",\"message\": \"b9aeb7632f1f4281eedcf28a684828a42d02de71254fb88e13ddcb9a79adf51d9770c58d7e7efcdbb1515f1136c3be3e\"}"; + String jsonMemo = "{\"from\": \"BTS6nB7gw1EawYXRofLvuivLsboVmh2inXroQgSQqYfAc5Bamk4Vq\",\"to\": \"BTS4xAQGg2ePLeDGZvQFpsh9CjMhQvRnVkPp6jPoE6neVPotRfZX9\",\"nonce\": \"8000000000000000\",\"message\": \"b9aeb7632f1f4281eedcf28a684828a42d02de71254fb88e13ddcb9a79adf51d9770c58d7e7efcdbb1515f1136c3be3e\"}"; GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(Memo.class, new Memo.MemoDeserializer()); Memo memo = gsonBuilder.create().fromJson(jsonMemo, Memo.class); Assert.assertEquals("Source address should match the serialized one", "BTS6nB7gw1EawYXRofLvuivLsboVmh2inXroQgSQqYfAc5Bamk4Vq", memo.getSource().toString()); Assert.assertEquals("Destination address should match the serialized one", "BTS4xAQGg2ePLeDGZvQFpsh9CjMhQvRnVkPp6jPoE6neVPotRfZX9", memo.getDestination().toString()); - Assert.assertEquals("Nonce should match serialized one", Long.parseLong("15f2d8ee4ec23", 16), memo.getNonce()); + Assert.assertEquals("Nonce should match serialized one", new BigInteger("8000000000000000", 10), memo.getNonce()); Assert.assertArrayEquals(Util.hexToBytes("b9aeb7632f1f4281eedcf28a684828a42d02de71254fb88e13ddcb9a79adf51d9770c58d7e7efcdbb1515f1136c3be3e"), memo.getByteMessage()); } } \ No newline at end of file From cf647a65badbd923d0f7eac777b228981a8df6cb Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 2 Nov 2017 17:32:55 -0500 Subject: [PATCH 2/4] Adding the memo in both test transfer operations --- .../src/test/java/cy/agorise/graphenej/TransactionTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java b/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java index a844699..6b9b1ad 100644 --- a/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java +++ b/graphenej/src/test/java/cy/agorise/graphenej/TransactionTest.java @@ -154,6 +154,7 @@ public class TransactionTest { .setSource(bilthon_7) .setDestination(bilthon_5) // bilthon-5 .setFee(new AssetAmount(UnsignedLong.valueOf(FEE_AMOUNT), CORE_ASSET)) + .setMemo(memo) .build(); // Creating operation 2 @@ -162,6 +163,7 @@ public class TransactionTest { .setSource(bilthon_7) // bilthon-15 .setDestination(bilthon_16) // bilthon-16 .setFee(new AssetAmount(UnsignedLong.valueOf(FEE_AMOUNT), CORE_ASSET)) + .setMemo(memo) .build(); From 0192728bd5b3e7a3549fabf5b6abef07835b6252 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 2 Nov 2017 17:38:01 -0500 Subject: [PATCH 3/4] Using hexlify in order to obtain the bytes from the BigInteger nonce --- .../cy/agorise/graphenej/objects/Memo.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java b/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java index 6472852..8a12ada 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java @@ -121,10 +121,8 @@ public class Memo implements ByteSerializable, JsonSerializable { MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); // Getting nonce bytes - byte[] paddedNonceBytes = new byte[8]; - byte[] originalNonceBytes = nonce.toByteArray(); - System.arraycopy(originalNonceBytes, 0, paddedNonceBytes, 8 - originalNonceBytes.length, originalNonceBytes.length); -// byte[] nonceBytes = nonce.toByteArray(); + String stringNonce = nonce.toString(); + byte[] nonceBytes = Arrays.copyOfRange(Util.hexlify(stringNonce), 0, stringNonce.length()); // Getting shared secret byte[] secret = publicKey.getKey().getPubKeyPoint().multiply(privateKey.getPrivKey()).normalize().getXCoord().getEncoded(); @@ -132,7 +130,7 @@ public class Memo implements ByteSerializable, JsonSerializable { // SHA-512 of shared secret byte[] ss = sha512.digest(secret); - byte[] seed = Bytes.concat(paddedNonceBytes, Util.hexlify(Util.bytesToHex(ss))); + byte[] seed = Bytes.concat(nonceBytes, Util.hexlify(Util.bytesToHex(ss))); // Calculating checksum byte[] sha256Msg = sha256.digest(message.getBytes()); @@ -178,12 +176,8 @@ public class Memo implements ByteSerializable, JsonSerializable { MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); // Getting nonce bytes - byte[] paddedNonceBytes = new byte[8]; - byte[] originalNonceBytes = nonce.toByteArray(); - System.arraycopy(originalNonceBytes, 0, paddedNonceBytes, 8 - originalNonceBytes.length, originalNonceBytes.length); - System.out.println("Nonce Bytes length......: "+originalNonceBytes.length); - System.out.println("Templated Bytes.........: "+Util.bytesToHex(originalNonceBytes)); - System.out.println("Nonce bytes.............: "+Util.bytesToHex(paddedNonceBytes)); + String stringNonce = nonce.toString(); + byte[] nonceBytes = Arrays.copyOfRange(Util.hexlify(stringNonce), 0, stringNonce.length()); // Getting shared secret byte[] secret = publicKey.getKey().getPubKeyPoint().multiply(privateKey.getPrivKey()).normalize().getXCoord().getEncoded(); @@ -191,8 +185,7 @@ public class Memo implements ByteSerializable, JsonSerializable { // SHA-512 of shared secret byte[] ss = sha512.digest(secret); - byte[] seed = Bytes.concat(paddedNonceBytes, Util.hexlify(Util.bytesToHex(ss))); - System.out.println("seed: "+Util.bytesToHex(seed)); + byte[] seed = Bytes.concat(nonceBytes, Util.hexlify(Util.bytesToHex(ss))); // Calculating checksum byte[] sha256Msg = sha256.digest(message); @@ -244,7 +237,12 @@ public class Memo implements ByteSerializable, JsonSerializable { new byte[]{(byte) this.message.length}, this.message); } else { - byte[] nonceBytes = Util.revertBytes(nonce.toByteArray()); + + byte[] paddedNonceBytes = new byte[8]; + byte[] originalNonceBytes = nonce.toByteArray(); + System.arraycopy(originalNonceBytes, 0, paddedNonceBytes, 8 - originalNonceBytes.length, originalNonceBytes.length); + byte[] nonceBytes = Util.revertBytes(paddedNonceBytes); +// byte[] nonceBytes = Util.revertBytes(nonce.toByteArray()); ECPoint senderPoint = ECKey.compressPoint(from.getPublicKey().getKey().getPubKeyPoint()); PublicKey senderPublicKey = new PublicKey(ECKey.fromPublicOnly(senderPoint)); From 3d4b2719bb1d4726ad318f5d7fa4c70953f5417b Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 2 Nov 2017 17:57:15 -0500 Subject: [PATCH 4/4] Fixing memo toJsonObject test and deserializing memo's nonce only as a decimal number --- .../java/cy/agorise/graphenej/objects/Memo.java | 13 ++++++------- .../java/cy/agorise/graphenej/objects/MemoTest.java | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java b/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java index 8a12ada..27e1b3a 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/objects/Memo.java @@ -294,13 +294,12 @@ public class Memo implements ByteSerializable, JsonSerializable { JsonObject jsonObject = json.getAsJsonObject(); String fromAddress = jsonObject.get(KEY_FROM).getAsString(); String toAddress = jsonObject.get(KEY_TO).getAsString(); - BigInteger nonce; - System.out.println("Trying to deserialize memo with nonce: <"+jsonObject.get(KEY_NONCE).getAsString()+">"); - try{ - nonce = new BigInteger(jsonObject.get(KEY_NONCE).getAsString(), 10); - }catch(NumberFormatException e){ - nonce = new BigInteger(jsonObject.get(KEY_NONCE).getAsString(), 16); - } + + // Apparently the nonce is always coming from the full node as a string containing a + // decimal number. This is at odds with the result of the #toJsonObject method + // which encodes this data in hexadecimal. + BigInteger nonce = new BigInteger(jsonObject.get(KEY_NONCE).getAsString(), 10); + String msg = jsonObject.get(KEY_MESSAGE).getAsString(); Memo memo = null; try{ diff --git a/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java b/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java index c22172f..190ce96 100644 --- a/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java +++ b/graphenej/src/test/java/cy/agorise/graphenej/objects/MemoTest.java @@ -132,7 +132,7 @@ public class MemoTest { JsonObject expected = new JsonObject(); expected.addProperty("from", new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(DumpedPrivateKey.fromBase58(null, TestAccounts.Bilthon16.WIF).getKey().getPrivKeyBytes()).getPubKey())).toString()); expected.addProperty("to", new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(DumpedPrivateKey.fromBase58(null, TestAccounts.Bilthon7.WIF).getKey().getPrivKeyBytes()).getPubKey())).toString()); - expected.addProperty("nonce", String.format("%d", shortEncryptedMessageNonce)); + expected.addProperty("nonce", String.format("%x", shortEncryptedMessageNonce)); expected.addProperty("message", "93c398e05f2a36a535f82880032a062d"); assertEquals("Memo instance should generate a valid JsonObject",expected, jsonObject); }