[#9354] Unlock keycard account without a card

To make it work `encryption-public-key` and `whisper-private-key` are
stored on the devices when a user chooses this option. The former key is
used for multiaccount's database encryption, the latter is needed for a
messaging. In case if a user wants to sign a transaction the card is
still needed, we don't store wallet's keys on the device.

Other things were fixed/added:
- A user can enable biometric auth for a regular account when chooses
  to save the password on the device (if biometric auth is available).
  This is done for feature parity between keycard and "on device"
  accounts.
- The option to create/restore an account on a keycard is not shown on
  the devices which do not support NFC. Currently, the app just crashes
  if the user continues a flow which is not supported by the device.
This commit is contained in:
Roman Volosovskyi 2019-11-28 11:57:58 +02:00
parent cd0486227d
commit 5bb6997d1d
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
22 changed files with 450 additions and 157 deletions

View File

@ -686,7 +686,7 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:hardwallet.callback/check-nfc-support-success :hardwallet.callback/check-nfc-support-success
(fn [cofx [_ supported?]] (fn [cofx [_ supported?]]
(hardwallet/set-nfc-support cofx supported?))) (hardwallet/set-nfc-supported cofx supported?)))
(handlers/register-handler-fx (handlers/register-handler-fx
:hardwallet.callback/on-card-connected :hardwallet.callback/on-card-connected
@ -1592,4 +1592,4 @@
(commands.sending/send public-key (commands.sending/send public-key
request-command request-command
{:asset (name symbol) {:asset (name symbol)
:amount (str (money/internal->formatted amount symbol decimals))}))))) :amount (str (money/internal->formatted amount symbol decimals))})))))

View File

@ -2,20 +2,16 @@
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.multiaccounts.create.core :as multiaccounts.create] [status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.multiaccounts.login.core :as multiaccounts.login]
[status-im.multiaccounts.logout.core :as multiaccounts.logout] [status-im.multiaccounts.logout.core :as multiaccounts.logout]
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.mnemonic :as mnemonic] [status-im.ethereum.mnemonic :as mnemonic]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.node.core :as node]
[status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.navigation :as navigation]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.datetime :as utils.datetime] [status-im.utils.datetime :as utils.datetime]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.wallet.core :as wallet]
[taoensso.timbre :as log] [taoensso.timbre :as log]
status-im.hardwallet.fx status-im.hardwallet.fx
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
@ -23,7 +19,9 @@
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.ui.components.bottom-sheet.core :as bottom-sheet] [status-im.ui.components.bottom-sheet.core :as bottom-sheet]
[status-im.multiaccounts.recover.core :as recover] [status-im.multiaccounts.recover.core :as recover]
[status-im.ethereum.eip55 :as eip55])) [status-im.ethereum.eip55 :as eip55]
[status-im.utils.keychain.core :as keychain]
[status-im.hardwallet.nfc :as nfc]))
(def default-pin "000000") (def default-pin "000000")
@ -59,6 +57,11 @@
(:keycard-pairing (:keycard-pairing
(find-multiaccount-by-key-uid db key-uid)))))) (find-multiaccount-by-key-uid db key-uid))))))
(re-frame/reg-fx
:hardwallet/set-nfc-supported
(fn [supported?]
(nfc/set-nfc-supported? supported?)))
(fx/defn listen-to-hardware-back-button (fx/defn listen-to-hardware-back-button
[{:keys [db]}] [{:keys [db]}]
(when-not (get-in db [:hardwallet :back-button-listener]) (when-not (get-in db [:hardwallet :back-button-listener])
@ -164,10 +167,10 @@
remove-instance-uid? (assoc :keycard-instance-uid nil)) remove-instance-uid? (assoc :keycard-instance-uid nil))
{})) {}))
(defn hardwallet-supported? [{:keys [db]}] (defn hardwallet-supported? []
(and config/hardwallet-enabled? (and config/hardwallet-enabled?
platform/android? platform/android?
(get-in db [:hardwallet :nfc-supported?]))) (nfc/nfc-supported?)))
(fx/defn unauthorized-operation (fx/defn unauthorized-operation
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
@ -628,7 +631,7 @@
pairing (get-in db [:multiaccounts/multiaccounts multiaccount-address :keycard-pairing]) pairing (get-in db [:multiaccounts/multiaccounts multiaccount-address :keycard-pairing])
pin (string/join (get-in db [:hardwallet :pin :login]))] pin (string/join (get-in db [:hardwallet :pin :login]))]
(when (and pairing (when (and pairing
(not (empty? pin))) (seq pin))
{:db (-> db {:db (-> db
(assoc-in [:hardwallet :pin :status] :verifying)) (assoc-in [:hardwallet :pin :status] :verifying))
:hardwallet/get-keys {:pairing pairing :hardwallet/get-keys {:pairing pairing
@ -723,9 +726,9 @@
(when on-card-read (when on-card-read
(dispatch-event on-card-read))))))) (dispatch-event on-card-read)))))))
(fx/defn set-nfc-support (fx/defn set-nfc-supported
[{:keys [db]} supported?] [_ supported?]
{:db (assoc-in db [:hardwallet :nfc-supported?] supported?)}) {:hardwallet/set-nfc-supported supported?})
(fx/defn keycard-option-pressed (fx/defn keycard-option-pressed
{:events [:onboarding.ui/keycard-option-pressed]} {:events [:onboarding.ui/keycard-option-pressed]}
@ -1774,14 +1777,15 @@
(let [account-data (js->clj data :keywordize-keys true)] (let [account-data (js->clj data :keywordize-keys true)]
(fx/merge cofx (fx/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:hardwallet :multiaccount] (-> account-data (assoc-in [:hardwallet :multiaccount]
(update :address #(str "0x" %)) (-> account-data
(update :whisper-address #(str "0x" %)) (update :address ethereum/normalized-hex)
(update :wallet-address #(str "0x" %)) (update :whisper-address ethereum/normalized-hex)
(update :public-key #(str "0x" %)) (update :wallet-address ethereum/normalized-hex)
(update :whisper-public-key #(str "0x" %)) (update :public-key ethereum/normalized-hex)
(update :wallet-public-key #(str "0x" %)) (update :whisper-public-key ethereum/normalized-hex)
(update :instance-uid #(get-in db [:hardwallet :multiaccount :instance-uid] %)))) (update :wallet-public-key ethereum/normalized-hex)
(update :instance-uid #(get-in db [:hardwallet :multiaccount :instance-uid] %))))
(assoc-in [:hardwallet :multiaccount-wallet-address] (:wallet-address account-data)) (assoc-in [:hardwallet :multiaccount-wallet-address] (:wallet-address account-data))
(assoc-in [:hardwallet :multiaccount-whisper-public-key] (:whisper-public-key account-data)) (assoc-in [:hardwallet :multiaccount-whisper-public-key] (:whisper-public-key account-data))
(assoc-in [:hardwallet :application-info :key-uid] (:key-uid account-data)) (assoc-in [:hardwallet :application-info :key-uid] (:key-uid account-data))
@ -1808,28 +1812,65 @@
(fx/defn on-get-keys-success (fx/defn on-get-keys-success
[{:keys [db] :as cofx} data] [{:keys [db] :as cofx} data]
(let [{:keys [address whisper-address encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true) (let [{:keys [address encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true)
address (str "0x" address) address (ethereum/normalized-hex address)
{:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address]) {:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address])
key-uid (get-in db [:hardwallet :application-info :key-uid]) key-uid (get-in db [:hardwallet :application-info :key-uid])
multiaccount-data (types/clj->json {:name name :address address :photo-path photo-path})] multiaccount-data (types/clj->json {:name name :address address :photo-path photo-path})
(fx/merge cofx save-keys? (get-in db [:multiaccounts/login :save-password?])]
{:db (-> db (fx/merge
(assoc-in [:hardwallet :pin :status] nil) cofx
(assoc-in [:hardwallet :pin :login] []) {:db
(assoc-in [:hardwallet :multiaccount] (update account-data :whisper-public-key #(str "0x" %))) (-> db
(assoc-in [:hardwallet :flow] nil) (assoc-in [:hardwallet :pin :status] nil)
(update :multiaccounts/login assoc (assoc-in [:hardwallet :pin :login] [])
:password encryption-public-key (assoc-in [:hardwallet :multiaccount]
:address address (update account-data :whisper-public-key ethereum/normalized-hex))
:photo-path photo-path (assoc-in [:hardwallet :flow] nil)
:name name)) (update :multiaccounts/login assoc
:hardwallet/get-application-info {:pairing (get-pairing db key-uid)} :password encryption-public-key
:hardwallet/login-with-keycard {:multiaccount-data multiaccount-data :address address
:password encryption-public-key :photo-path photo-path
:chat-key whisper-private-key}} :name name))
(clear-on-card-connected)
(clear-on-card-read)))) :hardwallet/get-application-info {:pairing (get-pairing db key-uid)}
:hardwallet/login-with-keycard {:multiaccount-data multiaccount-data
:password encryption-public-key
:chat-key whisper-private-key}}
(when save-keys?
(keychain/save-hardwallet-keys address encryption-public-key whisper-private-key))
(clear-on-card-connected)
(clear-on-card-read))))
(fx/defn on-hardwallet-keychain-keys
{:events [:multiaccounts.login.callback/get-hardwallet-keys-success]}
[{:keys [db] :as cofx} address [encryption-public-key whisper-private-key :as creds]]
(if (nil? creds)
(navigation/navigate-to-cofx cofx :keycard-login-pin nil)
(let [{:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address])
multiaccount-data (types/clj->json {:name name
:address address
:photo-path photo-path})
account-data {:address address
:encryption-public-key encryption-public-key
:whisper-private-key whisper-private-key}]
{:db
(-> db
(assoc-in [:hardwallet :pin :status] nil)
(assoc-in [:hardwallet :pin :login] [])
(assoc-in [:hardwallet :multiaccount]
(update account-data :whisper-public-key ethereum/normalized-hex))
(assoc-in [:hardwallet :flow] nil)
(update :multiaccounts/login assoc
:password encryption-public-key
:address address
:photo-path photo-path
:name name
:save-password? true))
:hardwallet/login-with-keycard
{:multiaccount-data multiaccount-data
:password encryption-public-key
:chat-key whisper-private-key}})))
(fx/defn on-get-keys-error (fx/defn on-get-keys-error
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]

View File

@ -0,0 +1,9 @@
(ns status-im.hardwallet.nfc)
(def is-nfc-supported? (atom nil))
(defn set-nfc-supported? [supported?]
(reset! is-nfc-supported? supported?))
(defn nfc-supported? []
@is-nfc-supported?)

View File

@ -11,7 +11,8 @@
[status-im.react-native.js-dependencies :as js-dependencies] [status-im.react-native.js-dependencies :as js-dependencies]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.keychain.core :as keychain])) [status-im.utils.keychain.core :as keychain]
[taoensso.timbre :as log]))
;; currently, for android, react-native-touch-id ;; currently, for android, react-native-touch-id
;; is not returning supported biometric type ;; is not returning supported biometric type
@ -81,6 +82,7 @@
(.catch #(callback nil)))) (.catch #(callback nil))))
(defn get-supported [callback] (defn get-supported [callback]
(log/debug "[biometric] get-supported")
(cond platform/ios? (do-get-supported callback) (cond platform/ios? (do-get-supported callback)
platform/android? (if android-device-blacklisted? platform/android? (if android-device-blacklisted?
(callback nil) (callback nil)
@ -91,6 +93,7 @@
([cb] ([cb]
(authenticate-fx cb nil)) (authenticate-fx cb nil))
([cb {:keys [reason ios-fallback-label]}] ([cb {:keys [reason ios-fallback-label]}]
(log/debug "[biometric] authenticate-fx")
(-> (.authenticate js-dependencies/touchid reason (authenticate-options ios-fallback-label)) (-> (.authenticate js-dependencies/touchid reason (authenticate-options ios-fallback-label))
(.then #(cb success-result)) (.then #(cb success-result))
(.catch #(cb (generate-error-result %)))))) (.catch #(cb (generate-error-result %))))))
@ -115,9 +118,14 @@
(authenticate-fx #(cb %) options))) (authenticate-fx #(cb %) options)))
(fx/defn update-biometric [{db :db :as cofx} biometric-auth?] (fx/defn update-biometric [{db :db :as cofx} biometric-auth?]
(let [address (get-in db [:multiaccount :address])] (let [address (or (get-in db [:multiaccount :address])
(get-in db [:multiaccounts/login :address]))]
(fx/merge cofx (fx/merge cofx
(keychain/save-auth-method address (if biometric-auth? "biometric" "none")) (keychain/save-auth-method
address
(if biometric-auth?
keychain/auth-method-biometric
keychain/auth-method-none))
#(when-not biometric-auth? #(when-not biometric-auth?
{:keychain/clear-user-password address})))) {:keychain/clear-user-password address}))))
@ -143,9 +151,10 @@
(fx/defn biometric-init-done (fx/defn biometric-init-done
{:events [:biometric-init-done]} {:events [:biometric-init-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}] [cofx {:keys [bioauth-success bioauth-message bioauth-code]}]
(if bioauth-success (if bioauth-success
(if (= "password" (get-in cofx [:db :auth-method])) (if (= keychain/auth-method-password
(get-in cofx [:db :auth-method]))
(update-biometric cofx true) (update-biometric cofx true)
(popover/show-popover cofx {:view :enable-biometric})) (popover/show-popover cofx {:view :enable-biometric}))
(show-message cofx bioauth-message bioauth-code))) (show-message cofx bioauth-message bioauth-code)))
@ -157,4 +166,33 @@
cofx cofx
#(re-frame/dispatch [:biometric-auth-done %]) #(re-frame/dispatch [:biometric-auth-done %])
{:reason (i18n/label :t/biometric-auth-reason-login) {:reason (i18n/label :t/biometric-auth-reason-login)
:ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)})) :ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)}))
(fx/defn enable
{:events [:biometric/enable]}
[cofx]
(fx/merge
cofx
(popover/hide-popover)
(authenticate #(re-frame/dispatch [:biometric/setup-done %]) {})))
(fx/defn disable
{:events [:biometric/disable]}
[{:keys [db] :as cofx}]
(fx/merge
cofx
{:db (-> db
(assoc :auth-method keychain/auth-method-none)
(assoc-in [:multiaccounts/login :save-password?] false))}
(popover/hide-popover)))
(fx/defn setup-done
{:events [:biometric/setup-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(log/debug "[biometric] setup-done"
"bioauth-success" bioauth-success
"bioauth-message" bioauth-message
"bioauth-code" bioauth-code)
(if bioauth-success
{:db (assoc db :auth-method keychain/auth-method-biometric-prepare)}
(show-message cofx bioauth-message bioauth-code)))

View File

@ -5,6 +5,7 @@
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.hardwallet.nfc :as nfc]
[status-im.multiaccounts.db :as db] [status-im.multiaccounts.db :as db]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.node.core :as node] [status-im.node.core :as node]
@ -30,14 +31,16 @@
(defn decrement-step [step] (defn decrement-step [step]
(let [inverted (map-invert step-kw-to-num)] (let [inverted (map-invert step-kw-to-num)]
(if (and (= step :create-code) (if (and (= step :create-code)
(not platform/android?)) (or (not platform/android?)
(not (nfc/nfc-supported?))))
:choose-key :choose-key
(inverted (dec (step-kw-to-num step)))))) (inverted (dec (step-kw-to-num step))))))
(defn inc-step [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) (if (and (= step :choose-key)
(not platform/android?)) (or (not platform/android?)
(not (nfc/nfc-supported?))))
:create-code :create-code
(inverted (inc (step-kw-to-num step)))))) (inverted (inc (step-kw-to-num step))))))
@ -138,7 +141,9 @@
(fx/defn intro-step-forward (fx/defn intro-step-forward
{:events [:intro-wizard/step-forward-pressed]} {:events [:intro-wizard/step-forward-pressed]}
[{:keys [db] :as cofx} {:keys [skip?] :as opts}] [{:keys [db] :as cofx} {:keys [skip?] :as opts}]
(let [{:keys [step selected-storage-type processing? weak-password?]} (:intro-wizard db)] (let [{:keys [step selected-storage-type processing? weak-password?]} (:intro-wizard db)]
(log/debug "[multiaccount.create] intro-step-forward"
"step" step)
(cond (confirm-failure? db) (cond (confirm-failure? db)
(on-confirm-failure cofx) (on-confirm-failure cofx)

View File

@ -31,7 +31,9 @@
[status-im.ui.screens.db :refer [app-db]] [status-im.ui.screens.db :refer [app-db]]
[status-im.multiaccounts.biometric.core :as biometric] [status-im.multiaccounts.biometric.core :as biometric]
[status-im.utils.identicon :as identicon] [status-im.utils.identicon :as identicon]
[status-im.ethereum.eip55 :as eip55])) [status-im.ethereum.eip55 :as eip55]
[status-im.popover.core :as popover]
[status-im.hardwallet.nfc :as nfc]))
(def rpc-endpoint "https://goerli.infura.io/v3/f315575765b14720b32382a61a89341a") (def rpc-endpoint "https://goerli.infura.io/v3/f315575765b14720b32382a61a89341a")
(def contract-address "0xfbf4c8e2B41fAfF8c616a0E49Fb4365a5355Ffaf") (def contract-address "0xfbf4c8e2B41fAfF8c616a0E49Fb4365a5355Ffaf")
@ -171,19 +173,24 @@
(when-not platform/desktop? (when-not platform/desktop?
(initialize-wallet))))) (initialize-wallet)))))
(defn get-new-auth-method [auth-method save-password?]
(if save-password?
(when-not (or (= keychain/auth-method-biometric auth-method)
(= keychain/auth-method-password auth-method))
(if (= auth-method keychain/auth-method-biometric-prepare)
keychain/auth-method-biometric
keychain/auth-method-password))
(when (and auth-method
(not= auth-method keychain/auth-method-none))
keychain/auth-method-none)))
(fx/defn login-only-events (fx/defn login-only-events
[{:keys [db] :as cofx} address password save-password?] [{:keys [db] :as cofx} address password save-password?]
(let [auth-method (:auth-method db) (let [auth-method (:auth-method db)
new-auth-method (if save-password? new-auth-method (get-new-auth-method auth-method save-password?)]
(when-not (or (= "biometric" auth-method) (log/debug "[login] login-only-events"
(= "password" auth-method)) "auth-method" auth-method
(if (= auth-method "biometric-prepare") "new-auth-method" new-auth-method)
"biometric"
"password"))
(when (and auth-method
(not= auth-method
"none"))
"none"))]
(fx/merge cofx (fx/merge cofx
{:db (assoc db :chats/loading? true) {:db (assoc db :chats/loading? true)
::json-rpc/call ::json-rpc/call
@ -202,8 +209,7 @@
:on-success #(re-frame/dispatch [::get-config-callback %])}]} :on-success #(re-frame/dispatch [::get-config-callback %])}]}
(when save-password? (when save-password?
(keychain/save-user-password address password)) (keychain/save-user-password address password))
(when new-auth-method (keychain/save-auth-method address (or new-auth-method auth-method))
(keychain/save-auth-method address new-auth-method))
(navigation/navigate-to-cofx :home nil) (navigation/navigate-to-cofx :home nil)
(when platform/desktop? (when platform/desktop?
(chat-model/update-dock-badge-label))))) (chat-model/update-dock-badge-label)))))
@ -291,55 +297,106 @@
(fx/defn open-login (fx/defn open-login
[{:keys [db] :as cofx} address photo-path name public-key] [{:keys [db] :as cofx} address photo-path name public-key]
(let [keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts address :keycard-pairing]))] (fx/merge cofx
(fx/merge cofx {:db (-> db
{:db (-> db (update :multiaccounts/login assoc
(update :multiaccounts/login assoc :public-key public-key
:public-key public-key :address address
:address address :photo-path photo-path
:photo-path photo-path :name name)
:name name) (assoc :profile/photo-added? (= (identicon/identicon public-key) photo-path))
(assoc :profile/photo-added? (= (identicon/identicon public-key) photo-path)) (update :multiaccounts/login dissoc
(update :multiaccounts/login dissoc :error
:error :password))}
:password))} (keychain/get-auth-method address)))
(if keycard-multiaccount?
(open-keycard-login)
(keychain/get-auth-method address)))))
(fx/defn open-login-callback (fx/defn open-login-callback
{:events [:multiaccounts.login.callback/get-user-password-success]} {:events [:multiaccounts.login.callback/get-user-password-success]}
[{:keys [db] :as cofx} password] [{:keys [db] :as cofx} password]
(if password (let [address (get-in db [:multiaccounts/login :address])
(fx/merge cofx keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts
{:db (update-in db [:multiaccounts/login] assoc :password password :save-password? true)} address
(navigation/navigate-to-cofx :progress nil) :keycard-pairing]))]
login) (if password
(navigation/navigate-to-cofx cofx :login nil))) (fx/merge
cofx
{:db (update-in db [:multiaccounts/login] assoc
:password password
:save-password? true)}
(navigation/navigate-to-cofx :progress nil)
login)
(navigation/navigate-to-cofx
cofx
(if keycard-account? :keycard-login-pin :login)
nil))))
(fx/defn get-credentials
[{:keys [db] :as cofx} address]
(let [keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts address :keycard-pairing]))]
(log/debug "[login] get-credentials"
"keycard-multiacc?" keycard-multiaccount?)
(if keycard-multiaccount?
(keychain/get-hardwallet-keys cofx address)
(keychain/get-user-password cofx address))))
(fx/defn get-auth-method-success (fx/defn get-auth-method-success
"Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\"" "Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\""
{:events [:multiaccounts.login/get-auth-method-success]} {:events [:multiaccounts.login/get-auth-method-success]}
[{:keys [db] :as cofx} auth-method] [{:keys [db] :as cofx} auth-method]
(let [address (get-in db [:multiaccounts/login :address])] (let [address (get-in db [:multiaccounts/login :address])
keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts address :keycard-pairing]))]
(log/debug "[login] get-auth-method-success"
"auth-method" auth-method
"keycard-multiacc?" keycard-multiaccount?)
(fx/merge cofx (fx/merge cofx
{:db (assoc db :auth-method auth-method)} {:db (assoc db :auth-method auth-method)}
#(case auth-method #(case auth-method
"biometric" keychain/auth-method-biometric
(biometric/biometric-auth %) (biometric/biometric-auth %)
"password" keychain/auth-method-password
(keychain/get-user-password % address) (get-credentials % address)
;;nil or "none" or "biometric-prepare" ;;nil or "none" or "biometric-prepare"
(open-login-callback % nil))))) (if keycard-multiaccount?
(open-keycard-login %)
(open-login-callback % nil))))))
(fx/defn biometric-auth-done (fx/defn biometric-auth-done
{:events [:biometric-auth-done]} {:events [:biometric-auth-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}] [{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(let [address (get-in db [:multiaccounts/login :address])] (let [address (get-in db [:multiaccounts/login :address])]
(log/debug "[biometric] biometric-auth-done"
"bioauth-success" bioauth-success
"bioauth-message" bioauth-message
"bioauth-code" bioauth-code)
(if bioauth-success (if bioauth-success
(keychain/get-user-password cofx address) (get-credentials cofx address)
(fx/merge cofx (fx/merge cofx
{:db (assoc-in db [:multiaccounts/login :save-password?] true)} {:db (assoc-in db [:multiaccounts/login :save-password?] true)}
(biometric/show-message bioauth-message bioauth-code) (biometric/show-message bioauth-message bioauth-code)
(open-login-callback nil))))) (open-login-callback nil)))))
(fx/defn save-password
{:events [:multiaccounts/save-password]}
[{:keys [db] :as cofx} save-password?]
(let [bioauth-supported? (boolean (get db :supported-biometric-auth))
previous-auth-method (get db :auth-method)]
(log/debug "[login] save-password"
"save-password?" save-password?
"bioauth-supported?" bioauth-supported?
"previous-auth-method" previous-auth-method)
(fx/merge
cofx
{:db (cond-> (assoc db :auth-method keychain/auth-method-none)
(or save-password?
(not bioauth-supported?)
(and (not save-password?)
bioauth-supported?
(= previous-auth-method keychain/auth-method-none)))
(assoc-in [:multiaccounts/login :save-password?] save-password?))}
(when bioauth-supported?
(if save-password?
(popover/show-popover {:view :secure-with-biometric})
(when-not (= previous-auth-method keychain/auth-method-none)
(popover/show-popover {:view :disable-password-saving})))))))

View File

@ -22,7 +22,7 @@
(fx/defn logout (fx/defn logout
{:events [:logout]} {:events [:logout]}
[cofx] [cofx]
(logout-method cofx "none")) (logout-method cofx keychain/auth-method-none))
(fx/defn show-logout-confirmation [_] (fx/defn show-logout-confirmation [_]
{:ui/show-confirmation {:ui/show-confirmation
@ -34,9 +34,9 @@
(fx/defn biometric-logout (fx/defn biometric-logout
{:events [:biometric-logout]} {:events [:biometric-logout]}
[{:keys [db] :as cofx}] [cofx]
(fx/merge cofx (fx/merge cofx
(logout-method "biometric-prepare") (logout-method keychain/auth-method-biometric-prepare)
(fn [{:keys [db]}] (fn [{:keys [db]}]
{:db (assoc-in db [:multiaccounts/login :save-password?] true)}))) {:db (assoc-in db [:multiaccounts/login :save-password?] true)})))

View File

@ -4,9 +4,9 @@
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.mnemonic :as mnemonic] [status-im.ethereum.mnemonic :as mnemonic]
[status-im.hardwallet.nfc :as nfc]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.multiaccounts.create.core :as multiaccounts.create] [status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.multiaccounts.db :as db]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.popover.core :as popover] [status-im.popover.core :as popover]
[status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.navigation :as navigation]
@ -14,8 +14,7 @@
[status-im.utils.security :as security] [status-im.utils.security :as security]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]))
[status-im.ethereum.eip55 :as eip55]))
(defn existing-account? (defn existing-account?
[root-key multiaccounts] [root-key multiaccounts]
@ -225,7 +224,8 @@
assoc :step :select-key-storage assoc :step :select-key-storage
:forward-action :multiaccounts.recover/select-storage-next-pressed :forward-action :multiaccounts.recover/select-storage-next-pressed
:selected-storage-type :default)} :selected-storage-type :default)}
(if platform/android? (if (and platform/android?
(nfc/nfc-supported?))
(navigation/navigate-to-cofx :recover-multiaccount-select-storage nil) (navigation/navigate-to-cofx :recover-multiaccount-select-storage nil)
(select-storage-next-pressed)))) (select-storage-next-pressed))))

View File

@ -165,8 +165,7 @@
(defn login-with-keycard (defn login-with-keycard
[{:keys [multiaccount-data password chat-key]}] [{:keys [multiaccount-data password chat-key]}]
(log/debug "[native-module] login-with-keycard" (log/debug "[native-module] login-with-keycard")
"password" password)
(clear-web-data) (clear-web-data)
(.loginWithKeycard (status) multiaccount-data password chat-key)) (.loginWithKeycard (status) multiaccount-data password chat-key))

View File

@ -14,6 +14,8 @@
(fx/defn status-node-started (fx/defn status-node-started
[{db :db :as cofx} {:keys [error]}] [{db :db :as cofx} {:keys [error]}]
(log/debug "[signals] status-node-started"
"error" error)
(if error (if error
{:db (-> db {:db (-> db
(update :multiaccounts/login dissoc :processing) (update :multiaccounts/login dissoc :processing)

View File

@ -8,17 +8,73 @@
[status-im.ui.components.icons.vector-icons :as icons] [status-im.ui.components.icons.vector-icons :as icons]
[status-im.i18n :as i18n])) [status-im.i18n :as i18n]))
(views/defview enable-biometric-popover [] (defn get-supported-biometric-auth []
(views/letsubs [supported-biometric-auth [:supported-biometric-auth]] @(re-frame/subscribe [:supported-biometric-auth]))
(let [bio-type-label (biometric/get-label supported-biometric-auth)]
[react/view {:padding 24 :align-items :center} (defn get-bio-type-label []
[react/view {:margin-bottom 16 :width 32 :height 32 :background-color colors/blue-light (biometric/get-label (get-supported-biometric-auth)))
:border-radius 16 :align-items :center :justify-content :center}
[icons/icon (if (= supported-biometric-auth :FaceID) :faceid :print)]] (defn biometric-popover
[react/text {:style {:typography :title-bold}} (str (i18n/label :t/enable) " " bio-type-label)] [{:keys [title-label description-label description-text
[react/text {:style {:margin-bottom 25 :margin-top 10 :text-align :center}} ok-button-label cancel-button-label on-cancel on-confirm]}]
(i18n/label :t/to-enable-biometric {:bio-type-label bio-type-label})] (let [supported-biometric-auth (get-supported-biometric-auth)
[button/button {:label (i18n/label :t/ok-save-pass) :style {:margin-bottom 16} bio-type-label (get-bio-type-label)]
:on-press #(re-frame/dispatch [:biometric-logout])}] [react/view {:padding 24
[button/button {:label :t/cancel :type :secondary :align-items :center}
:on-press #(re-frame/dispatch [:hide-popover])}]]))) [react/view {:margin-bottom 16
:width 32
:height 32
:background-color colors/blue-light
:border-radius 16
:align-items :center
:justify-content :center}
[icons/icon (if (= supported-biometric-auth :FaceID) :faceid :print)]]
[react/text {:style {:typography :title-bold}}
(str (i18n/label title-label {:bio-type-label bio-type-label}))]
(vec
(concat
[react/nested-text {:style {:margin-bottom 25
:margin-top 10
:text-align :center}}]
(if description-label
[(i18n/label description-label {:bio-type-label bio-type-label})]
description-text)))
[button/button {:label (i18n/label ok-button-label
{:bio-type-label bio-type-label})
:style {:margin-bottom 16}
:on-press #(re-frame/dispatch [on-confirm])}]
[button/button {:label (or cancel-button-label :t/cancel)
:type :secondary
:on-press #(re-frame/dispatch [(or on-cancel :hide-popover)])}]]))
(defn disable-password-saving-popover []
(let [bio-label-type (get-bio-type-label)]
[biometric-popover
{:title-label :t/biometric-disable-password-title
:ok-button-label :t/continue
:on-confirm :biometric/disable
:description-text
[[{:style {:color colors/gray}}
(i18n/label :t/biometric-disable-password-description)]
[{} (i18n/label :t/biometric-disable-bioauth
{:bio-type-label bio-label-type})]]}]))
(defn enable-biometric-popover []
[biometric-popover
{:title-label :t/enable
:description-label :t/to-enable-biometric
:ok-button-label :t/biometric-enable-button
:on-confirm :biometric-logout}])
(defn secure-with-biometric-popover []
(let [bio-label-type (get-bio-type-label)]
[biometric-popover
{:title-label :t/biometric-secure-with
:ok-button-label :t/biometric-enable-button
:on-confirm :biometric/enable
:description-text
[[{:style {:color colors/gray}} (i18n/label :t/biometric-enable)]
[{} (i18n/label :t/biometric-sign-in {:bio-type-label bio-label-type})]]}]))

View File

@ -54,8 +54,7 @@
:registry {} :registry {}
:stickers/packs-owned #{} :stickers/packs-owned #{}
:stickers/packs-pending #{} :stickers/packs-pending #{}
:hardwallet {:nfc-supported? false :hardwallet {:nfc-enabled? false
:nfc-enabled? false
:pin {:original [] :pin {:original []
:confirmation [] :confirmation []
:current [] :current []

View File

@ -7,8 +7,9 @@
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.screens.hardwallet.pin.styles :as styles] [status-im.ui.screens.hardwallet.pin.styles :as styles]
[status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.toolbar.actions :as actions] [status-im.ui.components.toolbar.actions :as toolbar.actions]
[status-im.ui.components.toolbar.actions :as toolbar.actions])) [status-im.ui.components.checkbox.view :as checkbox]
[status-im.utils.platform :as platform]))
(defn numpad-button [n step enabled? small-screen?] (defn numpad-button [n step enabled? small-screen?]
[react/touchable-highlight [react/touchable-highlight
@ -68,8 +69,21 @@
(repeat (- 12 (count puk)) (repeat (- 12 (count puk))
nil))))]) nil))))])
(defn pin-view [{:keys [pin title-label description-label step status error-label (defn save-password []
retry-counter small-screen?]}] (let [{:keys [save-password?]} @(re-frame/subscribe [:multiaccounts/login])
auth-method @(re-frame/subscribe [:auth-method])]
(when-not (and platform/android? (not auth-method))
[react/view
{:style {:flex-direction :row}}
[checkbox/checkbox
{:checked? save-password?
:style {:margin-right 10}
:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
[react/text (i18n/label :t/hardwallet-dont-ask-card)]])))
(defn pin-view
[{:keys [pin title-label description-label step status error-label
retry-counter small-screen? save-password-checkbox?]}]
(let [enabled? (not= status :verifying)] (let [enabled? (not= status :verifying)]
[react/scroll-view [react/scroll-view
[react/view styles/pin-container [react/view styles/pin-container
@ -81,8 +95,7 @@
[react/text {:style styles/create-pin-text [react/text {:style styles/create-pin-text
:number-of-lines 2} :number-of-lines 2}
(i18n/label description-label)]) (i18n/label description-label)])
[react/view {:margin-top 40 [react/view {:flex 1}
:height (if small-screen? 18 22)}
(case status (case status
:verifying [react/view styles/waiting-indicator-container :verifying [react/view styles/waiting-indicator-container
[react/activity-indicator {:animating true [react/activity-indicator {:animating true
@ -94,6 +107,8 @@
[react/view {:margin-top (if (= step :puk) 24 8)} [react/view {:margin-top (if (= step :puk) 24 8)}
[react/text {:style {:text-align :center}} [react/text {:style {:text-align :center}}
(i18n/label :t/pin-retries-left {:number retry-counter})]]))] (i18n/label :t/pin-retries-left {:number retry-counter})]]))]
(when save-password-checkbox?
[save-password])
(if (= step :puk) (if (= step :puk)
[puk-indicators pin status] [puk-indicators pin status]
[pin-indicators pin status nil]) [pin-indicators pin status nil])

View File

@ -4,4 +4,4 @@
(def container (def container
{:flex 1 {:flex 1
:justify-content :space-between :justify-content :space-between
:background-color colors/white}) :background-color colors/white})

View File

@ -2,6 +2,7 @@
(:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.multiaccounts.core :as multiaccounts] (:require [status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.components.checkbox.view :as checkbox]
[status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.toolbar.actions :as actions] [status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.screens.keycard.styles :as styles] [status-im.ui.screens.keycard.styles :as styles]
@ -370,8 +371,7 @@
retry-counter [:hardwallet/retry-counter]] retry-counter [:hardwallet/retry-counter]]
[react/view styles/container [react/view styles/container
[toolbar/toolbar [toolbar/toolbar
{:transparent? true {:transparent? true}
:style {:margin-top 32}}
(when multiple-multiaccounts? (when multiple-multiaccounts?
[toolbar/nav-button [toolbar/nav-button
(actions/back (actions/back
@ -385,12 +385,11 @@
[react/view {:flex 1 [react/view {:flex 1
:flex-direction :column :flex-direction :column
:justify-content :space-between :justify-content :space-between
:align-items :center :align-items :center}
:margin-top (if small-screen? 28 46)}
[react/view {:flex-direction :column [react/view {:flex-direction :column
:flex 1
:justify-content :center :justify-content :center
:align-items :center} :align-items :center
:height 140}
[react/view {:margin-horizontal 16 [react/view {:margin-horizontal 16
:flex-direction :column} :flex-direction :column}
[react/view {:justify-content :center [react/view {:justify-content :center
@ -431,12 +430,13 @@
:ellipsize-mode :middle} :ellipsize-mode :middle}
(utils.core/truncate-str address 14 true)]]] (utils.core/truncate-str address 14 true)]]]
[pin.views/pin-view [pin.views/pin-view
{:pin pin {:pin pin
:retry-counter retry-counter :retry-counter retry-counter
:small-screen? small-screen? :small-screen? small-screen?
:status status :status status
:error-label error-label :error-label error-label
:step enter-step}] :step enter-step
:save-password-checkbox? true}]
[react/view {:margin-bottom (if small-screen? 25 32)} [react/view {:margin-bottom (if small-screen? 25 32)}
[react/touchable-highlight [react/touchable-highlight
{:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])} {:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])}

View File

@ -92,7 +92,7 @@
:margin-top 19}} :margin-top 19}}
[checkbox/checkbox {:checked? save-password? [checkbox/checkbox {:checked? save-password?
:style {:margin-left 3 :margin-right 10} :style {:margin-left 3 :margin-right 10}
:on-value-change #(re-frame/dispatch [:set-in [:multiaccounts/login :save-password?] %])}] :on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
[react/text (i18n/label :t/save-password)]]))]] [react/text (i18n/label :t/save-password)]]))]]
(when processing (when processing
[react/view styles/processing-view [react/view styles/processing-view

View File

@ -1,23 +1,13 @@
(ns status-im.ui.screens.multiaccounts.recover.views (ns status-im.ui.screens.multiaccounts.recover.views
(:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.ui.components.text-input.view :as text-input]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im.hardwallet.core :as hardwallet] [status-im.hardwallet.core :as hardwallet]
[status-im.hardwallet.nfc :as nfc]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.ui.components.styles :as components.styles]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.ui.components.common.common :as components.common]
[status-im.utils.security :as security]
[status-im.ui.components.colors :as colors] [status-im.ui.components.colors :as colors]
[status-im.utils.gfycat.core :as gfy]
[status-im.utils.identicon :as identicon]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.screens.intro.views :as intro.views]
[status-im.utils.utils :as utils]
[status-im.constants :as constants]
[status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.list-item.views :as list-item]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.react-native.resources :as resources] [status-im.react-native.resources :as resources]
@ -68,7 +58,8 @@
:icon :main-icons/text :icon :main-icons/text
:on-press #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-pressed])}] :on-press #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-pressed])}]
(when (and config/hardwallet-enabled? (when (and config/hardwallet-enabled?
platform/android?) platform/android?
(nfc/nfc-supported?))
[list-item/list-item [list-item/list-item
{:theme :action {:theme :action
:title :t/recover-with-keycard :title :t/recover-with-keycard
@ -86,6 +77,9 @@
:style {:width 24 :height 24}}]] :style {:width 24 :height 24}}]]
:on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]]) :on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]])
(def bottom-sheet (defn bottom-sheet []
{:content bottom-sheet-view {:content bottom-sheet-view
:content-height (if platform/android? 130 65)}) :content-height (if (and platform/android?
(nfc/nfc-supported?))
130
65)})

View File

@ -106,6 +106,12 @@
(= :enable-biometric view) (= :enable-biometric view)
[biometric/enable-biometric-popover] [biometric/enable-biometric-popover]
(= :secure-with-biometric view)
[biometric/secure-with-biometric-popover]
(= :disable-password-saving view)
[biometric/disable-password-saving-popover]
:else :else
[view])]]]]])))}))) [view])]]]]])))})))

View File

@ -31,12 +31,12 @@
:title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth)) :title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth))
:container-margin-bottom 8 :container-margin-bottom 8
:accessibility-label :biometric-auth-settings-switch :accessibility-label :biometric-auth-settings-switch
:disabled? (or (not (some? supported-biometric-auth)) keycard?) :disabled? (not (some? supported-biometric-auth))
:accessories [[react/switch :accessories [[react/switch
{:track-color #js {:true colors/blue :false nil} {:track-color #js {:true colors/blue :false nil}
:value (boolean biometric-auth?) :value (boolean biometric-auth?)
:on-value-change #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched %]) :on-value-change #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched %])
:disabled (or (not (some? supported-biometric-auth)) keycard?)}]] :disabled (not supported-biometric-auth)}]]
:on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched :on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched
((complement boolean) biometric-auth?)])} ((complement boolean) biometric-auth?)])}
;; TODO - uncomment when implemented ;; TODO - uncomment when implemented

View File

@ -78,7 +78,7 @@
(merge home.sheet/group-chat-actions) (merge home.sheet/group-chat-actions)
(= view :recover-sheet) (= view :recover-sheet)
(merge recover.views/bottom-sheet)) (merge (recover.views/bottom-sheet)))
height-atom (reagent/atom (if (:content-height opts) (:content-height opts) nil))] height-atom (reagent/atom (if (:content-height opts) (:content-height opts) nil))]
[bottom-sheet-comp opts height-atom]))) [bottom-sheet-comp opts height-atom])))

View File

@ -77,7 +77,11 @@
#js {:authenticationType (enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")}) #js {:authenticationType (enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")})
(.then callback))) (.then callback)))
(defn- whisper-key-name [address]
(str address "-whisper"))
(defn can-save-user-password? [callback] (defn can-save-user-password? [callback]
(log/debug "[keychain] can-save-user-password?")
(cond (cond
platform/ios? platform/ios?
(check-conditions callback device-encrypted?) (check-conditions callback device-encrypted?)
@ -91,6 +95,7 @@
(defn save-credentials (defn save-credentials
"Stores the credentials for the address to the Keychain" "Stores the credentials for the address to the Keychain"
[server username password callback] [server username password callback]
(log/debug "[keychain] save-credentials")
(-> (.setInternetCredentials rn/keychain (string/lower-case server) username password (-> (.setInternetCredentials rn/keychain (string/lower-case server) username password
keychain-secure-hardware keychain-restricted-availability) keychain-secure-hardware keychain-restricted-availability)
(.then callback))) (.then callback)))
@ -98,18 +103,27 @@
(defn get-credentials (defn get-credentials
"Gets the credentials for a specified server from the Keychain" "Gets the credentials for a specified server from the Keychain"
[server callback] [server callback]
(log/debug "[keychain] get-credentials")
(if platform/mobile? (if platform/mobile?
(-> (.getInternetCredentials rn/keychain (string/lower-case server)) (-> (.getInternetCredentials rn/keychain (string/lower-case server))
(.then callback)) (.then callback))
(callback))) ;; no-op for Desktop (callback))) ;; no-op for Desktop
(def ^:const auth-method-password "password")
(def ^:const auth-method-biometric "biometric")
(def ^:const auth-method-biometric-prepare "biometric-prepare")
(def ^:const auth-method-none "none")
(re-frame/reg-fx (re-frame/reg-fx
:keychain/get-auth-method :keychain/get-auth-method
(fn [[address callback]] (fn [[address callback]]
(can-save-user-password? (can-save-user-password?
(fn [can-save?] (fn [can-save?]
(if can-save? (if can-save?
(get-credentials (str address "-auth") #(callback (if % (.-password %) "none"))) (get-credentials (str address "-auth")
#(callback (if %
(.-password %)
auth-method-none)))
(callback nil)))))) (callback nil))))))
(re-frame/reg-fx (re-frame/reg-fx
@ -117,6 +131,22 @@
(fn [[address callback]] (fn [[address callback]]
(get-credentials address #(if % (callback (security/mask-data (.-password %))) (callback nil))))) (get-credentials address #(if % (callback (security/mask-data (.-password %))) (callback nil)))))
(re-frame/reg-fx
:keychain/get-hardwallet-keys
(fn [[address callback]]
(get-credentials
address
(fn [encryption-key-data]
(if encryption-key-data
(get-credentials
(whisper-key-name address)
(fn [whisper-key-data]
(if whisper-key-data
(callback [(.-password encryption-key-data)
(.-password whisper-key-data)])
(callback nil))))
(callback nil))))))
(re-frame/reg-fx (re-frame/reg-fx
:keychain/save-user-password :keychain/save-user-password
(fn [[address password]] (fn [[address password]]
@ -134,6 +164,8 @@
(re-frame/reg-fx (re-frame/reg-fx
:keychain/save-auth-method :keychain/save-auth-method
(fn [[address method]] (fn [[address method]]
(log/debug "[keychain] :keychain/save-auth-method"
"method" method)
(save-credentials (save-credentials
(str address "-auth") (str address "-auth")
address address
@ -145,6 +177,24 @@
"The app will continue to work normally, " "The app will continue to work normally, "
"but you will have to login again next time you launch it.")))))) "but you will have to login again next time you launch it."))))))
(re-frame/reg-fx
:keychain/save-hardwallet-keys
(fn [[address encryption-public-key whisper-private-key]]
(save-credentials
address
address
encryption-public-key
#(when-not %
(log/error
(str "Error while saving encryption-public-key"))))
(save-credentials
(whisper-key-name address)
address
whisper-private-key
#(when-not %
(log/error
(str "Error while saving whisper-private-key"))))))
(re-frame/reg-fx (re-frame/reg-fx
:keychain/clear-user-password :keychain/clear-user-password
(fn [address] (fn [address]
@ -160,13 +210,27 @@
(fx/defn get-user-password (fx/defn get-user-password
[_ address] [_ address]
{:keychain/get-user-password {:keychain/get-user-password
[address #(re-frame/dispatch [:multiaccounts.login.callback/get-user-password-success % address])]}) [address
#(re-frame/dispatch
[:multiaccounts.login.callback/get-user-password-success % address])]})
(fx/defn get-hardwallet-keys
[_ address]
{:keychain/get-hardwallet-keys
[address
#(re-frame/dispatch
[:multiaccounts.login.callback/get-hardwallet-keys-success address %])]})
(fx/defn save-user-password (fx/defn save-user-password
[cofx address password] [_ address password]
{:keychain/save-user-password [address password]}) {:keychain/save-user-password [address password]})
(fx/defn save-hardwallet-keys
[_ address encryption-public-key whisper-private-key]
{:keychain/save-hardwallet-keys [address
encryption-public-key
whisper-private-key]})
(fx/defn save-auth-method (fx/defn save-auth-method
[{:keys [db]} address method] [{:keys [db]} address method]
{:db (assoc db :auth-method method) {:db (assoc db :auth-method method)
:keychain/save-auth-method [address method]}) :keychain/save-auth-method [address method]})

View File

@ -68,6 +68,13 @@
"biometric-auth-reason-login": "Login in Status", "biometric-auth-reason-login": "Login in Status",
"biometric-auth-reason-verify": "Verify authentication", "biometric-auth-reason-verify": "Verify authentication",
"biometric-auth-setting-label": "Use biometric authentication", "biometric-auth-setting-label": "Use biometric authentication",
"biometric-secure-with": "Secure with {{bio-type-label}}",
"biometric-sign-in": "{{bio-type-label}} sign in",
"biometric-enable": "If you don't want to use your Keycard each time to access the app, enable ",
"biometric-disable-bioauth": "disable {{bio-type-label}}",
"biometric-disable-password-title": "Disable password saving",
"biometric-disable-password-description": "If you disable this, you will also ",
"biometric-enable-button": "Enable {{bio-type-label}}",
"blank-keycard-text": "You can proceed with your keycard once you've generated your keys and name", "blank-keycard-text": "You can proceed with your keycard once you've generated your keys and name",
"blank-keycard-title": "Looks like youve tapped \na blank keycard", "blank-keycard-title": "Looks like youve tapped \na blank keycard",
"block": "Block", "block": "Block",
@ -497,6 +504,7 @@
"group-info": "Group info", "group-info": "Group info",
"gwei": "Gwei", "gwei": "Gwei",
"hash": "Hash", "hash": "Hash",
"hardwallet-dont-ask-card": "Don't ask for card to sign in",
"help": "help", "help": "help",
"help-capitalized": "Help", "help-capitalized": "Help",
"help-center": "Help Center", "help-center": "Help Center",