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
This commit is contained in:
parent
fe3e597453
commit
68fd9a749d
3 changed files with 149 additions and 7 deletions
|
@ -19,6 +19,7 @@ import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
|
||||||
import cy.agorise.crystalwallet.apigenerator.BitsharesFaucetApiGenerator;
|
import cy.agorise.crystalwallet.apigenerator.BitsharesFaucetApiGenerator;
|
||||||
import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
|
import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
|
||||||
import cy.agorise.crystalwallet.application.constant.BitsharesConstant;
|
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.CryptoNetEquivalentRequest;
|
||||||
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
|
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
|
||||||
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener;
|
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener;
|
||||||
|
@ -247,12 +248,22 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
|
||||||
AccountProperties prop = (AccountProperties) answer;
|
AccountProperties prop = (AccountProperties) answer;
|
||||||
//TODO change the way to compare keys
|
//TODO change the way to compare keys
|
||||||
BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0);
|
BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0);
|
||||||
System.out.println(bk.getPublicAddress("BTS").toString());
|
|
||||||
for(PublicKey activeKey : prop.owner.getKeyAuthList()){
|
for(PublicKey activeKey : prop.owner.getKeyAuthList()){
|
||||||
if((new Address(activeKey.getKey(),"BTS")).toString().equals(bk.getPublicAddress("BTS").toString())){
|
if((new Address(activeKey.getKey(),"BTS")).toString().equals(bk.getPublicAddress("BTS").toString())){
|
||||||
|
System.out.println("Mnemonic brainkey correct");
|
||||||
importRequest.setMnemonicIsCorrect(true);
|
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);
|
importRequest.setMnemonicIsCorrect(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,34 @@ package cy.agorise.crystalwallet.manager;
|
||||||
|
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
import org.bitcoinj.core.ECKey;
|
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.BufferedOutputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
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.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
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.AccountSeedDao;
|
||||||
import cy.agorise.crystalwallet.dao.CrystalDatabase;
|
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.Address;
|
||||||
import cy.agorise.graphenej.BrainKey;
|
import cy.agorise.graphenej.BrainKey;
|
||||||
import cy.agorise.graphenej.FileBin;
|
import cy.agorise.graphenej.FileBin;
|
||||||
|
import cy.agorise.graphenej.Util;
|
||||||
import cy.agorise.graphenej.api.GetKeyReferences;
|
import cy.agorise.graphenej.api.GetKeyReferences;
|
||||||
import cy.agorise.graphenej.models.backup.LinkedAccount;
|
import cy.agorise.graphenej.models.backup.LinkedAccount;
|
||||||
import cy.agorise.graphenej.models.backup.PrivateKeyBackup;
|
import cy.agorise.graphenej.models.backup.PrivateKeyBackup;
|
||||||
|
@ -81,12 +99,15 @@ public class FileBackupManager implements FileServiceRequestsListener {
|
||||||
if(fileName == null){
|
if(fileName == null){
|
||||||
fileName = bitsharesSeedName.accountName;
|
fileName = bitsharesSeedName.accountName;
|
||||||
}
|
}
|
||||||
BrainKey brainKey = new BrainKey(bitsharesSeedName.accountSeed, 0); //TODO chain to use BIP39
|
|
||||||
|
int sequence = 0;
|
||||||
//TODO adapt CHAIN ID
|
//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);
|
wallets.add(wallet);
|
||||||
|
|
||||||
|
BrainKey brainKey = new BrainKey(bitsharesSeedName.accountSeed, sequence); //TODO chain to use BIP39
|
||||||
PrivateKeyBackup keyBackup = new PrivateKeyBackup(brainKey.getPrivateKey().getPrivKeyBytes(),
|
PrivateKeyBackup keyBackup = new PrivateKeyBackup(brainKey.getPrivateKey().getPrivKeyBytes(),
|
||||||
brainKey.getSequenceNumber(), brainKey.getSequenceNumber(), wallet.getEncryptionKey(request.getPassword()));
|
sequence, sequence, wallet.getEncryptionKey(request.getPassword()));
|
||||||
keys.add(keyBackup);
|
keys.add(keyBackup);
|
||||||
LinkedAccount linkedAccount = new LinkedAccount(bitsharesSeedName.accountName, CryptoNetManager.getChaindId(CryptoNet.BITSHARES));
|
LinkedAccount linkedAccount = new LinkedAccount(bitsharesSeedName.accountName, CryptoNetManager.getChaindId(CryptoNet.BITSHARES));
|
||||||
accounts.add(linkedAccount);
|
accounts.add(linkedAccount);
|
||||||
|
@ -192,7 +213,7 @@ public class FileBackupManager implements FileServiceRequestsListener {
|
||||||
byteArray[i] = readBytes.get(i).byteValue();
|
byteArray[i] = readBytes.get(i).byteValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
WalletBackup walletBackup = FileBin.deserializeWalletBackup(byteArray,request.getPassword());
|
WalletBackup walletBackup = deserializeWalletBackup(byteArray,request.getPassword());
|
||||||
if(walletBackup == null){
|
if(walletBackup == null){
|
||||||
//TODO handle error
|
//TODO handle error
|
||||||
System.out.println("FileBackupManager error walletBackup null");
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
package cy.agorise.crystalwallet.models.seed;
|
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 org.bitcoinj.crypto.MnemonicCode;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import cy.agorise.crystalwallet.enums.SeedType;
|
import cy.agorise.crystalwallet.enums.SeedType;
|
||||||
import cy.agorise.crystalwallet.models.AccountSeed;
|
import cy.agorise.crystalwallet.models.AccountSeed;
|
||||||
|
@ -90,4 +92,19 @@ public class BIP39 extends AccountSeed{
|
||||||
public byte[] getSeed() {
|
public byte[] getSeed() {
|
||||||
return MnemonicCode.toSeed(Arrays.asList(this.getMasterSeed().split(" ")), "");
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue