From ee5d53eba9517f54db3941f154f8566cec58e2c4 Mon Sep 17 00:00:00 2001 From: Roman Volosovskyi Date: Thu, 7 May 2020 13:57:10 +0300 Subject: [PATCH] [keycard] Account generation & tx signing on simulated card --- .../ios/RCTStatus/RCTStatus.m | 10 ++ src/status_im/hardwallet/card.cljs | 3 + src/status_im/hardwallet/core.cljs | 2 + src/status_im/hardwallet/fx.cljs | 3 +- src/status_im/hardwallet/ios_keycard.cljs | 3 +- src/status_im/hardwallet/keycard.cljs | 3 +- src/status_im/hardwallet/real_keycard.cljs | 4 + .../hardwallet/simulated_keycard.cljs | 126 +++++++++++++----- src/status_im/hardwallet/wallet.cljs | 15 ++- src/status_im/multiaccounts/create/core.cljs | 10 +- src/status_im/multiaccounts/recover/core.cljs | 6 +- src/status_im/ui/screens/intro/views.cljs | 9 +- .../screens/multiaccounts/recover/views.cljs | 3 +- .../ui/screens/profile/user/views.cljs | 3 +- src/status_im/wallet/accounts/core.cljs | 10 +- 15 files changed, 152 insertions(+), 58 deletions(-) diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index c17d4f74cf..39e4015ebb 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -276,6 +276,16 @@ RCT_EXPORT_METHOD(multiAccountImportPrivateKey:(NSString *)json callback(@[result]); } +//////////////////////////////////////////////////////////////////// hashTransaction +RCT_EXPORT_METHOD(hashTransaction:(NSString *)txArgsJSON + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"HashTransaction() method called"); +#endif + NSString *result = StatusgoHashTransaction(txArgsJSON); + callback(@[result]); +} + //////////////////////////////////////////////////////////////////// multiAccountImportMnemonic RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json callback:(RCTResponseSenderBlock)callback) { diff --git a/src/status_im/hardwallet/card.cljs b/src/status_im/hardwallet/card.cljs index 4cc0fdac7a..8b87a74989 100644 --- a/src/status_im/hardwallet/card.cljs +++ b/src/status_im/hardwallet/card.cljs @@ -412,3 +412,6 @@ (defn login [args] (keycard/login card args)) + +(defn send-transaction-with-signature [args] + (keycard/send-transaction-with-signature card args)) diff --git a/src/status_im/hardwallet/core.cljs b/src/status_im/hardwallet/core.cljs index 7dd1bf513d..16bbd2d283 100644 --- a/src/status_im/hardwallet/core.cljs +++ b/src/status_im/hardwallet/core.cljs @@ -1,6 +1,8 @@ (ns status-im.hardwallet.core (:require [status-im.hardwallet.change-pin :as change-pin] [status-im.hardwallet.common :as common] + status-im.hardwallet.delete-key + status-im.hardwallet.export-key [status-im.hardwallet.login :as login] [status-im.hardwallet.mnemonic :as mnemonic] [status-im.hardwallet.onboarding :as onboarding] diff --git a/src/status_im/hardwallet/fx.cljs b/src/status_im/hardwallet/fx.cljs index a90b484602..9b78e43804 100644 --- a/src/status_im/hardwallet/fx.cljs +++ b/src/status_im/hardwallet/fx.cljs @@ -100,8 +100,7 @@ (re-frame/reg-fx :send-transaction-with-signature - (fn [{:keys [transaction signature on-completed]}] - (status/send-transaction-with-signature transaction signature on-completed))) + card/send-transaction-with-signature) (re-frame/reg-fx :hardwallet/persist-pairings diff --git a/src/status_im/hardwallet/ios_keycard.cljs b/src/status_im/hardwallet/ios_keycard.cljs index 1374fddd54..a814061f7d 100644 --- a/src/status_im/hardwallet/ios_keycard.cljs +++ b/src/status_im/hardwallet/ios_keycard.cljs @@ -29,4 +29,5 @@ (get-keys [this args]) (sign [this args]) (save-multiaccount-and-login [this args]) - (login [this args])) + (login [this args]) + (send-transaction-with-signature [this args])) diff --git a/src/status_im/hardwallet/keycard.cljs b/src/status_im/hardwallet/keycard.cljs index 61e44cfb6d..02b6cac0cc 100644 --- a/src/status_im/hardwallet/keycard.cljs +++ b/src/status_im/hardwallet/keycard.cljs @@ -29,4 +29,5 @@ (sign [this args]) (sign-typed-data [this args]) (save-multiaccount-and-login [this args]) - (login [this args])) + (login [this args]) + (send-transaction-with-signature [this args])) diff --git a/src/status_im/hardwallet/real_keycard.cljs b/src/status_im/hardwallet/real_keycard.cljs index b18e7ab19d..1f44cd2ac6 100644 --- a/src/status_im/hardwallet/real_keycard.cljs +++ b/src/status_im/hardwallet/real_keycard.cljs @@ -211,6 +211,10 @@ (defn login [args] (status/login-with-keycard args)) +(defn send-transaction-with-signature + [{:keys [transaction signature on-completed]}] + (status/send-transaction-with-signature transaction signature on-completed)) + (defrecord RealKeycard [] keycard/Keycard (keycard/check-nfc-support [this args] diff --git a/src/status_im/hardwallet/simulated_keycard.cljs b/src/status_im/hardwallet/simulated_keycard.cljs index 90396e9338..8c100e8365 100644 --- a/src/status_im/hardwallet/simulated_keycard.cljs +++ b/src/status_im/hardwallet/simulated_keycard.cljs @@ -1,11 +1,15 @@ (ns status-im.hardwallet.simulated-keycard - (:require [re-frame.db :as re-frame.db] + (:require [re-frame.core :as re-frame] + [re-frame.db :as re-frame.db] [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.hardwallet.keycard :as keycard] [status-im.native-module.core :as status] [status-im.utils.types :as types] - [status-im.utils.utils :as utils])) + [status-im.utils.utils :as utils] + [status-im.i18n :as i18n] + [clojure.string :as string] + [taoensso.timbre :as log])) (def initial-state {:card-connected? false @@ -23,7 +27,7 @@ {:free-pairing-slots 5 :app-version "2.2" :secure-channel-pub-key "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e", - :instance-uid "9c3f27ee5dfc39c2b14f4d6d3379cd68" + :instance-uid "1b360b10a9a68b7d494e8f059059f118" :paired? true :has-master-key? true :initialized? true @@ -79,21 +83,23 @@ (defn install-applet [_]) (defn install-cash-applet [_]) -(def kk1-password "6d9ZHjn94kFP4bPm") +(def kk1-password "000000") (defn init-card [{:keys [pin on-success]}] (swap! state assoc :application-info {:free-pairing-slots 5 :app-version "2.2" - :secure-channel-pub-key "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e", :key-uid "", :instance-uid "9c3f27ee5dfc39c2b14f4d6d3379cd68" + :secure-channel-pub-key "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e", + :key-uid "", + :instance-uid "1b360b10a9a68b7d494e8f059059f118" :paired? false :has-master-key? false :initialized? true}) (swap! state assoc :pin pin) (later #(on-success {:password kk1-password - :puk "320612366918" - :pin pin}))) + :puk "000000000000" + :pin pin}))) (defn install-applet-and-init-card [_]) @@ -109,9 +115,23 @@ (= pairing kk1-pair)) (let [{:keys [id address public-key derived key-uid]} multiaccount - whisper (get derived constants/path-whisper-keyword) - wallet (get derived constants/path-default-wallet-keyword) - password (ethereum/sha3 pin)] + whisper (get derived constants/path-whisper-keyword) + wallet (get derived constants/path-default-wallet-keyword) + wallet-root (get derived constants/path-wallet-root-keyword) + password (ethereum/sha3 pin) + response {:key-uid key-uid + :encryption-public-key (ethereum/sha3 pin) + :address address + :whisper-public-key (:public-key whisper) + :instance-uid "1b360b10a9a68b7d494e8f059059f118" + :wallet-root-public-key (:public-key wallet-root) + :wallet-root-address (:address wallet-root) + :whisper-address (:address whisper) + :public-key public-key + :whisper-private-key "34bc7d0c258c4f2ac1dac4fd6c55c9478bac1f4a9d8b9f1152c8551ab7187b43" + :wallet-address (:address wallet) + :wallet-public-key (:public-key wallet)}] + (log/debug "[simulated kk] generate-and-load-key response" response) (status/multiaccount-store-derived id [constants/path-wallet-root @@ -119,19 +139,7 @@ constants/path-whisper constants/path-default-wallet] password - #(on-success - {:key-uid key-uid - :encryption-public-key (ethereum/sha3 pin) - :address address - :whisper-public-key (:public-key whisper) - :instance-uid "1b360b10a9a68b7d494e8f059059f118" - :wallet-root-public-key "0463187f5c917eef481e04af704c14e57a9e8596516f0ec10a4556561ad49b5aa249976ec545d37d04f4d4c7d1c0d9a2141dc61e458b09631d25fa7858c6323ea3" - :wallet-root-address "e034a084d2282e265f83e3fdfa48b42c3d53312a" - :whisper-address (:address whisper) - :public-key public-key - :whisper-private-key "34bc7d0c258c4f2ac1dac4fd6c55c9478bac1f4a9d8b9f1152c8551ab7187b43" - :wallet-address (:address wallet) - :wallet-public-key (:public-key wallet)}))))) + #(on-success response))))) (defn unblock-pin [_]) @@ -140,22 +148,70 @@ (= pin (get @state :pin))) (later #(on-success 3)))) -(defn change-pin [_]) -(defn unpair [_]) -(defn delete [_]) -(defn remove-key [_]) -(defn remove-key-with-unpair [_]) -(defn export-key [_]) +(defn change-pin [args] + (log/warn "change-pin not implemented" args)) +(defn unpair [args] + (log/warn "unpair not implemented" args)) +(defn delete [args] + (log/warn "delete not implemented" args)) +(defn remove-key [args] + (log/warn "remove-key not implemented" args)) +(defn remove-key-with-unpair [args] + (log/warn "remove-key-with-unpair not implemented" args)) + +(defn normalize-path [path] + (if (string/starts-with? path "m/") + (str constants/path-wallet-root + "/" (last (string/split path "/"))) + path)) + +(defn export-key [{:keys [pin on-success on-failure]}] + (let [wallet-root-address (get-in + @re-frame.db/app-db + [:multiaccount :wallet-root-address]) + accounts (get @re-frame.db/app-db :multiaccount/accounts) + hashed-password (ethereum/sha3 pin) + path-num (inc (get-in @re-frame.db/app-db [:multiaccount :latest-derived-path])) + path (str "m/" path-num)] + (status/multiaccount-load-account + wallet-root-address + hashed-password + (fn [value] + (let [{:keys [id error]} (types/json->clj value)] + (if error + (re-frame/dispatch [::new-account-error :password-error error]) + (status/multiaccount-derive-addresses + id + [path] + (fn [derived] + (let [derived-address (get-in (types/json->clj derived) [(keyword path) :address])] + (if (some #(= derived-address (get % :address)) accounts) + (re-frame/dispatch [::new-account-error :account-error (i18n/label :t/account-exists-title)]) + (status/multiaccount-store-derived + id + [path] + hashed-password + (fn [result] + (let [{:keys [error] :as result} (types/json->clj result) + {:keys [publicKey]} (get result (keyword path))] + (if error + (on-failure error) + (on-success publicKey))))))))))))))) + (defn unpair-and-delete [_]) (defn get-keys [{:keys [on-success pin]}] + (swap! state assoc :pin pin) ;;TODO(rasom): verify password before callback (later #(on-success {:key-uid (get-in @state [:application-info :key-uid]) :encryption-public-key (ethereum/sha3 pin)}))) -(defn sign [_]) -(defn sign-typed-data [_]) +(defn sign [{:keys [on-success]}] + (on-success "123")) + +(defn sign-typed-data [args] + (log/warn "sign-typed-data not implemented" args)) (defn save-multiaccount-and-login [{:keys [multiaccount-data password settings node-config accounts-data]}] @@ -169,6 +225,10 @@ (defn login [{:keys [multiaccount-data password]}] (status/login multiaccount-data password)) +(defn send-transaction-with-signature + [{:keys [transaction on-completed]}] + (status/send-transaction transaction (ethereum/sha3 (:pin @state)) on-completed)) + (defrecord SimulatedKeycard [] keycard/Keycard (keycard/check-nfc-support [this args] @@ -224,4 +284,6 @@ (keycard/save-multiaccount-and-login [this args] (save-multiaccount-and-login args)) (keycard/login [this args] - (login args))) + (login args)) + (keycard/send-transaction-with-signature [this args] + (send-transaction-with-signature args))) diff --git a/src/status_im/hardwallet/wallet.cljs b/src/status_im/hardwallet/wallet.cljs index 64a7131639..91bfa6931e 100644 --- a/src/status_im/hardwallet/wallet.cljs +++ b/src/status_im/hardwallet/wallet.cljs @@ -5,7 +5,8 @@ [status-im.hardwallet.common :as common] [status-im.constants :as constants] [status-im.ethereum.eip55 :as eip55] - [status-im.ui.components.bottom-sheet.core :as bottom-sheet])) + [status-im.ui.components.bottom-sheet.core :as bottom-sheet] + [status-im.utils.hex :as utils.hex])) (fx/defn show-pin-sheet {:events [:hardwallet/new-account-pin-sheet]} @@ -41,10 +42,14 @@ (assoc-in db [:hardwallet :on-export-success] #(vector :wallet.accounts/account-stored - {;; Strip leading 04 prefix denoting uncompressed key format - :address (eip55/address->checksum (str "0x" (ethereum/public-key->address (subs % 2)))) - :public-key (str "0x" %) - :path path})) + (let [public-key (utils.hex/normalize-hex %)] + {;; Strip leading 04 prefix denoting uncompressed key format + :address (eip55/address->checksum + (str "0x" + (ethereum/public-key->address + (subs public-key 2)))) + :public-key (str "0x" public-key) + :path path}))) :hardwallet/export-key {:pin pin :pairing pairing :path path}})) diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index 4cd29fbe1d..be9153fb81 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -13,6 +13,7 @@ [status-im.ui.components.bottom-sheet.core :as bottom-sheet] [status-im.ui.components.colors :as colors] [status-im.navigation :as navigation] + [status-im.utils.config :as config] [status-im.utils.fx :as fx] [status-im.utils.platform :as platform] [status-im.utils.security :as security] @@ -37,9 +38,10 @@ (inverted (dec (step-kw-to-num step)))))) (defn inc-step [step] - (let [inverted (map-invert step-kw-to-num)] + (let [inverted (map-invert step-kw-to-num)] (if (and (= step :choose-key) - (or (not platform/android?) + (or (not (or platform/android? + config/keycard-test-menu-enabled?)) (not (nfc/nfc-supported?)))) :create-code (inverted (inc (step-kw-to-num step)))))) @@ -320,7 +322,9 @@ (status/multiaccount-generate-and-derive-addresses 5 12 - [constants/path-whisper constants/path-default-wallet] + [constants/path-whisper + constants/path-wallet-root + constants/path-default-wallet] #(re-frame/dispatch [:intro-wizard/on-keys-generated (mapv normalize-multiaccount-data-keys (types/json->clj %))])))) diff --git a/src/status_im/multiaccounts/recover/core.cljs b/src/status_im/multiaccounts/recover/core.cljs index 753ba79927..5fd5018ccf 100644 --- a/src/status_im/multiaccounts/recover/core.cljs +++ b/src/status_im/multiaccounts/recover/core.cljs @@ -16,7 +16,8 @@ [status-im.utils.platform :as platform] [status-im.utils.utils :as utils] [status-im.ui.components.bottom-sheet.core :as bottom-sheet] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [status-im.utils.config :as config])) (defn existing-account? [multiaccounts key-uid] @@ -250,7 +251,8 @@ assoc :step :select-key-storage :forward-action :multiaccounts.recover/select-storage-next-pressed :selected-storage-type :default)} - (if (and platform/android? + (if (and (or platform/android? + config/keycard-test-menu-enabled?) (nfc/nfc-supported?)) (navigation/navigate-to-cofx :recover-multiaccount-select-storage nil) (select-storage-next-pressed)))) diff --git a/src/status_im/ui/screens/intro/views.cljs b/src/status_im/ui/screens/intro/views.cljs index 087bbbed10..016a311c2f 100644 --- a/src/status_im/ui/screens/intro/views.cljs +++ b/src/status_im/ui/screens/intro/views.cljs @@ -176,8 +176,13 @@ (i18n/label type)]] [react/touchable-highlight {:accessibility-label (keyword (str "select-storage-" type)) - :on-press #(re-frame/dispatch [:intro-wizard/on-key-storage-selected (if (and config/hardwallet-enabled? - platform/android?) type :default)])} + :on-press #(re-frame/dispatch + [:intro-wizard/on-key-storage-selected + (if (and config/hardwallet-enabled? + (or platform/android? + config/keycard-test-menu-enabled?)) + type + :default)])} [react/view (assoc (styles/list-item selected?) :align-items :flex-start :padding-top 16 diff --git a/src/status_im/ui/screens/multiaccounts/recover/views.cljs b/src/status_im/ui/screens/multiaccounts/recover/views.cljs index b1043b7df9..71cb5bc628 100644 --- a/src/status_im/ui/screens/multiaccounts/recover/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/recover/views.cljs @@ -50,7 +50,8 @@ :icon :main-icons/text :on-press #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-pressed])}] (when (and config/hardwallet-enabled? - platform/android? + (or platform/android? + config/keycard-test-menu-enabled?) (nfc/nfc-supported?)) [list-item/list-item {:theme :action diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index c902007236..74c7d5bc68 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -138,7 +138,8 @@ :accessibility-label :sync-settings-button :accessories [:chevron] :on-press #(re-frame/dispatch [:navigate-to :sync-settings])} - (when (and platform/android? + (when (and (or platform/android? + config/keycard-test-menu-enabled?) config/hardwallet-enabled? keycard-account?) {:icon :main-icons/keycard diff --git a/src/status_im/wallet/accounts/core.cljs b/src/status_im/wallet/accounts/core.cljs index 33b9ba82fd..5aec90e174 100644 --- a/src/status_im/wallet/accounts/core.cljs +++ b/src/status_im/wallet/accounts/core.cljs @@ -136,14 +136,8 @@ path-num (inc (get-in db [:multiaccount :latest-derived-path])) accounts (:multiaccount/accounts db)] {:db (assoc-in db [:add-account :step] :generating) - ::generate-account {:derivation-info (if wallet-root-address - ;; Use the walllet-root-address for stored on disk keys - ;; This needs to be the RELATIVE path to the key used to derive - {:path (str "m/" path-num) - :address wallet-root-address} - ;; Fallback on the master account for keycards, use the absolute path - {:path (str constants/path-wallet-root "/" path-num) - :address (get-in db [:multiaccount :address])}) + ::generate-account {:derivation-info {:path (str "m/" path-num) + :address wallet-root-address} :hashed-password hashed-password :accounts accounts}}))