diff options
author | Ian Coleman <coleman.ian@gmail.com> | 2016-08-23 10:31:39 +1000 |
---|---|---|
committer | Ian Coleman <coleman.ian@gmail.com> | 2016-08-23 10:31:39 +1000 |
commit | efe41586705d3eba480da31bb1002c4e54e73ef0 (patch) | |
tree | f071e764935ce06c165fdc014ed29172271c3cef | |
parent | 563e401a4f3880da88bfc69cf51be5a1becd4c66 (diff) | |
download | BIP39-efe41586705d3eba480da31bb1002c4e54e73ef0.tar.gz BIP39-efe41586705d3eba480da31bb1002c4e54e73ef0.tar.zst BIP39-efe41586705d3eba480da31bb1002c4e54e73ef0.zip |
BIP32 Root Key can be specified by user
-rw-r--r-- | bip39-standalone.html | 87 | ||||
-rw-r--r-- | src/index.html | 2 | ||||
-rw-r--r-- | src/js/index.js | 85 |
3 files changed, 150 insertions, 24 deletions
diff --git a/bip39-standalone.html b/bip39-standalone.html index b1fe90e..9d0de5e 100644 --- a/bip39-standalone.html +++ b/bip39-standalone.html | |||
@@ -109,7 +109,7 @@ | |||
109 | <div class="form-group"> | 109 | <div class="form-group"> |
110 | <label for="root-key" class="col-sm-2 control-label">BIP32 Root Key</label> | 110 | <label for="root-key" class="col-sm-2 control-label">BIP32 Root Key</label> |
111 | <div class="col-sm-10"> | 111 | <div class="col-sm-10"> |
112 | <textarea id="root-key" class="root-key form-control" readonly="readonly"></textarea> | 112 | <textarea id="root-key" class="root-key form-control"></textarea> |
113 | </div> | 113 | </div> |
114 | </div> | 114 | </div> |
115 | </form> | 115 | </form> |
@@ -14830,6 +14830,7 @@ var Mnemonic = function(language) { | |||
14830 | var showPrivKey = true; | 14830 | var showPrivKey = true; |
14831 | 14831 | ||
14832 | var phraseChangeTimeoutEvent = null; | 14832 | var phraseChangeTimeoutEvent = null; |
14833 | var rootKeyChangedTimeoutEvent = null; | ||
14833 | 14834 | ||
14834 | var DOM = {}; | 14835 | var DOM = {}; |
14835 | DOM.network = $(".network"); | 14836 | DOM.network = $(".network"); |
@@ -14868,12 +14869,13 @@ var Mnemonic = function(language) { | |||
14868 | DOM.passphrase.on("input", delayedPhraseChanged); | 14869 | DOM.passphrase.on("input", delayedPhraseChanged); |
14869 | DOM.generate.on("click", generateClicked); | 14870 | DOM.generate.on("click", generateClicked); |
14870 | DOM.more.on("click", showMore); | 14871 | DOM.more.on("click", showMore); |
14871 | DOM.bip32path.on("input", delayedPhraseChanged); | 14872 | DOM.rootKey.on("input", delayedRootKeyChanged); |
14872 | DOM.bip44purpose.on("input", delayedPhraseChanged); | 14873 | DOM.bip32path.on("input", calcForDerivationPath); |
14873 | DOM.bip44coin.on("input", delayedPhraseChanged); | 14874 | DOM.bip44purpose.on("input", calcForDerivationPath); |
14874 | DOM.bip44account.on("input", delayedPhraseChanged); | 14875 | DOM.bip44coin.on("input", calcForDerivationPath); |
14875 | DOM.bip44change.on("input", delayedPhraseChanged); | 14876 | DOM.bip44account.on("input", calcForDerivationPath); |
14876 | DOM.tab.on("click", delayedPhraseChanged); | 14877 | DOM.bip44change.on("input", calcForDerivationPath); |
14878 | DOM.tab.on("shown.bs.tab", calcForDerivationPath); | ||
14877 | DOM.indexToggle.on("click", toggleIndexes); | 14879 | DOM.indexToggle.on("click", toggleIndexes); |
14878 | DOM.addressToggle.on("click", toggleAddresses); | 14880 | DOM.addressToggle.on("click", toggleAddresses); |
14879 | DOM.privateKeyToggle.on("click", togglePrivateKeys); | 14881 | DOM.privateKeyToggle.on("click", togglePrivateKeys); |
@@ -14888,7 +14890,7 @@ var Mnemonic = function(language) { | |||
14888 | function networkChanged(e) { | 14890 | function networkChanged(e) { |
14889 | var network = e.target.value; | 14891 | var network = e.target.value; |
14890 | networks[network].onSelect(); | 14892 | networks[network].onSelect(); |
14891 | delayedPhraseChanged(); | 14893 | displayBip32Info(); |
14892 | } | 14894 | } |
14893 | 14895 | ||
14894 | function delayedPhraseChanged() { | 14896 | function delayedPhraseChanged() { |
@@ -14905,12 +14907,57 @@ var Mnemonic = function(language) { | |||
14905 | hideValidationError(); | 14907 | hideValidationError(); |
14906 | // Get the mnemonic phrase | 14908 | // Get the mnemonic phrase |
14907 | var phrase = DOM.phrase.val(); | 14909 | var phrase = DOM.phrase.val(); |
14908 | var passphrase = DOM.passphrase.val(); | ||
14909 | var errorText = findPhraseErrors(phrase); | 14910 | var errorText = findPhraseErrors(phrase); |
14910 | if (errorText) { | 14911 | if (errorText) { |
14911 | showValidationError(errorText); | 14912 | showValidationError(errorText); |
14912 | return; | 14913 | return; |
14913 | } | 14914 | } |
14915 | // Calculate and display | ||
14916 | var passphrase = DOM.passphrase.val(); | ||
14917 | calcBip32RootKeyFromSeed(phrase, passphrase); | ||
14918 | calcForDerivationPath(); | ||
14919 | hidePending(); | ||
14920 | } | ||
14921 | |||
14922 | function delayedRootKeyChanged() { | ||
14923 | // Warn if there is an existing mnemonic or passphrase. | ||
14924 | if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) { | ||
14925 | if (!confirm("This will clear existing mnemonic and passphrase")) { | ||
14926 | DOM.rootKey.val(bip32RootKey); | ||
14927 | return | ||
14928 | } | ||
14929 | } | ||
14930 | hideValidationError(); | ||
14931 | showPending(); | ||
14932 | // Clear existing mnemonic and passphrase | ||
14933 | DOM.phrase.val(""); | ||
14934 | DOM.passphrase.val(""); | ||
14935 | seed = null; | ||
14936 | if (rootKeyChangedTimeoutEvent != null) { | ||
14937 | clearTimeout(rootKeyChangedTimeoutEvent); | ||
14938 | } | ||
14939 | rootKeyChangedTimeoutEvent = setTimeout(rootKeyChanged, 400); | ||
14940 | } | ||
14941 | |||
14942 | function rootKeyChanged() { | ||
14943 | showPending(); | ||
14944 | hideValidationError(); | ||
14945 | // Validate the root key TODO | ||
14946 | var rootKeyBase58 = DOM.rootKey.val(); | ||
14947 | var errorText = validateRootKey(rootKeyBase58); | ||
14948 | if (errorText) { | ||
14949 | showValidationError(errorText); | ||
14950 | return; | ||
14951 | } | ||
14952 | // Calculate and display | ||
14953 | calcBip32RootKeyFromBase58(rootKeyBase58); | ||
14954 | calcForDerivationPath(); | ||
14955 | hidePending(); | ||
14956 | } | ||
14957 | |||
14958 | function calcForDerivationPath() { | ||
14959 | showPending(); | ||
14960 | hideValidationError(); | ||
14914 | // Get the derivation path | 14961 | // Get the derivation path |
14915 | var derivationPath = getDerivationPath(); | 14962 | var derivationPath = getDerivationPath(); |
14916 | var errorText = findDerivationPathErrors(derivationPath); | 14963 | var errorText = findDerivationPathErrors(derivationPath); |
@@ -14918,8 +14965,7 @@ var Mnemonic = function(language) { | |||
14918 | showValidationError(errorText); | 14965 | showValidationError(errorText); |
14919 | return; | 14966 | return; |
14920 | } | 14967 | } |
14921 | // Calculate and display | 14968 | calcBip32ExtendedKey(derivationPath); |
14922 | calcBip32Seed(phrase, passphrase, derivationPath); | ||
14923 | displayBip32Info(); | 14969 | displayBip32Info(); |
14924 | hidePending(); | 14970 | hidePending(); |
14925 | } | 14971 | } |
@@ -14966,9 +15012,16 @@ var Mnemonic = function(language) { | |||
14966 | return words; | 15012 | return words; |
14967 | } | 15013 | } |
14968 | 15014 | ||
14969 | function calcBip32Seed(phrase, passphrase, path) { | 15015 | function calcBip32RootKeyFromSeed(phrase, passphrase) { |
14970 | seed = mnemonic.toSeed(phrase, passphrase); | 15016 | seed = mnemonic.toSeed(phrase, passphrase); |
14971 | bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network); | 15017 | bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network); |
15018 | } | ||
15019 | |||
15020 | function calcBip32RootKeyFromBase58(rootKeyBase58) { | ||
15021 | bip32RootKey = bitcoin.HDNode.fromBase58(rootKeyBase58); | ||
15022 | } | ||
15023 | |||
15024 | function calcBip32ExtendedKey(path) { | ||
14972 | bip32ExtendedKey = bip32RootKey; | 15025 | bip32ExtendedKey = bip32RootKey; |
14973 | // Derive the key from the path | 15026 | // Derive the key from the path |
14974 | var pathBits = path.split("/"); | 15027 | var pathBits = path.split("/"); |
@@ -15031,6 +15084,16 @@ var Mnemonic = function(language) { | |||
15031 | return false; | 15084 | return false; |
15032 | } | 15085 | } |
15033 | 15086 | ||
15087 | function validateRootKey(rootKeyBase58) { | ||
15088 | try { | ||
15089 | bitcoin.HDNode.fromBase58(rootKeyBase58); | ||
15090 | } | ||
15091 | catch (e) { | ||
15092 | return "Invalid root key"; | ||
15093 | } | ||
15094 | return ""; | ||
15095 | } | ||
15096 | |||
15034 | function getDerivationPath() { | 15097 | function getDerivationPath() { |
15035 | if (DOM.bip44tab.hasClass("active")) { | 15098 | if (DOM.bip44tab.hasClass("active")) { |
15036 | var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44); | 15099 | var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44); |
diff --git a/src/index.html b/src/index.html index 6708675..fcdf109 100644 --- a/src/index.html +++ b/src/index.html | |||
@@ -105,7 +105,7 @@ | |||
105 | <div class="form-group"> | 105 | <div class="form-group"> |
106 | <label for="root-key" class="col-sm-2 control-label">BIP32 Root Key</label> | 106 | <label for="root-key" class="col-sm-2 control-label">BIP32 Root Key</label> |
107 | <div class="col-sm-10"> | 107 | <div class="col-sm-10"> |
108 | <textarea id="root-key" class="root-key form-control" readonly="readonly"></textarea> | 108 | <textarea id="root-key" class="root-key form-control"></textarea> |
109 | </div> | 109 | </div> |
110 | </div> | 110 | </div> |
111 | </form> | 111 | </form> |
diff --git a/src/js/index.js b/src/js/index.js index f3582b0..88e891c 100644 --- a/src/js/index.js +++ b/src/js/index.js | |||
@@ -12,6 +12,7 @@ | |||
12 | var showPrivKey = true; | 12 | var showPrivKey = true; |
13 | 13 | ||
14 | var phraseChangeTimeoutEvent = null; | 14 | var phraseChangeTimeoutEvent = null; |
15 | var rootKeyChangedTimeoutEvent = null; | ||
15 | 16 | ||
16 | var DOM = {}; | 17 | var DOM = {}; |
17 | DOM.network = $(".network"); | 18 | DOM.network = $(".network"); |
@@ -50,12 +51,13 @@ | |||
50 | DOM.passphrase.on("input", delayedPhraseChanged); | 51 | DOM.passphrase.on("input", delayedPhraseChanged); |
51 | DOM.generate.on("click", generateClicked); | 52 | DOM.generate.on("click", generateClicked); |
52 | DOM.more.on("click", showMore); | 53 | DOM.more.on("click", showMore); |
53 | DOM.bip32path.on("input", delayedPhraseChanged); | 54 | DOM.rootKey.on("input", delayedRootKeyChanged); |
54 | DOM.bip44purpose.on("input", delayedPhraseChanged); | 55 | DOM.bip32path.on("input", calcForDerivationPath); |
55 | DOM.bip44coin.on("input", delayedPhraseChanged); | 56 | DOM.bip44purpose.on("input", calcForDerivationPath); |
56 | DOM.bip44account.on("input", delayedPhraseChanged); | 57 | DOM.bip44coin.on("input", calcForDerivationPath); |
57 | DOM.bip44change.on("input", delayedPhraseChanged); | 58 | DOM.bip44account.on("input", calcForDerivationPath); |
58 | DOM.tab.on("click", delayedPhraseChanged); | 59 | DOM.bip44change.on("input", calcForDerivationPath); |
60 | DOM.tab.on("shown.bs.tab", calcForDerivationPath); | ||
59 | DOM.indexToggle.on("click", toggleIndexes); | 61 | DOM.indexToggle.on("click", toggleIndexes); |
60 | DOM.addressToggle.on("click", toggleAddresses); | 62 | DOM.addressToggle.on("click", toggleAddresses); |
61 | DOM.privateKeyToggle.on("click", togglePrivateKeys); | 63 | DOM.privateKeyToggle.on("click", togglePrivateKeys); |
@@ -70,7 +72,7 @@ | |||
70 | function networkChanged(e) { | 72 | function networkChanged(e) { |
71 | var network = e.target.value; | 73 | var network = e.target.value; |
72 | networks[network].onSelect(); | 74 | networks[network].onSelect(); |
73 | delayedPhraseChanged(); | 75 | displayBip32Info(); |
74 | } | 76 | } |
75 | 77 | ||
76 | function delayedPhraseChanged() { | 78 | function delayedPhraseChanged() { |
@@ -87,12 +89,57 @@ | |||
87 | hideValidationError(); | 89 | hideValidationError(); |
88 | // Get the mnemonic phrase | 90 | // Get the mnemonic phrase |
89 | var phrase = DOM.phrase.val(); | 91 | var phrase = DOM.phrase.val(); |
90 | var passphrase = DOM.passphrase.val(); | ||
91 | var errorText = findPhraseErrors(phrase); | 92 | var errorText = findPhraseErrors(phrase); |
92 | if (errorText) { | 93 | if (errorText) { |
93 | showValidationError(errorText); | 94 | showValidationError(errorText); |
94 | return; | 95 | return; |
95 | } | 96 | } |
97 | // Calculate and display | ||
98 | var passphrase = DOM.passphrase.val(); | ||
99 | calcBip32RootKeyFromSeed(phrase, passphrase); | ||
100 | calcForDerivationPath(); | ||
101 | hidePending(); | ||
102 | } | ||
103 | |||
104 | function delayedRootKeyChanged() { | ||
105 | // Warn if there is an existing mnemonic or passphrase. | ||
106 | if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) { | ||
107 | if (!confirm("This will clear existing mnemonic and passphrase")) { | ||
108 | DOM.rootKey.val(bip32RootKey); | ||
109 | return | ||
110 | } | ||
111 | } | ||
112 | hideValidationError(); | ||
113 | showPending(); | ||
114 | // Clear existing mnemonic and passphrase | ||
115 | DOM.phrase.val(""); | ||
116 | DOM.passphrase.val(""); | ||
117 | seed = null; | ||
118 | if (rootKeyChangedTimeoutEvent != null) { | ||
119 | clearTimeout(rootKeyChangedTimeoutEvent); | ||
120 | } | ||
121 | rootKeyChangedTimeoutEvent = setTimeout(rootKeyChanged, 400); | ||
122 | } | ||
123 | |||
124 | function rootKeyChanged() { | ||
125 | showPending(); | ||
126 | hideValidationError(); | ||
127 | // Validate the root key TODO | ||
128 | var rootKeyBase58 = DOM.rootKey.val(); | ||
129 | var errorText = validateRootKey(rootKeyBase58); | ||
130 | if (errorText) { | ||
131 | showValidationError(errorText); | ||
132 | return; | ||
133 | } | ||
134 | // Calculate and display | ||
135 | calcBip32RootKeyFromBase58(rootKeyBase58); | ||
136 | calcForDerivationPath(); | ||
137 | hidePending(); | ||
138 | } | ||
139 | |||
140 | function calcForDerivationPath() { | ||
141 | showPending(); | ||
142 | hideValidationError(); | ||
96 | // Get the derivation path | 143 | // Get the derivation path |
97 | var derivationPath = getDerivationPath(); | 144 | var derivationPath = getDerivationPath(); |
98 | var errorText = findDerivationPathErrors(derivationPath); | 145 | var errorText = findDerivationPathErrors(derivationPath); |
@@ -100,8 +147,7 @@ | |||
100 | showValidationError(errorText); | 147 | showValidationError(errorText); |
101 | return; | 148 | return; |
102 | } | 149 | } |
103 | // Calculate and display | 150 | calcBip32ExtendedKey(derivationPath); |
104 | calcBip32Seed(phrase, passphrase, derivationPath); | ||
105 | displayBip32Info(); | 151 | displayBip32Info(); |
106 | hidePending(); | 152 | hidePending(); |
107 | } | 153 | } |
@@ -148,9 +194,16 @@ | |||
148 | return words; | 194 | return words; |
149 | } | 195 | } |
150 | 196 | ||
151 | function calcBip32Seed(phrase, passphrase, path) { | 197 | function calcBip32RootKeyFromSeed(phrase, passphrase) { |
152 | seed = mnemonic.toSeed(phrase, passphrase); | 198 | seed = mnemonic.toSeed(phrase, passphrase); |
153 | bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network); | 199 | bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network); |
200 | } | ||
201 | |||
202 | function calcBip32RootKeyFromBase58(rootKeyBase58) { | ||
203 | bip32RootKey = bitcoin.HDNode.fromBase58(rootKeyBase58); | ||
204 | } | ||
205 | |||
206 | function calcBip32ExtendedKey(path) { | ||
154 | bip32ExtendedKey = bip32RootKey; | 207 | bip32ExtendedKey = bip32RootKey; |
155 | // Derive the key from the path | 208 | // Derive the key from the path |
156 | var pathBits = path.split("/"); | 209 | var pathBits = path.split("/"); |
@@ -213,6 +266,16 @@ | |||
213 | return false; | 266 | return false; |
214 | } | 267 | } |
215 | 268 | ||
269 | function validateRootKey(rootKeyBase58) { | ||
270 | try { | ||
271 | bitcoin.HDNode.fromBase58(rootKeyBase58); | ||
272 | } | ||
273 | catch (e) { | ||
274 | return "Invalid root key"; | ||
275 | } | ||
276 | return ""; | ||
277 | } | ||
278 | |||
216 | function getDerivationPath() { | 279 | function getDerivationPath() { |
217 | if (DOM.bip44tab.hasClass("active")) { | 280 | if (DOM.bip44tab.hasClass("active")) { |
218 | var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44); | 281 | var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44); |