From dc0c591a3d0fa346d52eba9c71b24f7ac1957e4c Mon Sep 17 00:00:00 2001 From: Henry Varona Date: Sun, 4 Dec 2016 21:23:52 -0400 Subject: [PATCH 1/2] First class memo implementation --- .../com/luminiasoft/bitshares/FileBin.java | 95 ++-------------- .../java/com/luminiasoft/bitshares/Memo.java | 78 ++++++++++++- .../java/com/luminiasoft/bitshares/Util.java | 104 ++++++++++++++++++ 3 files changed, 189 insertions(+), 88 deletions(-) diff --git a/src/main/java/com/luminiasoft/bitshares/FileBin.java b/src/main/java/com/luminiasoft/bitshares/FileBin.java index 797e50e..d5fca7a 100644 --- a/src/main/java/com/luminiasoft/bitshares/FileBin.java +++ b/src/main/java/com/luminiasoft/bitshares/FileBin.java @@ -49,14 +49,13 @@ public abstract class FileBin { byte[] finalKey = randomECKey.getPubKeyPoint().multiply(ECKey.fromPrivate(md.digest(password.getBytes("UTF-8"))).getPrivKey()).normalize().getXCoord().getEncoded(); MessageDigest md1 = MessageDigest.getInstance("SHA-512"); finalKey = md1.digest(finalKey); - byte[] rawData = decryptAES(rawDataEncripted, byteToString(finalKey).getBytes()); + byte[] rawData = Util.decryptAES(rawDataEncripted, Util.byteToString(finalKey).getBytes()); byte[] checksum = new byte[4]; System.arraycopy(rawData, 0, checksum, 0, 4); byte[] compressedData = new byte[rawData.length - 4]; System.arraycopy(rawData, 4, compressedData, 0, compressedData.length); - System.out.println("Despues:"+byteToString(compressedData)); byte[] wallet_object_bytes = Util.decompress(compressedData, Util.XZ); String wallet_string = new String(wallet_object_bytes, "UTF-8"); JsonObject wallet = new JsonParser().parse(wallet_string).getAsJsonObject(); @@ -69,7 +68,7 @@ public abstract class FileBin { byte[] encKey_enc = new BigInteger(wallet.get("encryption_key").getAsString(), 16).toByteArray(); byte[] temp = new byte[encKey_enc.length - (encKey_enc[0] == 0 ? 1 : 0)]; System.arraycopy(encKey_enc, (encKey_enc[0] == 0 ? 1 : 0), temp, 0, temp.length); - byte[] encKey = decryptAES(temp, password.getBytes("UTF-8")); + byte[] encKey = Util.decryptAES(temp, password.getBytes("UTF-8")); temp = new byte[encKey.length]; System.arraycopy(encKey, 0, temp, 0, temp.length); @@ -79,7 +78,7 @@ public abstract class FileBin { System.arraycopy(encBrain, 1, temp2, 0, temp2.length); encBrain = temp2; } - String BrainKey = new String((decryptAES(encBrain, temp)), "UTF-8"); + String BrainKey = new String((Util.decryptAES(encBrain, temp)), "UTF-8"); return BrainKey; @@ -105,15 +104,15 @@ public abstract class FileBin { //randomStrengthener.addEntropySource(new AndroidRandomSource()); SecureRandom secureRandom = randomStrengthener.generateAndSeedRandomNumberGenerator(); secureRandom.nextBytes(encKey); - byte[] encKey_enc = encryptAES(encKey, password.getBytes("UTF-8")); - byte[] encBrain = encryptAES(BrainKey.getBytes("ASCII"), encKey); + byte[] encKey_enc = Util.encryptAES(encKey, password.getBytes("UTF-8")); + byte[] encBrain = Util.encryptAES(BrainKey.getBytes("ASCII"), encKey); /** * Data to Store */ JsonObject wallet = new JsonObject(); - wallet.add("encryption_key", new JsonParser().parse(byteToString(encKey_enc))); - wallet.add("encrypted_brainkey", new JsonParser().parse(byteToString(encBrain))); + wallet.add("encryption_key", new JsonParser().parse(Util.byteToString(encKey_enc))); + wallet.add("encrypted_brainkey", new JsonParser().parse(Util.byteToString(encBrain))); JsonObject wallet_object = new JsonObject(); wallet_object.add("wallet", wallet); JsonArray accountNames = new JsonArray(); @@ -122,7 +121,6 @@ public abstract class FileBin { accountNames.add(jsonAccountName); wallet_object.add("linked_accounts", accountNames); byte[] compressedData = Util.compress(wallet_object.toString().getBytes("UTF-8"), Util.XZ); - System.out.println("Antes:"+byteToString(compressedData)); MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] checksum = md.digest(compressedData); byte[] rawData = new byte[compressedData.length + 4]; @@ -135,7 +133,7 @@ public abstract class FileBin { byte[] finalKey = randomECKey.getPubKeyPoint().multiply(ECKey.fromPrivate(md.digest(password.getBytes("UTF-8"))).getPrivKey()).normalize().getXCoord().getEncoded(); MessageDigest md1 = MessageDigest.getInstance("SHA-512"); finalKey = md1.digest(finalKey); - rawData = encryptAES(rawData, byteToString(finalKey).getBytes()); + rawData = Util.encryptAES(rawData, Util.byteToString(finalKey).getBytes()); byte[] result = new byte[rawData.length + randPubKey.length]; System.arraycopy(randPubKey, 0, result, 0, randPubKey.length); @@ -149,80 +147,5 @@ public abstract class FileBin { return null; } - private static byte[] encryptAES(byte[] input, byte[] key) { - try { - MessageDigest md = MessageDigest.getInstance("SHA-512"); - byte[] result = md.digest(key); - byte[] ivBytes = new byte[16]; - System.arraycopy(result, 32, ivBytes, 0, 16); - byte[] sksBytes = new byte[32]; - System.arraycopy(result, 0, sksBytes, 0, 32); - PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine())); - cipher.init(true, new ParametersWithIV(new KeyParameter(sksBytes), ivBytes)); - byte[] temp = new byte[input.length + (16 - (input.length % 16))]; - System.arraycopy(input, 0, temp, 0, input.length); - Arrays.fill(temp, input.length, temp.length, (byte) (16 - (input.length % 16))); - byte[] out = new byte[cipher.getOutputSize(temp.length)]; - int proc = cipher.processBytes(temp, 0, temp.length, out, 0); - cipher.doFinal(out, proc); - temp = new byte[out.length - 16]; - System.arraycopy(out, 0, temp, 0, temp.length); - return temp; - } catch (NoSuchAlgorithmException | DataLengthException | IllegalStateException | InvalidCipherTextException ex) { - } - return null; - } - - private static byte[] decryptAES(byte[] input, byte[] key) { - try { - MessageDigest md = MessageDigest.getInstance("SHA-512"); - byte[] result = md.digest(key); - byte[] ivBytes = new byte[16]; - System.arraycopy(result, 32, ivBytes, 0, 16); - byte[] sksBytes = new byte[32]; - System.arraycopy(result, 0, sksBytes, 0, 32); - PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine())); - cipher.init(false, new ParametersWithIV(new KeyParameter(sksBytes), ivBytes)); - - byte[] pre_out = new byte[cipher.getOutputSize(input.length)]; - int proc = cipher.processBytes(input, 0, input.length, pre_out, 0); - int proc2 = cipher.doFinal(pre_out, proc); - byte[] out = new byte[proc+proc2]; - System.arraycopy(pre_out, 0, out, 0, proc+proc2); - - //Unpadding - byte countByte = (byte)((byte)out[out.length-1] % 16); - int count = countByte & 0xFF; - - if ((count > 15) || (count <= 0)){ - return out; - } - - byte[] temp = new byte[count]; - System.arraycopy(out, out.length - count, temp, 0, temp.length); - byte[] temp2 = new byte[count]; - Arrays.fill(temp2, (byte) count); - if (Arrays.equals(temp, temp2)) { - temp = new byte[out.length - count]; - System.arraycopy(out, 0, temp, 0, out.length - count); - return temp; - } else { - return out; - } - } catch (NoSuchAlgorithmException | DataLengthException | IllegalStateException | InvalidCipherTextException ex) { - ex.printStackTrace(); - } - return null; - } - - public static String byteToString(byte[] input) { - StringBuilder result = new StringBuilder(); - for (byte in : input) { - if ((in & 0xff) < 0x10) { - result.append("0"); - } - result.append(Integer.toHexString(in & 0xff)); - } - return result.toString(); - } + } diff --git a/src/main/java/com/luminiasoft/bitshares/Memo.java b/src/main/java/com/luminiasoft/bitshares/Memo.java index 67650b3..1a2ddf0 100644 --- a/src/main/java/com/luminiasoft/bitshares/Memo.java +++ b/src/main/java/com/luminiasoft/bitshares/Memo.java @@ -1,15 +1,89 @@ package com.luminiasoft.bitshares; +import com.luminiasoft.bitshares.crypto.SecureRandomStrengthener; import com.luminiasoft.bitshares.interfaces.ByteSerializable; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import org.bitcoinj.core.ECKey; /** * Created by nelson on 11/9/16. */ public class Memo implements ByteSerializable { //TODO: Give this class a proper implementation - + private byte[] from; + private byte[] to; + private byte[] nonce = new byte[8]; + private byte[] message; + @Override public byte[] toBytes() { - return new byte[1]; + if ((this.from == null) || (this.to == null) || (this.nonce == null) ||(this.message == null)){ + return new byte[1]; + } + + byte[] result = new byte[this.from.length+this.to.length+this.nonce.length+this.message.length]; + System.arraycopy(this.from, 0, result, 0, this.from.length); + System.arraycopy(this.to, 0, result, this.from.length, this.to.length); + System.arraycopy(this.nonce, 0, result, this.from.length+this.to.length, this.nonce.length); + System.arraycopy(this.message, 0, result, this.from.length+this.to.length+this.nonce.length, this.message.length); + + return result; + } + + public Memo(){ + this.from = null; + this.nonce = null; + this.to = null; + this.message = null; + } + + public Memo(byte[] private_key, byte[] public_key, byte[] msg){ + this(private_key,public_key,msg,0); + } + + public Memo(byte[] private_key, byte[] public_key, byte[] msg, long custom_nonce){ + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + ECKey privateECKey = ECKey.fromPrivate(md.digest(private_key)); + + this.from = privateECKey.getPubKey(); + this.to = public_key; + + if (custom_nonce == 0){ + SecureRandomStrengthener randomStrengthener = SecureRandomStrengthener.getInstance(); + //randomStrengthener.addEntropySource(new AndroidRandomSource()); + SecureRandom secureRandom = randomStrengthener.generateAndSeedRandomNumberGenerator(); + secureRandom.nextBytes(nonce); + + long time = System.currentTimeMillis(); + + for (int i = 7;i >=1; i--){ + this.nonce[i] = (byte)(time&0xff); + time = time/0x100; + } + } else { + for (int i = 7;i >=0; i--){ + this.nonce[i] = (byte)(custom_nonce&0xff); + custom_nonce = custom_nonce/0x100; + } + } + + + byte[] secret = privateECKey.getPubKeyPoint().multiply(ECKey.fromPublicOnly(md.digest(public_key)).getPrivKey()).normalize().getXCoord().getEncoded(); + byte[] finalKey = new byte[secret.length + this.nonce.length]; + + byte[] sha256Msg = md.digest(msg); + byte[] serialChecksum = new byte[4]; + System.arraycopy(sha256Msg, 0, serialChecksum, 0, 4); + byte[] msgFinal = new byte[serialChecksum.length + msg.length]; + System.arraycopy(serialChecksum, 0, msgFinal, 0, serialChecksum.length); + System.arraycopy(msg, 0, msgFinal, serialChecksum.length, msg.length); + + this.message = Util.encryptAES(msgFinal, finalKey); + } catch (NoSuchAlgorithmException ex){ + + } } } diff --git a/src/main/java/com/luminiasoft/bitshares/Util.java b/src/main/java/com/luminiasoft/bitshares/Util.java index 03d00e8..ebdd535 100644 --- a/src/main/java/com/luminiasoft/bitshares/Util.java +++ b/src/main/java/com/luminiasoft/bitshares/Util.java @@ -11,8 +11,18 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.engines.AESFastEngine; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; /** * Class used to encapsulate common utility methods @@ -114,4 +124,98 @@ public class Util { } return null; } + + /** + * Function to encrypt a message with AES + * @param input data to encrypt + * @param key key for encryption + * @return AES Encription of input + */ + public static byte[] encryptAES(byte[] input, byte[] key) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-512"); + byte[] result = md.digest(key); + byte[] ivBytes = new byte[16]; + System.arraycopy(result, 32, ivBytes, 0, 16); + byte[] sksBytes = new byte[32]; + System.arraycopy(result, 0, sksBytes, 0, 32); + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine())); + cipher.init(true, new ParametersWithIV(new KeyParameter(sksBytes), ivBytes)); + byte[] temp = new byte[input.length + (16 - (input.length % 16))]; + System.arraycopy(input, 0, temp, 0, input.length); + Arrays.fill(temp, input.length, temp.length, (byte) (16 - (input.length % 16))); + byte[] out = new byte[cipher.getOutputSize(temp.length)]; + int proc = cipher.processBytes(temp, 0, temp.length, out, 0); + cipher.doFinal(out, proc); + temp = new byte[out.length - 16]; + System.arraycopy(out, 0, temp, 0, temp.length); + return temp; + } catch (NoSuchAlgorithmException | DataLengthException | IllegalStateException | InvalidCipherTextException ex) { + } + return null; + } + + /** + * Function to decrypt a message with AES encryption + * @param input data to decrypt + * @param key key for decryption + * @return input decrypted with AES. Null if the decrypt failed (Bad Key) + */ + public static byte[] decryptAES(byte[] input, byte[] key) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-512"); + byte[] result = md.digest(key); + byte[] ivBytes = new byte[16]; + System.arraycopy(result, 32, ivBytes, 0, 16); + byte[] sksBytes = new byte[32]; + System.arraycopy(result, 0, sksBytes, 0, 32); + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine())); + cipher.init(false, new ParametersWithIV(new KeyParameter(sksBytes), ivBytes)); + + byte[] pre_out = new byte[cipher.getOutputSize(input.length)]; + int proc = cipher.processBytes(input, 0, input.length, pre_out, 0); + int proc2 = cipher.doFinal(pre_out, proc); + byte[] out = new byte[proc+proc2]; + System.arraycopy(pre_out, 0, out, 0, proc+proc2); + + //Unpadding + byte countByte = (byte)((byte)out[out.length-1] % 16); + int count = countByte & 0xFF; + + if ((count > 15) || (count <= 0)){ + return out; + } + + byte[] temp = new byte[count]; + System.arraycopy(out, out.length - count, temp, 0, temp.length); + byte[] temp2 = new byte[count]; + Arrays.fill(temp2, (byte) count); + if (Arrays.equals(temp, temp2)) { + temp = new byte[out.length - count]; + System.arraycopy(out, 0, temp, 0, out.length - count); + return temp; + } else { + return out; + } + } catch (NoSuchAlgorithmException | DataLengthException | IllegalStateException | InvalidCipherTextException ex) { + ex.printStackTrace(); + } + return null; + } + + /** + * Transform an array of bytes to an hex String representation + * @param input array of bytes to transform as a string + * @return Input as a String + */ + public static String byteToString(byte[] input) { + StringBuilder result = new StringBuilder(); + for (byte in : input) { + if ((in & 0xff) < 0x10) { + result.append("0"); + } + result.append(Integer.toHexString(in & 0xff)); + } + return result.toString(); + } } From 9d4ca57823604965fa940c07c0752249d701cd59 Mon Sep 17 00:00:00 2001 From: Henry Varona Date: Mon, 5 Dec 2016 21:34:21 -0400 Subject: [PATCH 2/2] EncodeMessage y DecodeMessage functions implemented in Memo --- .../java/com/luminiasoft/bitshares/Memo.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/luminiasoft/bitshares/Memo.java b/src/main/java/com/luminiasoft/bitshares/Memo.java index 1a2ddf0..5e99267 100644 --- a/src/main/java/com/luminiasoft/bitshares/Memo.java +++ b/src/main/java/com/luminiasoft/bitshares/Memo.java @@ -39,11 +39,11 @@ public class Memo implements ByteSerializable { this.message = null; } - public Memo(byte[] private_key, byte[] public_key, byte[] msg){ - this(private_key,public_key,msg,0); + public void encodeMessage(byte[] private_key, byte[] public_key, byte[] msg){ + this.encodeMessage(private_key,public_key,msg,0); } - public Memo(byte[] private_key, byte[] public_key, byte[] msg, long custom_nonce){ + public void encodeMessage(byte[] private_key, byte[] public_key, byte[] msg, long custom_nonce){ try { MessageDigest md = MessageDigest.getInstance("SHA-256"); ECKey privateECKey = ECKey.fromPrivate(md.digest(private_key)); @@ -73,7 +73,10 @@ public class Memo implements ByteSerializable { byte[] secret = privateECKey.getPubKeyPoint().multiply(ECKey.fromPublicOnly(md.digest(public_key)).getPrivKey()).normalize().getXCoord().getEncoded(); byte[] finalKey = new byte[secret.length + this.nonce.length]; - + System.arraycopy(secret, 0, finalKey, 0, secret.length); + System.arraycopy(this.nonce, 0, finalKey, secret.length, this.nonce.length); + + byte[] sha256Msg = md.digest(msg); byte[] serialChecksum = new byte[4]; System.arraycopy(sha256Msg, 0, serialChecksum, 0, 4); @@ -86,4 +89,26 @@ public class Memo implements ByteSerializable { } } + + public void decodeMessage(byte[] private_key, byte[] public_key, byte[] msg, byte[] nonce){ + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + ECKey privateECKey = ECKey.fromPrivate(md.digest(private_key)); + this.to = privateECKey.getPubKey(); + this.from = public_key; + this.nonce = nonce; + + byte[] secret = privateECKey.getPubKeyPoint().multiply(ECKey.fromPublicOnly(md.digest(public_key)).getPrivKey()).normalize().getXCoord().getEncoded(); + byte[] finalKey = new byte[secret.length + this.nonce.length]; + System.arraycopy(secret, 0, finalKey, 0, secret.length); + System.arraycopy(this.nonce, 0, finalKey, secret.length, this.nonce.length); + + byte[] msgFinal = Util.decryptAES(msg, finalKey); + byte[] decodedMsg = new byte[msgFinal.length-4]; + System.arraycopy(msgFinal, 4, decodedMsg, 0, decodedMsg.length); + this.message = decodedMsg; + } catch (NoSuchAlgorithmException ex){ + + } + } }