From 68fd9a749d4fbf7a28eca472d67b5552a0640454 Mon Sep 17 00:00:00 2001 From: henry Date: Sun, 8 Apr 2018 22:10:26 -0400 Subject: [PATCH] Added a copy of the graphenej library to fix the decompress reading EOIFException problem Added a getter for the active key of bitshares using bip39 seeds. Change the Bitshares account validator to implement the bip39 --- .../manager/BitsharesAccountManager.java | 13 +- .../manager/FileBackupManager.java | 122 +++++++++++++++++- .../crystalwallet/models/seed/BIP39.java | 21 ++- 3 files changed, 149 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java b/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java index ee0478f..1c4ab74 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java +++ b/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java @@ -19,6 +19,7 @@ import cy.agorise.crystalwallet.apigenerator.ApiRequestListener; import cy.agorise.crystalwallet.apigenerator.BitsharesFaucetApiGenerator; import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; import cy.agorise.crystalwallet.application.constant.BitsharesConstant; +import cy.agorise.crystalwallet.models.seed.BIP39; import cy.agorise.crystalwallet.requestmanagers.CryptoNetEquivalentRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener; @@ -247,12 +248,22 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI AccountProperties prop = (AccountProperties) answer; //TODO change the way to compare keys BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0); - System.out.println(bk.getPublicAddress("BTS").toString()); for(PublicKey activeKey : prop.owner.getKeyAuthList()){ if((new Address(activeKey.getKey(),"BTS")).toString().equals(bk.getPublicAddress("BTS").toString())){ + System.out.println("Mnemonic brainkey correct"); importRequest.setMnemonicIsCorrect(true); + return; } } + BIP39 bip39 = new BIP39(-1, importRequest.getMnemonic()); + for(PublicKey activeKey : prop.active.getKeyAuthList()){ + if((new Address(activeKey.getKey(),"BTS")).toString().equals(new Address(ECKey.fromPublicOnly(bip39.getBitsharesActiveKey(0).getPubKey())).toString())){ + System.out.println("Mnemonic BIP39 correct"); + importRequest.setMnemonicIsCorrect(true); + return; + } + } + System.out.println("Mnemonic incorrect"); importRequest.setMnemonicIsCorrect(false); } diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/FileBackupManager.java b/app/src/main/java/cy/agorise/crystalwallet/manager/FileBackupManager.java index 3a40ca9..2195b68 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/manager/FileBackupManager.java +++ b/app/src/main/java/cy/agorise/crystalwallet/manager/FileBackupManager.java @@ -2,17 +2,34 @@ package cy.agorise.crystalwallet.manager; import android.os.Environment; +import com.google.common.primitives.Bytes; +import com.google.gson.GsonBuilder; + import org.bitcoinj.core.ECKey; +import org.tukaani.xz.CorruptedInputException; +import org.tukaani.xz.LZMAInputStream; +import org.tukaani.xz.XZInputStream; import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; +import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import cy.agorise.crystalwallet.dao.AccountSeedDao; import cy.agorise.crystalwallet.dao.CrystalDatabase; @@ -32,6 +49,7 @@ import cy.agorise.crystalwallet.requestmanagers.ValidateImportBitsharesAccountRe import cy.agorise.graphenej.Address; import cy.agorise.graphenej.BrainKey; import cy.agorise.graphenej.FileBin; +import cy.agorise.graphenej.Util; import cy.agorise.graphenej.api.GetKeyReferences; import cy.agorise.graphenej.models.backup.LinkedAccount; import cy.agorise.graphenej.models.backup.PrivateKeyBackup; @@ -81,12 +99,15 @@ public class FileBackupManager implements FileServiceRequestsListener { if(fileName == null){ fileName = bitsharesSeedName.accountName; } - BrainKey brainKey = new BrainKey(bitsharesSeedName.accountSeed, 0); //TODO chain to use BIP39 + + int sequence = 0; //TODO adapt CHAIN ID - Wallet wallet = new Wallet(bitsharesSeedName.accountName, brainKey.getBrainKey(), brainKey.getSequenceNumber(), CryptoNetManager.getChaindId(CryptoNet.BITSHARES), request.getPassword()); + Wallet wallet = new Wallet(bitsharesSeedName.accountName, bitsharesSeedName.accountSeed, sequence, CryptoNetManager.getChaindId(CryptoNet.BITSHARES), request.getPassword()); wallets.add(wallet); + + BrainKey brainKey = new BrainKey(bitsharesSeedName.accountSeed, sequence); //TODO chain to use BIP39 PrivateKeyBackup keyBackup = new PrivateKeyBackup(brainKey.getPrivateKey().getPrivKeyBytes(), - brainKey.getSequenceNumber(), brainKey.getSequenceNumber(), wallet.getEncryptionKey(request.getPassword())); + sequence, sequence, wallet.getEncryptionKey(request.getPassword())); keys.add(keyBackup); LinkedAccount linkedAccount = new LinkedAccount(bitsharesSeedName.accountName, CryptoNetManager.getChaindId(CryptoNet.BITSHARES)); accounts.add(linkedAccount); @@ -192,7 +213,7 @@ public class FileBackupManager implements FileServiceRequestsListener { byteArray[i] = readBytes.get(i).byteValue(); } - WalletBackup walletBackup = FileBin.deserializeWalletBackup(byteArray,request.getPassword()); + WalletBackup walletBackup = deserializeWalletBackup(byteArray,request.getPassword()); if(walletBackup == null){ //TODO handle error System.out.println("FileBackupManager error walletBackup null"); @@ -262,5 +283,98 @@ public class FileBackupManager implements FileServiceRequestsListener { } + /** + * This part is copied from the graphenej library, to edit possible error. + * TODO in graphenej library to catch EOFException reading files + */ + + public static WalletBackup deserializeWalletBackup(byte[] input, String password){ + try{ + byte[] publicKey = new byte[33]; + byte[] rawDataEncripted = new byte[input.length - 33]; + + System.arraycopy(input, 0, publicKey, 0, 33); + System.arraycopy(input, 33, rawDataEncripted, 0, rawDataEncripted.length); + + MessageDigest md = MessageDigest.getInstance("SHA-256"); + + ECKey randomECKey = ECKey.fromPublicOnly(publicKey); + 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[] decryptedData = Util.decryptAES(rawDataEncripted, Util.bytesToHex(finalKey).getBytes()); + + byte[] checksum = new byte[4]; + System.arraycopy(decryptedData, 0, checksum, 0, 4); + byte[] compressedData = new byte[decryptedData.length - 4]; + System.arraycopy(decryptedData, 4, compressedData, 0, compressedData.length); + + byte[] decompressedData = decompress(compressedData, Util.LZMA); + String walletString = new String(decompressedData, "UTF-8"); + System.out.println("Wallet str: "+walletString); + return new GsonBuilder().create().fromJson(walletString, WalletBackup.class); + }catch(NoSuchAlgorithmException e){ + System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage()); + } catch (UnsupportedEncodingException e) { + System.out.println("UnsupportedEncodingException. Msg: "+e.getMessage()); + } + return null; + } + + public static byte[] decompress(byte[] inputBytes, int which) { + InputStream in = null; + try { + System.out.println("Bytes: "+Util.bytesToHex(inputBytes)); + ByteArrayInputStream input = new ByteArrayInputStream(inputBytes); + ByteArrayOutputStream output = new ByteArrayOutputStream(16*2048); + if(which == Util.XZ) { + in = new XZInputStream(input); + }else if(which == Util.LZMA){ + in = new LZMAInputStream(input); + } + int size; + try{ + while ((size = in.read()) != -1) { + output.write(size); + } + }catch(CorruptedInputException e){ + // Taking property byte + byte[] properties = Arrays.copyOfRange(inputBytes, 0, 1); + // Taking dict size bytes + byte[] dictSize = Arrays.copyOfRange(inputBytes, 1, 5); + // Taking uncompressed size bytes + byte[] uncompressedSize = Arrays.copyOfRange(inputBytes, 5, 13); + + // Reversing bytes in header + byte[] header = Bytes.concat(properties, Util.revertBytes(dictSize), Util.revertBytes(uncompressedSize)); + byte[] payload = Arrays.copyOfRange(inputBytes, 13, inputBytes.length); + + // Trying again + input = new ByteArrayInputStream(Bytes.concat(header, payload)); + output = new ByteArrayOutputStream(2048); + if(which == Util.XZ) { + in = new XZInputStream(input); + }else if(which == Util.LZMA){ + in = new LZMAInputStream(input); + } + try{ + while ((size = in.read()) != -1) { + output.write(size); + } + }catch(CorruptedInputException ex){ + System.out.println("CorruptedInputException. Msg: "+ex.getMessage()); + } + }catch(EOFException e){ + + } + in.close(); + return output.toByteArray(); + } catch (IOException ex) { + Logger.getLogger(Util.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + + } diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/seed/BIP39.java b/app/src/main/java/cy/agorise/crystalwallet/models/seed/BIP39.java index 9aac4a9..9b6e609 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/models/seed/BIP39.java +++ b/app/src/main/java/cy/agorise/crystalwallet/models/seed/BIP39.java @@ -1,13 +1,15 @@ package cy.agorise.crystalwallet.models.seed; +import org.bitcoinj.core.ECKey; +import org.bitcoinj.crypto.ChildNumber; +import org.bitcoinj.crypto.DeterministicKey; +import org.bitcoinj.crypto.HDKeyDerivation; import org.bitcoinj.crypto.MnemonicCode; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import cy.agorise.crystalwallet.enums.SeedType; import cy.agorise.crystalwallet.models.AccountSeed; @@ -90,4 +92,19 @@ public class BIP39 extends AccountSeed{ public byte[] getSeed() { return MnemonicCode.toSeed(Arrays.asList(this.getMasterSeed().split(" ")), ""); } + + public ECKey getBitsharesActiveKey(int number){ + DeterministicKey masterKey = HDKeyDerivation.createMasterPrivateKey(this.getSeed()); + DeterministicKey purposeKey = HDKeyDerivation.deriveChildKey(masterKey, + new ChildNumber(48, true)); + DeterministicKey networkKey = HDKeyDerivation.deriveChildKey(purposeKey, + new ChildNumber(1, true)); + DeterministicKey accountIndexKey = HDKeyDerivation.deriveChildKey(networkKey, + new ChildNumber(0, true)); + DeterministicKey permission = HDKeyDerivation.deriveChildKey(accountIndexKey, + new ChildNumber(1, true)); + DeterministicKey address = HDKeyDerivation.deriveChildKey(permission, + new ChildNumber(number, false)); //TODO implement multiple Address and accounts + return org.bitcoinj.core.ECKey.fromPrivate(address.getPrivKeyBytes()); + } }