├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── .project ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── consenlabs │ │ └── tokencore │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── org │ │ └── consenlabs │ │ └── tokencore │ │ ├── foundation │ │ ├── crypto │ │ │ ├── AES.java │ │ │ ├── CipherParams.java │ │ │ ├── Crypto.java │ │ │ ├── EncPair.java │ │ │ ├── Hash.java │ │ │ ├── KDFParams.java │ │ │ ├── Keccak.java │ │ │ ├── Multihash.java │ │ │ ├── PBKDF2Crypto.java │ │ │ ├── PBKDF2Params.java │ │ │ ├── SCryptCrypto.java │ │ │ └── SCryptParams.java │ │ ├── rlp │ │ │ ├── RlpEncoder.java │ │ │ ├── RlpList.java │ │ │ ├── RlpString.java │ │ │ └── RlpType.java │ │ └── utils │ │ │ ├── ByteUtil.java │ │ │ ├── CachedDerivedKey.java │ │ │ ├── DateUtil.java │ │ │ ├── MnemonicUtil.java │ │ │ └── NumericUtil.java │ │ └── wallet │ │ ├── Identity.java │ │ ├── KeystoreStorage.java │ │ ├── Wallet.java │ │ ├── WalletManager.java │ │ ├── address │ │ ├── AddressCreator.java │ │ ├── AddressCreatorManager.java │ │ ├── BitcoinAddressCreator.java │ │ ├── EthereumAddressCreator.java │ │ └── SegWitBitcoinAddressCreator.java │ │ ├── keystore │ │ ├── EOSKeystore.java │ │ ├── EncMnemonicKeystore.java │ │ ├── ExportableKeystore.java │ │ ├── HDMnemonicKeystore.java │ │ ├── IMTKeystore.java │ │ ├── IdentityKeystore.java │ │ ├── Keystore.java │ │ ├── LegacyEOSKeystore.java │ │ ├── V3Ignore.java │ │ ├── V3Keystore.java │ │ ├── V3MnemonicKeystore.java │ │ └── WalletKeystore.java │ │ ├── model │ │ ├── BIP44Util.java │ │ ├── ChainId.java │ │ ├── ChainType.java │ │ ├── KeyPair.java │ │ ├── Messages.java │ │ ├── Metadata.java │ │ ├── MnemonicAndPath.java │ │ ├── Network.java │ │ └── TokenException.java │ │ ├── transaction │ │ ├── BitcoinTransaction.java │ │ ├── EOSECDSASigner.java │ │ ├── EOSKey.java │ │ ├── EOSSign.java │ │ ├── EOSTransaction.java │ │ ├── EthereumSign.java │ │ ├── EthereumTransaction.java │ │ ├── MyHMacDSAKCalculator.java │ │ ├── SignatureData.java │ │ ├── TransactionSigner.java │ │ ├── TxMultiSignResult.java │ │ └── TxSignResult.java │ │ └── validators │ │ ├── ETHAddressValidator.java │ │ ├── MetadataValidator.java │ │ ├── PrivateKeyValidator.java │ │ ├── Validator.java │ │ └── WIFValidator.java │ └── test │ ├── java │ └── org │ │ └── consenlabs │ │ └── tokencore │ │ ├── ExampleUnitTest.java │ │ ├── foundation │ │ ├── crypto │ │ │ ├── AESTest.java │ │ │ ├── CryptoTest.java │ │ │ ├── HashTest.java │ │ │ ├── MultihashTest.java │ │ │ ├── PBKDFTest.java │ │ │ └── ScryptTest.java │ │ ├── rlp │ │ │ ├── RLPTestCase.java │ │ │ └── RlpEncoderTest.java │ │ └── utils │ │ │ ├── ByteUtilTest.java │ │ │ ├── MnemonicUtilTest.java │ │ │ └── NumericUtilTest.java │ │ ├── testutils │ │ ├── LocalFileStorage.java │ │ └── ResourcesManager.java │ │ └── wallet │ │ ├── EOSWalletTest.java │ │ ├── IdentityTest.java │ │ ├── SampleKey.java │ │ ├── SegWitWalletTest.java │ │ ├── StorageTest.java │ │ ├── TokenCoreTour.java │ │ ├── WalletManagerTest.java │ │ ├── WalletSupport.java │ │ ├── address │ │ └── AddressCreatorTest.java │ │ ├── keystore │ │ └── KeystoreTest.java │ │ ├── transaction │ │ ├── BitcoinTransactionTest.java │ │ ├── EthereumSignTest.java │ │ └── EthereumTransactionTest.java │ │ └── validators │ │ └── ValidatorTest.java │ └── resources │ ├── EOSSignTestcase.txt │ ├── eos_migration_keystore │ └── 5776d691-6a29-4111-81b1-9c3b053b9eaf.json │ ├── invalid_keystores │ ├── address_empty.json │ ├── address_wrong.json │ ├── cipher_empty.json │ ├── cipher_wrong.json │ ├── cipherparams_empty.json │ ├── cipherparams_wrong.json │ ├── ciphertext_empty.json │ ├── ciphertext_wrong.json │ ├── kdf_empty.json │ ├── kdf_wrong.json │ ├── kdfparams_dklen_empty.json │ ├── kdfparams_empty.json │ ├── kdfparams_n_empty.json │ ├── kdfparams_pbkdf2_c_empty.json │ ├── kdfparams_pbkdf2_c_wrong.json │ ├── kdfparams_pbkdf2_salt_empty.json │ ├── kdfparams_pbkdf2_salt_wrong.json │ ├── kdfparams_salt_empty.json │ ├── kdfparams_salt_wrong.json │ ├── kdfparams_scrypt_p_empty.json │ ├── kdfparams_scrypt_r_empty.json │ ├── mac_empty.json │ └── mac_wrong.json │ ├── keystore │ ├── 02a55ab6-554a-4e78-bc26-6a7acced7e5e.json │ ├── 045861fe-0e9b-4069-92aa-0ac03cad55e0.json │ ├── 175169f7-5a35-4df7-93c1-1ff612168e71.json │ ├── 3831346d-0b81-405b-89cf-cdb1d010430e.json │ ├── 42c275c6-957a-49e8-9eb3-43c21cbf583f.json │ ├── 7f5406be-b5ee-4497-948c-877deab8c994.json │ └── identity.json │ ├── rlptest.json │ ├── ttTransactionTest.json │ └── ttTransactionTestEip155VitaliksTests.json ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # IntelliJ 37 | *.iml 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/gradle.xml 41 | .idea/assetWizardSettings.xml 42 | .idea/dictionaries 43 | .idea/libraries 44 | .idea/caches 45 | 46 | # Keystore files 47 | # Uncomment the following lines if you do not want to check your keystore files in. 48 | #*.jks 49 | #*.keystore 50 | 51 | # External native build folder generated in Android Studio 2.2 and later 52 | .externalNativeBuild 53 | 54 | # Google Services (e.g. APIs or Firebase) 55 | google-services.json 56 | 57 | # Freeline 58 | freeline.py 59 | freeline/ 60 | freeline_project_description.json 61 | 62 | # fastlane 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | fastlane/readme.md -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android-token-core 4 | Project android-token-core created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://jitpack.io/v/consenlabs/token-core-android.svg)](https://jitpack.io/#consenlabs/token-core-android) 2 | 3 | 4 | ## Token Core 5 | TokenCore is a blockchain library. TokenCore provides the relatively consistent API that allows you to manage your wallets and sign transactions in BTC, ETH and EOS chains simultaneously. 6 | In addition, TokenCore introduces the concept of 'identity', you can use the same mnemonic to manage wallets on the three chains. 7 | 8 | ## Installation 9 | 10 | Step 1. Add the JitPack repository to your build file 11 | Add it in your root build.gradle at the end of repositories: 12 | ```groovy 13 | allprojects { 14 | repositories { 15 | ... 16 | maven { url 'https://jitpack.io' } 17 | } 18 | } 19 | ``` 20 | 21 | Step 2. Add the dependency 22 | ``` 23 | dependencies { 24 | implementation 'com.github.consenlabs:token-core-android:v0.1' 25 | } 26 | ``` 27 | 28 | Step 3. Add the JAVA8 support to your build.gradle 29 | ``` 30 | android { 31 | …… 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | …… 37 | } 38 | ``` 39 | 40 | Step 4. (Optional) When you wants to debug your app in Android Oreo you shoud add the blow code to your build.gradle. Pls ref to https://issuetracker.google.com/issues/65941637 for more info. 41 | ``` 42 | android { 43 | …… 44 | packagingOptions { 45 | exclude 'lib/x86_64/darwin/libscrypt.dylib' 46 | } 47 | …… 48 | } 49 | ``` 50 | ## Try the API 51 | ### Init the storage to store the keystore file 52 | ``` 53 | public class MainActivity extends AppCompatActivity implements KeystoreStorage { 54 | 55 | @Override 56 | protected void onCreate(Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | setContentView(R.layout.activity_main); 59 | WalletManager.storage = this; 60 | WalletManager.scanWallets(); 61 | } 62 | 63 | public File getKeystoreDir() { 64 | return this.getFilesDir(); 65 | } 66 | } 67 | ``` 68 | 69 | ### Create new Identity and derive the eth, btc wallets 70 | ```java 71 | // You should create or recover Identity first before you create other wallets 72 | // The last param, Metadata.P2WPKH means that the derived btc wallet is a SegWit wallet 73 | Identity identity = Identity.createIdentity("MyFirstIdentity", SampleKey.PASSWORD, SampleKey.PASSWORD_HINT, Network.MAINNET, Metadata.P2WPKH); 74 | 75 | 76 | Wallet ethereumWallet = identity.getWallets().get(0); 77 | Wallet bitcoinWallet = identity.getWallets().get(1); 78 | ``` 79 | ### Export Wallet 80 | ```java 81 | 82 | String prvKey = WalletManager.exportPrivateKey(ethereumWallet.getId(), SampleKey.PASSWORD); 83 | System.out.println(String.format("PrivateKey: %s", prvKey)); 84 | String mnemonic = WalletManager.exportMnemonic(ethereumWallet.getId(), SampleKey.PASSWORD).getMnemonic(); 85 | System.out.println(String.format("Mnemonic: %s", mnemonic)); 86 | String json = WalletManager.exportKeystore(ethereumWallet.getId(), SampleKey.PASSWORD); 87 | System.out.println(String.format("Keystore: %s", json)); 88 | 89 | // output: 90 | // PrivateKey: f653be3f639f45ea1ed3eb152829b6d881ce62257aa873891e06fa9569a8d9aa 91 | // Mnemonic: tide inmate cloud around wise bargain celery cement jungle melody galaxy grocery 92 | // Keystore: {"id":"c7575eba-3ae3-4cc3-86ba-2eb9c6839cad","version":3,"crypto":{"ciphertext":"7083ba3dd5470ba4be4237604625e05fa6b668954d270beb848365cbf6933ec5","mac":"f4f9ea8d42ff348b11fc146c396da446cc975309b3538e08a58c0b218bddd15d","cipher":"aes-128-ctr","cipherparams":{"iv":"db3f523faf4da4f1c6edcd7bc1386879"},"kdf":"pbkdf2","kdfparams":{"dklen":32,"c":10240,"prf":"hmac-sha256","salt":"0ce830e9f888dfe33c31e6cfc444d6f588161c9d4128d4066ee5dfdcbc5d0079"}},"address":"4a1c2072ac67b616e5c578fd9e2a4d30e0158471"} 93 | ``` 94 | 95 | ### SignTransaction 96 | ```java 97 | EthereumTransaction tran = new EthereumTransaction(BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to, BigInteger value, String data) 98 | TxSignResult result = tran.signTransaction(chainId, SampleKey.PASSWORD, ethereumWallet); 99 | String signedTx = result.getSignedTx(); // This is the signature result which you need to broadcast. 100 | String txHash = result.getTxHash(); // This is txHash which you can use for locating your transaction record 101 | ``` 102 | 103 | ## Copyright and License 104 | 105 | ``` 106 | Copyright 2018 imToken PTE. LTD. 107 | 108 | Licensed under the Apache License, Version 2.0 (the "License"); 109 | you may not use this file except in compliance with the License. 110 | You may obtain a copy of the License at 111 | 112 | http://www.apache.org/licenses/LICENSE-2.0 113 | 114 | Unless required by applicable law or agreed to in writing, software 115 | distributed under the License is distributed on an "AS IS" BASIS, 116 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 117 | See the License for the specific language governing permissions and 118 | limitations under the License. 119 | ``` 120 | 121 | ## Thanks and more info 122 | Thanks bitcoinj, CoreBitcoin and others library. 123 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | group='com.github.consenlabs' 5 | 6 | android { 7 | compileSdkVersion 26 8 | defaultConfig { 9 | minSdkVersion 19 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | 16 | compileOptions { 17 | sourceCompatibility JavaVersion.VERSION_1_8 18 | targetCompatibility JavaVersion.VERSION_1_8 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | testOptions { 29 | unitTests.returnDefaultValues = true 30 | } 31 | } 32 | 33 | 34 | dependencies { 35 | compile 'com.fasterxml.jackson.core:jackson-databind:2.9.0' 36 | compile 'org.bitcoinj:bitcoinj-core:0.14.3' 37 | testCompile 'junit:junit:4.12' 38 | testCompile 'org.mockito:mockito-core:1.+' 39 | // compile group: 'com.google.protobuf', name: 'protobuf-lite', version: '3.0.1' 40 | compile 'com.google.protobuf:protobuf-java:3.5.1' 41 | testCompile group: 'org.json', name: 'json', version: '20171018' 42 | 43 | implementation fileTree(dir: 'libs', include: ['*.jar']) 44 | testImplementation 'junit:junit:4.12' 45 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 46 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/consenlabs/tokencore/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("org.consenlabs.tokencore", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/AES.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.spec.IvParameterSpec; 5 | import javax.crypto.spec.SecretKeySpec; 6 | 7 | public class AES { 8 | 9 | public enum AESType { 10 | CTR, CBC 11 | } 12 | 13 | public static byte[] encryptByCTR(byte[] data, byte[] key, byte[] iv) { 14 | return doAES(data, key, iv, Cipher.ENCRYPT_MODE, AESType.CTR, "PKCS5Padding"); 15 | } 16 | 17 | public static byte[] decryptByCTR(byte[] ciphertext, byte[] key, byte[] iv) { 18 | return doAES(ciphertext, key, iv, Cipher.DECRYPT_MODE, AESType.CTR, "PKCS5Padding"); 19 | } 20 | 21 | public static byte[] encryptByCBC(byte[] data, byte[] key, byte[] iv) { 22 | return doAES(data, key, iv, Cipher.ENCRYPT_MODE, AESType.CBC, "PKCS5Padding"); 23 | } 24 | 25 | public static byte[] decryptByCBC(byte[] ciphertext, byte[] key, byte[] iv) { 26 | return doAES(ciphertext, key, iv, Cipher.DECRYPT_MODE, AESType.CBC, "PKCS5Padding"); 27 | } 28 | 29 | public static byte[] encryptByCTRNoPadding(byte[] data, byte[] key, byte[] iv) { 30 | return doAES(data, key, iv, Cipher.ENCRYPT_MODE, AESType.CTR, "NoPadding"); 31 | } 32 | 33 | public static byte[] decryptByCTRNoPadding(byte[] ciphertext, byte[] key, byte[] iv) { 34 | return doAES(ciphertext, key, iv, Cipher.DECRYPT_MODE, AESType.CTR, "NoPadding"); 35 | } 36 | 37 | public static byte[] encryptByCBCNoPadding(byte[] data, byte[] key, byte[] iv) { 38 | return doAES(data, key, iv, Cipher.ENCRYPT_MODE, AESType.CBC, "NoPadding"); 39 | } 40 | 41 | public static byte[] decryptByCBCNoPadding(byte[] ciphertext, byte[] key, byte[] iv) { 42 | return doAES(ciphertext, key, iv, Cipher.DECRYPT_MODE, AESType.CBC, "NoPadding"); 43 | } 44 | 45 | private static byte[] doAES(byte[] data, byte[] key, byte[] iv, int cipherMode, AESType type, String paddingType) { 46 | String aesType; 47 | if (type == AESType.CBC) { 48 | aesType = "CBC"; 49 | } else { 50 | aesType = "CTR"; 51 | } 52 | try { 53 | IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 54 | SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); 55 | 56 | String algorithmDesc = String.format("AES/%s/%s", aesType, paddingType); 57 | Cipher cipher = Cipher.getInstance(algorithmDesc); 58 | cipher.init(cipherMode, secretKeySpec, ivParameterSpec); 59 | return cipher.doFinal(data); 60 | } catch (Exception ignored) { 61 | } 62 | return new byte[0]; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/CipherParams.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.google.common.base.Strings; 5 | 6 | import org.consenlabs.tokencore.wallet.model.Messages; 7 | import org.consenlabs.tokencore.wallet.model.TokenException; 8 | 9 | /** 10 | * Created by xyz on 2018/2/3. 11 | */ 12 | public class CipherParams { 13 | private String iv; 14 | 15 | CipherParams() { 16 | } 17 | 18 | public String getIv() { 19 | return iv; 20 | } 21 | 22 | public void setIv(String iv) { 23 | this.iv = iv; 24 | } 25 | 26 | @JsonIgnore 27 | public void validate() { 28 | if (Strings.isNullOrEmpty(iv)) { 29 | throw new TokenException(Messages.CIPHER_FAIL); 30 | } 31 | } 32 | 33 | @Override 34 | public boolean equals(Object o) { 35 | if (this == o) { 36 | return true; 37 | } 38 | 39 | if (!(o instanceof CipherParams)) { 40 | return false; 41 | } 42 | 43 | CipherParams that = (CipherParams) o; 44 | 45 | return getIv() != null 46 | ? getIv().equals(that.getIv()) : that.getIv() == null; 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | return getIv() != null ? getIv().hashCode() : 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/EncPair.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | public class EncPair { 4 | private String encStr; 5 | private String nonce; 6 | 7 | public String getNonce() { 8 | return nonce; 9 | } 10 | 11 | public void setNonce(String nonce) { 12 | this.nonce = nonce; 13 | } 14 | 15 | public String getEncStr() { 16 | return encStr; 17 | } 18 | 19 | public void setEncStr(String encStr) { 20 | this.encStr = encStr; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/Hash.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import org.bitcoinj.core.Sha256Hash; 4 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 5 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 6 | import org.consenlabs.tokencore.wallet.model.Messages; 7 | import org.consenlabs.tokencore.wallet.model.TokenException; 8 | 9 | import java.security.InvalidKeyException; 10 | import java.security.MessageDigest; 11 | import java.security.NoSuchAlgorithmException; 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import javax.crypto.Mac; 17 | import javax.crypto.spec.SecretKeySpec; 18 | 19 | public class Hash { 20 | 21 | public static String keccak256(String hex) { 22 | byte[] bytes = NumericUtil.hexToBytes(hex); 23 | byte[] result = keccak256(bytes); 24 | return NumericUtil.bytesToHex(result); 25 | } 26 | 27 | public static byte[] keccak256(byte[] input) { 28 | return keccak256(input, 0, input.length); 29 | } 30 | 31 | public static byte[] generateMac(byte[] derivedKey, byte[] cipherText) { 32 | byte[] result = new byte[16 + cipherText.length]; 33 | 34 | System.arraycopy(derivedKey, 16, result, 0, 16); 35 | System.arraycopy(cipherText, 0, result, 16, cipherText.length); 36 | 37 | return Hash.keccak256(result); 38 | } 39 | 40 | public static String sha256(String hexInput) { 41 | byte[] bytes = NumericUtil.hexToBytes(hexInput); 42 | byte[] result = sha256(bytes); 43 | return NumericUtil.bytesToHex(result); 44 | } 45 | 46 | public static byte[] sha256(byte[] input) { 47 | return sha256(input, 0, input.length); 48 | } 49 | 50 | private static byte[] keccak256(byte[] input, int offset, int length) { 51 | 52 | Keccak keccak = new Keccak(256); 53 | keccak.update(input, offset, length); 54 | 55 | return keccak.digest().array(); 56 | } 57 | 58 | private static byte[] sha256(byte[] input, int offset, int length) { 59 | try { 60 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 61 | md.update(input, offset, length); 62 | return md.digest(); 63 | } catch (Exception ex) { 64 | throw new TokenException(Messages.WALLET_SHA256); 65 | } 66 | } 67 | 68 | public static byte[] hmacSHA256(byte[] key, byte[] data) { 69 | try { 70 | Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); 71 | SecretKeySpec secret_key = new SecretKeySpec(key, "HmacSHA256"); 72 | sha256_HMAC.init(secret_key); 73 | 74 | return sha256_HMAC.doFinal(data); 75 | } catch (NoSuchAlgorithmException | InvalidKeyException ex) { 76 | throw new TokenException(Messages.WALLET_SHA256); 77 | } 78 | } 79 | 80 | public static byte[] merkleHash(byte[] oriData) { 81 | 82 | if (oriData == null || oriData.length == 0) { 83 | throw new IllegalArgumentException("data should not be null"); 84 | } 85 | int chunkSize = 1024; 86 | List hashes = new ArrayList<>(); 87 | for (int pos = 0; pos < oriData.length; pos += chunkSize) { 88 | int end = Math.min(pos + chunkSize, oriData.length); 89 | hashes.add(Sha256Hash.hashTwice(Arrays.copyOfRange(oriData, pos, end))); 90 | } 91 | 92 | int j = 0; 93 | for (int size = hashes.size(); size > 1; size = (size + 1) / 2) { 94 | for (int i = 0; i < size; i += 2) { 95 | int i2 = Math.min(i + 1, size - 1); 96 | hashes.add(Sha256Hash.hashTwice(ByteUtil.concat(hashes.get(j + i), hashes.get(j + i2)))); 97 | } 98 | j += size; 99 | } 100 | 101 | return hashes.get(hashes.size() - 1); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/KDFParams.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | /** 6 | * Created by xyz on 2018/2/2. 7 | */ 8 | 9 | 10 | interface KDFParams { 11 | int DK_LEN = 32; 12 | 13 | int getDklen(); 14 | 15 | String getSalt(); 16 | 17 | @JsonIgnore 18 | void validate(); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/Multihash.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import org.bitcoinj.core.Base58; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.DataInput; 7 | import java.io.DataOutput; 8 | import java.io.IOException; 9 | import java.util.Arrays; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | 13 | public class Multihash { 14 | public enum Type { 15 | md5(0xd5, 16), 16 | sha1(0x11, 20), 17 | sha2_256(0x12, 32), 18 | sha2_512(0x13, 64), 19 | sha3_512(0x14, 64), 20 | blake2b(0x40, 64), 21 | blake2s(0x41, 32); 22 | 23 | public int index, length; 24 | 25 | Type(int index, int length) { 26 | this.index = index; 27 | this.length = length; 28 | } 29 | 30 | private static Map lookup = new TreeMap<>(); 31 | static { 32 | for (Type t: Type.values()) 33 | lookup.put(t.index, t); 34 | } 35 | 36 | public static Type lookup(int t) { 37 | if (!lookup.containsKey(t)) 38 | throw new IllegalStateException("Unknown Multihash type: "+t); 39 | return lookup.get(t); 40 | } 41 | } 42 | 43 | public final Type type; 44 | private final byte[] hash; 45 | 46 | public Multihash(Type type, byte[] hash) { 47 | if (hash.length > 127) 48 | throw new IllegalStateException("Unsupported hash size: "+hash.length); 49 | if (hash.length != type.length) 50 | throw new IllegalStateException("Incorrect hash length: " + hash.length + " != "+type.length); 51 | this.type = type; 52 | this.hash = hash; 53 | } 54 | 55 | public Multihash(Multihash toClone) { 56 | this(toClone.type, toClone.hash); // N.B. despite being a byte[], hash is immutable 57 | } 58 | 59 | private Multihash(byte[] multihash) { 60 | this(Type.lookup(multihash[0] & 0xff), Arrays.copyOfRange(multihash, 2, multihash.length)); 61 | } 62 | 63 | private byte[] toBytes() { 64 | byte[] res = new byte[hash.length+2]; 65 | res[0] = (byte)type.index; 66 | res[1] = (byte)hash.length; 67 | System.arraycopy(hash, 0, res, 2, hash.length); 68 | return res; 69 | } 70 | 71 | public void serialize(DataOutput dout) throws IOException { 72 | dout.write(toBytes()); 73 | } 74 | 75 | public static Multihash deserialize(DataInput din) throws IOException { 76 | int type = din.readUnsignedByte(); 77 | int len = din.readUnsignedByte(); 78 | Type t = Type.lookup(type); 79 | byte[] hash = new byte[len]; 80 | din.readFully(hash); 81 | return new Multihash(t, hash); 82 | } 83 | 84 | @Override 85 | public String toString() { 86 | return toBase58(); 87 | } 88 | 89 | @Override 90 | public boolean equals(Object o) { 91 | if (!(o instanceof Multihash)) 92 | return false; 93 | return type == ((Multihash) o).type && Arrays.equals(hash, ((Multihash) o).hash); 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | return Arrays.hashCode(hash) ^ type.hashCode(); 99 | } 100 | 101 | private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); 102 | 103 | public String toHex() { 104 | byte[] bytes = toBytes(); 105 | char[] hexChars = new char[bytes.length * 2]; 106 | for ( int j = 0; j < bytes.length; j++ ) { 107 | int v = bytes[j] & 0xFF; 108 | hexChars[j * 2] = hexArray[v >>> 4]; 109 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 110 | } 111 | return new String(hexChars); 112 | } 113 | 114 | public String toBase58() { 115 | return Base58.encode(toBytes()); 116 | } 117 | 118 | public static Multihash fromHex(String hex) { 119 | if (hex.length() % 2 != 0) 120 | throw new IllegalStateException("Uneven number of hex digits!"); 121 | ByteArrayOutputStream bout = new ByteArrayOutputStream(); 122 | for (int i=0; i < hex.length()-1; i+= 2) 123 | bout.write(Integer.valueOf(hex.substring(i, i+2), 16)); 124 | return new Multihash(bout.toByteArray()); 125 | } 126 | 127 | public static Multihash fromBase58(String base58) { 128 | return new Multihash(Base58.decode(base58)); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/PBKDF2Crypto.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import org.consenlabs.tokencore.wallet.model.Messages; 4 | import org.consenlabs.tokencore.wallet.model.TokenException; 5 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 6 | import org.spongycastle.crypto.digests.SHA256Digest; 7 | import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; 8 | import org.spongycastle.crypto.params.KeyParameter; 9 | 10 | /** 11 | * Created by xyz on 2018/2/3. 12 | */ 13 | 14 | public final class PBKDF2Crypto extends Crypto { 15 | static final String PBKDF2 = "pbkdf2"; 16 | 17 | PBKDF2Crypto() { 18 | super(); 19 | this.kdf = PBKDF2; 20 | } 21 | 22 | public static PBKDF2Crypto createPBKDF2Crypto() { 23 | PBKDF2Crypto crypto = new PBKDF2Crypto(); 24 | byte[] salt = NumericUtil.generateRandomBytes(SALT_LENGTH); 25 | PBKDF2Params pbkdf2Params = PBKDF2Params.createPBKDF2Params(); 26 | pbkdf2Params.setSalt(NumericUtil.bytesToHex(salt)); 27 | crypto.kdfparams = pbkdf2Params; 28 | return crypto; 29 | } 30 | 31 | @Override 32 | public byte[] generateDerivedKey(byte[] password) { 33 | PBKDF2Params params = this.kdfparams; 34 | if (!PBKDF2Params.PRF.equals(params.getPrf())) { 35 | throw new TokenException(Messages.PRF_UNSUPPORTED); 36 | } 37 | 38 | PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest()); 39 | generator.init(password, NumericUtil.hexToBytes(params.getSalt()), params.getC()); 40 | return ((KeyParameter) generator.generateDerivedParameters(256)).getKey(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/PBKDF2Params.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.consenlabs.tokencore.wallet.model.Messages; 6 | import org.consenlabs.tokencore.wallet.model.TokenException; 7 | 8 | /** 9 | * Created by xyz on 2018/2/2. 10 | */ 11 | public class PBKDF2Params implements KDFParams { 12 | static final String PRF = "hmac-sha256"; 13 | static final int C_LIGHT = 10240; 14 | 15 | private int dklen = 0; 16 | private int c = 0; 17 | private String prf = ""; 18 | private String salt; 19 | 20 | public PBKDF2Params() { 21 | } 22 | 23 | public static PBKDF2Params createPBKDF2Params() { 24 | PBKDF2Params params = new PBKDF2Params(); 25 | params.dklen = DK_LEN; 26 | params.c = C_LIGHT; 27 | params.prf = PRF; 28 | return params; 29 | } 30 | 31 | public int getDklen() { 32 | return dklen; 33 | } 34 | 35 | public void setDklen(int dklen) { 36 | this.dklen = dklen; 37 | } 38 | 39 | public int getC() { 40 | return c; 41 | } 42 | 43 | public void setC(int c) { 44 | this.c = c; 45 | } 46 | 47 | public String getPrf() { 48 | return prf; 49 | } 50 | 51 | public void setPrf(String prf) { 52 | this.prf = prf; 53 | } 54 | 55 | public String getSalt() { 56 | return salt; 57 | } 58 | 59 | @Override 60 | public void validate() { 61 | if (dklen == 0 || c == 0 || Strings.isNullOrEmpty(salt) || Strings.isNullOrEmpty(prf)) { 62 | throw new TokenException(Messages.KDF_PARAMS_INVALID); 63 | } 64 | } 65 | 66 | public void setSalt(String salt) { 67 | this.salt = salt; 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) { 73 | return true; 74 | } 75 | if (!(o instanceof PBKDF2Params)) { 76 | return false; 77 | } 78 | 79 | PBKDF2Params that = (PBKDF2Params) o; 80 | 81 | if (dklen != that.dklen) { 82 | return false; 83 | } 84 | if (c != that.c) { 85 | return false; 86 | } 87 | if (getPrf() != null 88 | ? !getPrf().equals(that.getPrf()) 89 | : that.getPrf() != null) { 90 | return false; 91 | } 92 | return getSalt() != null 93 | ? getSalt().equals(that.getSalt()) : that.getSalt() == null; 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | int result = dklen; 99 | result = 31 * result + c; 100 | result = 31 * result + (getPrf() != null ? getPrf().hashCode() : 0); 101 | result = 31 * result + (getSalt() != null ? getSalt().hashCode() : 0); 102 | return result; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/SCryptCrypto.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import com.lambdaworks.crypto.SCrypt; 4 | 5 | import org.consenlabs.tokencore.wallet.model.Messages; 6 | import org.consenlabs.tokencore.wallet.model.TokenException; 7 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 8 | 9 | import java.security.GeneralSecurityException; 10 | 11 | /** 12 | * Created by xyz on 2018/2/3. 13 | */ 14 | 15 | public class SCryptCrypto extends Crypto { 16 | static final String SCRYPT = "scrypt"; 17 | 18 | 19 | public SCryptCrypto() { 20 | super(); 21 | this.kdf = SCRYPT; 22 | } 23 | 24 | public static SCryptCrypto createSCryptCrypto() { 25 | SCryptCrypto crypto = new SCryptCrypto(); 26 | byte[] salt = NumericUtil.generateRandomBytes(SALT_LENGTH); 27 | SCryptParams params = SCryptParams.createSCryptParams(); 28 | params.setSalt(NumericUtil.bytesToHex(salt)); 29 | crypto.kdfparams = params; 30 | return crypto; 31 | } 32 | 33 | @Override 34 | public byte[] generateDerivedKey(byte[] password) { 35 | int dkLen = this.kdfparams.getDklen(); 36 | int n = this.kdfparams.getN(); 37 | int p = this.kdfparams.getP(); 38 | int r = this.kdfparams.getR(); 39 | byte[] salt = NumericUtil.hexToBytes(this.kdfparams.getSalt()); 40 | try { 41 | return SCrypt.scrypt(password, salt, n, r, p, dkLen); 42 | } catch (GeneralSecurityException e) { 43 | throw new TokenException(Messages.SCRYPT_PARAMS_INVALID, e); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/crypto/SCryptParams.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.consenlabs.tokencore.wallet.model.Messages; 6 | import org.consenlabs.tokencore.wallet.model.TokenException; 7 | 8 | /** 9 | * Created by xyz on 2018/2/2. 10 | */ 11 | public class SCryptParams implements KDFParams { 12 | static final int COST_FACTOR = 8192; 13 | static final int BLOCK_SIZE_FACTOR = 8; 14 | static final int PARALLELIZATION_FACTOR = 1; 15 | private int dklen = 0; 16 | private int n = 0; 17 | private int p = 0; 18 | private int r = 0; 19 | private String salt; 20 | 21 | public SCryptParams() { 22 | } 23 | 24 | public static SCryptParams createSCryptParams() { 25 | SCryptParams params = new SCryptParams(); 26 | params.dklen = DK_LEN; 27 | params.n = COST_FACTOR; 28 | params.p = PARALLELIZATION_FACTOR; 29 | params.r = BLOCK_SIZE_FACTOR; 30 | return params; 31 | } 32 | 33 | public int getDklen() { 34 | return dklen; 35 | } 36 | 37 | public void setDklen(int dklen) { 38 | this.dklen = dklen; 39 | } 40 | 41 | public int getN() { 42 | return n; 43 | } 44 | 45 | public void setN(int n) { 46 | this.n = n; 47 | } 48 | 49 | public int getP() { 50 | return p; 51 | } 52 | 53 | public void setP(int p) { 54 | this.p = p; 55 | } 56 | 57 | public int getR() { 58 | return r; 59 | } 60 | 61 | public void setR(int r) { 62 | this.r = r; 63 | } 64 | 65 | public String getSalt() { 66 | return salt; 67 | } 68 | 69 | @Override 70 | public void validate() { 71 | if (n == 0 || dklen == 0 || p == 0 || r == 0 || Strings.isNullOrEmpty(salt)) { 72 | throw new TokenException(Messages.KDF_PARAMS_INVALID); 73 | } 74 | } 75 | 76 | public void setSalt(String salt) { 77 | this.salt = salt; 78 | } 79 | 80 | @Override 81 | public boolean equals(Object o) { 82 | if (this == o) { 83 | return true; 84 | } 85 | if (!(o instanceof SCryptParams)) { 86 | return false; 87 | } 88 | 89 | SCryptParams that = (SCryptParams) o; 90 | 91 | if (dklen != that.dklen) { 92 | return false; 93 | } 94 | if (n != that.n) { 95 | return false; 96 | } 97 | if (p != that.p) { 98 | return false; 99 | } 100 | if (r != that.r) { 101 | return false; 102 | } 103 | return getSalt() != null 104 | ? getSalt().equals(that.getSalt()) : that.getSalt() == null; 105 | } 106 | 107 | @Override 108 | public int hashCode() { 109 | int result = dklen; 110 | result = 31 * result + n; 111 | result = 31 * result + p; 112 | result = 31 * result + r; 113 | result = 31 * result + (getSalt() != null ? getSalt().hashCode() : 0); 114 | return result; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/rlp/RlpEncoder.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.rlp; 2 | 3 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | /** 9 | *

Recursive Length Prefix (RLP) encoder.

10 | * 11 | *

For the specification, refer to p16 of the 12 | * yellow paper and here.

13 | */ 14 | public class RlpEncoder { 15 | 16 | private static final int STRING_OFFSET = 0x80; 17 | private static final int LIST_OFFSET = 0xc0; 18 | 19 | public static byte[] encode(RlpType value) { 20 | if (value instanceof RlpString) { 21 | return encodeString((RlpString) value); 22 | } else { 23 | return encodeList((RlpList) value); 24 | } 25 | } 26 | 27 | private static byte[] encode(byte[] bytesValue, int offset) { 28 | if (bytesValue.length == 1 29 | && offset == STRING_OFFSET 30 | && bytesValue[0] >= (byte) 0x00 31 | && bytesValue[0] <= (byte) 0x7f) { 32 | return bytesValue; 33 | } else if (bytesValue.length <= 55) { 34 | byte[] result = new byte[bytesValue.length + 1]; 35 | result[0] = (byte) (offset + bytesValue.length); 36 | System.arraycopy(bytesValue, 0, result, 1, bytesValue.length); 37 | return result; 38 | } else { 39 | byte[] encodedStringLength = toMinimalByteArray(bytesValue.length); 40 | byte[] result = new byte[bytesValue.length + encodedStringLength.length + 1]; 41 | 42 | result[0] = (byte) ((offset + 0x37) + encodedStringLength.length); 43 | System.arraycopy(encodedStringLength, 0, result, 1, encodedStringLength.length); 44 | System.arraycopy( 45 | bytesValue, 0, result, encodedStringLength.length + 1, bytesValue.length); 46 | return result; 47 | } 48 | } 49 | 50 | private static byte[] encodeString(RlpString value) { 51 | return encode(value.getBytes(), STRING_OFFSET); 52 | } 53 | 54 | private static byte[] toMinimalByteArray(int value) { 55 | byte[] encoded = toByteArray(value); 56 | 57 | for (int i = 0; i < encoded.length; i++) { 58 | if (encoded[i] != 0) { 59 | return Arrays.copyOfRange(encoded, i, encoded.length); 60 | } 61 | } 62 | 63 | return new byte[]{ }; 64 | } 65 | 66 | private static byte[] toByteArray(int value) { 67 | return new byte[] { 68 | (byte) ((value >> 24) & 0xff), 69 | (byte) ((value >> 16) & 0xff), 70 | (byte) ((value >> 8) & 0xff), 71 | (byte) (value & 0xff) 72 | }; 73 | } 74 | 75 | private static byte[] encodeList(RlpList value) { 76 | List values = value.getValues(); 77 | if (values.isEmpty()) { 78 | return encode(new byte[]{ }, LIST_OFFSET); 79 | } else { 80 | byte[] result = new byte[0]; 81 | for (RlpType entry:values) { 82 | result = ByteUtil.concat(result, encode(entry)); 83 | } 84 | return encode(result, LIST_OFFSET); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/rlp/RlpList.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.rlp; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /** 7 | * RLP list type. 8 | */ 9 | public class RlpList implements RlpType { 10 | private final List values; 11 | 12 | public RlpList(RlpType... values) { 13 | this.values = Arrays.asList(values); 14 | } 15 | 16 | public RlpList(List values) { 17 | this.values = values; 18 | } 19 | 20 | public List getValues() { 21 | return values; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/rlp/RlpString.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.rlp; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Arrays; 5 | 6 | 7 | /** 8 | * RLP string type. 9 | */ 10 | public class RlpString implements RlpType { 11 | private static final byte[] EMPTY = new byte[]{ }; 12 | 13 | private final byte[] value; 14 | 15 | private RlpString(byte[] value) { 16 | this.value = value; 17 | } 18 | 19 | public byte[] getBytes() { 20 | return value; 21 | } 22 | 23 | public static RlpString create(byte[] value) { 24 | return new RlpString(value); 25 | } 26 | 27 | public static RlpString create(byte value) { 28 | return new RlpString(new byte[]{ value }); 29 | } 30 | 31 | public static RlpString create(BigInteger value) { 32 | // RLP encoding only supports positive integer values 33 | if (value.signum() < 1) { 34 | return new RlpString(EMPTY); 35 | } else { 36 | byte[] bytes = value.toByteArray(); 37 | if (bytes[0] == 0) { // remove leading zero 38 | return new RlpString(Arrays.copyOfRange(bytes, 1, bytes.length)); 39 | } else { 40 | return new RlpString(bytes); 41 | } 42 | } 43 | } 44 | 45 | public static RlpString create(long value) { 46 | return create(BigInteger.valueOf(value)); 47 | } 48 | 49 | public static RlpString create(String value) { 50 | return new RlpString(value.getBytes()); 51 | } 52 | 53 | @Override 54 | public boolean equals(Object o) { 55 | if (this == o) { 56 | return true; 57 | } 58 | if (o == null || getClass() != o.getClass()) { 59 | return false; 60 | } 61 | 62 | RlpString rlpString = (RlpString) o; 63 | 64 | return Arrays.equals(value, rlpString.value); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return Arrays.hashCode(value); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/rlp/RlpType.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.rlp; 2 | 3 | /** 4 | * Base RLP type. 5 | */ 6 | public interface RlpType { 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/utils/ByteUtil.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.utils; 2 | 3 | import java.util.Arrays; 4 | 5 | public class ByteUtil { 6 | private static byte[] trimLeadingBytes(byte[] bytes, byte b) { 7 | int offset = 0; 8 | for (; offset < bytes.length - 1; offset++) { 9 | if (bytes[offset] != b) { 10 | break; 11 | } 12 | } 13 | return Arrays.copyOfRange(bytes, offset, bytes.length); 14 | } 15 | 16 | public static byte[] trimLeadingZeroes(byte[] bytes) { 17 | return trimLeadingBytes(bytes, (byte) 0); 18 | } 19 | 20 | public static byte[] concat(byte[] b1, byte[] b2) { 21 | byte[] result = Arrays.copyOf(b1, b1.length + b2.length); 22 | System.arraycopy(b2, 0, result, b1.length, b2.length); 23 | return result; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/utils/CachedDerivedKey.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.utils; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.consenlabs.tokencore.foundation.crypto.Hash; 6 | 7 | public class CachedDerivedKey { 8 | private String hashedPassword; 9 | private byte[] derivedKey; 10 | 11 | 12 | public CachedDerivedKey(String password, byte[] derivedKey) { 13 | this.hashedPassword = hash(password); 14 | this.derivedKey = derivedKey; 15 | } 16 | 17 | private String hash(String password) { 18 | return NumericUtil.bytesToHex(Hash.sha256(Hash.sha256(password.getBytes()))); 19 | } 20 | 21 | public byte[] getDerivedKey(String password) { 22 | if (hashedPassword != null && hashedPassword.equals(hash(password))) { 23 | return derivedKey; 24 | } 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/utils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.utils; 2 | 3 | import java.util.Calendar; 4 | import java.util.TimeZone; 5 | 6 | public class DateUtil { 7 | public static long getUTCTime() { 8 | Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 9 | return cal.getTimeInMillis()/1000; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/foundation/utils/MnemonicUtil.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.utils; 2 | 3 | import org.bitcoinj.crypto.MnemonicCode; 4 | import org.consenlabs.tokencore.wallet.model.Messages; 5 | import org.consenlabs.tokencore.wallet.model.TokenException; 6 | 7 | import java.util.List; 8 | 9 | public class MnemonicUtil { 10 | public static void validateMnemonics(List mnemonicCodes) { 11 | try { 12 | MnemonicCode.INSTANCE.check(mnemonicCodes); 13 | } catch (org.bitcoinj.crypto.MnemonicException.MnemonicLengthException e) { 14 | throw new TokenException(Messages.MNEMONIC_INVALID_LENGTH); 15 | } catch (org.bitcoinj.crypto.MnemonicException.MnemonicWordException e) { 16 | throw new TokenException(Messages.MNEMONIC_BAD_WORD); 17 | } catch (Exception e) { 18 | throw new TokenException(Messages.MNEMONIC_CHECKSUM); 19 | } 20 | } 21 | 22 | public static List randomMnemonicCodes() { 23 | return toMnemonicCodes(NumericUtil.generateRandomBytes(16)); 24 | } 25 | 26 | 27 | private static List toMnemonicCodes(byte[] entropy) { 28 | try { 29 | return MnemonicCode.INSTANCE.toMnemonic(entropy); 30 | } catch (org.bitcoinj.crypto.MnemonicException.MnemonicLengthException e) { 31 | throw new TokenException(Messages.MNEMONIC_INVALID_LENGTH); 32 | } catch (Exception e) { 33 | throw new TokenException(Messages.MNEMONIC_CHECKSUM); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/KeystoreStorage.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Created by xyz on 2018/4/8. 7 | */ 8 | 9 | public interface KeystoreStorage { 10 | File getKeystoreDir(); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/Wallet.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 6 | import org.consenlabs.tokencore.wallet.keystore.EOSKeystore; 7 | import org.consenlabs.tokencore.wallet.keystore.EncMnemonicKeystore; 8 | import org.consenlabs.tokencore.wallet.keystore.ExportableKeystore; 9 | import org.consenlabs.tokencore.wallet.keystore.HDMnemonicKeystore; 10 | import org.consenlabs.tokencore.wallet.keystore.IMTKeystore; 11 | import org.consenlabs.tokencore.wallet.keystore.LegacyEOSKeystore; 12 | import org.consenlabs.tokencore.wallet.keystore.V3Ignore; 13 | import org.consenlabs.tokencore.wallet.keystore.V3Keystore; 14 | import org.consenlabs.tokencore.wallet.keystore.V3MnemonicKeystore; 15 | import org.consenlabs.tokencore.wallet.model.KeyPair; 16 | import org.consenlabs.tokencore.wallet.model.Messages; 17 | import org.consenlabs.tokencore.wallet.model.Metadata; 18 | import org.consenlabs.tokencore.wallet.model.MnemonicAndPath; 19 | import org.consenlabs.tokencore.wallet.model.TokenException; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Collections; 23 | import java.util.List; 24 | 25 | 26 | public class Wallet { 27 | private IMTKeystore keystore; 28 | 29 | public IMTKeystore getKeystore() { 30 | return keystore; 31 | } 32 | 33 | public Wallet(IMTKeystore keystore) { 34 | this.keystore = keystore; 35 | } 36 | 37 | public String getId() { 38 | return this.keystore.getId(); 39 | } 40 | 41 | public String getAddress() { 42 | return this.keystore.getAddress(); 43 | } 44 | 45 | public Metadata getMetadata() { 46 | return keystore.getMetadata(); 47 | } 48 | 49 | public String getEncXPub() { 50 | if (keystore instanceof HDMnemonicKeystore) { 51 | return ((HDMnemonicKeystore) keystore).getEncryptXPub(); 52 | } 53 | return null; 54 | } 55 | 56 | public byte[] decryptMainKey(String password) { 57 | return keystore.decryptCiphertext(password); 58 | } 59 | 60 | MnemonicAndPath exportMnemonic(String password) { 61 | if (keystore instanceof EncMnemonicKeystore) { 62 | EncMnemonicKeystore encMnemonicKeystore = (EncMnemonicKeystore) keystore; 63 | String mnemonic = encMnemonicKeystore.decryptMnemonic(password); 64 | String path = encMnemonicKeystore.getMnemonicPath(); 65 | return new MnemonicAndPath(mnemonic, path); 66 | } 67 | return null; 68 | } 69 | 70 | String exportKeystore(String password) { 71 | if (keystore instanceof ExportableKeystore) { 72 | if (!keystore.verifyPassword(password)) { 73 | throw new TokenException(Messages.WALLET_INVALID_PASSWORD); 74 | } 75 | 76 | try { 77 | ObjectMapper mapper = new ObjectMapper(); 78 | mapper.addMixIn(IMTKeystore.class, V3Ignore.class); 79 | return mapper.writeValueAsString(keystore); 80 | } catch (Exception ex) { 81 | throw new TokenException(Messages.WALLET_INVALID, ex); 82 | } 83 | } else { 84 | throw new TokenException(Messages.CAN_NOT_EXPORT_MNEMONIC); 85 | } 86 | } 87 | 88 | public String exportPrivateKey(String password) { 89 | if (keystore instanceof V3Keystore || keystore instanceof V3MnemonicKeystore) { 90 | byte[] decrypted = keystore.decryptCiphertext(password); 91 | if (keystore.getMetadata().getSource().equals(Metadata.FROM_WIF)) { 92 | return new String(decrypted); 93 | } else { 94 | return NumericUtil.bytesToHex(decrypted); 95 | } 96 | } 97 | throw new TokenException(Messages.ILLEGAL_OPERATION); 98 | } 99 | 100 | boolean verifyPassword(String password) { 101 | return keystore.verifyPassword(password); 102 | } 103 | 104 | public byte[] decryptPrvKeyFor(String pubKey, String password) { 105 | if (!(keystore instanceof EOSKeystore)) { 106 | throw new TokenException("This method is only for EOS wallet!"); 107 | } 108 | 109 | EOSKeystore eosKeystore = (EOSKeystore) keystore; 110 | return eosKeystore.decryptPrivateKeyFor(pubKey, password); 111 | } 112 | 113 | public String newReceiveAddress(int nextRecvIdx) { 114 | if (keystore instanceof HDMnemonicKeystore) { 115 | return ((HDMnemonicKeystore) keystore).newReceiveAddress(nextRecvIdx); 116 | } else { 117 | return keystore.getAddress(); 118 | } 119 | } 120 | 121 | public long getCreatedAt() { 122 | return this.keystore.getMetadata().getTimestamp(); 123 | } 124 | 125 | boolean hasMnemonic() { 126 | return this.keystore instanceof EncMnemonicKeystore; 127 | } 128 | 129 | public List exportPrivateKeys(String password) { 130 | if (keystore instanceof EOSKeystore) { 131 | return ((EOSKeystore)keystore).exportPrivateKeys(password); 132 | } else if (keystore instanceof LegacyEOSKeystore) { 133 | return ((LegacyEOSKeystore)keystore).exportPrivateKeys(password); 134 | } else { 135 | throw new TokenException("Only eos wallet can export multi private keys"); 136 | } 137 | } 138 | 139 | public void setAccountName(String accountName) { 140 | if (!(keystore instanceof EOSKeystore)) { 141 | throw new TokenException("Only EOS wallet can update account name!"); 142 | } 143 | ((EOSKeystore) keystore).setAccountName(accountName); 144 | } 145 | 146 | public List getKeyPathPrivates() { 147 | 148 | if (keystore instanceof EOSKeystore) { 149 | return ((EOSKeystore) keystore).getKeyPathPrivates(); 150 | } else if (keystore instanceof LegacyEOSKeystore) { 151 | return Collections.emptyList(); 152 | } else { 153 | throw new TokenException("Only EOS wallet can export all public keys!"); 154 | } 155 | 156 | } 157 | 158 | boolean delete(String password) { 159 | return keystore.verifyPassword(password) && WalletManager.generateWalletFile(keystore.getId()).delete(); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/address/AddressCreator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.address; 2 | 3 | public interface AddressCreator { 4 | String fromPrivateKey(String prvKeyHex); 5 | String fromPrivateKey(byte[] prvKeyBytes); 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/address/AddressCreatorManager.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.address; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.bitcoinj.core.NetworkParameters; 6 | import org.bitcoinj.params.MainNetParams; 7 | import org.bitcoinj.params.TestNet3Params; 8 | import org.consenlabs.tokencore.wallet.model.Messages; 9 | import org.consenlabs.tokencore.wallet.model.Metadata; 10 | import org.consenlabs.tokencore.wallet.model.Network; 11 | import org.consenlabs.tokencore.wallet.model.TokenException; 12 | import org.consenlabs.tokencore.wallet.model.ChainId; 13 | import org.consenlabs.tokencore.wallet.model.ChainType; 14 | 15 | public class AddressCreatorManager { 16 | 17 | public static AddressCreator getInstance(String type, boolean isMainnet, String segWit) { 18 | if (ChainType.ETHEREUM.equals(type)) { 19 | return new EthereumAddressCreator(); 20 | } else if (ChainType.BITCOIN.equals(type)) { 21 | 22 | NetworkParameters network = isMainnet ? MainNetParams.get() : TestNet3Params.get(); 23 | if (Metadata.P2WPKH.equals(segWit)) { 24 | return new SegWitBitcoinAddressCreator(network); 25 | } 26 | return new BitcoinAddressCreator(network); 27 | } else { 28 | throw new TokenException(Messages.WALLET_INVALID_TYPE); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/address/BitcoinAddressCreator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.address; 2 | 3 | import org.bitcoinj.core.DumpedPrivateKey; 4 | import org.bitcoinj.core.ECKey; 5 | import org.bitcoinj.core.NetworkParameters; 6 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 7 | 8 | public class BitcoinAddressCreator implements AddressCreator { 9 | private NetworkParameters networkParameters; 10 | 11 | public BitcoinAddressCreator(NetworkParameters networkParameters) { 12 | this.networkParameters = networkParameters; 13 | } 14 | 15 | @Override 16 | public String fromPrivateKey(String prvKeyHex) { 17 | ECKey key; 18 | if (prvKeyHex.length() == 51 || prvKeyHex.length() == 52) { 19 | DumpedPrivateKey dumpedPrivateKey = DumpedPrivateKey.fromBase58(networkParameters, prvKeyHex); 20 | key = dumpedPrivateKey.getKey(); 21 | } else { 22 | key = ECKey.fromPrivate(NumericUtil.hexToBytes(prvKeyHex)); 23 | } 24 | return key.toAddress(this.networkParameters).toBase58(); 25 | } 26 | 27 | @Override 28 | public String fromPrivateKey(byte[] prvKeyBytes) { 29 | ECKey key = ECKey.fromPrivate(prvKeyBytes); 30 | return key.toAddress(this.networkParameters).toBase58(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/address/EthereumAddressCreator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.address; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.bitcoinj.core.ECKey; 6 | import org.consenlabs.tokencore.foundation.crypto.Hash; 7 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 8 | 9 | import java.math.BigInteger; 10 | import java.util.Arrays; 11 | import java.util.regex.Pattern; 12 | 13 | import static com.google.common.base.Preconditions.checkState; 14 | 15 | 16 | public class EthereumAddressCreator implements AddressCreator { 17 | 18 | private static final int PUBLIC_KEY_SIZE = 64; 19 | private static final int PUBLIC_KEY_LENGTH_IN_HEX = PUBLIC_KEY_SIZE << 1; 20 | private static final int ADDRESS_LENGTH = 20; 21 | private static final int ADDRESS_LENGTH_IN_HEX = ADDRESS_LENGTH << 1; 22 | 23 | 24 | public String fromPublicKey(BigInteger publicKey) { 25 | byte[] pubKeyBytes = NumericUtil.bigIntegerToBytesWithZeroPadded(publicKey, PUBLIC_KEY_SIZE); 26 | return publicKeyToAddress(pubKeyBytes); 27 | } 28 | 29 | private String publicKeyToAddress(byte[] pubKeyBytes) { 30 | byte[] hashedBytes = Hash.keccak256(pubKeyBytes); 31 | byte[] addrBytes = Arrays.copyOfRange(hashedBytes, hashedBytes.length - 20, hashedBytes.length); 32 | return NumericUtil.bytesToHex(addrBytes); 33 | } 34 | 35 | @Override 36 | public String fromPrivateKey(String prvKeyHex) { 37 | ECKey key = ECKey.fromPrivate(NumericUtil.hexToBytes(prvKeyHex), false); 38 | return fromECKey(key); 39 | } 40 | 41 | @Override 42 | public String fromPrivateKey(byte[] prvKeyBytes) { 43 | ECKey key = ECKey.fromPrivate(prvKeyBytes, false); 44 | return fromECKey(key); 45 | } 46 | 47 | private String fromECKey(ECKey key) { 48 | byte[] pubKeyBytes = key.getPubKey(); 49 | return publicKeyToAddress(Arrays.copyOfRange(pubKeyBytes, 1, pubKeyBytes.length)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/address/SegWitBitcoinAddressCreator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.address; 2 | 3 | import org.bitcoinj.core.Address; 4 | import org.bitcoinj.core.DumpedPrivateKey; 5 | import org.bitcoinj.core.ECKey; 6 | import org.bitcoinj.core.NetworkParameters; 7 | import org.bitcoinj.core.Utils; 8 | import org.bitcoinj.params.MainNetParams; 9 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 10 | import org.consenlabs.tokencore.wallet.model.Messages; 11 | import org.consenlabs.tokencore.wallet.model.TokenException; 12 | 13 | public class SegWitBitcoinAddressCreator implements AddressCreator{ 14 | private NetworkParameters networkParameters; 15 | 16 | public SegWitBitcoinAddressCreator(NetworkParameters networkParameters) { 17 | this.networkParameters = networkParameters; 18 | } 19 | 20 | @Override 21 | public String fromPrivateKey(String prvKeyHex) { 22 | ECKey key; 23 | if (prvKeyHex.length() == 51 || prvKeyHex.length() == 52) { 24 | DumpedPrivateKey dumpedPrivateKey = DumpedPrivateKey.fromBase58(networkParameters, prvKeyHex); 25 | key = dumpedPrivateKey.getKey(); 26 | if (!key.isCompressed()) { 27 | throw new TokenException(Messages.SEGWIT_NEEDS_COMPRESS_PUBLIC_KEY); 28 | } 29 | } else { 30 | key = ECKey.fromPrivate(NumericUtil.hexToBytes(prvKeyHex), true); 31 | } 32 | return calcSegWitAddress(key.getPubKeyHash()); 33 | } 34 | 35 | @Override 36 | public String fromPrivateKey(byte[] prvKeyBytes) { 37 | ECKey key = ECKey.fromPrivate(prvKeyBytes, true); 38 | return calcSegWitAddress(key.getPubKeyHash()); 39 | } 40 | 41 | private String calcSegWitAddress(byte[] pubKeyHash) { 42 | String redeemScript = String.format("0x0014%s", NumericUtil.bytesToHex(pubKeyHash)); 43 | return Address.fromP2SHHash(networkParameters, Utils.sha256hash160(NumericUtil.hexToBytes(redeemScript))).toBase58(); 44 | } 45 | 46 | public Address fromPrivateKey(ECKey ecKey) { 47 | String redeemScript = String.format("0x0014%s", NumericUtil.bytesToHex(ecKey.getPubKeyHash())); 48 | return Address.fromP2SHHash(networkParameters, Utils.sha256hash160(NumericUtil.hexToBytes(redeemScript))); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/EncMnemonicKeystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import org.consenlabs.tokencore.foundation.crypto.Crypto; 4 | import org.consenlabs.tokencore.foundation.crypto.EncPair; 5 | 6 | import java.nio.charset.Charset; 7 | 8 | public interface EncMnemonicKeystore { 9 | EncPair getEncMnemonic(); 10 | void setEncMnemonic(EncPair encMnemonic); 11 | String getMnemonicPath(); 12 | Crypto getCrypto(); 13 | 14 | default void createEncMnemonic(String password, String mnemonic) { 15 | EncPair encMnemonic = getCrypto().deriveEncPair(password, mnemonic.getBytes(Charset.forName("UTF-8"))); 16 | this.setEncMnemonic(encMnemonic); 17 | } 18 | default String decryptMnemonic(String password) { 19 | return new String(getCrypto().decryptEncPair(password, getEncMnemonic())); 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/ExportableKeystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | public interface ExportableKeystore { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/IMTKeystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import com.fasterxml.jackson.annotation.JsonGetter; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | 6 | import org.consenlabs.tokencore.wallet.model.Metadata; 7 | 8 | public abstract class IMTKeystore extends WalletKeystore { 9 | @JsonIgnore 10 | Metadata metadata; 11 | 12 | @JsonGetter(value = "imTokenMeta") 13 | public Metadata getMetadata() { 14 | return metadata; 15 | } 16 | 17 | public void setMetadata(Metadata metadata) { 18 | this.metadata = metadata; 19 | } 20 | 21 | public IMTKeystore() { 22 | super(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/IdentityKeystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import com.fasterxml.jackson.annotation.JsonGetter; 4 | import com.google.common.base.Joiner; 5 | 6 | import org.bitcoinj.core.Base58; 7 | import org.bitcoinj.core.ECKey; 8 | import org.bitcoinj.core.NetworkParameters; 9 | import org.bitcoinj.core.Sha256Hash; 10 | import org.bitcoinj.crypto.DeterministicKey; 11 | import org.bitcoinj.crypto.HDKeyDerivation; 12 | import org.bitcoinj.params.MainNetParams; 13 | import org.bitcoinj.params.TestNet3Params; 14 | import org.bitcoinj.wallet.DeterministicSeed; 15 | import org.consenlabs.tokencore.foundation.crypto.Crypto; 16 | import org.consenlabs.tokencore.foundation.crypto.EncPair; 17 | import org.consenlabs.tokencore.foundation.crypto.Hash; 18 | import org.consenlabs.tokencore.foundation.crypto.Multihash; 19 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 20 | import org.consenlabs.tokencore.foundation.utils.DateUtil; 21 | import org.consenlabs.tokencore.foundation.utils.MnemonicUtil; 22 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 23 | import org.consenlabs.tokencore.wallet.model.Metadata; 24 | 25 | import java.nio.charset.Charset; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | /** 31 | * Created by xyz on 2017/12/11. 32 | */ 33 | 34 | public class IdentityKeystore extends Keystore implements EncMnemonicKeystore { 35 | private static final int VERSION = 10000; 36 | 37 | public void setIdentifier(String identifier) { 38 | this.identifier = identifier; 39 | } 40 | 41 | public void setWalletIDs(List walletIDs) { 42 | this.walletIDs = walletIDs; 43 | } 44 | 45 | public void setIpfsId(String ipfsId) { 46 | this.ipfsId = ipfsId; 47 | 48 | } 49 | 50 | 51 | public void setEncKey(String encKey) { 52 | this.encKey = encKey; 53 | } 54 | 55 | public void setEncAuthKey(EncPair encAuthKey) { 56 | this.encAuthKey = encAuthKey; 57 | } 58 | 59 | public void setMnemonicPath(String mnemonicPath) { 60 | this.mnemonicPath = mnemonicPath; 61 | } 62 | 63 | private String identifier; 64 | private String ipfsId; 65 | private String encKey; 66 | private EncPair encAuthKey; 67 | private EncPair encMnemonic; 68 | private String mnemonicPath; 69 | 70 | public void setMetadata(Metadata metadata) { 71 | this.metadata = metadata; 72 | } 73 | 74 | private Metadata metadata; 75 | private List walletIDs = new ArrayList<>(); 76 | 77 | public IdentityKeystore() { 78 | 79 | } 80 | 81 | 82 | public IdentityKeystore(Metadata metadata, List mnemonicCodes, String password) { 83 | MnemonicUtil.validateMnemonics(mnemonicCodes); 84 | 85 | DeterministicSeed seed = new DeterministicSeed(mnemonicCodes, null, "", 0L); 86 | 87 | DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed.getSeedBytes()); 88 | byte[] masterKey = masterPrivateKey.getPrivKeyBytes(); 89 | 90 | String salt = metadata.isMainNet() ? "Automatic Backup Key Mainnet" : "Automatic Backup Key Testnet"; 91 | byte[] backupKey = Hash.hmacSHA256(masterKey, salt.getBytes(Charset.forName("ASCII"))); 92 | byte[] authenticationKey = Hash.hmacSHA256(backupKey, "Authentication Key".getBytes(Charset.forName("UTF-8"))); 93 | ECKey authKey = ECKey.fromPrivate(authenticationKey); 94 | 95 | NetworkParameters networkParameters = metadata.isMainNet() ? MainNetParams.get() : TestNet3Params.get(); 96 | 97 | String aPubHashHex = NumericUtil.bytesToHex(authKey.getPubKeyHash()); 98 | int networkHeader = networkParameters.getAddressHeader(); 99 | int version = 2; 100 | // this magic hex will start with 'im' after base58check 101 | String magicHex = "0fdc0c"; 102 | String fullIdentifier = String.format("%s%02x%02x%s", magicHex, (byte) networkHeader, (byte) version, aPubHashHex); 103 | byte[] fullIdentifierBytes = NumericUtil.hexToBytes(fullIdentifier); 104 | byte[] checksumBytes = Arrays.copyOfRange(Sha256Hash.hashTwice(fullIdentifierBytes), 0, 4); 105 | byte[] identifierWithChecksum = ByteUtil.concat(fullIdentifierBytes, checksumBytes); 106 | this.identifier = Base58.encode(identifierWithChecksum); 107 | 108 | byte[] encKeyFullBytes = Hash.hmacSHA256(backupKey, "Encryption Key".getBytes(Charset.forName("UTF-8"))); 109 | this.encKey = NumericUtil.bytesToHex(encKeyFullBytes); 110 | 111 | ECKey ecKey = ECKey.fromPrivate(encKeyFullBytes, false); 112 | this.ipfsId = new Multihash(Multihash.Type.sha2_256, Hash.sha256(ecKey.getPubKey())).toBase58(); 113 | Crypto crypto = Crypto.createPBKDF2CryptoWithKDFCached(password, masterPrivateKey.serializePrivB58(networkParameters).getBytes(Charset.forName("UTF-8"))); 114 | 115 | this.encAuthKey = crypto.deriveEncPair(password, authenticationKey); 116 | this.encMnemonic = crypto.deriveEncPair(password, Joiner.on(" ").join(mnemonicCodes).getBytes()); 117 | crypto.clearCachedDerivedKey(); 118 | metadata.setTimestamp(DateUtil.getUTCTime()); 119 | 120 | metadata.setSegWit(null); 121 | this.metadata = metadata; 122 | this.crypto = crypto; 123 | 124 | this.version = VERSION; 125 | this.walletIDs = new ArrayList<>(); 126 | } 127 | 128 | @Override 129 | public EncPair getEncMnemonic() { 130 | return this.encMnemonic; 131 | } 132 | 133 | @Override 134 | public void setEncMnemonic(EncPair encMnemonic) { 135 | this.encMnemonic = encMnemonic; 136 | } 137 | 138 | @Override 139 | public String getMnemonicPath() { 140 | return this.mnemonicPath; 141 | } 142 | 143 | public List getWalletIDs() { 144 | return walletIDs; 145 | } 146 | 147 | @JsonGetter(value = "imTokenMeta") 148 | public Metadata getMetadata() { 149 | return metadata; 150 | } 151 | 152 | public String getIdentifier() { 153 | return identifier; 154 | } 155 | 156 | public String getIpfsId() { 157 | return ipfsId; 158 | } 159 | 160 | public String getEncKey() { 161 | return encKey; 162 | } 163 | 164 | public EncPair getEncAuthKey() { 165 | return encAuthKey; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/Keystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import org.consenlabs.tokencore.foundation.crypto.Crypto; 6 | 7 | import java.util.UUID; 8 | 9 | /** 10 | * Created by xyz on 2018/2/5. 11 | */ 12 | 13 | public abstract class Keystore { 14 | String id; 15 | int version; 16 | 17 | Crypto crypto; 18 | 19 | public Keystore() { 20 | this.id = UUID.randomUUID().toString(); 21 | } 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public void setId(String id) { 27 | this.id = id; 28 | } 29 | 30 | public int getVersion() { 31 | return version; 32 | } 33 | 34 | public void setVersion(int version) { 35 | this.version = version; 36 | } 37 | 38 | public Crypto getCrypto() { 39 | return crypto; 40 | } 41 | 42 | public void setCrypto(Crypto crypto) { 43 | this.crypto = crypto; 44 | } 45 | 46 | public boolean verifyPassword(String password) { 47 | return getCrypto().verifyPassword(password); 48 | } 49 | 50 | public byte[] decryptCiphertext(String password) { 51 | return getCrypto().decrypt(password); 52 | } 53 | } 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/LegacyEOSKeystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.consenlabs.tokencore.foundation.crypto.Crypto; 6 | import org.consenlabs.tokencore.wallet.model.ChainType; 7 | import org.consenlabs.tokencore.wallet.model.KeyPair; 8 | import org.consenlabs.tokencore.wallet.model.Metadata; 9 | import org.consenlabs.tokencore.wallet.model.TokenException; 10 | import org.consenlabs.tokencore.wallet.transaction.EOSKey; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.UUID; 15 | 16 | @Deprecated 17 | public class LegacyEOSKeystore extends V3Keystore { 18 | 19 | public LegacyEOSKeystore() { 20 | } 21 | 22 | public static LegacyEOSKeystore create(Metadata metadata, String accountName, String password, String prvKeyHex) { 23 | return new LegacyEOSKeystore(metadata, accountName, password, prvKeyHex, ""); 24 | } 25 | 26 | public List exportPrivateKeys(String password) { 27 | byte[] decrypted = decryptCiphertext(password); 28 | String wif = new String(decrypted); 29 | EOSKey key = EOSKey.fromWIF(wif); 30 | KeyPair keyPair = new KeyPair(); 31 | keyPair.setPrivateKey(wif); 32 | keyPair.setPublicKey(key.getPublicKeyAsHex()); 33 | return Collections.singletonList(keyPair); 34 | } 35 | 36 | 37 | @Deprecated 38 | public LegacyEOSKeystore(Metadata metadata, String address, String password, String prvKeyHex, String id) { 39 | 40 | if (!metadata.getChainType().equals(ChainType.EOS)) { 41 | throw new TokenException("Only init eos keystore in this constructor"); 42 | } 43 | byte[] prvKeyBytes = prvKeyHex.getBytes(); 44 | this.address = address; 45 | this.crypto = Crypto.createPBKDF2Crypto(password, prvKeyBytes); 46 | metadata.setWalletType(Metadata.V3); 47 | this.metadata = metadata; 48 | this.version = VERSION; 49 | this.id = Strings.isNullOrEmpty(id) ? UUID.randomUUID().toString() : id; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/V3Ignore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import com.fasterxml.jackson.annotation.JsonGetter; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | 6 | import org.consenlabs.tokencore.foundation.crypto.EncPair; 7 | import org.consenlabs.tokencore.wallet.model.Metadata; 8 | 9 | /** 10 | * Created by xyz on 2018/2/8. 11 | */ 12 | public abstract class V3Ignore { 13 | @JsonIgnore 14 | public abstract EncPair getEncMnemonic(); 15 | 16 | @JsonIgnore 17 | @JsonGetter(value = "imTokenMeta") 18 | public abstract Metadata getMetadata(); 19 | 20 | @JsonIgnore 21 | public abstract String getMnemonicPath(); 22 | 23 | @JsonIgnore 24 | public abstract String getEncXPub(); 25 | 26 | @JsonIgnore 27 | public abstract int getMnemonicIndex(); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/V3Keystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.bitcoinj.core.NetworkParameters; 6 | import org.bitcoinj.params.MainNetParams; 7 | import org.bitcoinj.params.TestNet3Params; 8 | import org.consenlabs.tokencore.foundation.crypto.Crypto; 9 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 10 | import org.consenlabs.tokencore.wallet.address.BitcoinAddressCreator; 11 | import org.consenlabs.tokencore.wallet.address.EthereumAddressCreator; 12 | import org.consenlabs.tokencore.wallet.address.SegWitBitcoinAddressCreator; 13 | import org.consenlabs.tokencore.wallet.model.ChainType; 14 | import org.consenlabs.tokencore.wallet.model.Metadata; 15 | import org.consenlabs.tokencore.wallet.model.TokenException; 16 | import org.consenlabs.tokencore.wallet.validators.PrivateKeyValidator; 17 | import org.consenlabs.tokencore.wallet.validators.WIFValidator; 18 | 19 | import java.util.UUID; 20 | 21 | /** 22 | * Created by xyz on 2018/2/5. 23 | */ 24 | 25 | public class V3Keystore extends IMTKeystore implements ExportableKeystore { 26 | public static final int VERSION = 3; 27 | 28 | public V3Keystore() { 29 | } 30 | 31 | public static V3Keystore create(Metadata metadata, String password, String prvKeyHex) { 32 | return new V3Keystore(metadata, password, prvKeyHex, ""); 33 | } 34 | 35 | public V3Keystore(Metadata metadata, String password, String prvKeyHex, String id) { 36 | byte[] prvKeyBytes; 37 | if (metadata.getChainType().equals(ChainType.BITCOIN)) { 38 | NetworkParameters network = metadata.isMainNet() ? MainNetParams.get() : TestNet3Params.get(); 39 | prvKeyBytes = prvKeyHex.getBytes(); 40 | new WIFValidator(prvKeyHex, network).validate(); 41 | if (Metadata.P2WPKH.equals(metadata.getSegWit())) { 42 | this.address = new SegWitBitcoinAddressCreator(network).fromPrivateKey(prvKeyHex); 43 | } else { 44 | this.address = new BitcoinAddressCreator(network).fromPrivateKey(prvKeyHex); 45 | } 46 | } else if (metadata.getChainType().equals(ChainType.ETHEREUM)) { 47 | prvKeyBytes = NumericUtil.hexToBytes(prvKeyHex); 48 | new PrivateKeyValidator(prvKeyHex).validate(); 49 | this.address = new EthereumAddressCreator().fromPrivateKey(prvKeyBytes); 50 | } else { 51 | throw new TokenException("Can't init eos keystore in this constructor"); 52 | } 53 | this.crypto = Crypto.createPBKDF2Crypto(password, prvKeyBytes); 54 | metadata.setWalletType(Metadata.V3); 55 | this.metadata = metadata; 56 | this.version = VERSION; 57 | this.id = Strings.isNullOrEmpty(id) ? UUID.randomUUID().toString() : id; 58 | } 59 | 60 | 61 | @Override 62 | public Keystore changePassword(String oldPassword, String newPassword) { 63 | byte[] decrypted = this.crypto.decrypt(oldPassword); 64 | String prvKeyHex; 65 | if (Metadata.FROM_WIF.equals(getMetadata().getSource())) { 66 | prvKeyHex = new String(decrypted); 67 | } else { 68 | prvKeyHex = NumericUtil.bytesToHex(decrypted); 69 | } 70 | return new V3Keystore(metadata, newPassword, prvKeyHex, this.id); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/V3MnemonicKeystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.google.common.base.Strings; 5 | 6 | import org.bitcoinj.crypto.ChildNumber; 7 | import org.bitcoinj.wallet.DeterministicKeyChain; 8 | import org.bitcoinj.wallet.DeterministicSeed; 9 | import org.consenlabs.tokencore.foundation.crypto.Crypto; 10 | import org.consenlabs.tokencore.foundation.crypto.EncPair; 11 | import org.consenlabs.tokencore.foundation.utils.DateUtil; 12 | import org.consenlabs.tokencore.foundation.utils.MnemonicUtil; 13 | import org.consenlabs.tokencore.wallet.model.BIP44Util; 14 | import org.consenlabs.tokencore.wallet.model.Metadata; 15 | import org.consenlabs.tokencore.wallet.address.AddressCreatorManager; 16 | 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.UUID; 20 | 21 | /** 22 | * Created by xyz on 2018/2/5. 23 | */ 24 | 25 | public class V3MnemonicKeystore extends IMTKeystore implements EncMnemonicKeystore, ExportableKeystore { 26 | private static final int VERSION = 3; 27 | private EncPair encMnemonic; 28 | private String mnemonicPath; 29 | 30 | @Override 31 | public EncPair getEncMnemonic() { 32 | return encMnemonic; 33 | } 34 | 35 | @Override 36 | public void setEncMnemonic(EncPair encMnemonic) { 37 | this.encMnemonic = encMnemonic; 38 | } 39 | 40 | @Override 41 | public String getMnemonicPath() { 42 | return this.mnemonicPath; 43 | } 44 | 45 | public void setMnemonicPath(String mnemonicPath) { 46 | this.mnemonicPath = mnemonicPath; 47 | } 48 | 49 | public V3MnemonicKeystore() { 50 | super(); 51 | } 52 | 53 | public static V3MnemonicKeystore create(Metadata metadata, String password, List mnemonicCodes, String path) { 54 | return new V3MnemonicKeystore(metadata, password, mnemonicCodes, path, ""); 55 | } 56 | 57 | 58 | private V3MnemonicKeystore(Metadata metadata, String password, List mnemonicCodes, String path, String id) { 59 | MnemonicUtil.validateMnemonics(mnemonicCodes); 60 | DeterministicSeed seed = new DeterministicSeed(mnemonicCodes, null, "", 0L); 61 | DeterministicKeyChain keyChain = DeterministicKeyChain.builder().seed(seed).build(); 62 | 63 | this.mnemonicPath = path; 64 | List zeroPath = BIP44Util.generatePath(path); 65 | 66 | byte[] prvKeyBytes = keyChain.getKeyByPath(zeroPath, true).getPrivKeyBytes(); 67 | this.crypto = Crypto.createPBKDF2CryptoWithKDFCached(password, prvKeyBytes); 68 | this.encMnemonic = crypto.deriveEncPair(password, Joiner.on(" ").join(mnemonicCodes).getBytes()); 69 | this.crypto.clearCachedDerivedKey(); 70 | 71 | this.address = AddressCreatorManager.getInstance(metadata.getChainType(), metadata.isMainNet(), metadata.getSegWit()).fromPrivateKey(prvKeyBytes); 72 | metadata.setTimestamp(DateUtil.getUTCTime()); 73 | metadata.setWalletType(Metadata.V3); 74 | this.metadata = metadata; 75 | this.version = VERSION; 76 | this.id = Strings.isNullOrEmpty(id) ? UUID.randomUUID().toString() : id; 77 | } 78 | 79 | 80 | 81 | @Override 82 | public Keystore changePassword(String oldPassword, String newPassword) { 83 | String mnemonic = new String(getCrypto().decryptEncPair(oldPassword, this.getEncMnemonic())); 84 | List mnemonicCodes = Arrays.asList(mnemonic.split(" ")); 85 | return new V3MnemonicKeystore(this.metadata, newPassword, mnemonicCodes, this.mnemonicPath, this.id); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/keystore/WalletKeystore.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.keystore; 2 | 3 | public abstract class WalletKeystore extends Keystore { 4 | String address; 5 | 6 | public String getAddress() { 7 | return address; 8 | } 9 | 10 | public void setAddress(String address) { 11 | this.address = address; 12 | } 13 | 14 | public abstract Keystore changePassword(String oldPassword, String newPassword); 15 | 16 | public WalletKeystore() { super();} 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/BIP44Util.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.bitcoinj.crypto.ChildNumber; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class BIP44Util { 10 | public final static String BITCOIN_MAINNET_PATH = "m/44'/0'/0'"; 11 | public final static String BITCOIN_TESTNET_PATH = "m/44'/1'/0'"; 12 | public final static String BITCOIN_SEGWIT_MAIN_PATH = "m/49'/0'/0'"; 13 | public final static String BITCOIN_SEGWIT_TESTNET_PATH = "m/49'/1'/0'"; 14 | public final static String ETHEREUM_PATH = "m/44'/60'/0'/0/0"; 15 | public final static String EOS_PATH = "m/44'/194'"; 16 | public final static String EOS_SLIP48 = "m/48'/4'/0'/0'/0',m/48'/4'/1'/0'/0'"; 17 | public final static String EOS_LEDGER = "m/44'/194'/0'/0/0"; 18 | 19 | 20 | public static ImmutableList generatePath(String path) { 21 | List list = new ArrayList<>(); 22 | for (String p : path.split("/")) { 23 | if ("m".equalsIgnoreCase(p) || "".equals(p.trim())) { 24 | continue; 25 | } else if (p.charAt(p.length() - 1) == '\'') { 26 | list.add(new ChildNumber(Integer.parseInt(p.substring(0, p.length() - 1)), true)); 27 | } else { 28 | list.add(new ChildNumber(Integer.parseInt(p), false)); 29 | } 30 | } 31 | 32 | ImmutableList.Builder builder = ImmutableList.builder(); 33 | return builder.addAll(list).build(); 34 | } 35 | 36 | public static String getBTCMnemonicPath(String segWit, boolean isMainnet) { 37 | if (Metadata.P2WPKH.equalsIgnoreCase(segWit)) { 38 | return isMainnet ? BITCOIN_SEGWIT_MAIN_PATH : BITCOIN_SEGWIT_TESTNET_PATH; 39 | } else { 40 | return isMainnet ? BITCOIN_MAINNET_PATH : BITCOIN_TESTNET_PATH; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/ChainId.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | public class ChainId { 4 | public static final int BITCOIN_MAINNET = 0; 5 | public static final int BITCOIN_TESTNET = 1; 6 | 7 | // ref: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md 8 | public static final int ETHEREUM_MAINNET = 1; 9 | public static final int ETHEREUM_ROPSTEN = 3; 10 | public static final int ETHEREUM_KOVAN = 42; 11 | public static final int ETHEREUM_CLASSIC_MAINNET = 61; 12 | public static final int ETHEREUM_CLASSIC_TESTNET = 62; 13 | 14 | public static final int ANY = 999; 15 | 16 | public static final String EOS_MAINNET = "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906"; 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/ChainType.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | public class ChainType { 4 | public final static String ETHEREUM = "ETHEREUM"; 5 | public final static String BITCOIN = "BITCOIN"; 6 | public final static String EOS = "EOS"; 7 | 8 | 9 | public static void validate(String type) { 10 | if (!ETHEREUM.equals(type) && !BITCOIN.equals(type) && !EOS.equals(type)) { 11 | throw new TokenException(Messages.WALLET_INVALID_TYPE); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/KeyPair.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | import java.util.Objects; 4 | 5 | public class KeyPair { 6 | 7 | String privateKey; 8 | String publicKey; 9 | 10 | public KeyPair() { 11 | } 12 | 13 | public KeyPair(String privateKey, String publicKey) { 14 | this.privateKey = privateKey; 15 | this.publicKey = publicKey; 16 | } 17 | 18 | public String getPrivateKey() { 19 | return privateKey; 20 | } 21 | 22 | public void setPrivateKey(String privateKey) { 23 | this.privateKey = privateKey; 24 | } 25 | 26 | public String getPublicKey() { 27 | return publicKey; 28 | } 29 | 30 | public void setPublicKey(String publicKey) { 31 | this.publicKey = publicKey; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object o) { 36 | if (this == o) return true; 37 | if (o == null || getClass() != o.getClass()) return false; 38 | KeyPair keyPair = (KeyPair) o; 39 | return Objects.equals(privateKey, keyPair.privateKey) && 40 | Objects.equals(publicKey, keyPair.publicKey); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | 46 | return Objects.hash(privateKey, publicKey); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/Messages.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | public class Messages { 4 | public static final String UNKNOWN = "unknown"; 5 | 6 | public static final String WALLET_INVALID_PASSWORD = "password_incorrect"; 7 | public static final String PASSWORD_BLANK = "password_blank"; 8 | public static final String PASSWORD_WEAK = "password_weak"; 9 | 10 | public static final String MNEMONIC_BAD_WORD = "mnemonic_word_invalid"; 11 | public static final String MNEMONIC_INVALID_LENGTH = "mnemonic_length_invalid"; 12 | public static final String MNEMONIC_CHECKSUM = "mnemonic_checksum_invalid"; 13 | 14 | public static final String INVALID_MNEMONIC_PATH = "invalid_mnemonic_path"; 15 | 16 | public static final String APPLICATION_NOT_READY = "application_not_ready"; 17 | 18 | public static final String WALLET_NOT_FOUND = "wallet_not_found"; 19 | 20 | public static final String WALLET_INVALID_KEYSTORE = "keystore_invalid"; 21 | public static final String WALLET_STORE_FAIL = "store_wallet_failed"; 22 | public static final String WALLET_INVALID = "keystore_invalid"; 23 | public static final String KDF_UNSUPPORTED = "kdf_unsupported"; 24 | 25 | public static final String WALLET_INVALID_TYPE = "unsupported_chain"; 26 | public static final String WALLET_INVALID_ADDRESS = "address_invalid"; 27 | 28 | public static final String INVALID_TRANSACTION_DATA = "transaction_data_invalid"; 29 | public static final String WALLET_EXISTS = "address_already_exist"; 30 | public static final String INVALID_WALLET_VERSION = "keystore_version_invalid"; 31 | public static final String WALLET_HD_NOT_SUPPORT_PRIVATE = "hd_not_support_private"; 32 | public static final String WALLET_SHA256 = "sha256"; 33 | 34 | public static final String IPFS_CHECK_SIGNATURE = "check_signature"; 35 | public static final String NOT_UTF8 = "not_utf8"; 36 | 37 | public static final String MAC_UNMATCH = "mac_unmatch"; 38 | public static final String PRIVATE_KEY_ADDRESS_NOT_MATCH = "private_key_address_not_match"; 39 | 40 | 41 | public static final String CAN_NOT_TO_JSON = "can_not_to_json" ; 42 | public static final String CAN_NOT_FROM_JSON = "can_not_from_json" ; 43 | 44 | public static final String SCRYPT_PARAMS_INVALID = "scrypt_params_invalid"; 45 | public static final String PRF_UNSUPPORTED = "prf_unsupported"; 46 | public static final String KDF_PARAMS_INVALID = "kdf_params_invalid"; 47 | public static final String CIPHER_FAIL = "cipher_unsupported"; 48 | 49 | 50 | 51 | public static final String INVALID_BIG_NUMBER = "big_number_invalid"; 52 | public static final String INVALID_NEGATIVE = "negative_invalid"; 53 | public static final String INVALID_HEX = "hex_invalid"; 54 | 55 | public static final String INSUFFICIENT_FUNDS = "insufficient_funds"; 56 | public static final String CAN_NOT_FOUND_PRIVATE_KEY = "can_not_found_private_key"; 57 | public static final String WIF_WRONG_NETWORK = "wif_wrong_network"; 58 | public static final String WIF_INVALID = "wif_invalid"; 59 | public static final String PRIVATE_KEY_INVALID = "privatekey_invalid"; 60 | public static final String CAN_NOT_EXPORT_MNEMONIC = "not_support_export_keystore"; 61 | 62 | public static final String INVALID_IDENTITY = "invalid_identity"; 63 | 64 | public static final String UNSUPPORT_SEND_TARGET = "not_support_send_target"; 65 | public static final String ILLEGAL_OPERATION = "illegal_operation"; 66 | public static final String UNSUPPORT_ENCRYPTION_DATA_VERSION = "unsupport_encryption_data_version"; 67 | public static final String INVALID_ENCRYPTION_DATA_SIGNATURE = "invalid_encryption_data_signature"; 68 | 69 | public static final String ENCRYPT_XPUB_ERROR = "encrypt_xpub_error"; 70 | 71 | public static final String SEGWIT_NEEDS_COMPRESS_PUBLIC_KEY = "segwit_needs_compress_public_key"; 72 | public static final String EOS_PRIVATE_PUBLIC_NOT_MATCH = "eos_private_public_not_match"; 73 | public static final String EOS_PUBLIC_KEY_NOT_FOUND = "eos_public_key_not_found"; 74 | public static final String EOS_ACCOUNT_NAME_INVALID = "eos_account_name_invalid"; 75 | 76 | public static final String AMOUNT_LESS_THAN_MINIMUM = "amount_less_than_minimum"; 77 | public static final String KEYSTORE_CONTAINS_INVALID_PRIVATE_KEY = "keystore_contains_invalid_private_key"; 78 | 79 | public static final String REQUIRED_EOS_WALLET = "required_eos_wallet"; 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/Metadata.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class Metadata implements Cloneable { 9 | public static final String FROM_MNEMONIC = "MNEMONIC"; 10 | public static final String FROM_KEYSTORE = "KEYSTORE"; 11 | public static final String FROM_PRIVATE = "PRIVATE"; 12 | public static final String FROM_WIF = "WIF"; 13 | public static final String FROM_NEW_IDENTITY = "NEW_IDENTITY"; 14 | public static final String FROM_RECOVERED_IDENTITY = "RECOVERED_IDENTITY"; 15 | 16 | public static final String P2WPKH = "P2WPKH"; 17 | public static final String NONE = "NONE"; 18 | 19 | public static final String NORMAL = "NORMAL"; 20 | 21 | public static final String HD = "HD"; 22 | public static final String RANDOM = "RANDOM"; 23 | public static final String HD_SHA256 = "HD_SHA256"; 24 | public static final String V3 = "V3"; 25 | 26 | 27 | private String name; 28 | private String passwordHint; 29 | private String chainType; 30 | private long timestamp; 31 | private String network; 32 | private List backup = new ArrayList<>(); 33 | private String source; 34 | private String mode = NORMAL; 35 | private String walletType; 36 | private String segWit; 37 | 38 | // for jackson serial 39 | public Metadata() { 40 | } 41 | 42 | 43 | @Override 44 | public Metadata clone() { 45 | 46 | Metadata metadata = null; 47 | try { 48 | metadata = (Metadata) super.clone(); 49 | } catch (CloneNotSupportedException ex) { 50 | throw new TokenException("Clone metadata filed"); 51 | } 52 | metadata.backup = new ArrayList<>(backup); 53 | return metadata; 54 | } 55 | 56 | public String getSegWit() { 57 | return segWit; 58 | } 59 | 60 | public void setSegWit(String segWit) { 61 | this.segWit = segWit; 62 | } 63 | 64 | public Metadata(String type, String network, String name, String passwordHint) { 65 | this.chainType = type; 66 | this.name = name; 67 | this.passwordHint = passwordHint; 68 | 69 | this.timestamp = System.currentTimeMillis() / 1000; 70 | this.network = network; 71 | } 72 | 73 | public Metadata(String type, String network, String name, String passwordHint, String segWit) { 74 | this.chainType = type; 75 | this.name = name; 76 | this.passwordHint = passwordHint; 77 | this.timestamp = System.currentTimeMillis() / 1000; 78 | this.network = network; 79 | this.segWit = segWit; 80 | } 81 | 82 | public String getName() { 83 | return name; 84 | } 85 | 86 | public void setName(String name) { 87 | this.name = name; 88 | } 89 | 90 | public String getPasswordHint() { 91 | return passwordHint; 92 | } 93 | 94 | public void setPasswordHint(String passwordHint) { 95 | this.passwordHint = passwordHint; 96 | } 97 | 98 | public String getChainType() { 99 | return chainType; 100 | } 101 | 102 | public void setChainType(String chainType) { 103 | this.chainType = chainType; 104 | } 105 | 106 | public long getTimestamp() { 107 | return timestamp; 108 | } 109 | 110 | public void setTimestamp(long timestamp) { 111 | this.timestamp = timestamp; 112 | } 113 | 114 | public List getBackup() { 115 | return backup; 116 | } 117 | 118 | public void setBackup(List backup) { 119 | this.backup = backup; 120 | } 121 | 122 | public String getSource() { 123 | return source; 124 | } 125 | 126 | public void setSource(String source) { 127 | this.source = source; 128 | } 129 | 130 | public String getMode() { 131 | return mode; 132 | } 133 | 134 | public void setMode(String mode) { 135 | this.mode = mode; 136 | } 137 | 138 | public String getWalletType() { 139 | return walletType; 140 | } 141 | 142 | public void setWalletType(String walletType) { 143 | this.walletType = walletType; 144 | } 145 | 146 | @JsonIgnore 147 | public Boolean isMainNet() { 148 | return Network.MAINNET.equalsIgnoreCase(network); 149 | } 150 | 151 | public String getNetwork() { 152 | return network; 153 | } 154 | 155 | public void setNetwork(String network) { 156 | this.network = network; 157 | } 158 | 159 | 160 | } 161 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/MnemonicAndPath.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | public class MnemonicAndPath { 4 | public String getMnemonic() { 5 | return mnemonic; 6 | } 7 | 8 | public String getPath() { 9 | return path; 10 | } 11 | 12 | private final String mnemonic; 13 | private final String path; 14 | 15 | public MnemonicAndPath(String mnemonic, String path) { 16 | this.path = path; 17 | this.mnemonic = mnemonic; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/Network.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | /** 4 | * Created by xyz on 2018/3/7. 5 | */ 6 | 7 | public class Network { 8 | public static final String MAINNET = "MAINNET"; 9 | public static final String TESTNET = "TESTNET"; 10 | public static final String KOVAN = "KOVAN"; 11 | public static final String ROPSTEN = "ROPSTEN"; 12 | 13 | private String network; 14 | 15 | public Network(String network) { 16 | this.network = network; 17 | } 18 | 19 | public boolean isMainnet() { 20 | return MAINNET.equalsIgnoreCase(this.network); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/model/TokenException.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.model; 2 | 3 | public class TokenException extends RuntimeException { 4 | private static final long serialVersionUID = 4300404932829403534L; 5 | 6 | public TokenException(String message) { 7 | super(message); 8 | } 9 | 10 | public TokenException(String message, Exception e) { 11 | super(message, e); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/EOSECDSASigner.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import org.bitcoinj.core.ECKey; 4 | import org.spongycastle.crypto.CipherParameters; 5 | import org.spongycastle.crypto.params.ECDomainParameters; 6 | import org.spongycastle.crypto.params.ECKeyParameters; 7 | import org.spongycastle.crypto.params.ECPrivateKeyParameters; 8 | import org.spongycastle.crypto.params.ECPublicKeyParameters; 9 | import org.spongycastle.crypto.params.ParametersWithRandom; 10 | import org.spongycastle.crypto.signers.DSAKCalculator; 11 | import org.spongycastle.math.ec.ECAlgorithms; 12 | import org.spongycastle.math.ec.ECMultiplier; 13 | import org.spongycastle.math.ec.ECPoint; 14 | import org.spongycastle.math.ec.FixedPointCombMultiplier; 15 | 16 | import java.math.BigInteger; 17 | import java.security.SecureRandom; 18 | 19 | import static java.math.BigDecimal.ZERO; 20 | import static java.math.BigInteger.ONE; 21 | 22 | /** 23 | * !!! We copy the code from BitcoinJ !!! 24 | * EOS extends the data and then hash it by rcf6979 to generate Canonical Signatures. 25 | * The BitcoinJ doesn't expose the same api as libsecp256k1, we can't overwrite it by inheriting 26 | */ 27 | public class EOSECDSASigner { 28 | private final DSAKCalculator kCalculator; 29 | 30 | private ECKeyParameters key; 31 | private SecureRandom random; 32 | 33 | /** 34 | * Configuration with an alternate, possibly deterministic calculator of K. 35 | * 36 | * @param kCalculator a K value calculator. 37 | */ 38 | public EOSECDSASigner(DSAKCalculator kCalculator) { 39 | this.kCalculator = kCalculator; 40 | } 41 | 42 | public void init( 43 | boolean forSigning, 44 | CipherParameters param) { 45 | SecureRandom providedRandom = null; 46 | 47 | if (forSigning) { 48 | if (param instanceof ParametersWithRandom) { 49 | ParametersWithRandom rParam = (ParametersWithRandom) param; 50 | 51 | this.key = (ECPrivateKeyParameters) rParam.getParameters(); 52 | providedRandom = rParam.getRandom(); 53 | } else { 54 | this.key = (ECPrivateKeyParameters) param; 55 | } 56 | } else { 57 | this.key = (ECPublicKeyParameters) param; 58 | } 59 | 60 | this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom); 61 | } 62 | 63 | // 5.3 pg 28 64 | 65 | /** 66 | * generate a signature for the given message using the key we were 67 | * initialised with. For conventional DSA the message should be a SHA-1 68 | * hash of the message of interest. 69 | * 70 | * @param message the message that will be verified later. 71 | */ 72 | public BigInteger[] generateSignature( 73 | byte[] message) { 74 | ECDomainParameters ec = key.getParameters(); 75 | BigInteger n = ec.getN(); 76 | BigInteger e = calculateE(n, message); 77 | BigInteger d = ((ECPrivateKeyParameters) key).getD(); 78 | 79 | int nonce = 1; 80 | BigInteger r, s; 81 | while (true) { 82 | 83 | kCalculator.init(n, d, message); 84 | ECMultiplier basePointMultiplier = createBasePointMultiplier(); 85 | 86 | // 5.3.2 87 | do // generate s 88 | { 89 | BigInteger k = BigInteger.ZERO; 90 | do // generate r 91 | { 92 | k = kCalculator.nextK(); 93 | for (int i = 0; i < nonce; i++) { 94 | k = kCalculator.nextK(); 95 | } 96 | 97 | ECPoint p = basePointMultiplier.multiply(ec.getG(), k).normalize(); 98 | 99 | // 5.3.3 100 | r = p.getAffineXCoord().toBigInteger().mod(n); 101 | } 102 | while (r.equals(ZERO)); 103 | 104 | // Compute s = (k^-1)*(h + Kx*privkey) 105 | s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); 106 | } 107 | while (s.equals(ZERO)); 108 | 109 | byte[] der = new ECKey.ECDSASignature(r, s).toCanonicalised().encodeToDER(); 110 | 111 | int lenR = der[3]; 112 | int lenS = der[5 + lenR]; 113 | if (lenR == 32 && lenS == 32) { 114 | break; 115 | } 116 | nonce++; 117 | } 118 | 119 | return new BigInteger[]{r, s}; 120 | } 121 | 122 | // 5.4 pg 29 123 | 124 | /** 125 | * return true if the value r and s represent a DSA signature for 126 | * the passed in message (for standard DSA the message should be 127 | * a SHA-1 hash of the real message to be verified). 128 | */ 129 | public boolean verifySignature( 130 | byte[] message, 131 | BigInteger r, 132 | BigInteger s) { 133 | ECDomainParameters ec = key.getParameters(); 134 | BigInteger n = ec.getN(); 135 | BigInteger e = calculateE(n, message); 136 | 137 | // r in the range [1,n-1] 138 | if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0) { 139 | return false; 140 | } 141 | 142 | // s in the range [1,n-1] 143 | if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0) { 144 | return false; 145 | } 146 | 147 | BigInteger c = s.modInverse(n); 148 | 149 | BigInteger u1 = e.multiply(c).mod(n); 150 | BigInteger u2 = r.multiply(c).mod(n); 151 | 152 | ECPoint G = ec.getG(); 153 | ECPoint Q = ((ECPublicKeyParameters) key).getQ(); 154 | 155 | ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2).normalize(); 156 | 157 | // components must be bogus. 158 | if (point.isInfinity()) { 159 | return false; 160 | } 161 | 162 | BigInteger v = point.getAffineXCoord().toBigInteger().mod(n); 163 | 164 | return v.equals(r); 165 | } 166 | 167 | protected BigInteger calculateE(BigInteger n, byte[] message) { 168 | int log2n = n.bitLength(); 169 | int messageBitLength = message.length * 8; 170 | 171 | BigInteger e = new BigInteger(1, message); 172 | if (log2n < messageBitLength) { 173 | e = e.shiftRight(messageBitLength - log2n); 174 | } 175 | return e; 176 | } 177 | 178 | protected ECMultiplier createBasePointMultiplier() { 179 | return new FixedPointCombMultiplier(); 180 | } 181 | 182 | protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided) { 183 | return !needed ? null : (provided != null) ? provided : new SecureRandom(); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/EOSKey.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import org.bitcoinj.core.AddressFormatException; 4 | import org.bitcoinj.core.Base58; 5 | import org.bitcoinj.core.DumpedPrivateKey; 6 | import org.bitcoinj.core.ECKey; 7 | import org.bitcoinj.core.VersionedChecksummedBytes; 8 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 9 | import org.spongycastle.crypto.digests.RIPEMD160Digest; 10 | 11 | import java.util.Arrays; 12 | 13 | public class EOSKey extends VersionedChecksummedBytes { 14 | 15 | protected EOSKey(String encoded) throws AddressFormatException { 16 | super(encoded); 17 | } 18 | 19 | protected EOSKey(int version, byte[] bytes) { 20 | super(version, bytes); 21 | } 22 | 23 | public static EOSKey fromWIF(String wif) { 24 | return new EOSKey(wif); 25 | } 26 | 27 | public static EOSKey fromPrivate(byte[] prvKey) { 28 | // EOS doesn't distinguish between mainnet and testnet. 29 | return new EOSKey(128, prvKey); 30 | } 31 | 32 | public static String privateToPublicKey(byte[] prvKey) { 33 | return new EOSKey(128, prvKey).getPublicKeyAsHex(); 34 | } 35 | 36 | public String getPublicKeyAsHex() { 37 | ECKey ecKey = ECKey.fromPrivate(bytes); 38 | byte[] pubKeyData = ecKey.getPubKey(); 39 | RIPEMD160Digest digest = new RIPEMD160Digest(); 40 | digest.update(pubKeyData, 0, pubKeyData.length); 41 | byte[] out = new byte[20]; 42 | digest.doFinal(out, 0); 43 | byte[] checksumBytes = Arrays.copyOfRange(out, 0, 4); 44 | 45 | pubKeyData = ByteUtil.concat(pubKeyData, checksumBytes); 46 | return "EOS" + Base58.encode(pubKeyData); 47 | } 48 | 49 | public byte[] getPrivateKey() { 50 | return bytes; 51 | } 52 | 53 | ECKey getECKey() { 54 | return ECKey.fromPrivate(bytes, true); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/EOSSign.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import org.bitcoinj.core.Base58; 4 | import org.bitcoinj.core.ECKey; 5 | import org.bitcoinj.core.Sha256Hash; 6 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 7 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 8 | import org.consenlabs.tokencore.wallet.model.TokenException; 9 | import org.spongycastle.crypto.digests.RIPEMD160Digest; 10 | import org.spongycastle.crypto.digests.SHA256Digest; 11 | import org.spongycastle.crypto.params.ECPrivateKeyParameters; 12 | import org.spongycastle.crypto.signers.HMacDSAKCalculator; 13 | 14 | import java.math.BigInteger; 15 | import java.util.Arrays; 16 | 17 | import static org.bitcoinj.core.ECKey.CURVE; 18 | 19 | public class EOSSign { 20 | 21 | @Deprecated 22 | public static String sign(byte[] dataSha256, String wif) { 23 | SignatureData signatureData = signAsRecoverable(dataSha256, EOSKey.fromWIF(wif).getECKey()); 24 | byte[] sigResult = ByteUtil.concat(NumericUtil.intToBytes(signatureData.getV()), signatureData.getR()); 25 | sigResult = ByteUtil.concat(sigResult, signatureData.getS()); 26 | return serialEOSSignature(sigResult); 27 | } 28 | 29 | public static String sign(byte[] dataSha256, byte[] prvKey) { 30 | ECKey ecKey = EOSKey.fromPrivate(prvKey).getECKey(); 31 | SignatureData signatureData = signAsRecoverable(dataSha256, ecKey); 32 | byte[] sigResult = ByteUtil.concat(NumericUtil.intToBytes(signatureData.getV()), signatureData.getR()); 33 | sigResult = ByteUtil.concat(sigResult, signatureData.getS()); 34 | return serialEOSSignature(sigResult); 35 | } 36 | 37 | 38 | private static SignatureData signAsRecoverable(byte[] value, ECKey ecKey) { 39 | int recId = -1; 40 | ECKey.ECDSASignature sig = eosSign(value, ecKey.getPrivKey()); 41 | for (int i = 0; i < 4; i++) { 42 | ECKey recoverKey = ECKey.recoverFromSignature(i, sig, Sha256Hash.wrap(value), false); 43 | if (recoverKey != null && recoverKey.getPubKeyPoint().equals(ecKey.getPubKeyPoint())) { 44 | recId = i; 45 | break; 46 | } 47 | } 48 | 49 | if (recId == -1) { 50 | throw new TokenException("Could not construct a recoverable key. This should never happen."); 51 | } 52 | int headerByte = recId + 27 + 4; 53 | // 1 header + 32 bytes for R + 32 bytes for S 54 | byte v = (byte) headerByte; 55 | byte[] r = NumericUtil.bigIntegerToBytesWithZeroPadded(sig.r, 32); 56 | byte[] s = NumericUtil.bigIntegerToBytesWithZeroPadded(sig.s, 32); 57 | 58 | return new SignatureData(v, r, s); 59 | 60 | } 61 | 62 | private static ECKey.ECDSASignature eosSign(byte[] input, BigInteger privateKeyForSigning) { 63 | EOSECDSASigner signer = new EOSECDSASigner(new MyHMacDSAKCalculator(new SHA256Digest())); 64 | ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, CURVE); 65 | signer.init(true, privKey); 66 | BigInteger[] components = signer.generateSignature(input); 67 | return new ECKey.ECDSASignature(components[0], components[1]).toCanonicalised(); 68 | } 69 | 70 | private static String serialEOSSignature(byte[] data) { 71 | byte[] toHash = ByteUtil.concat(data, "K1".getBytes()); 72 | RIPEMD160Digest digest = new RIPEMD160Digest(); 73 | digest.update(toHash, 0, toHash.length); 74 | byte[] out = new byte[20]; 75 | digest.doFinal(out, 0); 76 | byte[] checksumBytes = Arrays.copyOfRange(out, 0, 4); 77 | data = ByteUtil.concat(data, checksumBytes); 78 | return "SIG_K1_" + Base58.encode(data); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/EOSTransaction.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import org.consenlabs.tokencore.foundation.crypto.Hash; 4 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 5 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 6 | import org.consenlabs.tokencore.wallet.Wallet; 7 | import org.consenlabs.tokencore.wallet.keystore.V3Keystore; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public class EOSTransaction implements TransactionSigner { 14 | 15 | private byte[] txBuf; 16 | private List txsToSign; 17 | 18 | public EOSTransaction(byte[] txBuf) { 19 | this.txBuf = txBuf; 20 | } 21 | 22 | public EOSTransaction(List txsToSign) { 23 | this.txsToSign = txsToSign; 24 | } 25 | 26 | 27 | @Deprecated 28 | @Override 29 | public TxSignResult signTransaction(String chainId, String password, Wallet wallet) { 30 | String transactionID = NumericUtil.bytesToHex(Hash.sha256(txBuf)); 31 | txBuf = ByteUtil.concat(NumericUtil.hexToBytes(chainId), txBuf); 32 | 33 | byte[] zeroBuf = new byte[32]; 34 | Arrays.fill(zeroBuf, (byte) 0); 35 | txBuf = ByteUtil.concat(txBuf, zeroBuf); 36 | 37 | String wif = wallet.exportPrivateKey(password); 38 | String signed = EOSSign.sign(Hash.sha256(txBuf), wif); 39 | 40 | return new TxSignResult(signed, transactionID); 41 | } 42 | 43 | public List signTransactions(String chainId, String password, Wallet wallet) { 44 | List results = new ArrayList<>(txsToSign.size()); 45 | for (ToSignObj toSignObj : txsToSign) { 46 | 47 | byte[] txBuf = NumericUtil.hexToBytes(toSignObj.txHex); 48 | String transactionID = NumericUtil.bytesToHex(Hash.sha256(txBuf)); 49 | 50 | byte[] txChainIDBuf = ByteUtil.concat(NumericUtil.hexToBytes(chainId), txBuf); 51 | 52 | byte[] zeroBuf = new byte[32]; 53 | Arrays.fill(zeroBuf, (byte) 0); 54 | byte[] fullTxBuf = ByteUtil.concat(txChainIDBuf, zeroBuf); 55 | 56 | byte[] hashedTx = Hash.sha256(fullTxBuf); 57 | 58 | List signatures = new ArrayList<>(toSignObj.publicKeys.size()); 59 | for (String pubKey : toSignObj.publicKeys) { 60 | String signed; 61 | if (wallet.getKeystore().getVersion() == V3Keystore.VERSION) { 62 | signed = EOSSign.sign(hashedTx, wallet.exportPrivateKey(password)); 63 | } else { 64 | signed = EOSSign.sign(hashedTx, wallet.decryptPrvKeyFor(pubKey, password)); 65 | } 66 | 67 | signatures.add(signed); 68 | } 69 | 70 | TxMultiSignResult signedResult = new TxMultiSignResult(transactionID, signatures); 71 | results.add(signedResult); 72 | } 73 | return results; 74 | } 75 | 76 | public static class ToSignObj { 77 | private String txHex; 78 | private List publicKeys; 79 | 80 | public String getTxHex() { 81 | return txHex; 82 | } 83 | 84 | public void setTxHex(String txHex) { 85 | this.txHex = txHex; 86 | } 87 | 88 | public List getPublicKeys() { 89 | return publicKeys; 90 | } 91 | 92 | public void setPublicKeys(List publicKeys) { 93 | this.publicKeys = publicKeys; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/EthereumSign.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | 4 | import com.subgraph.orchid.encoders.Hex; 5 | 6 | import org.bitcoinj.core.Sha256Hash; 7 | import org.consenlabs.tokencore.foundation.crypto.Hash; 8 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 9 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 10 | import org.bitcoinj.core.ECKey; 11 | import org.consenlabs.tokencore.wallet.address.EthereumAddressCreator; 12 | 13 | import java.math.BigInteger; 14 | import java.nio.charset.Charset; 15 | import java.security.SignatureException; 16 | import java.util.Arrays; 17 | import java.util.Locale; 18 | 19 | import static com.google.common.base.Preconditions.checkState; 20 | 21 | /** 22 | * Created by xyz on 2017/12/20. 23 | */ 24 | 25 | public class EthereumSign { 26 | 27 | public static String personalSign(String data, byte[] prvKeyBytes) { 28 | byte[] dataBytes = dataToBytes(data); 29 | int msgLen = dataBytes.length; 30 | String headerMsg = String.format(Locale.ENGLISH, "\u0019Ethereum Signed Message:\n%d", msgLen); 31 | byte[] headerMsgBytes = headerMsg.getBytes(Charset.forName("UTF-8")); 32 | byte[] dataToSign = ByteUtil.concat(headerMsgBytes, dataBytes); 33 | return signMessage(dataToSign, prvKeyBytes).toString(); 34 | } 35 | 36 | public static String sign(String data, byte[] prvKeyBytes) { 37 | return signMessage(dataToBytes(data), prvKeyBytes).toString(); 38 | } 39 | 40 | public static BigInteger ecRecover(String data, String signature) throws SignatureException { 41 | byte[] msgBytes = dataToBytes(data); 42 | signature = NumericUtil.cleanHexPrefix(signature); 43 | byte[] r = Hex.decode(signature.substring(0, 64)); 44 | byte[] s = Hex.decode(signature.substring(64, 128)); 45 | int receiveId = Integer.valueOf(signature.substring(128), 16); 46 | SignatureData signatureData = new SignatureData((byte) receiveId, r, s); 47 | 48 | return signedMessageToKey(msgBytes, signatureData); 49 | } 50 | 51 | public static String recoverAddress(String data, String signature) { 52 | try { 53 | BigInteger pubKey = ecRecover(data, signature); 54 | return new EthereumAddressCreator().fromPublicKey(pubKey); 55 | } catch (SignatureException e) { 56 | return ""; 57 | } 58 | } 59 | 60 | private static byte[] dataToBytes(String data) { 61 | byte[] messageBytes; 62 | if (NumericUtil.isValidHex(data)) { 63 | messageBytes = NumericUtil.hexToBytes(data); 64 | } else { 65 | messageBytes = data.getBytes(Charset.forName("UTF-8")); 66 | } 67 | return messageBytes; 68 | } 69 | 70 | static SignatureData signMessage(byte[] message, byte[] prvKeyBytes) { 71 | ECKey ecKey = ECKey.fromPrivate(prvKeyBytes); 72 | byte[] messageHash = Hash.keccak256(message); 73 | return signAsRecoverable(messageHash, ecKey); 74 | } 75 | 76 | /** 77 | * Given an arbitrary piece of text and an Ethereum message signature encoded in bytes, 78 | * returns the public key that was used to sign it. This can then be compared to the expected 79 | * public key to determine if the signature was correct. 80 | * 81 | * @param message RLP encoded message. 82 | * @param signatureData The message signature components 83 | * @return the public key used to sign the message 84 | * @throws SignatureException If the public key could not be recovered or if there was a 85 | * signature format error. 86 | */ 87 | private static BigInteger signedMessageToKey(byte[] message, SignatureData signatureData) throws SignatureException { 88 | 89 | byte[] r = signatureData.getR(); 90 | byte[] s = signatureData.getS(); 91 | checkState(r != null && r.length == 32, "r must be 32 bytes"); 92 | checkState(s != null && s.length == 32, "s must be 32 bytes"); 93 | 94 | int header = signatureData.getV() & 0xFF; 95 | // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, 96 | // 0x1D = second key with even y, 0x1E = second key with odd y 97 | if (header < 27 || header > 34) { 98 | throw new SignatureException("Header byte out of range: " + header); 99 | } 100 | 101 | ECKey.ECDSASignature sig = new ECKey.ECDSASignature( 102 | new BigInteger(1, signatureData.getR()), 103 | new BigInteger(1, signatureData.getS())); 104 | 105 | byte[] messageHash = Hash.keccak256(message); 106 | int recId = header - 27; 107 | ECKey key = ECKey.recoverFromSignature(recId, sig, Sha256Hash.wrap(messageHash), false); 108 | if (key == null) { 109 | throw new SignatureException("Could not recover public key from signature"); 110 | } 111 | byte[] pubKeyBytes = key.getPubKeyPoint().getEncoded(false); 112 | return NumericUtil.bytesToBigInteger(Arrays.copyOfRange(pubKeyBytes, 1, pubKeyBytes.length)); 113 | } 114 | 115 | public static SignatureData signAsRecoverable(byte[] value, ECKey ecKey) { 116 | 117 | ECKey.ECDSASignature sig = ecKey.sign(Sha256Hash.wrap(value)); 118 | 119 | // Now we have to work backwards to figure out the recId needed to recover the signature. 120 | int recId = -1; 121 | for (int i = 0; i < 4; i++) { 122 | ECKey recoverKey = ECKey.recoverFromSignature(i, sig, Sha256Hash.wrap(value), false); 123 | if (recoverKey != null && recoverKey.getPubKeyPoint().equals(ecKey.getPubKeyPoint())) { 124 | recId = i; 125 | break; 126 | } 127 | } 128 | if (recId == -1) { 129 | throw new RuntimeException( 130 | "Could not construct a recoverable key. This should never happen."); 131 | } 132 | 133 | int headerByte = recId + 27; 134 | 135 | // 1 header + 32 bytes for R + 32 bytes for S 136 | byte v = (byte) headerByte; 137 | byte[] r = NumericUtil.bigIntegerToBytesWithZeroPadded(sig.r, 32); 138 | byte[] s = NumericUtil.bigIntegerToBytesWithZeroPadded(sig.s, 32); 139 | 140 | return new SignatureData(v, r, s); 141 | } 142 | 143 | 144 | } 145 | 146 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/EthereumTransaction.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | 4 | import org.consenlabs.tokencore.foundation.crypto.Hash; 5 | import org.consenlabs.tokencore.foundation.rlp.RlpEncoder; 6 | import org.consenlabs.tokencore.foundation.rlp.RlpList; 7 | import org.consenlabs.tokencore.foundation.rlp.RlpString; 8 | import org.consenlabs.tokencore.foundation.rlp.RlpType; 9 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 10 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 11 | import org.consenlabs.tokencore.wallet.Wallet; 12 | 13 | import java.math.BigInteger; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Transaction class used for signing transactions locally.
19 | * For the specification, refer to p4 of the 20 | *

21 | * yellow paper. 22 | */ 23 | public class EthereumTransaction implements TransactionSigner { 24 | 25 | private BigInteger nonce; 26 | private BigInteger gasPrice; 27 | private BigInteger gasLimit; 28 | private String to; 29 | private BigInteger value; 30 | private String data; 31 | 32 | public EthereumTransaction(BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to, 33 | BigInteger value, String data) { 34 | this.nonce = nonce; 35 | this.gasPrice = gasPrice; 36 | this.gasLimit = gasLimit; 37 | this.to = to; 38 | this.value = value; 39 | 40 | if (data != null) { 41 | this.data = NumericUtil.cleanHexPrefix(data); 42 | } 43 | } 44 | 45 | public BigInteger getNonce() { 46 | return nonce; 47 | } 48 | 49 | public BigInteger getGasPrice() { 50 | return gasPrice; 51 | } 52 | 53 | public BigInteger getGasLimit() { 54 | return gasLimit; 55 | } 56 | 57 | public String getTo() { 58 | return to; 59 | } 60 | 61 | public BigInteger getValue() { 62 | return value; 63 | } 64 | 65 | public String getData() { 66 | return data; 67 | } 68 | 69 | @Override 70 | public TxSignResult signTransaction(String chainID, String password, Wallet wallet) { 71 | 72 | String signedTx = signTransaction(Integer.parseInt(chainID), wallet.decryptMainKey(password)); 73 | String txHash = this.calcTxHash(signedTx); 74 | return new TxSignResult(signedTx, txHash); 75 | } 76 | 77 | String signTransaction(int chainId, byte[] privateKey) { 78 | SignatureData signatureData = new SignatureData(chainId, new byte[]{}, new byte[]{}); 79 | byte[] encodedTransaction = encodeToRLP(signatureData); 80 | signatureData = EthereumSign.signMessage(encodedTransaction, privateKey); 81 | 82 | SignatureData eip155SignatureData = createEip155SignatureData(signatureData, chainId); 83 | byte[] rawSignedTx = encodeToRLP(eip155SignatureData); 84 | return NumericUtil.bytesToHex(rawSignedTx); 85 | } 86 | 87 | String calcTxHash(String signedTx) { 88 | return NumericUtil.prependHexPrefix(Hash.keccak256(signedTx)); 89 | } 90 | 91 | private static SignatureData createEip155SignatureData(SignatureData signatureData, int chainId) { 92 | int v = signatureData.getV() + (chainId * 2) + 8; 93 | 94 | return new SignatureData(v, signatureData.getR(), signatureData.getS()); 95 | } 96 | 97 | byte[] encodeToRLP(SignatureData signatureData) { 98 | List values = asRlpValues(signatureData); 99 | RlpList rlpList = new RlpList(values); 100 | return RlpEncoder.encode(rlpList); 101 | } 102 | 103 | List asRlpValues(SignatureData signatureData) { 104 | List result = new ArrayList<>(); 105 | 106 | result.add(RlpString.create(getNonce())); 107 | result.add(RlpString.create(getGasPrice())); 108 | result.add(RlpString.create(getGasLimit())); 109 | 110 | // an empty to address (contract creation) should not be encoded as a numeric 0 value 111 | String to = getTo(); 112 | if (to != null && to.length() > 0) { 113 | // addresses that start with zeros should be encoded with the zeros included, not 114 | // as numeric values 115 | result.add(RlpString.create(NumericUtil.hexToBytes(to))); 116 | } else { 117 | result.add(RlpString.create("")); 118 | } 119 | 120 | result.add(RlpString.create(getValue())); 121 | 122 | // value field will already be hex encoded, so we need to convert into binary first 123 | byte[] data = NumericUtil.hexToBytes(getData()); 124 | result.add(RlpString.create(data)); 125 | 126 | if (signatureData != null && signatureData.getV() > 0) { 127 | result.add(RlpString.create(signatureData.getV())); 128 | result.add(RlpString.create(ByteUtil.trimLeadingZeroes(signatureData.getR()))); 129 | result.add(RlpString.create(ByteUtil.trimLeadingZeroes(signatureData.getS()))); 130 | } 131 | 132 | return result; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/MyHMacDSAKCalculator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import org.spongycastle.crypto.Digest; 4 | import org.spongycastle.crypto.macs.HMac; 5 | import org.spongycastle.crypto.params.KeyParameter; 6 | import org.spongycastle.crypto.signers.DSAKCalculator; 7 | import org.spongycastle.util.Arrays; 8 | import org.spongycastle.util.BigIntegers; 9 | 10 | import java.math.BigInteger; 11 | import java.security.SecureRandom; 12 | 13 | public class MyHMacDSAKCalculator implements DSAKCalculator { 14 | private static final BigInteger ZERO = BigInteger.valueOf(0); 15 | 16 | private final HMac hMac; 17 | private final byte[] K; 18 | private final byte[] V; 19 | 20 | private BigInteger n; 21 | 22 | private boolean needTry; 23 | 24 | /** 25 | * Base constructor. 26 | * 27 | * @param digest digest to build the HMAC on. 28 | */ 29 | public MyHMacDSAKCalculator(Digest digest) { 30 | this.hMac = new HMac(digest); 31 | this.V = new byte[hMac.getMacSize()]; 32 | this.K = new byte[hMac.getMacSize()]; 33 | } 34 | 35 | public boolean isDeterministic() { 36 | return true; 37 | } 38 | 39 | public void init(BigInteger n, SecureRandom random) { 40 | throw new IllegalStateException("Operation not supported"); 41 | } 42 | 43 | public void init(BigInteger n, BigInteger d, byte[] message) { 44 | this.n = n; 45 | this.needTry = false; 46 | 47 | Arrays.fill(V, (byte) 0x01); 48 | Arrays.fill(K, (byte) 0); 49 | 50 | byte[] x = new byte[(n.bitLength() + 7) / 8]; 51 | byte[] dVal = BigIntegers.asUnsignedByteArray(d); 52 | 53 | System.arraycopy(dVal, 0, x, x.length - dVal.length, dVal.length); 54 | 55 | byte[] m = new byte[(n.bitLength() + 7) / 8]; 56 | 57 | BigInteger mInt = bitsToInt(message); 58 | 59 | if (mInt.compareTo(n) > 0) { 60 | mInt = mInt.subtract(n); 61 | } 62 | 63 | byte[] mVal = BigIntegers.asUnsignedByteArray(mInt); 64 | 65 | System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length); 66 | 67 | hMac.init(new KeyParameter(K)); 68 | 69 | hMac.update(V, 0, V.length); 70 | hMac.update((byte) 0x00); 71 | hMac.update(x, 0, x.length); 72 | hMac.update(m, 0, m.length); 73 | 74 | hMac.doFinal(K, 0); 75 | 76 | hMac.init(new KeyParameter(K)); 77 | 78 | hMac.update(V, 0, V.length); 79 | 80 | hMac.doFinal(V, 0); 81 | 82 | hMac.update(V, 0, V.length); 83 | hMac.update((byte) 0x01); 84 | hMac.update(x, 0, x.length); 85 | hMac.update(m, 0, m.length); 86 | 87 | hMac.doFinal(K, 0); 88 | 89 | hMac.init(new KeyParameter(K)); 90 | 91 | hMac.update(V, 0, V.length); 92 | 93 | hMac.doFinal(V, 0); 94 | } 95 | 96 | public BigInteger nextK() { 97 | byte[] t = new byte[((n.bitLength() + 7) / 8)]; 98 | 99 | if (needTry) { 100 | hMac.init(new KeyParameter(K)); 101 | hMac.update(V, 0, V.length); 102 | hMac.update((byte) 0x00); 103 | 104 | hMac.doFinal(K, 0); 105 | 106 | hMac.init(new KeyParameter(K)); 107 | 108 | hMac.update(V, 0, V.length); 109 | 110 | hMac.doFinal(V, 0); 111 | } 112 | 113 | int tOff = 0; 114 | 115 | while (tOff < t.length) { 116 | hMac.init(new KeyParameter(K)); 117 | hMac.update(V, 0, V.length); 118 | 119 | hMac.doFinal(V, 0); 120 | 121 | int len = Math.min(t.length - tOff, V.length); 122 | System.arraycopy(V, 0, t, tOff, len); 123 | tOff += len; 124 | } 125 | 126 | BigInteger k = bitsToInt(t); 127 | needTry = true; 128 | return k; 129 | 130 | } 131 | 132 | private BigInteger bitsToInt(byte[] t) { 133 | BigInteger v = new BigInteger(1, t); 134 | 135 | if (t.length * 8 > n.bitLength()) { 136 | v = v.shiftRight(t.length * 8 - n.bitLength()); 137 | } 138 | 139 | return v; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/SignatureData.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 4 | 5 | import java.util.Arrays; 6 | 7 | /** 8 | * Created by xyz on 2018/3/2. 9 | */ 10 | public class SignatureData { 11 | private final int v; 12 | private final byte[] r; 13 | private final byte[] s; 14 | 15 | public SignatureData(int v, byte[] r, byte[] s) { 16 | this.v = v; 17 | this.r = r; 18 | this.s = s; 19 | } 20 | 21 | public int getV() { 22 | return v; 23 | } 24 | 25 | public byte[] getR() { 26 | return r; 27 | } 28 | 29 | public byte[] getS() { 30 | return s; 31 | } 32 | 33 | @Override 34 | public boolean equals(Object o) { 35 | if (this == o) { 36 | return true; 37 | } 38 | if (o == null || getClass() != o.getClass()) { 39 | return false; 40 | } 41 | 42 | SignatureData that = (SignatureData) o; 43 | 44 | if (v != that.v) { 45 | return false; 46 | } 47 | if (!Arrays.equals(r, that.r)) { 48 | return false; 49 | } 50 | return Arrays.equals(s, that.s); 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | int result = v; 56 | result = 31 * result + Arrays.hashCode(r); 57 | result = 31 * result + Arrays.hashCode(s); 58 | return result; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | String r = NumericUtil.bytesToHex(getR()); 64 | String s = NumericUtil.bytesToHex(getS()); 65 | return String.format("%s%s%02x", r, s, getV()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/TransactionSigner.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import org.consenlabs.tokencore.wallet.Wallet; 4 | 5 | public interface TransactionSigner { 6 | TxSignResult signTransaction(String chainId, String password, Wallet wallet); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/TxMultiSignResult.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | import java.util.List; 4 | 5 | public class TxMultiSignResult { 6 | 7 | public TxMultiSignResult(String txHash, List signed) { 8 | this.txHash = txHash; 9 | this.signed = signed; 10 | } 11 | 12 | String txHash; 13 | List signed; 14 | 15 | public String getTxHash() { 16 | return txHash; 17 | } 18 | 19 | public void setTxHash(String txHash) { 20 | this.txHash = txHash; 21 | } 22 | 23 | public List getSigned() { 24 | return signed; 25 | } 26 | 27 | public void setSigned(List signed) { 28 | this.signed = signed; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/transaction/TxSignResult.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | /** 4 | * Created by xyz on 2018/1/22. 5 | */ 6 | 7 | public class TxSignResult { 8 | private String signedTx; 9 | private String txHash; 10 | private String wtxID; 11 | 12 | public String getWtxID() { 13 | return wtxID; 14 | } 15 | 16 | public void setWtxID(String wtxID) { 17 | this.wtxID = wtxID; 18 | } 19 | 20 | public String getSignedTx() { 21 | return signedTx; 22 | } 23 | 24 | public void setSignedTx(String signedTx) { 25 | this.signedTx = signedTx; 26 | } 27 | 28 | public String getTxHash() { 29 | return txHash; 30 | } 31 | 32 | public void setTxHash(String txHash) { 33 | this.txHash = txHash; 34 | } 35 | 36 | public TxSignResult(String signedTx, String txHash) { 37 | this.signedTx = signedTx; 38 | this.txHash = txHash; 39 | } 40 | 41 | public TxSignResult(String signedTx, String txHash, String wtxID) { 42 | this.signedTx = signedTx; 43 | this.txHash = txHash; 44 | this.wtxID = wtxID; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/validators/ETHAddressValidator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.validators; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.consenlabs.tokencore.foundation.crypto.Hash; 6 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 7 | import org.consenlabs.tokencore.wallet.model.Messages; 8 | import org.consenlabs.tokencore.wallet.model.TokenException; 9 | 10 | import java.util.regex.Pattern; 11 | 12 | import static com.google.common.base.Preconditions.checkState; 13 | 14 | /** 15 | * Created by xyz on 2018/3/12. 16 | */ 17 | 18 | public class ETHAddressValidator implements Validator { 19 | private String address; 20 | 21 | private static final Pattern ignoreCaseAddrPattern = Pattern.compile("^(0x)?[0-9a-f]{40}$", 22 | Pattern.CASE_INSENSITIVE); 23 | private static final Pattern lowerCaseAddrPattern = Pattern.compile("^(0x)?[0-9a-f]{40}$"); 24 | private static final Pattern upperCaseAddrPattern = Pattern.compile("^(0x)?[0-9A-F]{40}$"); 25 | 26 | 27 | public ETHAddressValidator(String address) { 28 | this.address = address; 29 | } 30 | 31 | @Override 32 | public Void validate() { 33 | if (!isValidAddress(this.address)) { 34 | throw new TokenException(Messages.WALLET_INVALID_ADDRESS); 35 | } 36 | return null; 37 | } 38 | 39 | 40 | private boolean isValidAddress(String address) { 41 | 42 | // if not [0-9]{40} return false 43 | if (Strings.isNullOrEmpty(address) || !ignoreCaseAddrPattern.matcher(address).find()) { 44 | return false; 45 | } else if (lowerCaseAddrPattern.matcher(address).find() || upperCaseAddrPattern.matcher(address).find()) { 46 | // if it's all small caps or caps return true 47 | return true; 48 | } else { 49 | // if it is mixed caps it is a checksum address and needs to be validated 50 | return validateChecksumAddress(address); 51 | } 52 | } 53 | 54 | private boolean validateChecksumAddress(String address) { 55 | address = NumericUtil.cleanHexPrefix(address); 56 | checkState(address.length() == 40); 57 | 58 | String lowerAddress = NumericUtil.cleanHexPrefix(address).toLowerCase(); 59 | String hash = NumericUtil.bytesToHex(Hash.keccak256(lowerAddress.getBytes())); 60 | 61 | for (int i = 0; i < 40; i++) { 62 | if (Character.isLetter(address.charAt(i))) { 63 | // each uppercase letter should correlate with a first bit of 1 in the hash 64 | // char with the same index, and each lowercase letter with a 0 bit 65 | int charInt = Integer.parseInt(Character.toString(hash.charAt(i)), 16); 66 | if ((Character.isUpperCase(address.charAt(i)) && charInt <= 7) 67 | || (Character.isLowerCase(address.charAt(i)) && charInt > 7)) { 68 | return false; 69 | } 70 | } 71 | } 72 | return true; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/validators/MetadataValidator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.validators; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import org.consenlabs.tokencore.wallet.model.ChainType; 6 | import org.consenlabs.tokencore.wallet.model.Metadata; 7 | 8 | import java.util.HashMap; 9 | 10 | import static com.google.common.base.Preconditions.checkState; 11 | 12 | /** 13 | * Created by xyz on 2018/4/10. 14 | */ 15 | 16 | public final class MetadataValidator implements Validator { 17 | private HashMap map; 18 | private String source; 19 | 20 | public MetadataValidator(HashMap map) { 21 | this.map = map; 22 | } 23 | 24 | public MetadataValidator(HashMap map, String source) { 25 | this.map = map; 26 | this.source = source; 27 | } 28 | 29 | @Override 30 | public Metadata validate() { 31 | String name = (String) map.get("name"); 32 | String passwordHint = (String) map.get("passwordHint"); 33 | String chainType = (String) map.get("chainType"); 34 | String network = null; 35 | String segWit = null; 36 | if (!ChainType.ETHEREUM.equalsIgnoreCase(chainType)) { 37 | if (map.containsKey("network")) { 38 | network = ((String) map.get("network")).toUpperCase(); 39 | } 40 | if (map.containsKey("segWit")) { 41 | segWit = ((String) map.get("segWit")).toUpperCase(); 42 | } 43 | } 44 | 45 | checkState(!Strings.isNullOrEmpty(name), "Can't allow empty name"); 46 | ChainType.validate(chainType); 47 | 48 | Metadata metadata = new Metadata(chainType, network, name, passwordHint); 49 | if (!Strings.isNullOrEmpty(this.source)) { 50 | metadata.setSource(this.source); 51 | } 52 | metadata.setSegWit(segWit); 53 | return metadata; 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/validators/PrivateKeyValidator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.validators; 2 | 3 | import org.bitcoinj.core.ECKey; 4 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 5 | import org.consenlabs.tokencore.wallet.model.Messages; 6 | import org.consenlabs.tokencore.wallet.model.TokenException; 7 | 8 | import java.math.BigInteger; 9 | 10 | 11 | /** 12 | * Created by xyz on 2018/2/27. 13 | */ 14 | 15 | public final class PrivateKeyValidator implements Validator { 16 | 17 | private String privateKey; 18 | 19 | public PrivateKeyValidator(String prvKey) { 20 | this.privateKey = prvKey; 21 | } 22 | 23 | // todo: BitcoinJ provides a wrapper of NativeSecp256k1, but not provide a workable .so file 24 | // For stability, we do not compile the .so by ourselves, instead, we write some simple java code. 25 | @Override 26 | public String validate() { 27 | try { 28 | // validating private key 29 | BigInteger pkNum = NumericUtil.hexToBigInteger(privateKey); 30 | if (NumericUtil.hexToBytes(this.privateKey).length != 32 31 | || pkNum.compareTo((ECKey.CURVE.getN().subtract(BigInteger.ONE))) >= 0 32 | || pkNum.compareTo(BigInteger.ONE) <= 0) { 33 | throw new TokenException(Messages.PRIVATE_KEY_INVALID); 34 | } 35 | 36 | // validating public key 37 | byte[] pubKeyBytes = ECKey.fromPrivate(pkNum).getPubKey(); 38 | BigInteger pubKeyNum = new BigInteger(1, pubKeyBytes); 39 | if (pubKeyNum.compareTo(BigInteger.ZERO) == 0) { 40 | throw new TokenException(Messages.PRIVATE_KEY_INVALID); 41 | } 42 | } catch (Exception ex) { 43 | throw new TokenException(Messages.PRIVATE_KEY_INVALID); 44 | } 45 | return this.privateKey; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/validators/Validator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.validators; 2 | 3 | /** 4 | * Created by xyz on 2018/2/27. 5 | */ 6 | 7 | public interface Validator { 8 | T validate(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/org/consenlabs/tokencore/wallet/validators/WIFValidator.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.validators; 2 | 3 | import org.bitcoinj.core.AddressFormatException; 4 | import org.bitcoinj.core.DumpedPrivateKey; 5 | import org.bitcoinj.core.NetworkParameters; 6 | import org.bitcoinj.core.WrongNetworkException; 7 | import org.consenlabs.tokencore.wallet.model.Messages; 8 | import org.consenlabs.tokencore.wallet.model.TokenException; 9 | 10 | /** 11 | * Created by xyz on 2018/2/27. 12 | */ 13 | 14 | public final class WIFValidator implements Validator { 15 | 16 | String wif; 17 | NetworkParameters network; 18 | boolean requireCompressed = false; 19 | public WIFValidator(String wif, NetworkParameters network) { 20 | this.wif = wif; 21 | this.network = network; 22 | } 23 | 24 | public WIFValidator(String wif, NetworkParameters network, boolean requireCompressed) { 25 | this.wif = wif; 26 | this.network = network; 27 | this.requireCompressed = requireCompressed; 28 | } 29 | 30 | @Override 31 | public String validate() { 32 | try { 33 | DumpedPrivateKey.fromBase58(network, wif); 34 | } catch (WrongNetworkException addressException) { 35 | throw new TokenException(Messages.WIF_WRONG_NETWORK); 36 | } catch (AddressFormatException addressFormatException) { 37 | throw new TokenException(Messages.WIF_INVALID); 38 | } 39 | if (requireCompressed && !DumpedPrivateKey.fromBase58(network, wif).getKey().isCompressed()) { 40 | throw new TokenException(Messages.SEGWIT_NEEDS_COMPRESS_PUBLIC_KEY); 41 | } 42 | return this.wif; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/crypto/AESTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 4 | import org.junit.Test; 5 | 6 | import static junit.framework.Assert.assertEquals; 7 | 8 | /** 9 | * Created by jesushula on 15/12/2016. 10 | */ 11 | 12 | public class AESTest { 13 | 14 | // expected, input text, key, iv 15 | private String[][] CTRExample = new String[][]{new String[]{"5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d", "f06d69cdc7da0faffb1008270bca38f5", "6087dab2f9fdbbfaddc31a909735c1e6"}, 16 | new String[]{"f1a47dd870488797347517eb4641c6b1927268ae1cc04dde003d4b2877f7781a", "c951ac12154816ea03176fbba327f64fc3adb741df8e81a4fa1e80612050b80a", "868c3e73b6b2222c52492a2295f9666e", "3c700055452aa9f5d0e9da0304988f6f"}, 17 | new String[]{"2d743dda0caabdfb9fca0034d33cd0da7fb1ffe78cb80d643d67bf3f2aa12819", "366438120cd47b27fa77d8d80b56e02759ecdb9b8702bf3cdcbbc858ee78340e", "6cd5c42099904171bfd370723bd38f70", "915da6af8d96b558167ff4e663e057b2"}}; 18 | 19 | private String[][] CBCExample = new String[][]{new String[]{"946ddddd56d77b12afb9c352d879ee10a2e2f7d576623b0495ce44b0141c4584bdf525f0ab95763300bef6028a88265c", "c951ac12154816ea03176fbba327f64fc3adb741df8e81a4fa1e80612050b80a", "868c3e73b6b2222c52492a2295f9666e", "3c700055452aa9f5d0e9da0304988f6f"}, 20 | new String[]{"07533e172414bfa50e99dba4a0ce603f654ebfa1ff46277c3e0c577fdc87f6bb4e4fe16c5a94ce6ce14cfa069821ef9bbadb2863671be96fc4f96a879395f77d", "cb19dce82bdb902efb5b5b75d0fe4c4c09dee0e99ef222af35dd8da136bde8995f7fd84acfb1679fe2e91de783a5e006", "9fc409900f835bb38302e976e16c49e7", "16d67ba0ce5a339ff2f07951253e6ba8"}, 21 | new String[]{"4ee50259867f9994fc1d711332d72679bb38ca4e0e1b8b07468a0ae901726dabe379535d8aa12206bf076237877d0cda", "366438120cd47b27fa77d8d80b56e02759ecdb9b8702bf3cdcbbc858ee78340e", "6cd5c42099904171bfd370723bd38f70", "915da6af8d96b558167ff4e663e057b2"}}; 22 | 23 | @Test 24 | public void doCTREncrypt() { 25 | for (String[] example : CTRExample) { 26 | byte[] encrypt = AES.encryptByCTR(NumericUtil.hexToBytes(example[1]), 27 | NumericUtil.hexToBytes(example[2]), 28 | NumericUtil.hexToBytes(example[3])); 29 | assertEquals(example[0], NumericUtil.bytesToHex(encrypt)); 30 | } 31 | } 32 | 33 | @Test 34 | public void doCTRDecrypt() { 35 | for (String[] example : CTRExample) { 36 | byte[] decrypt = AES.decryptByCTR(NumericUtil.hexToBytes(example[0]), 37 | NumericUtil.hexToBytes(example[2]), 38 | NumericUtil.hexToBytes(example[3])); 39 | assertEquals(example[1], NumericUtil.bytesToHex(decrypt)); 40 | } 41 | } 42 | 43 | @Test 44 | public void doCBCEncrypt() { 45 | for (String[] example : CBCExample) { 46 | byte[] decrypt = AES.encryptByCBC(NumericUtil.hexToBytes(example[1]), 47 | NumericUtil.hexToBytes(example[2]), 48 | NumericUtil.hexToBytes(example[3])); 49 | assertEquals(example[0], NumericUtil.bytesToHex(decrypt)); 50 | } 51 | } 52 | 53 | @Test 54 | public void doCBCDecrypt() { 55 | for (String[] example : CBCExample) { 56 | byte[] decrypt = AES.decryptByCBC(NumericUtil.hexToBytes(example[0]), 57 | NumericUtil.hexToBytes(example[2]), 58 | NumericUtil.hexToBytes(example[3])); 59 | assertEquals(example[1], NumericUtil.bytesToHex(decrypt)); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/crypto/HashTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import org.consenlabs.tokencore.foundation.crypto.Hash; 4 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 5 | import org.junit.Test; 6 | 7 | import static org.bitcoinj.core.Utils.HEX; 8 | import static org.junit.Assert.assertEquals; 9 | 10 | /** 11 | * Created by jesushula on 15/12/2016. 12 | */ 13 | 14 | public class HashTest { 15 | // input, expected 16 | private String[][] SHA3Example = new String[][]{new String[]{HEX.encode("".getBytes()), "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"}, 17 | new String[]{HEX.encode("helloworld".getBytes()), "fa26db7ca85ead399216e7c6316bc50ed24393c3122b582735e7f3b0f91b93f0"}, 18 | new String[]{"3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1", "82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28"}}; 19 | 20 | @Test 21 | public void testKeccak256() { 22 | for (String[] example : SHA3Example) { 23 | assertEquals(example[1], Hash.keccak256(example[0])); 24 | } 25 | } 26 | 27 | @Test 28 | public void testSha256() { 29 | String msg = "0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1"; 30 | String expected = "58bbda5e10bc11a32d808e40f9da2161a64f00b5557762a161626afe19137445"; 31 | assertEquals(expected, Hash.sha256(msg)); 32 | 33 | byte[] bytes = NumericUtil.hexToBytes(msg); 34 | assertEquals(expected, NumericUtil.bytesToHex(Hash.sha256(bytes))); 35 | } 36 | 37 | @Test 38 | public void testGenerateMac() { 39 | 40 | String[][] example = new String[][]{ 41 | new String[]{ 42 | "5334a31e27d5a4ea3dd815ffc8a0483b1a370ef32baf4f26349985027ab01d6e1fa23e6e0aea328814d3e2aefaf2c1f47b96c86ec24dc5cd66d7050fb249ec03d985b4793a9703f7ac189ecef67fb5a29797e2952a3efd12fe5565465b1fadeaaced304d142b2d2d46f3e8986ac3e2", 43 | "592bbbd0596b97439ee041e7c0679aba846031e66730c5f20e3626d59bfd22bd", 44 | "9600a5081753d9bc0f0855d55a592925ea5784a6c45c7a5c161ffa82e140e4eb" 45 | }, 46 | new String[]{ 47 | "ebef0d152fe4c889d2a903ec5c3adfdd7c8c8e207679c86475f3cf9d48f4455766953d3a4305fd37b35ae2e87082daa649bb51e55940c566ec21772a11a6606ccde7b26b88ac5e5f6e9569787041324ab978e695656776cd961139713ebe3fdd23027a205683f108579e162a839e69", 48 | "539d8bea635f1b8cba4ab4802e7c557dad1e097df6c4b75a6aab0385fd4e2cb9", 49 | "8dabc02eb079e935ce7dc4ec6639aaad8dc2de1275479465e86b6a475f7ebe24" 50 | }, 51 | new String[]{ 52 | "30c50f3c3bba03c6186ca65675b1b447b9dd803cbd86d12708ea9581a6ad5e930c0064c2c549cb354cace5e2a860abfb11f66589858c11751eceeaa11b61c8eee3d60646a26b39d9d8744b8dadc2cb7568e63e55f04428fea52c073b94ee570fe7f13266b338094d5ee85970129c12", 53 | "2cb813ecf4a1cc47fd80817f63f88bb17b35dcb34f08facf186dbba67c640c08", 54 | "973d56c4662e64db58d097c156290f2b7efecfbc357cfa8b7e71b032dacedeec" 55 | } 56 | }; 57 | for (String[] testData : example) { 58 | byte[] cipherText = NumericUtil.hexToBytes(testData[0]); 59 | byte[] derivedKey = NumericUtil.hexToBytes(testData[1]); 60 | 61 | String expected = testData[2]; 62 | assertEquals(expected, NumericUtil.bytesToHex(Hash.generateMac(derivedKey, cipherText))); 63 | } 64 | 65 | } 66 | 67 | 68 | @Test 69 | public void testCalcMerkleRoot() { 70 | String[][] expected = new String[][]{ 71 | new String[]{"1000", "3fa2b684fa9d80f04b70187e6c9ff1c8dd422ce1846beb79cf5e1546c7062d41"}, 72 | new String[]{"2000", "4b19aa611413ba9a6b89a2be7833bb835349b9e9e9872c5eacfc82daa2e5f08f"}, 73 | new String[]{"3000", "c9ec2ec071ed70d02802decd912a1e8d124420556789384efaab80fcb7ce7ecb"}, 74 | new String[]{"4000", "5cfa6745c50787e3d97a1322789713036f8cab7ba534d2a996bea015d811640c"}, 75 | new String[]{"5000", "233bc40f24c071507474a9c978f0f0099d0c457f9874326640be55a8a8b96325"}, 76 | new String[]{"1024", "5a6c9dcbec66882a3de754eb13e61d8908e6c0b67a23c9d524224ecd93746290"}, 77 | new String[]{"2048", "5ee830087937da00520c4ce3793c5c7b951d37771d69a098415ddf7d682a39d9"}, 78 | }; 79 | for (String[] testCase : expected) { 80 | int dataLength = Integer.valueOf(testCase[0]); 81 | byte[] data = new byte[dataLength]; 82 | for (int i = 0; i < dataLength; i++) { 83 | data[i] = (byte) (i / 1024); 84 | } 85 | assertEquals(testCase[1], NumericUtil.bytesToHex(Hash.merkleHash(data))); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/crypto/MultihashTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import org.consenlabs.tokencore.foundation.crypto.Hash; 4 | import org.consenlabs.tokencore.foundation.crypto.Multihash; 5 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 6 | import org.junit.Test; 7 | import static org.junit.Assert.*; 8 | 9 | /** 10 | * Created by xyz on 2018/1/31. 11 | */ 12 | 13 | public class MultihashTest { 14 | @Test 15 | public void testMultihashSha() { 16 | // aPub, ipfsID 17 | String[][] fixture = new String[][] { 18 | new String[] { 19 | "031e7e1b26a0eb92f86c2c1c0bd326d9f82fffc7c490a6eef870a7090babb8d507", 20 | "QmVe1YF5Ts6EQXnhUcN3mJKPMfQSVgmmaYZ2JbWvCF9pn2" 21 | }, 22 | new String[] { 23 | "022c6eb4159440968497a0f0a25f16bbac3507915139f82aa34f3af0668c1f503b", 24 | "QmQdT5yuC17EKvwVMSLP5b1rGjT6TQFXJGtQTECUGa6fF6" 25 | } 26 | }; 27 | 28 | for (String[] testData : fixture) { 29 | byte[] apub = NumericUtil.hexToBytes(testData[0]); 30 | String ipfsID = new Multihash(Multihash.Type.sha2_256, Hash.sha256(apub)).toBase58(); 31 | assertEquals(testData[1], ipfsID); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/crypto/PBKDFTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import org.spongycastle.crypto.digests.SHA256Digest; 7 | import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; 8 | import org.spongycastle.crypto.params.KeyParameter; 9 | 10 | /** 11 | * Created by jesushula on 15/12/2016. 12 | */ 13 | 14 | public class PBKDFTest { 15 | 16 | // salt, iterations, dklen, password, expected 17 | String[][] PBKDFExample = new String[][]{new String[]{"salt", "4096", "32", "password", "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a"}, 18 | new String[]{"saltSALTsaltSALTsaltSALTsaltSALTsalt", "4096", "40", "passwordPASSWORDpassword", "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9"}}; 19 | 20 | @Test 21 | public void derive() { 22 | for (String[] example : PBKDFExample) { 23 | PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest()); 24 | gen.init(example[3].getBytes(), example[0].getBytes(), Integer.parseInt(example[1])); 25 | byte[] derivedKey = ((KeyParameter) gen.generateDerivedParameters(Integer.parseInt(example[2]) * 8)).getKey(); 26 | Assert.assertEquals(NumericUtil.bytesToHex(derivedKey), example[4]); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/crypto/ScryptTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.crypto; 2 | 3 | /** 4 | * Created by jesushula on 15/12/2016. 5 | */ 6 | 7 | import com.lambdaworks.crypto.SCrypt; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 12 | import org.junit.Test; 13 | 14 | import java.security.GeneralSecurityException; 15 | 16 | 17 | public class ScryptTest { 18 | // salt, r, n, p, password, expected, dklen 19 | String[][] ScryptExample = new String[][]{ 20 | new String[]{"ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19", "1", "262144", "8", "testpassword", "fac192ceb5fd772906bea3e118a69e8bbb5cc24229e20d8766fd298291bba6bd", "32"}}; 21 | 22 | @Test 23 | public void derive() throws GeneralSecurityException { 24 | for(String[] example : ScryptExample) { 25 | byte[] derivedKey = SCrypt.scrypt(example[4].getBytes(), NumericUtil.hexToBytes(example[0]), 26 | Integer.parseInt(example[2]), Integer.parseInt(example[1]), Integer.parseInt(example[3]), Integer.parseInt(example[6])); 27 | assertEquals(example[5], NumericUtil.bytesToHex(derivedKey)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/rlp/RLPTestCase.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.rlp; 2 | 3 | /** 4 | * Created by xyz on 2018/3/9. 5 | */ 6 | 7 | public class RLPTestCase { 8 | private Object in; 9 | private String out; 10 | 11 | public Object getIn() { 12 | return in; 13 | } 14 | 15 | public void setIn(Object in) { 16 | this.in = in; 17 | } 18 | 19 | public String getOut() { 20 | return out; 21 | } 22 | 23 | public void setOut(String out) { 24 | this.out = out; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/rlp/RlpEncoderTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.rlp; 2 | 3 | import com.fasterxml.jackson.databind.JavaType; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | 6 | import junit.framework.Assert; 7 | 8 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 9 | import org.consenlabs.tokencore.testutils.ResourcesManager; 10 | import org.junit.Test; 11 | 12 | import java.io.IOException; 13 | import java.math.BigInteger; 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | import static org.hamcrest.CoreMatchers.is; 20 | import static org.junit.Assert.assertThat; 21 | 22 | public class RlpEncoderTest { 23 | 24 | /** 25 | * For further examples see https://github.com/ethereum/tests/tree/develop/RLPTests. 26 | */ 27 | @Test 28 | public void testEthereumRLPTests() { 29 | Map cases = loadRLPTestCases(); 30 | cases.forEach((key, aCase) -> { 31 | RlpType rlpType = buildRLPType(aCase.getIn()); 32 | String message = "Comparing " + key + ": "; 33 | String actualRlp = NumericUtil.bytesToHex(RlpEncoder.encode(rlpType)); 34 | Assert.assertEquals(message, aCase.getOut(), actualRlp); 35 | }); 36 | } 37 | 38 | private RlpType buildRLPType(Object in) { 39 | if (in instanceof ArrayList) { 40 | List elementList = new ArrayList<>(); 41 | for (Object o : ((ArrayList) in).toArray()) { 42 | elementList.add(buildRLPType(o)); 43 | } 44 | return new RlpList(elementList); 45 | } else { 46 | if (in instanceof String) { 47 | String s = in.toString(); 48 | if (s.startsWith("#")) { 49 | return RlpString.create(new BigInteger(s.substring(1))); 50 | } else { 51 | return RlpString.create(s); 52 | } 53 | } else if (in instanceof Integer) { 54 | return RlpString.create(Integer.parseInt(in.toString())); 55 | } 56 | 57 | } 58 | throw new IllegalArgumentException("can't parse format: " + in); 59 | } 60 | 61 | private Map loadRLPTestCases() { 62 | ObjectMapper mapper = new ObjectMapper(); 63 | JavaType type = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, RLPTestCase.class); 64 | 65 | Map cases = null; 66 | 67 | String testContent = ResourcesManager.readFileContent("rlptest.json"); 68 | 69 | try { 70 | cases = mapper.readValue(testContent, type); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | 74 | } 75 | return cases; 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/utils/ByteUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.utils; 2 | 3 | import org.consenlabs.tokencore.foundation.utils.ByteUtil; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Created by xyz on 2018/2/1. 9 | */ 10 | 11 | public class ByteUtilTest { 12 | 13 | @Test 14 | public void testTrimLeadingZeroes() { 15 | byte[][][] exampleData = new byte[][][] { 16 | new byte[][] { 17 | new byte[] { 0, 0, 0, 0, 1, 2, 3, 4}, 18 | new byte[] { 1, 2, 3, 4} 19 | }, 20 | new byte[][] { 21 | new byte[] { 1, 2, 3, 4}, 22 | new byte[] { 1, 2, 3, 4} 23 | }, 24 | new byte[][] { 25 | new byte[] { 1, 2, 3, 4, 0, 0}, 26 | new byte[] { 1, 2, 3, 4, 0, 0} 27 | } 28 | }; 29 | 30 | for (byte[][] test : exampleData) { 31 | assertArrayEquals(ByteUtil.trimLeadingZeroes(test[0]), test[1]); 32 | } 33 | } 34 | 35 | @Test 36 | public void testConcat() { 37 | 38 | byte[][][] exampleData = new byte[][][] { 39 | 40 | new byte[][] { 41 | new byte[] { 0, 0, 0, 0, 1, 2, 3, 4}, 42 | new byte[] { 5, 6, 7, 8}, 43 | new byte[] { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}, 44 | }, 45 | new byte[][] { 46 | new byte[] { 1, 2, 3, 4}, 47 | new byte[] { 1, 2, 3, 4}, 48 | new byte[] { 1, 2, 3, 4, 1, 2, 3, 4}, 49 | }, 50 | new byte[][] { 51 | new byte[] { 1, 2, 3, 4, 0, 0}, 52 | new byte[0], 53 | new byte[] { 1, 2, 3, 4, 0, 0}, 54 | }, 55 | new byte[][] { 56 | new byte[0], 57 | new byte[] { 1, 2, 3, 4, 0, 0}, 58 | new byte[] { 1, 2, 3, 4, 0, 0}, 59 | } 60 | }; 61 | 62 | for (byte[][] test : exampleData) { 63 | byte[] concatResult = ByteUtil.concat(test[0], test[1]); 64 | assertArrayEquals(concatResult, test[2]); 65 | } 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/foundation/utils/MnemonicUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.foundation.utils; 2 | 3 | import org.consenlabs.tokencore.wallet.model.Messages; 4 | import org.consenlabs.tokencore.wallet.model.TokenException; 5 | import org.consenlabs.tokencore.wallet.SampleKey; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.ExpectedException; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | /** 16 | * Created by xyz on 2018/2/1. 17 | */ 18 | 19 | public class MnemonicUtilTest { 20 | 21 | @Rule 22 | public ExpectedException thrown = ExpectedException.none(); 23 | 24 | @Test 25 | public void testValidateMnemonic() { 26 | try { 27 | List mnemonic = Arrays.asList(SampleKey.MNEMONIC.split(" ")); 28 | MnemonicUtil.validateMnemonics(mnemonic); 29 | assertTrue(true); 30 | } catch (TokenException ex) { 31 | fail("Should not throw any exception"); 32 | } 33 | 34 | } 35 | 36 | @Test 37 | public void testMnemonicShortLengthException() { 38 | thrown.expect(TokenException.class); 39 | thrown.expectMessage(Messages.MNEMONIC_INVALID_LENGTH); 40 | 41 | String test = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"; 42 | List mnemonic = Arrays.asList(test.split(" ")); 43 | MnemonicUtil.validateMnemonics(mnemonic); 44 | fail("Should throw a exception"); 45 | } 46 | 47 | @Test 48 | public void testMnemonicLongLengthException() { 49 | thrown.expect(TokenException.class); 50 | thrown.expectMessage(Messages.MNEMONIC_INVALID_LENGTH); 51 | 52 | String test = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"; 53 | List mnemonic = Arrays.asList(test.split(" ")); 54 | MnemonicUtil.validateMnemonics(mnemonic); 55 | fail("Should throw a exception"); 56 | } 57 | 58 | @Test 59 | public void testMnemonicBadWordException() { 60 | thrown.expect(TokenException.class); 61 | thrown.expectMessage(Messages.MNEMONIC_BAD_WORD); 62 | // "hot" not in english mnemonic word list 63 | String test = "hot zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"; 64 | List mnemonic = Arrays.asList(test.split(" ")); 65 | MnemonicUtil.validateMnemonics(mnemonic); 66 | fail("Should throw a exception"); 67 | } 68 | 69 | @Test 70 | public void testMnemonicBadChecksumException() { 71 | thrown.expect(TokenException.class); 72 | thrown.expectMessage(Messages.MNEMONIC_CHECKSUM); 73 | // "hot" not in english mnemonic word list 74 | String test = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"; 75 | List mnemonic = Arrays.asList(test.split(" ")); 76 | MnemonicUtil.validateMnemonics(mnemonic); 77 | fail("Should throw a exception"); 78 | } 79 | 80 | @Test 81 | public void testRandomMnemonicCodes() { 82 | List mnemonic = MnemonicUtil.randomMnemonicCodes(); 83 | assertEquals("Should be 12 words", 12, mnemonic.size()); 84 | MnemonicUtil.validateMnemonics(mnemonic); 85 | assertTrue("Should valid", true); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/testutils/LocalFileStorage.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.testutils; 2 | 3 | import org.consenlabs.tokencore.wallet.KeystoreStorage; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * Created by xyz on 2018/4/8. 9 | */ 10 | 11 | public class LocalFileStorage implements KeystoreStorage { 12 | @Override 13 | public File getKeystoreDir() { 14 | return new File("/tmp/imtoken"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/testutils/ResourcesManager.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.testutils; 2 | 3 | import com.google.common.base.Joiner; 4 | 5 | import org.json.JSONException; 6 | import org.json.JSONObject; 7 | 8 | import java.io.IOException; 9 | import java.net.URL; 10 | import java.nio.file.Files; 11 | import java.nio.file.Paths; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | import java.util.concurrent.Future; 18 | 19 | /** 20 | * Created by xyz on 2018/3/9. 21 | */ 22 | 23 | public class ResourcesManager { 24 | 25 | public static JSONObject loadTestJSON(String filename) { 26 | JSONObject jsonObject = null; 27 | try { 28 | String content = readFileContent(filename); 29 | jsonObject = new JSONObject(content); 30 | } catch (JSONException ex) { 31 | ex.printStackTrace(); 32 | } 33 | return jsonObject; 34 | } 35 | 36 | public static String readFileContent(String filename) { 37 | 38 | try { 39 | URL url = ResourcesManager.class.getClassLoader().getResource(filename); 40 | return Joiner.on("").join(Files.readAllLines(Paths.get(url.getFile()))); 41 | } catch (IOException e) { 42 | return ""; 43 | } 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/wallet/SampleKey.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet; 2 | 3 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 4 | import org.bitcoinj.core.ECKey; 5 | import java.math.BigInteger; 6 | 7 | /** 8 | * from web3j sample key 9 | */ 10 | public class SampleKey { 11 | public static final String NAME = "imToken Test"; 12 | public static final String PRIVATE_KEY_STRING = 13 | "a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6"; 14 | public static final String PRIVATE_KEY_WIF = "L2hfzPyVC1jWH7n2QLTe7tVTb6btg9smp5UVzhEBxLYaSFF7sCZB"; 15 | public static final String TESTNET_WIF = "cT4fTJyLd5RmSZFHnkGmVCzXDKuJLbyTt7cy77ghTTCagzNdPH1j"; 16 | public static final String PRIVATE_KEY_TESTNET_WIF = "cT4fTJyLd5RmSZFHnkGmVCzXDKuJLbyTt7cy77ghTTCagzNdPH1j"; 17 | static final String PUBLIC_KEY_STRING = 18 | "0x506bc1dc099358e5137292f4efdd57e400f29ba5132aa5d12b18dac1c1f6aab" 19 | + "a645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76"; 20 | public static final String ADDRESS = "ef678007d18427e6022059dbc264f27507cd1ffc"; 21 | public static final String ADDRESS_NO_PREFIX = NumericUtil.cleanHexPrefix(ADDRESS); 22 | 23 | public static final String MNEMONIC = "inject kidney empty canal shadow pact comfort wife crush horse wife sketch"; 24 | public static final String OTHER_MNEMONIC = "spy excess school tiger quick link olympic timber final learn rebuild dragon"; 25 | public static final String BITCOIN_TESTNET_HD_PATH = "m/44'/1'/0"; 26 | public static final String BITCOIN_MAINNET_HD_PATH = "m/44'/0'/0'"; 27 | public static final String ETHEREUM_HD_PATH = "m/44'/60'/0'/0/0"; 28 | 29 | public static final String PASSWORD = "Insecure Pa55w0rd"; 30 | public static final String NEW_PASSWORD = "NEW_Password"; 31 | public static final String WRONG_PASSWORD = "Wrong Password"; 32 | public static final String PASSWORD_HINT = "Password Hint"; 33 | 34 | public static final BigInteger PRIVATE_KEY = NumericUtil.hexToBigInteger(PRIVATE_KEY_STRING); 35 | public static final BigInteger PUBLIC_KEY = NumericUtil.hexToBigInteger(PUBLIC_KEY_STRING); 36 | 37 | public static final ECKey KEY_PAIR = ECKey.fromPrivate(PRIVATE_KEY,false); 38 | 39 | public static String V3JSON = "{\"version\":3,\"id\":\"83d0cf85-6230-4b93-aadc-e9220df32674\",\"address\":\"41983f2e3af196c1df429a3ff5cdecc45c82c600\",\"Crypto\":{\"ciphertext\":\"11ba4af9f87ed29b2fdcf04e581caf50d687559f9a0a667dc1f7d389db2bfa24\",\"cipherparams\":{\"iv\":\"dc65f752f9ee37f546c4b3c0aefac1dd\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"997d65727201163620a597f8e7540f67d284bb1a27dbf6f7267ea723c1b400ad\",\"n\":8192,\"r\":8,\"p\":1},\"mac\":\"4444445aecc1b4d6c823419afce45baf57e5680f7bb9b7e6a0e810859b83aec1\"}}"; 40 | 41 | 42 | public static String WRONG_V3JSON = "{\"version\":3,\"id\":\"83d0cf85-6230-4b93-aadc-e9220df32674\",\"address\":\"41983f2e3af196c1df429a3ff5cdecc45c82c600\",\"Crypto\":{\"ciphertext\":\"11ba4af9f87ed29b2fdcf04e581caf50d687559f9a0a667dc1f7d389db2bfa24\",\"cipherparams\":{\"iv\":\"dc65f752f9ee37f546c4b3c0aefac1dd\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"997d65727201163620a597f8e7540f67d284bb1a27dbf6f7267ea723c1b400ad\",\"n\":8192,\"r\":8,\"p\":1}}}"; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/wallet/TokenCoreTour.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet; 2 | 3 | import org.bitcoinj.core.ECKey; 4 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 5 | import org.consenlabs.tokencore.wallet.model.BIP44Util; 6 | import org.consenlabs.tokencore.wallet.model.ChainId; 7 | import org.consenlabs.tokencore.wallet.model.ChainType; 8 | import org.consenlabs.tokencore.wallet.model.Metadata; 9 | import org.consenlabs.tokencore.wallet.model.Network; 10 | import org.consenlabs.tokencore.wallet.transaction.BitcoinTransaction; 11 | import org.consenlabs.tokencore.wallet.transaction.EthereumTransaction; 12 | import org.consenlabs.tokencore.wallet.transaction.TxSignResult; 13 | import org.junit.Test; 14 | 15 | import java.math.BigInteger; 16 | import java.util.ArrayList; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertNotEquals; 20 | 21 | public class TokenCoreTour extends WalletSupport { 22 | @Test 23 | public void createEthereumWalletExample() { 24 | System.out.println("-------- Create ethereum wallet example: "); 25 | Metadata metadata = new Metadata(ChainType.ETHEREUM, Network.MAINNET, "name", "passwordHint"); 26 | metadata.setSource(Metadata.FROM_PRIVATE); 27 | Wallet wallet = WalletManager.importWalletFromPrivateKey(metadata, 28 | SampleKey.PRIVATE_KEY_STRING, 29 | SampleKey.PASSWORD, true); 30 | 31 | ECKey key = ECKey.fromPrivate(wallet.decryptMainKey(SampleKey.PASSWORD)); 32 | assertEquals( 33 | NumericUtil.bigIntegerToHex(key.getPrivKey()), 34 | SampleKey.PRIVATE_KEY_STRING); 35 | 36 | assertNotEquals(wallet.verifyPassword("benn"), true); 37 | System.out.println("Ethereum wallet privateKey: " + wallet.exportPrivateKey(SampleKey.PASSWORD)); 38 | System.out.println("Ethereum wallet address: " + wallet.getAddress()); 39 | wallet.delete(SampleKey.PASSWORD); 40 | } 41 | 42 | @Test 43 | public void signEthereumTransactionExample() { 44 | Metadata metadata = new Metadata(); 45 | metadata.setSource(Metadata.FROM_PRIVATE); 46 | metadata.setWalletType(Metadata.V3); 47 | metadata.setChainType(ChainType.ETHEREUM); 48 | Wallet wallet = WalletManager.importWalletFromPrivateKey(metadata, SampleKey.PRIVATE_KEY_STRING, SampleKey.PASSWORD, true); 49 | 50 | EthereumTransaction ethTx = new EthereumTransaction(BigInteger.valueOf(8L), BigInteger.valueOf(20000000008L), 51 | BigInteger.valueOf(189000L), "0x3535353535353535353535353535353535353535", BigInteger.valueOf(512), ""); 52 | System.out.println(); 53 | System.out.println("-------- Ethereum transaction sign example: "); 54 | String result = ethTx.signTransaction("0", SampleKey.PASSWORD, wallet).getSignedTx(); 55 | System.out.println("Signed result: " + result); 56 | } 57 | 58 | @Test 59 | public void createBTCHDWalletExample() { 60 | System.out.println(); 61 | System.out.println("-------- Create BTC Wallet Example: "); 62 | Metadata metadata = new Metadata(); 63 | metadata.setSource(Metadata.FROM_MNEMONIC); 64 | metadata.setWalletType(Metadata.HD); 65 | metadata.setChainType(ChainType.BITCOIN); 66 | Wallet wallet = WalletManager.importWalletFromMnemonic(metadata, SampleKey.MNEMONIC, BIP44Util.BITCOIN_MAINNET_PATH, SampleKey.PASSWORD, true); 67 | System.out.println("m/44'/0'/0'/0/0 address: " + wallet.getAddress()); 68 | System.out.println("m/44'/0'/0'/1/0 address: " + wallet.newReceiveAddress(0)); 69 | System.out.println("Enc XPub(Encrypted with 'aes-cbc-128'): " + wallet.getEncXPub()); 70 | } 71 | 72 | @Test 73 | public void bitcoinTransactionSignExample() { 74 | System.out.println(); 75 | System.out.println("-------- Bitcoin transaction sign example: "); 76 | Metadata walletMetadata = new Metadata(ChainType.BITCOIN, Network.TESTNET, "name", "passwordHint"); 77 | walletMetadata.setSource(Metadata.FROM_MNEMONIC); 78 | walletMetadata.setNetwork(Network.TESTNET); 79 | Wallet wallet = WalletManager.importWalletFromMnemonic(walletMetadata, SampleKey.MNEMONIC, BIP44Util.BITCOIN_TESTNET_PATH, 80 | SampleKey.PASSWORD, true); 81 | 82 | TxSignResult signedResult = createMultiUXTOOnTestnet().signTransaction(Integer.toString(ChainId.BITCOIN_TESTNET), SampleKey.PASSWORD, wallet); 83 | System.out.println("Sign Result: " + signedResult.getSignedTx()); 84 | } 85 | 86 | 87 | private static BitcoinTransaction createMultiUXTOOnTestnet() { 88 | ArrayList utxo = new ArrayList<>(); 89 | 90 | utxo.add(new BitcoinTransaction.UTXO( 91 | "983adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a", 0, 92 | 200000000, "mh7jj2ELSQUvRQELbn9qyA4q5nADhmJmUC", 93 | "76a914118c3123196e030a8a607c22bafc1577af61497d88ac", 94 | "0/22")); 95 | utxo.add(new BitcoinTransaction.UTXO( 96 | "45ef8ac7f78b3d7d5ce71ae7934aea02f4ece1af458773f12af8ca4d79a9b531", 1, 97 | 200000000, "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN", 98 | "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac", 99 | "0/0")); 100 | utxo.add(new BitcoinTransaction.UTXO( 101 | "14c67e92611dc33df31887bbc468fbbb6df4b77f551071d888a195d1df402ca9", 0, 102 | 200000000, "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN", 103 | "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac", 104 | "0/0")); 105 | utxo.add(new BitcoinTransaction.UTXO( 106 | "117fb6b85ded92e87ee3b599fb0468f13aa0c24b4a442a0d334fb184883e9ab9", 1, 107 | 200000000, "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN", 108 | "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac", 109 | "0/0")); 110 | 111 | BitcoinTransaction tran = new BitcoinTransaction("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3", 53, 112 | 750000000, 502130, utxo); 113 | 114 | return tran; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/wallet/WalletSupport.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet; 2 | 3 | import org.consenlabs.tokencore.testutils.LocalFileStorage; 4 | import org.consenlabs.tokencore.wallet.validators.PrivateKeyValidator; 5 | import org.junit.After; 6 | import org.junit.Before; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Paths; 12 | 13 | import static org.mockito.Matchers.isA; 14 | import static org.mockito.Mockito.doNothing; 15 | import static org.mockito.Mockito.mock; 16 | 17 | public class WalletSupport { 18 | 19 | public static final String KEYSTORE_DIR = "/tmp/imtoken/wallets"; 20 | 21 | @Before 22 | public void setUp() { 23 | try { 24 | Files.createDirectories(Paths.get(KEYSTORE_DIR)); 25 | } catch (IOException ignored) { 26 | } 27 | WalletManager.storage = new LocalFileStorage(); 28 | WalletManager.scanWallets(); 29 | 30 | Identity identity = mock(Identity.class); 31 | doNothing().when(identity).addWallet(isA(Wallet.class)); 32 | Identity.currentIdentity = identity; 33 | } 34 | 35 | @After 36 | public void tearDown() { 37 | File dir = new File(KEYSTORE_DIR); 38 | String[] children = dir.list(); 39 | if (children == null) return; 40 | for (String aChildren : children) { 41 | (new File(dir, aChildren)).delete(); 42 | } 43 | WalletManager.clearKeystoreMap(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/wallet/address/AddressCreatorTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.address; 2 | 3 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 4 | import org.consenlabs.tokencore.wallet.model.ChainType; 5 | import org.consenlabs.tokencore.wallet.SampleKey; 6 | import org.consenlabs.tokencore.wallet.model.Metadata; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | /** 13 | * Created by xyz on 2018/2/2. 14 | */ 15 | 16 | public class AddressCreatorTest { 17 | @Test 18 | public void testGenerateBTCAddress() { 19 | AddressCreator creator; 20 | 21 | creator = AddressCreatorManager.getInstance(ChainType.BITCOIN, true, Metadata.NONE); 22 | assertTrue(creator instanceof BitcoinAddressCreator); 23 | 24 | assertEquals("1N3RC53vbaDNrziTdWmctBEeQ4fo4quNpq", creator.fromPrivateKey(SampleKey.PRIVATE_KEY_WIF)); 25 | assertEquals("1N3RC53vbaDNrziTdWmctBEeQ4fo4quNpq", creator.fromPrivateKey(NumericUtil.hexToBytes(SampleKey.PRIVATE_KEY_STRING))); 26 | 27 | creator = AddressCreatorManager.getInstance(ChainType.BITCOIN, false, Metadata.NONE); 28 | assertTrue(creator instanceof BitcoinAddressCreator); 29 | assertEquals("n2ZNV88uQbede7C5M5jzi6SyG4GVuPpng6", creator.fromPrivateKey(SampleKey.PRIVATE_KEY_TESTNET_WIF)); 30 | assertEquals("n2ZNV88uQbede7C5M5jzi6SyG4GVuPpng6", creator.fromPrivateKey(NumericUtil.hexToBytes(SampleKey.PRIVATE_KEY_STRING))); 31 | } 32 | 33 | 34 | @Test 35 | public void testGenerateBTCP2WPKHAddress() { 36 | AddressCreator creator; 37 | 38 | creator = AddressCreatorManager.getInstance(ChainType.BITCOIN, true, Metadata.P2WPKH); 39 | assertTrue(creator instanceof SegWitBitcoinAddressCreator); 40 | 41 | assertEquals("3Js9bGaZSQCNLudeGRHL4NExVinc25RbuG", creator.fromPrivateKey(SampleKey.PRIVATE_KEY_WIF)); 42 | assertEquals("3Js9bGaZSQCNLudeGRHL4NExVinc25RbuG", creator.fromPrivateKey(NumericUtil.hexToBytes(SampleKey.PRIVATE_KEY_STRING))); 43 | 44 | creator = AddressCreatorManager.getInstance(ChainType.BITCOIN, false, Metadata.P2WPKH); 45 | assertTrue(creator instanceof SegWitBitcoinAddressCreator); 46 | assertEquals("2NARMf1Wb3rhiYhGBwYuCgKEDi4zmojTsvk", creator.fromPrivateKey(SampleKey.PRIVATE_KEY_TESTNET_WIF)); 47 | assertEquals("2NARMf1Wb3rhiYhGBwYuCgKEDi4zmojTsvk", creator.fromPrivateKey(NumericUtil.hexToBytes(SampleKey.PRIVATE_KEY_STRING))); 48 | } 49 | 50 | @Test 51 | public void testGenerateETHAddress() { 52 | AddressCreator creator; 53 | creator = AddressCreatorManager.getInstance(ChainType.ETHEREUM, false, Metadata.NONE); 54 | assertTrue(creator instanceof EthereumAddressCreator); 55 | assertEquals("ef678007d18427e6022059dbc264f27507cd1ffc", creator.fromPrivateKey(SampleKey.PRIVATE_KEY_STRING)); 56 | assertEquals("ef678007d18427e6022059dbc264f27507cd1ffc", creator.fromPrivateKey(NumericUtil.hexToBytes(SampleKey.PRIVATE_KEY_STRING))); 57 | 58 | creator = AddressCreatorManager.getInstance(ChainType.ETHEREUM, true, Metadata.NONE); 59 | assertTrue(creator instanceof EthereumAddressCreator); 60 | assertEquals("ef678007d18427e6022059dbc264f27507cd1ffc", creator.fromPrivateKey(SampleKey.PRIVATE_KEY_STRING)); 61 | assertEquals("ef678007d18427e6022059dbc264f27507cd1ffc", creator.fromPrivateKey(NumericUtil.hexToBytes(SampleKey.PRIVATE_KEY_STRING))); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/test/java/org/consenlabs/tokencore/wallet/transaction/EthereumTransactionTest.java: -------------------------------------------------------------------------------- 1 | package org.consenlabs.tokencore.wallet.transaction; 2 | 3 | 4 | import org.consenlabs.tokencore.foundation.rlp.RlpString; 5 | import org.consenlabs.tokencore.foundation.rlp.RlpType; 6 | import org.consenlabs.tokencore.foundation.utils.NumericUtil; 7 | import org.bitcoinj.core.ECKey; 8 | import org.hamcrest.core.IsEqual; 9 | import org.junit.Test; 10 | 11 | import java.math.BigInteger; 12 | import java.util.List; 13 | 14 | import static org.hamcrest.CoreMatchers.is; 15 | import static org.junit.Assert.assertThat; 16 | 17 | public class EthereumTransactionTest { 18 | 19 | 20 | @Test 21 | public void testEtherTransactionAsRlpValues() { 22 | SignatureData signatureData = new SignatureData((byte) 0, new byte[32], new byte[32]); 23 | List rlpStrings = createEtherTransaction().asRlpValues(signatureData); 24 | assertThat(rlpStrings.size(), is(6)); 25 | assertThat(rlpStrings.get(3), IsEqual.equalTo(RlpString.create(new BigInteger("add5355", 16)))); 26 | } 27 | 28 | @Test 29 | public void testEip155Encode() { 30 | SignatureData signatureData = new SignatureData((byte) 1, new byte[]{}, new byte[]{}); 31 | assertThat(createEip155RawTransaction().encodeToRLP(signatureData), 32 | is(NumericUtil.hexToBytes( 33 | "0xec098504a817c800825208943535353535353535353535353535353535353535880de0" 34 | + "b6b3a764000080018080"))); 35 | } 36 | 37 | @Test 38 | public void testEip155Transaction() { 39 | // https://github.com/ethereum/EIPs/issues/155 40 | ECKey ecKey = ECKey.fromPrivate(NumericUtil.hexToBytes("0x4646464646464646464646464646464646464646464646464646464646464646")); 41 | 42 | assertThat(createEip155RawTransaction().signTransaction(1, ecKey.getPrivKeyBytes()), 43 | is( "f86c098504a817c800825208943535353535353535353535353535353535353535880" 44 | + "de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d" 45 | + "3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf55" 46 | + "5c9f3dc64214b297fb1966a3b6d83")); 47 | } 48 | 49 | @Test 50 | public void testETCTransaction() { 51 | // https://github.com/ethereum/EIPs/issues/155 52 | ECKey ecKey = ECKey.fromPrivate(NumericUtil.hexToBytes("0x4646464646464646464646464646464646464646464646464646464646464646")); 53 | 54 | assertThat(createEip155RawTransaction().signTransaction((byte) 61, ecKey.getPrivKeyBytes()), 55 | is( "f86d098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080819da09e59aa73a10ec8fe5a97fe7560806315624c1a67aeeb59310fdc0001ba2b38a0a0719b723ff1b40c21c4235cbbbdaac0bf775be8f479c31caea806710f70f98927")); 56 | } 57 | 58 | private static EthereumTransaction createEtherTransaction() { 59 | return new EthereumTransaction( 60 | BigInteger.ZERO, BigInteger.ONE, BigInteger.TEN, "0xadd5355", 61 | BigInteger.valueOf(Long.MAX_VALUE), ""); 62 | } 63 | 64 | private static EthereumTransaction createEip155RawTransaction() { 65 | return new EthereumTransaction( 66 | BigInteger.valueOf(9), BigInteger.valueOf(20000000000L), 67 | BigInteger.valueOf(21000), "0x3535353535353535353535353535353535353535", 68 | BigInteger.valueOf(1000000000000000000L),""); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/test/resources/eos_migration_keystore/5776d691-6a29-4111-81b1-9c3b053b9eaf.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "5776d691-6a29-4111-81b1-9c3b053b9eaf", 3 | "keyPathPrivates": [ 4 | { 5 | "privateKey": { 6 | "encStr": "11b79c073136d4f5a31ebdeef01becdb4df2087ceb8482dff2877f5dd4ed3e79", 7 | "nonce": "df711a8a46728dde478a4ec97d1c8ac7" 8 | }, 9 | "publicKey": "EOS6ATRYTiYK3pguimUuZJPu8UGXvHnhjD1qcEXCj3vbbq7SHRvpA" 10 | }, 11 | { 12 | "privateKey": { 13 | "encStr": "a611558aa5cac9c3f57af6fd91e70b690294de8ca275583d12e3941e850a36af", 14 | "nonce": "10fb240bfbb819d826e697ba89f69d1c" 15 | }, 16 | "publicKey": "EOS8EhtUqZ9WaNuJHsXmcUzD5nh9zp5ZqQpAg73KoNKMGnkwj9B8A" 17 | } 18 | ], 19 | "mnemonicPath": "m/44'/194'", 20 | "address": "longhairzlh3", 21 | "version": 10001, 22 | "crypto": { 23 | "mac": "f8fbe312f7b07750c5522d72be5fc23b38c3e68839ddb160ecbf519e33e4ec06", 24 | "cipherparams": { 25 | "iv": "e5c66a6f4551141d43029a3643d72951" 26 | }, 27 | "kdfparams": { 28 | "dklen": 32, 29 | "r": 8, 30 | "salt": "9ca622bfc9aed03af485444c7f6bee148ac58266bc131994284f4469b35e4b85", 31 | "p": 1, 32 | "n": 262144 33 | }, 34 | "cipher": "aes-128-ctr", 35 | "ciphertext": "9d84b60244b410d9c3a02d2e2bf53a0d7b3d5d0704aa57c9f9d02b32f43cb603", 36 | "kdf": "scrypt" 37 | }, 38 | "imTokenMeta": { 39 | "backup": [], 40 | "name": "EOS", 41 | "segWit": "NONE", 42 | "source": "RECOVERED_IDENTITY", 43 | "passwordHint": "", 44 | "version": "iOS-2.0.10.305", 45 | "mode": "normal", 46 | "chainType": "EOS", 47 | "timestamp": "1536922978", 48 | "network": "MAINNET", 49 | "walletType": "HD_SHA256" 50 | }, 51 | "encMnemonic": { 52 | "encStr": "bc9b2f7cd01e8706176bc64dc73f0694cedae2bf7ea2508290c1657a569c748e18e919c53fb8d1d57e32585ca8deccf68de1cf02f3fa7297f38232fc8f54d066e9371ed090049954b3a3688f1b37ebc4", 53 | "nonce": "cf0dbc1027caec7436862304df65fc95" 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/address_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "Crypto": { 5 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 6 | "cipherparams": { 7 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 8 | }, 9 | "cipher": "aes-128-ctr", 10 | "kdf": "scrypt", 11 | "kdfparams": { 12 | "dklen": 32, 13 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 14 | "n": 8192, 15 | "r": 8, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "keystore_invalid" 21 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/address_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ab3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 20 | }, 21 | "err": "private_key_address_not_match" 22 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/cipher_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "kdf": "scrypt", 11 | "kdfparams": { 12 | "dklen": 32, 13 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 14 | "n": 8192, 15 | "r": 8, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "keystore_invalid" 21 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/cipher_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-129-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 20 | }, 21 | "err": "keystore_invalid" 22 | } 23 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/cipherparams_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipher": "aes-128-ctr", 8 | "kdf": "scrypt", 9 | "kdfparams": { 10 | "dklen": 32, 11 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 12 | "n": 8192, 13 | "r": 8, 14 | "p": 1 15 | }, 16 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 17 | }, 18 | "err": "keystore_invalid" 19 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/cipherparams_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "791d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 20 | }, 21 | "err": "private_key_address_not_match" 22 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/ciphertext_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "cipherparams": { 7 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 8 | }, 9 | "cipher": "aes-128-ctr", 10 | "kdf": "scrypt", 11 | "kdfparams": { 12 | "dklen": 32, 13 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 14 | "n": 8192, 15 | "r": 8, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "keystore_invalid" 21 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/ciphertext_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "29a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 20 | }, 21 | "err": "mac_unmatch" 22 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdf_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdfparams": { 12 | "dklen": 32, 13 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 14 | "n": 8192, 15 | "r": 8, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "keystore_invalid" 21 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdf_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scryption", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 20 | }, 21 | "err": "keystore_invalid" 22 | } 23 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_dklen_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 14 | "n": 8192, 15 | "r": 8, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "kdf_params_invalid" 21 | } 22 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 13 | }, 14 | "err": "keystore_invalid" 15 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_n_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "r": 8, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "kdf_params_invalid" 21 | } 22 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_pbkdf2_c_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "c9bc8d86f756290e62eb3ef9afbe3f9f" 6 | }, 7 | "ciphertext": "ee73387483087c3c9250668e1295984b198c336906dfc1a4d810a18628addda9", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "dklen": 32, 11 | "prf": "hmac-sha256", 12 | "salt": "383091f0e852dda2d2d73c95e0c02058171842cdea5a0fcb427c8efef20bea36" 13 | }, 14 | "mac": "7310d600144355229c16e91194ddbb1b111acc86ead26fba0a70b040fb99af05" 15 | }, 16 | "id": "fe8dd0aa-7db6-4d67-bafa-a220845d9b29", 17 | "version": 3, 18 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 19 | "err": "kdf_params_invalid" 20 | } 21 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_pbkdf2_c_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "c9bc8d86f756290e62eb3ef9afbe3f9f" 6 | }, 7 | "ciphertext": "ee73387483087c3c9250668e1295984b198c336906dfc1a4d810a18628addda9", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 1024, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "383091f0e852dda2d2d73c95e0c02058171842cdea5a0fcb427c8efef20bea36" 14 | }, 15 | "mac": "7310d600144355229c16e91194ddbb1b111acc86ead26fba0a70b040fb99af05" 16 | }, 17 | "id": "fe8dd0aa-7db6-4d67-bafa-a220845d9b29", 18 | "version": 3, 19 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 20 | "err": "mac_unmatch" 21 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_pbkdf2_salt_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "c9bc8d86f756290e62eb3ef9afbe3f9f" 6 | }, 7 | "ciphertext": "ee73387483087c3c9250668e1295984b198c336906dfc1a4d810a18628addda9", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 10240, 11 | "dklen": 32, 12 | "prf": "hmac-sha256" 13 | }, 14 | "mac": "7310d600144355229c16e91194ddbb1b111acc86ead26fba0a70b040fb99af05" 15 | }, 16 | "id": "fe8dd0aa-7db6-4d67-bafa-a220845d9b29", 17 | "version": 3, 18 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 19 | "err": "kdf_params_invalid" 20 | } 21 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_pbkdf2_salt_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "c9bc8d86f756290e62eb3ef9afbe3f9f" 6 | }, 7 | "ciphertext": "ee73387483087c3c9250668e1295984b198c336906dfc1a4d810a18628addda9", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 10240, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "483091f0e852dda2d2d73c95e0c02058171842cdea5a0fcb427c8efef20bea36" 14 | }, 15 | "mac": "7310d600144355229c16e91194ddbb1b111acc86ead26fba0a70b040fb99af05" 16 | }, 17 | "id": "fe8dd0aa-7db6-4d67-bafa-a220845d9b29", 18 | "version": 3, 19 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 20 | "err": "mac_unmatch" 21 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_salt_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "n": 8192, 15 | "r": 8, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "kdf_params_invalid" 21 | } 22 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_salt_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "88832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 20 | }, 21 | "err": "mac_unmatch" 22 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_scrypt_p_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "kdf_params_invalid" 21 | } 22 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/kdfparams_scrypt_r_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "p": 1 17 | }, 18 | "mac": "e743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 19 | }, 20 | "err": "kdf_params_invalid" 21 | } 22 | -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/mac_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | } 19 | }, 20 | "err": "keystore_invalid" 21 | } -------------------------------------------------------------------------------- /app/src/test/resources/invalid_keystores/mac_wrong.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "5896e547-edb6-47d1-b960-b793624198e5", 4 | "address": "ca3b0795a42b46be755ce282924d17024c70fd88", 5 | "Crypto": { 6 | "ciphertext": "19a9dc09327a9b4114bf359924ff39093c0f3ebf0993e1c42821e5f1813cedbe", 7 | "cipherparams": { 8 | "iv": "691d7876d449e9f66ed3938e4a3635d7" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "87832e1217a1ec18bed3563fce0866711557b3f7365f51b6b5e92ff327ddb83a", 15 | "n": 8192, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "f743fdf489e22eaa3c3f84f02f546d37d39ecabc1793ae45bc6b08c6104ff3a8" 20 | }, 21 | "err": "mac_unmatch" 22 | } -------------------------------------------------------------------------------- /app/src/test/resources/keystore/02a55ab6-554a-4e78-bc26-6a7acced7e5e.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "437ef8c8553df9910ad117ecec5b8c05" 6 | }, 7 | "ciphertext": "acabec2bd6fab27d867ebabe0ded9c64c85aebd294d29ecf537e563474ebb931522dbb977e0644830516550255edde02c507863cb083b55f2f0f759c2f8a885a81a6518237e7b65b7cf3e912fb36e42a13a7b2df3d401e5ff778a412a6d4c5516645770c4b12f2e30551542c699eef", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 65535, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "33c8f2d27fe994a1e7d51108c7811cdaa2b821cc6760ed760954b4b67a1bcd8c" 14 | }, 15 | "mac": "6b86a18f4ba9f3f428e256e72a3d832dcf0cd1cb820ec61e413a64d83b012059" 16 | }, 17 | "id": "02a55ab6-554a-4e78-bc26-6a7acced7e5e", 18 | "version": 44, 19 | "address": "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN", 20 | "encMnemonic": { 21 | "encStr": "840fad94f4bf4128f629bc1dec731d156283cc4099e3c7659a3bf382031443fcdce6debaaef444393c446d2b4007064c010f6a442b3ad0ff0851c1bd638ba251afa92d3106457bd78c49", 22 | "nonce": "4d691a7f0cb6396e96e8dc3e4f35dccd" 23 | }, 24 | "info": { 25 | "curve": "spec256k1", 26 | "purpuse": "sign" 27 | }, 28 | "mnemonicPath": "m/44'/1'/0'", 29 | "xpub": "tpubDCpWeoTY6x4BR2PqoTFJnEdfYbjnC4G8VvKoDUPFjt2dvZJWkMRxLST1pbVW56P7zY3L5jq9MRSeff2xsLnvf9qBBN9AgvrhwfZgw5dJG6R", 30 | "imTokenMeta": { 31 | "backup": [], 32 | "chainType": "BITCOIN", 33 | "network": "TESTNET", 34 | "name": "BTC", 35 | "passwordHint": "", 36 | "source": "RECOVERED_IDENTITY", 37 | "walletType": "HD", 38 | "timestamp": 1519611221, 39 | "segWit": "NONE" 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/test/resources/keystore/045861fe-0e9b-4069-92aa-0ac03cad55e0.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "a322450a5d78b355d3f10d32424bdeb7" 6 | }, 7 | "ciphertext": "7323633304b6e10fce17725b2f6ff8190b8e2f1c4fdb29904802e8eb9cb1ac6b", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 65535, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "51bcbf8d464d96fca108a6bd7779381076a3f5a6ca5242eb12c8c219f1015767" 14 | }, 15 | "mac": "cf81fa8f858554a21d00a376923138e727567f686f30f77fe3bba31b40a91c56" 16 | }, 17 | "id": "045861fe-0e9b-4069-92aa-0ac03cad55e0", 18 | "version": 3, 19 | "address": "41983f2e3af196c1df429a3ff5cdecc45c82c600", 20 | "imTokenMeta": { 21 | "backup": [], 22 | "chainType": "ETHEREUM", 23 | "mode": "NORMAL", 24 | "name": "ETH-Wallet-2", 25 | "passwordHint": "", 26 | "source": "KEYSTORE", 27 | "timestamp": 1519611469, 28 | "walletType": "V3" 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/test/resources/keystore/175169f7-5a35-4df7-93c1-1ff612168e71.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "3a442e8b02843edf71b8d3a9c9da2c3b" 6 | }, 7 | "ciphertext": "fcbcceae1d239f9575c55f4c4f81eeba44a6ad9d948f544af2ffee9efef2c038", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 65535, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "fa141145a343d9b6c7e2f12e0e56d564bc4d1b46cd48e8f7d898779e06357f1f" 14 | }, 15 | "mac": "50ee3b40129c5f18f9ff6982db0eb18504ea2e8f3d96e4ac062b4eb5849cf011" 16 | }, 17 | "id": "175169f7-5a35-4df7-93c1-1ff612168e71", 18 | "version": 3, 19 | "address": "6031564e7b2f5cc33737807b2e58daff870b590b", 20 | "encMnemonic": { 21 | "encStr": "267bda938e4edbf7c420e89c59c6862f9127c7275d012b1b607f9e91ddb94574e81e94f6d8155e3c85ede03f584e09916122f03c72b67a1f96ddbf291beb46894d9a02d30170a9444692", 22 | "nonce": "3cfe9f0b32b5d592e5fab54bd28863cd" 23 | }, 24 | "mnemonicPath": "m/44'/60'/0'/0/0", 25 | "imTokenMeta": { 26 | "backup": [], 27 | "chainType": "ETHEREUM", 28 | "name": "ETH", 29 | "passwordHint": "", 30 | "source": "RECOVERED_IDENTITY", 31 | "timestamp": 1519611221, 32 | "walletType": "V3" 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/test/resources/keystore/3831346d-0b81-405b-89cf-cdb1d010430e.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "3831346d-0b81-405b-89cf-cdb1d010430e", 3 | "version": 44, 4 | "crypto": { 5 | "ciphertext": "24c8cffd810042cfddc9315993525e530432d9207b4b968a72393c9524e29d18b24b7c04f8f674ecddf00b7be4b0fd4a4d190b80aa9726d8a4bede9f97acac19b4accdcf1cbebed54f8d73978edc053ecaa3638835853822d4059134b55d1fa7147a1919e41c20c703448ee39de657", 6 | "mac": "32d19c9b43f34a0362742e46907c9610b7cd01bae8b4ce4480aa6e8a403b04c6", 7 | "cipher": "aes-128-ctr", 8 | "cipherparams": { 9 | "iv": "d56f5856edeb2a9b5bf93d9e0a9cc161" 10 | }, 11 | "kdf": "pbkdf2", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "c": 65535, 15 | "prf": "hmac-sha256", 16 | "salt": "cf748fc0fc8b06d88e145a62f70c98d6726f4115f50088268fb60609186abc64" 17 | } 18 | }, 19 | "address": "2NCTX2isUH3bwkrSab6kJT1Eu9pWPqAStRp", 20 | "encMnemonic": { 21 | "encStr": "175c8cd7b0b97241a2c8d89d612033510ba6e5a92a586242bb48cf12debc704aff2af273814997bcec88a78367d22d046590d41e5beb3cc2348cafa32ac29fc2233c30d1503b9ccbc12f", 22 | "nonce": "fcbd79db1493da089ed468bafec061ca" 23 | }, 24 | "mnemonicPath": "m/44'/0'/0'", 25 | "xpub": "tpubDDDcs8o1LaKXKXaPTEVBUZJYTgNAte4xj24MtFCMsfrHku93ZZjy87CGyz93dcocR6x6JHdusHodD9EVcSQuDbmkAWznWZtvyqyMDqS6VK4", 26 | "info": { 27 | "curve": "spec256k1", 28 | "purpuse": "sign" 29 | }, 30 | "imTokenMeta": { 31 | "name": "name", 32 | "passwordHint": "passwordHint", 33 | "chainType": "BITCOIN", 34 | "timestamp": 1526462133, 35 | "network": "TESTNET", 36 | "backup": [], 37 | "source": "MNEMONIC", 38 | "mode": "NORMAL", 39 | "walletType": "HD", 40 | "segWit": "P2WPKH" 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/test/resources/keystore/42c275c6-957a-49e8-9eb3-43c21cbf583f.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "2cb9d4457b284e47877d08a5c9493b46" 6 | }, 7 | "ciphertext": "17ff4858e697455f4966c6072473f3501534bc20deb339b58aeb8db0bd9fe91777148d0a909f679fb6e3a7a64609034afeb72a", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 10240, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "37890eb305866aa07853d14e7666c2ed31e18efc1129f1c5a66b9cc93d03fd73" 14 | }, 15 | "mac": "4906577f075ad714f328e7b33829fdccfa8cd22eab2c0a8bc4f577824188ed16" 16 | }, 17 | "id": "42c275c6-957a-49e8-9eb3-43c21cbf583f", 18 | "version": 3, 19 | "address": "longhairzlh2", 20 | "imTokenMeta": { 21 | "backup": [], 22 | "chainType": "EOS", 23 | "mode": "NORMAL", 24 | "name": "EOS-Wallet-1", 25 | "network": "MAINNET", 26 | "passwordHint": "", 27 | "source": "WIF", 28 | "timestamp": 1535426384, 29 | "walletType": "V3" 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/test/resources/keystore/7f5406be-b5ee-4497-948c-877deab8c994.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "dce8a7d8d80f7b0772d93d94ee0a88c2" 6 | }, 7 | "ciphertext": "e00963172019129051f1abc699628a9ba929fcfa6bafbaeae95c2506dba74353", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 65535, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "c2aa983c4dc4e8dee98584abf639a98bf5e9389d961ae23e764434c5b11e093c" 14 | }, 15 | "mac": "aa29c325e8dddfbc7a9e614f78c52e95a2d94e4337f8d999da74397d837f3be0" 16 | }, 17 | "id": "7f5406be-b5ee-4497-948c-877deab8c994", 18 | "version": 10001, 19 | "address": "longhairzlh2", 20 | "encMnemonic": { 21 | "encStr": "3a4e064bdd271f54e0b1b60cfbfd02a627acd9d22478b73d5fb53efd642c7c8a6fd6e63bf81d7a2d05487e3e316616249aa0978c93744baedd3a76dabbdfab297671d61d12130d883609a9f450623660a32e", 22 | "nonce": "c20bed957c3abf241e3a9806db7314a4" 23 | }, 24 | "mnemonicPath": "m/44'/194'", 25 | "keyPathPrivates": [ 26 | { 27 | "encPrivate": { 28 | "encStr": "8657459f1ad4b7b8d2db4850b9072dab1da6d08cf248070068dc910df73c1dc5", 29 | "nonce": "cb64438515ef2565b7d0d1a036297bbd" 30 | }, 31 | "publicKey": "EOS8W4CoVEhTj6RHhazfw6wqtrHGk4kE4fYb2VzCexAk81SjPU1mL" 32 | }, 33 | { 34 | "encPrivate": { 35 | "encStr": "a69a4614b1efa0bb9680d3ee28204bf86853511c58273cfb0829047e2da52d3f", 36 | "nonce": "b1ce6665d381f7990222871452dceada" 37 | }, 38 | "publicKey": "EOS6ES7TPo4UzNAvM3wrS1WxTpn3aZw8NDG6Vtb4xMvZZLDy9eJja" 39 | } 40 | ], 41 | "imTokenMeta": { 42 | "backup": [], 43 | "chainType": "EOS", 44 | "mode": "NORMAL", 45 | "name": "EOS", 46 | "passwordHint": "", 47 | "source": "RECOVERED_IDENTITY", 48 | "timestamp": 1530537386, 49 | "walletType": "HD_SHA256" 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/test/resources/keystore/identity.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "76e228e45cc66228e1890dec1d211de6" 6 | }, 7 | "ciphertext": "18ce44f304402417eb96f3468a4f49450cb4b6916c32522ae5173960db929f486ff373781f1fca4742ed853b6c2d7e6951533a68805ae7586befc7d830718a3d8e9074ea6cd5bb361a1b06cf3c2cf8b4d82755c6639816b3ec1727cdd0b5e45a144f5cf943177a30ee9ecee2a65314", 8 | "kdf": "pbkdf2", 9 | "kdfparams": { 10 | "c": 65535, 11 | "dklen": 32, 12 | "prf": "hmac-sha256", 13 | "salt": "519463106b45fcb3ffcb39b1b8805cdb3d789a908502ce5f3b976dbca780a2d0" 14 | }, 15 | "mac": "359ea5fe92c0dce5ab03a31e354de1f33a9f7cb822f012d00da68bddc7b02c5d" 16 | }, 17 | "id": "a1b92c23-5fcb-43a7-a8fe-07bb56acd033", 18 | "version": 10000, 19 | "encAuthKey": { 20 | "encStr": "ba3afce29774e6225f6bd24df91fb911dc4583f8d999669912388ce461d49ac0", 21 | "nonce": "b1ab60c19a5fc6568bef1eea30d38a98" 22 | }, 23 | "encKey": "9513617c9b398edebfb46080a8f0cf6c", 24 | "encMnemonic": { 25 | "encStr": "01dfbda9bd529fda10233082e8bed9ea7b7acda60c7f676f9cc5a4cf7c6f65bd3426e5e3cfec27652d9e36f0f855dbd2f8f0693b79f82bece69cd291ec2979862a45b40e758f48336c41", 26 | "nonce": "869c45b926817db6a842eec33acf846e" 27 | }, 28 | "identifier": "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg", 29 | "ipfsId": "QmVoPZQnQjppqcD4gUh1KrfmjbWQCtXQpm2YrFG6Voquh2", 30 | "walletIDs": [ 31 | "175169f7-5a35-4df7-93c1-1ff612168e71", 32 | "02a55ab6-554a-4e78-bc26-6a7acced7e5e", 33 | "045861fe-0e9b-4069-92aa-0ac03cad55e0" 34 | ], 35 | "imTokenMeta": { 36 | "backup": [], 37 | "network": "MAINNET", 38 | "name": "identity_name", 39 | "passwordHint": "", 40 | "source": "RECOVERED_IDENTITY", 41 | "timestamp": 1519611220 42 | } 43 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.0' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/token-core-android/12533cdef957d4a7052acc0bd5f67329493cec47/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Apr 08 09:58:24 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------