diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 675560c85ad..f4afa28767f 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -67,7 +67,8 @@ (def ^:const profile-pictures-visibility-everyone 2) (def ^:const profile-pictures-visibility-none 3) -(def ^:const min-password-length 6) +(def ^:const min-password-length 8) ; This is the min length for new passwords +(def ^:const min-sign-in-password-length 6) ; This is the min length for signing in (def ^:const max-group-chat-participants 20) (def ^:const default-number-of-messages 20) (def ^:const default-number-of-pin-messages 3) @@ -171,4 +172,4 @@ (def ^:const visibility-status-inactive 4) (def ^:const wallet-connect-version-1 1) -(def ^:const wallet-connect-version-2 2) \ No newline at end of file +(def ^:const wallet-connect-version-2 2) diff --git a/src/status_im/multiaccounts/db.cljs b/src/status_im/multiaccounts/db.cljs index 9cdf5b5626b..81119b361a6 100644 --- a/src/status_im/multiaccounts/db.cljs +++ b/src/status_im/multiaccounts/db.cljs @@ -3,6 +3,6 @@ [status-im.constants :as const])) (defn valid-length? [password] - (>= (count password) const/min-password-length)) + (>= (count password) const/min-sign-in-password-length)) (spec/def ::password (spec/and :global/not-empty-string valid-length?)) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 702fb4e46f9..ad389a51091 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -32,6 +32,7 @@ [status-im.utils.gfycat.core :as gfycat] [status-im.utils.money :as money] [status-im.utils.security :as security] + [status-im.utils.password-utils :as pass] [status-im.wallet.db :as wallet.db] [status-im.wallet.utils :as wallet.utils] status-im.ui.screens.keycard.subs @@ -2948,9 +2949,9 @@ (and (pos? (count current-password)) (pos? (count new-password)) (pos? (count confirm-new-password)) - (>= (count new-password) 6) + (pass/valid-password new-password) (>= (count current-password) 6) - (= new-password confirm-new-password))}))) + (pass/confirm-password new-password confirm-new-password))}))) (re-frame/reg-sub :bookmarks/active diff --git a/src/status_im/ui/screens/onboarding/password/views.cljs b/src/status_im/ui/screens/onboarding/password/views.cljs index 225b65ddda5..fa2e3afceb7 100644 --- a/src/status_im/ui/screens/onboarding/password/views.cljs +++ b/src/status_im/ui/screens/onboarding/password/views.cljs @@ -3,17 +3,11 @@ [reagent.core :as reagent] [status-im.ui.components.toolbar :as toolbar] [status-im.i18n.i18n :as i18n] - [status-im.constants :as const] [status-im.utils.security :as security] + [status-im.utils.password-utils :as pass] [quo.react-native :as rn] [quo.core :as quo])) -(defn validate-password [password] - (>= (count password) const/min-password-length)) - -(defn confirm-password [password confirm] - (= password confirm)) - (defn screen [] (let [password (reagent/atom nil) confirm (reagent/atom nil) @@ -21,8 +15,8 @@ show-error (reagent/atom nil) confirm-ref (atom nil)] (fn [] - (let [valid-password (validate-password @password) - valid-form (confirm-password @password @confirm) + (let [valid-password (pass/valid-password @password) + valid-form (pass/confirm-password @password @confirm) {:keys [recovering?]} @(re-frame/subscribe [:intro-wizard]) on-submit (fn [] (when (not @processing?) @@ -52,6 +46,11 @@ :placeholder (i18n/label :t/password-placeholder) :on-change-text #(reset! password (security/mask-data %)) :return-key-type :next + ; When the password is not valid, but it already meets the minimum length + ; Then show the error about not allowing weak passwords like aaaa and 12345 + :error (when (and (not valid-password) + (pass/meets-minimum-length? @password)) + (i18n/label :t/password_error2)) :on-submit-editing #(when valid-password (some-> ^js @confirm-ref .focus))}]] [rn/view {:style {:padding 16 @@ -74,7 +73,7 @@ (> (count @password) (count @confirm)) (reset! show-error false) - (not (confirm-password @password @confirm)) + (not (pass/confirm-password @password @confirm)) (reset! show-error true) :else (reset! show-error false)))}]]] @@ -104,4 +103,4 @@ @processing?) :type :secondary :after :main-icons/next} - (i18n/label :t/next)]}))]])))) \ No newline at end of file + (i18n/label :t/next)]}))]])))) diff --git a/src/status_im/utils/password_utils.cljs b/src/status_im/utils/password_utils.cljs new file mode 100644 index 00000000000..6b85fae8c5f --- /dev/null +++ b/src/status_im/utils/password_utils.cljs @@ -0,0 +1,51 @@ +(ns status-im.utils.password-utils + (:require [status-im.constants :as const] + [status-im.utils.security :as security])) + +(defn ord + "Convert a character to a unicode integer" + [val] + (.charCodeAt val)) + +(defn to-numbers + "Maps a string to a array of integers representing the string" + [vals] + (map ord vals)) + +(defn diff + "Compares all characters in a string to the character to their right. + If the character matches the next char, then the value becomes 1, if + the characters are different, the value becomes 0." + [vals] + (map - (next vals) vals)) + +(defn is-same? + "Returns true if both values are the same." + [a b] + (= a b)) + +(defn all-same? + "Returns true if all characters in the give string are the same." + [word] + (let [first-letter (first word)] + (every? #{first-letter} word))) + +(defn is-sequential? + "Returns true if the unicode value of all characters in the given string are sequential" + [sequence] + (all-same? (diff (to-numbers sequence)))) + +(defn meets-minimum-length? + "Returns true if the given string's length is greater than the defined minimum password length" + [password] + (>= (count password) const/min-password-length)) + +(defn valid-password + "Returns true if all password requirements are met." + [password] + (and (meets-minimum-length? password) + (not (all-same? (security/safe-unmask-data password))) + (not (is-sequential? (security/safe-unmask-data password))))) + +(defn confirm-password [password confirm] + (= password confirm)) diff --git a/translations/en.json b/translations/en.json index e606c1f3489..e961579a348 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1014,9 +1014,10 @@ "pairing-please-set-a-name": "Please set a name for your device.", "passphrase": "Passphrase", "password": "Password", - "password-description": "At least 6 characters. Your password protects your keys. You need it to unlock Status and transact.", + "password-description": "At least 8 characters. It may also include unicode characters and emojis. Your password protects your keys. You need it to unlock Status and transact.", "password-placeholder2": "Confirm your password", "password_error1": "Passwords don't match.", + "password_error2": "Password does not meet minimum requirements. At least 8 characters and not like 12345678 or aaaaaaaa", "paste": "Paste", "paste-json": "Paste JSON", "pay-to-chat": "Pay to chat",