Merge pull request #78 from pointbiz/split-wallet
v2.9.0 Split Wallet with UI improvements and unit tests (Shamir's Secret Sharing).
This commit is contained in:
commit
27aedc4fb8
8 changed files with 1714 additions and 79 deletions
|
@ -24,6 +24,7 @@ module.exports = function (grunt) {
|
||||||
{ token: "//cryptojs.ripemd160.js", file: "./src/cryptojs.ripemd160.js" },
|
{ token: "//cryptojs.ripemd160.js", file: "./src/cryptojs.ripemd160.js" },
|
||||||
{ token: "//crypto-scrypt.js", file: "./src/crypto-scrypt.js" },
|
{ token: "//crypto-scrypt.js", file: "./src/crypto-scrypt.js" },
|
||||||
{ token: "//ellipticcurve.js", file: "./src/ellipticcurve.js" },
|
{ token: "//ellipticcurve.js", file: "./src/ellipticcurve.js" },
|
||||||
|
{ token: "//secrets.js", file: "./src/secrets.js" },
|
||||||
{ token: "//ninja.key.js", file: "./src/ninja.key.js" },
|
{ token: "//ninja.key.js", file: "./src/ninja.key.js" },
|
||||||
{ token: "//ninja.misc.js", file: "./src/ninja.misc.js" },
|
{ token: "//ninja.misc.js", file: "./src/ninja.misc.js" },
|
||||||
{ token: "//ninja.onload.js", file: "./src/ninja.onload.js" },
|
{ token: "//ninja.onload.js", file: "./src/ninja.onload.js" },
|
||||||
|
@ -34,6 +35,7 @@ module.exports = function (grunt) {
|
||||||
{ token: "//ninja.bulkwallet.js", file: "./src/ninja.bulkwallet.js" },
|
{ token: "//ninja.bulkwallet.js", file: "./src/ninja.bulkwallet.js" },
|
||||||
{ token: "//ninja.brainwallet.js", file: "./src/ninja.brainwallet.js" },
|
{ token: "//ninja.brainwallet.js", file: "./src/ninja.brainwallet.js" },
|
||||||
{ token: "//ninja.vanitywallet.js", file: "./src/ninja.vanitywallet.js" },
|
{ token: "//ninja.vanitywallet.js", file: "./src/ninja.vanitywallet.js" },
|
||||||
|
{ token: "//ninja.splitwallet.js", file: "./src/ninja.splitwallet.js" },
|
||||||
{ token: "//ninja.detailwallet.js", file: "./src/ninja.detailwallet.js" },
|
{ token: "//ninja.detailwallet.js", file: "./src/ninja.detailwallet.js" },
|
||||||
{ token: "//qrcode.js", file: "./src/qrcode.js" },
|
{ token: "//qrcode.js", file: "./src/qrcode.js" },
|
||||||
{ token: "//securerandom.js", file: "./src/securerandom.js" },
|
{ token: "//securerandom.js", file: "./src/securerandom.js" },
|
||||||
|
@ -46,4 +48,4 @@ module.exports = function (grunt) {
|
||||||
grunt.file.defaultEncoding = 'utf-8';
|
grunt.file.defaultEncoding = 'utf-8';
|
||||||
grunt.loadNpmTasks("grunt-combine");
|
grunt.loadNpmTasks("grunt-combine");
|
||||||
grunt.registerTask("default", ["combine:single"]);
|
grunt.registerTask("default", ["combine:single"]);
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -80,6 +80,9 @@
|
||||||
//ellipticcurve.js
|
//ellipticcurve.js
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
//secrets.js
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
//biginteger.js
|
//biginteger.js
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
@ -126,19 +129,20 @@
|
||||||
<div id="tagline">Open Source JavaScript Client-Side Bitcoin Wallet Generator</div>
|
<div id="tagline">Open Source JavaScript Client-Side Bitcoin Wallet Generator</div>
|
||||||
<div id="seedpoolarea"><textarea rows="16" cols="62" id="seedpool"></textarea></div>
|
<div id="seedpoolarea"><textarea rows="16" cols="62" id="seedpool"></textarea></div>
|
||||||
<div id="testnet"></div>
|
<div id="testnet"></div>
|
||||||
<div class="menu" id="menu">
|
<ul class="menu" id="menu">
|
||||||
<div class="tab selected" id="singlewallet" onclick="ninja.tabSwitch(this);">Single Wallet</div>
|
<li class="tab selected" id="singlewallet" onclick="ninja.tabSwitch(this);">Single Wallet
|
||||||
<div class="tab" id="paperwallet" onclick="ninja.tabSwitch(this);">Paper Wallet</div>
|
<li class="tab" id="paperwallet" onclick="ninja.tabSwitch(this);">Paper Wallet
|
||||||
<div class="tab" id="bulkwallet" onclick="ninja.tabSwitch(this);">Bulk Wallet</div>
|
<li class="tab" id="bulkwallet" onclick="ninja.tabSwitch(this);">Bulk Wallet
|
||||||
<div class="tab" id="brainwallet" onclick="ninja.tabSwitch(this);">Brain Wallet</div>
|
<li class="tab" id="brainwallet" onclick="ninja.tabSwitch(this);">Brain Wallet
|
||||||
<div class="tab" id="vanitywallet" onclick="ninja.tabSwitch(this);">Vanity Wallet</div>
|
<li class="tab" id="vanitywallet" onclick="ninja.tabSwitch(this);">Vanity Wallet
|
||||||
<div class="tab" id="detailwallet" onclick="ninja.tabSwitch(this);">Wallet Details</div>
|
<li class="tab" id="splitwallet" onclick="ninja.tabSwitch(this);">Split Wallet
|
||||||
</div>
|
<li class="tab" id="detailwallet" onclick="ninja.tabSwitch(this);">Wallet Details
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div id="generate">
|
<div id="generate">
|
||||||
<span id="generatelabelbitcoinaddress">Generating Bitcoin Address...</span><br />
|
<span id="generatelabelbitcoinaddress">Generating Bitcoin Address...</span><br />
|
||||||
<span id="generatelabelmovemouse">MOVE your mouse around to add some extra randomness... </span><span id="mousemovelimit"></span><br />
|
<span id="generatelabelmovemouse">MOVE your mouse around to add some extra randomness... </span><span id="mousemovelimit"></span><br />
|
||||||
<span id="generatelabelkeypress">OR type some random characters into this textbox</span> <input type="text" id="generatekeyinput" onkeypress="ninja.seeder.seedKeyPress(event);" /><br />
|
<span id="generatelabelkeypress">OR type some random characters into this textbox</span> <input type="text" id="generatekeyinput" onkeydown="ninja.seeder.seedKeyPress(event);" /><br />
|
||||||
<div id="seedpooldisplay"></div>
|
<div id="seedpooldisplay"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -178,7 +182,7 @@
|
||||||
<p id="singletip2"><b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the "Generate New Address" button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.</p>
|
<p id="singletip2"><b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the "Generate New Address" button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.</p>
|
||||||
<p id="singletip3"><b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.</p>
|
<p id="singletip3"><b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.</p>
|
||||||
<p id="singletip4"><b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.</p>
|
<p id="singletip4"><b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.</p>
|
||||||
<p id="singletip5"><b>Spend your bitcoins</b> by going to blockchain.info or mtgox.com and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.</p>
|
<p id="singletip5"><b>Spend your bitcoins</b> by going to blockchain.info and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -333,6 +337,33 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="splitarea" class="walletarea">
|
||||||
|
<div id="splitcommands" class="commands " >
|
||||||
|
<label id="splitlabelthreshold">Minimum share threshold needed to combine</label>
|
||||||
|
<input type="text" id="splitthreshold" value="2" size="4"/>
|
||||||
|
<br/>
|
||||||
|
<label id="splitlabelshares">Number of shares</label>
|
||||||
|
<input type="text" id="splitshares" value="3" size="4"/>
|
||||||
|
<span><input type="button" id="splitview" value="Generate" onclick="ninja.wallets.splitwallet.splitKey();"></span>
|
||||||
|
<div id="splitstep1icon" class="more " onclick="ninja.wallets.splitwallet.openCloseStep(1);"></div>
|
||||||
|
</div>
|
||||||
|
<div id="splitstep1area"></div>
|
||||||
|
|
||||||
|
<div id="combinecommands" class="left commands">
|
||||||
|
<span>
|
||||||
|
<label id="combinelabelentershares">Enter Available Shares (whitespace separated)</label>
|
||||||
|
<textarea id="combineinput" cols="60" rows="10"></textarea><br/>
|
||||||
|
</span>
|
||||||
|
<span><input type="button" id="combineview" value="Combine Shares" onclick="ninja.wallets.splitwallet.combineShares();"></span>
|
||||||
|
</div>
|
||||||
|
<div id="splitstep2area">
|
||||||
|
<div id="combineoutput">
|
||||||
|
<label id="combinelabelprivatekey">Combined Private Key</label>
|
||||||
|
<div id="combinedprivatekey" class="output"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="detailarea" class="walletarea">
|
<div id="detailarea" class="walletarea">
|
||||||
<div id="detailcommands" class="commands">
|
<div id="detailcommands" class="commands">
|
||||||
<span><label id="detaillabelenterprivatekey" for="detailprivkey">Enter Private Key</label></span>
|
<span><label id="detaillabelenterprivatekey" for="detailprivkey">Enter Private Key</label></span>
|
||||||
|
@ -421,6 +452,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="footer" class="footer">
|
<div id="footer" class="footer">
|
||||||
<div class="authorbtc">
|
<div class="authorbtc">
|
||||||
<div>
|
<div>
|
||||||
|
@ -431,7 +463,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="authorpgp">
|
<div class="authorpgp">
|
||||||
<span class="item">
|
<span class="item">
|
||||||
<a href="pgpsignedmsg.txt" target="_blank"><span id="footerlabelversion">Version History</span> (v2.8.1)</a>
|
<a href="pgpsignedmsg.txt" target="_blank"><span id="footerlabelversion">Version History</span> (v2.9.0)</a>
|
||||||
(<a href="ninja_bitaddress.org.txt" target="_blank" id="footerlabelpgp">PGP</a>)
|
(<a href="ninja_bitaddress.org.txt" target="_blank" id="footerlabelpgp">PGP</a>)
|
||||||
</span>
|
</span>
|
||||||
<span class="item">527B 5C82 B1F6 B2DB 72A0<br />ECBF 8749 7B91 6397 4F5A</span>
|
<span class="item">527B 5C82 B1F6 B2DB 72A0<br />ECBF 8749 7B91 6397 4F5A</span>
|
||||||
|
@ -473,6 +505,9 @@
|
||||||
//ninja.detailwallet.js
|
//ninja.detailwallet.js
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
//ninja.splitwallet.js
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
//ninja.unittests.js
|
//ninja.unittests.js
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
74
src/main.css
74
src/main.css
|
@ -1,10 +1,10 @@
|
||||||
.more { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDEvMDIvMTLltnQyAAAB1UlEQVQ4jYWTS04bQRCGv3q0x8gMYJCwknCGLDgLVwiH4grhLFaUXdhkQ0A8pBg/FOLpnmbhMYzxRKlNS1Vdf/31V5XknGnb+eXJCBjzbzu9OLu+azu845Opysej4wHmshF4uJ2TUrb3CV0gIBAKRboC5C2vdkDE9fdty6/xDegvXz+NgDbFUejZ+PjDgExmtpxS9vYwMe5u5iyX8RRoa5Ic+C4qx9KUN1MGu4E618yqJ5axAp44KA7ZL3eYzp/HKdVIw7WK8d6BuDvcod9TQlBEIOXEdPlElSoUJabIIs4Z7h9yNDwgqOMayLXw7epHVIBggrsgspZPUBQyiCgugRQji7TAVDF1XB2TlQoOYCqovkmpopS9fcoiM3ue0rOCYf8IU8NklWxiiOQ3EPXtWagIqo6KYWYEc4IGvMViA6RrnCJKVS9B8ypRHG1YKNa0Ur+C+MPt/I2BKWVZUO4FgvQ47PcptEDF+T2Z8TiZUMWIyGtpd+Bze5VTSqP57O/4YG+AN/RXbSiPkwmL5z/be/L+mM4vT2JKeUW7EXD1erMz/Lo4u77f0K9DDhdA1XG11jh9vWBb99Z9gAg5QZ2hzpmUa0RSW4f/gqSY0s3Vz+tufEjvHS8Tg6BXC7qVbQAAAABJRU5ErkJggg==)
|
.more { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDEvMDIvMTLltnQyAAAB1UlEQVQ4jYWTS04bQRCGv3q0x8gMYJCwknCGLDgLVwiH4grhLFaUXdhkQ0A8pBg/FOLpnmbhMYzxRKlNS1Vdf/31V5XknGnb+eXJCBjzbzu9OLu+azu845Opysej4wHmshF4uJ2TUrb3CV0gIBAKRboC5C2vdkDE9fdty6/xDegvXz+NgDbFUejZ+PjDgExmtpxS9vYwMe5u5iyX8RRoa5Ic+C4qx9KUN1MGu4E618yqJ5axAp44KA7ZL3eYzp/HKdVIw7WK8d6BuDvcod9TQlBEIOXEdPlElSoUJabIIs4Z7h9yNDwgqOMayLXw7epHVIBggrsgspZPUBQyiCgugRQji7TAVDF1XB2TlQoOYCqovkmpopS9fcoiM3ue0rOCYf8IU8NklWxiiOQ3EPXtWagIqo6KYWYEc4IGvMViA6RrnCJKVS9B8ypRHG1YKNa0Ur+C+MPt/I2BKWVZUO4FgvQ47PcptEDF+T2Z8TiZUMWIyGtpd+Bze5VTSqP57O/4YG+AN/RXbSiPkwmL5z/be/L+mM4vT2JKeUW7EXD1erMz/Lo4u77f0K9DDhdA1XG11jh9vWBb99Z9gAg5QZ2hzpmUa0RSW4f/gqSY0s3Vz+tufEjvHS8Tg6BXC7qVbQAAAABJRU5ErkJggg==)
|
||||||
no-repeat left center; width: 17px; height: 17px; display: inline-block; float: right; }
|
no-repeat left center; width: 17px; height: 17px; display: inline-block; float: right; cursor: pointer; }
|
||||||
.less { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDEvMDIvMTLltnQyAAABuklEQVQ4ja2US25TQRBFT336OSEY5ESyBfEakNiLt0AW5S2QvQQxAiZIYBwSz/yByH7dxcB2bPMME+hJS/W5fetWVUtE8K/HfzdcXfdfqsr4onuGuRz4Jrdzcg6Gg9HfQYAxAqmlSMMlQJO5/oliE4AtQLcR++btZQ+wPVsvVXbTfXFGEMyWU9rVM0yMu/Gc5bJ+DdztxWcH3otKVzbPmyq5LnwfzSgEBMxlhqJEBFWVKKUgG66rur53oH7aOeWkUlJSRCBHZracssorlLXttHpCpzonaYukjmsiivDu08daAZIJ7oLIVg9BUQgQUVwSua5Z5AWmiqnj6pisVXAAU0F1J6WK0q6e024Fs4cplbXonFxgapisk00MkdiBqDd7oSKoOiqGmZHMSZrwPRYHIMfaKaKsyhI01oni6IaFYptSyiOIT27nOwaq5FyQrUAIC/nBhK+UErRSos55z4878CrneJyTnHOvquymf3mOb+hvy/jw+QuLh5/NORkORvsGrq77dc6xpr0RcH07y3oF8G04GN0f6HdEDhdA1XG1vXb6dsAa+3Z8AREiQwkoEeQoiBzocHDkf/wnvwC5IpRVsUDNUgAAAABJRU5ErkJggg==)
|
.less { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDEvMDIvMTLltnQyAAABuklEQVQ4ja2US25TQRBFT336OSEY5ESyBfEakNiLt0AW5S2QvQQxAiZIYBwSz/yByH7dxcB2bPMME+hJS/W5fetWVUtE8K/HfzdcXfdfqsr4onuGuRz4Jrdzcg6Gg9HfQYAxAqmlSMMlQJO5/oliE4AtQLcR++btZQ+wPVsvVXbTfXFGEMyWU9rVM0yMu/Gc5bJ+DdztxWcH3otKVzbPmyq5LnwfzSgEBMxlhqJEBFWVKKUgG66rur53oH7aOeWkUlJSRCBHZracssorlLXttHpCpzonaYukjmsiivDu08daAZIJ7oLIVg9BUQgQUVwSua5Z5AWmiqnj6pisVXAAU0F1J6WK0q6e024Fs4cplbXonFxgapisk00MkdiBqDd7oSKoOiqGmZHMSZrwPRYHIMfaKaKsyhI01oni6IaFYptSyiOIT27nOwaq5FyQrUAIC/nBhK+UErRSos55z4878CrneJyTnHOvquymf3mOb+hvy/jw+QuLh5/NORkORvsGrq77dc6xpr0RcH07y3oF8G04GN0f6HdEDhdA1XG1vXb6dsAa+3Z8AREiQwkoEeQoiBzocHDkf/wnvwC5IpRVsUDNUgAAAABJRU5ErkJggg==)
|
||||||
no-repeat left center; width: 17px; height: 17px; display: inline-block; float: right; }
|
no-repeat left center; width: 17px; height: 17px; display: inline-block; float: right; }
|
||||||
a { position: relative; z-index: 20; }
|
a { position: relative; z-index: 20; }
|
||||||
.right { text-align: right; }
|
.right { text-align: right; }
|
||||||
.walletarea { display: none; border: 2px solid green; }
|
.walletarea { display: none; border: 2px solid #009900; }
|
||||||
hr { margin: 20px 0; border-top: 2px dashed #008000; }
|
hr { margin: 20px 0; border-top: 2px dashed #008000; }
|
||||||
.keyarea { height: 110px; text-align: left; position: relative; padding: 5px; }
|
.keyarea { height: 110px; text-align: left; position: relative; padding: 5px; }
|
||||||
.keyarea .public { float: left; }
|
.keyarea .public { float: left; }
|
||||||
|
@ -21,13 +21,14 @@ body { font-family: Arial; }
|
||||||
.question { padding: 10px 15px; text-align: left; cursor: pointer; }
|
.question { padding: 10px 15px; text-align: left; cursor: pointer; }
|
||||||
.question:hover, .expandable:hover { color: #77777A; }
|
.question:hover, .expandable:hover { color: #77777A; }
|
||||||
.answer { padding: 0 15px 10px 25px; text-align: left; display: none; font-size: 80%; }
|
.answer { padding: 0 15px 10px 25px; text-align: left; display: none; font-size: 80%; }
|
||||||
.faq { border: 0; border-top: 2px solid green; }
|
.faq { border: 0; border-top: 2px solid #009900; }
|
||||||
|
|
||||||
|
#wallets { clear: both; }
|
||||||
#btcaddress, #btcprivwif, #detailaddress, #detailaddresscomp, #detailprivwif, #detailprivwifcomp { font-family: monospace; font-size: 1.25em; }
|
#btcaddress, #btcprivwif, #detailaddress, #detailaddresscomp, #detailprivwif, #detailprivwifcomp { font-family: monospace; font-size: 1.25em; }
|
||||||
#seedpoolarea { display: none; }
|
#seedpoolarea { display: none; }
|
||||||
#seedpooldisplay { font-family: monospace; font-size: 1em; width: 640px; padding: 15px 5px; word-wrap: break-word; }
|
#seedpooldisplay { font-family: monospace; font-size: 1em; width: 640px; padding: 15px 5px; word-wrap: break-word; }
|
||||||
.seedpoint { width: 6px; height: 6px; display: block; border-radius: 3px; background-color: green; position: absolute; z-index: 10; }
|
.seedpoint { width: 6px; height: 6px; display: block; border-radius: 3px; background-color: #009900; position: absolute; z-index: 10; }
|
||||||
#generate { font-family: monospace; font-size: 1.25em; height: 305px; text-align: left; position: relative; padding: 5px; border: 2px solid green; }
|
#generate { font-family: monospace; font-size: 1.25em; height: 305px; text-align: left; position: relative; padding: 5px; border: 2px solid #009900; clear: both; }
|
||||||
#generate span { padding: 5px 5px 0 5px; }
|
#generate span { padding: 5px 5px 0 5px; }
|
||||||
#generatekeyinput { position: relative; z-index: 20; }
|
#generatekeyinput { position: relative; z-index: 20; }
|
||||||
#keyarea { height: 250px; }
|
#keyarea { height: 250px; }
|
||||||
|
@ -40,14 +41,14 @@ body { font-family: Arial; }
|
||||||
#keyarea .public { width: 30%; display: table-cell; }
|
#keyarea .public { width: 30%; display: table-cell; }
|
||||||
#singlearea { font-size: 90%; }
|
#singlearea { font-size: 90%; }
|
||||||
#singlesecret { position: relative; top: -130px; float: right; right: 200px; color: red; font-weight: bolder; font-size: 200%; }
|
#singlesecret { position: relative; top: -130px; float: right; right: 200px; color: red; font-weight: bolder; font-size: 200%; }
|
||||||
#singleshare { position: relative; top: -110px; float: left; left: 160px; color: green; font-weight: bolder; font-size: 200%; }
|
#singleshare { position: relative; top: -110px; float: left; left: 160px; color: #009900; font-weight: bolder; font-size: 200%; }
|
||||||
#singlesafety { text-align: left; padding: 5px; border-top: 2px solid green; top: -25px; position: relative; }
|
#singlesafety { text-align: left; padding: 5px; border-top: 2px solid #009900; top: -25px; position: relative; }
|
||||||
|
|
||||||
#main { position: relative; text-align: center; margin: 0px auto; width: 1005px; }
|
#main { position: relative; text-align: center; margin: 0px auto; width: 808px; }
|
||||||
#logo { width: 578px; height: 80px; }
|
#logo { width: 578px; height: 80px; }
|
||||||
|
|
||||||
#paperarea { min-height: 120px; display: none; }
|
#paperarea { min-height: 120px; display: none; }
|
||||||
#paperarea .keyarea { border: 2px solid green; border-top: 0; }
|
#paperarea .keyarea { border: 2px solid #009900; border-top: 0; }
|
||||||
#paperarea .keyarea.art { display: block; height: auto; border: 0; font-family: Ubuntu, Arial; padding: 0; margin: 0; }
|
#paperarea .keyarea.art { display: block; height: auto; border: 0; font-family: Ubuntu, Arial; padding: 0; margin: 0; }
|
||||||
#paperarea .artwallet .papersvg { width: 486px; height: 261px; border: 0; margin: 0; padding: 0; left: 0; }
|
#paperarea .artwallet .papersvg { width: 486px; height: 261px; border: 0; margin: 0; padding: 0; left: 0; }
|
||||||
#paperarea .artwallet .qrcode_public { top: 52px; left: 17px; z-index: 100; margin: 0; float: none; display: block; position: absolute; background-color: #FFFFFF;
|
#paperarea .artwallet .qrcode_public { top: 52px; left: 17px; z-index: 100; margin: 0; float: none; display: block; position: absolute; background-color: #FFFFFF;
|
||||||
|
@ -111,12 +112,24 @@ body { font-family: Arial; }
|
||||||
#vanityarea .label { text-decoration: underline; }
|
#vanityarea .label { text-decoration: underline; }
|
||||||
#vanityarea .output { font-family: monospace; font-size: 1.25em; display: block; }
|
#vanityarea .output { font-family: monospace; font-size: 1.25em; display: block; }
|
||||||
#vanityarea .notes { text-align: left; font-size: 80%; padding: 0 0 20px 0; }
|
#vanityarea .notes { text-align: left; font-size: 80%; padding: 0 0 20px 0; }
|
||||||
#vanitystep1area { display: none; text-align: left; position: relative; padding: 15px; border-bottom: 2px solid green; }
|
#vanitystep1area { display: none; text-align: left; position: relative; padding: 15px; border-bottom: 2px solid #009900; }
|
||||||
#vanitystep1label { padding-left: 5px; }
|
#vanitystep1label { padding-left: 5px; }
|
||||||
#vanitystep2area { border-top: 2px solid green; display: block; padding: 15px; }
|
#vanitystep2area { border-top: 2px solid #009900; display: block; padding: 15px; }
|
||||||
#vanitystep2inputs { padding: 0 15px 10px 15px; }
|
#vanitystep2inputs { padding: 0 15px 10px 15px; }
|
||||||
#vanitycalc { margin-top: 5px; }
|
#vanitycalc { margin-top: 5px; }
|
||||||
|
|
||||||
|
#splitarea { text-align: left; }
|
||||||
|
#splitarea span { padding: 0; }
|
||||||
|
#splitcommands { padding: 10px 15px; text-align: left; }
|
||||||
|
#combinecommands { padding: 10px 15px; }
|
||||||
|
#splitstep1area { display: none; text-align: left; position: relative; padding: 0; border-bottom: 2px solid #009900; }
|
||||||
|
.splitsharerow { border-bottom: 2px solid #009900; padding: 15px; }
|
||||||
|
.splitsharerow:last-child { border-bottom: 0; }
|
||||||
|
#combinelabelprivatekey { text-decoration: underline; }
|
||||||
|
#splitarea .output { display: block; font-family: monospace; font-size: 1.25em; }
|
||||||
|
#splitarea span.output { display: inline; }
|
||||||
|
#splitstep2area { padding: 10px 15px; }
|
||||||
|
|
||||||
.englishjson { text-align: center; padding: 40px 0 20px 0; }
|
.englishjson { text-align: center; padding: 40px 0 20px 0; }
|
||||||
.unittests { text-align: center; }
|
.unittests { text-align: center; }
|
||||||
.unittests div { width: 894px; font-family: monospace; text-align: left; margin: auto; padding: 5px; border: 1px solid black; }
|
.unittests div { width: 894px; font-family: monospace; text-align: left; margin: auto; padding: 5px; border: 1px solid black; }
|
||||||
|
@ -134,14 +147,31 @@ body { font-family: Arial; }
|
||||||
@media screen
|
@media screen
|
||||||
{
|
{
|
||||||
#tagline { margin: 0 0 15px 0; font-style: italic; }
|
#tagline { margin: 0 0 15px 0; font-style: italic; }
|
||||||
.menu { text-align: left; }
|
.menu
|
||||||
.menu .tab { border-top-left-radius: 5px; border-top-right-radius: 5px; display: inline-block; background-color: #6C8C6C;
|
{
|
||||||
border: 2px solid green; padding: 5px; margin: 0 2px 0 0; position: relative; top: 2px; z-index: 110; cursor: pointer; }
|
text-align: left; margin: 0; padding: 0; display: block;
|
||||||
.menu .tab:hover { color: #FFF; }
|
background-color: #009900; /* # 009900 # 53c100 */
|
||||||
.menu .tab.selected { background-color: #FFF; border-bottom: 2px solid #FFF; cursor: default; }
|
border-top-left-radius: 5px; border-top-right-radius: 5px;
|
||||||
|
}
|
||||||
|
.menu .tab
|
||||||
|
{
|
||||||
|
position: relative; display: inline-block; border: 0px solid red;
|
||||||
|
margin: 0; list-style: none; z-index: 110; cursor: pointer;
|
||||||
|
top: 1px; padding: 10px 20px; width: 162px; text-align: center;
|
||||||
|
}
|
||||||
|
.menu .tab.selected
|
||||||
|
{
|
||||||
|
cursor: default;
|
||||||
|
background-color: #FFF; margin: 6px 16px; padding: 4px; border-radius: 5px;
|
||||||
|
}
|
||||||
|
.menu .tab:hover
|
||||||
|
{
|
||||||
|
background-color: #FFF; margin: 6px 16px; padding: 4px; border-radius: 5px;
|
||||||
|
}
|
||||||
.menu .tab.selected:hover { color: #000; }
|
.menu .tab.selected:hover { color: #000; }
|
||||||
.pagebreak { height: 50px; }
|
|
||||||
.commands { border-bottom: 2px solid green; padding: 10px 2px; margin-bottom: 0; }
|
.pagebreak { height: 50px; }
|
||||||
|
.commands { border-bottom: 2px solid #009900; padding: 10px 2px; margin-bottom: 0; }
|
||||||
.commands .row { padding: 0 0; text-align: left; }
|
.commands .row { padding: 0 0; text-align: left; }
|
||||||
.commands .row.extra { padding-top: 6px; }
|
.commands .row.extra { padding-top: 6px; }
|
||||||
.commands span { padding: 0 10px; }
|
.commands span { padding: 0 10px; }
|
||||||
|
@ -150,7 +180,7 @@ body { font-family: Arial; }
|
||||||
.expandable { padding: 10px 15px; text-align: left; cursor: pointer; }
|
.expandable { padding: 10px 15px; text-align: left; cursor: pointer; }
|
||||||
|
|
||||||
#menu { visibility: hidden; font-size: 90%; }
|
#menu { visibility: hidden; font-size: 90%; }
|
||||||
#culturemenu { text-align: right; padding: 0 20px; }
|
#culturemenu { text-align: right; padding: 0 20px; margin-bottom: 3px; }
|
||||||
#culturemenu span { padding: 3px; }
|
#culturemenu span { padding: 3px; }
|
||||||
#culturemenu .selected { text-decoration: none; color: #000000; }
|
#culturemenu .selected { text-decoration: none; color: #000000; }
|
||||||
|
|
||||||
|
@ -163,7 +193,7 @@ body { font-family: Arial; }
|
||||||
#detailcommands span { padding: 0 10px; }
|
#detailcommands span { padding: 0 10px; }
|
||||||
#detailprivkey { width: 250px; }
|
#detailprivkey { width: 250px; }
|
||||||
#detailprivkeypassphrase { width: 250px; }
|
#detailprivkeypassphrase { width: 250px; }
|
||||||
.paper .commands { border: 2px solid green; }
|
.paper .commands { border: 2px solid #009900; }
|
||||||
#bulkstartindex, #paperlimit, #paperlimitperpage { width: 35px; }
|
#bulkstartindex, #paperlimit, #paperlimitperpage { width: 35px; }
|
||||||
#bulklimit { width: 45px; }
|
#bulklimit { width: 45px; }
|
||||||
|
|
||||||
|
@ -182,7 +212,7 @@ body { font-family: Arial; }
|
||||||
#main { width: auto; }
|
#main { width: auto; }
|
||||||
#singlearea { border: 0; }
|
#singlearea { border: 0; }
|
||||||
#singlesafety { border: 0; }
|
#singlesafety { border: 0; }
|
||||||
#paperarea .keyarea:first-child { border-top: 2px solid green; }
|
#paperarea .keyarea:first-child { border-top: 2px solid #009900; }
|
||||||
#paperarea .keyarea.art:first-child { border: 0; }
|
#paperarea .keyarea.art:first-child { border: 0; }
|
||||||
.pagebreak { height: 1px; }
|
.pagebreak { height: 1px; }
|
||||||
.paper #logo { display: none; }
|
.paper #logo { display: none; }
|
||||||
|
|
113
src/ninja.splitwallet.js
Normal file
113
src/ninja.splitwallet.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
ninja.wallets.splitwallet = {
|
||||||
|
open: function () {
|
||||||
|
document.getElementById("splitarea").style.display = "block";
|
||||||
|
secrets.setRNG();
|
||||||
|
secrets.init(7); // 7 bits allows for up to 127 shares
|
||||||
|
},
|
||||||
|
|
||||||
|
close: function () {
|
||||||
|
document.getElementById("splitarea").style.display = "none";
|
||||||
|
},
|
||||||
|
|
||||||
|
mkOutputRow: function (s, id, lbltxt) {
|
||||||
|
var row = document.createElement("div");
|
||||||
|
var label = document.createElement("label");
|
||||||
|
label.innerHTML = lbltxt;
|
||||||
|
var qr = document.createElement("div");
|
||||||
|
var output = document.createElement("span");
|
||||||
|
output.setAttribute("class", "output");
|
||||||
|
output.innerHTML = s;
|
||||||
|
|
||||||
|
qr.setAttribute("id", id);
|
||||||
|
row.setAttribute("class", "splitsharerow");
|
||||||
|
row.appendChild(label);
|
||||||
|
row.appendChild(output);
|
||||||
|
row.appendChild(qr);
|
||||||
|
row.appendChild(document.createElement("br"));
|
||||||
|
|
||||||
|
return row;
|
||||||
|
},
|
||||||
|
|
||||||
|
stripLeadZeros: function (hex) { return hex.split(/^0+/).slice(-1)[0]; },
|
||||||
|
|
||||||
|
hexToBytes: function (hex) {
|
||||||
|
//if input has odd number of digits, pad it
|
||||||
|
if (hex.length % 2 == 1)
|
||||||
|
hex = "0" + hex;
|
||||||
|
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
||||||
|
bytes.push(parseInt(hex.substr(c, 2), 16));
|
||||||
|
return bytes;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Split a private key and update information in the HTML
|
||||||
|
splitKey: function () {
|
||||||
|
try {
|
||||||
|
var numshares = parseInt(document.getElementById('splitshares').value);
|
||||||
|
var threshold = parseInt(document.getElementById('splitthreshold').value);
|
||||||
|
var key = new Bitcoin.ECKey(false);
|
||||||
|
var bitcoinAddress = key.getBitcoinAddress();
|
||||||
|
var shares = ninja.wallets.splitwallet.getFormattedShares(key.getBitcoinHexFormat(), numshares, threshold);
|
||||||
|
|
||||||
|
var output = document.createElement("div");
|
||||||
|
output.setAttribute("id", "splitoutput");
|
||||||
|
var m = {};
|
||||||
|
output.appendChild(this.mkOutputRow(bitcoinAddress, "split_addr", "Bitcoin Address: "));
|
||||||
|
m["split_addr"] = bitcoinAddress;
|
||||||
|
|
||||||
|
for (var i = 0; i < shares.length; i++) {
|
||||||
|
var id = "split_qr_" + i;
|
||||||
|
output.appendChild(this.mkOutputRow(shares[i], id, "Share " + (i + 1) + ": "));
|
||||||
|
m[id] = shares[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("splitstep1area").innerHTML = output.innerHTML;
|
||||||
|
ninja.qrCode.showQrCode(m);
|
||||||
|
|
||||||
|
document.getElementById("splitstep1area").style.display = "block";
|
||||||
|
document.getElementById("splitstep1icon").setAttribute("class", "less");
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// browser does not have sufficient JavaScript support to generate a bitcoin address
|
||||||
|
alert(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Combine shares of a private key to retrieve the key
|
||||||
|
combineShares: function () {
|
||||||
|
try {
|
||||||
|
document.getElementById("combinedprivatekey").innerHTML = "";
|
||||||
|
var shares = document.getElementById("combineinput").value.trim().split(/\W+/);
|
||||||
|
var combinedBytes = ninja.wallets.splitwallet.combineFormattedShares(shares);
|
||||||
|
var privkeyBase58 = new Bitcoin.ECKey(combinedBytes).getBitcoinWalletImportFormat();
|
||||||
|
document.getElementById("combinedprivatekey").innerHTML = privkeyBase58;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
alert(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// generate shares and format them in base58
|
||||||
|
getFormattedShares: function (key, numshares, threshold) {
|
||||||
|
var shares = secrets.share(key, numshares, threshold).map(ninja.wallets.splitwallet.hexToBytes).map(Bitcoin.Base58.encode);
|
||||||
|
return shares;
|
||||||
|
},
|
||||||
|
|
||||||
|
// combine base58 formatted shares and return a bitcoin byte array
|
||||||
|
combineFormattedShares: function (shares) {
|
||||||
|
var combined = secrets.combine(shares.map(Bitcoin.Base58.decode).map(Crypto.util.bytesToHex).map(ninja.wallets.splitwallet.stripLeadZeros));
|
||||||
|
return ninja.wallets.splitwallet.hexToBytes(combined);
|
||||||
|
},
|
||||||
|
|
||||||
|
openCloseStep: function (num) {
|
||||||
|
// do close
|
||||||
|
if (document.getElementById("splitstep" + num + "area").style.display == "block") {
|
||||||
|
document.getElementById("splitstep" + num + "area").style.display = "none";
|
||||||
|
document.getElementById("splitstep" + num + "icon").setAttribute("class", "more");
|
||||||
|
}
|
||||||
|
// do open
|
||||||
|
else {
|
||||||
|
document.getElementById("splitstep" + num + "area").style.display = "block";
|
||||||
|
document.getElementById("splitstep" + num + "icon").setAttribute("class", "less");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -101,7 +101,7 @@ ninja.translator = {
|
||||||
"singletip2": "<b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the Generate New Address button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.", //TODO: please translate
|
"singletip2": "<b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the Generate New Address button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.", //TODO: please translate
|
||||||
"singletip3": "<b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.", //TODO: please translate
|
"singletip3": "<b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.", //TODO: please translate
|
||||||
"singletip4": "<b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.", //TODO: please translate
|
"singletip4": "<b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.", //TODO: please translate
|
||||||
"singletip5": "<b>Spend your bitcoins</b> by going to blockchain.info or mtgox.com and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.", //TODO: please translate
|
"singletip5": "<b>Spend your bitcoins</b> by going to blockchain.info and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.", //TODO: please translate
|
||||||
|
|
||||||
// paper wallet html
|
// paper wallet html
|
||||||
"paperlabelhideart": "Ocultar diseño",
|
"paperlabelhideart": "Ocultar diseño",
|
||||||
|
@ -235,7 +235,7 @@ ninja.translator = {
|
||||||
"singletip2": "<b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the Generate New Address button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.", //TODO: please translate
|
"singletip2": "<b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the Generate New Address button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.", //TODO: please translate
|
||||||
"singletip3": "<b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.", //TODO: please translate
|
"singletip3": "<b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.", //TODO: please translate
|
||||||
"singletip4": "<b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.", //TODO: please translate
|
"singletip4": "<b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.", //TODO: please translate
|
||||||
"singletip5": "<b>Spend your bitcoins</b> by going to blockchain.info or mtgox.com and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.", //TODO: please translate
|
"singletip5": "<b>Spend your bitcoins</b> by going to blockchain.info and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.", //TODO: please translate
|
||||||
|
|
||||||
// paper wallet html
|
// paper wallet html
|
||||||
"paperlabelhideart": "Retirer Le Style?",
|
"paperlabelhideart": "Retirer Le Style?",
|
||||||
|
@ -369,7 +369,7 @@ ninja.translator = {
|
||||||
"singletip2": "<b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the Generate New Address button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.", //TODO: please translate
|
"singletip2": "<b>To safeguard this wallet</b> you must print or otherwise record the Bitcoin address and private key. It is important to make a backup copy of the private key and store it in a safe location. This site does not have knowledge of your private key. If you are familiar with PGP you can download this all-in-one HTML page and check that you have an authentic version from the author of this site by matching the SHA1 hash of this HTML with the SHA1 hash available in the signed version history document linked on the footer of this site. If you leave/refresh the site or press the Generate New Address button then a new private key will be generated and the previously displayed private key will not be retrievable. Your Bitcoin private key should be kept a secret. Whomever you share the private key with has access to spend all the bitcoins associated with that address. If you print your wallet then store it in a zip lock bag to keep it safe from water. Treat a paper wallet like cash.", //TODO: please translate
|
||||||
"singletip3": "<b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.", //TODO: please translate
|
"singletip3": "<b>Add funds</b> to this wallet by instructing others to send bitcoins to your Bitcoin address.", //TODO: please translate
|
||||||
"singletip4": "<b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.", //TODO: please translate
|
"singletip4": "<b>Check your balance</b> by going to blockchain.info or blockexplorer.com and entering your Bitcoin address.", //TODO: please translate
|
||||||
"singletip5": "<b>Spend your bitcoins</b> by going to blockchain.info or mtgox.com and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.", //TODO: please translate
|
"singletip5": "<b>Spend your bitcoins</b> by going to blockchain.info and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.", //TODO: please translate
|
||||||
|
|
||||||
// paper wallet html
|
// paper wallet html
|
||||||
"paperlabelhideart": "Απόκρυψη γραφικού;",
|
"paperlabelhideart": "Απόκρυψη γραφικού;",
|
||||||
|
@ -503,7 +503,7 @@ ninja.translator = {
|
||||||
"singletip2": "<b>Per mettere in sicurezza questo portafogli</b> devi stampare o quantomeno salvare l'indirizzo bitcoin e la Chiave privata. È molto importante fare una copia di backup della chiave privata e conservarla in un posto sicuro. Questo sito non conosce la tua chiave privata. Se hai familiarità con PGP, puoi scaricare per intero questa pagina HTML e controllare la sua autentiticità. Puoi confrontare il codice SHA1 della pagina scaricata con il codice firmato dall'autore che trovi nella cronologia delle versioni (in fondo alla pagina). Se abbandoni/aggiorni la pagina web oppure premi il tasto Genera, un nuovo indirizzo sostituirà quello vecchio che non potrà più essere recuperato. La chiave privata dovrebbe essere tenuta segreta, chiunque conosca la chiave privata può avere accesso e spendere i tuoi bitcoin. Se stampi il tuo portafogli conservalo in una busta di plastica sigillata per tenerla al riparo dall'acqua. Tratta quanto stampato alla stregua di una banconota.",
|
"singletip2": "<b>Per mettere in sicurezza questo portafogli</b> devi stampare o quantomeno salvare l'indirizzo bitcoin e la Chiave privata. È molto importante fare una copia di backup della chiave privata e conservarla in un posto sicuro. Questo sito non conosce la tua chiave privata. Se hai familiarità con PGP, puoi scaricare per intero questa pagina HTML e controllare la sua autentiticità. Puoi confrontare il codice SHA1 della pagina scaricata con il codice firmato dall'autore che trovi nella cronologia delle versioni (in fondo alla pagina). Se abbandoni/aggiorni la pagina web oppure premi il tasto Genera, un nuovo indirizzo sostituirà quello vecchio che non potrà più essere recuperato. La chiave privata dovrebbe essere tenuta segreta, chiunque conosca la chiave privata può avere accesso e spendere i tuoi bitcoin. Se stampi il tuo portafogli conservalo in una busta di plastica sigillata per tenerla al riparo dall'acqua. Tratta quanto stampato alla stregua di una banconota.",
|
||||||
"singletip3": "<b>Ricevi fondi</b> su questo portafogli mostrando l'indirizzo bitcoin per il versamento.",
|
"singletip3": "<b>Ricevi fondi</b> su questo portafogli mostrando l'indirizzo bitcoin per il versamento.",
|
||||||
"singletip4": "<b>Controlla il saldo</b> visitando blockchain.info o blockexplorer.com cercando il tuo indirizzo bitcoin.",
|
"singletip4": "<b>Controlla il saldo</b> visitando blockchain.info o blockexplorer.com cercando il tuo indirizzo bitcoin.",
|
||||||
"singletip5": "<b>Spendi i tuoi bitcoin</b> aprendo un account su blockchain.info o mtgox.com usando la chiave privata. Puoi anche spendere i tuoi bitcoin scaricando il popolare client p2p ed importando in esso il portafogli. Tieni presente che quando importi una chiave nel client p2p, nel momento in cui spendi le monete, la chiave viene raggruppata insieme alle altre presenti nel programma con i restanti bitcoin. Quando esegui una transazione gli spiccioli verranno invitati verso un altro indirizzo all'interno del tuo portafogli gestito dal client p2p. Quindi dovresti tenere un backup del portafogli contenuto nel client p2p e tenere questo in un posto sicuro fin tanto terrai dei bitcoin lì. Satoshi consiglia di non cancellare mai un portafogli. ",
|
"singletip5": "<b>Spendi i tuoi bitcoin</b> aprendo un account su blockchain.info usando la chiave privata. Puoi anche spendere i tuoi bitcoin scaricando il popolare client p2p ed importando in esso il portafogli. Tieni presente che quando importi una chiave nel client p2p, nel momento in cui spendi le monete, la chiave viene raggruppata insieme alle altre presenti nel programma con i restanti bitcoin. Quando esegui una transazione gli spiccioli verranno invitati verso un altro indirizzo all'interno del tuo portafogli gestito dal client p2p. Quindi dovresti tenere un backup del portafogli contenuto nel client p2p e tenere questo in un posto sicuro fin tanto terrai dei bitcoin lì. Satoshi consiglia di non cancellare mai un portafogli. ",
|
||||||
|
|
||||||
// paper wallet html
|
// paper wallet html
|
||||||
"paperlabelhideart": "Senza grafica?",
|
"paperlabelhideart": "Senza grafica?",
|
||||||
|
@ -638,7 +638,7 @@ ninja.translator = {
|
||||||
"singletip2": "<b>Um dieses Wallet zu schützen,</b> musst du es entweder ausdrucken oder anderweitig die Bitcoin-Adresse und den privaten Schlüssel sichern. Fertige auf jeden Fall eine Kopie des privaten Schlüssels an und bewahre sie an einem sicheren Ort auf. Der private Schlüssel liegt nur lokal auf deinem Rechner vor und wurde nicht ins Internet übertragen. Falls du dich mit PGP auskennst, kannst du dir diese all-in-one HTML-Seite herunterladen. Um zu überprüfen, ob die heruntergeladene Version authentisch ist, kannst du den SHA1-Hash dieser Seite mit dem SHA1-Hash in der signierten Versionsgeschichte am unteren Ende dieser Seite abgleichen. Wenn du diese Seite verlässt, sie neulädst bzw. den \"Neues Wallet erstellen\"-Button drückst, wird ein neues Wallet erstellt und das vorherige wird nicht mehr abrufbar sein. Du solltest deinen privaten Schlüssel geheim halten. Wer den privaten Schlüssel hat, kann damit auf alle im Wallet befindlichen Bitcoin zugreifen und sie nach Belieben ausgeben. Behandle dein gedrucktes Wallet wie echtes Geld!",
|
"singletip2": "<b>Um dieses Wallet zu schützen,</b> musst du es entweder ausdrucken oder anderweitig die Bitcoin-Adresse und den privaten Schlüssel sichern. Fertige auf jeden Fall eine Kopie des privaten Schlüssels an und bewahre sie an einem sicheren Ort auf. Der private Schlüssel liegt nur lokal auf deinem Rechner vor und wurde nicht ins Internet übertragen. Falls du dich mit PGP auskennst, kannst du dir diese all-in-one HTML-Seite herunterladen. Um zu überprüfen, ob die heruntergeladene Version authentisch ist, kannst du den SHA1-Hash dieser Seite mit dem SHA1-Hash in der signierten Versionsgeschichte am unteren Ende dieser Seite abgleichen. Wenn du diese Seite verlässt, sie neulädst bzw. den \"Neues Wallet erstellen\"-Button drückst, wird ein neues Wallet erstellt und das vorherige wird nicht mehr abrufbar sein. Du solltest deinen privaten Schlüssel geheim halten. Wer den privaten Schlüssel hat, kann damit auf alle im Wallet befindlichen Bitcoin zugreifen und sie nach Belieben ausgeben. Behandle dein gedrucktes Wallet wie echtes Geld!",
|
||||||
"singletip3": "Du kannst <b>Guthaben</b> zu deinem Wallet <b>hinzufügen</b>, indem du genau wie bei anderen Überweisungen Bitcoins an die Bitcoin-Adresse deines Wallets schickst.",
|
"singletip3": "Du kannst <b>Guthaben</b> zu deinem Wallet <b>hinzufügen</b>, indem du genau wie bei anderen Überweisungen Bitcoins an die Bitcoin-Adresse deines Wallets schickst.",
|
||||||
"singletip4": "<b>Überprüfe dein Guthaben,</b> indem du deine Bitcoin-Adresse auf blockchain.info bzw. blockexplorer.com eingibst.",
|
"singletip4": "<b>Überprüfe dein Guthaben,</b> indem du deine Bitcoin-Adresse auf blockchain.info bzw. blockexplorer.com eingibst.",
|
||||||
"singletip5": "Du kannst deine <b>Bitcoins ausgeben</b>, indem du das gesamte mit deinem privaten Schlüssel verbundene Guthaben auf deinen Account bei blockchain.info bzw. mtgox.com überträgst. Alternativ kannst du dir ein Bitcoinprogramm herunterladen und deinen privaten Schlüssel in dieses importieren. Beachte dabei aber, dass, sobald du Bitcoins mit dem Programm sendest, dein privater Schlüssel mit den anderen privaten Schlüsseln, die vom Programm bereitgestellt werden, verbunden wird. Bei einer Überweisung wird etwas Rückgeld an eine der Bitcoin-Adressen des Programms geschickt. Deswegen musst du, um tatsächlich dein gesamtes Guthaben zu sichern, ein Backup vom gesamten Wallet des Programms, das nun auch deinen importierten privaten Schlüssel enthält, anfertigen. Satoshi rät, dass man unter keinen Umständen ein Wallet löschen sollte.",
|
"singletip5": "Du kannst deine <b>Bitcoins ausgeben</b>, indem du das gesamte mit deinem privaten Schlüssel verbundene Guthaben auf deinen Account bei blockchain.info überträgst. Alternativ kannst du dir ein Bitcoinprogramm herunterladen und deinen privaten Schlüssel in dieses importieren. Beachte dabei aber, dass, sobald du Bitcoins mit dem Programm sendest, dein privater Schlüssel mit den anderen privaten Schlüsseln, die vom Programm bereitgestellt werden, verbunden wird. Bei einer Überweisung wird etwas Rückgeld an eine der Bitcoin-Adressen des Programms geschickt. Deswegen musst du, um tatsächlich dein gesamtes Guthaben zu sichern, ein Backup vom gesamten Wallet des Programms, das nun auch deinen importierten privaten Schlüssel enthält, anfertigen. Satoshi rät, dass man unter keinen Umständen ein Wallet löschen sollte.",
|
||||||
|
|
||||||
// paper wallet html
|
// paper wallet html
|
||||||
"paperlabelhideart": "Grafische Gestaltung ausblenden?",
|
"paperlabelhideart": "Grafische Gestaltung ausblenden?",
|
||||||
|
@ -772,7 +772,7 @@ ninja.translator = {
|
||||||
"singletip2": "<b>Pro zabezpečení této peněženky</b> musíte tuto Bitcoin adresu a soukromý klíč vytisknout a nebo jinak poznamenat. Je důležité provést zálohu soukromého klíče a jeho uschování na bezpečném místě. Tato webová stránka nemá žádné informace o vašem soukromém klíči. Pokud ovládáte PGP, můžete celou tuto stránku stáhnout v jednom HTML souboru a ověřit její pravost srovnáním SHA1 hashe s podepsaným dokumentem historie verzí. Odkaz naleznete v patičce této stránky. Pokud opustíte či obnovíte tuto stránku nebo kliknete na 'Vytvořit novou adresu' dojde k vygenerování nového soukromého klíče a předtím zobrazený klíč bude ztracen. Váš soukromý klíč musíte uchovat v tajnosti. Každý kdo má tento klíč k dispozici může utratit všechny peníze v této peněžence. Pokud budete peněženku tisknout, uzavřete ji do nepropustného obalu nebo ji zalaminujte. Tím zabráníte jejímu poškození vodou. Chovejte se k této peněžence jako k normálním bankovkám.",
|
"singletip2": "<b>Pro zabezpečení této peněženky</b> musíte tuto Bitcoin adresu a soukromý klíč vytisknout a nebo jinak poznamenat. Je důležité provést zálohu soukromého klíče a jeho uschování na bezpečném místě. Tato webová stránka nemá žádné informace o vašem soukromém klíči. Pokud ovládáte PGP, můžete celou tuto stránku stáhnout v jednom HTML souboru a ověřit její pravost srovnáním SHA1 hashe s podepsaným dokumentem historie verzí. Odkaz naleznete v patičce této stránky. Pokud opustíte či obnovíte tuto stránku nebo kliknete na 'Vytvořit novou adresu' dojde k vygenerování nového soukromého klíče a předtím zobrazený klíč bude ztracen. Váš soukromý klíč musíte uchovat v tajnosti. Každý kdo má tento klíč k dispozici může utratit všechny peníze v této peněžence. Pokud budete peněženku tisknout, uzavřete ji do nepropustného obalu nebo ji zalaminujte. Tím zabráníte jejímu poškození vodou. Chovejte se k této peněžence jako k normálním bankovkám.",
|
||||||
"singletip3": "<b>Pro vložení</b> peněz do této peněženky stačí zaslat peníze na Bitcoin adresu.",
|
"singletip3": "<b>Pro vložení</b> peněz do této peněženky stačí zaslat peníze na Bitcoin adresu.",
|
||||||
"singletip4": "<b>Zkontrolovat zůstatek</b> můžete na webové stránce blockchain.info nebo blockexplorer.com po zadání Bitcoin adresy.",
|
"singletip4": "<b>Zkontrolovat zůstatek</b> můžete na webové stránce blockchain.info nebo blockexplorer.com po zadání Bitcoin adresy.",
|
||||||
"singletip5": "<b>Utratit Bitcoiny</b> můžete pomocí blockchain.info či mtgox.com načtením celého zůstatku pomocí soukromého klíče do vašeho účtu. Utratit zůstatek můžete také pomocí jednoho z P2P Bitcoin klientů naimportováním soukromého klíče. Myslete na to, že importem klíče do klienta se stane součástí jeho peněženky. Pokud převedete někomu peníze, nespotřebovaný zůstatek se zašle na jinou Bitcoin adresu uvedenou v P2P klienta. Tuto novou adresu musíte vyzálohovat a udržovat v bezpečí. Satoshi doporučuje, že by nikdo nikdy neměl mazat peněženku.",
|
"singletip5": "<b>Utratit Bitcoiny</b> můžete pomocí blockchain.info načtením celého zůstatku pomocí soukromého klíče do vašeho účtu. Utratit zůstatek můžete také pomocí jednoho z P2P Bitcoin klientů naimportováním soukromého klíče. Myslete na to, že importem klíče do klienta se stane součástí jeho peněženky. Pokud převedete někomu peníze, nespotřebovaný zůstatek se zašle na jinou Bitcoin adresu uvedenou v P2P klienta. Tuto novou adresu musíte vyzálohovat a udržovat v bezpečí. Satoshi doporučuje, že by nikdo nikdy neměl mazat peněženku.",
|
||||||
"singleshare": "SDÍLEJTE",
|
"singleshare": "SDÍLEJTE",
|
||||||
"singlesecret": "SOUKROMÉ",
|
"singlesecret": "SOUKROMÉ",
|
||||||
|
|
||||||
|
|
|
@ -458,6 +458,113 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// test split wallet
|
||||||
|
testSplitAndCombinePrivateKey2of2: function () {
|
||||||
|
// lowercase hex key
|
||||||
|
var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW
|
||||||
|
var numshares = 2;
|
||||||
|
var threshold = 2;
|
||||||
|
secrets.setRNG();
|
||||||
|
secrets.init(7);
|
||||||
|
|
||||||
|
var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold);
|
||||||
|
var combined = ninja.wallets.splitwallet.combineFormattedShares(shares);
|
||||||
|
var btcKey = new Bitcoin.ECKey(combined);
|
||||||
|
|
||||||
|
if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// Example use case #1:
|
||||||
|
// Division of 3 shares:
|
||||||
|
// 1 share in a safety deposit box ("Box")
|
||||||
|
// 1 share at Home
|
||||||
|
// 1 share at Work
|
||||||
|
// Threshold of 2 can be redeemed in these permutations
|
||||||
|
// Home + Box
|
||||||
|
// Work + Box
|
||||||
|
// Home + Work
|
||||||
|
testSplitAndCombinePrivateKey2of3: function () {
|
||||||
|
// lowercase hex key
|
||||||
|
var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW
|
||||||
|
var numshares = 3;
|
||||||
|
var threshold = 2;
|
||||||
|
secrets.setRNG();
|
||||||
|
secrets.init(7);
|
||||||
|
|
||||||
|
var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold);
|
||||||
|
shares.shift();
|
||||||
|
var combined = ninja.wallets.splitwallet.combineFormattedShares(shares);
|
||||||
|
var btcKey = new Bitcoin.ECKey(combined);
|
||||||
|
|
||||||
|
if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
testSplitAndCombinePrivateKey2of4: function () {
|
||||||
|
// uppercase hex key
|
||||||
|
var key = "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B"; //5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb
|
||||||
|
var numshares = 4;
|
||||||
|
var threshold = 2;
|
||||||
|
secrets.setRNG();
|
||||||
|
secrets.init(7);
|
||||||
|
|
||||||
|
var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold);
|
||||||
|
shares.shift();
|
||||||
|
shares.shift();
|
||||||
|
var combined = ninja.wallets.splitwallet.combineFormattedShares(shares);
|
||||||
|
var btcKey = new Bitcoin.ECKey(combined);
|
||||||
|
|
||||||
|
if (btcKey.getBitcoinHexFormat() != key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// Example use case #2:
|
||||||
|
// Division of 13 shares:
|
||||||
|
// 4 shares in a safety deposit box ("Box")
|
||||||
|
// 3 shares with good friend Angie
|
||||||
|
// 3 shares with good friend Fred
|
||||||
|
// 3 shares with Self at home or office
|
||||||
|
// Threshold of 7 can be redeemed in these permutations
|
||||||
|
// Self + Box (no trust to spend your coins but your friends are backing up your shares)
|
||||||
|
// Angie + Box (Angie will send btc to executor of your will)
|
||||||
|
// Fred + Box (if Angie hasn't already then Fred will send btc to executor of your will)
|
||||||
|
// Angie + Fred + Self (bank fire/theft then you with both your friends can spend the coins)
|
||||||
|
testSplitAndCombinePrivateKey7of13: function () {
|
||||||
|
var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da";
|
||||||
|
var numshares = 12;
|
||||||
|
var threshold = 7;
|
||||||
|
secrets.setRNG();
|
||||||
|
secrets.init(7);
|
||||||
|
|
||||||
|
var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold);
|
||||||
|
var combined = ninja.wallets.splitwallet.combineFormattedShares(shares);
|
||||||
|
var btcKey = new Bitcoin.ECKey(combined);
|
||||||
|
|
||||||
|
if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
testCombinePrivateKeyFromXofYShares: function () {
|
||||||
|
var key = "5K9nHKqbwc1xXpa6wV5p3AaCnubvxQDBukKaFkq7ThAkxgMTMEh";
|
||||||
|
// these are 4 of 6 shares
|
||||||
|
var shares = ["3XxjMASmrkk6eXMM9kAJA7qiqViNVBfiwA1GQDLvg4PVScL", "3Y2DkcPuNX8VKZwpnDdxw55wJtcnCvv2nALqe8nBLViHvck",
|
||||||
|
"3Y6qv7kyGwgRBKVHVbUNtzmLYAZWQtTPztPwR8wc7uf4MXR", "3YD4TowZn6jw5ss8U89vrcPHonFW4vSs9VKq8MupV5kevG4"]
|
||||||
|
secrets.setRNG();
|
||||||
|
secrets.init(7);
|
||||||
|
|
||||||
|
var combined = ninja.wallets.splitwallet.combineFormattedShares(shares);
|
||||||
|
var btcKey = new Bitcoin.ECKey(combined);
|
||||||
|
if (btcKey.getBitcoinWalletImportFormat() != key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
532
src/secrets.js
Normal file
532
src/secrets.js
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
// secrets.js - by Alexander Stetsyuk - released under MIT License
|
||||||
|
(function(exports, global){
|
||||||
|
var defaults = {
|
||||||
|
bits: 8, // default number of bits
|
||||||
|
radix: 16, // work with HEX by default
|
||||||
|
minBits: 3,
|
||||||
|
maxBits: 20, // this permits 1,048,575 shares, though going this high is NOT recommended in JS!
|
||||||
|
|
||||||
|
bytesPerChar: 2,
|
||||||
|
maxBytesPerChar: 6, // Math.pow(256,7) > Math.pow(2,53)
|
||||||
|
|
||||||
|
// Primitive polynomials (in decimal form) for Galois Fields GF(2^n), for 2 <= n <= 30
|
||||||
|
// The index of each term in the array corresponds to the n for that polynomial
|
||||||
|
// i.e. to get the polynomial for n=16, use primitivePolynomials[16]
|
||||||
|
primitivePolynomials: [null,null,1,3,3,5,3,3,29,17,9,5,83,27,43,3,45,9,39,39,9,5,3,33,27,9,71,39,9,5,83],
|
||||||
|
|
||||||
|
// warning for insecure PRNG
|
||||||
|
warning: 'WARNING:\nA secure random number generator was not found.\nUsing Math.random(), which is NOT cryptographically strong!'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Protected settings object
|
||||||
|
var config = {};
|
||||||
|
|
||||||
|
/** @expose **/
|
||||||
|
exports.getConfig = function(){
|
||||||
|
return {
|
||||||
|
'bits': config.bits,
|
||||||
|
'unsafePRNG': config.unsafePRNG
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function init(bits){
|
||||||
|
if(bits && (typeof bits !== 'number' || bits%1 !== 0 || bits<defaults.minBits || bits>defaults.maxBits)){
|
||||||
|
throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' + defaults.maxBits + ', inclusive.')
|
||||||
|
}
|
||||||
|
|
||||||
|
config.radix = defaults.radix;
|
||||||
|
config.bits = bits || defaults.bits;
|
||||||
|
config.size = Math.pow(2, config.bits);
|
||||||
|
config.max = config.size - 1;
|
||||||
|
|
||||||
|
// Construct the exp and log tables for multiplication.
|
||||||
|
var logs = [], exps = [], x = 1, primitive = defaults.primitivePolynomials[config.bits];
|
||||||
|
for(var i=0; i<config.size; i++){
|
||||||
|
exps[i] = x;
|
||||||
|
logs[x] = i;
|
||||||
|
x <<= 1;
|
||||||
|
if(x >= config.size){
|
||||||
|
x ^= primitive;
|
||||||
|
x &= config.max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.logs = logs;
|
||||||
|
config.exps = exps;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @expose **/
|
||||||
|
exports.init = init;
|
||||||
|
|
||||||
|
function isInited(){
|
||||||
|
if(!config.bits || !config.size || !config.max || !config.logs || !config.exps || config.logs.length !== config.size || config.exps.length !== config.size){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a pseudo-random number generator of the form function(bits){}
|
||||||
|
// which should output a random string of 1's and 0's of length `bits`
|
||||||
|
function getRNG(){
|
||||||
|
var randomBits, crypto;
|
||||||
|
|
||||||
|
function construct(bits, arr, radix, size){
|
||||||
|
var str = '',
|
||||||
|
i = 0,
|
||||||
|
len = arr.length-1;
|
||||||
|
while( i<len || (str.length < bits) ){
|
||||||
|
str += padLeft(parseInt(arr[i], radix).toString(2), size);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
str = str.substr(-bits);
|
||||||
|
if( (str.match(/0/g)||[]).length === str.length){ // all zeros?
|
||||||
|
return null;
|
||||||
|
}else{
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// node.js crypto.randomBytes()
|
||||||
|
if(typeof require === 'function' && (crypto=require('crypto')) && (randomBits=crypto['randomBytes'])){
|
||||||
|
return function(bits){
|
||||||
|
var bytes = Math.ceil(bits/8),
|
||||||
|
str = null;
|
||||||
|
|
||||||
|
while( str === null ){
|
||||||
|
str = construct(bits, randomBits(bytes).toString('hex'), 16, 4);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// browsers with window.crypto.getRandomValues()
|
||||||
|
if(global['crypto'] && typeof global['crypto']['getRandomValues'] === 'function' && typeof global['Uint32Array'] === 'function'){
|
||||||
|
crypto = global['crypto'];
|
||||||
|
return function(bits){
|
||||||
|
var elems = Math.ceil(bits/32),
|
||||||
|
str = null,
|
||||||
|
arr = new global['Uint32Array'](elems);
|
||||||
|
|
||||||
|
while( str === null ){
|
||||||
|
crypto['getRandomValues'](arr);
|
||||||
|
str = construct(bits, arr, 10, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A totally insecure RNG!!! (except in Safari)
|
||||||
|
// Will produce a warning every time it is called.
|
||||||
|
config.unsafePRNG = true;
|
||||||
|
warn();
|
||||||
|
|
||||||
|
var bitsPerNum = 32;
|
||||||
|
var max = Math.pow(2,bitsPerNum)-1;
|
||||||
|
return function(bits){
|
||||||
|
var elems = Math.ceil(bits/bitsPerNum);
|
||||||
|
var arr = [], str=null;
|
||||||
|
while(str===null){
|
||||||
|
for(var i=0; i<elems; i++){
|
||||||
|
arr[i] = Math.floor(Math.random() * max + 1);
|
||||||
|
}
|
||||||
|
str = construct(bits, arr, 10, bitsPerNum);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Warn about using insecure rng.
|
||||||
|
// Called when Math.random() is being used.
|
||||||
|
function warn(){
|
||||||
|
global['console']['warn'](defaults.warning);
|
||||||
|
if(typeof global['alert'] === 'function' && config.alert){
|
||||||
|
global['alert'](defaults.warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the PRNG to use. If no RNG function is supplied, pick a default using getRNG()
|
||||||
|
/** @expose **/
|
||||||
|
exports.setRNG = function(rng, alert){
|
||||||
|
if(!isInited()){
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
config.unsafePRNG=false;
|
||||||
|
rng = rng || getRNG();
|
||||||
|
|
||||||
|
// test the RNG (5 times)
|
||||||
|
if(typeof rng !== 'function' || typeof rng(config.bits) !== 'string' || !parseInt(rng(config.bits),2) || rng(config.bits).length > config.bits || rng(config.bits).length < config.bits){
|
||||||
|
throw new Error("Random number generator is invalid. Supply an RNG of the form function(bits){} that returns a string containing 'bits' number of random 1's and 0's.")
|
||||||
|
}else{
|
||||||
|
config.rng = rng;
|
||||||
|
}
|
||||||
|
config.alert = !!alert;
|
||||||
|
|
||||||
|
return !!config.unsafePRNG;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isSetRNG(){
|
||||||
|
return typeof config.rng === 'function';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates a random bits-length number string using the PRNG
|
||||||
|
/** @expose **/
|
||||||
|
exports.random = function(bits){
|
||||||
|
if(!isSetRNG()){
|
||||||
|
this.setRNG();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof bits !== 'number' || bits%1 !== 0 || bits < 2){
|
||||||
|
throw new Error('Number of bits must be an integer greater than 1.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.unsafePRNG){
|
||||||
|
warn();
|
||||||
|
}
|
||||||
|
return bin2hex(config.rng(bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divides a `secret` number String str expressed in radix `inputRadix` (optional, default 16)
|
||||||
|
// into `numShares` shares, each expressed in radix `outputRadix` (optional, default to `inputRadix`),
|
||||||
|
// requiring `threshold` number of shares to reconstruct the secret.
|
||||||
|
// Optionally, zero-pads the secret to a length that is a multiple of padLength before sharing.
|
||||||
|
/** @expose **/
|
||||||
|
exports.share = function(secret, numShares, threshold, padLength, withoutPrefix){
|
||||||
|
if(!isInited()){
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
if(!isSetRNG()){
|
||||||
|
this.setRNG();
|
||||||
|
}
|
||||||
|
|
||||||
|
padLength = padLength || 0;
|
||||||
|
|
||||||
|
if(typeof secret !== 'string'){
|
||||||
|
throw new Error('Secret must be a string.');
|
||||||
|
}
|
||||||
|
if(typeof numShares !== 'number' || numShares%1 !== 0 || numShares < 2){
|
||||||
|
throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive.')
|
||||||
|
}
|
||||||
|
if(numShares > config.max){
|
||||||
|
var neededBits = Math.ceil(Math.log(numShares +1)/Math.LN2);
|
||||||
|
throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive. To create ' + numShares + ' shares, use at least ' + neededBits + ' bits.')
|
||||||
|
}
|
||||||
|
if(typeof threshold !== 'number' || threshold%1 !== 0 || threshold < 2){
|
||||||
|
throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive.');
|
||||||
|
}
|
||||||
|
if(threshold > config.max){
|
||||||
|
var neededBits = Math.ceil(Math.log(threshold +1)/Math.LN2);
|
||||||
|
throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive. To use a threshold of ' + threshold + ', use at least ' + neededBits + ' bits.');
|
||||||
|
}
|
||||||
|
if(typeof padLength !== 'number' || padLength%1 !== 0 ){
|
||||||
|
throw new Error('Zero-pad length must be an integer greater than 1.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.unsafePRNG){
|
||||||
|
warn();
|
||||||
|
}
|
||||||
|
|
||||||
|
secret = '1' + hex2bin(secret); // append a 1 so that we can preserve the correct number of leading zeros in our secret
|
||||||
|
secret = split(secret, padLength);
|
||||||
|
var x = new Array(numShares), y = new Array(numShares);
|
||||||
|
for(var i=0, len = secret.length; i<len; i++){
|
||||||
|
var subShares = this._getShares(secret[i], numShares, threshold);
|
||||||
|
for(var j=0; j<numShares; j++){
|
||||||
|
x[j] = x[j] || subShares[j].x.toString(config.radix);
|
||||||
|
y[j] = padLeft(subShares[j].y.toString(2)) + (y[j] ? y[j] : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var padding = config.max.toString(config.radix).length;
|
||||||
|
if(withoutPrefix){
|
||||||
|
for(var i=0; i<numShares; i++){
|
||||||
|
x[i] = bin2hex(y[i]);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
for(var i=0; i<numShares; i++){
|
||||||
|
x[i] = config.bits.toString(36).toUpperCase() + padLeft(x[i],padding) + bin2hex(y[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the basic polynomial generation and evaluation function
|
||||||
|
// for a `config.bits`-length secret (NOT an arbitrary length)
|
||||||
|
// Note: no error-checking at this stage! If `secrets` is NOT
|
||||||
|
// a NUMBER less than 2^bits-1, the output will be incorrect!
|
||||||
|
/** @expose **/
|
||||||
|
exports._getShares = function(secret, numShares, threshold){
|
||||||
|
var shares = [];
|
||||||
|
var coeffs = [secret];
|
||||||
|
|
||||||
|
for(var i=1; i<threshold; i++){
|
||||||
|
coeffs[i] = parseInt(config.rng(config.bits),2);
|
||||||
|
}
|
||||||
|
for(var i=1, len = numShares+1; i<len; i++){
|
||||||
|
shares[i-1] = {
|
||||||
|
x: i,
|
||||||
|
y: horner(i, coeffs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shares;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Polynomial evaluation at `x` using Horner's Method
|
||||||
|
// TODO: this can possibly be sped up using other methods
|
||||||
|
// NOTE: fx=fx * x + coeff[i] -> exp(log(fx) + log(x)) + coeff[i],
|
||||||
|
// so if fx===0, just set fx to coeff[i] because
|
||||||
|
// using the exp/log form will result in incorrect value
|
||||||
|
function horner(x, coeffs){
|
||||||
|
var logx = config.logs[x];
|
||||||
|
var fx = 0;
|
||||||
|
for(var i=coeffs.length-1; i>=0; i--){
|
||||||
|
if(fx === 0){
|
||||||
|
fx = coeffs[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fx = config.exps[ (logx + config.logs[fx]) % config.max ] ^ coeffs[i];
|
||||||
|
}
|
||||||
|
return fx;
|
||||||
|
};
|
||||||
|
|
||||||
|
function inArray(arr,val){
|
||||||
|
for(var i = 0,len=arr.length; i < len; i++) {
|
||||||
|
if(arr[i] === val){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function processShare(share){
|
||||||
|
|
||||||
|
var bits = parseInt(share[0], 36);
|
||||||
|
if(bits && (typeof bits !== 'number' || bits%1 !== 0 || bits<defaults.minBits || bits>defaults.maxBits)){
|
||||||
|
throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' + defaults.maxBits + ', inclusive.')
|
||||||
|
}
|
||||||
|
|
||||||
|
var max = Math.pow(2, bits) - 1;
|
||||||
|
var idLength = max.toString(config.radix).length;
|
||||||
|
|
||||||
|
var id = parseInt(share.substr(1, idLength), config.radix);
|
||||||
|
if(typeof id !== 'number' || id%1 !== 0 || id<1 || id>max){
|
||||||
|
throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
|
||||||
|
}
|
||||||
|
share = share.substr(idLength + 1);
|
||||||
|
if(!share.length){
|
||||||
|
throw new Error('Invalid share: zero-length share.')
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'bits': bits,
|
||||||
|
'id': id,
|
||||||
|
'value': share
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @expose **/
|
||||||
|
exports._processShare = processShare;
|
||||||
|
|
||||||
|
// Protected method that evaluates the Lagrange interpolation
|
||||||
|
// polynomial at x=`at` for individual config.bits-length
|
||||||
|
// segments of each share in the `shares` Array.
|
||||||
|
// Each share is expressed in base `inputRadix`. The output
|
||||||
|
// is expressed in base `outputRadix'
|
||||||
|
function combine(at, shares){
|
||||||
|
var setBits, share, x = [], y = [], result = '', idx;
|
||||||
|
|
||||||
|
for(var i=0, len = shares.length; i<len; i++){
|
||||||
|
share = processShare(shares[i]);
|
||||||
|
if(typeof setBits === 'undefined'){
|
||||||
|
setBits = share['bits'];
|
||||||
|
}else if(share['bits'] !== setBits){
|
||||||
|
throw new Error('Mismatched shares: Different bit settings.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.bits !== setBits){
|
||||||
|
init(setBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(inArray(x, share['id'])){ // repeated x value?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = x.push(share['id']) - 1;
|
||||||
|
share = split(hex2bin(share['value']));
|
||||||
|
for(var j=0, len2 = share.length; j<len2; j++){
|
||||||
|
y[j] = y[j] || [];
|
||||||
|
y[j][idx] = share[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i=0, len=y.length; i<len; i++){
|
||||||
|
result = padLeft(lagrange(at, x, y[i]).toString(2)) + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(at===0){// reconstructing the secret
|
||||||
|
var idx = result.indexOf('1'); //find the first 1
|
||||||
|
return bin2hex(result.slice(idx+1));
|
||||||
|
}else{// generating a new share
|
||||||
|
return bin2hex(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Combine `shares` Array into the original secret
|
||||||
|
/** @expose **/
|
||||||
|
exports.combine = function(shares){
|
||||||
|
return combine(0, shares);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate a new share with id `id` (a number between 1 and 2^bits-1)
|
||||||
|
// `id` can be a Number or a String in the default radix (16)
|
||||||
|
/** @expose **/
|
||||||
|
exports.newShare = function(id, shares){
|
||||||
|
if(typeof id === 'string'){
|
||||||
|
id = parseInt(id, config.radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
var share = processShare(shares[0]);
|
||||||
|
var max = Math.pow(2, share['bits']) - 1;
|
||||||
|
|
||||||
|
if(typeof id !== 'number' || id%1 !== 0 || id<1 || id>max){
|
||||||
|
throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
|
||||||
|
}
|
||||||
|
|
||||||
|
var padding = max.toString(config.radix).length;
|
||||||
|
return config.bits.toString(36).toUpperCase() + padLeft(id.toString(config.radix), padding) + combine(id, shares);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Evaluate the Lagrange interpolation polynomial at x = `at`
|
||||||
|
// using x and y Arrays that are of the same length, with
|
||||||
|
// corresponding elements constituting points on the polynomial.
|
||||||
|
function lagrange(at, x, y){
|
||||||
|
var sum = 0,
|
||||||
|
product,
|
||||||
|
i, j;
|
||||||
|
|
||||||
|
for(var i=0, len = x.length; i<len; i++){
|
||||||
|
if(!y[i]){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
product = config.logs[y[i]];
|
||||||
|
for(var j=0; j<len; j++){
|
||||||
|
if(i === j){ continue; }
|
||||||
|
if(at === x[j]){ // happens when computing a share that is in the list of shares used to compute it
|
||||||
|
product = -1; // fix for a zero product term, after which the sum should be sum^0 = sum, not sum^1
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
product = ( product + config.logs[at ^ x[j]] - config.logs[x[i] ^ x[j]] + config.max/* to make sure it's not negative */ ) % config.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum = product === -1 ? sum : sum ^ config.exps[product]; // though exps[-1]= undefined and undefined ^ anything = anything in chrome, this behavior may not hold everywhere, so do the check
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @expose **/
|
||||||
|
exports._lagrange = lagrange;
|
||||||
|
|
||||||
|
// Splits a number string `bits`-length segments, after first
|
||||||
|
// optionally zero-padding it to a length that is a multiple of `padLength.
|
||||||
|
// Returns array of integers (each less than 2^bits-1), with each element
|
||||||
|
// representing a `bits`-length segment of the input string from right to left,
|
||||||
|
// i.e. parts[0] represents the right-most `bits`-length segment of the input string.
|
||||||
|
function split(str, padLength){
|
||||||
|
if(padLength){
|
||||||
|
str = padLeft(str, padLength)
|
||||||
|
}
|
||||||
|
var parts = [];
|
||||||
|
for(var i=str.length; i>config.bits; i-=config.bits){
|
||||||
|
parts.push(parseInt(str.slice(i-config.bits, i), 2));
|
||||||
|
}
|
||||||
|
parts.push(parseInt(str.slice(0, i), 2));
|
||||||
|
return parts;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pads a string `str` with zeros on the left so that its length is a multiple of `bits`
|
||||||
|
function padLeft(str, bits){
|
||||||
|
bits = bits || config.bits
|
||||||
|
var missing = str.length % bits;
|
||||||
|
return (missing ? new Array(bits - missing + 1).join('0') : '') + str;
|
||||||
|
};
|
||||||
|
|
||||||
|
function hex2bin(str){
|
||||||
|
var bin = '', num;
|
||||||
|
for(var i=str.length - 1; i>=0; i--){
|
||||||
|
num = parseInt(str[i], 16)
|
||||||
|
if(isNaN(num)){
|
||||||
|
throw new Error('Invalid hex character.')
|
||||||
|
}
|
||||||
|
bin = padLeft(num.toString(2), 4) + bin;
|
||||||
|
}
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bin2hex(str){
|
||||||
|
var hex = '', num;
|
||||||
|
str = padLeft(str, 4);
|
||||||
|
for(var i=str.length; i>=4; i-=4){
|
||||||
|
num = parseInt(str.slice(i-4, i), 2);
|
||||||
|
if(isNaN(num)){
|
||||||
|
throw new Error('Invalid binary character.')
|
||||||
|
}
|
||||||
|
hex = num.toString(16) + hex;
|
||||||
|
}
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a given UTF16 character string to the HEX representation.
|
||||||
|
// Each character of the input string is represented by
|
||||||
|
// `bytesPerChar` bytes in the output string.
|
||||||
|
/** @expose **/
|
||||||
|
exports.str2hex = function(str, bytesPerChar){
|
||||||
|
if(typeof str !== 'string'){
|
||||||
|
throw new Error('Input must be a character string.');
|
||||||
|
}
|
||||||
|
bytesPerChar = bytesPerChar || defaults.bytesPerChar;
|
||||||
|
|
||||||
|
if(typeof bytesPerChar !== 'number' || bytesPerChar%1 !== 0 || bytesPerChar<1 || bytesPerChar > defaults.maxBytesPerChar){
|
||||||
|
throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar + ', inclusive.')
|
||||||
|
}
|
||||||
|
|
||||||
|
var hexChars = 2*bytesPerChar;
|
||||||
|
var max = Math.pow(16, hexChars) - 1;
|
||||||
|
var out = '', num;
|
||||||
|
for(var i=0, len=str.length; i<len; i++){
|
||||||
|
num = str[i].charCodeAt();
|
||||||
|
if(isNaN(num)){
|
||||||
|
throw new Error('Invalid character: ' + str[i]);
|
||||||
|
}else if(num > max){
|
||||||
|
var neededBytes = Math.ceil(Math.log(num+1)/Math.log(256));
|
||||||
|
throw new Error('Invalid character code (' + num +'). Maximum allowable is 256^bytes-1 (' + max + '). To convert this character, use at least ' + neededBytes + ' bytes.')
|
||||||
|
}else{
|
||||||
|
out = padLeft(num.toString(16), hexChars) + out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts a given HEX number string to a UTF16 character string.
|
||||||
|
/** @expose **/
|
||||||
|
exports.hex2str = function(str, bytesPerChar){
|
||||||
|
if(typeof str !== 'string'){
|
||||||
|
throw new Error('Input must be a hexadecimal string.');
|
||||||
|
}
|
||||||
|
bytesPerChar = bytesPerChar || defaults.bytesPerChar;
|
||||||
|
|
||||||
|
if(typeof bytesPerChar !== 'number' || bytesPerChar%1 !== 0 || bytesPerChar<1 || bytesPerChar > defaults.maxBytesPerChar){
|
||||||
|
throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar + ', inclusive.')
|
||||||
|
}
|
||||||
|
|
||||||
|
var hexChars = 2*bytesPerChar;
|
||||||
|
var out = '';
|
||||||
|
str = padLeft(str, hexChars);
|
||||||
|
for(var i=0, len = str.length; i<len; i+=hexChars){
|
||||||
|
out = String.fromCharCode(parseInt(str.slice(i, i+hexChars),16)) + out;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
// by default, initialize without an RNG
|
||||||
|
exports.init();
|
||||||
|
})(typeof module !== 'undefined' && module['exports'] ? module['exports'] : (window['secrets'] = {}), typeof GLOBAL !== 'undefined' ? GLOBAL : window );
|
Loading…
Reference in a new issue