Add BIP39 validation for seed phrase recovery
Signed-off-by: yenda <eric@status.im>
This commit is contained in:
parent
b93e6d8d78
commit
3bcf7ecf29
|
@ -614,5 +614,6 @@ var TopLevel = {
|
||||||
"multiAccountReset" : function () {},
|
"multiAccountReset" : function () {},
|
||||||
"multiAccountLoadAccount" : function () {},
|
"multiAccountLoadAccount" : function () {},
|
||||||
"multiAccountStoreAccount" : function () {},
|
"multiAccountStoreAccount" : function () {},
|
||||||
"multiAccountImportMnemonic" : function () {}
|
"multiAccountImportMnemonic" : function () {},
|
||||||
|
"validateMnemonic" : function () {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}]
|
||||||
|
|
|
@ -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))
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "디앱",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue