From 36e4035bacf580ae1a245d39ff6ea179e113ee9c Mon Sep 17 00:00:00 2001 From: Victor Xu Date: Thu, 21 Feb 2019 15:03:45 +0800 Subject: [PATCH 1/2] sign by cardlet for eNotes --- app/build.gradle | 37 +++--- app/src/main/AndroidManifest.xml | 12 ++ .../activity/login/LoginActivity.java | 67 +++++++++++ .../main/BottomNavigationActivity.java | 85 +++++++++++++ .../activity/transfer/TransferActivity.java | 54 +++++++-- .../fragment/exchange/BuySellFragment.java | 56 ++++++++- app/src/main/res/values-en/strings.xml | 9 ++ app/src/main/res/values-zh/strings.xml | 9 ++ app/src/main/res/values/strings.xml | 4 + app/src/main/res/xml/nfc_tech_filter.xml | 7 ++ build.gradle | 1 + config.gradle | 2 +- data-provider/build.gradle | 1 + .../graphene/chain/SignedTransaction.java | 77 ++++++++++++ .../websocket/BitsharesWalletWraper.java | 7 ++ .../cybex/provider/websocket/WalletApi.java | 31 +++++ module-base/build.gradle | 3 + .../cybex/basemodule/base/BaseActivity.java | 113 +++++++++++++++++- .../cybex/basemodule/dialog/CybexDialog.java | 7 +- .../src/main/res/values-en/strings.xml | 10 ++ .../src/main/res/values-zh/strings.xml | 10 ++ module-base/src/main/res/values/strings.xml | 10 ++ 22 files changed, 579 insertions(+), 33 deletions(-) create mode 100755 app/src/main/res/values-en/strings.xml create mode 100755 app/src/main/res/values-zh/strings.xml create mode 100755 app/src/main/res/xml/nfc_tech_filter.xml create mode 100644 module-base/src/main/res/values-en/strings.xml create mode 100644 module-base/src/main/res/values-zh/strings.xml create mode 100644 module-base/src/main/res/values/strings.xml diff --git a/app/build.gradle b/app/build.gradle index 83a31948..c3a8a04c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,22 +11,22 @@ android { def keystoreFile = '' def keystorePassword = '' - Properties properties = new Properties() - properties.load(project.rootProject.file('keystores/release.keystore.properties').newDataInputStream()) - - keystoreAlias = properties.getProperty("key.alias") - keystoreAliasPassword = properties.getProperty("key.alias.password") - keystoreFile = file(properties.getProperty("key.path")) - keystorePassword = properties.getProperty("key.store.password") - - signingConfigs { - release { - keyAlias keystoreAlias - keyPassword keystoreAliasPassword - storeFile keystoreFile - storePassword keystorePassword - } - } +// Properties properties = new Properties() +// properties.load(project.rootProject.file('keystores/release.keystore.properties').newDataInputStream()) +// +// keystoreAlias = properties.getProperty("key.alias") +// keystoreAliasPassword = properties.getProperty("key.alias.password") +// keystoreFile = file(properties.getProperty("key.path")) +// keystorePassword = properties.getProperty("key.store.password") +// +// signingConfigs { +// release { +// keyAlias keystoreAlias +// keyPassword keystoreAliasPassword +// storeFile keystoreFile +// storePassword keystorePassword +// } +// } flavorDimensions "default" productFlavors { @@ -67,7 +67,7 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - signingConfig signingConfigs.release +// signingConfig signingConfigs.release } } lintOptions { @@ -133,7 +133,8 @@ dependencies { //Jadeticon implementation 'com.github.WycliffeAssociates:jdenticon-kotlin:-SNAPSHOT' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' - + //eNotes + implementation 'io.enotes.sdk:eNotes:1.0.0' implementation project(':MPChartLib') if (isModule == "true") { implementation project(':module-eto') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 319f1ac9..3f957a1a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,11 @@ xmlns:tools="http://schemas.android.com/tools" package="com.cybexmobile"> + + + @@ -57,6 +62,13 @@ + + + + + reply) { public void run() { hideLoadDialog(); if (result == 0) { + setLoginFrom(false); Intent returnIntent = new Intent(); returnIntent.putExtra(INTENT_PARAM_LOGIN_IN, true); returnIntent.putExtra(INTENT_PARAM_NAME, email); @@ -285,6 +309,49 @@ public void onFailure() { } } + private void loginByENotes() { + // Store values at the time of the login attempt. + String email = mUserNameView.getText().toString().trim(); + String password = mPasswordView.getText().toString().trim(); + if (TextUtils.isEmpty(email) || TextUtils.isEmpty(password)) { + return; + } + showLoadDialog(true); + try { + BitsharesWalletWraper.getInstance().get_account_object(email, new MessageCallback>() { + @Override + public void onMessage(Reply reply) { + AccountObject accountObject = reply.result; + int result = BitsharesWalletWraper.getInstance().import_account_password(accountObject, email, password); + runOnUiThread(new Runnable() { + @Override + public void run() { + hideLoadDialog(); + setLoginFrom(true); + Intent returnIntent = new Intent(); + returnIntent.putExtra(INTENT_PARAM_LOGIN_IN, true); + returnIntent.putExtra(INTENT_PARAM_NAME, email); + setResult(Activity.RESULT_OK, returnIntent); + EventBus.getDefault().post(new Event.LoginIn(email)); + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + sharedPreferences.edit().putBoolean(PREF_IS_LOGIN_IN, true).apply(); + sharedPreferences.edit().putString(PREF_NAME, email).apply(); + finish(); + } + }); + + } + + @Override + public void onFailure() { + hideLoadDialog(); + } + }); + } catch (NetworkStatusException e) { + e.printStackTrace(); + } + } + @Override public Loader onCreateLoader(int i, Bundle bundle) { return new CursorLoader(this, diff --git a/app/src/main/java/com/cybexmobile/activity/main/BottomNavigationActivity.java b/app/src/main/java/com/cybexmobile/activity/main/BottomNavigationActivity.java index bcd2e7a8..d4a1cb9a 100644 --- a/app/src/main/java/com/cybexmobile/activity/main/BottomNavigationActivity.java +++ b/app/src/main/java/com/cybexmobile/activity/main/BottomNavigationActivity.java @@ -4,8 +4,13 @@ import android.app.Dialog; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; +import android.nfc.NfcAdapter; +import android.nfc.Tag; import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.design.widget.BottomNavigationView; import android.support.v4.app.FragmentManager; @@ -19,9 +24,13 @@ import com.cybex.basemodule.constant.Constant; import com.cybex.basemodule.service.WebSocketService; import com.cybex.eto.fragment.EtoFragment; +import com.cybex.provider.exception.NetworkStatusException; +import com.cybex.provider.graphene.chain.AccountObject; import com.cybex.provider.http.response.AppConfigResponse; import com.cybex.provider.market.WatchlistData; import com.cybex.provider.websocket.BitsharesWalletWraper; +import com.cybex.provider.websocket.MessageCallback; +import com.cybex.provider.websocket.Reply; import com.cybex.provider.websocket.apihk.LimitOrderWrapper; import com.cybexmobile.BuildConfig; import com.cybexmobile.activity.markets.MarketsActivity; @@ -37,6 +46,7 @@ import com.cybexmobile.helper.BottomNavigationViewHelper; import com.cybexmobile.R; +import org.ethereum.util.ByteUtil; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -44,11 +54,17 @@ import java.util.ArrayList; import java.util.Locale; +import io.enotes.sdk.repository.card.Command; +import io.enotes.sdk.repository.card.CommandException; +import io.enotes.sdk.repository.card.TLVBox; +import io.enotes.sdk.repository.db.entity.Card; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; +import static com.cybex.basemodule.constant.Constant.PREF_IS_LOGIN_IN; +import static com.cybex.basemodule.constant.Constant.PREF_NAME; import static com.cybexmobile.activity.markets.MarketsActivity.RESULT_CODE_BACK; import static com.cybex.basemodule.constant.Constant.INTENT_PARAM_ACTION; import static com.cybex.basemodule.constant.Constant.INTENT_PARAM_WATCHLIST; @@ -104,6 +120,73 @@ protected void onCreate(Bundle savedInstanceState) { } } + @Override + protected void readCardOnSuccess(Card card) { + super.readCardOnSuccess(card); + if (mBottomNavigationView.getSelectedItemId() != R.id.navigation_exchange) { + try { + byte[] bytes = ByteUtil.hexStringToBytes(cardManager.transmitApdu(Command.newCmd().setDesc("cybex_account").setCmdStr("00CA0032"))); + TLVBox tlvBox = TLVBox.parse(bytes, 0, bytes.length); + String stringValue = new String(tlvBox.getBytesValue(0x32)); + setLoginPublicKey(card.getCurrencyPubKey()); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + loginByENotes(stringValue, "123456789"); + } + }, 500); + } catch (CommandException e) { + e.printStackTrace(); + } + } + } + + private void loginByENotes(String email, String password) { + if (TextUtils.isEmpty(email) || TextUtils.isEmpty(password)) { + return; + } + showLoadDialog(true); + try { + BitsharesWalletWraper.getInstance().get_account_object(email, new MessageCallback>() { + @Override + public void onMessage(Reply reply) { + AccountObject accountObject = reply.result; + int result = BitsharesWalletWraper.getInstance().import_account_password(accountObject, email, password); + runOnUiThread(new Runnable() { + @Override + public void run() { + hideLoadDialog(); + setLoginFrom(true); + EventBus.getDefault().post(new Event.LoginIn(email)); + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + sharedPreferences.edit().putBoolean(PREF_IS_LOGIN_IN, true).apply(); + sharedPreferences.edit().putString(PREF_NAME, email).apply(); + + mBottomNavigationView.setSelectedItemId(R.id.navigation_account); + } + }); + + } + + @Override + public void onFailure() { + hideLoadDialog(); + } + }); + } catch (NetworkStatusException e) { + e.printStackTrace(); + } + } + + private void parseIntent() { + Intent intent = getIntent(); + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag != null) {// from nfc + cardManager.parseNfcTag(tag); + } + + } + @Subscribe(threadMode = ThreadMode.MAIN) public void onConfigChanged(Event.ConfigChanged event) { isRecreate = true; @@ -156,6 +239,7 @@ protected void onDestroy() { } LimitOrderWrapper.getInstance().disconnect(); BitsharesWalletWraper.getInstance().cancelLockWalletTime(); + BaseActivity.cardApp=null; } @Override @@ -170,6 +254,7 @@ protected void onNewIntent(Intent intent) { super.onNewIntent(intent); //register after recreate that activity isRecreate = true; + parseIntent(); recreate(); } diff --git a/app/src/main/java/com/cybexmobile/activity/transfer/TransferActivity.java b/app/src/main/java/com/cybexmobile/activity/transfer/TransferActivity.java index deae2f2d..21327c1d 100644 --- a/app/src/main/java/com/cybexmobile/activity/transfer/TransferActivity.java +++ b/app/src/main/java/com/cybexmobile/activity/transfer/TransferActivity.java @@ -82,6 +82,8 @@ import butterknife.OnFocusChange; import butterknife.OnTextChanged; import butterknife.Unbinder; +import io.enotes.sdk.repository.db.entity.Card; +import io.enotes.sdk.utils.ReaderUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; @@ -166,7 +168,7 @@ public class TransferActivity extends BaseActivity implements private int mTotalLockTime; private int mLockTimeUnit = 1; - + private Card mCard; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -832,7 +834,7 @@ private void clearTransferData() { resetTransferButtonState(); checkIsLockAndLoadTransferFee(ASSET_ID_CYB, false); } - + private UnlockDialog unlockDialog; /** * 检查钱包锁定状态 -> 加载转账手续费 */ @@ -840,8 +842,8 @@ private void checkIsLockAndLoadTransferFee(String feeAssetId, boolean isLoadFeeT if (mFromAccountObject == null) { return; } - if (BitsharesWalletWraper.getInstance().is_locked()) { - CybexDialog.showUnlockWalletDialog(getSupportFragmentManager(), mFromAccountObject, + if (BitsharesWalletWraper.getInstance().is_locked() && !isLoginFromENotes()) { + unlockDialog = CybexDialog.showUnlockWalletDialog(getSupportFragmentManager(), mFromAccountObject, mFromAccountObject.name, new UnlockDialog.UnLockDialogClickListener() { @Override public void onUnLocked(String password) { @@ -853,12 +855,19 @@ public void onUnLocked(String password) { } } + @Override + protected void readCardOnSuccess(Card card) { + super.readCardOnSuccess(card); + mCard = card; + if (unlockDialog != null) unlockDialog.dismiss(); + } + /** * 检查钱包锁定状态 -> 加载转账手续费 -> 转账 */ private void checkIsLockAndTransfer() { - if (BitsharesWalletWraper.getInstance().is_locked()) { - CybexDialog.showUnlockWalletDialog(getSupportFragmentManager(), mFromAccountObject, mFromAccountObject.name, new UnlockDialog.UnLockDialogClickListener() { + if (BitsharesWalletWraper.getInstance().is_locked() && !isLoginFromENotes()) { + unlockDialog = CybexDialog.showUnlockWalletDialog(getSupportFragmentManager(), mFromAccountObject, mFromAccountObject.name, new UnlockDialog.UnLockDialogClickListener() { @Override public void onUnLocked(String password) { showTransferConfirmationDialog(); @@ -901,6 +910,29 @@ private void toTransfer() { if (mFromAccountObject == null || mToAccountObject == null || mSelectedAccountBalanceObjectItem == null) { return; } + if (isLoginFromENotes()) { + if (!cardManager.isConnected()) { + if (ReaderUtils.supportNfc(this)) + showToast(this, getString(R.string.error_connect_card)); + else + showToast(this, getString(R.string.error_connect_card_ble)); + return; + } + if (!cardManager.isPresent()) { + if (ReaderUtils.supportNfc(this)) + showToast(this, getString(R.string.error_connect_card)); + else + showToast(this, getString(R.string.error_connect_card_ble)); + return; + } + if (mCard != null && !mCard.getCurrencyPubKey().equals(getLoginPublicKey())) { + showToast(this, getString(R.string.please_right_card)); + return; + } + if (BaseActivity.cardApp != null) { + mCard = BaseActivity.cardApp; + } + } showLoadDialog(); Operations.base_operation transferOperation; if (mSwLockTime.isChecked() && mSelectedLockTimePublicKey != null && mTotalLockTime != 0) { @@ -934,8 +966,14 @@ private void toTransfer() { BitsharesWalletWraper.getInstance().get_dynamic_global_properties(new MessageCallback>() { @Override public void onMessage(Reply reply) { - SignedTransaction signedTransaction = BitsharesWalletWraper.getInstance().getSignedTransaction( - mFromAccountObject, transferOperation, ID_TRANSER_OPERATION, reply.result); + SignedTransaction signedTransaction; + if (mCard == null) { + signedTransaction = BitsharesWalletWraper.getInstance().getSignedTransaction( + mFromAccountObject, transferOperation, ID_TRANSER_OPERATION, reply.result); + } else { + signedTransaction = BitsharesWalletWraper.getInstance().getSignedTransactionByENotes(cardManager, mCard, + mFromAccountObject, transferOperation, ID_TRANSER_OPERATION, reply.result); + } try { BitsharesWalletWraper.getInstance().broadcast_transaction_with_callback(signedTransaction, mTransferCallback); } catch (NetworkStatusException e) { diff --git a/app/src/main/java/com/cybexmobile/fragment/exchange/BuySellFragment.java b/app/src/main/java/com/cybexmobile/fragment/exchange/BuySellFragment.java index 5f063349..894208b2 100644 --- a/app/src/main/java/com/cybexmobile/fragment/exchange/BuySellFragment.java +++ b/app/src/main/java/com/cybexmobile/fragment/exchange/BuySellFragment.java @@ -3,6 +3,7 @@ import android.app.Dialog; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -25,7 +26,9 @@ import android.widget.EditText; import android.widget.FrameLayout; import android.widget.TextView; +import android.widget.Toast; +import com.cybex.basemodule.base.BaseActivity; import com.cybex.basemodule.base.BaseFragment; import com.cybex.provider.market.WatchlistData; import com.cybex.provider.websocket.MessageCallback; @@ -63,6 +66,8 @@ import butterknife.OnTextChanged; import butterknife.OnTouch; import butterknife.Unbinder; +import io.enotes.sdk.core.CardManager; +import io.enotes.sdk.utils.ReaderUtils; import static com.cybex.basemodule.constant.Constant.INTENT_PARAM_PRECISION; import static com.cybex.basemodule.constant.Constant.INTENT_PARAM_SPINNER_POSITION; @@ -763,11 +768,53 @@ public void onUnLocked(String password) { }); } + protected boolean isLoginFromENotes() { + SharedPreferences sharedPreferences = getActivity().getSharedPreferences("enotes", Context.MODE_PRIVATE); + return sharedPreferences.getBoolean("from", true); + } + + protected String getLoginPublicKey() { + SharedPreferences sharedPreferences = getActivity().getSharedPreferences("enotes", Context.MODE_PRIVATE); + return sharedPreferences.getString("key", ""); + } + + protected void showToast(Context context, String text) { + Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); + + } + /** * 挂单 */ private void toExchange(){ + BaseActivity activity = (BaseActivity) getActivity(); try { + if (isLoginFromENotes()) { + + CardManager cardManager = activity.cardManager; + if (!cardManager.isConnected()) { + if (ReaderUtils.supportNfc(activity)) + showToast(activity, getString(R.string.error_connect_card)); + else + showToast(activity, getString(R.string.error_connect_card_ble)); + return; + } + if (!cardManager.isPresent()) { + if (ReaderUtils.supportNfc(activity)) + showToast(activity, getString(R.string.error_connect_card)); + else + showToast(activity, getString(R.string.error_connect_card_ble)); + return; + } + if (activity.currentCard != null && !activity.currentCard.getCurrencyPubKey().equals(getLoginPublicKey())) { + showToast(activity, getString(R.string.please_right_card)); + return; + } + if(BaseActivity.cardApp!=null){ + activity.currentCard = BaseActivity.cardApp; + } + } + BitsharesWalletWraper.getInstance().get_dynamic_global_properties(new MessageCallback>() { @Override public void onMessage(Reply reply) { @@ -786,8 +833,13 @@ public void onMessage(Reply reply) { mCurrentAction.equals(ACTION_BUY) ? mWatchlistData.getQuoteAsset().id : mWatchlistData.getBaseAsset().id, mIsCybBalanceEnough ? mCybExchangeFee.amount : mBaseOrQuoteExchangeFee.amount, amountSell, amountReceive); - SignedTransaction signedTransaction = BitsharesWalletWraper.getInstance().getSignedTransaction( - mFullAccountObject.account, operation, ID_CREATE_LIMIT_ORDER_OPERATION, reply.result); + SignedTransaction signedTransaction; + if (activity.currentCard == null) + signedTransaction = BitsharesWalletWraper.getInstance().getSignedTransaction( + mFullAccountObject.account, operation, ID_CREATE_LIMIT_ORDER_OPERATION, reply.result); + else + signedTransaction = BitsharesWalletWraper.getInstance().getSignedTransactionByENotes(activity.cardManager, activity.currentCard, + mFullAccountObject.account, operation, ID_CREATE_LIMIT_ORDER_OPERATION, reply.result); try { BitsharesWalletWraper.getInstance().broadcast_transaction_with_callback(signedTransaction, mLimitOrderCreateCallback); } catch (NetworkStatusException e) { diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml new file mode 100755 index 00000000..6c47df43 --- /dev/null +++ b/app/src/main/res/values-en/strings.xml @@ -0,0 +1,9 @@ + + CybexDex + + + Hello blank fragment + Please scan your eNotes with the phone. + Please connect the bluetooth NFC reader. + Please scan the correct card! + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml new file mode 100755 index 00000000..e7fc1810 --- /dev/null +++ b/app/src/main/res/values-zh/strings.xml @@ -0,0 +1,9 @@ + + CybexDex + + + Hello blank fragment + 请将eNotes靠近手机。 + 请将eNotes靠近蓝牙NFC读卡器。 + 请放置正确的eNotes! + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0396526f..f7c8c112 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,4 +3,8 @@ Hello blank fragment + Please scan your eNotes with the phone. + Please connect the bluetooth NFC reader. + Please scan the correct card! + diff --git a/app/src/main/res/xml/nfc_tech_filter.xml b/app/src/main/res/xml/nfc_tech_filter.xml new file mode 100755 index 00000000..acb29b68 --- /dev/null +++ b/app/src/main/res/xml/nfc_tech_filter.xml @@ -0,0 +1,7 @@ + + + + + android.nfc.tech.IsoDep + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 32d3d8e8..51a16eda 100644 --- a/build.gradle +++ b/build.gradle @@ -37,5 +37,6 @@ allprojects { google() jcenter() maven {url 'https://jitpack.io'} + maven { url "https://dl.bintray.com/cryptoenotes/eNotes" } } } \ No newline at end of file diff --git a/config.gradle b/config.gradle index 6e3fb8c5..e8dababc 100644 --- a/config.gradle +++ b/config.gradle @@ -4,7 +4,7 @@ ext { android = [ compileSdkVersion : 27, buildToolsVersion : '28.0.2', - minSdkVersion : 17, + minSdkVersion : 22, targetSdkVersion : 27, ] diff --git a/data-provider/build.gradle b/data-provider/build.gradle index 18f42c15..9ea454a1 100644 --- a/data-provider/build.gradle +++ b/data-provider/build.gradle @@ -61,6 +61,7 @@ dependencies { implementation 'com.github.bilthon:graphenej:0.4.2' implementation 'com.fasterxml.jackson.core:jackson-databind:2.6.3' implementation 'com.belerweb:pinyin4j:2.5.0' + implementation 'io.enotes.sdk:eNotes:1.0.0' } greendao { diff --git a/data-provider/src/main/java/com/cybex/provider/graphene/chain/SignedTransaction.java b/data-provider/src/main/java/com/cybex/provider/graphene/chain/SignedTransaction.java index d04f757b..4b0e3afa 100644 --- a/data-provider/src/main/java/com/cybex/provider/graphene/chain/SignedTransaction.java +++ b/data-provider/src/main/java/com/cybex/provider/graphene/chain/SignedTransaction.java @@ -4,9 +4,23 @@ import com.cybex.provider.crypto.Sha256Object; +import org.ethereum.crypto.ECKey; +import org.ethereum.util.ByteUtil; + +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import io.enotes.sdk.constant.ErrorCode; +import io.enotes.sdk.core.CardManager; +import io.enotes.sdk.repository.card.CommandException; +import io.enotes.sdk.repository.card.Commands; +import io.enotes.sdk.repository.card.TLVBox; +import io.enotes.sdk.repository.db.entity.Card; +import mrd.bitlib.crypto.Signature; +import mrd.bitlib.crypto.SignedMessage; +import mrd.bitlib.util.Sha256Hash; + public class SignedTransaction extends Transaction { private final static char[] hexArray = "0123456789abcdef".toCharArray(); transient List SignaturesBuffer = new ArrayList<>(); @@ -18,6 +32,12 @@ public void sign(Types.private_key_type privateKeyType, Sha256Object chain_id) { signatures.add(bytesToHex(privateKeyType.getPrivateKey().sign_compact(digest, true).data)); } + public void signByENotes(CardManager cardManager, Card card, Types.private_key_type privateKeyType, Sha256Object chain_id) { + Sha256Object digest = sig_digest(chain_id); + SignaturesBuffer.add(sign_compactByENotes(cardManager, card, digest, true)); + signatures.add(bytesToHex(sign_compactByENotes(cardManager, card, digest, true).data)); + } + public String sign(Types.private_key_type privateKeyType) { Sha256Object digest = sig_digest(); SignaturesBuffer.add(privateKeyType.getPrivateKey().sign_compact(digest, true)); @@ -26,6 +46,63 @@ public String sign(Types.private_key_type privateKeyType) { return signatures.get(0); } + public CompactSignature sign_compactByENotes(CardManager cardManager, Card card, Sha256Object digest, boolean require_canonical) { + CompactSignature signature = null; + try { + + + while (true) { + SignedMessage signedMessage = signHashByENotes(cardManager, card, new Sha256Hash(digest.hash)); + byte[] byteCompact = signedMessage.bitcoinEncodingOfSignature(); + signature = new CompactSignature(byteCompact); + + boolean bResult = PublicKey.is_canonical(signature); + if (bResult == true) { + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return signature; + } + + public SignedMessage signHashByENotes(CardManager cardManager, Card card, Sha256Hash hashToSign) { + TLVBox tlvBox = new TLVBox(); + tlvBox.putBytesValue(Commands.TLVTag.Transaction_Hash, hashToSign.getBytes()); + + try { + byte[] bytes = ByteUtil.hexStringToBytes(cardManager.transmitApdu(Commands.signTX(tlvBox.serialize()))); + TLVBox signatureTLV = TLVBox.parse(bytes, 0, bytes.length); + String signature = signatureTLV.getStringValue(Commands.TLVTag.Transaction_signature); + if (signature.length() != 128) { + throw new CommandException(ErrorCode.INVALID_CARD, "please_right_card"); + } + String r = signature.substring(0, 64); + String s = signature.substring(64); + ECKey.ECDSASignature signature1 = new ECKey.ECDSASignature(new BigInteger(r, 16), new BigInteger(s, 16)).toCanonicalised(); + Signature sig = new Signature(signature1.r,signature1.s); + + // Now we have to work backwards to figure out the recId needed to recover the signature. + mrd.bitlib.crypto.PublicKey targetPubKey = new mrd.bitlib.crypto.PublicKey(card.getBitCoinECKey().getPubKeyPoint().getEncoded(true)); + boolean compressed = targetPubKey.isCompressed(); + int recId = -1; + for (int i = 0; i < 4; i++) { + + mrd.bitlib.crypto.PublicKey k = SignedMessage.recoverFromSignature(i, sig, hashToSign, compressed); + if (k != null && targetPubKey.equals(k)) { + recId = i; + break; + } + } + return SignedMessage.from(sig, targetPubKey, recId); + } catch (CommandException e) { + e.printStackTrace(); + } + return null; + } + private String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for ( int j = 0; j < bytes.length; j++ ) { diff --git a/data-provider/src/main/java/com/cybex/provider/websocket/BitsharesWalletWraper.java b/data-provider/src/main/java/com/cybex/provider/websocket/BitsharesWalletWraper.java index c0ed521e..abb2e33c 100644 --- a/data-provider/src/main/java/com/cybex/provider/websocket/BitsharesWalletWraper.java +++ b/data-provider/src/main/java/com/cybex/provider/websocket/BitsharesWalletWraper.java @@ -37,6 +37,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import io.enotes.sdk.core.CardManager; +import io.enotes.sdk.repository.db.entity.Card; import io.reactivex.Flowable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -442,6 +444,11 @@ public SignedTransaction getSignedTransaction(AccountObject accountObject, Opera return mWalletApi.getSignedTransaction(accountObject, operation, operationId, dynamicGlobalPropertyObject); } + public SignedTransaction getSignedTransactionByENotes(CardManager cardManager, Card card, AccountObject accountObject, Operations.base_operation operation, int operationId, DynamicGlobalPropertyObject dynamicGlobalPropertyObject) { + + return mWalletApi.getSignedTransactionByENotes(cardManager, card, accountObject, operation, operationId, dynamicGlobalPropertyObject); + } + public String getChatMessageSignature(AccountObject accountObject, String message){ return mWalletApi.getChatMessageSignature(accountObject, message); } diff --git a/data-provider/src/main/java/com/cybex/provider/websocket/WalletApi.java b/data-provider/src/main/java/com/cybex/provider/websocket/WalletApi.java index 01872f99..cf9ddedc 100644 --- a/data-provider/src/main/java/com/cybex/provider/websocket/WalletApi.java +++ b/data-provider/src/main/java/com/cybex/provider/websocket/WalletApi.java @@ -61,6 +61,9 @@ import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; +import io.enotes.sdk.core.CardManager; +import io.enotes.sdk.repository.db.entity.Card; + public class WalletApi { class wallet_object { @@ -829,6 +832,34 @@ public SignedTransaction getSignedTransaction(AccountObject accountObject, Opera return signedTransaction; } + public SignedTransaction getSignedTransactionByENotes(CardManager cardManager, Card card, AccountObject accountObject, Operations.base_operation operation, int operationId, DynamicGlobalPropertyObject dynamicGlobalPropertyObject) { + SignedTransaction signedTransaction = new SignedTransaction(); + Operations.operation_type operationType = new Operations.operation_type(); + operationType.nOperationType = operationId; + operationType.operationContent = operation; + signedTransaction.operationTypes = new ArrayList<>(); + signedTransaction.operationTypes.add(operationType); + signedTransaction.operations = new ArrayList<>(); + List listInOperations = new ArrayList<>(); + listInOperations.add(operationId); + listInOperations.add(operation); + signedTransaction.operations.add(listInOperations); + signedTransaction.extensions = new HashSet<>(); + + signedTransaction.set_reference_block(dynamicGlobalPropertyObject.head_block_id); + + Date dateObject = dynamicGlobalPropertyObject.time; + Calendar calender = Calendar.getInstance(); + calender.setTime(dateObject); + calender.add(Calendar.SECOND, 30); + dateObject = calender.getTime(); + + signedTransaction.set_expiration(dateObject); + Types.private_key_type privateKey = null; + signedTransaction.signByENotes(cardManager, card, privateKey, mWebSocketClient.getmChainIdObject()); + return signedTransaction; + } + public String getChatMessageSignature(AccountObject accountObject, String message) { Sha256Object.encoder encoder = new Sha256Object.encoder(); if(message != null){ diff --git a/module-base/build.gradle b/module-base/build.gradle index 5f9a876d..7c3f9c6d 100644 --- a/module-base/build.gradle +++ b/module-base/build.gradle @@ -57,4 +57,7 @@ dependencies { //rxpermissions api 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar' + //eNotes + implementation 'io.enotes.sdk:eNotes:1.0.0' + } diff --git a/module-base/src/main/java/com/cybex/basemodule/base/BaseActivity.java b/module-base/src/main/java/com/cybex/basemodule/base/BaseActivity.java index 54386238..c7bc093d 100644 --- a/module-base/src/main/java/com/cybex/basemodule/base/BaseActivity.java +++ b/module-base/src/main/java/com/cybex/basemodule/base/BaseActivity.java @@ -3,9 +3,12 @@ import android.Manifest; import android.annotation.TargetApi; import android.app.ActivityManager; +import android.app.Dialog; +import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.net.ConnectivityManager; @@ -22,6 +25,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.MenuItem; +import android.widget.Toast; import com.cybex.basemodule.R; @@ -33,6 +37,8 @@ import com.cybex.basemodule.injection.module.BaseActivityModule; import com.cybex.basemodule.receiver.NetWorkBroadcastReceiver; import com.cybex.basemodule.receiver.NetworkChangedCallback; +import com.cybex.provider.graphene.chain.PublicKey; +import com.cybex.provider.graphene.chain.Types; import com.cybex.provider.utils.NetworkUtils; import com.tbruyelle.rxpermissions2.Permission; import com.tbruyelle.rxpermissions2.RxPermissions; @@ -45,6 +51,13 @@ import java.util.List; import java.util.Locale; +import io.enotes.sdk.constant.ErrorCode; +import io.enotes.sdk.constant.Status; +import io.enotes.sdk.core.Callback; +import io.enotes.sdk.core.CardManager; +import io.enotes.sdk.repository.base.Resource; +import io.enotes.sdk.repository.db.entity.Card; +import io.enotes.sdk.utils.Utils; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -70,7 +83,8 @@ public abstract class BaseActivity extends AppCompatActivity { private NetWorkBroadcastReceiver mNetWorkBroadcastReceiver; private NetworkChangedCallback mNetworkChangedCallback; private BaseActivityComponent mBaseActivityComponent; - + public CardManager cardManager; + public Card currentCard; @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(updateResources(newBase)); @@ -94,6 +108,103 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { registerNetWorkReceiver(); onLazyLoad(); } + cardManager = new CardManager(this); + setCardReaderCallback(); + } + + protected Dialog dialog; + private int tagLostCount; + + private void setCardReaderCallback() { + if (cardManager != null) { + cardManager.setReadCardCallback(new Callback() { + @Override + public void onCallBack(Resource resource) { + if (resource.status == Status.SUCCESS) { + BaseActivity.this.readCardOnSuccess(resource.data); + } else if (resource.status == Status.NFC_CONNECTED) { + BaseActivity.this.nfcStartReadCard(); + } else if (resource.status == Status.ERROR) { + BaseActivity.this.readCardError(resource.errorCode, resource.message); + } else if (resource.status == Status.BLUETOOTH_PARSING) { + } + } + }); + } + } + public static Card cardApp; + protected void nfcStartReadCard() { + if (tagLostCount < 4) { + dialog = new ProgressDialog(this); + dialog.show(); + } + } + /** + * read card successful + * + * @param card + */ + protected void readCardOnSuccess(Card card) { + currentCard = card; + cardApp = card; + Types.public_key_type public_key_type = new Types.public_key_type(new PublicKey(card.getBitCoinECKey().getPubKeyPoint().getEncoded(true), true), true); + Log.i("eNotes", public_key_type.toString()); + tagLostCount = 0; + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + } + + protected void readCardError(int code, String message) { + if (code == ErrorCode.BLUETOOTH_DISCONNECT) { + showToast(this, getString(R.string.bluetooth_connect_fail)); + } else if (code == ErrorCode.INVALID_CARD) { + showToast(this, getString(R.string.invalid_card)); + } else if (code == ErrorCode.NOT_SUPPORT_CARD) { + showToast(this, getString(R.string.not_support_card)); + } else if (code == ErrorCode.NOT_FIND_CARD) { + showToast(this, getString(R.string.can_not_find_card)); + } else if (code == ErrorCode.NFC_DISCONNECTED) { + showToast(this, getString(R.string.tag_connection_lost)); + } else if (code == ErrorCode.CALL_CERT_PUB_KEY_ERROR) { + if (Utils.isNetworkConnected(this)) { + showToast(this, message); + } else { + showToast(this, getString(R.string.network_unavailable)); + } + } else { + showToast(this, message); + } + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + } + + protected void showToast(Context context, String text) { + Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); + + } + + protected boolean isLoginFromENotes() { + SharedPreferences sharedPreferences = getSharedPreferences("enotes", Context.MODE_PRIVATE); + return sharedPreferences.getBoolean("from", true); + } + + protected void setLoginFrom(boolean flag) { + SharedPreferences.Editor editor = getSharedPreferences("enotes", Context.MODE_PRIVATE).edit(); + editor.putBoolean("from", flag); + editor.commit(); + } + + protected String getLoginPublicKey() { + SharedPreferences sharedPreferences = getSharedPreferences("enotes", Context.MODE_PRIVATE); + return sharedPreferences.getString("key", ""); + } + + protected void setLoginPublicKey(String key) { + SharedPreferences.Editor editor = getSharedPreferences("enotes", Context.MODE_PRIVATE).edit(); + editor.putString("key", key); + editor.commit(); } @Override diff --git a/module-base/src/main/java/com/cybex/basemodule/dialog/CybexDialog.java b/module-base/src/main/java/com/cybex/basemodule/dialog/CybexDialog.java index bd44b38f..0fbe57ad 100644 --- a/module-base/src/main/java/com/cybex/basemodule/dialog/CybexDialog.java +++ b/module-base/src/main/java/com/cybex/basemodule/dialog/CybexDialog.java @@ -327,7 +327,7 @@ public void onClick(View v) { dialog.show(); } - public static void showUnlockWalletDialog(FragmentManager fragmentManager, + public static UnlockDialog showUnlockWalletDialog(FragmentManager fragmentManager, AccountObject accountObject, String username, UnlockDialog.UnLockDialogClickListener unLockListener, @@ -340,13 +340,14 @@ public static void showUnlockWalletDialog(FragmentManager fragmentManager, dialog.show(fragmentManager, UnlockDialog.class.getSimpleName()); dialog.setUnLockListener(unLockListener); dialog.setOnDismissListener(onDismissListener); + return dialog; } - public static void showUnlockWalletDialog(FragmentManager fragmentManager, + public static UnlockDialog showUnlockWalletDialog(FragmentManager fragmentManager, AccountObject accountObject, String username, UnlockDialog.UnLockDialogClickListener unLockListener){ - showUnlockWalletDialog(fragmentManager, accountObject, username, unLockListener, null); + return showUnlockWalletDialog(fragmentManager, accountObject, username, unLockListener, null); } public static void showAddAddressDialog(Context context, String message, String subMessage, diff --git a/module-base/src/main/res/values-en/strings.xml b/module-base/src/main/res/values-en/strings.xml new file mode 100644 index 00000000..c34fd3bd --- /dev/null +++ b/module-base/src/main/res/values-en/strings.xml @@ -0,0 +1,10 @@ + + + Bluetooth connection lost! + Invalid eNotes! + No support for the card. Please upgrade the client. + Can not find card + Scan failed, please do not move your eNotes when scanning. + The network connection is not available, please check the network setting. + + \ No newline at end of file diff --git a/module-base/src/main/res/values-zh/strings.xml b/module-base/src/main/res/values-zh/strings.xml new file mode 100644 index 00000000..3be60947 --- /dev/null +++ b/module-base/src/main/res/values-zh/strings.xml @@ -0,0 +1,10 @@ + + + 蓝牙连接已断开! + 无效的eNotes! + 当前软件版本不支持该eNotes,请升级到最新版本。 + 没有发现eNotes + 正在读取,请勿移动eNotes! + 网络连接不可用,请检查网络设置! + + \ No newline at end of file diff --git a/module-base/src/main/res/values/strings.xml b/module-base/src/main/res/values/strings.xml new file mode 100644 index 00000000..c34fd3bd --- /dev/null +++ b/module-base/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ + + + Bluetooth connection lost! + Invalid eNotes! + No support for the card. Please upgrade the client. + Can not find card + Scan failed, please do not move your eNotes when scanning. + The network connection is not available, please check the network setting. + + \ No newline at end of file From d8073555737d148b1e87bfd8a2a33f13802769b8 Mon Sep 17 00:00:00 2001 From: Victor Xu Date: Thu, 21 Feb 2019 15:07:04 +0800 Subject: [PATCH 2/2] modify build.gradle --- app/build.gradle | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c3a8a04c..8897dd60 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,22 +11,22 @@ android { def keystoreFile = '' def keystorePassword = '' -// Properties properties = new Properties() -// properties.load(project.rootProject.file('keystores/release.keystore.properties').newDataInputStream()) -// -// keystoreAlias = properties.getProperty("key.alias") -// keystoreAliasPassword = properties.getProperty("key.alias.password") -// keystoreFile = file(properties.getProperty("key.path")) -// keystorePassword = properties.getProperty("key.store.password") -// -// signingConfigs { -// release { -// keyAlias keystoreAlias -// keyPassword keystoreAliasPassword -// storeFile keystoreFile -// storePassword keystorePassword -// } -// } + Properties properties = new Properties() + properties.load(project.rootProject.file('keystores/release.keystore.properties').newDataInputStream()) + + keystoreAlias = properties.getProperty("key.alias") + keystoreAliasPassword = properties.getProperty("key.alias.password") + keystoreFile = file(properties.getProperty("key.path")) + keystorePassword = properties.getProperty("key.store.password") + + signingConfigs { + release { + keyAlias keystoreAlias + keyPassword keystoreAliasPassword + storeFile keystoreFile + storePassword keystorePassword + } + } flavorDimensions "default" productFlavors { @@ -67,7 +67,7 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" -// signingConfig signingConfigs.release + signingConfig signingConfigs.release } } lintOptions {