diff --git a/LICENCE b/LICENSE similarity index 100% rename from LICENCE rename to LICENSE diff --git a/README.md b/README.md index 209dad5..393fb62 100644 --- a/README.md +++ b/README.md @@ -25,5 +25,6 @@ Coinb.in supports a number of key features such as: - Compatible with bitcoin-qt - An offical .onion address for tor users. - Offline qrcode creator and scanning tool +- HD (bip32) support Donate to 1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg to see more development! diff --git a/index.html b/index.html index a0a8213..efa7018 100644 --- a/index.html +++ b/index.html @@ -20,6 +20,7 @@ + @@ -53,6 +54,7 @@ @@ -90,7 +92,7 @@

Open Source

-

Coinbin is an open source web based wallet written in javascript and released under the MIT licence which means its free to use and edit.

+

Coinbin is an open source web based wallet written in javascript and released under the MIT license which means its free to use and edit.

@@ -361,6 +363,37 @@
+
+

New HD Address making bip32 even easier

+

Use the form below to generate a master hierarchical deterministic address.

+ + +
+ + + + +
+ + +
+ + + + +
+ +

Address Options

+

You can use the advanced options below to generate different kind of master address.

+ +
+ + +
+ + +
+

Transaction Create a new transaction

Use this page to create a raw transaction

@@ -518,7 +551,7 @@

Verify transactions and other scripts

-

Enter the raw transaction, redeem script, pubkey or wif key to convert it into a readable format that can be verified manually.

+

Enter the raw transaction, redeem script, pubkey, hd address or wif key to convert it into a readable format that can be verified manually.

@@ -595,6 +628,95 @@

Address:

+ + @@ -663,7 +785,7 @@

Github https://github.com/OutCast3k/coinbin/

TOR 4zpinp6gdkjfplhk.onion

Information

-

Coinb.in is a free and open source project released under the MIT licence, originally by OutCast3k in 2013. Discussion of the project can be found at bitcointalk.org during its early testing stages when its primary focus was to develop a proof of concept multisig solution in javascript.

+

Coinb.in is a free and open source project released under the MIT license, originally by OutCast3k in 2013. Discussion of the project can be found at bitcointalk.org during its early testing stages when its primary focus was to develop a proof of concept multisig solution in javascript.

Coinb.in is run and funded by the generosity of others in terms of development and hosting.

Privacy

Coinb.in beleives strongly in privacy, not only do we support the use of TOR, the site does not collect and store IP or transaction data via our servers nor do we store your bitcoins private key. We do use google analytics to track hits and route traffic via cloudflare using an SSL certificate.

diff --git a/js/coin.js b/js/coin.js index ba3527f..73e108d 100644 --- a/js/coin.js +++ b/js/coin.js @@ -1,6 +1,6 @@ /* Coinjs 0.01 beta by OutCast3k{at}gmail.com - A bitcoin framework loosely based on bitcoinjs. + A bitcoin frameworkcoinjs. http://github.com/OutCast3k/coinjs or http://coinb.in/coinjs */ @@ -13,10 +13,12 @@ coinjs.pub = 0x00; coinjs.priv = 0x80; coinjs.multisig = 0x05; + coinjs.hdkey = {'prv':0x0488ade4, 'pub':0x0488b21e}; + coinjs.compressed = false; /* other vars */ - coinjs.developer = '1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg'; + coinjs.developer = '1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg'; // bitcoin /* bit(coinb.in) api vars */ coinjs.host = ('https:'==document.location.protocol?'https://':'http://')+'coinb.in/api/'; @@ -26,8 +28,8 @@ /* start of address functions */ /* generate a private and public keypair, with address and WIF address */ - coinjs.newKeys = function(string){ - var privkey = (string) ? Crypto.SHA256(string) : this.newPrivkey(); + coinjs.newKeys = function(input){ + var privkey = (input) ? Crypto.SHA256(input) : this.newPrivkey(); var pubkey = this.newPubkey(privkey); return { 'privkey': privkey, @@ -376,9 +378,199 @@ return result_txt; }; + /* start of hd functions, thanks bip32.org */ + coinjs.hd = function(data){ + + var r = {}; + + /* some hd value parsing */ + r.parse = function() { + + var bytes = []; + + // some quick validation + if(typeof(data) == 'string'){ + var decoded = coinjs.base58decode(data); + if(decoded.length == 82){ + var checksum = decoded.slice(78, 82); + var hash = Crypto.SHA256(Crypto.SHA256(decoded.slice(0, 78), { asBytes: true } ), { asBytes: true } ); + if(checksum[0]==hash[0] && checksum[1]==hash[1] && checksum[2]==hash[2] && checksum[3]==hash[3]){ + bytes = decoded.slice(0, 78); + } + } + } + + // actual parsing code + if(bytes && bytes.length>0) { + r.version = coinjs.uint(bytes.slice(0, 4) , 4); + r.depth = coinjs.uint(bytes.slice(4, 5) ,1); + r.parent_fingerprint = bytes.slice(5, 9); + r.child_index = coinjs.uint(bytes.slice(9, 13), 4); + r.chain_code = bytes.slice(13, 45); + r.key_bytes = bytes.slice(45, 78); + + var c = coinjs.compressed; // get current default + coinjs.compressed = true; + + if(r.key_bytes[0] == 0x00) { + r.type = 'private'; + var privkey = (r.key_bytes).slice(1, 33); + var privkeyHex = Crypto.util.bytesToHex(privkey); + var pubkey = coinjs.newPubkey(privkeyHex); + + r.keys = {'privkey':privkeyHex, + 'pubkey':pubkey, + 'address':coinjs.pubkey2address(pubkey), + 'wif':coinjs.privkey2wif(privkeyHex)}; + + } else if(r.key_bytes[0] == 0x02 || r.key_bytes[0] == 0x03) { + r.type = 'public'; + var pubkeyHex = Crypto.util.bytesToHex(r.key_bytes); + + r.keys = {'pubkey': pubkeyHex, + 'address':coinjs.pubkey2address(pubkeyHex)}; + } else { + r.type = 'invalid'; + } + + r.keys_extended = r.extend(); + + coinjs.compressed = c; // reset to default + } + } + + r.extend = function(){ + var hd = coinjs.hd(); + return hd.make({'depth':(this.depth*1)+1, + 'parent_fingerprint':this.parent_fingerprint, + 'child_index':this.child_index, + 'chain_code':this.chain_code, + 'privkey':this.keys.privkey, + 'pubkey':this.keys.pubkey}); + } + + r.derive = function(i){ + i = (i)?i:0; + var blob = (Crypto.util.hexToBytes(this.keys.pubkey)).concat(coinjs.numToBytes(i,4).reverse()); + + var j = new jsSHA(Crypto.util.bytesToHex(blob), 'HEX'); + var hash = j.getHMAC(Crypto.util.bytesToHex(r.chain_code), "HEX", "SHA-512", "HEX"); + + var il = new BigInteger(hash.slice(0, 64), 16); + var ir = Crypto.util.hexToBytes(hash.slice(64,128)); + + var ecparams = EllipticCurve.getSECCurveByName("secp256k1"); + var curve = ecparams.getCurve(); + + var k, key, pubkey, o; + + o = coinjs.clone(this); + o.chain_code = ir; + o.child_index = i; + + if(this.type=='private'){ + k = il.add(new BigInteger([0].concat(Crypto.util.hexToBytes(this.keys.privkey)))).mod(ecparams.getN()); + key = Crypto.util.bytesToHex(k.toByteArrayUnsigned()); + + pubkey = coinjs.newPubkey(key); + + o.keys = {'privkey':key, + 'pubkey':pubkey, + 'wif':coinjs.privkey2wif(key), + 'address':coinjs.pubkey2address(pubkey)}; + + } else if (this.type=='public'){ + q = ecparams.curve.decodePointHex(this.keys.pubkey); + var curvePt = ecparams.getG().multiply(il).add(q); + + var x = curvePt.getX().toBigInteger(); + var y = curvePt.getY().toBigInteger(); + + var publicKeyBytesCompressed = EllipticCurve.integerToBytes(x,32) + if (y.isEven()){ + publicKeyBytesCompressed.unshift(0x02) + } else { + publicKeyBytesCompressed.unshift(0x03) + } + pubkey = Crypto.util.bytesToHex(publicKeyBytesCompressed); + + o.keys = {'pubkey':pubkey, + 'address':coinjs.pubkey2address(pubkey)} + } else { + // fail + } + + o.parent_fingerprint = (ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(r.keys.pubkey),{asBytes:true}),{asBytes:true})).slice(0,4); + o.keys_extended = o.extend(); + + return o; + } + + r.master = function(pass) { + var seed = (pass) ? Crypto.SHA256(pass) : coinjs.newPrivkey(); + var hasher = new jsSHA(seed, 'HEX'); + var I = hasher.getHMAC("Bitcoin seed", "TEXT", "SHA-512", "HEX"); + + var privkey = Crypto.util.hexToBytes(I.slice(0, 64)); + var chain = Crypto.util.hexToBytes(I.slice(64, 128)); + + var hd = coinjs.hd(); + return hd.make({'depth':0, + 'parent_fingerprint':[0,0,0,0], + 'child_index':0, + 'chain_code':chain, + 'privkey':I.slice(0, 64), + 'pubkey':coinjs.newPubkey(I.slice(0, 64))}); + } + + r.make = function(data){ // { (int) depth, (array) parent_fingerprint, (int) child_index, (byte array) chain_code, (hex str) privkey, (hex str) pubkey} + var k = []; + + //depth + k.push(data.depth*1); + + //parent fingerprint + k = k.concat(data.parent_fingerprint); + + //child index + k = k.concat((coinjs.numToBytes(data.child_index, 4)).reverse()); + + // Chain code + k = k.concat(data.chain_code); + + var o = {}; // results + + // Extended HD Private Key + if(data.privkey){ + var prv = (coinjs.numToBytes(coinjs.hdkey.prv, 4)).reverse(); + prv = prv.concat(k); + prv.push(0x00); + prv = prv.concat(Crypto.util.hexToBytes(data.privkey)); + var hash = Crypto.SHA256( Crypto.SHA256(prv, { asBytes: true } ), { asBytes: true } ); + var checksum = hash.slice(0, 4); + var ret = prv.concat(checksum); + o.privkey = coinjs.base58encode(ret); + } + + // Extended HD Public Key + if(data.pubkey){ + var pub = (coinjs.numToBytes(coinjs.hdkey.pub, 4)).reverse(); + pub = pub.concat(k); + pub = pub.concat(Crypto.util.hexToBytes(data.pubkey)); + var hash = Crypto.SHA256( Crypto.SHA256(pub, { asBytes: true } ), { asBytes: true } ); + var checksum = hash.slice(0, 4); + var ret = pub.concat(checksum); + o.pubkey = coinjs.base58encode(ret); + } + return o; + } + + r.parse(); + return r; + } + /* start of script functions */ - coinjs.script = function(data) { var r = {}; @@ -1211,6 +1403,17 @@ else return bytes[0] + 256 * coinjs.bytesToNum(bytes.slice(1)); } + coinjs.uint = function(f, size) { + if (f.length < size) + throw new Error("not enough data"); + var n = 0; + for (var i = 0; i < size; i++) { + n *= 256; + n += f[i]; + } + return n; + } + coinjs.isArray = function(o){ return Object.prototype.toString.call(o) === '[object Array]'; } diff --git a/js/coinbin.js b/js/coinbin.js index c41bef3..5dcc8b9 100644 --- a/js/coinbin.js +++ b/js/coinbin.js @@ -331,6 +331,32 @@ $(document).ready(function() { } }); + /* new -> Hd address code */ + + $(".deriveHDbtn").click(function(){ + $("#verifyScript").val($("input[type='text']",$(this).parent().parent()).val()); + window.location = "#verify"; + $("#verifyBtn").click(); + }); + + $("#newHDKeysBtn").click(function(){ + coinjs.compressed = true; + var s = ($("#newHDBrainwallet").is(":checked")) ? $("#HDBrainwallet").val() : null; + var hd = coinjs.hd(); + var pair = hd.master(s); + $("#newHDxpub").val(pair.pubkey); + $("#newHDxprv").val(pair.privkey); + + }); + + $("#newHDBrainwallet").click(function(){ + if($(this).is(":checked")){ + $("#HDBrainwallet").removeClass("hidden"); + } else { + $("#HDBrainwallet").addClass("hidden"); + } + }); + /* new -> transaction code */ $("#recipients .addressAddTo").click(function(){ @@ -626,7 +652,9 @@ $(document).ready(function() { if(!decodeTransactionScript()){ if(!decodePrivKey()){ if(!decodePubKey()){ - $("#verifyStatus").removeClass('hidden').fadeOut().fadeIn(); + if(!decodeHDaddress()){ + $("#verifyStatus").removeClass('hidden').fadeOut().fadeIn(); + } } } } @@ -780,6 +808,50 @@ $(document).ready(function() { } } + function decodeHDaddress(){ + var s = $("#verifyScript").val(); + if(s.match(/^x[prv|pub]/)){ + try { + var hd = coinjs.hd(s); + $("#verifyHDaddress .hdKey").html(s); + $("#verifyHDaddress .chain_code").val(Crypto.util.bytesToHex(hd.chain_code)); + $("#verifyHDaddress .depth").val(hd.depth); + $("#verifyHDaddress .version").val('0x'+(hd.version).toString(16)); + $("#verifyHDaddress .child_index").val(hd.child_index); + $("#verifyHDaddress .hdwifkey").val((hd.keys.wif)?hd.keys.wif:''); + $("#verifyHDaddress .key_type").html((((hd.depth==0 && hd.child_index==0)?'Master':'Derived')+' '+hd.type).toLowerCase()); + $("#verifyHDaddress .parent_fingerprint").val(Crypto.util.bytesToHex(hd.parent_fingerprint)); + $("#verifyHDaddress .derived_data table tbody").html(""); + deriveHDaddress(); + $(".verifyLink").attr('href','?verify='+$("#verifyScript").val()); + $("#verifyHDaddress").removeClass("hidden"); + return true; + } catch (e) { + return false; + } + } + } + + function deriveHDaddress() { + var hd = coinjs.hd($("#verifyHDaddress .hdKey").html()); + var index_start = $("#verifyHDaddress .derivation_index_start").val()*1; + var index_end = $("#verifyHDaddress .derivation_index_end").val()*1; + var html = ''; + $("#verifyHDaddress .derived_data table tbody").html(""); + for(var i=index_start;i<=index_end;i++){ + var derived = hd.derive(i); + html += ''; + html += ''+i+''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + } + $(html).appendTo("#verifyHDaddress .derived_data table tbody"); + } + + /* sign code */ $("#signBtn").click(function(){ @@ -894,7 +966,8 @@ $(document).ready(function() { $(".pubkeyAdd").click(); } - $("#newKeysBtn").click(); + $("#newKeysBtn, #newHDKeysBtn").click(); + validateOutputAmount(); diff --git a/js/sha512.js b/js/sha512.js new file mode 100644 index 0000000..f98481a --- /dev/null +++ b/js/sha512.js @@ -0,0 +1,32 @@ +(function() {/* + A JavaScript implementation of the SHA family of hashes, as defined in FIPS + PUB 180-2 as well as the corresponding HMAC implementation as defined in + FIPS PUB 198a + + Copyright Brian Turek 2008-2012 + Distributed under the BSD License + See http://caligatio.github.com/jsSHA/ for more information + + Several functions taken from Paul Johnson +*/ +function n(a){throw a;}var q=null;function s(a,b){this.a=a;this.b=b}function u(a,b){var d=[],h=(1<>>5]|=(a.charCodeAt(g/b)&h)<<32-b-g%32;return{value:d,binLen:f}}function x(a){var b=[],d=a.length,h,f;0!==d%2&&n("String of HEX type must be in byte increments");for(h=0;h>>3]|=f<<24-4*(h%8);return{value:b,binLen:4*d}} +function B(a){var b=[],d=0,h,f,g,k,m;-1===a.search(/^[a-zA-Z0-9=+\/]+$/)&&n("Invalid character in base-64 string");h=a.indexOf("=");a=a.replace(/\=/g,"");-1!==h&&h