Add BIP39 validation for seed phrase recovery

Signed-off-by: yenda <eric@status.im>
This commit is contained in:
acolytec3 2019-12-31 05:50:45 -05:00 committed by yenda
parent b93e6d8d78
commit 3bcf7ecf29
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
14 changed files with 58 additions and 32 deletions

View File

@ -614,5 +614,6 @@ var TopLevel = {
"multiAccountReset" : function () {}, "multiAccountReset" : function () {},
"multiAccountLoadAccount" : function () {}, "multiAccountLoadAccount" : function () {},
"multiAccountStoreAccount" : function () {}, "multiAccountStoreAccount" : function () {},
"multiAccountImportMnemonic" : function () {} "multiAccountImportMnemonic" : function () {},
"validateMnemonic" : function () {}
} }

View File

@ -1253,4 +1253,26 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
public void isDeviceRooted(final Callback callback) { public void isDeviceRooted(final Callback callback) {
callback.invoke(rootedDevice); callback.invoke(rootedDevice);
} }
@ReactMethod
public void validateMnemonic(final String seed, final Callback callback) {
Log.d(TAG, "validateMnemonic");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String resValidateMnemonic = Statusgo.validateMnemonic(seed);
Log.d(TAG, resValidateMnemonic);
callback.invoke(resValidateMnemonic);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
} }

View File

@ -601,6 +601,15 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(identicon:(NSString *)publicKey) {
return StatusgoIdenticon(publicKey); return StatusgoIdenticon(publicKey);
} }
RCT_EXPORT_METHOD(validateMnemonic:(NSString *)seed
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"validateMnemonic() method called");
#endif
NSString *result = StatusgoValidateMnemonic(seed);
callback(@[result]);
}
RCT_EXPORT_METHOD(identiconAsync:(NSString *)publicKey RCT_EXPORT_METHOD(identiconAsync:(NSString *)publicKey
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
#if DEBUG #if DEBUG

View File

@ -6,7 +6,7 @@
(defn sanitize-passphrase [s] (defn sanitize-passphrase [s]
(-> (string/trim s) (-> (string/trim s)
(string/replace #"\s+" " "))) (string/replace #"[\s\n]+" " ")))
(defn passphrase->words [s] (defn passphrase->words [s]
(when s (when s

View File

@ -22,10 +22,13 @@
[root-key multiaccounts] [root-key multiaccounts]
(contains? multiaccounts (:key-uid root-key))) (contains? multiaccounts (:key-uid root-key)))
(re-frame/reg-fx
::validate-mnemonic
(fn [[passphrase callback]]
(status/validate-mnemonic passphrase callback)))
(defn check-phrase-warnings [recovery-phrase] (defn check-phrase-warnings [recovery-phrase]
(cond (string/blank? recovery-phrase) :required-field (cond (string/blank? recovery-phrase) :required-field))
(not (mnemonic/valid-words? recovery-phrase)) :recovery-phrase-invalid
(not (mnemonic/status-generated-phrase? recovery-phrase)) :recovery-phrase-unknown-words))
(fx/defn set-phrase (fx/defn set-phrase
{:events [:multiaccounts.recover/passphrase-input-changed]} {:events [:multiaccounts.recover/passphrase-input-changed]}
@ -160,15 +163,21 @@
(navigation/navigate-to-cofx :recover-multiaccount-enter-phrase nil))) (navigation/navigate-to-cofx :recover-multiaccount-enter-phrase nil)))
(fx/defn proceed-to-import-mnemonic (fx/defn proceed-to-import-mnemonic
{:events [:multiaccounts.recover/enter-phrase-next-pressed]} {:events [:multiaccounts.recover/phrase-validated]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx} phrase-warnings]
(let [{:keys [password passphrase]} (:intro-wizard db)] (let [{:keys [password passphrase]} (:intro-wizard db)]
(if (check-phrase-warnings passphrase) (if-not (string/blank? (:error (types/json->clj phrase-warnings)))
(popover/show-popover cofx {:view :custom-seed-phrase}) (popover/show-popover cofx {:view :custom-seed-phrase})
(when (mnemonic/valid-length? passphrase) (when (mnemonic/valid-length? passphrase)
{::import-multiaccount {:passphrase passphrase {::import-multiaccount {:passphrase (mnemonic/sanitize-passphrase passphrase)
:password password}})))) :password password}}))))
(fx/defn seed-phrase-next-pressed
{:events [:multiaccounts.recover/enter-phrase-next-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [passphrase]} (:intro-wizard db)]
{::validate-mnemonic [passphrase #(re-frame/dispatch [:multiaccounts.recover/phrase-validated %])]}))
(fx/defn continue-to-import-mnemonic (fx/defn continue-to-import-mnemonic
{:events [::continue-pressed]} {:events [::continue-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]

View File

@ -338,3 +338,9 @@
[seed callback] [seed callback]
(log/debug "[native-module] gfycat-identicon-async") (log/debug "[native-module] gfycat-identicon-async")
(.generateAliasAndIdenticonAsync (status) seed callback)) (.generateAliasAndIdenticonAsync (status) seed callback))
(defn validate-mnemonic
"Validate that a mnemonic conforms to BIP39 dictionary/checksum standards"
[mnemonic callback]
(log/debug "[native-module] validate-mnemonic")
(.validateMnemonic (status) mnemonic callback))

View File

@ -34,15 +34,9 @@
:line-height 22}} :line-height 22}}
(i18n/label :t/custom-seed-phrase-text-1) (i18n/label :t/custom-seed-phrase-text-1)
[{:style {:color colors/black}} [{:style {:color colors/black}}
(i18n/label :t/custom-seed-phrase-text-2)] (i18n/label :t/custom-seed-phrase-text-2)]]]
(i18n/label :t/custom-seed-phrase-text-3)
[{:style {:color colors/black}}
(i18n/label :t/custom-seed-phrase-text-4)]]]
[react/view {:margin-vertical 24 [react/view {:margin-vertical 24
:align-items :center} :align-items :center}
[button/button {:on-press #(re-frame/dispatch [::multiaccounts.recover/continue-pressed])
:accessibility-label :continue-custom-seed-phrase
:label (i18n/label :t/continue)}]
[button/button {:on-press #(re-frame/dispatch [:hide-popover]) [button/button {:on-press #(re-frame/dispatch [:hide-popover])
:accessibility-label :cancel-custom-seed-phrase :accessibility-label :cancel-custom-seed-phrase
:type :secondary :type :secondary

View File

@ -2,7 +2,6 @@
(:require [cljs.test :refer-macros [deftest is testing]] (:require [cljs.test :refer-macros [deftest is testing]]
[status-im.multiaccounts.recover.core :as models] [status-im.multiaccounts.recover.core :as models]
[status-im.multiaccounts.create.core :as multiaccounts.create] [status-im.multiaccounts.create.core :as multiaccounts.create]
[clojure.string :as string]
[status-im.utils.security :as security] [status-im.utils.security :as security]
[status-im.i18n :as i18n])) [status-im.i18n :as i18n]))
@ -10,9 +9,7 @@
(deftest check-phrase-warnings (deftest check-phrase-warnings
(is (nil? (models/check-phrase-warnings "monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey"))) (is (= :required-field (models/check-phrase-warnings ""))))
(is (nil? (models/check-phrase-warnings "game buzz method pretty olympic fat quit display velvet unveil marine crater")))
(is (= :recovery-phrase-unknown-words (models/check-phrase-warnings "game buzz method pretty zeus fat quit display velvet unveil marine crater"))))
;;;; handlers ;;;; handlers

View File

@ -289,8 +289,6 @@
"custom-seed-phrase": "عبارة البذور المخصصة", "custom-seed-phrase": "عبارة البذور المخصصة",
"custom-seed-phrase-text-1": "هذا يبدو وكأنه عبارة أولية مخصصة ولا يتطابق مع قاموس Status. هذا يمكن أن يعني أيضا", "custom-seed-phrase-text-1": "هذا يبدو وكأنه عبارة أولية مخصصة ولا يتطابق مع قاموس Status. هذا يمكن أن يعني أيضا",
"custom-seed-phrase-text-2": "بعض الكلمات بها أخطاء إملائية.", "custom-seed-phrase-text-2": "بعض الكلمات بها أخطاء إملائية.",
"custom-seed-phrase-text-3": "إذا كان الأمر كذلك ، فسوف ينتهي إنشاء",
"custom-seed-phrase-text-4": "حساب جديد",
"dapp": "ÐApp", "dapp": "ÐApp",
"dapp-would-like-to-connect-wallet": "ترغب في الاتصال", "dapp-would-like-to-connect-wallet": "ترغب في الاتصال",
"dapps": "ÐApps", "dapps": "ÐApps",

View File

@ -1116,8 +1116,6 @@
"custom-seed-phrase": "Custom seed phrase", "custom-seed-phrase": "Custom seed phrase",
"custom-seed-phrase-text-1": "This looks like a custom seed phrase and doesn't match the Status dictionary. This could also mean ", "custom-seed-phrase-text-1": "This looks like a custom seed phrase and doesn't match the Status dictionary. This could also mean ",
"custom-seed-phrase-text-2": "some words are misspelled.", "custom-seed-phrase-text-2": "some words are misspelled.",
"custom-seed-phrase-text-3": " If so, you'll end up creating a",
"custom-seed-phrase-text-4": " new account",
"to-enable-biometric": "To enable {{bio-type-label}}, your must save your password on the unlock screen", "to-enable-biometric": "To enable {{bio-type-label}}, your must save your password on the unlock screen",
"ok-save-pass": "OK, save password", "ok-save-pass": "OK, save password",
"lock-app-with": "Lock app with", "lock-app-with": "Lock app with",

View File

@ -281,8 +281,6 @@
"custom-seed-phrase": "Phrase de récupération personnalisée", "custom-seed-phrase": "Phrase de récupération personnalisée",
"custom-seed-phrase-text-1": "Cela ressemble à une phrase de récupération personnalisée et ne correspond pas au dictionnaire de Status. Cela pourrait aussi signifier", "custom-seed-phrase-text-1": "Cela ressemble à une phrase de récupération personnalisée et ne correspond pas au dictionnaire de Status. Cela pourrait aussi signifier",
"custom-seed-phrase-text-2": "certains mots sont mal orthographiés.", "custom-seed-phrase-text-2": "certains mots sont mal orthographiés.",
"custom-seed-phrase-text-3": "Si c'est le cas, vous allez créer un",
"custom-seed-phrase-text-4": "nouveau compte",
"dapp": "ÐApp", "dapp": "ÐApp",
"dapp-would-like-to-connect-wallet": "souhaite se connecter à votre portefeuille", "dapp-would-like-to-connect-wallet": "souhaite se connecter à votre portefeuille",
"dapps": "ÐApps", "dapps": "ÐApps",

View File

@ -281,8 +281,6 @@
"custom-seed-phrase": "Frase di recupero personalizzata", "custom-seed-phrase": "Frase di recupero personalizzata",
"custom-seed-phrase-text-1": "Sembra una frase seed personalizzata e non corrisponde al dizionario di Status. Questo potrebbe anche significare", "custom-seed-phrase-text-1": "Sembra una frase seed personalizzata e non corrisponde al dizionario di Status. Questo potrebbe anche significare",
"custom-seed-phrase-text-2": "alcune parole sono scritte male.", "custom-seed-phrase-text-2": "alcune parole sono scritte male.",
"custom-seed-phrase-text-3": "In tal caso, finirai per creare un",
"custom-seed-phrase-text-4": "Nuovo account",
"dapp": "ÐApp", "dapp": "ÐApp",
"dapp-would-like-to-connect-wallet": "vorrebbe connettersi a", "dapp-would-like-to-connect-wallet": "vorrebbe connettersi a",
"dapps": "ÐApps", "dapps": "ÐApps",

View File

@ -279,8 +279,6 @@
"custom-seed-phrase": "커스텀 시드 구문", "custom-seed-phrase": "커스텀 시드 구문",
"custom-seed-phrase-text-1": "커스텀 시드 구문이 스테이터스 딕셔너리와 일치하지 않습니다. 또한", "custom-seed-phrase-text-1": "커스텀 시드 구문이 스테이터스 딕셔너리와 일치하지 않습니다. 또한",
"custom-seed-phrase-text-2": "일부 단어의 철자가 잘못되었을 수 있습니다.", "custom-seed-phrase-text-2": "일부 단어의 철자가 잘못되었을 수 있습니다.",
"custom-seed-phrase-text-3": " 이 경우",
"custom-seed-phrase-text-4": "새 계정을 생성하게 됩니다.",
"dapp": "디앱", "dapp": "디앱",
"dapp-would-like-to-connect-wallet": "(이)가 사용자 다음에 연결합니다", "dapp-would-like-to-connect-wallet": "(이)가 사용자 다음에 연결합니다",
"dapps": "디앱", "dapps": "디앱",

View File

@ -279,8 +279,6 @@
"custom-seed-phrase": "自定义助记词", "custom-seed-phrase": "自定义助记词",
"custom-seed-phrase-text-1": "这好像是一个自定义的助记词与“Status”字库不匹配。这也可能意味着", "custom-seed-phrase-text-1": "这好像是一个自定义的助记词与“Status”字库不匹配。这也可能意味着",
"custom-seed-phrase-text-2": "有些单词拼写错误。", "custom-seed-phrase-text-2": "有些单词拼写错误。",
"custom-seed-phrase-text-3": "如果是这样,您最终将创建一个",
"custom-seed-phrase-text-4": "新账户",
"dapp": "ÐApp", "dapp": "ÐApp",
"dapp-would-like-to-connect-wallet": "想要连接到", "dapp-would-like-to-connect-wallet": "想要连接到",
"dapps": "ÐApp", "dapps": "ÐApp",