Merge branch 'master' of https://github.com/Agorise/crystal-wallet-android
This commit is contained in:
commit
cf5aa56d32
6 changed files with 236 additions and 7 deletions
1
app/src/main/assets/bip39dict.txt
Normal file
1
app/src/main/assets/bip39dict.txt
Normal file
File diff suppressed because one or more lines are too long
1
app/src/main/assets/brainkeydict.txt
Normal file
1
app/src/main/assets/brainkeydict.txt
Normal file
File diff suppressed because one or more lines are too long
48
app/src/main/assets/correctedI18nCurrencySymbols.properties
Normal file
48
app/src/main/assets/correctedI18nCurrencySymbols.properties
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Note that these are the currency symbols to use for the specified code when displaying in a DIFFERENT locale than
|
||||||
|
# the home locale of the currency. This file can be edited as needed. In addition, if in some case one specific locale
|
||||||
|
# would use a symbol DIFFERENT than the standard international one listed here, then an additional properties file
|
||||||
|
# can be added making use of the standard ResourceBundle loading algorithm. For example, if we decided we wanted to
|
||||||
|
# show US dollars as just $ instead of US$ when in the UK, we could create a file i18nCurrencySymbols_en_GB.properties
|
||||||
|
# with the entry USD=$
|
||||||
|
ARS=$AR
|
||||||
|
AUD=AU$
|
||||||
|
BOB=$b
|
||||||
|
BRL=R$
|
||||||
|
CAD=CAN$
|
||||||
|
CLP=Ch$
|
||||||
|
COP=COL$
|
||||||
|
CRC=\u20A1
|
||||||
|
HRK=kn
|
||||||
|
CZK=K\u010D
|
||||||
|
DOP=RD$
|
||||||
|
XCD=EC$
|
||||||
|
EUR=\u20AC
|
||||||
|
GTQ=Q
|
||||||
|
GYD=G$
|
||||||
|
HNL=L
|
||||||
|
HKD=HK$
|
||||||
|
HUF=Ft
|
||||||
|
INR=\u20B9
|
||||||
|
IDR=Rp
|
||||||
|
ILS=\u20AA
|
||||||
|
JMD=J$
|
||||||
|
JPY=JP\u00A5
|
||||||
|
KRW=\u20A9
|
||||||
|
NZD=NZ$
|
||||||
|
NIO=C$
|
||||||
|
PAB=B/.
|
||||||
|
PYG=Gs
|
||||||
|
PEN=S/.
|
||||||
|
PHP=\u20B1
|
||||||
|
PLN=\u007A\u0142
|
||||||
|
RON=lei
|
||||||
|
SGD=S$
|
||||||
|
ZAR=R
|
||||||
|
TWD=NT$
|
||||||
|
THB=\u0E3F
|
||||||
|
TTD=TT$
|
||||||
|
GBP=\u00A3
|
||||||
|
USD=$
|
||||||
|
UYU=$U
|
||||||
|
VEF=Bs
|
||||||
|
VND=\u20AB
|
|
@ -1,13 +1,26 @@
|
||||||
package cy.agorise.crystalwallet.models;
|
package cy.agorise.crystalwallet.models;
|
||||||
|
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import android.arch.persistence.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import android.arch.persistence.room.Entity;
|
||||||
import android.arch.persistence.room.PrimaryKey;
|
import android.arch.persistence.room.PrimaryKey;
|
||||||
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v7.recyclerview.extensions.DiffCallback;
|
import android.support.v7.recyclerview.extensions.DiffCallback;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.ECKey;
|
||||||
|
import org.bitcoinj.crypto.HDKeyDerivation;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
import cy.agorise.crystalwallet.enums.SeedType;
|
import cy.agorise.crystalwallet.enums.SeedType;
|
||||||
|
import cy.agorise.crystalwallet.models.seed.BIP39;
|
||||||
|
import cy.agorise.graphenej.BrainKey;
|
||||||
|
|
||||||
|
import static cy.agorise.crystalwallet.enums.SeedType.BRAINKEY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a type of crypto seed for HD wallets
|
* Represents a type of crypto seed for HD wallets
|
||||||
|
@ -97,4 +110,46 @@ public class AccountSeed {
|
||||||
return mMasterSeed.equals(that.mMasterSeed);
|
return mMasterSeed.equals(that.mMasterSeed);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AccountSeed getAccountSeed(SeedType type, Context context){
|
||||||
|
BufferedReader reader = null;
|
||||||
|
switch (type) {
|
||||||
|
case BRAINKEY:
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new InputStreamReader(context.getAssets().open("brainkeydict.txt"), "UTF-8"));
|
||||||
|
|
||||||
|
String dictionary = reader.readLine();
|
||||||
|
|
||||||
|
String brainKeySuggestion = BrainKey.suggest(dictionary);
|
||||||
|
AccountSeed seed = new AccountSeed();
|
||||||
|
seed.setMasterSeed(brainKeySuggestion);
|
||||||
|
seed.setType(BRAINKEY);
|
||||||
|
return seed;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
case BIP39:
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new InputStreamReader(context.getAssets().open("bip39dict.txt"), "UTF-8"));
|
||||||
|
String dictionary = reader.readLine();
|
||||||
|
//TODO save in db
|
||||||
|
return new BIP39(dictionary.split(","));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECKey getPrivateKey(){
|
||||||
|
switch (this.type) {
|
||||||
|
case BRAINKEY:
|
||||||
|
return new BrainKey(this.mMasterSeed,0).getPrivateKey();
|
||||||
|
case BIP39:
|
||||||
|
return HDKeyDerivation.createMasterPrivateKey(new BIP39(mId, mMasterSeed).getSeed());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ package cy.agorise.crystalwallet.models;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.bitcoinj.core.ECKey;
|
import org.bitcoinj.core.ECKey;
|
||||||
|
import org.bitcoinj.crypto.ChildNumber;
|
||||||
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
|
import org.bitcoinj.crypto.HDKeyDerivation;
|
||||||
|
|
||||||
import cy.agorise.crystalwallet.dao.CrystalDatabase;
|
import cy.agorise.crystalwallet.dao.CrystalDatabase;
|
||||||
import cy.agorise.crystalwallet.enums.SeedType;
|
import cy.agorise.crystalwallet.enums.SeedType;
|
||||||
|
@ -55,10 +58,20 @@ public class GrapheneAccount extends CryptoNetAccount {
|
||||||
if(seed == null)
|
if(seed == null)
|
||||||
return null;
|
return null;
|
||||||
if(seed.getType().equals(SeedType.BRAINKEY)){
|
if(seed.getType().equals(SeedType.BRAINKEY)){
|
||||||
return new BrainKey(seed.getMasterSeed(),0).getPrivateKey();
|
return seed.getPrivateKey();
|
||||||
}else{
|
}else{
|
||||||
//TODO implement slip48
|
DeterministicKey masterKey = (DeterministicKey) seed.getPrivateKey();
|
||||||
return null;
|
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(0, true));
|
||||||
|
DeterministicKey address = HDKeyDerivation.deriveChildKey(permission,
|
||||||
|
new ChildNumber(0, true));
|
||||||
|
return address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +85,17 @@ public class GrapheneAccount extends CryptoNetAccount {
|
||||||
if(seed.getType().equals(SeedType.BRAINKEY)){
|
if(seed.getType().equals(SeedType.BRAINKEY)){
|
||||||
return new BrainKey(seed.getMasterSeed(),0).getPrivateKey();
|
return new BrainKey(seed.getMasterSeed(),0).getPrivateKey();
|
||||||
}else{
|
}else{
|
||||||
//TODO implement slip48
|
DeterministicKey masterKey = (DeterministicKey) seed.getPrivateKey();
|
||||||
return null;
|
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));
|
||||||
|
return HDKeyDerivation.deriveChildKey(permission,
|
||||||
|
new ChildNumber(0, true)); //TODO implement multiple Address and accounts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,8 +109,17 @@ public class GrapheneAccount extends CryptoNetAccount {
|
||||||
if(seed.getType().equals(SeedType.BRAINKEY)){
|
if(seed.getType().equals(SeedType.BRAINKEY)){
|
||||||
return new BrainKey(seed.getMasterSeed(),0).getPrivateKey();
|
return new BrainKey(seed.getMasterSeed(),0).getPrivateKey();
|
||||||
}else{
|
}else{
|
||||||
//TODO implement slip48
|
DeterministicKey masterKey = (DeterministicKey) seed.getPrivateKey();
|
||||||
return null;
|
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(3, true));
|
||||||
|
return HDKeyDerivation.deriveChildKey(permission,
|
||||||
|
new ChildNumber(0, true)); //TODO implement multiple Address and accounts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package cy.agorise.crystalwallet.models.seed;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import cy.agorise.graphenej.crypto.SecureRandomGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by henry on 6/1/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class BIP39 extends AccountSeed{
|
||||||
|
/**
|
||||||
|
* Teh amount of words for this seed
|
||||||
|
*/
|
||||||
|
private final int wmWordNumber = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor from the dataabse
|
||||||
|
* @param id The id on the database of this seed
|
||||||
|
*/
|
||||||
|
public BIP39(long id, String words) {
|
||||||
|
this.setId(id);
|
||||||
|
this.setType(SeedType.BIP39);
|
||||||
|
this.setMasterSeed(words);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that generates the list of words
|
||||||
|
* @param wordList Dictionary to be used
|
||||||
|
*/
|
||||||
|
public BIP39(String[] wordList) {
|
||||||
|
try {
|
||||||
|
this.setType(SeedType.BIP39);
|
||||||
|
int entropySize = ((this.wmWordNumber * 11) / 8) * 8;
|
||||||
|
// We get a true random number
|
||||||
|
SecureRandom secureRandom = SecureRandomGenerator.getSecureRandom();
|
||||||
|
byte[] entropy = new byte[entropySize / 8];
|
||||||
|
secureRandom.nextBytes(entropy);
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||||
|
byte[] shaResult = md.digest(entropy);
|
||||||
|
int mask = 0x80;
|
||||||
|
int cheksum = 0;
|
||||||
|
for (int i = 0; i < entropySize / 32; i++) {
|
||||||
|
cheksum = cheksum ^ (shaResult[0] & mask);
|
||||||
|
mask = mask / 2;
|
||||||
|
}
|
||||||
|
int[] wordsIndex = new int[(entropySize / 11) + 1];
|
||||||
|
for (int i = 0; i < wordsIndex.length; i++) {
|
||||||
|
wordsIndex[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastIndex = 0;
|
||||||
|
int lastBit = 0;
|
||||||
|
for (int i = 0; i < entropy.length; i++) {
|
||||||
|
for (int j = 7; j >= 0; j--) {
|
||||||
|
if (lastBit == 11) {
|
||||||
|
lastBit = 0;
|
||||||
|
++lastIndex;
|
||||||
|
}
|
||||||
|
wordsIndex[lastIndex] = wordsIndex[lastIndex] ^ ((int) (Math.pow(2, 11 - (lastBit + 1))) * (entropy[i] & ((int) Math.pow(2, j))) >> j);
|
||||||
|
++lastBit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 7; j >= 0; j--) {
|
||||||
|
if (lastBit == 11) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wordsIndex[lastIndex] = wordsIndex[lastIndex] ^ ((int) (Math.pow(2, 11 - (lastBit + 1))) * (cheksum & ((int) Math.pow(2, j))) >> j);
|
||||||
|
++lastBit;
|
||||||
|
}
|
||||||
|
StringBuilder words = new StringBuilder();
|
||||||
|
for (int windex : wordsIndex) {
|
||||||
|
words.append(wordList[windex]).append(" ");
|
||||||
|
}
|
||||||
|
words.deleteCharAt(words.length() - 1);
|
||||||
|
this.setMasterSeed(words.toString());
|
||||||
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSeed() {
|
||||||
|
return MnemonicCode.toSeed(Arrays.asList(this.getMasterSeed().split(" ")), "");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue