diff --git a/resources/images/icons2/20x20/touch-id@2x.png b/resources/images/icons2/20x20/touch-id@2x.png new file mode 100644 index 0000000000..1202fd4ab4 Binary files /dev/null and b/resources/images/icons2/20x20/touch-id@2x.png differ diff --git a/resources/images/icons2/20x20/touch-id@3x.png b/resources/images/icons2/20x20/touch-id@3x.png new file mode 100644 index 0000000000..a72665f566 Binary files /dev/null and b/resources/images/icons2/20x20/touch-id@3x.png differ diff --git a/src/status_im/common/biometric/events.cljs b/src/status_im/common/biometric/events.cljs index feedcc843b..79848bb0c7 100644 --- a/src/status_im/common/biometric/events.cljs +++ b/src/status_im/common/biometric/events.cljs @@ -24,11 +24,17 @@ (defn get-label-by-type [biometric-type] - (case biometric-type + (condp = biometric-type :fingerprint (i18n/label :t/biometric-fingerprint) :FaceID (i18n/label :t/biometric-faceid) (i18n/label :t/biometric-touchid))) +(defn get-icon-by-type + [biometric-type] + (condp = biometric-type + :FaceID :i/face-id + :i/touch-id)) + (re-frame/reg-fx :biometric/get-supported-biometric-type (fn [] @@ -126,3 +132,26 @@ {:events [:biometric/authenticate]} [_ opts] {:biometric/authenticate opts}) + +(rf/reg-event-fx + :biometric/on-enable-success + (fn [{:keys [db]} [password]] + (let [key-uid (get-in db [:profile/profile :key-uid])] + {:db (assoc db :auth-method constants/auth-method-biometric) + :dispatch [:keychain/save-password-and-auth-method + {:key-uid key-uid + :masked-password password}]}))) + +(rf/reg-event-fx + :biometric/enable + (fn [_ [password]] + {:dispatch [:biometric/authenticate + {:on-success #(rf/dispatch [:biometric/on-enable-success password]) + :on-fail #(rf/dispatch [:biometric/show-message %])}]})) + +(rf/reg-event-fx + :biometric/disable + (fn [{:keys [db]}] + (let [key-uid (get-in db [:profile/profile :key-uid])] + {:db (assoc db :auth-method constants/auth-method-none) + :keychain/clear-user-password key-uid}))) diff --git a/src/status_im/common/keychain/events.cljs b/src/status_im/common/keychain/events.cljs index ae3fd41320..bd479aa5f7 100644 --- a/src/status_im/common/keychain/events.cljs +++ b/src/status_im/common/keychain/events.cljs @@ -131,6 +131,7 @@ :keychain/clear-user-password (fn [key-uid] (keychain/reset-credentials (password-migration-key-name key-uid)) + (keychain/reset-credentials (str key-uid "-auth")) (keychain/reset-credentials key-uid))) (re-frame/reg-fx @@ -142,6 +143,11 @@ (.then #(when on-success (on-success))) (.catch #(when on-error (on-error %)))))) +(re-frame/reg-event-fx + :keychain/save-password-and-auth-method + (fn [_ [opts]] + {:keychain/save-password-and-auth-method opts})) + ;; NOTE: migrating the plaintext password in the keychain ;; with the hashed one. Added due to the sync onboarding ;; flow, where the password arrives already hashed. diff --git a/src/status_im/common/standard_authentication/events.cljs b/src/status_im/common/standard_authentication/events.cljs index 96793574b3..e97a0e49f9 100644 --- a/src/status_im/common/standard_authentication/events.cljs +++ b/src/status_im/common/standard_authentication/events.cljs @@ -6,3 +6,8 @@ (fn [{:keys [db]} [callback]] (let [key-uid (get-in db [:profile/profile :key-uid])] {:fx [[:keychain/get-user-password [key-uid callback]]]}))) + +(rf/reg-event-fx + :standard-auth/reset-login-password + (fn [{:keys [db]}] + {:db (update db :profile/login dissoc :password :error)})) diff --git a/src/status_im/common/standard_authentication/standard_auth/authorize.cljs b/src/status_im/common/standard_authentication/standard_auth/authorize.cljs index 13e15e2a84..1784b34bbf 100644 --- a/src/status_im/common/standard_authentication/standard_auth/authorize.cljs +++ b/src/status_im/common/standard_authentication/standard_auth/authorize.cljs @@ -1,6 +1,5 @@ (ns status-im.common.standard-authentication.standard-auth.authorize (:require - [native-module.core :as native-module] [react-native.touch-id :as biometric] [status-im.common.standard-authentication.enter-password.view :as enter-password] [taoensso.timbre :as log] @@ -18,11 +17,11 @@ auth-button-label theme blur? auth-button-icon-left]}] (let [handle-auth-success (fn [biometric?] (fn [entered-password] - (let [sha3-pwd (if biometric? - (str (security/safe-unmask-data entered-password)) - (native-module/sha3 (str (security/safe-unmask-data - entered-password))))] - (on-auth-success sha3-pwd)))) + (let [sha3-masked-password (if biometric? + entered-password + (security/hash-masked-password + entered-password))] + (on-auth-success sha3-masked-password)))) password-login (fn [{:keys [on-press-biometrics]}] (rf/dispatch [:show-bottom-sheet {:on-close on-close @@ -53,10 +52,12 @@ (password-login {:on-press-biometrics #(on-press-biometrics on-press-biometrics)}))}))] - (biometric/get-supported-type - (fn [biometric-type] - (if (and biometric-auth? biometric-type) - (biometrics-login biometrics-login) - (do - (reset-password) - (password-login {}))))))) + (if biometric-auth? + (biometric/get-supported-type + (fn [biometric-type] + (if biometric-type + (biometrics-login biometrics-login) + (do + (reset-password) + (password-login {}))))) + (password-login {})))) diff --git a/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs b/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs index 1ff8abc138..1085aee23f 100644 --- a/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs +++ b/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs @@ -5,6 +5,7 @@ [react-native.core :as rn] [reagent.core :as reagent] [status-im.common.standard-authentication.standard-auth.authorize :as authorize] + [status-im.constants :as constants] [utils.re-frame :as rf])) (defn- view-internal @@ -15,7 +16,7 @@ #(reset! reset-slider? true) 200)) auth-method (rf/sub [:auth-method]) - biometric-auth? (= auth-method "biometric")] + biometric-auth? (= auth-method constants/auth-method-biometric)] (fn [{:keys [track-text customization-color auth-button-label diff --git a/src/status_im/contexts/profile/settings/list_items.cljs b/src/status_im/contexts/profile/settings/list_items.cljs index 4f6301db16..8c7008ccd0 100644 --- a/src/status_im/contexts/profile/settings/list_items.cljs +++ b/src/status_im/contexts/profile/settings/list_items.cljs @@ -11,7 +11,7 @@ :blur? true :action :arrow} {:title (i18n/label :t/password) - :on-press not-implemented/alert + :on-press #(rf/dispatch [:open-modal :settings-password]) :image-props :i/password :image :icon :blur? true diff --git a/src/status_im/contexts/profile/settings/screens/password/style.cljs b/src/status_im/contexts/profile/settings/screens/password/style.cljs new file mode 100644 index 0000000000..f66a212dad --- /dev/null +++ b/src/status_im/contexts/profile/settings/screens/password/style.cljs @@ -0,0 +1,10 @@ +(ns status-im.contexts.profile.settings.screens.password.style) + +(defn navigation + [top-inset] + {:padding-top top-inset}) + +(def header + {:padding-horizontal 20 + :padding-bottom 8 + :padding-top 12}) diff --git a/src/status_im/contexts/profile/settings/screens/password/view.cljs b/src/status_im/contexts/profile/settings/screens/password/view.cljs new file mode 100644 index 0000000000..5d9020f914 --- /dev/null +++ b/src/status_im/contexts/profile/settings/screens/password/view.cljs @@ -0,0 +1,86 @@ +(ns status-im.contexts.profile.settings.screens.password.view + (:require [quo.core :as quo] + [quo.theme :as quo.theme] + [react-native.core :as rn] + [react-native.safe-area :as safe-area] + [status-im.common.biometric.events :as biometric] + [status-im.common.not-implemented :as not-implemented] + [status-im.common.standard-authentication.standard-auth.authorize :as authorize] + [status-im.constants :as constants] + [status-im.contexts.profile.settings.screens.password.style :as style] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn- on-press-biometric-enable + [button-label theme] + (fn [] + (authorize/authorize + {:biometric-auth? false + :blur? true + :theme theme + :auth-button-label (i18n/label :t/biometric-enable-button {:bio-type-label button-label}) + :on-close (fn [] (rf/dispatch [:standard-auth/reset-login-password])) + :on-auth-success (fn [password] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch [:standard-auth/reset-login-password]) + (rf/dispatch [:biometric/enable password]))}))) + +(defn- get-biometric-item + [theme] + (let [auth-method (rf/sub [:auth-method]) + biometric-type (rf/sub [:biometric/supported-type]) + label (biometric/get-label-by-type biometric-type) + icon (biometric/get-icon-by-type biometric-type) + supported? (boolean biometric-type) + enabled? (= auth-method constants/auth-method-biometric) + biometric-on? (and supported? enabled?) + press-handler (if biometric-on? + (fn [] (rf/dispatch [:biometric/disable])) + (on-press-biometric-enable label theme))] + {:title label + :image-props icon + :image :icon + :blur? true + :action :selector + :action-props {:disabled? (not supported?) + :on-change press-handler + :checked? biometric-on?} + :on-press press-handler})) + +(defn- get-change-password-item + [] + {:title (i18n/label :t/change-password) + :on-press not-implemented/alert + :blur? true + :image :icon + :image-props :i/password + :action :arrow}) + +(defn- view-internal + [{:keys [theme]}] + (let [insets (safe-area/get-insets)] + [quo/overlay {:type :shell} + [rn/view + {:key :navigation + :style (style/navigation (:top insets))} + [quo/page-nav + {:background :blur + :icon-name :i/arrow-left + :on-press #(rf/dispatch [:navigate-back])}]] + [rn/view + {:key :header + :style style/header} + [quo/text + {:accessibility-label :password-settings-label + :weight :semi-bold + :number-of-lines 1 + :size :heading-1} + (i18n/label :t/password)]] + [quo/category + {:key :category + :data [(get-biometric-item theme) + (get-change-password-item)] + :blur? true + :list-type :settings}]])) + +(def view (quo.theme/with-theme view-internal)) diff --git a/src/status_im/contexts/syncing/events.cljs b/src/status_im/contexts/syncing/events.cljs index d4e31db62d..d4492b1742 100644 --- a/src/status_im/contexts/syncing/events.cljs +++ b/src/status_im/contexts/syncing/events.cljs @@ -11,6 +11,7 @@ [status-im.contexts.syncing.utils :as sync-utils] [taoensso.timbre :as log] [utils.re-frame :as rf] + [utils.security.core :as security] [utils.transforms :as transforms])) (rf/defn local-pairing-update-role @@ -106,7 +107,8 @@ config-map (.stringify js/JSON (clj->js {:senderConfig {:keyUID key-uid :keystorePath "" - :password sha3-pwd + :password (security/safe-unmask-data + sha3-pwd) :deviceType platform/os} :serverConfig {:timeout 0}}))] (native-module/get-connection-string-for-bootstrapping-another-device diff --git a/src/status_im/contexts/wallet/create_account/view.cljs b/src/status_im/contexts/wallet/create_account/view.cljs index 783b7deed0..67f0016468 100644 --- a/src/status_im/contexts/wallet/create_account/view.cljs +++ b/src/status_im/contexts/wallet/create_account/view.cljs @@ -15,6 +15,7 @@ [utils.i18n :as i18n] [utils.re-frame :as rf] [utils.responsiveness :refer [iphone-11-Pro-20-pixel-from-width]] + [utils.security.core :as security] [utils.string])) (defn keypair-string @@ -119,9 +120,8 @@ :track-text (i18n/label :t/slide-to-create-account) :customization-color @account-color :on-auth-success (fn [entered-password] - (prn entered-password) (rf/dispatch [:wallet/derive-address-and-add-account - {:sha3-pwd entered-password + {:sha3-pwd (security/safe-unmask-data entered-password) :emoji @emoji :color @account-color :path @derivation-path diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index 8ef658d759..0f4d12e0fd 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -37,6 +37,7 @@ [status-im.contexts.profile.edit.name.view :as edit-name] [status-im.contexts.profile.edit.view :as edit-profile] [status-im.contexts.profile.profiles.view :as profiles] + [status-im.contexts.profile.settings.screens.password.view :as settings-password] [status-im.contexts.profile.settings.view :as settings] [status-im.contexts.shell.activity-center.view :as activity-center] [status-im.contexts.shell.jump-to.view :as shell] @@ -367,7 +368,13 @@ :options (merge options/dark-screen {:modalPresentationStyle :overCurrentContext}) - :component scan-profile-qr-page/view}] + :component scan-profile-qr-page/view} + + ;; Settings + + {:name :settings-password + :options options/transparent-screen-options + :component settings-password/view}] (when js/goog.DEBUG [{:name :dev-component-preview diff --git a/translations/en.json b/translations/en.json index c86c84a434..2743018a7f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -139,7 +139,7 @@ "change-log-level": "Confirm and restart the app to change log level to {{log-level}}", "change-logging-enabled": "Are you sure you want to {{enable}} logging?", "change-passcode": "Change Passcode", - "change-password": "Change Password", + "change-password": "Change password", "change-pin": "Change 6-digit passcode", "change-puk": "Change 12-digit PUK", "change-pairing": "Change pairing code",