[#7006] login with keycard

Signed-off-by: Dmitry Novotochinov <dmitry.novot@gmail.com>
This commit is contained in:
Dmitry Novotochinov 2018-12-04 16:49:09 +03:00
parent b96063ce56
commit 1b836bf7ec
No known key found for this signature in database
GPG Key ID: 43D1DAF5AD39C927
27 changed files with 534 additions and 152 deletions

View File

@ -1 +1 @@
0.19.0-beta.9
0.20.0-beta.0

View File

@ -54,7 +54,7 @@
"react-native-safe-area-view": "0.9.0",
"react-native-securerandom": "git+https://github.com/status-im/react-native-securerandom.git#0.1.1-2",
"react-native-splash-screen": "3.1.1",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.3.2",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.3.4",
"react-native-svg": "6.5.2",
"react-native-tcp": "git+https://github.com/status-im/react-native-tcp.git#v3.3.0-1-status",
"react-native-udp": "git+https://github.com/status-im/react-native-udp.git#2.3.1-1",

View File

@ -6012,9 +6012,10 @@ react-native-splash-screen@3.1.1:
resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.1.1.tgz#1a4e46c9fdce53ff52af2a2cb4181788c4e30b30"
integrity sha512-PU2YocOSGbLjL9Vgcq/cwMNuHHKNjjuPpa1IPMuWo+6EB/fSZ5VOmxSa7+eucQe3631s3NhGuk3eHKahU03a4Q==
"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.3.2":
version "2.3.2"
resolved "git+https://github.com/status-im/react-native-status-keycard#4c2aada5dc3b9d106935ab1747ab33ad4e94547e"
"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.3.4":
version "2.3.4"
resolved "git+https://github.com/status-im/react-native-status-keycard.git#04b0fb6fcbe588bd7fa076e1b0379e661bf1bddc"
react-native-svg@6.5.2:
version "6.5.2"

View File

@ -413,6 +413,26 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void loginWithKeycard(final String whisperPrivateKey, final String encryptionPublicKey, final Callback callback) {
Log.d(TAG, "loginWithKeycard");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.LoginWithKeycard(whisperPrivateKey, encryptionPublicKey);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void createAccount(final String password, final Callback callback) {
Log.d(TAG, "createAccount");

View File

@ -41,8 +41,13 @@
;;;; Handlers
(fx/defn login [cofx]
(let [{:keys [address password]} (accounts.db/credentials cofx)]
{:accounts.login/login [address password]}))
(if (get-in cofx [:db :hardwallet :whisper-private-key])
{:hardwallet/login-with-keycard (-> cofx
(get-in [:db :hardwallet])
(select-keys [:whisper-private-key :encryption-public-key])
(assoc :on-result #(re-frame/dispatch [:accounts.login.callback/login-success %])))}
(let [{:keys [address password]} (accounts.db/credentials cofx)]
{:accounts.login/login [address password]})))
(fx/defn initialize-wallet [cofx]
(fx/merge cofx
@ -88,7 +93,14 @@
(if success
(fx/merge
cofx
{:db (dissoc db :accounts/login)
{:db (-> db
(dissoc :accounts/login)
(update :hardwallet dissoc
:on-card-read
:card-read-in-progress?
:pin
:whisper-private-key
:encryption-public-key))
:web3/set-default-account [web3 address]
:web3/fetch-node-version [web3
#(re-frame/dispatch
@ -202,18 +214,35 @@
(unknown-realm-error {:realm-error realm-error
:erase-button erase-button})))))
(fx/defn open-login [{:keys [db]} address photo-path name]
{:db (-> db
(update :accounts/login assoc
:address address
:photo-path photo-path
:name name)
(update :accounts/login dissoc
:error
:password))
:keychain/can-save-user-password? nil
:keychain/get-user-password [address
#(re-frame/dispatch [:accounts.login.callback/get-user-password-success %])]})
(fx/defn open-keycard-login
[{:keys [db] :as cofx}]
(let [navigation-stack (:navigation-stack db)]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :enter-step] :login)}
(if (empty? navigation-stack)
(navigation/navigate-to-cofx :accounts nil)
(navigation/navigate-to-cofx :enter-pin nil)))))
(fx/defn get-user-password
[_ address]
{:keychain/can-save-user-password? nil
:keychain/get-user-password [address
#(re-frame/dispatch [:accounts.login.callback/get-user-password-success %])]})
(fx/defn open-login [{:keys [db] :as cofx} address photo-path name]
(let [keycard-account? (get-in db [:accounts/accounts address :keycard-instance-uid])]
(fx/merge cofx
{:db (-> db
(update :accounts/login assoc
:address address
:photo-path photo-path
:name name)
(update :accounts/login dissoc
:error
:password))}
(if keycard-account?
(open-keycard-login)
(get-user-password address)))))
(fx/defn open-login-callback
[{:keys [db] :as cofx} password]

View File

@ -891,8 +891,8 @@
(handlers/register-handler-fx
:hardwallet.callback/on-get-application-info-success
(fn [cofx [_ info]]
(hardwallet/on-get-application-info-success cofx info)))
(fn [cofx [_ info on-success]]
(hardwallet/on-get-application-info-success cofx info on-success)))
(handlers/register-handler-fx
:hardwallet.callback/on-get-application-info-error
@ -1012,6 +1012,26 @@
(fn [cofx [_ error]]
(hardwallet/on-delete-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-get-keys-success
(fn [cofx [_ data]]
(hardwallet/on-get-keys-success cofx data)))
(handlers/register-handler-fx
:hardwallet/auto-login
(fn [cofx _]
(hardwallet/login-with-keycard cofx true)))
(handlers/register-handler-fx
:hardwallet/login-with-keycard
(fn [cofx _]
(hardwallet/login-with-keycard cofx false)))
(handlers/register-handler-fx
:hardwallet.callback/on-get-keys-error
(fn [cofx [_ error]]
(hardwallet/on-get-keys-error cofx error)))
(handlers/register-handler-fx
:hardwallet.ui/status-hardwallet-option-pressed
(fn [cofx _]

View File

@ -50,10 +50,10 @@
"keyCardOnDisconnected"
#(re-frame/dispatch [:hardwallet.callback/on-card-disconnected %]))}])))
(defn get-application-info [pairing]
(defn get-application-info [{:keys [pairing on-success]}]
(.. keycard
(getApplicationInfo (str pairing))
(then #(re-frame/dispatch [:hardwallet.callback/on-get-application-info-success %]))
(then #(re-frame/dispatch [:hardwallet.callback/on-get-application-info-success % on-success]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-get-application-info-error (error-object->map %)]))))
(defn install-applet-and-init-card []
@ -133,3 +133,10 @@
(unpairAndDelete pairing pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-delete-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-delete-error (error-object->map %)])))))
(defn get-keys
[{:keys [pairing pin]}]
(.. keycard
(getKeys pairing pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-get-keys-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-get-keys-error (error-object->map %)]))))

View File

@ -11,7 +11,8 @@
[status-im.node.core :as node]
[status-im.utils.datetime :as utils.datetime]
[status-im.data-store.accounts :as accounts-store]
[clojure.string :as string]))
[clojure.string :as string]
[status-im.accounts.login.core :as accounts.login]))
(def default-pin "000000")
@ -81,31 +82,63 @@
[{:keys [db]}]
{:db (assoc-in db [:hardwallet :reset-card :disabled?] false)})
(defn enter-pin-screen-did-load
[{:keys [db]}]
{:db (-> db
(assoc-in [:hardwallet :pin :login] [])
(assoc-in [:hardwallet :pin :current] []))})
(defn hardwallet-connect-screen-did-load
[{:keys [db]}]
{:db (assoc-in db [:hardwallet :card-read-in-progress?] false)})
(defn accounts-screen-did-load
[{:keys [db]}]
{:db (assoc-in db [:hardwallet :setup-step] nil)})
(fx/defn on-register-card-events
[{:keys [db]} listeners]
{:db (update-in db [:hardwallet :listeners] merge listeners)})
(fx/defn clear-on-card-read
[{:keys [db]}]
{:db (assoc-in db [:hardwallet :on-card-read] nil)})
(fx/defn on-get-application-info-success
[{:keys [db] :as cofx} info]
[{:keys [db]} info on-success]
(let [info' (js->clj info :keywordize-keys true)
{:keys [pin-retry-counter puk-retry-counter]} info'
enter-step (get-in db [:hardwallet :pin :enter-step])
enter-step' (if (zero? pin-retry-counter) :puk enter-step)]
(fx/merge cofx
connect-screen? (= (:view-id db) :hardwallet-connect)
enter-step (if (zero? pin-retry-counter)
:puk
(get-in db [:hardwallet :pin :enter-step]))]
(fx/merge {}
{:db (-> db
(assoc-in [:hardwallet :pin :enter-step] enter-step')
(assoc-in [:hardwallet :pin :enter-step] enter-step)
(assoc-in [:hardwallet :application-info] info')
(assoc-in [:hardwallet :application-info :applet-installed?] true)
(assoc-in [:hardwallet :application-info-error] nil))}
(when (zero? puk-retry-counter)
(navigation/navigate-to-cofx :keycard-settings nil)))))
(when-not connect-screen?
(clear-on-card-read))
(if (zero? puk-retry-counter)
(navigation/navigate-to-cofx :keycard-settings nil)
(when on-success
#(assoc % :dispatch [on-success]))))))
(fx/defn on-get-application-info-error
[{:keys [db]} error]
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] application info error " error)
{:db (-> db
(assoc-in [:hardwallet :application-info-error] error)
(assoc-in [:hardwallet :application-info :applet-installed?] false))})
(let [on-card-read (get-in db [:hardwallet :on-card-read])
login? (= on-card-read :hardwallet/login-with-keycard)]
(if login?
(fx/merge cofx
{:db (assoc-in db [:hardwallet :on-card-read] nil)
:utils/show-popup {:title (i18n/label :t/wrong-card)
:content (i18n/label :t/wrong-card-text)}}
(navigation/navigate-to-cofx :accounts nil))
{:db (-> db
(assoc-in [:hardwallet :application-info-error] error)
(assoc-in [:hardwallet :application-info :applet-installed?] false))})))
(fx/defn set-nfc-support
[{:keys [db]} supported?]
@ -122,12 +155,12 @@
:db (-> db
(assoc-in [:hardwallet :setup-step] :begin)
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :on-card-read] nil)
(assoc-in [:hardwallet :pin :on-verified] nil))}
(navigation/navigate-to-cofx :hardwallet-connect nil)))
(fx/defn success-button-pressed [cofx]
;; login not implemented yet
)
(navigation/navigate-to-cofx cofx :home nil))
(fx/defn change-pin-pressed
[{:keys [db] :as cofx}]
@ -323,7 +356,7 @@
[{:keys [db] :as cofx}]
(let [pairing (get-pairing db)]
(fx/merge cofx
{:hardwallet/get-application-info pairing
{:hardwallet/get-application-info {:pairing pairing}
:db (-> db
(update-in [:hardwallet :pin] merge {:status nil
:enter-step :original
@ -336,13 +369,13 @@
[{:keys [db]} error]
(let [pairing (get-pairing db)]
(log/debug "[hardwallet] unblock pin error" error)
{:hardwallet/get-application-info pairing
{:hardwallet/get-application-info {:pairing pairing}
:db (update-in db [:hardwallet :pin] merge {:status :error
:error-label :t/puk-mismatch
:enter-step :puk
:puk []})}))
(fx/defn get-application-info [cofx pairing]
{:hardwallet/get-application-info pairing})
{:hardwallet/get-application-info {:pairing pairing}})
(fx/defn on-verify-pin-success
[{:keys [db] :as cofx}]
@ -361,7 +394,7 @@
[{:keys [db]} error]
(let [pairing (get-pairing db)]
(log/debug "[hardwallet] verify pin error" error)
{:hardwallet/get-application-info pairing
{:hardwallet/get-application-info {:pairing pairing}
:db (update-in db [:hardwallet :pin] merge {:status :error
:error-label :t/pin-mismatch
:enter-step :current
@ -449,11 +482,15 @@
(fx/defn update-pin
[{:keys [db] :as cofx} number enter-step]
(fx/merge cofx
{:db (-> db
(update-in [:hardwallet :pin enter-step] (fnil conj []) number)
(assoc-in [:hardwallet :pin :status] nil))}
(handle-pin-input enter-step)))
(let [numbers-entered (count (get-in db [:hardwallet :pin enter-step]))
need-update? (if (= enter-step :puk)
(< numbers-entered puk-code-length)
(< numbers-entered pin-code-length))]
(fx/merge cofx
{:db (cond-> (assoc-in db [:hardwallet :pin :status] nil)
need-update? (update-in [:hardwallet :pin enter-step] (fnil conj []) number))}
(when need-update?
(handle-pin-input enter-step)))))
(defn- pin-enter-error [fx error-label]
(update-in fx [:db :hardwallet :pin] merge {:status :error
@ -462,7 +499,26 @@
:original []
:confirmation []}))
(fx/defn get-keys-from-keycard
[{:keys [db]}]
(let [{:keys [pairing]} (get-in db [:hardwallet :secrets])
pin (string/join (get-in db [:hardwallet :pin :login]))]
(when (and pairing
(not (empty? pin)))
{:db (-> db
(assoc-in [:hardwallet :pin :status] :verifying))
:hardwallet/get-keys {:pairing pairing
:pin pin}})))
(fx/defn wait-for-card-tap
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :on-card-read] :hardwallet/login-with-keycard))}
(navigation/navigate-to-cofx :hardwallet-connect nil)))
; PIN enter steps:
; login - PIN is used to login
; current - current PIN to perform actions which require PIN auth
; original - new PIN when user changes it or creates new one
; confirmation - confirmation for new PIN
@ -473,6 +529,10 @@
numbers-entered (count pin)]
(cond-> {:db (assoc-in db [:hardwallet :pin :status] nil)}
(and (= enter-step :login)
(= 6 numbers-entered))
(wait-for-card-tap)
(and (= enter-step :original)
(= pin-code-length numbers-entered))
(proceed-to-pin-confirmation)
@ -517,24 +577,71 @@
{:hardwallet/generate-mnemonic {:pairing pairing}}))
(fx/defn dispatch-on-card-connected-event
[{:keys [db]} event]
{;:db (assoc-in db [:hardwallet :on-card-connected] nil)
:dispatch [event]})
[_ event]
{:dispatch [event]})
(defn login-with-keycard
[{:keys [db] :as cofx} auto-login?]
(let [pairing (get-pairing db)
account-login-address (get-in db [:accounts/login :address])
account-was-manually-selected? account-login-address
account-instance-uid (get-in db [:accounts/accounts account-login-address :keycard-instance-uid])
keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid])
account-mismatch? (if account-was-manually-selected?
(not= account-instance-uid keycard-instance-uid)
(->> (:accounts/accounts db)
vals
(filter #(= keycard-instance-uid (:keycard-instance-uid %)))
empty?))]
(cond
(empty? keycard-instance-uid)
(fx/merge cofx
{:utils/show-popup {:title (i18n/label :t/no-account-on-card)
:content (i18n/label :t/no-account-on-card-text)}}
(navigation/navigate-to-cofx :accounts nil))
account-mismatch?
(fx/merge cofx
{:db (dissoc db :accounts/login)
:utils/show-popup {:title (i18n/label (if auto-login? :t/account-not-listed :t/wrong-card))
:content (i18n/label (if auto-login? :t/account-not-listed-text :t/wrong-card-text))}}
(navigation/navigate-to-cofx :accounts nil))
(empty? pairing)
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/no-pairing-on-device)}}
auto-login?
(navigation/navigate-to-cofx cofx :enter-pin nil)
:else
(get-keys-from-keycard cofx))))
(fx/defn on-card-connected
[{:keys [db] :as cofx} data]
(log/debug "[hardwallet] card connected " data)
(let [return-to-step (get-in db [:hardwallet :return-to-step])
setup-running? (get-in db [:hardwallet :setup-step])
pin-enter-step (get-in db [:hardwallet :pin :enter-step])
login? (= :login pin-enter-step)
accounts-screen? (= :accounts (:view-id db))
auto-login? accounts-screen?
on-card-connected (get-in db [:hardwallet :on-card-connected])
on-card-read (if auto-login?
:hardwallet/auto-login
(get-in db [:hardwallet :on-card-read]))
pairing (get-pairing db)]
(fx/merge cofx
{:db (cond-> db
return-to-step (assoc-in [:hardwallet :setup-step] return-to-step)
true (assoc-in [:hardwallet :card-connected?] true)
true (assoc-in [:hardwallet :card-read-in-progress?] (boolean on-card-read))
true (assoc-in [:hardwallet :return-to-step] nil))
:hardwallet/get-application-info pairing}
(when on-card-connected
:hardwallet/get-application-info {:pairing pairing
:on-success on-card-read}}
(when (and on-card-connected
(not login?))
(dispatch-on-card-connected-event on-card-connected))
(when setup-running?
(navigation/navigate-to-cofx :hardwallet-setup nil)))))
@ -545,7 +652,9 @@
(let [setup-running? (get-in db [:hardwallet :setup-step])
on-card-connected (get-in db [:hardwallet :on-card-connected])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :card-connected?] false)}
{:db (-> db
(assoc-in [:hardwallet :card-connected?] false)
(assoc-in [:hardwallet :card-read-in-progress?] false))}
(when (or setup-running?
on-card-connected)
(navigation/navigate-to-cofx :hardwallet-connect nil)))))
@ -683,7 +792,7 @@
:keycard-paired-on paired-on}
encryption-public-key
{:seed-backed-up? true
:login? false})
:login? true})
(navigation/navigate-to-cofx :hardwallet-success nil))))
(fx/defn on-generate-and-load-key-success
@ -692,9 +801,9 @@
whisper-private-key
whisper-address
wallet-address
instance-uid
encryption-public-key]} (js->clj data :keywordize-keys true)
whisper-public-key' (str "0x" whisper-public-key)
keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid])]
whisper-public-key' (str "0x" whisper-public-key)]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :whisper-public-key] whisper-public-key')
@ -702,7 +811,9 @@
(assoc-in [:hardwallet :whisper-address] whisper-address)
(assoc-in [:hardwallet :wallet-address] wallet-address)
(assoc-in [:hardwallet :encryption-public-key] encryption-public-key)
(assoc-in [:hardwallet :keycard-instance-uid] keycard-instance-uid)
(assoc-in [:hardwallet :keycard-instance-uid] instance-uid)
(update :hardwallet dissoc :recovery-phrase)
(update-in [:hardwallet :secrets] dissoc :pin :puk :password)
(assoc :node/on-ready :create-keycard-account)
(assoc :accounts/new-installation-id (random-guid-generator))
(update-in [:hardwallet :secrets] dissoc :mnemonic))}
@ -716,3 +827,39 @@
(assoc-in [:hardwallet :return-to-step] :recovery-phrase)
(assoc-in [:hardwallet :setup-error] error))}
(process-error code)))
(fx/defn on-get-keys-success
[{:keys [db] :as cofx} data]
(let [{:keys [whisper-public-key
whisper-private-key
wallet-address
encryption-public-key]} (js->clj data :keywordize-keys true)
whisper-public-key' (str "0x" whisper-public-key)
{:keys [photo-path name]} (get-in db [:accounts/accounts wallet-address])
password encryption-public-key]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :status] nil)
(assoc-in [:hardwallet :whisper-public-key] whisper-public-key')
(assoc-in [:hardwallet :whisper-private-key] whisper-private-key)
(assoc-in [:hardwallet :wallet-address] wallet-address)
(assoc-in [:hardwallet :encryption-public-key] encryption-public-key)
(update :accounts/login assoc
:password password
:address wallet-address
:photo-path photo-path
:name name))}
(accounts.login/user-login true))))
(fx/defn on-get-keys-error
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] get keys error: " error)
(let [tag-was-lost? (= "Tag was lost." (:error error))]
(if tag-was-lost?
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/tag-was-lost)}}
(fx/merge cofx
{:hardwallet/get-application-info {:pairing (get-pairing db)}
:db (update-in db [:hardwallet :pin] merge {:status :error
:error-label :t/pin-mismatch})}
(navigation/navigate-to-cofx :enter-pin nil)))))

View File

@ -1,8 +1,9 @@
(ns status-im.hardwallet.fx
(:require [re-frame.core :as re-frame]
[status-im.hardwallet.card :as card]
[status-im.react-native.js-dependencies :as js-dependencies]
[status-im.utils.datetime :as utils.datetime]))
[status-im.utils.datetime :as utils.datetime]
[status-im.native-module.core :as statusgo]
[status-im.react-native.js-dependencies :as js-dependencies]))
(re-frame/reg-fx
:hardwallet/get-application-info
@ -68,6 +69,10 @@
:hardwallet/unpair-and-delete
card/unpair-and-delete)
(re-frame/reg-fx
:hardwallet/get-keys
card/get-keys)
;TODO remove when keycard login will be ready
(re-frame/reg-fx
:hardwallet/persist-pairing
@ -98,3 +103,7 @@
(.. js-dependencies/react-native
-AsyncStorage
(removeItem "status-keycard-pairing"))))
(re-frame/reg-fx
:hardwallet/login-with-keycard
statusgo/login-with-keycard)

View File

@ -28,6 +28,10 @@
(defn verify [address password callback]
(native-module/verify address password callback))
(defn login-with-keycard
[{:keys [whisper-private-key encryption-public-key on-result]}]
(native-module/login-with-keycard whisper-private-key encryption-public-key on-result))
(defn set-soft-input-mode [mode]
(native-module/set-soft-input-mode mode))

View File

@ -75,6 +75,10 @@
(when (and @node-started status)
(.verify status address password on-result)))
(defn login-with-keycard [whisper-private-key encryption-public-key on-result]
(when (and @node-started status)
(.loginWithKeycard status whisper-private-key encryption-public-key on-result)))
(defn set-soft-input-mode [mode]
(when status
(.setSoftInputMode status mode)))

View File

@ -37,7 +37,7 @@
(def account-badge-text-view
{:margin-left 16
:margin-right 21
:margin-right 31
:flex-shrink 1})
(def account-badge-text

View File

@ -10,6 +10,11 @@
(fn [db]
(:accounts/accounts db)))
(re-frame/reg-sub
:accounts/login
(fn [db]
(:accounts/login db)))
(re-frame/reg-sub
:account/account
(fn [db]

View File

@ -14,14 +14,18 @@
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.privacy-policy.views :as privacy-policy]))
(defn account-view [{:keys [address photo-path name public-key]}]
(defn account-view [{:keys [address photo-path name public-key keycard-instance-uid]}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:accounts.login.ui/account-selected address photo-path name])}
[react/view styles/account-view
[photos/photo photo-path {:size styles/account-image-size}]
[react/view styles/account-badge-text-view
[react/text {:style styles/account-badge-text
:numberOfLines 1}
name]
[react/view {:flex-direction :row}
[react/text {:style styles/account-badge-text
:numberOfLines 1}
name]
(when keycard-instance-uid
[icons/icon :icons/hardwallet {:color colors/blue
:container-style {:margin-left 7}}])]
[react/text {:style styles/account-badge-pub-key-text
:ellipsize-mode :middle
:numberOfLines 1}

View File

@ -176,4 +176,7 @@
#(case view-id
:keycard-settings (hardwallet/settings-screen-did-load %)
:reset-card (hardwallet/reset-card-screen-did-load %)
:enter-pin (hardwallet/enter-pin-screen-did-load %)
:hardwallet-connect (hardwallet/hardwallet-connect-screen-did-load %)
:accounts (hardwallet/accounts-screen-did-load %)
nil))))

View File

@ -4,4 +4,9 @@
(re-frame/reg-sub
:hardwallet/nfc-enabled?
(fn [db]
(get-in db [:hardwallet :nfc-enabled?])))
(get-in db [:hardwallet :nfc-enabled?])))
(re-frame/reg-sub
:hardwallet/card-read-in-progress?
(fn [db]
(get-in db [:hardwallet :card-read-in-progress?] false)))

View File

@ -11,15 +11,20 @@
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]))
(defn nfc-enabled []
[react/view styles/nfc-enabled-container
[react/view
[react/image {:source (:hold-card-animation resources/ui)
:style styles/phone-nfc-on-image}]]
[react/view styles/turn-nfc-text-container
[react/text {:style styles/status-hardwallet-text
:number-of-lines 2}
(i18n/label :t/hold-card)]]])
(defview nfc-enabled []
(letsubs [card-read-in-progress? [:hardwallet/card-read-in-progress?]]
[react/view styles/nfc-enabled-container
[react/view
[react/image {:source (:hold-card-animation resources/ui)
:style styles/phone-nfc-on-image}]]
[react/view styles/turn-nfc-text-container
[react/text {:style styles/status-hardwallet-text
:number-of-lines 2}
(i18n/label :t/hold-card)]]
(when card-read-in-progress?
[react/view {:margin-top 35}
[react/activity-indicator {:animating true
:size :large}]])]))
(defn nfc-disabled []
[react/view styles/nfc-disabled-container

View File

@ -0,0 +1,45 @@
(ns status-im.ui.screens.hardwallet.login.styles
(:require [status-im.ui.components.colors :as colors]))
(def container
{:flex 1
:background-color colors/white})
(def inner-container
{:flex-direction :column
:flex 1
:align-items :center
:justify-content :space-between})
(def login-view
{:flex 1
:margin-horizontal 16})
(def login-badge-container
{:margin-top 24})
(def processing-view
{:flex 1
:align-items :center
:justify-content :center})
(def sign-you-in
{:margin-top 16
:font-size 13
:color colors/black})
(def bottom-button-container
{:flex-direction :row
:margin-horizontal 12
:margin-vertical 15
:align-items :center})
(def login-badge
{:align-items :center})
(def login-badge-image-size 56)
(def login-badge-name
{:font-size 15
:color colors/black
:margin-top 8})

View File

@ -0,0 +1,48 @@
(ns status-im.ui.screens.hardwallet.login.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.screens.hardwallet.pin.views :as pin.views]
[status-im.ui.screens.hardwallet.connect.views :as connect.views]
[status-im.ui.screens.hardwallet.components :as components]
[status-im.ui.screens.hardwallet.login.styles :as styles]
[status-im.ui.screens.hardwallet.settings.views :as settings.views]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.screens.chat.photos :as photos]
[status-im.i18n :as i18n]
[status-im.ui.components.toolbar.actions :as toolbar.actions]
[status-im.ui.components.react :as react.components]
[re-frame.core :as re-frame]))
(defview hardwallet-login []
(letsubs [{:keys [photo-path name processing]} [:get :accounts/login]
nfc-enabled? [:hardwallet/nfc-enabled?]]
[react/keyboard-avoiding-view styles/container
[status-bar/status-bar]
[toolbar/toolbar
nil
[toolbar/nav-button
(toolbar.actions/back #(re-frame/dispatch [:navigate-to-clean :accounts]))]
[toolbar/content-title (i18n/label :t/sign-in-to-status)]]
[components.common/separator]
[react/view styles/login-view
[react/view styles/login-badge-container
[react/view styles/login-badge
[photos/photo photo-path {:size styles/login-badge-image-size}]
[react/view
[react/text {:style styles/login-badge-name
:numberOfLines 1}
name]]]
[react/view
(if nfc-enabled?
[connect.views/nfc-enabled]
[connect.views/nfc-disabled])]]]
(when processing
[react/view styles/processing-view
[react.components/activity-indicator {:animating true}]
[react/i18n-text {:style styles/sign-you-in :key :sign-you-in}]])
(when-not processing
[react/view {:style styles/bottom-button-container}
[react/view {:style {:flex 1}}]])]))

View File

@ -2,6 +2,10 @@
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.colors :as colors]))
(def container
{:flex 1
:background-color colors/white})
(defstyle pin-container
{:flex 1
:flex-direction :column

View File

@ -6,6 +6,11 @@
(fn [db]
(get-in db [:hardwallet :pin :original])))
(re-frame/reg-sub
:hardwallet/login-pin
(fn [db]
(get-in db [:hardwallet :pin :login])))
(re-frame/reg-sub
:hardwallet/pin-confirmation
(fn [db]

View File

@ -6,7 +6,10 @@
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.hardwallet.pin.styles :as styles]
[status-im.ui.screens.hardwallet.components :as components]))
[status-im.ui.screens.hardwallet.components :as components]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.actions :as actions]))
(defn numpad-button [n step enabled?]
[react/touchable-highlight
@ -98,22 +101,40 @@
[pin-indicators pin]))
[numpad step enabled?]]]]))
(defview main []
(letsubs [original [:hardwallet/original-pin]
confirmation [:hardwallet/pin-confirmation]
enter-step [:hardwallet/pin-enter-step]
(def pin-retries 3)
(def puk-retries 5)
(defview enter-pin []
(letsubs [pin [:hardwallet/pin]
step [:hardwallet/pin-enter-step]
status [:hardwallet/pin-status]
pin-retry-counter [:hardwallet/pin-retry-counter]
puk-retry-counter [:hardwallet/puk-retry-counter]
error-label [:hardwallet/pin-error-label]]
(case enter-step
:original [pin-view {:pin original
:title-label :t/create-pin
:description-label :t/create-pin-description
:step :original
:status status
:error-label error-label}]
:confirmation [pin-view {:pin confirmation
:title-label :t/repeat-pin
:description-label :t/create-pin-description
:step :confirmation
:status status
:error-label error-label}])))
[react/view {:flex 1
:background-color colors/white}
[status-bar/status-bar]
[toolbar/toolbar nil toolbar/default-nav-back nil]
(if (zero? pin-retry-counter)
[pin-view {:pin pin
:retry-counter (when (< puk-retry-counter puk-retries) puk-retry-counter)
:title-label :t/enter-puk-code
:description-label :t/enter-puk-code-description
:step step
:status status
:error-label error-label}]
[pin-view {:pin pin
:retry-counter (when (< pin-retry-counter pin-retries) pin-retry-counter)
:title-label (case step
:current :t/current-pin
:login :t/current-pin
:original :t/create-pin
:confirmation :t/repeat-pin
:t/current-pin)
:description-label (case step
:current :t/current-pin-description
:login :t/login-pin-description
:t/new-pin-description)
:step step
:status status
:error-label error-label}])]))

View File

@ -13,46 +13,6 @@
[status-im.ui.components.common.common :as components.common]
[reagent.core :as reagent]))
(def pin-retries 3)
(def puk-retries 5)
(defview enter-pin []
(letsubs [pin [:hardwallet/pin]
step [:hardwallet/pin-enter-step]
status [:hardwallet/pin-status]
pin-retry-counter [:hardwallet/pin-retry-counter]
puk-retry-counter [:hardwallet/puk-retry-counter]
error-label [:hardwallet/pin-error-label]]
[react/keyboard-avoiding-view {:flex 1}
[react/view {:flex 1
:background-color colors/white}
[react/view {:flex-direction :column
:flex 1
:align-items :center
:justify-content :space-between}
[components/maintain-card nil]
(if (zero? pin-retry-counter)
[pin.views/pin-view {:pin pin
:retry-counter (when (< puk-retry-counter puk-retries) puk-retry-counter)
:title-label :t/enter-puk-code
:description-label :t/enter-puk-code-description
:step step
:status status
:error-label error-label}]
[pin.views/pin-view {:pin pin
:retry-counter (when (< pin-retry-counter pin-retries) pin-retry-counter)
:title-label (case step
:current :t/current-pin
:original :t/create-pin
:confirmation :t/repeat-pin
:t/current-pin)
:description-label (case step
:current :t/current-pin-description
:t/new-pin-description)
:step step
:status status
:error-label error-label}])]]]))
(defn- action-row [{:keys [icon label on-press color-theme]}]
[react/touchable-highlight
{:on-press on-press}

View File

@ -378,7 +378,7 @@
:no-slots [no-slots]
:card-already-linked [card-already-linked]
:pairing [pairing]
:pin [pin.views/main]
:pin [pin.views/enter-pin]
:recovery-phrase [recovery-phrase]
:recovery-phrase-confirm-word1 [recovery-phrase-confirm-word step]
:recovery-phrase-confirm-word2 [recovery-phrase-confirm-word step]

View File

@ -53,7 +53,7 @@
[status-im.ui.screens.pairing.views :refer [installations]]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.views :refer [edit-bootnode]]
[status-im.ui.screens.currency-settings.views :refer [currency-settings]]
[status-im.ui.screens.hardwallet.settings.views :refer [keycard-settings enter-pin reset-card]]
[status-im.ui.screens.hardwallet.settings.views :refer [keycard-settings reset-card]]
[status-im.ui.screens.help-center.views :refer [help-center]]
[status-im.ui.screens.browser.views :refer [browser]]
[status-im.ui.screens.add-new.open-dapp.views :refer [open-dapp dapp-description]]
@ -61,6 +61,7 @@
[status-im.ui.screens.accounts.create.views :refer [create-account]]
[status-im.ui.screens.hardwallet.authentication-method.views :refer [hardwallet-authentication-method]]
[status-im.ui.screens.hardwallet.connect.views :refer [hardwallet-connect]]
[status-im.ui.screens.hardwallet.pin.views :refer [enter-pin]]
[status-im.ui.screens.hardwallet.setup.views :refer [hardwallet-setup]]
[status-im.ui.screens.hardwallet.success.views :refer [hardwallet-success]]
[status-im.ui.screens.profile.seed.views :refer [backup-seed]]
@ -144,10 +145,12 @@
config/hardwallet-enabled?
(assoc :hardwallet-authentication-method hardwallet-authentication-method
:hardwallet-connect hardwallet-connect
:enter-pin enter-pin
:hardwallet-setup hardwallet-setup
:hardwallet-success hardwallet-success)))
(cond-> {:headerMode "none"}
(#{:intro :login :progress} view-id)
; add view-id here if you'd like that view to be first view when app is started
(#{:intro :login :progress :accounts} view-id)
(assoc :initialRouteName (name view-id))))}
:chat-stack
{:screen
@ -155,23 +158,28 @@
(stack-screens
{:main-stack
{:screens
{:home (main-tabs/get-main-tab :home)
:chat chat
:profile profile.contact/profile
:new add-new
:new-chat new-chat
:qr-scanner qr-scanner
:new-group new-group
:add-participants-toggle-list add-participants-toggle-list
:contact-toggle-list contact-toggle-list
:group-chat-profile profile.group-chat/group-chat-profile
:new-public-chat new-public-chat
:open-dapp open-dapp
:dapp-description dapp-description
:browser browser
:stickers stickers/packs
:stickers-pack stickers/pack
:login login}
(cond->
{:home (main-tabs/get-main-tab :home)
:chat chat
:profile profile.contact/profile
:new add-new
:new-chat new-chat
:qr-scanner qr-scanner
:new-group new-group
:add-participants-toggle-list add-participants-toggle-list
:contact-toggle-list contact-toggle-list
:group-chat-profile profile.group-chat/group-chat-profile
:new-public-chat new-public-chat
:open-dapp open-dapp
:dapp-description dapp-description
:browser browser
:stickers stickers/packs
:stickers-pack stickers/pack
:login login}
config/hardwallet-enabled?
(assoc :hardwallet-connect hardwallet-connect
:enter-pin enter-pin))
:config
{:headerMode "none"
:initialRouteName "home"}}

View File

@ -233,3 +233,19 @@
(is (contains? efx :utils/show-popup)))
(testing "Logout."
(is (= [:accounts.logout.ui/logout-confirmed] (:dispatch efx)))))))
(deftest login
(testing "login with keycard"
(let [wpk "c56c7ac797c27b3790ce02c2459e9957c5d20d7a2c55320535526ce9e4dcbbef"
epk "04f43da85ff1c333f3e7277b9ac4df92c9120fbb251f1dede7d41286e8c055acfeb845f6d2654821afca25da119daff9043530b296ee0e28e202ba92ec5842d617"
db {:hardwallet {:encryption-public-key epk
:whisper-private-key wpk
:wallet-address "83278851e290d2488b6add2a257259f5741a3b7d"
:whisper-public-key "0x04491c1272149d7fa668afa45968c9914c0661641ace7dbcbc585c15070257840a0b4b1f71ce66c2147e281e1a44d6231b4731a26f6cc0a49e9616bbc7fc2f1a93"
:whisper-address "b8bec30855ff20c2ddab32282e2b2c8c8baca70d"}}
result (login.core/login {:db db})]
(is (= (-> result (get :hardwallet/login-with-keycard) keys count)
3))
(is (= (get-in result [:hardwallet/login-with-keycard :whisper-private-key wpk])))
(is (= (get-in result [:hardwallet/login-with-keycard :encryption-public-key epk])))
(is (fn? (get-in result [:hardwallet/login-with-keycard :on-result]))))))

View File

@ -838,13 +838,17 @@
"pin-unblocked-description": "Your new PIN {{pin}}",
"current-pin": "Enter PIN",
"current-pin-description": "Enter your PIN to proceed",
"login-pin-description": "Enter your PIN code to login\nto your account",
"new-pin-description": "Enter new PIN code",
"change-pin": "Change PIN",
"enter-pin": "Enter PIN",
"enter-pin-description": "Enter your PIN code to login\n to your account",
"create-pin": "Create a PIN",
"create-pin-description": "You'll need your card + this PIN to log in and to confirm transactions",
"repeat-pin": "Repeat new PIN",
"puk-mismatch": "PUK code does not match",
"pin-mismatch": "PIN does not match",
"tag-was-lost": "Tag was lost",
"cannot-use-default-pin": "PIN 000000 is not allowed.\nPlease use another number",
"pin-changed": "PIN has been changed to {{pin}}",
"pin-retries-left": "You have {{number}} retries left",
@ -862,6 +866,14 @@
"no-pairing-slots-available": "No pairing slots are available.\n You could unpair the device\n that already paired with the card",
"card-already-linked": "Card is already linked to another account",
"keycard-unauthorized-operation": "You're unauthorized to perform this operation.",
"no-account-on-card": "No account on this card",
"no-account-on-card-text": "The card you are presenting does not hold any account. Please try again with the right card",
"wrong-card": "Wrong card",
"wrong-card-text": "You have tapped a card that does not correspond to the account you selected. Please try again.",
"account-not-listed": "Account not listed",
"account-not-listed-text": "The account on this card is not listed on your phone. Would you like to login with this new account?",
"wrong-card-text": "You have tapped a card that does not correspond to the account you selected. Please try again.",
"no-pairing-on-device": "Card is not paired to this device",
"help": "help",
"help-capitalized": "Help",
"pairing-card": "Pairing card",