commit c0d740d2d4cd35ab18dc35e23936db42f17ad2ee Author: Stefan Thomas <justmoon@members.fsf.org> Date: Wed May 4 17:02:56 2011 +0100 Initial import diff --git a/address.js b/address.js new file mode 100644 index 0000000..ed38d79 --- /dev/null +++ b/address.js @@ -0,0 +1,47 @@ +Bitcoin.Address = function (bytes) { + if ("string" == typeof bytes) { + bytes = Bitcoin.Address.decodeString(bytes); + } + this.hash = bytes; +}; + +Bitcoin.Address.prototype.toString = function () { + // Get a copy of the hash + var hash = this.hash.slice(0); + + // Version + hash.unshift(0x00); + + var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); + + var bytes = hash.concat(checksum.slice(0,4)); + + return Bitcoin.Base58.encode(bytes); +}; + +Bitcoin.Address.prototype.getHashBase64 = function () { + return Crypto.util.bytesToBase64(this.hash); +}; + +Bitcoin.Address.decodeString = function (string) { + var bytes = Bitcoin.Base58.decode(string); + + var hash = bytes.slice(0, 21); + + var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); + + if (checksum[0] != bytes[21] || + checksum[1] != bytes[22] || + checksum[2] != bytes[23] || + checksum[3] != bytes[24]) { + throw "Checksum validation failed!"; + } + + var version = hash.shift(); + + if (version != 0) { + throw "Version "+version+" not supported!"; + } + + return hash; +}; diff --git a/base58.js b/base58.js new file mode 100644 index 0000000..88fd2b8 --- /dev/null +++ b/base58.js @@ -0,0 +1,67 @@ +(function () { + var B58 = Bitcoin.Base58 = { + alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", + base: BigInteger.valueOf(58), + + /** + * Convert a byte array to a base58-encoded string. + * + * Written by Mike Hearn for BitcoinJ. + * Copyright (c) 2011 Google Inc. + * + * Ported to JavaScript by Stefan Thomas. + */ + encode: function (input) { + var bi = new BigInteger(input); + var chars = []; + + while (bi.compareTo(B58.base) >= 0) { + var mod = bi.mod(B58.base); + chars.unshift(B58.alphabet[mod.intValue()]); + bi = bi.subtract(mod).divide(B58.base); + } + chars.unshift(B58.alphabet[bi.intValue()]); + + // Convert leading zeros too. + for (var i = 0; i < input.length; i++) { + if (input[i] == 0x00) { + chars.unshift(B58.alphabet[0]); + } else break; + } + + s = chars.join(''); + return s; + }, + + /** + * Convert a base58-encoded string to a byte array. + * + * Written by Mike Hearn for BitcoinJ. + * Copyright (c) 2011 Google Inc. + * + * Ported to JavaScript by Stefan Thomas. + */ + decode: function (input) { + bi = BigInteger.valueOf(0); + var leadingZerosNum = 0; + for (var i = input.length - 1; i >= 0; i--) { + var alphaIndex = B58.alphabet.indexOf(input[i]); + bi = bi.add(BigInteger.valueOf(alphaIndex) + .multiply(B58.base.pow(input.length - 1 -i))); + + // This counts leading zero bytes + if (input[i] == "1") leadingZerosNum++; + else leadingZerosNum = 0; + } + var bytes = bi.toByteArrayUnsigned(); + + // Remove leading zeros arbitrarily added by BigInteger + while (bytes[0] == 0) bytes.shift(); + + // Add right amount of leading zeros + while (leadingZerosNum-- > 0) bytes.unshift(0); + + return bytes; + } + }; +})(); diff --git a/bitcoin.js b/bitcoin.js new file mode 100755 index 0000000..42c5d13 --- /dev/null +++ b/bitcoin.js @@ -0,0 +1,164 @@ +var Bitcoin = {}; + + +function makeKeypair() +{ + // Generate private key + var n = ecparams.getN(); + var n1 = n.subtract(BigInteger.ONE); + var r = new BigInteger(n.bitLength(), rng); + + var privateKey = r.mod(n1).add(BigInteger.ONE); + + // Generate public key + var G = ecparams.getG(); + var publicPoint = G.multiply(privateKey); + + return {priv: privateKey, pubkey: publicPoint}; +}; + +function serializeTransaction(tx) +{ + var buffer = []; + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.version)])); + buffer = buffer.concat(numToVarInt(tx.ins.length)); + for (var i = 0; i < tx.ins.length; i++) { + var txin = tx.ins[i]; + buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash)); + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.index)])); + var scriptBytes = Crypto.util.base64ToBytes(txin.script); + buffer = buffer.concat(numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)])); + } + buffer = buffer.concat(numToVarInt(tx.outs.length)); + for (var i = 0; i < tx.outs.length; i++) { + var txout = tx.outs[i]; + var valueHex = (new BigInteger(txout.value, 10)).toString(16); + while (valueHex.length < 16) valueHex = "0" + valueHex; + buffer = buffer.concat(Crypto.util.hexToBytes(valueHex)); + var scriptBytes = Crypto.util.base64ToBytes(txout.script); + buffer = buffer.concat(numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); + } + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(tx.lock_time)])); + + return buffer; +}; + +var OP_CODESEPARATOR = 171; + +var SIGHASH_ALL = 1; +var SIGHASH_NONE = 2; +var SIGHASH_SINGLE = 3; +var SIGHASH_ANYONECANPAY = 80; + +function hashTransactionForSignature(scriptCode, tx, inIndex, hashType) +{ + // TODO: We need to actually deep copy here + var txTmp = tx; + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode = scriptCode.filter(function (val) { + return val !== OP_CODESEPARATOR; + }); + + // Blank out other inputs' signatures + for (var i = 0; i < txTmp.ins.length; i++) { + txTmp.ins[i].script = Crypto.util.bytesToBase64([]); + } + txTmp.ins[inIndex].script = Crypto.util.bytesToBase64(scriptCode); + + // Blank out some of the outputs + if ((hashType & 0x1f) == SIGHASH_NONE) { + txTmp.outs = []; + + // Let the others update at will + for (var i = 0; i < txTmp.ins.length; i++) + if (i != inIndex) + txTmp.ins[i].sequence = 0; + } else if ((hashType & 0x1f) == SIGHASH_SINGLE) { + // TODO: Implement + } + + // Blank out other inputs completely, not recommended for open transactions + if (hashType & SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]]; + } + + var buffer = serializeTransaction(txTmp); + + buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)])); + + return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); +}; + +function verifyTransactionSignature(tx) { + var hash = hashTransactionForSignature([], tx, 0, 0); + return Crypto.util.bytesToHex(hash); +}; + +function numToVarInt(i) +{ + // TODO: THIS IS TOTALLY UNTESTED! + if (i < 0xfd) { + // unsigned char + return [i]; + } else if (i <= 1<<16) { + // unsigned short (LE) + return [0xfd, i >>> 8, i & 255]; + } else if (i <= 1<<32) { + // unsigned int (LE) + return [0xfe].concat(Crypto.util.wordsToBytes([i])); + } else { + // unsigned long long (LE) + return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i])); + } +}; + +var testTx = { + "version":"1", + "lock_time":"0", + "block": { + "hash":"N/A", + "height":115806 + }, + "index":6, + "hash":"WUFzjKubG1kqfJWMb4qZdlhU2F3l5NGXN7AUg8Jwl14=", + "ins":[{ + "outpoint":{ + "hash":"nqcbMM1oRhfLdZga11q7x0CpUMujm+vtxHXO9V0gnwE=", + "index":0 + }, + "script":"RzBEAiB2XXkx1pca9SlfCmCGNUVf+h2sAFBttcxG1VnypIcvEgIgXrOp7LSdYBYp3nPsQAz8BOLD3K4pAlXfZImP1rkzk2EBQQRi7NcODzNfnVqLtG79Axp5UF6EhFIhCmzqKqssfKpfCIOmzCuXEeDFUFvFzeGLJx5N+wp2qRS1TqYezGD3yERk", + "sequence":4294967295 + }], + "outs":[{ + "value":"3000000000", + "script":"dqkUBLZwqhAPRVgZvwI8MN5gLHbU8NOIrA==" + },{ + "value":"25937000000", + "script":"dqkUQ82gJ0O5vOBg6yK5/yorLLV5zLKIrA==" + }] +}; + +/* TODO: Make this stuff into test cases ;) +$(function () { + var key = new Bitcoin.ECKey(Crypto.util.hexToBytes("5c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4a")); + key = new Bitcoin.ECKey(Crypto.util.hexToBytes("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19")); + //console.log(key.getBitcoinAddress().toString()); + var message = Crypto.util.hexToBytes("2aec28d323ee7b06a799d540d224b351161fe48967174ca5e43164e86137da11"); + message = [0]; + //var out = key.sign(message); + //console.log("pubkey: "+Crypto.util.bytesToHex(key.getPub())); + //console.log("sig: "+Crypto.util.bytesToHex(out)); + + //console.log(key.verify(message, out)); + + //console.log(Bitcoin.ECDSA.verify(message, Crypto.util.hexToBytes("3046022100dffbc26774fc841bbe1c1362fd643609c6e42dcb274763476d87af2c0597e89e022100c59e3c13b96b316cae9fa0ab0260612c7a133a6fe2b3445b6bf80b3123bf274d"), Crypto.util.hexToBytes("0401de173aa944eacf7e44e5073baca93fb34fe4b7897a1c82c92dfdc8a1f75ef58cd1b06e8052096980cb6e1ad6d3df143c34b3d7394bae2782a4df570554c2fb"))); + + //console.log(Bitcoin.ECDSA.verify(Crypto.util.hexToBytes("230aba77ccde46bb17fcb0295a92c0cc42a6ea9f439aaadeb0094625f49e6ed8"), Crypto.util.hexToBytes("3046022100a3ee5408f0003d8ef00ff2e0537f54ba09771626ff70dca1f01296b05c510e85022100d4dc70a5bb50685b65833a97e536909a6951dd247a2fdbde6688c33ba6d6407501"),Crypto.util.hexToBytes("04a19c1f07c7a0868d86dbb37510305843cc730eb3bea8a99d92131f44950cecd923788419bfef2f635fad621d753f30d4b4b63b29da44b4f3d92db974537ad5a4"))); + //console.log(Bitcoin.ECDSA.verify(Crypto.util.hexToBytes("c2c75bb77d7a5acddceb1d45ceef58e7451fd0d3abc9d4c16df7848eefafe00d"), Crypto.util.hexToBytes("3045022100ff9362dadcbf1f6ef954bc8eb27144bbb4f49abd32be1eb04c311151dcf4bcf802205112c2ca6a25aefb8be98bf460c5a9056c01253f31e118d80b81ec9604e3201a01"),Crypto.util.hexToBytes("04fe62ce7892ec209310c176ef7f06565865e286e8699e884603657efa9aa51086785099d544d4e04f1f7b4b065205c1783fade8daf4ba1e0d1962292e8eb722cd"))); +}); +*/ diff --git a/crypto-js/crypto-min.js b/crypto-js/crypto-min.js new file mode 100755 index 0000000..dc5b80d --- /dev/null +++ b/crypto-js/crypto-min.js @@ -0,0 +1,7 @@ +/* + * Crypto-JS v2.0.0 + * http://code.google.com/p/crypto-js/ + * Copyright (c) 2009, Jeff Mott. All rights reserved. + * http://code.google.com/p/crypto-js/wiki/License + */ +(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})(); \ No newline at end of file diff --git a/crypto-js/crypto.js b/crypto-js/crypto.js new file mode 100755 index 0000000..1f0f089 --- /dev/null +++ b/crypto-js/crypto.js @@ -0,0 +1,160 @@ +/*! + * Crypto-JS v2.0.0 + * http://code.google.com/p/crypto-js/ + * Copyright (c) 2009, Jeff Mott. All rights reserved. + * http://code.google.com/p/crypto-js/wiki/License + */ +(function(){ + +var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// Global Crypto object +var Crypto = window.Crypto = {}; + +// Crypto utilities +var util = Crypto.util = { + + // Bit-wise rotate left + rotl: function (n, b) { + return (n << b) | (n >>> (32 - b)); + }, + + // Bit-wise rotate right + rotr: function (n, b) { + return (n << (32 - b)) | (n >>> b); + }, + + // Swap big-endian to little-endian and vice versa + endian: function (n) { + + // If number given, swap endian + if (n.constructor == Number) { + return util.rotl(n, 8) & 0x00FF00FF | + util.rotl(n, 24) & 0xFF00FF00; + } + + // Else, assume array and swap all items + for (var i = 0; i < n.length; i++) + n[i] = util.endian(n[i]); + return n; + + }, + + // Generate an array of any length of random bytes + randomBytes: function (n) { + for (var bytes = []; n > 0; n--) + bytes.push(Math.floor(Math.random() * 256)); + return bytes; + }, + + // Convert a byte array to big-endian 32-bit words + bytesToWords: function (bytes) { + for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) + words[b >>> 5] |= bytes[i] << (24 - b % 32); + return words; + }, + + // Convert big-endian 32-bit words to a byte array + wordsToBytes: function (words) { + for (var bytes = [], b = 0; b < words.length * 32; b += 8) + bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); + return bytes; + }, + + // Convert a byte array to a hex string + bytesToHex: function (bytes) { + for (var hex = [], i = 0; i < bytes.length; i++) { + hex.push((bytes[i] >>> 4).toString(16)); + hex.push((bytes[i] & 0xF).toString(16)); + } + return hex.join(""); + }, + + // Convert a hex string to a byte array + hexToBytes: function (hex) { + for (var bytes = [], c = 0; c < hex.length; c += 2) + bytes.push(parseInt(hex.substr(c, 2), 16)); + return bytes; + }, + + // Convert a byte array to a base-64 string + bytesToBase64: function (bytes) { + + // Use browser-native function if it exists + if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes)); + + for(var base64 = [], i = 0; i < bytes.length; i += 3) { + var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + for (var j = 0; j < 4; j++) { + if (i * 8 + j * 6 <= bytes.length * 8) + base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)); + else base64.push("="); + } + } + + return base64.join(""); + + }, + + // Convert a base-64 string to a byte array + base64ToBytes: function (base64) { + + // Use browser-native function if it exists + if (typeof atob == "function") return Binary.stringToBytes(atob(base64)); + + // Remove non-base-64 characters + base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); + + for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) { + if (imod4 == 0) continue; + bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) | + (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))); + } + + return bytes; + + } + +}; + +// Crypto mode namespace +Crypto.mode = {}; + +// Crypto character encodings +var charenc = Crypto.charenc = {}; + +// UTF-8 encoding +var UTF8 = charenc.UTF8 = { + + // Convert a string to a byte array + stringToBytes: function (str) { + return Binary.stringToBytes(unescape(encodeURIComponent(str))); + }, + + // Convert a byte array to a string + bytesToString: function (bytes) { + return decodeURIComponent(escape(Binary.bytesToString(bytes))); + } + +}; + +// Binary encoding +var Binary = charenc.Binary = { + + // Convert a string to a byte array + stringToBytes: function (str) { + for (var bytes = [], i = 0; i < str.length; i++) + bytes.push(str.charCodeAt(i)); + return bytes; + }, + + // Convert a byte array to a string + bytesToString: function (bytes) { + for (var str = [], i = 0; i < bytes.length; i++) + str.push(String.fromCharCode(bytes[i])); + return str.join(""); + } + +}; + +})(); diff --git a/crypto-js/ripemd160.js b/crypto-js/ripemd160.js new file mode 100755 index 0000000..ff4b2a6 --- /dev/null +++ b/crypto-js/ripemd160.js @@ -0,0 +1,170 @@ +/*! + * Crypto-JS v2.0.0 + * http://code.google.com/p/crypto-js/ + * Copyright (c) 2009, Jeff Mott. All rights reserved. + * http://code.google.com/p/crypto-js/wiki/License + * + * A JavaScript implementation of the RIPEMD-160 Algorithm + * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/ + * Ported to Crypto-JS by Stefan Thomas. + */ + +(function () { + // Shortcuts + var C = Crypto, + util = C.util, + charenc = C.charenc, + UTF8 = charenc.UTF8, + Binary = charenc.Binary; + + // Convert a byte array to little-endian 32-bit words + util.bytesToLWords = function (bytes) { + + var output = Array(bytes.length >> 2); + for (var i = 0; i < output.length; i++) + output[i] = 0; + for (var i = 0; i < bytes.length * 8; i += 8) + output[i>>5] |= (bytes[i / 8] & 0xFF) << (i%32); + return output; + }; + + // Convert little-endian 32-bit words to a byte array + util.lWordsToBytes = function (words) { + var output = []; + for (var i = 0; i < words.length * 32; i += 8) + output.push((words[i>>5] >>> (i % 32)) & 0xff); + return output; + }; + + // Public API + var RIPEMD160 = C.RIPEMD160 = function (message, options) { + var digestbytes = util.lWordsToBytes(RIPEMD160._rmd160(message)); + return options && options.asBytes ? digestbytes : + options && options.asString ? Binary.bytesToString(digestbytes) : + util.bytesToHex(digestbytes); + }; + + // The core + RIPEMD160._rmd160 = function (message) + { + // Convert to byte array + if (message.constructor == String) message = UTF8.stringToBytes(message); + + var x = util.bytesToLWords(message), + len = message.length * 8; + + /* append padding */ + x[len >> 5] |= 0x80 << (len % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var h0 = 0x67452301; + var h1 = 0xefcdab89; + var h2 = 0x98badcfe; + var h3 = 0x10325476; + var h4 = 0xc3d2e1f0; + + for (var i = 0; i < x.length; i += 16) { + var T; + var A1 = h0, B1 = h1, C1 = h2, D1 = h3, E1 = h4; + var A2 = h0, B2 = h1, C2 = h2, D2 = h3, E2 = h4; + for (var j = 0; j <= 79; ++j) { + T = safe_add(A1, rmd160_f(j, B1, C1, D1)); + T = safe_add(T, x[i + rmd160_r1[j]]); + T = safe_add(T, rmd160_K1(j)); + T = safe_add(bit_rol(T, rmd160_s1[j]), E1); + A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T; + T = safe_add(A2, rmd160_f(79-j, B2, C2, D2)); + T = safe_add(T, x[i + rmd160_r2[j]]); + T = safe_add(T, rmd160_K2(j)); + T = safe_add(bit_rol(T, rmd160_s2[j]), E2); + A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T; + } + T = safe_add(h1, safe_add(C1, D2)); + h1 = safe_add(h2, safe_add(D1, E2)); + h2 = safe_add(h3, safe_add(E1, A2)); + h3 = safe_add(h4, safe_add(A1, B2)); + h4 = safe_add(h0, safe_add(B1, C2)); + h0 = T; + } + return [h0, h1, h2, h3, h4]; + } + + function rmd160_f(j, x, y, z) + { + return ( 0 <= j && j <= 15) ? (x ^ y ^ z) : + (16 <= j && j <= 31) ? (x & y) | (~x & z) : + (32 <= j && j <= 47) ? (x | ~y) ^ z : + (48 <= j && j <= 63) ? (x & z) | (y & ~z) : + (64 <= j && j <= 79) ? x ^ (y | ~z) : + "rmd160_f: j out of range"; + } + function rmd160_K1(j) + { + return ( 0 <= j && j <= 15) ? 0x00000000 : + (16 <= j && j <= 31) ? 0x5a827999 : + (32 <= j && j <= 47) ? 0x6ed9eba1 : + (48 <= j && j <= 63) ? 0x8f1bbcdc : + (64 <= j && j <= 79) ? 0xa953fd4e : + "rmd160_K1: j out of range"; + } + function rmd160_K2(j) + { + return ( 0 <= j && j <= 15) ? 0x50a28be6 : + (16 <= j && j <= 31) ? 0x5c4dd124 : + (32 <= j && j <= 47) ? 0x6d703ef3 : + (48 <= j && j <= 63) ? 0x7a6d76e9 : + (64 <= j && j <= 79) ? 0x00000000 : + "rmd160_K2: j out of range"; + } + var rmd160_r1 = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 + ]; + var rmd160_r2 = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 + ]; + var rmd160_s1 = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 + ]; + var rmd160_s2 = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 + ]; + + /* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ + function safe_add(x, y) + { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + /* + * Bitwise rotate a 32-bit number to the left. + */ + function bit_rol(num, cnt) + { + return (num << cnt) | (num >>> (32 - cnt)); + } +})(); diff --git a/crypto-js/sha256-min.js b/crypto-js/sha256-min.js new file mode 100755 index 0000000..d3a4118 --- /dev/null +++ b/crypto-js/sha256-min.js @@ -0,0 +1,7 @@ +/* + * Crypto-JS v2.0.0 + * http://code.google.com/p/crypto-js/ + * Copyright (c) 2009, Jeff Mott. All rights reserved. + * http://code.google.com/p/crypto-js/wiki/License + */ +(function(){var g=Crypto,b=g.util,c=g.charenc,f=c.UTF8,e=c.Binary;var a=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];var d=g.SHA256=function(j,h){var i=b.wordsToBytes(d._sha256(j));return h&&h.asBytes?i:h&&h.asString?e.bytesToString(i):b.bytesToHex(i)};d._sha256=function(q){if(q.constructor==String){q=f.stringToBytes(q)}var y=b.bytesToWords(q),z=q.length*8,r=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],s=[],K,J,I,G,F,E,D,C,B,A,p,o;y[z>>5]|=128<<(24-z%32);y[((z+64>>9)<<4)+15]=z;for(var B=0;B<y.length;B+=16){K=r[0];J=r[1];I=r[2];G=r[3];F=r[4];E=r[5];D=r[6];C=r[7];for(var A=0;A<64;A++){if(A<16){s[A]=y[A+B]}else{var n=s[A-15],u=s[A-2],M=((n<<25)|(n>>>7))^((n<<14)|(n>>>18))^(n>>>3),L=((u<<15)|(u>>>17))^((u<<13)|(u>>>19))^(u>>>10);s[A]=M+(s[A-7]>>>0)+L+(s[A-16]>>>0)}var t=F&E^~F&D,k=K&J^K&I^J&I,x=((K<<30)|(K>>>2))^((K<<19)|(K>>>13))^((K<<10)|(K>>>22)),v=((F<<26)|(F>>>6))^((F<<21)|(F>>>11))^((F<<7)|(F>>>25));p=(C>>>0)+v+t+(a[A])+(s[A]>>>0);o=x+k;C=D;D=E;E=F;F=G+p;G=I;I=J;J=K;K=p+o}r[0]+=K;r[1]+=J;r[2]+=I;r[3]+=G;r[4]+=F;r[5]+=E;r[6]+=D;r[7]+=C}return r};d._blocksize=16})(); \ No newline at end of file diff --git a/crypto-js/sha256.js b/crypto-js/sha256.js new file mode 100755 index 0000000..c5a2d01 --- /dev/null +++ b/crypto-js/sha256.js @@ -0,0 +1,133 @@ +/*! + * Crypto-JS v2.0.0 + * http://code.google.com/p/crypto-js/ + * Copyright (c) 2009, Jeff Mott. All rights reserved. + * http://code.google.com/p/crypto-js/wiki/License + */ +(function(){ + +// Shortcuts +var C = Crypto, + util = C.util, + charenc = C.charenc, + UTF8 = charenc.UTF8, + Binary = charenc.Binary; + +// Constants +var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ]; + +// Public API +var SHA256 = C.SHA256 = function (message, options) { + var digestbytes = util.wordsToBytes(SHA256._sha256(message)); + return options && options.asBytes ? digestbytes : + options && options.asString ? Binary.bytesToString(digestbytes) : + util.bytesToHex(digestbytes); +}; + +// The core +SHA256._sha256 = function (message) { + + // Convert to byte array + if (message.constructor == String) message = UTF8.stringToBytes(message); + /* else, assume byte array already */ + + var m = util.bytesToWords(message), + l = message.length * 8, + H = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ], + w = [], + a, b, c, d, e, f, g, h, i, j, + t1, t2; + + // Padding + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for (var i = 0; i < m.length; i += 16) { + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + + for (var j = 0; j < 64; j++) { + + if (j < 16) w[j] = m[j + i]; + else { + + var gamma0x = w[j - 15], + gamma1x = w[j - 2], + gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ + ((gamma0x << 14) | (gamma0x >>> 18)) ^ + (gamma0x >>> 3), + gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ + ((gamma1x << 13) | (gamma1x >>> 19)) ^ + (gamma1x >>> 10); + + w[j] = gamma0 + (w[j - 7] >>> 0) + + gamma1 + (w[j - 16] >>> 0); + + } + + var ch = e & f ^ ~e & g, + maj = a & b ^ a & c ^ b & c, + sigma0 = ((a << 30) | (a >>> 2)) ^ + ((a << 19) | (a >>> 13)) ^ + ((a << 10) | (a >>> 22)), + sigma1 = ((e << 26) | (e >>> 6)) ^ + ((e << 21) | (e >>> 11)) ^ + ((e << 7) | (e >>> 25)); + + + t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0); + t2 = sigma0 + maj; + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + + } + + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; + + } + + return H; + +}; + +// Package private blocksize +SHA256._blocksize = 16; + +})(); diff --git a/ecdsa.js b/ecdsa.js new file mode 100644 index 0000000..1c66791 --- /dev/null +++ b/ecdsa.js @@ -0,0 +1,294 @@ +function integerToBytes(i, len) { + var bytes = i.toByteArrayUnsigned(); + + if (len < bytes.length) { + bytes = bytes.slice(bytes.length-len); + } else while (len > bytes.length) { + bytes.unshift(0); + } + + return bytes; +}; + +ECFieldElementFp.prototype.getByteLength = function () { + return Math.floor((this.toBigInteger().bitLength() + 7) / 8); +}; + +ECPointFp.prototype.getEncoded = function (compressed) { + var x = this.getX().toBigInteger(); + var y = this.getY().toBigInteger(); + + if (compressed) { + var PC; + } + + var len = this.getX().getByteLength(); + + var enc = integerToBytes(x, len); + + if (compressed) { + if (y.testBit(0)) { + enc.unshift(0x02); + } else { + enc.unshift(0x03); + } + } else { + enc.unshift(0x04); + enc = enc.concat(integerToBytes(y, len)); + } + return enc; +}; + +ECPointFp.decodeFrom = function (curve, enc) { + var type = enc.shift(); + var xBa = enc.slice(0, enc.length/2); + xBa.unshift(0); + var x = new BigInteger(xBa); + var yBa = enc.slice(enc.length/2, enc.length); + yBa.unshift(0); + var y = new BigInteger(yBa); + return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)); +}; + +ECPointFp.prototype.add2D = function (b) { + if(this.isInfinity()) return b; + if(b.isInfinity()) return this; + + if (this.x.equals(b.x)) { + if (this.y.equals(b.y)) { + // this = b, i.e. this must be doubled + return this.twice(); + } + // this = -b, i.e. the result is the point at infinity + return this.curve.getInfinity(); + } + + var x_x = b.x.subtract(this.x); + var y_y = b.y.subtract(this.y); + var gamma = y_y.divide(x_x); + /*console.log("b.y: ", Crypto.util.bytesToHex(b.y.toBigInteger().toByteArrayUnsigned())); +console.log("this.y: ", Crypto.util.bytesToHex(this.y.toBigInteger().toByteArrayUnsigned())); +console.log("b.y-this.y: ", Crypto.util.bytesToHex(b.y.subtract(this.y).toBigInteger().toByteArrayUnsigned()));*/ + var x3 = gamma.square().subtract(this.x).subtract(b.x); + var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + + return new ECPointFp(this.curve, x3, y3); +}; + +ECPointFp.prototype.twice2D = function () { + if (this.isInfinity()) return this; + if (this.y.toBigInteger().signum() == 0) { + // if y1 == 0, then (x1, y1) == (x1, -y1) + // and hence this = -this and thus 2(x1, y1) == infinity + return this.curve.getInfinity(); + } + + var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); + var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); + var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO)); + + var x3 = gamma.square().subtract(this.x.multiply(TWO)); + var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + + return new ECPointFp(this.curve, x3, y3); +}; + +ECPointFp.prototype.multiply2D = function (k) { + if(this.isInfinity()) return this; + if(k.signum() == 0) return this.curve.getInfinity(); + + var e = k; + var h = e.multiply(new BigInteger("3")); + + var neg = this.negate(); + var R = this; + + var i; + for (i = h.bitLength() - 2; i > 0; --i) { + R = R.twice(); + + var hBit = h.testBit(i); + var eBit = e.testBit(i); + + if (hBit != eBit) { + R = R.add2D(hBit ? this : neg); + } + } + + return R; +}; + +function dmp(v) { + if (!(v instanceof BigInteger)) v = v.toBigInteger(); + return Crypto.util.bytesToHex(v.toByteArrayUnsigned()); +}; + +Bitcoin.ECDSA = (function () { + var ecparams = getSECCurveByName("secp256k1"); + var rng = new SecureRandom(); + + function implShamirsTrick(P, k, Q, l) + { + var m = Math.max(k.bitLength(), l.bitLength()); + var Z = P.add2D(Q); + var R = P.curve.getInfinity(); + + console.log(P.curve, Q.curve); + console.log("Px: ", Crypto.util.bytesToHex(P.x.toBigInteger().toByteArrayUnsigned())); + console.log("Py: ", Crypto.util.bytesToHex(P.y.toBigInteger().toByteArrayUnsigned())); + console.log("Pz: ", Crypto.util.bytesToHex(P.z.toByteArrayUnsigned())); + console.log("Qx: ", Crypto.util.bytesToHex(Q.x.toBigInteger().toByteArrayUnsigned())); + console.log("Qy: ", Crypto.util.bytesToHex(Q.y.toBigInteger().toByteArrayUnsigned())); + console.log("Qz: ", Crypto.util.bytesToHex(Q.z.toByteArrayUnsigned())); + console.log("Zx: ", Crypto.util.bytesToHex(Z.x.toBigInteger().toByteArrayUnsigned())); + console.log("Zy: ", Crypto.util.bytesToHex(Z.y.toBigInteger().toByteArrayUnsigned())); + console.log("Zz: ", Crypto.util.bytesToHex(Z.z.toByteArrayUnsigned())); + + for (var i = m - 1; i >= 0; --i) { + R = R.twice2D(); + + R.z = BigInteger.ONE; + + if (k.testBit(i)) { + if (l.testBit(i)) { + R = R.add2D(Z); + if (i > (m-5)) console.log("RC: 1"); + } else { + R = R.add2D(P); + if (i > (m-5)) console.log("RC: 2"); + } + } else { + if (l.testBit(i)) { + R = R.add2D(Q); + if (i > (m-5)) console.log("RC: 3"); + } else { + if (i > (m-5)) console.log("RC: 4"); + } + } + if (i > (m-5)) { + console.log("Rx: ", Crypto.util.bytesToHex(R.x.toBigInteger().toByteArrayUnsigned())); + console.log("Ry: ", Crypto.util.bytesToHex(R.y.toBigInteger().toByteArrayUnsigned())); + console.log("Rz: ", Crypto.util.bytesToHex(R.z.toByteArrayUnsigned())); + } + } + + return R; + }; + + var ECDSA = { + getBigRandom: function (limit) { + return new BigInteger(limit.bitLength(), rng) + .mod(limit.subtract(BigInteger.ONE)) + .add(BigInteger.ONE) + ; + }, + sign: function (hash, priv) { + var d = priv; + var n = ecparams.getN(); + hash.unshift(0) + var e = new BigInteger(hash); + + console.log("signhash: "+ Crypto.util.bytesToHex(hash)); + console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned())); + + do { + var k = ECDSA.getBigRandom(n); + var G = ecparams.getG(); + var Q = G.multiply(k); + var r = Q.getX().toBigInteger().mod(n); + } while (r.compareTo(BigInteger.ZERO) <= 0); + + console.log("k: "+ Crypto.util.bytesToHex(k.toByteArrayUnsigned())); + console.log("r: "+ Crypto.util.bytesToHex(r.toByteArrayUnsigned())); + + var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); + + console.log("d*r: "+ Crypto.util.bytesToHex(d.multiply(r).toByteArrayUnsigned())); + console.log("e+d*r: "+ Crypto.util.bytesToHex(e.add(d.multiply(r)).toByteArrayUnsigned())); + console.log("s: "+ Crypto.util.bytesToHex(s.toByteArrayUnsigned())); + + var rBa = r.toByteArrayUnsigned(); + var sBa = s.toByteArrayUnsigned(); + + var sequence = []; + sequence.push(0x02); // INTEGER + sequence.push(rBa.length); + sequence = sequence.concat(rBa); + + sequence.push(0x02); // INTEGER + sequence.push(sBa.length); + sequence = sequence.concat(sBa); + + sequence.unshift(sequence.length); + sequence.unshift(0x30) // SEQUENCE + + return sequence; + }, + + verify: function (hash, sig, pubkey) { + var cursor; + if (sig[0] != 0x30) + throw new Error("Signature not a valid DERSequence"); + + cursor = 2; + if (sig[cursor] != 0x02) + throw new Error("First element in signature must be a DERInteger");; + var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); + + cursor += 2+sig[cursor+1]; + if (sig[cursor] != 0x02) + throw new Error("Second element in signature must be a DERInteger"); + var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]); + + cursor += 2+sig[cursor+1]; + + //if (cursor != sig.length) + // throw new Error("Extra bytes in signature"); + + var n = ecparams.getN(); + hash.unshift(0) + var e = new BigInteger(hash); + + console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned())); + + var r = new BigInteger(rBa); + var s = new BigInteger(sBa); + + if (r.compareTo(BigInteger.ONE) < 0 || + r.compareTo(n) >= 0) + return false; + + if (s.compareTo(BigInteger.ONE) < 0 || + s.compareTo(n) >= 0) + return false; + + var c = s.modInverse(n); + + var u1 = e.multiply(c).mod(n); + var u2 = r.multiply(c).mod(n); + + console.log("r: "+ Crypto.util.bytesToHex(r.toByteArrayUnsigned())); + console.log("u1: "+ Crypto.util.bytesToHex(u1.toByteArrayUnsigned())); + console.log("u2: "+ Crypto.util.bytesToHex(u2.toByteArrayUnsigned())); + + var G = ecparams.getG(); + var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey); + + console.log("G.x: ", Crypto.util.bytesToHex(G.x.toBigInteger().toByteArrayUnsigned())); + console.log("G.y: ", Crypto.util.bytesToHex(G.y.toBigInteger().toByteArrayUnsigned())); + console.log("Q.x: ", Crypto.util.bytesToHex(Q.x.toBigInteger().toByteArrayUnsigned())); + console.log("Q.y: ", Crypto.util.bytesToHex(Q.y.toBigInteger().toByteArrayUnsigned())); + + var point = implShamirsTrick(G, u1, Q, u2); + + console.log("P.x: ", Crypto.util.bytesToHex(point.x.toBigInteger().toByteArrayUnsigned())); + console.log("P.y: ", Crypto.util.bytesToHex(point.y.toBigInteger().toByteArrayUnsigned())); + + var v = point.x.toBigInteger().mod(n); + + return v.equals(r); + } + }; + + return ECDSA; +})(); diff --git a/eckey.js b/eckey.js new file mode 100644 index 0000000..3ebd5f7 --- /dev/null +++ b/eckey.js @@ -0,0 +1,56 @@ +Bitcoin.ECKey = (function () { + var ECDSA = Bitcoin.ECDSA; + var ecparams = getSECCurveByName("secp256k1"); + var rng = new SecureRandom(); + + var ECKey = function (input) { + if (!input) { + // Generate new key + var n = ecparams.getN(); + this.priv = ECDSA.getBigRandom(n); + } else if (input instanceof BigInteger) { + // Input is a private key value + this.priv = input; + } else if (Bitcoin.Util.isArray(input)) { + this.priv = new BigInteger(input); + } else if ("string" == typeof input) { + this.priv = new BigInteger(Crypto.util.base64ToBytes(input)); + } + }; + + ECKey.prototype.getPub = function () { + if (this.pub) return this.pub; + + return this.pub = ecparams.getG().multiply(this.priv).getEncoded(); + }; + + ECKey.prototype.getPubKeyHash = function () { + if (this.pubKeyHash) return this.pubKeyHash; + + return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub()); + }; + + ECKey.prototype.getBitcoinAddress = function () { + var hash = this.getPubKeyHash(); + var addr = new Bitcoin.Address(hash); + return addr; + }; + + ECKey.prototype.toString = function (format) { + if (format === "base64") { + return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()); + } else { + return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned()); + } + }; + + ECKey.prototype.sign = function (hash) { + return ECDSA.sign(hash, this.priv); + }; + + ECKey.prototype.verify = function (hash, sig) { + return ECDSA.verify(hash, sig, this.getPub()); + }; + + return ECKey; +})(); diff --git a/events/eventemitter.js b/events/eventemitter.js new file mode 100644 index 0000000..85d9dce --- /dev/null +++ b/events/eventemitter.js @@ -0,0 +1,98 @@ +/** + * EventEmitter Mixin + * + * Designed to be used in conjunction with a mixin "augment" function, + * such as http://chamnapchhorn.blogspot.com/2009/05/javascript-mixins.html + * + * @usage augment(MyClass, EventEmitter); + * my_inst = new MyClass(); + * my_inst.on('someEvent', function(e){ console.dir(e); }); + * my_inst.trigger('someEvent', {eventProp:'value'}); + * + * @example + * // create a 'class' + * MyClass = function() {} + * // augment it with EventEmitter + * EventEmitter.augment(MyClass.prototype); + * // create a method, which triggers an event + * MyClass.prototype.scrollComplete = function() { + * this.trigger('scrolled', {baz:'eck'}); + * }; + * + * // this callback is pulled out into a named function so that we can unbind it + * var callback = function(e) { + * console.log('the scrolled event was fired! this.foo='+this.foo+', e.baz='+e.baz); + * }; + * // create an instance of th class + * var myinstance = new MyClass(); + * // set a property on the instance + * myinstance.foo = 'bar'; + * // bind to the scrollComplete event + * myinstance.on('scrolled', callback, myinstance); + * // fire the method, which should trigger the event and therefore our callback + * myinstance.scrollComplete(); + * // unbind the event, so that our callback should not get called + * myinstance.removeListener('scrolled', callback); + * // this should now not fire the callback + * myinstance.scrollComplete(); + */ +var EventEmitter = function() {}; +/** + * Bind a callback to an event, with an option scope context + * + * @param {string} name the name of the event + * @param {function} callback the callback function to fire when the event is triggered + * @param {object} context the scope to use for the callback (which will become 'this' inside the callback) + */ +EventEmitter.prototype.on = function(name, callback, context) { + if (!context) context = this; + if (!this._listeners) this._listeners = {}; + if (!this._listeners[name]) this._listeners[name] = []; + if (!this._unbinders) this._unbinders = {}; + if (!this._unbinders[name]) this._unbinders[name] = []; + var f = function(e) { + callback.apply(context, [e]); + }; + this._unbinders[name].push(callback); + this._listeners[name].push(f); +}; +/** + * Trigger an event, firing all bound callbacks + * + * @param {string} name the name of the event + * @param {object} event the event object to be passed through to the callback + */ +EventEmitter.prototype.trigger = function(name, event) { + if (event === undefined) event = {} + if (!this._listeners) this._listeners = {}; + if (!this._listeners[name]) return; + var i = this._listeners[name].length; + while (i--) this._listeners[name][i](event); +}; +/** + * Remove a bound listener + * + * @param {string} name the name of the event + * @param {object} event the event object to be passed through to the callback + */ +EventEmitter.prototype.removeListener = function(name, callback) { + if (!this._unbinders) this._unbinders = {}; + if (!this._unbinders[name]) return; + var i = this._unbinders[name].length; + while (i--) { + if (this._unbinders[name][i] === callback) { + this._unbinders[name].splice(i, 1); + this._listeners[name].splice(i, 1); + } + } +}; +/** + * Augment an object with the EventEmitter mixin + * + * @param {object} obj The object to be augmented (often an object's protoype) + */ +EventEmitter.augment = function(obj) { + for (var method in EventEmitter.prototype) { + if (!obj[method]) obj[method] = EventEmitter.prototype[method]; + } +}; diff --git a/jsbn/ec.js b/jsbn/ec.js new file mode 100755 index 0000000..0e45f05 --- /dev/null +++ b/jsbn/ec.js @@ -0,0 +1,321 @@ +// Basic Javascript Elliptic Curve implementation +// Ported loosely from BouncyCastle's Java EC code +// Only Fp curves implemented for now + +// Requires jsbn.js and jsbn2.js + +// ---------------- +// ECFieldElementFp + +// constructor +function ECFieldElementFp(q,x) { + this.x = x; + // TODO if(x.compareTo(q) >= 0) error + this.q = q; +} + +function feFpEquals(other) { + if(other == this) return true; + return (this.q.equals(other.q) && this.x.equals(other.x)); +} + +function feFpToBigInteger() { + return this.x; +} + +function feFpNegate() { + return new ECFieldElementFp(this.q, this.x.negate().mod(this.q)); +} + +function feFpAdd(b) { + return new ECFieldElementFp(this.q, this.x.add(b.toBigInteger()).mod(this.q)); +} + +function feFpSubtract(b) { + /*console.log("b.y (int): ", Crypto.util.bytesToHex(this.x.toByteArrayUnsigned())); + console.log("this.y (int): ", Crypto.util.bytesToHex(b.toBigInteger().toByteArrayUnsigned())); + console.log("b.y-this.y (premod): ", Crypto.util.bytesToHex(this.x.subtract(b.toBigInteger()).toByteArrayUnsigned()));*/ + return new ECFieldElementFp(this.q, this.x.subtract(b.toBigInteger()).mod(this.q)); +} + +function feFpMultiply(b) { + return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger()).mod(this.q)); +} + +function feFpSquare() { + return new ECFieldElementFp(this.q, this.x.square().mod(this.q)); +} + +function feFpDivide(b) { + //console.log("x: ", Crypto.util.bytesToHex(this.x.toByteArrayUnsigned())); + return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(this.q)); +} + +ECFieldElementFp.prototype.equals = feFpEquals; +ECFieldElementFp.prototype.toBigInteger = feFpToBigInteger; +ECFieldElementFp.prototype.negate = feFpNegate; +ECFieldElementFp.prototype.add = feFpAdd; +ECFieldElementFp.prototype.subtract = feFpSubtract; +ECFieldElementFp.prototype.multiply = feFpMultiply; +ECFieldElementFp.prototype.square = feFpSquare; +ECFieldElementFp.prototype.divide = feFpDivide; + +// ---------------- +// ECPointFp + +// constructor +function ECPointFp(curve,x,y,z) { + this.curve = curve; + this.x = x; + this.y = y; + // Projective coordinates: either zinv == null or z * zinv == 1 + // z and zinv are just BigIntegers, not fieldElements + if(z == null) { + this.z = BigInteger.ONE; + } + else { + this.z = z; + } + this.zinv = null; + //TODO: compression flag +} + +function pointFpGetX() { + if(this.zinv == null) { + this.zinv = this.z.modInverse(this.curve.q); + } + return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q)); +} + +function pointFpGetY() { + if(this.zinv == null) { + this.zinv = this.z.modInverse(this.curve.q); + } + return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q)); +} + +function pointFpEquals(other) { + if(other == this) return true; + if(this.isInfinity()) return other.isInfinity(); + if(other.isInfinity()) return this.isInfinity(); + var u, v; + // u = Y2 * Z1 - Y1 * Z2 + u = other.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(other.z)).mod(this.curve.q); + if(!u.equals(BigInteger.ZERO)) return false; + // v = X2 * Z1 - X1 * Z2 + v = other.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(other.z)).mod(this.curve.q); + return v.equals(BigInteger.ZERO); +} + +function pointFpIsInfinity() { + if((this.x == null) && (this.y == null)) return true; + return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO); +} + +function pointFpNegate() { + return new ECPointFp(this.curve, this.x, this.y.negate(), this.z); +} + +function pointFpAdd(b) { + if(this.isInfinity()) return b; + if(b.isInfinity()) return this; + + // u = Y2 * Z1 - Y1 * Z2 + var u = b.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(b.z)).mod(this.curve.q); + // v = X2 * Z1 - X1 * Z2 + var v = b.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(b.z)).mod(this.curve.q); + + + if(BigInteger.ZERO.equals(v)) { + if(BigInteger.ZERO.equals(u)) { + return this.twice(); // this == b, so double + } + return this.curve.getInfinity(); // this = -b, so infinity + } + + var THREE = new BigInteger("3"); + var x1 = this.x.toBigInteger(); + var y1 = this.y.toBigInteger(); + var x2 = b.x.toBigInteger(); + var y2 = b.y.toBigInteger(); + + var v2 = v.square(); + var v3 = v2.multiply(v); + var x1v2 = x1.multiply(v2); + var zu2 = u.square().multiply(this.z); + + // x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3) + var x3 = zu2.subtract(x1v2.shiftLeft(1)).multiply(b.z).subtract(v3).multiply(v).mod(this.curve.q); + // y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3 + var y3 = x1v2.multiply(THREE).multiply(u).subtract(y1.multiply(v3)).subtract(zu2.multiply(u)).multiply(b.z).add(u.multiply(v3)).mod(this.curve.q); + // z3 = v^3 * z1 * z2 + var z3 = v3.multiply(this.z).multiply(b.z).mod(this.curve.q); + + return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3); +} + +function pointFpTwice() { + if(this.isInfinity()) return this; + if(this.y.toBigInteger().signum() == 0) return this.curve.getInfinity(); + + // TODO: optimized handling of constants + var THREE = new BigInteger("3"); + var x1 = this.x.toBigInteger(); + var y1 = this.y.toBigInteger(); + + var y1z1 = y1.multiply(this.z); + var y1sqz1 = y1z1.multiply(y1).mod(this.curve.q); + var a = this.curve.a.toBigInteger(); + + // w = 3 * x1^2 + a * z1^2 + var w = x1.square().multiply(THREE); + if(!BigInteger.ZERO.equals(a)) { + w = w.add(this.z.square().multiply(a)); + } + w = w.mod(this.curve.q); + // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1) + var x3 = w.square().subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(this.curve.q); + // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3 + var y3 = w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1)).shiftLeft(2).multiply(y1sqz1).subtract(w.square().multiply(w)).mod(this.curve.q); + // z3 = 8 * (y1 * z1)^3 + var z3 = y1z1.square().multiply(y1z1).shiftLeft(3).mod(this.curve.q); + + return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3); +} + +// Simple NAF (Non-Adjacent Form) multiplication algorithm +// TODO: modularize the multiplication algorithm +function pointFpMultiply(k) { + if(this.isInfinity()) return this; + if(k.signum() == 0) return this.curve.getInfinity(); + + var e = k; + var h = e.multiply(new BigInteger("3")); + + var neg = this.negate(); + var R = this; + + var i; + for(i = h.bitLength() - 2; i > 0; --i) { + R = R.twice(); + + var hBit = h.testBit(i); + var eBit = e.testBit(i); + + if (hBit != eBit) { + R = R.add(hBit ? this : neg); + } + } + + return R; +} + +// Compute this*j + x*k (simultaneous multiplication) +function pointFpMultiplyTwo(j,x,k) { + var i; + if(j.bitLength() > k.bitLength()) + i = j.bitLength() - 1; + else + i = k.bitLength() - 1; + + var R = this.curve.getInfinity(); + var both = this.add(x); + while(i >= 0) { + R = R.twice(); + if(j.testBit(i)) { + if(k.testBit(i)) { + R = R.add(both); + } + else { + R = R.add(this); + } + } + else { + if(k.testBit(i)) { + R = R.add(x); + } + } + --i; + } + + return R; +} + +ECPointFp.prototype.getX = pointFpGetX; +ECPointFp.prototype.getY = pointFpGetY; +ECPointFp.prototype.equals = pointFpEquals; +ECPointFp.prototype.isInfinity = pointFpIsInfinity; +ECPointFp.prototype.negate = pointFpNegate; +ECPointFp.prototype.add = pointFpAdd; +ECPointFp.prototype.twice = pointFpTwice; +ECPointFp.prototype.multiply = pointFpMultiply; +ECPointFp.prototype.multiplyTwo = pointFpMultiplyTwo; + +// ---------------- +// ECCurveFp + +// constructor +function ECCurveFp(q,a,b) { + this.q = q; + this.a = this.fromBigInteger(a); + this.b = this.fromBigInteger(b); + this.infinity = new ECPointFp(this, null, null); +} + +function curveFpGetQ() { + return this.q; +} + +function curveFpGetA() { + return this.a; +} + +function curveFpGetB() { + return this.b; +} + +function curveFpEquals(other) { + if(other == this) return true; + return(this.q.equals(other.q) && this.a.equals(other.a) && this.b.equals(other.b)); +} + +function curveFpGetInfinity() { + return this.infinity; +} + +function curveFpFromBigInteger(x) { + return new ECFieldElementFp(this.q, x); +} + +// for now, work with hex strings because they're easier in JS +function curveFpDecodePointHex(s) { + switch(parseInt(s.substr(0,2), 16)) { // first byte + case 0: + return this.infinity; + case 2: + case 3: + // point compression not supported yet + return null; + case 4: + case 6: + case 7: + var len = (s.length - 2) / 2; + var xHex = s.substr(2, len); + var yHex = s.substr(len+2, len); + + return new ECPointFp(this, + this.fromBigInteger(new BigInteger(xHex, 16)), + this.fromBigInteger(new BigInteger(yHex, 16))); + + default: // unsupported + return null; + } +} + +ECCurveFp.prototype.getQ = curveFpGetQ; +ECCurveFp.prototype.getA = curveFpGetA; +ECCurveFp.prototype.getB = curveFpGetB; +ECCurveFp.prototype.equals = curveFpEquals; +ECCurveFp.prototype.getInfinity = curveFpGetInfinity; +ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger; +ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex; diff --git a/jsbn/jsbn.js b/jsbn/jsbn.js new file mode 100755 index 0000000..928cc4f --- /dev/null +++ b/jsbn/jsbn.js @@ -0,0 +1,559 @@ +// Copyright (c) 2005 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; +} +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; +} +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} +if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; +} +else if(j_lm && (navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; +} +else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; +} + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<<dbits)-1); +BigInteger.prototype.DV = (1<<dbits); + +var BI_FP = 52; +BigInteger.prototype.FV = Math.pow(2,BI_FP); +BigInteger.prototype.F1 = BI_FP-dbits; +BigInteger.prototype.F2 = 2*dbits-BI_FP; + +// Digit conversions +var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; +var BI_RC = new Array(); +var rr,vv; +rr = "0".charCodeAt(0); +for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; +rr = "a".charCodeAt(0); +for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; +rr = "A".charCodeAt(0); +for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; + +function int2char(n) { return BI_RM.charAt(n); } +function intAt(s,i) { + var c = BI_RC[s.charCodeAt(i)]; + return (c==null)?-1:c; +} + +// (protected) copy this to r +function bnpCopyTo(r) { + for(var i = this.t-1; i >= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh; + this[this.t++] = (x>>(this.DB-sh)); + } + else + this[this.t-1] |= x<<sh; + sh += k; + if(sh >= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh; + } + this.clamp(); + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) clamp off excess high words +function bnpClamp() { + var c = this.s&this.DM; + while(this.t > 0 && this[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1<<k)-1, d, m = false, r = "", i = this.t; + var p = this.DB-(i*this.DB)%k; + if(i-- > 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<<p)-1))<<(k-p); + d |= this[--i]>>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<<cbs)-1; + var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i; + for(i = this.t-1; i >= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<<bs; + } + for(i = ds-1; i >= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<<bs)-1; + r[0] = this[ds]>>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<<cbs; + r[i-ds] = this[i]>>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs; + r.t = this.t-ds; + r.clamp(); +} + +// (protected) r = this - a +function bnpSubTo(a,r) { + var i = 0, c = 0, m = Math.min(a.t,this.t); + while(i < m) { + c += this[i]-a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2; + var i = r.t, j = i-ys, t = (q==null)?nbi():q; + y.dlShiftTo(j,t); + if(r.compareTo(t) >= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1<<i)) > 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); diff --git a/jsbn/jsbn2.js b/jsbn/jsbn2.js new file mode 100755 index 0000000..5b2b725 --- /dev/null +++ b/jsbn/jsbn2.js @@ -0,0 +1,656 @@ +// Copyright (c) 2005-2009 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero +// Version 1.2: square() API, isProbablePrime fix + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0]; +} + +// (public) return value as byte +function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0; + this.fromString(x,256); + } +} + +// (public) convert to bigendian byte array +function bnToByteArray() { + var i = this.t, r = new Array(); + r[0] = this.s; + var p = this.DB-(i*this.DB)%8, d, k = 0; + if(i-- > 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<<p)-1))<<(8-p); + d |= this[--i]>>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<<n) +function bnpChangeBit(n,op) { + var r = BigInteger.ONE.shiftLeft(n); + this.bitwiseTo(r,op,r); + return r; +} + +// (public) this | (1<<n) +function bnSetBit(n) { return this.changeBit(n,op_or); } + +// (public) this & ~(1<<n) +function bnClearBit(n) { return this.changeBit(n,op_andnot); } + +// (public) this ^ (1<<n) +function bnFlipBit(n) { return this.changeBit(n,op_xor); } + +// (protected) r = this + a +function bnpAddTo(a,r) { + var i = 0, c = 0, m = Math.min(a.t,this.t); + while(i < m) { + c += this[i]+a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this^2 +function bnSquare() { var r = nbi(); this.squareTo(r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1; + g[1] = z.convert(this); + if(k > 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1<<i)) == 0) { + z.sqrTo(r,r2); t = r; r = r2; r2 = t; + if(--i < 0) { i = this.DB-1; --j; } + } + } + return z.revert(r); +} + +// (public) gcd(this,a) (HAC 14.54) +function bnGCD(a) { + var x = (this.s<0)?this.negate():this.clone(); + var y = (a.s<0)?a.negate():a.clone(); + if(x.compareTo(y) < 0) { var t = x; x = y; y = t; } + var i = x.getLowestSetBit(), g = y.getLowestSetBit(); + if(g < 0) return x; + if(i < g) g = i; + if(g > 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + //Pick bases at random, instead of starting at 2 + a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// JSBN-specific extension +BigInteger.prototype.square = bnSquare; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) diff --git a/jsbn/prng4.js b/jsbn/prng4.js new file mode 100755 index 0000000..3034f3f --- /dev/null +++ b/jsbn/prng4.js @@ -0,0 +1,45 @@ +// prng4.js - uses Arcfour as a PRNG + +function Arcfour() { + this.i = 0; + this.j = 0; + this.S = new Array(); +} + +// Initialize arcfour context from key, an array of ints, each from [0..255] +function ARC4init(key) { + var i, j, t; + for(i = 0; i < 256; ++i) + this.S[i] = i; + j = 0; + for(i = 0; i < 256; ++i) { + j = (j + this.S[i] + key[i % key.length]) & 255; + t = this.S[i]; + this.S[i] = this.S[j]; + this.S[j] = t; + } + this.i = 0; + this.j = 0; +} + +function ARC4next() { + var t; + this.i = (this.i + 1) & 255; + this.j = (this.j + this.S[this.i]) & 255; + t = this.S[this.i]; + this.S[this.i] = this.S[this.j]; + this.S[this.j] = t; + return this.S[(t + this.S[this.i]) & 255]; +} + +Arcfour.prototype.init = ARC4init; +Arcfour.prototype.next = ARC4next; + +// Plug in your RNG constructor here +function prng_newstate() { + return new Arcfour(); +} + +// Pool size must be a multiple of 4 and greater than 32. +// An array of bytes the size of the pool will be passed to init() +var rng_psize = 256; diff --git a/jsbn/rng.js b/jsbn/rng.js new file mode 100755 index 0000000..03afc3a --- /dev/null +++ b/jsbn/rng.js @@ -0,0 +1,68 @@ +// Random number generator - requires a PRNG backend, e.g. prng4.js + +// For best results, put code like +// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'> +// in your main HTML document. + +var rng_state; +var rng_pool; +var rng_pptr; + +// Mix in a 32-bit integer into the pool +function rng_seed_int(x) { + rng_pool[rng_pptr++] ^= x & 255; + rng_pool[rng_pptr++] ^= (x >> 8) & 255; + rng_pool[rng_pptr++] ^= (x >> 16) & 255; + rng_pool[rng_pptr++] ^= (x >> 24) & 255; + if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; +} + +// Mix in the current time (w/milliseconds) into the pool +function rng_seed_time() { + rng_seed_int(new Date().getTime()); +} + +// Initialize the pool with junk if needed. +if(rng_pool == null) { + rng_pool = new Array(); + rng_pptr = 0; + var t; + if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) { + // Extract entropy (256 bits) from NS4 RNG if available + var z = window.crypto.random(32); + for(t = 0; t < z.length; ++t) + rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; + } + while(rng_pptr < rng_psize) { // extract some randomness from Math.random() + t = Math.floor(65536 * Math.random()); + rng_pool[rng_pptr++] = t >>> 8; + rng_pool[rng_pptr++] = t & 255; + } + rng_pptr = 0; + rng_seed_time(); + //rng_seed_int(window.screenX); + //rng_seed_int(window.screenY); +} + +function rng_get_byte() { + if(rng_state == null) { + rng_seed_time(); + rng_state = prng_newstate(); + rng_state.init(rng_pool); + for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) + rng_pool[rng_pptr] = 0; + rng_pptr = 0; + //rng_pool = null; + } + // TODO: allow reseeding after first request + return rng_state.next(); +} + +function rng_get_bytes(ba) { + var i; + for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); +} + +function SecureRandom() {} + +SecureRandom.prototype.nextBytes = rng_get_bytes; diff --git a/jsbn/sec.js b/jsbn/sec.js new file mode 100755 index 0000000..e496571 --- /dev/null +++ b/jsbn/sec.js @@ -0,0 +1,173 @@ +// Named EC curves + +// Requires ec.js, jsbn.js, and jsbn2.js + +// ---------------- +// X9ECParameters + +// constructor +function X9ECParameters(curve,g,n,h) { + this.curve = curve; + this.g = g; + this.n = n; + this.h = h; +} + +function x9getCurve() { + return this.curve; +} + +function x9getG() { + return this.g; +} + +function x9getN() { + return this.n; +} + +function x9getH() { + return this.h; +} + +X9ECParameters.prototype.getCurve = x9getCurve; +X9ECParameters.prototype.getG = x9getG; +X9ECParameters.prototype.getN = x9getN; +X9ECParameters.prototype.getH = x9getH; + +// ---------------- +// SECNamedCurves + +function fromHex(s) { return new BigInteger(s, 16); } + +function secp128r1() { + // p = 2^128 - 2^97 - 1 + var p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"); + var b = fromHex("E87579C11079F43DD824993C2CEE5ED3"); + //byte[] S = Hex.decode("000E0D4D696E6768756151750CC03A4473D03679"); + var n = fromHex("FFFFFFFE0000000075A30D1B9038A115"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "161FF7528B899B2D0C28607CA52C5B86" + + "CF5AC8395BAFEB13C02DA292DDED7A83"); + return new X9ECParameters(curve, G, n, h); +} + +function secp160k1() { + // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"); + var a = BigInteger.ZERO; + var b = fromHex("7"); + //byte[] S = null; + var n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB" + + "938CF935318FDCED6BC28286531733C3F03C4FEE"); + return new X9ECParameters(curve, G, n, h); +} + +function secp160r1() { + // p = 2^160 - 2^31 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"); + var b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"); + //byte[] S = Hex.decode("1053CDE42C14D696E67687561517533BF3F83345"); + var n = fromHex("0100000000000000000001F4C8F927AED3CA752257"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "4A96B5688EF573284664698968C38BB913CBFC82" + + "23A628553168947D59DCC912042351377AC5FB32"); + return new X9ECParameters(curve, G, n, h); +} + +function secp192k1() { + // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"); + var a = BigInteger.ZERO; + var b = fromHex("3"); + //byte[] S = null; + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D"); + return new X9ECParameters(curve, G, n, h); +} + +function secp192r1() { + // p = 2^192 - 2^64 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"); + var b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"); + //byte[] S = Hex.decode("3045AE6FC8422F64ED579528D38120EAE12196D5"); + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012" + + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811"); + return new X9ECParameters(curve, G, n, h); +} + +function secp224r1() { + // p = 2^224 - 2^96 + 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"); + var b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"); + //byte[] S = Hex.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"); + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34"); + return new X9ECParameters(curve, G, n, h); +} + +function secp256k1() { + // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); + var a = BigInteger.ZERO; + var b = fromHex("7"); + //byte[] S = null; + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"); + return new X9ECParameters(curve, G, n, h); +} + +function secp256r1() { + // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1 + var p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + var b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); + //byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90"); + var n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); + return new X9ECParameters(curve, G, n, h); +} + +// TODO: make this into a proper hashtable +function getSECCurveByName(name) { + if(name == "secp128r1") return secp128r1(); + if(name == "secp160k1") return secp160k1(); + if(name == "secp160r1") return secp160r1(); + if(name == "secp192k1") return secp192k1(); + if(name == "secp192r1") return secp192r1(); + if(name == "secp224r1") return secp224r1(); + if(name == "secp256k1") return secp256k1(); + if(name == "secp256r1") return secp256r1(); + return null; +} diff --git a/opcode.js b/opcode.js new file mode 100644 index 0000000..b7b2bce --- /dev/null +++ b/opcode.js @@ -0,0 +1,154 @@ +(function () { + var Opcode = Bitcoin.Opcode = function (num) { + this.code = num; + }; + + Opcode.prototype.toString = function () { + return Opcode.reverseMap[this.code]; + }; + + Opcode.map = { + // push value + OP_0 : 0, + OP_FALSE : 0, + OP_PUSHDATA1 : 76, + OP_PUSHDATA2 : 77, + OP_PUSHDATA4 : 78, + OP_1NEGATE : 79, + OP_RESERVED : 80, + OP_1 : 81, + OP_TRUE : 81, + OP_2 : 82, + OP_3 : 83, + OP_4 : 84, + OP_5 : 85, + OP_6 : 86, + OP_7 : 87, + OP_8 : 88, + OP_9 : 89, + OP_10 : 90, + OP_11 : 91, + OP_12 : 92, + OP_13 : 93, + OP_14 : 94, + OP_15 : 95, + OP_16 : 96, + + // control + OP_NOP : 97, + OP_VER : 98, + OP_IF : 99, + OP_NOTIF : 100, + OP_VERIF : 101, + OP_VERNOTIF : 102, + OP_ELSE : 103, + OP_ENDIF : 104, + OP_VERIFY : 105, + OP_RETURN : 106, + + // stack ops + OP_TOALTSTACK : 107, + OP_FROMALTSTACK : 108, + OP_2DROP : 109, + OP_2DUP : 110, + OP_3DUP : 111, + OP_2OVER : 112, + OP_2ROT : 113, + OP_2SWAP : 114, + OP_IFDUP : 115, + OP_DEPTH : 116, + OP_DROP : 117, + OP_DUP : 118, + OP_NIP : 119, + OP_OVER : 120, + OP_PICK : 121, + OP_ROLL : 122, + OP_ROT : 123, + OP_SWAP : 124, + OP_TUCK : 125, + + // splice ops + OP_CAT : 126, + OP_SUBSTR : 127, + OP_LEFT : 128, + OP_RIGHT : 129, + OP_SIZE : 130, + + // bit logic + OP_INVERT : 131, + OP_AND : 132, + OP_OR : 133, + OP_XOR : 134, + OP_EQUAL : 135, + OP_EQUALVERIFY : 136, + OP_RESERVED1 : 137, + OP_RESERVED2 : 138, + + // numeric + OP_1ADD : 139, + OP_1SUB : 140, + OP_2MUL : 141, + OP_2DIV : 142, + OP_NEGATE : 143, + OP_ABS : 144, + OP_NOT : 145, + OP_0NOTEQUAL : 146, + + OP_ADD : 147, + OP_SUB : 148, + OP_MUL : 149, + OP_DIV : 150, + OP_MOD : 151, + OP_LSHIFT : 152, + OP_RSHIFT : 153, + + OP_BOOLAND : 154, + OP_BOOLOR : 155, + OP_NUMEQUAL : 156, + OP_NUMEQUALVERIFY : 157, + OP_NUMNOTEQUAL : 158, + OP_LESSTHAN : 159, + OP_GREATERTHAN : 160, + OP_LESSTHANOREQUAL : 161, + OP_GREATERTHANOREQUAL : 162, + OP_MIN : 163, + OP_MAX : 164, + + OP_WITHIN : 165, + + // crypto + OP_RIPEMD160 : 166, + OP_SHA1 : 167, + OP_SHA256 : 168, + OP_HASH160 : 169, + OP_HASH256 : 170, + OP_CODESEPARATOR : 171, + OP_CHECKSIG : 172, + OP_CHECKSIGVERIFY : 173, + OP_CHECKMULTISIG : 174, + OP_CHECKMULTISIGVERIFY : 175, + + // expansion + OP_NOP1 : 176, + OP_NOP2 : 177, + OP_NOP3 : 178, + OP_NOP4 : 179, + OP_NOP5 : 180, + OP_NOP6 : 181, + OP_NOP7 : 182, + OP_NOP8 : 183, + OP_NOP9 : 184, + OP_NOP10 : 185, + + // template matching params + OP_PUBKEYHASH : 253, + OP_PUBKEY : 254, + OP_INVALIDOPCODE : 255, + }; + + Opcode.reverseMap = []; + + for (var i in Opcode.map) { + Opcode.reverseMap[Opcode.map[i]] = i; + } +})(); diff --git a/script.js b/script.js new file mode 100644 index 0000000..d9e68b7 --- /dev/null +++ b/script.js @@ -0,0 +1,183 @@ +(function () { + var Opcode = Bitcoin.Opcode; + + // Make opcodes available as pseudo-constants + for (var i in Opcode.map) { + eval("var " + i + " = " + Opcode.map[i] + ";"); + } + + var Script = Bitcoin.Script = function (data) { + if (!data) { + this.buffer = []; + } else if ("string" == typeof data) { + this.buffer = Crypto.util.base64ToBytes(data); + } else if (Bitcoin.Util.isArray(data)) { + this.buffer = data; + } else if (data instanceof Script) { + this.buffer = data.buffer; + } else { + throw new Error("Invalid script"); + } + + this.parse(); + }; + + Script.prototype.parse = function () { + var self = this; + + this.chunks = []; + + // Cursor + var i = 0; + + // Read n bytes and store result as a chunk + function readChunk(n) { + self.chunks.push(self.buffer.slice(i, i + n)); + i += n; + }; + + while (i < this.buffer.length) { + var opcode = this.buffer[i++]; + if (opcode >= 0xF0) { + // Two byte opcode + opcode = (opcode << 8) | this.buffer[i++]; + } + + var len; + if (opcode > 0 && opcode < OP_PUSHDATA1) { + // Read some bytes of data, opcode value is the length of data + readChunk(opcode); + } else if (opcode == OP_PUSHDATA1) { + len = this.buffer[i++]; + readChunk(len); + } else if (opcode == OP_PUSHDATA2) { + len = (this.buffer[i++] << 8) | this.buffer[i++]; + readChunk(len); + } else if (opcode == OP_PUSHDATA4) { + len = (this.buffer[i++] << 24) | + (this.buffer[i++] << 16) | + (this.buffer[i++] << 8) | + this.buffer[i++]; + readChunk(len); + } else { + this.chunks.push(opcode); + } + } + }; + + Script.prototype.getOutType = function () + { + if (this.chunks.length == 5 && + this.chunks[0] == OP_DUP && + this.chunks[1] == OP_HASH160 && + this.chunks[3] == OP_EQUALVERIFY && + this.chunks[4] == OP_CHECKSIG) { + + // Transfer to Bitcoin address + return 'Address'; + } else if (this.chunks.length == 2 && + this.chunks[1] == OP_CHECKSIG) { + + // Transfer to IP address + return 'Pubkey'; + } else { + return 'Strange'; + } + }; + + Script.prototype.simpleOutPubKeyHash = function () + { + switch (this.getOutType()) { + case 'Address': + return this.chunks[2]; + case 'Pubkey': + return Bitcoin.Util.sha256ripe160(this.chunks[0]); + default: + throw new Error("Encountered non-standard scriptPubKey"); + } + }; + + Script.prototype.getInType = function () + { + if (this.chunks.length == 1) { + // Direct IP to IP transactions only have the public key in their scriptSig. + return 'Pubkey'; + } else if (this.chunks.length == 2 && + Bitcoin.Util.isArray(this.chunks[0]) && + Bitcoin.Util.isArray(this.chunks[1])) { + return 'Address'; + } else { + console.log(this.chunks); + throw new Error("Encountered non-standard scriptSig"); + } + }; + + Script.prototype.simpleInPubKey = function () + { + switch (this.getInType()) { + case 'Address': + return this.chunks[1]; + case 'Pubkey': + return this.chunks[0]; + default: + throw new Error("Encountered non-standard scriptSig"); + } + }; + + Script.prototype.simpleInPubKeyHash = function () + { + return Bitcoin.Util.sha256ripe160(this.simpleInPubKey()); + }; + + Script.prototype.writeOp = function (opcode) + { + this.buffer.push(opcode); + this.chunks.push(opcode); + }; + + Script.prototype.writeBytes = function (data) + { + if (data.length < OP_PUSHDATA1) { + this.buffer.push(data.length); + } else if (data.length <= 0xff) { + this.buffer.push(OP_PUSHDATA1); + this.buffer.push(data.length); + } else if (data.length <= 0xffff) { + this.buffer.push(OP_PUSHDATA2); + this.buffer.push(data.length & 0xff); + this.buffer.push((data.length >>> 8) & 0xff); + } else { + this.buffer.push(OP_PUSHDATA4); + this.buffer.push(data.length & 0xff); + this.buffer.push((data.length >>> 8) & 0xff); + this.buffer.push((data.length >>> 16) & 0xff); + this.buffer.push((data.length >>> 24) & 0xff); + } + this.buffer = this.buffer.concat(data); + this.chunks.push(data); + }; + + Script.createOutputScript = function (address) + { + var script = new Script(); + script.writeOp(OP_DUP); + script.writeOp(OP_HASH160); + script.writeBytes(address.hash); + script.writeOp(OP_EQUALVERIFY); + script.writeOp(OP_CHECKSIG); + return script; + }; + + Script.createInputScript = function (signature, pubKey) + { + var script = new Script(); + script.writeBytes(signature); + script.writeBytes(pubKey); + return script; + }; + + Script.prototype.clone = function () + { + return new Script(this.buffer); + }; +})(); diff --git a/transaction.js b/transaction.js new file mode 100644 index 0000000..53ec29c --- /dev/null +++ b/transaction.js @@ -0,0 +1,326 @@ +(function () { + var Script = Bitcoin.Script; + + var Transaction = Bitcoin.Transaction = function (doc) { + this.version = 1; + this.lock_time = 0; + this.ins = []; + this.outs = []; + this.timestamp = null; + this.block = null; + + if (doc) { + if (doc.hash) this.hash = doc.hash; + if (doc.version) this.version = doc.version; + if (doc.lock_time) this.lock_time = doc.lock_time; + if (doc.ins && doc.ins.length) { + for (var i = 0; i < doc.ins.length; i++) { + this.addInput(new TransactionIn(doc.ins[i])); + } + } + if (doc.outs && doc.outs.length) { + for (var i = 0; i < doc.outs.length; i++) { + this.addOutput(new TransactionOut(doc.outs[i])); + } + } + if (doc.timestamp) this.timestamp = doc.timestamp; + if (doc.block) this.block = doc.block; + } + }; + + Transaction.objectify = function (txs) { + var objs = []; + for (var i = 0; i < txs.length; i++) { + objs.push(new Transaction(txs[i])); + } + return objs; + }; + + Transaction.prototype.addInput = function (tx, outIndex) { + if (arguments[0] instanceof TransactionIn) { + this.ins.push(arguments[0]); + } else { + this.ins.push(new TransactionIn({ + outpoint: { + hash: tx.hash, + index: outIndex + }, + script: new Bitcoin.Script(), + sequence: 4294967295 + })); + } + }; + + Transaction.prototype.addOutput = function (address, value) { + if (arguments[0] instanceof TransactionOut) { + this.outs.push(arguments[0]); + } else { + if (value instanceof BigInteger) { + value = value.toByteArrayUnsigned().reverse(); + while (value.length < 8) value.push(0); + } else if (Bitcoin.Util.isArray(value)) { + // Nothing to do + } + + this.outs.push(new TransactionOut({ + value: value, + script: Script.createOutputScript(address) + })); + } + }; + + Transaction.prototype.serialize = function () + { + var buffer = []; + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.version)]).reverse()); + buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.ins.length)); + for (var i = 0; i < this.ins.length; i++) { + var txin = this.ins[i]; + buffer = buffer.concat(Crypto.util.base64ToBytes(txin.outpoint.hash)); + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.outpoint.index)]).reverse()); + var scriptBytes = txin.script.buffer; + buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(txin.sequence)]).reverse()); + } + buffer = buffer.concat(Bitcoin.Util.numToVarInt(this.outs.length)); + for (var i = 0; i < this.outs.length; i++) { + var txout = this.outs[i]; + buffer = buffer.concat(txout.value); + var scriptBytes = txout.script.buffer; + buffer = buffer.concat(Bitcoin.Util.numToVarInt(scriptBytes.length)); + buffer = buffer.concat(scriptBytes); + } + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(this.lock_time)]).reverse()); + + return buffer; + }; + + var OP_CODESEPARATOR = 171; + + var SIGHASH_ALL = 1; + var SIGHASH_NONE = 2; + var SIGHASH_SINGLE = 3; + var SIGHASH_ANYONECANPAY = 80; + + Transaction.prototype.hashTransactionForSignature = function (connectedScript, inIndex, hashType) + { + var txTmp = this.clone(); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + /*scriptCode = scriptCode.filter(function (val) { + return val !== OP_CODESEPARATOR; + });*/ + + // Blank out other inputs' signatures + for (var i = 0; i < txTmp.ins.length; i++) { + txTmp.ins[i].script = new Script(); + } + + txTmp.ins[inIndex].script = connectedScript; + + // Blank out some of the outputs + if ((hashType & 0x1f) == SIGHASH_NONE) { + txTmp.outs = []; + + // Let the others update at will + for (var i = 0; i < txTmp.ins.length; i++) + if (i != inIndex) + txTmp.ins[i].sequence = 0; + } else if ((hashType & 0x1f) == SIGHASH_SINGLE) { + // TODO: Implement + } + + // Blank out other inputs completely, not recommended for open transactions + if (hashType & SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]]; + } + + var buffer = txTmp.serialize(); + + buffer = buffer.concat(Crypto.util.wordsToBytes([parseInt(hashType)]).reverse()); + + console.log("signtx: "+Crypto.util.bytesToHex(buffer)); + + var hash1 = Crypto.SHA256(buffer, {asBytes: true}); + + console.log("sha256_1: ", Crypto.util.bytesToHex(hash1)); + + return Crypto.SHA256(hash1, {asBytes: true}); + }; + + Transaction.prototype.getHash = function () + { + var buffer = this.serialize(); + return Crypto.SHA256(Crypto.SHA256(buffer, {asBytes: true}), {asBytes: true}); + }; + + Transaction.prototype.clone = function () + { + var newTx = new Transaction(); + newTx.version = this.version; + newTx.lock_time = this.lock_time; + for (var i = 0; i < this.ins.length; i++) { + var txin = this.ins[i].clone(); + newTx.addInput(txin); + } + for (var i = 0; i < this.outs.length; i++) { + var txout = this.outs[i].clone(); + newTx.addOutput(txout); + } + return newTx; + }; + + Transaction.prototype.getDescription = function (wallet) { + if (!(wallet instanceof Bitcoin.Wallet)) return ""; + + var allFromMe = true, + allToMe = true, + firstRecvHash = null, + firstMeRecvHash = null, + firstSendHash = null; + + for (var i = this.outs.length-1; i >= 0; i--) { + var txout = this.outs[i]; + var hash = txout.script.simpleOutPubKeyHash(); + if (!wallet.hasHash(hash)) { + allToMe = false; + } else { + firstMeRecvHash = hash; + } + firstRecvHash = hash; + } + for (var i = tx.ins.length-1; i >= 0; i--) { + var txin = tx.ins[i]; + firstSendHash = txin.script.simpleInPubKeyHash(); + if (!wallet.hasHash(firstSendHash)) { + allFromMe = false; + break; + } + } + + var impact = this.calcImpact(wallet); + + if (impact.sign > 0 && impact.value.compareTo(BigInteger.ZERO) > 0) { + return "Received with "+(new Bitcoin.Address(firstMeRecvHash)); + } else if (allFromMe && allToMe) { + return "Payment to yourself"; + } else if (allFromMe) { + return "Payment to "+(new Bitcoin.Address(firstRecvHash)); + } else { + return ""; + } + }; + + Transaction.prototype.getTotalValue = function () { + var totalValue = BigInteger.ZERO; + for (var j = 0; j < this.outs.length; j++) { + var txout = this.outs[j]; + totalValue = totalValue.add(Bitcoin.Util.valueToBigInt(txout.value)); + } + return totalValue; + }; + + /** + * Calculates the impact a transaction has on this wallet. + * + * Based on the its public keys, the wallet will calculate the + * credit or debit of this transaction. + * + * It will return an object with two properties: + * - sign: 1 or -1 depending on sign of the calculated impact. + * - value: amount of calculated impact + * + * @returns Object Impact on wallet + */ + Transaction.prototype.calcImpact = function (wallet) { + if (!(wallet instanceof Bitcoin.Wallet)) return BigInteger.ZERO; + + // Calculate credit to us from all outputs + var valueOut = BigInteger.ZERO; + for (var j = 0; j < this.outs.length; j++) { + var txout = this.outs[j]; + var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); + if (wallet.hasHash(hash)) { + valueOut = valueOut.add(Bitcoin.Util.valueToBigInt(txout.value)); + } + } + + // Calculate debit to us from all ins + var valueIn = BigInteger.ZERO; + for (var j = 0; j < this.ins.length; j++) { + var txin = this.ins[j]; + var hash = Crypto.util.bytesToBase64(txin.script.simpleInPubKeyHash()); + if (wallet.hasHash(hash)) { + var fromTx = wallet.txIndex[txin.outpoint.hash]; + if (fromTx) { + valueIn = valueIn.add(Bitcoin.Util.valueToBigInt(fromTx.outs[txin.outpoint.index].value)); + } + } + } + if (valueOut.compareTo(valueIn) >= 0) { + return { + sign: 1, + value: valueOut.subtract(valueIn) + }; + } else { + return { + sign: -1, + value: valueIn.subtract(valueOut) + }; + } + }; + + var TransactionIn = Bitcoin.TransactionIn = function (data) + { + this.outpoint = data.outpoint; + if (data.script instanceof Script) { + this.script = data.script; + } else { + this.script = new Script(data.script); + } + this.sequence = data.sequence; + }; + + TransactionIn.prototype.clone = function () + { + var newTxin = new TransactionIn({ + outpoint: { + hash: this.outpoint.hash, + index: this.outpoint.index + }, + script: this.script.clone(), + sequence: this.sequence + }); + return newTxin; + }; + + var TransactionOut = Bitcoin.TransactionOut = function (data) + { + if (data.script instanceof Script) { + this.script = data.script; + } else { + this.script = new Script(data.script); + } + + if (Bitcoin.Util.isArray(data.value)) { + this.value = data.value; + } else if ("string" == typeof data.value) { + var valueHex = (new BigInteger(data.value, 10)).toString(16); + while (valueHex.length < 16) valueHex = "0" + valueHex; + this.value = Crypto.util.hexToBytes(valueHex); + } + }; + + TransactionOut.prototype.clone = function () + { + var newTxout = new TransactionOut({ + script: this.script.clone(), + value: this.value.slice(0) + }); + return newTxout; + }; +})(); + + diff --git a/txdb.js b/txdb.js new file mode 100755 index 0000000..aff2784 --- /dev/null +++ b/txdb.js @@ -0,0 +1,56 @@ +var TransactionDatabase = function () { + this.txs = []; + this.txIndex = {}; +}; + +EventEmitter.augment(TransactionDatabase.prototype); + +TransactionDatabase.prototype.addTransaction = function (tx) { + this.addTransactionNoUpdate(tx); + $(this).trigger('update'); +}; + +TransactionDatabase.prototype.addTransactionNoUpdate = function (tx) { + // Return if transaction is already known + if (this.txIndex[tx.hash]) { + return; + } + + this.txs.push(new Bitcoin.Transaction(tx)); + this.txIndex[tx.hash] = tx; +}; + +TransactionDatabase.prototype.removeTransaction = function (hash) { + this.removeTransactionNoUpdate(hash); + $(this).trigger('update'); +}; + +TransactionDatabase.prototype.removeTransactionNoUpdate = function (hash) { + var tx = this.txIndex[hash]; + + if (!tx) { + // If the tx is not in the index, we don't actually waste our + // time looping through the array. + return; + } + + for (var i = 0, l = this.txs.length; i < l; i++) { + if (this.txs[i].hash == hash) { + this.txs.splice(i, 1); + break; + } + } + + delete this.txIndex[hash]; +}; + +TransactionDatabase.prototype.loadTransactions = function (txs) { + for (var i = 0; i < txs.length; i++) { + this.addTransactionNoUpdate(txs[i]); + } + $(this).trigger('update'); +}; + +TransactionDatabase.prototype.getTransactions = function () { + return this.txs; +}; diff --git a/util.js b/util.js new file mode 100644 index 0000000..97f594d --- /dev/null +++ b/util.js @@ -0,0 +1,65 @@ +// BigInteger monkey patching +BigInteger.valueOf = nbv; +BigInteger.prototype.toByteArrayUnsigned = function () { + var ba = this.toByteArray(); + return ba.map(function (v) { + return (v < 0) ? v + 256 : v; + }); +}; + +// Console ignore +var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", + "trace", "profile", "profileEnd"]; + +if ("undefined" == typeof window.console) window.console = {}; +for (var i = 0; i < names.length; ++i) + if ("undefined" == typeof window.console[names[i]]) + window.console[names[i]] = function() {}; + +// Bitcoin utility functions +Bitcoin.Util = { + isArray: Array.isArray || function(o) { + return Object.prototype.toString.call(o) === '[object Array]'; + }, + makeFilledArray: function (len, val) { + var array = []; + var i = 0; + while (i < len) { + array[i++] = val; + } + return array; + }, + numToVarInt: function (i) { + // TODO: THIS IS TOTALLY UNTESTED! + if (i < 0xfd) { + // unsigned char + return [i]; + } else if (i <= 1<<16) { + // unsigned short (LE) + return [0xfd, i >>> 8, i & 255]; + } else if (i <= 1<<32) { + // unsigned int (LE) + return [0xfe].concat(Crypto.util.wordsToBytes([i])); + } else { + // unsigned long long (LE) + return [0xff].concat(Crypto.util.wordsToBytes([i >>> 32, i])); + } + }, + valueToBigInt: function (valueBuffer) { + if (valueBuffer instanceof BigInteger) return valueBuffer; + return new BigInteger(valueBuffer); + }, + formatValue: function (valueBuffer) { + var value = this.valueToBigInt(valueBuffer).toString(); + var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0'; + var decimalPart = value.length > 8 ? value.substr(value.length-8) : value; + while (decimalPart.length < 8) decimalPart = "0"+decimalPart; + decimalPart = decimalPart.replace(/0*$/, ''); + while (decimalPart.length < 2) decimalPart += "0"; + return integerPart+"."+decimalPart; + }, + sha256ripe160: function (data) { + return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true}); + } +}; diff --git a/wallet.js b/wallet.js new file mode 100755 index 0000000..2a1565c --- /dev/null +++ b/wallet.js @@ -0,0 +1,245 @@ +Bitcoin.Wallet = (function () { + function supportsLocalStorage() { + try { + return 'localStorage' in window && window['localStorage'] !== null; + } catch (e) { + return false; + } + }; + + var Script = Bitcoin.Script, + TransactionIn = Bitcoin.TransactionIn, + TransactionOut = Bitcoin.TransactionOut; + + var Wallet = function () { + // Keychain + var keys = []; + this.addressHashes = []; + + // Transaction data + this.txIndex = {}; + this.unspentOuts = []; + + // Other fields + this.addressPointer = 0; + + this.addKey = function (key) { + if (!(key instanceof Bitcoin.ECKey)) { + key = new Bitcoin.ECKey(key); + } + keys.push(key); + this.addressHashes.push(key.getBitcoinAddress().getHashBase64()); + }; + + this.addKeys = function (keys) { + for (var i = 0; i < keys.length; i++) { + this.addKey(keys[i]); + } + }; + + this.save = function () { + var serializedWallet = []; + + for (var i = 0; i < keys.length; i++) { + serializedWallet.push(keys[i].toString('base64')); + } + + console.log(serializedWallet); + localStorage["wallet"] = serializedWallet; + }; + + this.clear = function () { + keys = []; + }; + + this.getAllAddresses = function () { + var addresses = []; + for (var i = 0; i < keys.length; i++) { + addresses.push(keys[i].getBitcoinAddress()); + } + return addresses; + }; + + this.getCurAddress = function () { + if (keys[this.addressPointer]) { + return keys[this.addressPointer].getBitcoinAddress(); + } else { + return null; + } + }; + + this.getNextAddress = function () { + if (keys.length) { + // TODO: Create new addresses if we run out + this.addressPointer = (this.addressPointer + 1) % keys.length; + return keys[this.addressPointer].getBitcoinAddress(); + } else { + return null; + } + }; + + this.signWithKey = function (pubKeyHash, hash) { + pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); + for (var i = 0; i < this.addressHashes.length; i++) { + if (this.addressHashes[i] == pubKeyHash) { + return keys[i].sign(hash); + } + } + throw new Error("Missing key for signature"); + }; + + this.getPubKeyFromHash = function (pubKeyHash) { + pubKeyHash = Crypto.util.bytesToBase64(pubKeyHash); + for (var i = 0; i < this.addressHashes.length; i++) { + if (this.addressHashes[i] == pubKeyHash) { + console.log(Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(keys[i].getPub())), pubKeyHash); + return keys[i].getPub(); + } + } + throw new Error("Hash unknown"); + }; + }; + + Wallet.prototype.generateAddress = function () { + this.addKey(new Bitcoin.ECKey()); + }; + + /** + * Detect and load a wallet from localStorage. + */ + Wallet.prototype.loadLocal = function () { + var wallet = localStorage["wallet"]; + + if (wallet) { + try { + this.addKeys(wallet.split(',')); + return true; + } catch (e) { + return false; + } + } else { + return false; + } + }; + + Wallet.prototype.initNew = function (callback) { + var self = this; + var total = 5; + function generateNum (n) { + self.generateAddress(); + if (n > 0) { + setTimeout(function () {generateNum(n-1);}, 1); + } else { + callback(total-n, total); + } + }; + generateNum(total, callback); + }; + + Wallet.prototype.process = function (tx) { + if (this.txIndex[tx.hash]) return; + + // Gather outputs + for (var j = 0; j < tx.outs.length; j++) { + var txout = new TransactionOut(tx.outs[j]); + var hash = Crypto.util.bytesToBase64(txout.script.simpleOutPubKeyHash()); + for (var k = 0; k < this.addressHashes.length; k++) { + if (this.addressHashes[k] === hash) { + this.unspentOuts.push({tx: tx, index: j, out: txout}); + break; + } + } + } + + // Remove spent outputs + for (var j = 0; j < tx.ins.length; j++) { + var txin = new TransactionIn(tx.ins[j]); + var pubkey = txin.script.simpleInPubKey(); + var hash = Crypto.util.bytesToBase64(Bitcoin.Util.sha256ripe160(pubkey)); + for (var k = 0; k < this.addressHashes.length; k++) { + if (this.addressHashes[k] === hash) { + for (var l = 0; l < this.unspentOuts.length; l++) { + if (txin.outpoint.hash == this.unspentOuts[l].tx.hash && + txin.outpoint.index == this.unspentOuts[l].index) { + this.unspentOuts.splice(l, 1); + } + } + break; + } + } + } + + // Index transaction + this.txIndex[tx.hash] = tx; + }; + + Wallet.prototype.getBalance = function () { + var balance = BigInteger.valueOf(0); + for (var i = 0; i < this.unspentOuts.length; i++) { + var txout = this.unspentOuts[i].out; + balance = balance.add(Bitcoin.Util.valueToBigInt(txout.value)); + } + return balance; + }; + + Wallet.prototype.createSend = function (address, sendValue) { + var selectedOuts = []; + var availableValue = BigInteger.ZERO; + for (var i = 0; i < this.unspentOuts.length; i++) { + selectedOuts.push(this.unspentOuts[i]); + availableValue = availableValue.add(Bitcoin.Util.valueToBigInt(this.unspentOuts[i].out.value)); + + if (availableValue.compareTo(sendValue) >= 0) break; + } + + console.log(selectedOuts); + + var changeValue = availableValue.subtract(sendValue); + + var sendTx = new Bitcoin.Transaction(); + + for (var i = 0; i < selectedOuts.length; i++) { + sendTx.addInput(selectedOuts[i].tx, selectedOuts[i].index); + } + + sendTx.addOutput(address, sendValue); + if (changeValue.compareTo(BigInteger.ZERO) > 0) { + sendTx.addOutput(this.getNextAddress(), changeValue); + } + + var hashType = 1; // SIGHASH_ALL + + for (var i = 0; i < sendTx.ins.length; i++) { + var hash = sendTx.hashTransactionForSignature(selectedOuts[i].out.script, i, hashType); + var pubKeyHash = selectedOuts[i].out.script.simpleOutPubKeyHash(); + var signature = this.signWithKey(pubKeyHash, hash); + + // Append hash type + signature.push(parseInt(hashType)); + + sendTx.ins[i].script = Script.createInputScript(signature, this.getPubKeyFromHash(pubKeyHash)); + } + + console.log(sendTx); + + console.log("pubkey: "+Crypto.util.bytesToHex(this.getPubKeyFromHash(pubKeyHash))); + + return sendTx; + }; + + /** + * Check to see if a pubKeyHash belongs to this wallet. + */ + Wallet.prototype.hasHash = function (hash) { + if (Bitcoin.Util.isArray(hash)) hash = Crypto.util.bytesToBase64(hash); + + // TODO: Just create an object with base64 hashes as keys for faster lookup + for (var k = 0; k < this.addressHashes.length; k++) { + if (this.addressHashes[k] === hash) return true; + } + return false; + }; + + return Wallet; +})(); +