grunt build system. unix line endings.
This commit is contained in:
parent
d77aaf687f
commit
a111f763b4
35 changed files with 16051 additions and 7999 deletions
49
Gruntfile.js
Normal file
49
Gruntfile.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
module.exports = function (grunt) {
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
combine: {
|
||||
single: {
|
||||
input: "./src/bitaddress-ui.html",
|
||||
output: "./bitaddress.org.html",
|
||||
tokens: [
|
||||
{ token: "//array.map.js", file: "./src/array.map.js" },
|
||||
{ token: "//biginteger.js", file: "./src/biginteger.js" },
|
||||
{ token: "//bitcoinjs-lib.js", file: "./src/bitcoinjs-lib.js" },
|
||||
{ token: "//bitcoinjs-lib.address.js", file: "./src/bitcoinjs-lib.address.js" },
|
||||
{ token: "//bitcoinjs-lib.base58.js", file: "./src/bitcoinjs-lib.base58.js" },
|
||||
{ token: "//bitcoinjs-lib.ecdsa.js", file: "./src/bitcoinjs-lib.ecdsa.js" },
|
||||
{ token: "//bitcoinjs-lib.eckey.js", file: "./src/bitcoinjs-lib.eckey.js" },
|
||||
{ token: "//bitcoinjs-lib.util.js", file: "./src/bitcoinjs-lib.util.js" },
|
||||
{ token: "//cryptojs.js", file: "./src/cryptojs.js" },
|
||||
{ token: "//cryptojs.sha256.js", file: "./src/cryptojs.sha256.js" },
|
||||
{ token: "//cryptojs.pbkdf2.js", file: "./src/cryptojs.pbkdf2.js" },
|
||||
{ token: "//cryptojs.hmac.js", file: "./src/cryptojs.hmac.js" },
|
||||
{ token: "//cryptojs.aes.js", file: "./src/cryptojs.aes.js" },
|
||||
{ token: "//cryptojs.blockmodes.js", file: "./src/cryptojs.blockmodes.js" },
|
||||
{ token: "//cryptojs.ripemd160.js", file: "./src/cryptojs.ripemd160.js" },
|
||||
{ token: "//crypto-scrypt.js", file: "./src/crypto-scrypt.js" },
|
||||
{ token: "//ellipticcurve.js", file: "./src/ellipticcurve.js" },
|
||||
{ token: "//ninja.key.js", file: "./src/ninja.key.js" },
|
||||
{ token: "//ninja.misc.js", file: "./src/ninja.misc.js" },
|
||||
{ token: "//ninja.onload.js", file: "./src/ninja.onload.js" },
|
||||
{ token: "//ninja.unittests.js", file: "./src/ninja.unittests.js" },
|
||||
{ token: "//ninja.translator.js", file: "./src/ninja.translator.js" },
|
||||
{ token: "//ninja.singlewallet.js", file: "./src/ninja.singlewallet.js" },
|
||||
{ token: "//ninja.paperwallet.js", file: "./src/ninja.paperwallet.js" },
|
||||
{ token: "//ninja.bulkwallet.js", file: "./src/ninja.bulkwallet.js" },
|
||||
{ token: "//ninja.brainwallet.js", file: "./src/ninja.brainwallet.js" },
|
||||
{ token: "//ninja.vanitywallet.js", file: "./src/ninja.vanitywallet.js" },
|
||||
{ token: "//ninja.detailwallet.js", file: "./src/ninja.detailwallet.js" },
|
||||
{ token: "//qrcode.js", file: "./src/qrcode.js" },
|
||||
{ token: "//securerandom.js", file: "./src/securerandom.js" },
|
||||
{ token: "//main.css", file: "./src/main.css" }
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.file.defaultEncoding = 'utf-8';
|
||||
grunt.loadNpmTasks("grunt-combine");
|
||||
grunt.registerTask("default", ["combine:single"]);
|
||||
};
|
15969
bitaddress.org.html
15969
bitaddress.org.html
File diff suppressed because one or more lines are too long
28
package.json
Normal file
28
package.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "bitaddress.org",
|
||||
"version": "2.5.0",
|
||||
"description": "Open Source JavaScript Client-Side Bitcoin Wallet Generator",
|
||||
"main": "Gruntfile.js",
|
||||
"dependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-combine": "~0.8.3"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": ""
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/pointbiz/bitaddress.org.git"
|
||||
},
|
||||
"keywords": [
|
||||
"bitcoin address wallet generator"
|
||||
],
|
||||
"author": "pointbiz",
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README",
|
||||
"gitHead": "d77aaf687fca1f0e28388b0a8de5eb3d89d4fad3",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pointbiz/bitaddress.org/issues"
|
||||
}
|
||||
}
|
57
src/array.map.js
Normal file
57
src/array.map.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Array.prototype.map function is in the public domain.
|
||||
// Production steps of ECMA-262, Edition 5, 15.4.4.19
|
||||
// Reference: http://es5.github.com/#x15.4.4.19
|
||||
if (!Array.prototype.map) {
|
||||
Array.prototype.map = function (callback, thisArg) {
|
||||
var T, A, k;
|
||||
if (this == null) {
|
||||
throw new TypeError(" this is null or not defined");
|
||||
}
|
||||
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
|
||||
var O = Object(this);
|
||||
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
|
||||
// 3. Let len be ToUint32(lenValue).
|
||||
var len = O.length >>> 0;
|
||||
// 4. If IsCallable(callback) is false, throw a TypeError exception.
|
||||
// See: http://es5.github.com/#x9.11
|
||||
if ({}.toString.call(callback) != "[object Function]") {
|
||||
throw new TypeError(callback + " is not a function");
|
||||
}
|
||||
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (thisArg) {
|
||||
T = thisArg;
|
||||
}
|
||||
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
|
||||
// the standard built-in constructor with that name and len is the value of len.
|
||||
A = new Array(len);
|
||||
// 7. Let k be 0
|
||||
k = 0;
|
||||
// 8. Repeat, while k < len
|
||||
while (k < len) {
|
||||
var kValue, mappedValue;
|
||||
// a. Let Pk be ToString(k).
|
||||
// This is implicit for LHS operands of the in operator
|
||||
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
|
||||
// This step can be combined with c
|
||||
// c. If kPresent is true, then
|
||||
if (k in O) {
|
||||
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
|
||||
kValue = O[k];
|
||||
// ii. Let mappedValue be the result of calling the Call internal method of callback
|
||||
// with T as the this value and argument list containing kValue, k, and O.
|
||||
mappedValue = callback.call(T, kValue, k, O);
|
||||
// iii. Call the DefineOwnProperty internal method of A with arguments
|
||||
// Pk, Property Descriptor {Value: mappedValue, Writable: true, Enumerable: true, Configurable: true},
|
||||
// and false.
|
||||
// In browsers that support Object.defineProperty, use the following:
|
||||
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
|
||||
// For best browser support, use the following:
|
||||
A[k] = mappedValue;
|
||||
}
|
||||
// d. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
// 9. return A
|
||||
return A;
|
||||
};
|
||||
}
|
1271
src/biginteger.js
Normal file
1271
src/biginteger.js
Normal file
File diff suppressed because it is too large
Load diff
428
src/bitaddress-ui.html
Normal file
428
src/bitaddress-ui.html
Normal file
File diff suppressed because one or more lines are too long
54
src/bitcoinjs-lib.address.js
Normal file
54
src/bitcoinjs-lib.address.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/09e8c6e184d6501a0c2c59d73ca64db5c0d3eb95/src/address.js
|
||||
Bitcoin.Address = function (bytes) {
|
||||
if ("string" == typeof bytes) {
|
||||
bytes = Bitcoin.Address.decodeString(bytes);
|
||||
}
|
||||
this.hash = bytes;
|
||||
this.version = Bitcoin.Address.networkVersion;
|
||||
};
|
||||
|
||||
Bitcoin.Address.networkVersion = 0x00; // mainnet
|
||||
|
||||
/**
|
||||
* Serialize this object as a standard Bitcoin address.
|
||||
*
|
||||
* Returns the address as a base58-encoded string in the standardized format.
|
||||
*/
|
||||
Bitcoin.Address.prototype.toString = function () {
|
||||
// Get a copy of the hash
|
||||
var hash = this.hash.slice(0);
|
||||
|
||||
// Version
|
||||
hash.unshift(this.version);
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a Bitcoin address contained in a string.
|
||||
*/
|
||||
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;
|
||||
};
|
72
src/bitcoinjs-lib.base58.js
Normal file
72
src/bitcoinjs-lib.base58.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/c952aaeb3ee472e3776655b8ea07299ebed702c7/src/base58.js
|
||||
(function (Bitcoin) {
|
||||
Bitcoin.Base58 = {
|
||||
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
||||
validRegex: /^[1-9A-HJ-NP-Za-km-z]+$/,
|
||||
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 = BigInteger.fromByteArrayUnsigned(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;
|
||||
}
|
||||
|
||||
return chars.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var bi = BigInteger.valueOf(0);
|
||||
var leadingZerosNum = 0;
|
||||
for (var i = input.length - 1; i >= 0; i--) {
|
||||
var alphaIndex = B58.alphabet.indexOf(input[i]);
|
||||
if (alphaIndex < 0) {
|
||||
throw "Invalid character";
|
||||
}
|
||||
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();
|
||||
|
||||
// Add leading zeros
|
||||
while (leadingZerosNum-- > 0) bytes.unshift(0);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
};
|
||||
|
||||
var B58 = Bitcoin.Base58;
|
||||
})(
|
||||
'undefined' != typeof Bitcoin ? Bitcoin : module.exports
|
||||
);
|
283
src/bitcoinjs-lib.ecdsa.js
Normal file
283
src/bitcoinjs-lib.ecdsa.js
Normal file
|
@ -0,0 +1,283 @@
|
|||
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/e90780d3d3b8fc0d027d2bcb38b80479902f223e/src/ecdsa.js
|
||||
Bitcoin.ECDSA = (function () {
|
||||
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
var rng = new SecureRandom();
|
||||
|
||||
var P_OVER_FOUR = null;
|
||||
|
||||
function implShamirsTrick(P, k, Q, l) {
|
||||
var m = Math.max(k.bitLength(), l.bitLength());
|
||||
var Z = P.add2D(Q);
|
||||
var R = P.curve.getInfinity();
|
||||
|
||||
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);
|
||||
} else {
|
||||
R = R.add2D(P);
|
||||
}
|
||||
} else {
|
||||
if (l.testBit(i)) {
|
||||
R = R.add2D(Q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||
|
||||
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);
|
||||
|
||||
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
|
||||
|
||||
return ECDSA.serializeSig(r, s);
|
||||
},
|
||||
|
||||
verify: function (hash, sig, pubkey) {
|
||||
var r, s;
|
||||
if (Bitcoin.Util.isArray(sig)) {
|
||||
var obj = ECDSA.parseSig(sig);
|
||||
r = obj.r;
|
||||
s = obj.s;
|
||||
} else if ("object" === typeof sig && sig.r && sig.s) {
|
||||
r = sig.r;
|
||||
s = sig.s;
|
||||
} else {
|
||||
throw "Invalid value for signature";
|
||||
}
|
||||
|
||||
var Q;
|
||||
if (pubkey instanceof ec.PointFp) {
|
||||
Q = pubkey;
|
||||
} else if (Bitcoin.Util.isArray(pubkey)) {
|
||||
Q = EllipticCurve.PointFp.decodeFrom(ecparams.getCurve(), pubkey);
|
||||
} else {
|
||||
throw "Invalid format for pubkey value, must be byte array or ec.PointFp";
|
||||
}
|
||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||
|
||||
return ECDSA.verifyRaw(e, r, s, Q);
|
||||
},
|
||||
|
||||
verifyRaw: function (e, r, s, Q) {
|
||||
var n = ecparams.getN();
|
||||
var G = ecparams.getG();
|
||||
|
||||
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);
|
||||
|
||||
// TODO(!!!): For some reason Shamir's trick isn't working with
|
||||
// signed message verification!? Probably an implementation
|
||||
// error!
|
||||
//var point = implShamirsTrick(G, u1, Q, u2);
|
||||
var point = G.multiply(u1).add(Q.multiply(u2));
|
||||
|
||||
var v = point.getX().toBigInteger().mod(n);
|
||||
|
||||
return v.equals(r);
|
||||
},
|
||||
|
||||
/**
|
||||
* Serialize a signature into DER format.
|
||||
*
|
||||
* Takes two BigIntegers representing r and s and returns a byte array.
|
||||
*/
|
||||
serializeSig: function (r, s) {
|
||||
var rBa = r.toByteArraySigned();
|
||||
var sBa = s.toByteArraySigned();
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses a byte array containing a DER-encoded signature.
|
||||
*
|
||||
* This function will return an object of the form:
|
||||
*
|
||||
* {
|
||||
* r: BigInteger,
|
||||
* s: BigInteger
|
||||
* }
|
||||
*/
|
||||
parseSig: function (sig) {
|
||||
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 r = BigInteger.fromByteArrayUnsigned(rBa);
|
||||
var s = BigInteger.fromByteArrayUnsigned(sBa);
|
||||
|
||||
return { r: r, s: s };
|
||||
},
|
||||
|
||||
parseSigCompact: function (sig) {
|
||||
if (sig.length !== 65) {
|
||||
throw "Signature has the wrong length";
|
||||
}
|
||||
|
||||
// Signature is prefixed with a type byte storing three bits of
|
||||
// information.
|
||||
var i = sig[0] - 27;
|
||||
if (i < 0 || i > 7) {
|
||||
throw "Invalid signature type";
|
||||
}
|
||||
|
||||
var n = ecparams.getN();
|
||||
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n);
|
||||
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n);
|
||||
|
||||
return { r: r, s: s, i: i };
|
||||
},
|
||||
|
||||
/**
|
||||
* Recover a public key from a signature.
|
||||
*
|
||||
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
|
||||
* Key Recovery Operation".
|
||||
*
|
||||
* http://www.secg.org/download/aid-780/sec1-v2.pdf
|
||||
*/
|
||||
recoverPubKey: function (r, s, hash, i) {
|
||||
// The recovery parameter i has two bits.
|
||||
i = i & 3;
|
||||
|
||||
// The less significant bit specifies whether the y coordinate
|
||||
// of the compressed point is even or not.
|
||||
var isYEven = i & 1;
|
||||
|
||||
// The more significant bit specifies whether we should use the
|
||||
// first or second candidate key.
|
||||
var isSecondKey = i >> 1;
|
||||
|
||||
var n = ecparams.getN();
|
||||
var G = ecparams.getG();
|
||||
var curve = ecparams.getCurve();
|
||||
var p = curve.getQ();
|
||||
var a = curve.getA().toBigInteger();
|
||||
var b = curve.getB().toBigInteger();
|
||||
|
||||
// We precalculate (p + 1) / 4 where p is if the field order
|
||||
if (!P_OVER_FOUR) {
|
||||
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4));
|
||||
}
|
||||
|
||||
// 1.1 Compute x
|
||||
var x = isSecondKey ? r.add(n) : r;
|
||||
|
||||
// 1.3 Convert x to point
|
||||
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);
|
||||
var beta = alpha.modPow(P_OVER_FOUR, p);
|
||||
|
||||
var xorOdd = beta.isEven() ? (i % 2) : ((i + 1) % 2);
|
||||
// If beta is even, but y isn't or vice versa, then convert it,
|
||||
// otherwise we're done and y == beta.
|
||||
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta);
|
||||
|
||||
// 1.4 Check that nR is at infinity
|
||||
var R = new EllipticCurve.PointFp(curve,
|
||||
curve.fromBigInteger(x),
|
||||
curve.fromBigInteger(y));
|
||||
R.validate();
|
||||
|
||||
// 1.5 Compute e from M
|
||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||
var eNeg = BigInteger.ZERO.subtract(e).mod(n);
|
||||
|
||||
// 1.6 Compute Q = r^-1 (sR - eG)
|
||||
var rInv = r.modInverse(n);
|
||||
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv);
|
||||
|
||||
Q.validate();
|
||||
if (!ECDSA.verifyRaw(e, r, s, Q)) {
|
||||
throw "Pubkey recovery unsuccessful";
|
||||
}
|
||||
|
||||
var pubKey = new Bitcoin.ECKey();
|
||||
pubKey.pub = Q;
|
||||
return pubKey;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate pubkey extraction parameter.
|
||||
*
|
||||
* When extracting a pubkey from a signature, we have to
|
||||
* distinguish four different cases. Rather than putting this
|
||||
* burden on the verifier, Bitcoin includes a 2-bit value with the
|
||||
* signature.
|
||||
*
|
||||
* This function simply tries all four cases and returns the value
|
||||
* that resulted in a successful pubkey recovery.
|
||||
*/
|
||||
calcPubkeyRecoveryParam: function (address, r, s, hash) {
|
||||
for (var i = 0; i < 4; i++) {
|
||||
try {
|
||||
var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i);
|
||||
if (pubkey.getBitcoinAddress().toString() == address) {
|
||||
return i;
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
throw "Unable to find valid recovery factor";
|
||||
}
|
||||
};
|
||||
|
||||
return ECDSA;
|
||||
})();
|
262
src/bitcoinjs-lib.eckey.js
Normal file
262
src/bitcoinjs-lib.eckey.js
Normal file
|
@ -0,0 +1,262 @@
|
|||
//https://raw.github.com/pointbiz/bitcoinjs-lib/9b2f94a028a7bc9bed94e0722563e9ff1d8e8db8/src/eckey.js
|
||||
Bitcoin.ECKey = (function () {
|
||||
var ECDSA = Bitcoin.ECDSA;
|
||||
var ecparams = EllipticCurve.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)) {
|
||||
// Prepend zero byte to prevent interpretation as negative integer
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(input);
|
||||
} else if ("string" == typeof input) {
|
||||
var bytes = null;
|
||||
if (ECKey.isWalletImportFormat(input)) {
|
||||
bytes = ECKey.decodeWalletImportFormat(input);
|
||||
} else if (ECKey.isCompressedWalletImportFormat(input)) {
|
||||
bytes = ECKey.decodeCompressedWalletImportFormat(input);
|
||||
this.compressed = true;
|
||||
} else if (ECKey.isMiniFormat(input)) {
|
||||
bytes = Crypto.SHA256(input, { asBytes: true });
|
||||
} else if (ECKey.isHexFormat(input)) {
|
||||
bytes = Crypto.util.hexToBytes(input);
|
||||
} else if (ECKey.isBase64Format(input)) {
|
||||
bytes = Crypto.util.base64ToBytes(input);
|
||||
}
|
||||
|
||||
if (bytes == null || bytes.length != 32) {
|
||||
this.priv = null;
|
||||
} else {
|
||||
// Prepend zero byte to prevent interpretation as negative integer
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
this.compressed = (this.compressed == undefined) ? !!ECKey.compressByDefault : this.compressed;
|
||||
};
|
||||
|
||||
ECKey.privateKeyPrefix = 0x80; // mainnet 0x80 testnet 0xEF
|
||||
|
||||
/**
|
||||
* Whether public keys should be returned compressed by default.
|
||||
*/
|
||||
ECKey.compressByDefault = false;
|
||||
|
||||
/**
|
||||
* Set whether the public key should be returned compressed or not.
|
||||
*/
|
||||
ECKey.prototype.setCompressed = function (v) {
|
||||
this.compressed = !!v;
|
||||
if (this.pubPoint) this.pubPoint.compressed = this.compressed;
|
||||
return this;
|
||||
};
|
||||
|
||||
/*
|
||||
* Return public key as a byte array in DER encoding
|
||||
*/
|
||||
ECKey.prototype.getPub = function () {
|
||||
if (this.compressed) {
|
||||
if (this.pubComp) return this.pubComp;
|
||||
return this.pubComp = this.getPubPoint().getEncoded(1);
|
||||
} else {
|
||||
if (this.pubUncomp) return this.pubUncomp;
|
||||
return this.pubUncomp = this.getPubPoint().getEncoded(0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return public point as ECPoint object.
|
||||
*/
|
||||
ECKey.prototype.getPubPoint = function () {
|
||||
if (!this.pubPoint) {
|
||||
this.pubPoint = ecparams.getG().multiply(this.priv);
|
||||
this.pubPoint.compressed = this.compressed;
|
||||
}
|
||||
return this.pubPoint;
|
||||
};
|
||||
|
||||
ECKey.prototype.getPubKeyHex = function () {
|
||||
if (this.compressed) {
|
||||
if (this.pubKeyHexComp) return this.pubKeyHexComp;
|
||||
return this.pubKeyHexComp = Crypto.util.bytesToHex(this.getPub()).toString().toUpperCase();
|
||||
} else {
|
||||
if (this.pubKeyHexUncomp) return this.pubKeyHexUncomp;
|
||||
return this.pubKeyHexUncomp = Crypto.util.bytesToHex(this.getPub()).toString().toUpperCase();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the pubKeyHash for this key.
|
||||
*
|
||||
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as
|
||||
* a byte array.
|
||||
*/
|
||||
ECKey.prototype.getPubKeyHash = function () {
|
||||
if (this.compressed) {
|
||||
if (this.pubKeyHashComp) return this.pubKeyHashComp;
|
||||
return this.pubKeyHashComp = Bitcoin.Util.sha256ripe160(this.getPub());
|
||||
} else {
|
||||
if (this.pubKeyHashUncomp) return this.pubKeyHashUncomp;
|
||||
return this.pubKeyHashUncomp = Bitcoin.Util.sha256ripe160(this.getPub());
|
||||
}
|
||||
};
|
||||
|
||||
ECKey.prototype.getBitcoinAddress = function () {
|
||||
var hash = this.getPubKeyHash();
|
||||
var addr = new Bitcoin.Address(hash);
|
||||
return addr.toString();
|
||||
};
|
||||
|
||||
/*
|
||||
* Takes a public point as a hex string or byte array
|
||||
*/
|
||||
ECKey.prototype.setPub = function (pub) {
|
||||
// byte array
|
||||
if (Bitcoin.Util.isArray(pub)) {
|
||||
pub = Crypto.util.bytesToHex(pub).toString().toUpperCase();
|
||||
}
|
||||
var ecPoint = ecparams.getCurve().decodePointHex(pub);
|
||||
this.setCompressed(ecPoint.compressed);
|
||||
this.pubPoint = ecPoint;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Sipa Private Key Wallet Import Format
|
||||
ECKey.prototype.getBitcoinWalletImportFormat = function () {
|
||||
var bytes = this.getBitcoinPrivateKeyByteArray();
|
||||
bytes.unshift(ECKey.privateKeyPrefix); // prepend 0x80 byte
|
||||
if (this.compressed) bytes.push(0x01); // append 0x01 byte for compressed format
|
||||
var checksum = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), { asBytes: true });
|
||||
bytes = bytes.concat(checksum.slice(0, 4));
|
||||
var privWif = Bitcoin.Base58.encode(bytes);
|
||||
return privWif;
|
||||
};
|
||||
|
||||
// Private Key Hex Format
|
||||
ECKey.prototype.getBitcoinHexFormat = function () {
|
||||
return Crypto.util.bytesToHex(this.getBitcoinPrivateKeyByteArray()).toString().toUpperCase();
|
||||
};
|
||||
|
||||
// Private Key Base64 Format
|
||||
ECKey.prototype.getBitcoinBase64Format = function () {
|
||||
return Crypto.util.bytesToBase64(this.getBitcoinPrivateKeyByteArray());
|
||||
};
|
||||
|
||||
ECKey.prototype.getBitcoinPrivateKeyByteArray = function () {
|
||||
// Get a copy of private key as a byte array
|
||||
var bytes = this.priv.toByteArrayUnsigned();
|
||||
// zero pad if private key is less than 32 bytes
|
||||
while (bytes.length < 32) bytes.unshift(0x00);
|
||||
return bytes;
|
||||
};
|
||||
|
||||
ECKey.prototype.toString = function (format) {
|
||||
format = format || "";
|
||||
if (format.toString().toLowerCase() == "base64" || format.toString().toLowerCase() == "b64") {
|
||||
return this.getBitcoinBase64Format();
|
||||
}
|
||||
// Wallet Import Format
|
||||
else if (format.toString().toLowerCase() == "wif") {
|
||||
return this.getBitcoinWalletImportFormat();
|
||||
}
|
||||
else {
|
||||
return this.getBitcoinHexFormat();
|
||||
}
|
||||
};
|
||||
|
||||
ECKey.prototype.sign = function (hash) {
|
||||
return ECDSA.sign(hash, this.priv);
|
||||
};
|
||||
|
||||
ECKey.prototype.verify = function (hash, sig) {
|
||||
return ECDSA.verify(hash, sig, this.getPub());
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a wallet import format private key contained in a string.
|
||||
*/
|
||||
ECKey.decodeWalletImportFormat = function (privStr) {
|
||||
var bytes = Bitcoin.Base58.decode(privStr);
|
||||
var hash = bytes.slice(0, 33);
|
||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, { asBytes: true }), { asBytes: true });
|
||||
if (checksum[0] != bytes[33] ||
|
||||
checksum[1] != bytes[34] ||
|
||||
checksum[2] != bytes[35] ||
|
||||
checksum[3] != bytes[36]) {
|
||||
throw "Checksum validation failed!";
|
||||
}
|
||||
var version = hash.shift();
|
||||
if (version != ECKey.privateKeyPrefix) {
|
||||
throw "Version " + version + " not supported!";
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a compressed wallet import format private key contained in a string.
|
||||
*/
|
||||
ECKey.decodeCompressedWalletImportFormat = function (privStr) {
|
||||
var bytes = Bitcoin.Base58.decode(privStr);
|
||||
var hash = bytes.slice(0, 34);
|
||||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, { asBytes: true }), { asBytes: true });
|
||||
if (checksum[0] != bytes[34] ||
|
||||
checksum[1] != bytes[35] ||
|
||||
checksum[2] != bytes[36] ||
|
||||
checksum[3] != bytes[37]) {
|
||||
throw "Checksum validation failed!";
|
||||
}
|
||||
var version = hash.shift();
|
||||
if (version != ECKey.privateKeyPrefix) {
|
||||
throw "Version " + version + " not supported!";
|
||||
}
|
||||
hash.pop();
|
||||
return hash;
|
||||
};
|
||||
|
||||
// 64 characters [0-9A-F]
|
||||
ECKey.isHexFormat = function (key) {
|
||||
key = key.toString();
|
||||
return /^[A-Fa-f0-9]{64}$/.test(key);
|
||||
};
|
||||
|
||||
// 51 characters base58, always starts with a '5'
|
||||
ECKey.isWalletImportFormat = function (key) {
|
||||
key = key.toString();
|
||||
return (ECKey.privateKeyPrefix == 0x80) ?
|
||||
(/^5[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}$/.test(key)) :
|
||||
(/^9[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}$/.test(key));
|
||||
};
|
||||
|
||||
// 52 characters base58
|
||||
ECKey.isCompressedWalletImportFormat = function (key) {
|
||||
key = key.toString();
|
||||
return (ECKey.privateKeyPrefix == 0x80) ?
|
||||
(/^[LK][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51}$/.test(key)) :
|
||||
(/^c[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51}$/.test(key));
|
||||
};
|
||||
|
||||
// 44 characters
|
||||
ECKey.isBase64Format = function (key) {
|
||||
key = key.toString();
|
||||
return (/^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=+\/]{44}$/.test(key));
|
||||
};
|
||||
|
||||
// 22, 26 or 30 characters, always starts with an 'S'
|
||||
ECKey.isMiniFormat = function (key) {
|
||||
key = key.toString();
|
||||
var validChars22 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21}$/.test(key);
|
||||
var validChars26 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{25}$/.test(key);
|
||||
var validChars30 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{29}$/.test(key);
|
||||
var testBytes = Crypto.SHA256(key + "?", { asBytes: true });
|
||||
|
||||
return ((testBytes[0] === 0x00 || testBytes[0] === 0x01) && (validChars22 || validChars26 || validChars30));
|
||||
};
|
||||
|
||||
return ECKey;
|
||||
})();
|
16
src/bitcoinjs-lib.js
Normal file
16
src/bitcoinjs-lib.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
Copyright (c) 2011 Stefan Thomas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/1a7fc9d063f864058809d06ef4542af40be3558f/src/bitcoin.js
|
||||
(function (exports) {
|
||||
var Bitcoin = exports;
|
||||
})(
|
||||
'object' === typeof module ? module.exports : (window.Bitcoin = {})
|
||||
);
|
105
src/bitcoinjs-lib.util.js
Normal file
105
src/bitcoinjs-lib.util.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/09e8c6e184d6501a0c2c59d73ca64db5c0d3eb95/src/util.js
|
||||
// Bitcoin utility functions
|
||||
Bitcoin.Util = {
|
||||
/**
|
||||
* Cross-browser compatibility version of Array.isArray.
|
||||
*/
|
||||
isArray: Array.isArray || function (o) {
|
||||
return Object.prototype.toString.call(o) === '[object Array]';
|
||||
},
|
||||
/**
|
||||
* Create an array of a certain length filled with a specific value.
|
||||
*/
|
||||
makeFilledArray: function (len, val) {
|
||||
var array = [];
|
||||
var i = 0;
|
||||
while (i < len) {
|
||||
array[i++] = val;
|
||||
}
|
||||
return array;
|
||||
},
|
||||
/**
|
||||
* Turn an integer into a "var_int".
|
||||
*
|
||||
* "var_int" is a variable length integer used by Bitcoin's binary format.
|
||||
*
|
||||
* Returns a byte array.
|
||||
*/
|
||||
numToVarInt: function (i) {
|
||||
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]));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Parse a Bitcoin value byte array, returning a BigInteger.
|
||||
*/
|
||||
valueToBigInt: function (valueBuffer) {
|
||||
if (valueBuffer instanceof BigInteger) return valueBuffer;
|
||||
|
||||
// Prepend zero byte to prevent interpretation as negative integer
|
||||
return BigInteger.fromByteArrayUnsigned(valueBuffer);
|
||||
},
|
||||
/**
|
||||
* Format a Bitcoin value as a string.
|
||||
*
|
||||
* Takes a BigInteger or byte-array and returns that amount of Bitcoins in a
|
||||
* nice standard formatting.
|
||||
*
|
||||
* Examples:
|
||||
* 12.3555
|
||||
* 0.1234
|
||||
* 900.99998888
|
||||
* 34.00
|
||||
*/
|
||||
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;
|
||||
},
|
||||
/**
|
||||
* Parse a floating point string as a Bitcoin value.
|
||||
*
|
||||
* Keep in mind that parsing user input is messy. You should always display
|
||||
* the parsed value back to the user to make sure we understood his input
|
||||
* correctly.
|
||||
*/
|
||||
parseValue: function (valueString) {
|
||||
// TODO: Detect other number formats (e.g. comma as decimal separator)
|
||||
var valueComp = valueString.split('.');
|
||||
var integralPart = valueComp[0];
|
||||
var fractionalPart = valueComp[1] || "0";
|
||||
while (fractionalPart.length < 8) fractionalPart += "0";
|
||||
fractionalPart = fractionalPart.replace(/^0+/g, '');
|
||||
var value = BigInteger.valueOf(parseInt(integralPart));
|
||||
value = value.multiply(BigInteger.valueOf(100000000));
|
||||
value = value.add(BigInteger.valueOf(parseInt(fractionalPart)));
|
||||
return value;
|
||||
},
|
||||
/**
|
||||
* Calculate RIPEMD160(SHA256(data)).
|
||||
*
|
||||
* Takes an arbitrary byte array as inputs and returns the hash as a byte
|
||||
* array.
|
||||
*/
|
||||
sha256ripe160: function (data) {
|
||||
return Crypto.RIPEMD160(Crypto.SHA256(data, { asBytes: true }), { asBytes: true });
|
||||
},
|
||||
// double sha256
|
||||
dsha256: function (data) {
|
||||
return Crypto.SHA256(Crypto.SHA256(data, { asBytes: true }), { asBytes: true });
|
||||
}
|
||||
};
|
295
src/crypto-scrypt.js
Normal file
295
src/crypto-scrypt.js
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2011 Intalio Pte, All Rights Reserved
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
// https://github.com/cheongwy/node-scrypt-js
|
||||
(function () {
|
||||
|
||||
var MAX_VALUE = 2147483647;
|
||||
var workerUrl = null;
|
||||
|
||||
//function scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen)
|
||||
/*
|
||||
* N = Cpu cost
|
||||
* r = Memory cost
|
||||
* p = parallelization cost
|
||||
*
|
||||
*/
|
||||
window.Crypto_scrypt = function (passwd, salt, N, r, p, dkLen, callback) {
|
||||
if (N == 0 || (N & (N - 1)) != 0) throw Error("N must be > 0 and a power of 2");
|
||||
|
||||
if (N > MAX_VALUE / 128 / r) throw Error("Parameter N is too large");
|
||||
if (r > MAX_VALUE / 128 / p) throw Error("Parameter r is too large");
|
||||
|
||||
var PBKDF2_opts = { iterations: 1, hasher: Crypto.SHA256, asBytes: true };
|
||||
|
||||
var B = Crypto.PBKDF2(passwd, salt, p * 128 * r, PBKDF2_opts);
|
||||
|
||||
try {
|
||||
var i = 0;
|
||||
var worksDone = 0;
|
||||
var makeWorker = function () {
|
||||
if (!workerUrl) {
|
||||
var code = '(' + scryptCore.toString() + ')()';
|
||||
var blob;
|
||||
try {
|
||||
blob = new Blob([code], { type: "text/javascript" });
|
||||
} catch (e) {
|
||||
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
|
||||
blob = new BlobBuilder();
|
||||
blob.append(code);
|
||||
blob = blob.getBlob("text/javascript");
|
||||
}
|
||||
workerUrl = URL.createObjectURL(blob);
|
||||
}
|
||||
var worker = new Worker(workerUrl);
|
||||
worker.onmessage = function (event) {
|
||||
var Bi = event.data[0], Bslice = event.data[1];
|
||||
worksDone++;
|
||||
|
||||
if (i < p) {
|
||||
worker.postMessage([N, r, p, B, i++]);
|
||||
}
|
||||
|
||||
var length = Bslice.length, destPos = Bi * 128 * r, srcPos = 0;
|
||||
while (length--) {
|
||||
B[destPos++] = Bslice[srcPos++];
|
||||
}
|
||||
|
||||
if (worksDone == p) {
|
||||
callback(Crypto.PBKDF2(passwd, B, dkLen, PBKDF2_opts));
|
||||
}
|
||||
};
|
||||
return worker;
|
||||
};
|
||||
var workers = [makeWorker(), makeWorker()];
|
||||
workers[0].postMessage([N, r, p, B, i++]);
|
||||
if (p > 1) {
|
||||
workers[1].postMessage([N, r, p, B, i++]);
|
||||
}
|
||||
} catch (e) {
|
||||
window.setTimeout(function () {
|
||||
scryptCore();
|
||||
callback(Crypto.PBKDF2(passwd, B, dkLen, PBKDF2_opts));
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// using this function to enclose everything needed to create a worker (but also invokable directly for synchronous use)
|
||||
function scryptCore() {
|
||||
var XY = [], V = [];
|
||||
|
||||
if (typeof B === 'undefined') {
|
||||
onmessage = function (event) {
|
||||
var data = event.data;
|
||||
var N = data[0], r = data[1], p = data[2], B = data[3], i = data[4];
|
||||
|
||||
var Bslice = [];
|
||||
arraycopy32(B, i * 128 * r, Bslice, 0, 128 * r);
|
||||
smix(Bslice, 0, r, N, V, XY);
|
||||
|
||||
postMessage([i, Bslice]);
|
||||
};
|
||||
} else {
|
||||
for (var i = 0; i < p; i++) {
|
||||
smix(B, i * 128 * r, r, N, V, XY);
|
||||
}
|
||||
}
|
||||
|
||||
function smix(B, Bi, r, N, V, XY) {
|
||||
var Xi = 0;
|
||||
var Yi = 128 * r;
|
||||
var i;
|
||||
|
||||
arraycopy32(B, Bi, XY, Xi, Yi);
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
arraycopy32(XY, Xi, V, i * Yi, Yi);
|
||||
blockmix_salsa8(XY, Xi, Yi, r);
|
||||
}
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
var j = integerify(XY, Xi, r) & (N - 1);
|
||||
blockxor(V, j * Yi, XY, Xi, Yi);
|
||||
blockmix_salsa8(XY, Xi, Yi, r);
|
||||
}
|
||||
|
||||
arraycopy32(XY, Xi, B, Bi, Yi);
|
||||
}
|
||||
|
||||
function blockmix_salsa8(BY, Bi, Yi, r) {
|
||||
var X = [];
|
||||
var i;
|
||||
|
||||
arraycopy32(BY, Bi + (2 * r - 1) * 64, X, 0, 64);
|
||||
|
||||
for (i = 0; i < 2 * r; i++) {
|
||||
blockxor(BY, i * 64, X, 0, 64);
|
||||
salsa20_8(X);
|
||||
arraycopy32(X, 0, BY, Yi + (i * 64), 64);
|
||||
}
|
||||
|
||||
for (i = 0; i < r; i++) {
|
||||
arraycopy32(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64);
|
||||
}
|
||||
|
||||
for (i = 0; i < r; i++) {
|
||||
arraycopy32(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64);
|
||||
}
|
||||
}
|
||||
|
||||
function R(a, b) {
|
||||
return (a << b) | (a >>> (32 - b));
|
||||
}
|
||||
|
||||
function salsa20_8(B) {
|
||||
var B32 = new Array(32);
|
||||
var x = new Array(32);
|
||||
var i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
B32[i] = (B[i * 4 + 0] & 0xff) << 0;
|
||||
B32[i] |= (B[i * 4 + 1] & 0xff) << 8;
|
||||
B32[i] |= (B[i * 4 + 2] & 0xff) << 16;
|
||||
B32[i] |= (B[i * 4 + 3] & 0xff) << 24;
|
||||
}
|
||||
|
||||
arraycopy(B32, 0, x, 0, 16);
|
||||
|
||||
for (i = 8; i > 0; i -= 2) {
|
||||
x[4] ^= R(x[0] + x[12], 7); x[8] ^= R(x[4] + x[0], 9);
|
||||
x[12] ^= R(x[8] + x[4], 13); x[0] ^= R(x[12] + x[8], 18);
|
||||
x[9] ^= R(x[5] + x[1], 7); x[13] ^= R(x[9] + x[5], 9);
|
||||
x[1] ^= R(x[13] + x[9], 13); x[5] ^= R(x[1] + x[13], 18);
|
||||
x[14] ^= R(x[10] + x[6], 7); x[2] ^= R(x[14] + x[10], 9);
|
||||
x[6] ^= R(x[2] + x[14], 13); x[10] ^= R(x[6] + x[2], 18);
|
||||
x[3] ^= R(x[15] + x[11], 7); x[7] ^= R(x[3] + x[15], 9);
|
||||
x[11] ^= R(x[7] + x[3], 13); x[15] ^= R(x[11] + x[7], 18);
|
||||
x[1] ^= R(x[0] + x[3], 7); x[2] ^= R(x[1] + x[0], 9);
|
||||
x[3] ^= R(x[2] + x[1], 13); x[0] ^= R(x[3] + x[2], 18);
|
||||
x[6] ^= R(x[5] + x[4], 7); x[7] ^= R(x[6] + x[5], 9);
|
||||
x[4] ^= R(x[7] + x[6], 13); x[5] ^= R(x[4] + x[7], 18);
|
||||
x[11] ^= R(x[10] + x[9], 7); x[8] ^= R(x[11] + x[10], 9);
|
||||
x[9] ^= R(x[8] + x[11], 13); x[10] ^= R(x[9] + x[8], 18);
|
||||
x[12] ^= R(x[15] + x[14], 7); x[13] ^= R(x[12] + x[15], 9);
|
||||
x[14] ^= R(x[13] + x[12], 13); x[15] ^= R(x[14] + x[13], 18);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i];
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
var bi = i * 4;
|
||||
B[bi + 0] = (B32[i] >> 0 & 0xff);
|
||||
B[bi + 1] = (B32[i] >> 8 & 0xff);
|
||||
B[bi + 2] = (B32[i] >> 16 & 0xff);
|
||||
B[bi + 3] = (B32[i] >> 24 & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
function blockxor(S, Si, D, Di, len) {
|
||||
var i = len >> 6;
|
||||
while (i--) {
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
D[Di++] ^= S[Si++]; D[Di++] ^= S[Si++];
|
||||
}
|
||||
}
|
||||
|
||||
function integerify(B, bi, r) {
|
||||
var n;
|
||||
|
||||
bi += (2 * r - 1) * 64;
|
||||
|
||||
n = (B[bi + 0] & 0xff) << 0;
|
||||
n |= (B[bi + 1] & 0xff) << 8;
|
||||
n |= (B[bi + 2] & 0xff) << 16;
|
||||
n |= (B[bi + 3] & 0xff) << 24;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
function arraycopy(src, srcPos, dest, destPos, length) {
|
||||
while (length--) {
|
||||
dest[destPos++] = src[srcPos++];
|
||||
}
|
||||
}
|
||||
|
||||
function arraycopy32(src, srcPos, dest, destPos, length) {
|
||||
var i = length >> 5;
|
||||
while (i--) {
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
|
||||
}
|
||||
}
|
||||
} // scryptCore
|
||||
}; // window.Crypto_scrypt
|
||||
})();
|
407
src/cryptojs.aes.js
Normal file
407
src/cryptojs.aes.js
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*!
|
||||
* Crypto-JS v2.5.4 AES.js
|
||||
* http://code.google.com/p/crypto-js/
|
||||
* Copyright (c) 2009-2013, 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;
|
||||
|
||||
// Precomputed SBOX
|
||||
var SBOX = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
||||
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
|
||||
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
|
||||
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
|
||||
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
||||
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
|
||||
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
||||
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
|
||||
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
|
||||
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
|
||||
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
|
||||
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
|
||||
|
||||
// Compute inverse SBOX lookup table
|
||||
for (var INVSBOX = [], i = 0; i < 256; i++) INVSBOX[SBOX[i]] = i;
|
||||
|
||||
// Compute multiplication in GF(2^8) lookup tables
|
||||
var MULT2 = [],
|
||||
MULT3 = [],
|
||||
MULT9 = [],
|
||||
MULTB = [],
|
||||
MULTD = [],
|
||||
MULTE = [];
|
||||
|
||||
function xtime(a, b) {
|
||||
for (var result = 0, i = 0; i < 8; i++) {
|
||||
if (b & 1) result ^= a;
|
||||
var hiBitSet = a & 0x80;
|
||||
a = (a << 1) & 0xFF;
|
||||
if (hiBitSet) a ^= 0x1b;
|
||||
b >>>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 256; i++) {
|
||||
MULT2[i] = xtime(i, 2);
|
||||
MULT3[i] = xtime(i, 3);
|
||||
MULT9[i] = xtime(i, 9);
|
||||
MULTB[i] = xtime(i, 0xB);
|
||||
MULTD[i] = xtime(i, 0xD);
|
||||
MULTE[i] = xtime(i, 0xE);
|
||||
}
|
||||
|
||||
// Precomputed RCon lookup
|
||||
var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
|
||||
|
||||
// Inner state
|
||||
var state = [[], [], [], []],
|
||||
keylength,
|
||||
nrounds,
|
||||
keyschedule;
|
||||
|
||||
var AES = C.AES = {
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
|
||||
encrypt: function (message, password, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
// Determine mode
|
||||
var mode = options.mode || new C.mode.OFB;
|
||||
|
||||
// Allow mode to override options
|
||||
if (mode.fixOptions) mode.fixOptions(options);
|
||||
|
||||
var
|
||||
|
||||
// Convert to bytes if message is a string
|
||||
m = (
|
||||
message.constructor == String ?
|
||||
UTF8.stringToBytes(message) :
|
||||
message
|
||||
),
|
||||
|
||||
// Generate random IV
|
||||
iv = options.iv || util.randomBytes(AES._blocksize * 4),
|
||||
|
||||
// Generate key
|
||||
k = (
|
||||
password.constructor == String ?
|
||||
// Derive key from pass-phrase
|
||||
C.PBKDF2(password, iv, 32, { asBytes: true }) :
|
||||
// else, assume byte array representing cryptographic key
|
||||
password
|
||||
);
|
||||
|
||||
// Encrypt
|
||||
AES._init(k);
|
||||
mode.encrypt(AES, m, iv);
|
||||
|
||||
// Return ciphertext
|
||||
m = options.iv ? m : iv.concat(m);
|
||||
return (options && options.asBytes) ? m : util.bytesToBase64(m);
|
||||
|
||||
},
|
||||
|
||||
decrypt: function (ciphertext, password, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
// Determine mode
|
||||
var mode = options.mode || new C.mode.OFB;
|
||||
|
||||
// Allow mode to override options
|
||||
if (mode.fixOptions) mode.fixOptions(options);
|
||||
|
||||
var
|
||||
|
||||
// Convert to bytes if ciphertext is a string
|
||||
c = (
|
||||
ciphertext.constructor == String ?
|
||||
util.base64ToBytes(ciphertext) :
|
||||
ciphertext
|
||||
),
|
||||
|
||||
// Separate IV and message
|
||||
iv = options.iv || c.splice(0, AES._blocksize * 4),
|
||||
|
||||
// Generate key
|
||||
k = (
|
||||
password.constructor == String ?
|
||||
// Derive key from pass-phrase
|
||||
C.PBKDF2(password, iv, 32, { asBytes: true }) :
|
||||
// else, assume byte array representing cryptographic key
|
||||
password
|
||||
);
|
||||
|
||||
// Decrypt
|
||||
AES._init(k);
|
||||
mode.decrypt(AES, c, iv);
|
||||
|
||||
// Return plaintext
|
||||
return (options && options.asBytes) ? c : UTF8.bytesToString(c);
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Package private methods and properties
|
||||
*/
|
||||
|
||||
_blocksize: 4,
|
||||
|
||||
_encryptblock: function (m, offset) {
|
||||
|
||||
// Set input
|
||||
for (var row = 0; row < AES._blocksize; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] = m[offset + col * 4 + row];
|
||||
}
|
||||
|
||||
// Add round key
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] ^= keyschedule[col][row];
|
||||
}
|
||||
|
||||
for (var round = 1; round < nrounds; round++) {
|
||||
|
||||
// Sub bytes
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] = SBOX[state[row][col]];
|
||||
}
|
||||
|
||||
// Shift rows
|
||||
state[1].push(state[1].shift());
|
||||
state[2].push(state[2].shift());
|
||||
state[2].push(state[2].shift());
|
||||
state[3].unshift(state[3].pop());
|
||||
|
||||
// Mix columns
|
||||
for (var col = 0; col < 4; col++) {
|
||||
|
||||
var s0 = state[0][col],
|
||||
s1 = state[1][col],
|
||||
s2 = state[2][col],
|
||||
s3 = state[3][col];
|
||||
|
||||
state[0][col] = MULT2[s0] ^ MULT3[s1] ^ s2 ^ s3;
|
||||
state[1][col] = s0 ^ MULT2[s1] ^ MULT3[s2] ^ s3;
|
||||
state[2][col] = s0 ^ s1 ^ MULT2[s2] ^ MULT3[s3];
|
||||
state[3][col] = MULT3[s0] ^ s1 ^ s2 ^ MULT2[s3];
|
||||
|
||||
}
|
||||
|
||||
// Add round key
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] ^= keyschedule[round * 4 + col][row];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Sub bytes
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] = SBOX[state[row][col]];
|
||||
}
|
||||
|
||||
// Shift rows
|
||||
state[1].push(state[1].shift());
|
||||
state[2].push(state[2].shift());
|
||||
state[2].push(state[2].shift());
|
||||
state[3].unshift(state[3].pop());
|
||||
|
||||
// Add round key
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] ^= keyschedule[nrounds * 4 + col][row];
|
||||
}
|
||||
|
||||
// Set output
|
||||
for (var row = 0; row < AES._blocksize; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
m[offset + col * 4 + row] = state[row][col];
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_decryptblock: function (c, offset) {
|
||||
|
||||
// Set input
|
||||
for (var row = 0; row < AES._blocksize; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] = c[offset + col * 4 + row];
|
||||
}
|
||||
|
||||
// Add round key
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] ^= keyschedule[nrounds * 4 + col][row];
|
||||
}
|
||||
|
||||
for (var round = 1; round < nrounds; round++) {
|
||||
|
||||
// Inv shift rows
|
||||
state[1].unshift(state[1].pop());
|
||||
state[2].push(state[2].shift());
|
||||
state[2].push(state[2].shift());
|
||||
state[3].push(state[3].shift());
|
||||
|
||||
// Inv sub bytes
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] = INVSBOX[state[row][col]];
|
||||
}
|
||||
|
||||
// Add round key
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] ^= keyschedule[(nrounds - round) * 4 + col][row];
|
||||
}
|
||||
|
||||
// Inv mix columns
|
||||
for (var col = 0; col < 4; col++) {
|
||||
|
||||
var s0 = state[0][col],
|
||||
s1 = state[1][col],
|
||||
s2 = state[2][col],
|
||||
s3 = state[3][col];
|
||||
|
||||
state[0][col] = MULTE[s0] ^ MULTB[s1] ^ MULTD[s2] ^ MULT9[s3];
|
||||
state[1][col] = MULT9[s0] ^ MULTE[s1] ^ MULTB[s2] ^ MULTD[s3];
|
||||
state[2][col] = MULTD[s0] ^ MULT9[s1] ^ MULTE[s2] ^ MULTB[s3];
|
||||
state[3][col] = MULTB[s0] ^ MULTD[s1] ^ MULT9[s2] ^ MULTE[s3];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Inv shift rows
|
||||
state[1].unshift(state[1].pop());
|
||||
state[2].push(state[2].shift());
|
||||
state[2].push(state[2].shift());
|
||||
state[3].push(state[3].shift());
|
||||
|
||||
// Inv sub bytes
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] = INVSBOX[state[row][col]];
|
||||
}
|
||||
|
||||
// Add round key
|
||||
for (var row = 0; row < 4; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
state[row][col] ^= keyschedule[col][row];
|
||||
}
|
||||
|
||||
// Set output
|
||||
for (var row = 0; row < AES._blocksize; row++) {
|
||||
for (var col = 0; col < 4; col++)
|
||||
c[offset + col * 4 + row] = state[row][col];
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Private methods
|
||||
*/
|
||||
|
||||
_init: function (k) {
|
||||
keylength = k.length / 4;
|
||||
nrounds = keylength + 6;
|
||||
AES._keyexpansion(k);
|
||||
},
|
||||
|
||||
// Generate a key schedule
|
||||
_keyexpansion: function (k) {
|
||||
|
||||
keyschedule = [];
|
||||
|
||||
for (var row = 0; row < keylength; row++) {
|
||||
keyschedule[row] = [
|
||||
k[row * 4],
|
||||
k[row * 4 + 1],
|
||||
k[row * 4 + 2],
|
||||
k[row * 4 + 3]
|
||||
];
|
||||
}
|
||||
|
||||
for (var row = keylength; row < AES._blocksize * (nrounds + 1); row++) {
|
||||
|
||||
var temp = [
|
||||
keyschedule[row - 1][0],
|
||||
keyschedule[row - 1][1],
|
||||
keyschedule[row - 1][2],
|
||||
keyschedule[row - 1][3]
|
||||
];
|
||||
|
||||
if (row % keylength == 0) {
|
||||
|
||||
// Rot word
|
||||
temp.push(temp.shift());
|
||||
|
||||
// Sub word
|
||||
temp[0] = SBOX[temp[0]];
|
||||
temp[1] = SBOX[temp[1]];
|
||||
temp[2] = SBOX[temp[2]];
|
||||
temp[3] = SBOX[temp[3]];
|
||||
|
||||
temp[0] ^= RCON[row / keylength];
|
||||
|
||||
} else if (keylength > 6 && row % keylength == 4) {
|
||||
|
||||
// Sub word
|
||||
temp[0] = SBOX[temp[0]];
|
||||
temp[1] = SBOX[temp[1]];
|
||||
temp[2] = SBOX[temp[2]];
|
||||
temp[3] = SBOX[temp[3]];
|
||||
|
||||
}
|
||||
|
||||
keyschedule[row] = [
|
||||
keyschedule[row - keylength][0] ^ temp[0],
|
||||
keyschedule[row - keylength][1] ^ temp[1],
|
||||
keyschedule[row - keylength][2] ^ temp[2],
|
||||
keyschedule[row - keylength][3] ^ temp[3]
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})();
|
410
src/cryptojs.blockmodes.js
Normal file
410
src/cryptojs.blockmodes.js
Normal file
|
@ -0,0 +1,410 @@
|
|||
/*!
|
||||
* Crypto-JS 2.5.4 BlockModes.js
|
||||
* contribution from Simon Greatrix
|
||||
*/
|
||||
|
||||
(function (C) {
|
||||
|
||||
// Create pad namespace
|
||||
var C_pad = C.pad = {};
|
||||
|
||||
// Calculate the number of padding bytes required.
|
||||
function _requiredPadding(cipher, message) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4;
|
||||
var reqd = blockSizeInBytes - message.length % blockSizeInBytes;
|
||||
return reqd;
|
||||
}
|
||||
|
||||
// Remove padding when the final byte gives the number of padding bytes.
|
||||
var _unpadLength = function (cipher, message, alg, padding) {
|
||||
var pad = message.pop();
|
||||
if (pad == 0) {
|
||||
throw new Error("Invalid zero-length padding specified for " + alg
|
||||
+ ". Wrong cipher specification or key used?");
|
||||
}
|
||||
var maxPad = cipher._blocksize * 4;
|
||||
if (pad > maxPad) {
|
||||
throw new Error("Invalid padding length of " + pad
|
||||
+ " specified for " + alg
|
||||
+ ". Wrong cipher specification or key used?");
|
||||
}
|
||||
for (var i = 1; i < pad; i++) {
|
||||
var b = message.pop();
|
||||
if (padding != undefined && padding != b) {
|
||||
throw new Error("Invalid padding byte of 0x" + b.toString(16)
|
||||
+ " specified for " + alg
|
||||
+ ". Wrong cipher specification or key used?");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// No-operation padding, used for stream ciphers
|
||||
C_pad.NoPadding = {
|
||||
pad: function (cipher, message) { },
|
||||
unpad: function (cipher, message) { }
|
||||
};
|
||||
|
||||
// Zero Padding.
|
||||
//
|
||||
// If the message is not an exact number of blocks, the final block is
|
||||
// completed with 0x00 bytes. There is no unpadding.
|
||||
C_pad.ZeroPadding = {
|
||||
pad: function (cipher, message) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4;
|
||||
var reqd = message.length % blockSizeInBytes;
|
||||
if (reqd != 0) {
|
||||
for (reqd = blockSizeInBytes - reqd; reqd > 0; reqd--) {
|
||||
message.push(0x00);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
unpad: function (cipher, message) {
|
||||
while (message[message.length - 1] == 0) {
|
||||
message.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ISO/IEC 7816-4 padding.
|
||||
//
|
||||
// Pads the plain text with an 0x80 byte followed by as many 0x00
|
||||
// bytes are required to complete the block.
|
||||
C_pad.iso7816 = {
|
||||
pad: function (cipher, message) {
|
||||
var reqd = _requiredPadding(cipher, message);
|
||||
message.push(0x80);
|
||||
for (; reqd > 1; reqd--) {
|
||||
message.push(0x00);
|
||||
}
|
||||
},
|
||||
|
||||
unpad: function (cipher, message) {
|
||||
var padLength;
|
||||
for (padLength = cipher._blocksize * 4; padLength > 0; padLength--) {
|
||||
var b = message.pop();
|
||||
if (b == 0x80) return;
|
||||
if (b != 0x00) {
|
||||
throw new Error("ISO-7816 padding byte must be 0, not 0x" + b.toString(16) + ". Wrong cipher specification or key used?");
|
||||
}
|
||||
}
|
||||
throw new Error("ISO-7816 padded beyond cipher block size. Wrong cipher specification or key used?");
|
||||
}
|
||||
};
|
||||
|
||||
// ANSI X.923 padding
|
||||
//
|
||||
// The final block is padded with zeros except for the last byte of the
|
||||
// last block which contains the number of padding bytes.
|
||||
C_pad.ansix923 = {
|
||||
pad: function (cipher, message) {
|
||||
var reqd = _requiredPadding(cipher, message);
|
||||
for (var i = 1; i < reqd; i++) {
|
||||
message.push(0x00);
|
||||
}
|
||||
message.push(reqd);
|
||||
},
|
||||
|
||||
unpad: function (cipher, message) {
|
||||
_unpadLength(cipher, message, "ANSI X.923", 0);
|
||||
}
|
||||
};
|
||||
|
||||
// ISO 10126
|
||||
//
|
||||
// The final block is padded with random bytes except for the last
|
||||
// byte of the last block which contains the number of padding bytes.
|
||||
C_pad.iso10126 = {
|
||||
pad: function (cipher, message) {
|
||||
var reqd = _requiredPadding(cipher, message);
|
||||
for (var i = 1; i < reqd; i++) {
|
||||
message.push(Math.floor(Math.random() * 256));
|
||||
}
|
||||
message.push(reqd);
|
||||
},
|
||||
|
||||
unpad: function (cipher, message) {
|
||||
_unpadLength(cipher, message, "ISO 10126", undefined);
|
||||
}
|
||||
};
|
||||
|
||||
// PKCS7 padding
|
||||
//
|
||||
// PKCS7 is described in RFC 5652. Padding is in whole bytes. The
|
||||
// value of each added byte is the number of bytes that are added,
|
||||
// i.e. N bytes, each of value N are added.
|
||||
C_pad.pkcs7 = {
|
||||
pad: function (cipher, message) {
|
||||
var reqd = _requiredPadding(cipher, message);
|
||||
for (var i = 0; i < reqd; i++) {
|
||||
message.push(reqd);
|
||||
}
|
||||
},
|
||||
|
||||
unpad: function (cipher, message) {
|
||||
_unpadLength(cipher, message, "PKCS 7", message[message.length - 1]);
|
||||
}
|
||||
};
|
||||
|
||||
// Create mode namespace
|
||||
var C_mode = C.mode = {};
|
||||
|
||||
/**
|
||||
* Mode base "class".
|
||||
*/
|
||||
var Mode = C_mode.Mode = function (padding) {
|
||||
if (padding) {
|
||||
this._padding = padding;
|
||||
}
|
||||
};
|
||||
|
||||
Mode.prototype = {
|
||||
encrypt: function (cipher, m, iv) {
|
||||
this._padding.pad(cipher, m);
|
||||
this._doEncrypt(cipher, m, iv);
|
||||
},
|
||||
|
||||
decrypt: function (cipher, m, iv) {
|
||||
this._doDecrypt(cipher, m, iv);
|
||||
this._padding.unpad(cipher, m);
|
||||
},
|
||||
|
||||
// Default padding
|
||||
_padding: C_pad.iso7816
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Electronic Code Book mode.
|
||||
*
|
||||
* ECB applies the cipher directly against each block of the input.
|
||||
*
|
||||
* ECB does not require an initialization vector.
|
||||
*/
|
||||
var ECB = C_mode.ECB = function () {
|
||||
// Call parent constructor
|
||||
Mode.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Inherit from Mode
|
||||
var ECB_prototype = ECB.prototype = new Mode;
|
||||
|
||||
// Concrete steps for Mode template
|
||||
ECB_prototype._doEncrypt = function (cipher, m, iv) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4;
|
||||
// Encrypt each block
|
||||
for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
|
||||
cipher._encryptblock(m, offset);
|
||||
}
|
||||
};
|
||||
ECB_prototype._doDecrypt = function (cipher, c, iv) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4;
|
||||
// Decrypt each block
|
||||
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
|
||||
cipher._decryptblock(c, offset);
|
||||
}
|
||||
};
|
||||
|
||||
// ECB never uses an IV
|
||||
ECB_prototype.fixOptions = function (options) {
|
||||
options.iv = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cipher block chaining
|
||||
*
|
||||
* The first block is XORed with the IV. Subsequent blocks are XOR with the
|
||||
* previous cipher output.
|
||||
*/
|
||||
var CBC = C_mode.CBC = function () {
|
||||
// Call parent constructor
|
||||
Mode.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Inherit from Mode
|
||||
var CBC_prototype = CBC.prototype = new Mode;
|
||||
|
||||
// Concrete steps for Mode template
|
||||
CBC_prototype._doEncrypt = function (cipher, m, iv) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4;
|
||||
|
||||
// Encrypt each block
|
||||
for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
|
||||
if (offset == 0) {
|
||||
// XOR first block using IV
|
||||
for (var i = 0; i < blockSizeInBytes; i++)
|
||||
m[i] ^= iv[i];
|
||||
} else {
|
||||
// XOR this block using previous crypted block
|
||||
for (var i = 0; i < blockSizeInBytes; i++)
|
||||
m[offset + i] ^= m[offset + i - blockSizeInBytes];
|
||||
}
|
||||
// Encrypt block
|
||||
cipher._encryptblock(m, offset);
|
||||
}
|
||||
};
|
||||
CBC_prototype._doDecrypt = function (cipher, c, iv) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4;
|
||||
|
||||
// At the start, the previously crypted block is the IV
|
||||
var prevCryptedBlock = iv;
|
||||
|
||||
// Decrypt each block
|
||||
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
|
||||
// Save this crypted block
|
||||
var thisCryptedBlock = c.slice(offset, offset + blockSizeInBytes);
|
||||
// Decrypt block
|
||||
cipher._decryptblock(c, offset);
|
||||
// XOR decrypted block using previous crypted block
|
||||
for (var i = 0; i < blockSizeInBytes; i++) {
|
||||
c[offset + i] ^= prevCryptedBlock[i];
|
||||
}
|
||||
prevCryptedBlock = thisCryptedBlock;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cipher feed back
|
||||
*
|
||||
* The cipher output is XORed with the plain text to produce the cipher output,
|
||||
* which is then fed back into the cipher to produce a bit pattern to XOR the
|
||||
* next block with.
|
||||
*
|
||||
* This is a stream cipher mode and does not require padding.
|
||||
*/
|
||||
var CFB = C_mode.CFB = function () {
|
||||
// Call parent constructor
|
||||
Mode.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Inherit from Mode
|
||||
var CFB_prototype = CFB.prototype = new Mode;
|
||||
|
||||
// Override padding
|
||||
CFB_prototype._padding = C_pad.NoPadding;
|
||||
|
||||
// Concrete steps for Mode template
|
||||
CFB_prototype._doEncrypt = function (cipher, m, iv) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4,
|
||||
keystream = iv.slice(0);
|
||||
|
||||
// Encrypt each byte
|
||||
for (var i = 0; i < m.length; i++) {
|
||||
|
||||
var j = i % blockSizeInBytes;
|
||||
if (j == 0) cipher._encryptblock(keystream, 0);
|
||||
|
||||
m[i] ^= keystream[j];
|
||||
keystream[j] = m[i];
|
||||
}
|
||||
};
|
||||
CFB_prototype._doDecrypt = function (cipher, c, iv) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4,
|
||||
keystream = iv.slice(0);
|
||||
|
||||
// Encrypt each byte
|
||||
for (var i = 0; i < c.length; i++) {
|
||||
|
||||
var j = i % blockSizeInBytes;
|
||||
if (j == 0) cipher._encryptblock(keystream, 0);
|
||||
|
||||
var b = c[i];
|
||||
c[i] ^= keystream[j];
|
||||
keystream[j] = b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Output feed back
|
||||
*
|
||||
* The cipher repeatedly encrypts its own output. The output is XORed with the
|
||||
* plain text to produce the cipher text.
|
||||
*
|
||||
* This is a stream cipher mode and does not require padding.
|
||||
*/
|
||||
var OFB = C_mode.OFB = function () {
|
||||
// Call parent constructor
|
||||
Mode.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Inherit from Mode
|
||||
var OFB_prototype = OFB.prototype = new Mode;
|
||||
|
||||
// Override padding
|
||||
OFB_prototype._padding = C_pad.NoPadding;
|
||||
|
||||
// Concrete steps for Mode template
|
||||
OFB_prototype._doEncrypt = function (cipher, m, iv) {
|
||||
|
||||
var blockSizeInBytes = cipher._blocksize * 4,
|
||||
keystream = iv.slice(0);
|
||||
|
||||
// Encrypt each byte
|
||||
for (var i = 0; i < m.length; i++) {
|
||||
|
||||
// Generate keystream
|
||||
if (i % blockSizeInBytes == 0)
|
||||
cipher._encryptblock(keystream, 0);
|
||||
|
||||
// Encrypt byte
|
||||
m[i] ^= keystream[i % blockSizeInBytes];
|
||||
|
||||
}
|
||||
};
|
||||
OFB_prototype._doDecrypt = OFB_prototype._doEncrypt;
|
||||
|
||||
/**
|
||||
* Counter
|
||||
* @author Gergely Risko
|
||||
*
|
||||
* After every block the last 4 bytes of the IV is increased by one
|
||||
* with carry and that IV is used for the next block.
|
||||
*
|
||||
* This is a stream cipher mode and does not require padding.
|
||||
*/
|
||||
var CTR = C_mode.CTR = function () {
|
||||
// Call parent constructor
|
||||
Mode.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Inherit from Mode
|
||||
var CTR_prototype = CTR.prototype = new Mode;
|
||||
|
||||
// Override padding
|
||||
CTR_prototype._padding = C_pad.NoPadding;
|
||||
|
||||
CTR_prototype._doEncrypt = function (cipher, m, iv) {
|
||||
var blockSizeInBytes = cipher._blocksize * 4;
|
||||
var counter = iv.slice(0);
|
||||
|
||||
for (var i = 0; i < m.length; ) {
|
||||
// do not lose iv
|
||||
var keystream = counter.slice(0);
|
||||
|
||||
// Generate keystream for next block
|
||||
cipher._encryptblock(keystream, 0);
|
||||
|
||||
// XOR keystream with block
|
||||
for (var j = 0; i < m.length && j < blockSizeInBytes; j++, i++) {
|
||||
m[i] ^= keystream[j];
|
||||
}
|
||||
|
||||
// Increase counter
|
||||
if (++(counter[blockSizeInBytes - 1]) == 256) {
|
||||
counter[blockSizeInBytes - 1] = 0;
|
||||
if (++(counter[blockSizeInBytes - 2]) == 256) {
|
||||
counter[blockSizeInBytes - 2] = 0;
|
||||
if (++(counter[blockSizeInBytes - 3]) == 256) {
|
||||
counter[blockSizeInBytes - 3] = 0;
|
||||
++(counter[blockSizeInBytes - 4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
CTR_prototype._doDecrypt = CTR_prototype._doEncrypt;
|
||||
|
||||
})(Crypto);
|
43
src/cryptojs.hmac.js
Normal file
43
src/cryptojs.hmac.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*!
|
||||
* Crypto-JS v2.5.4 HMAC.js
|
||||
* http://code.google.com/p/crypto-js/
|
||||
* Copyright (c) 2009-2013, 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;
|
||||
|
||||
C.HMAC = function (hasher, message, key, options) {
|
||||
|
||||
// Convert to byte arrays
|
||||
if (message.constructor == String) message = UTF8.stringToBytes(message);
|
||||
if (key.constructor == String) key = UTF8.stringToBytes(key);
|
||||
/* else, assume byte arrays already */
|
||||
|
||||
// Allow arbitrary length keys
|
||||
if (key.length > hasher._blocksize * 4)
|
||||
key = hasher(key, { asBytes: true });
|
||||
|
||||
// XOR keys with pad constants
|
||||
var okey = key.slice(0),
|
||||
ikey = key.slice(0);
|
||||
for (var i = 0; i < hasher._blocksize * 4; i++) {
|
||||
okey[i] ^= 0x5C;
|
||||
ikey[i] ^= 0x36;
|
||||
}
|
||||
|
||||
var hmacbytes = hasher(okey.concat(hasher(ikey.concat(message), { asBytes: true })), { asBytes: true });
|
||||
|
||||
return options && options.asBytes ? hmacbytes :
|
||||
options && options.asString ? Binary.bytesToString(hmacbytes) :
|
||||
util.bytesToHex(hmacbytes);
|
||||
|
||||
};
|
||||
|
||||
})();
|
149
src/cryptojs.js
Normal file
149
src/cryptojs.js
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*!
|
||||
* Crypto-JS v2.5.4 Crypto.js
|
||||
* http://code.google.com/p/crypto-js/
|
||||
* Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
|
||||
* http://code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
if (typeof Crypto == "undefined" || !Crypto.util) {
|
||||
(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] & 0xFF) << (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) {
|
||||
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) {
|
||||
// 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 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) & 0xFF);
|
||||
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("");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
}
|
54
src/cryptojs.pbkdf2.js
Normal file
54
src/cryptojs.pbkdf2.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*!
|
||||
* Crypto-JS v2.5.4 PBKDF2.js
|
||||
* http://code.google.com/p/crypto-js/
|
||||
* Copyright (c) 2009-2013, 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;
|
||||
|
||||
C.PBKDF2 = function (password, salt, keylen, options) {
|
||||
|
||||
// Convert to byte arrays
|
||||
if (password.constructor == String) password = UTF8.stringToBytes(password);
|
||||
if (salt.constructor == String) salt = UTF8.stringToBytes(salt);
|
||||
/* else, assume byte arrays already */
|
||||
|
||||
// Defaults
|
||||
var hasher = options && options.hasher || C.SHA1,
|
||||
iterations = options && options.iterations || 1;
|
||||
|
||||
// Pseudo-random function
|
||||
function PRF(password, salt) {
|
||||
return C.HMAC(hasher, salt, password, { asBytes: true });
|
||||
}
|
||||
|
||||
// Generate key
|
||||
var derivedKeyBytes = [],
|
||||
blockindex = 1;
|
||||
while (derivedKeyBytes.length < keylen) {
|
||||
var block = PRF(password, salt.concat(util.wordsToBytes([blockindex])));
|
||||
for (var u = block, i = 1; i < iterations; i++) {
|
||||
u = PRF(password, u);
|
||||
for (var j = 0; j < block.length; j++) block[j] ^= u[j];
|
||||
}
|
||||
derivedKeyBytes = derivedKeyBytes.concat(block);
|
||||
blockindex++;
|
||||
}
|
||||
|
||||
// Truncate excess bytes
|
||||
derivedKeyBytes.length = keylen;
|
||||
|
||||
return options && options.asBytes ? derivedKeyBytes :
|
||||
options && options.asString ? Binary.bytesToString(derivedKeyBytes) :
|
||||
util.bytesToHex(derivedKeyBytes);
|
||||
|
||||
};
|
||||
|
||||
})();
|
164
src/cryptojs.ripemd160.js
Normal file
164
src/cryptojs.ripemd160.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*!
|
||||
* Crypto-JS v2.0.0 RIPEMD-160
|
||||
* 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));
|
||||
}
|
||||
})();
|
135
src/cryptojs.sha256.js
Normal file
135
src/cryptojs.sha256.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*!
|
||||
* Crypto-JS v2.5.4 SHA256.js
|
||||
* http://code.google.com/p/crypto-js/
|
||||
* Copyright (c) 2009-2013, 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) >>> 0;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = (t1 + t2) >>> 0;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
SHA256._digestsize = 32;
|
||||
|
||||
})();
|
644
src/ellipticcurve.js
Normal file
644
src/ellipticcurve.js
Normal file
|
@ -0,0 +1,644 @@
|
|||
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/faa10f0f6a1fff0b9a99fffb9bc30cee33b17212/src/ecdsa.js
|
||||
/*!
|
||||
* Basic Javascript Elliptic Curve implementation
|
||||
* Ported loosely from BouncyCastle's Java EC code
|
||||
* Only Fp curves implemented for now
|
||||
*
|
||||
* Copyright Tom Wu, bitaddress.org BSD License.
|
||||
* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE
|
||||
*/
|
||||
(function () {
|
||||
|
||||
// Constructor function of Global EllipticCurve object
|
||||
var ec = window.EllipticCurve = function () { };
|
||||
|
||||
|
||||
// ----------------
|
||||
// ECFieldElementFp constructor
|
||||
// q instanceof BigInteger
|
||||
// x instanceof BigInteger
|
||||
ec.FieldElementFp = function (q, x) {
|
||||
this.x = x;
|
||||
// TODO if(x.compareTo(q) >= 0) error
|
||||
this.q = q;
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.equals = function (other) {
|
||||
if (other == this) return true;
|
||||
return (this.q.equals(other.q) && this.x.equals(other.x));
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.toBigInteger = function () {
|
||||
return this.x;
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.negate = function () {
|
||||
return new ec.FieldElementFp(this.q, this.x.negate().mod(this.q));
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.add = function (b) {
|
||||
return new ec.FieldElementFp(this.q, this.x.add(b.toBigInteger()).mod(this.q));
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.subtract = function (b) {
|
||||
return new ec.FieldElementFp(this.q, this.x.subtract(b.toBigInteger()).mod(this.q));
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.multiply = function (b) {
|
||||
return new ec.FieldElementFp(this.q, this.x.multiply(b.toBigInteger()).mod(this.q));
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.square = function () {
|
||||
return new ec.FieldElementFp(this.q, this.x.square().mod(this.q));
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.divide = function (b) {
|
||||
return new ec.FieldElementFp(this.q, this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(this.q));
|
||||
};
|
||||
|
||||
ec.FieldElementFp.prototype.getByteLength = function () {
|
||||
return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
|
||||
};
|
||||
|
||||
// D.1.4 91
|
||||
/**
|
||||
* return a sqrt root - the routine verifies that the calculation
|
||||
* returns the right value - if none exists it returns null.
|
||||
*
|
||||
* Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
||||
* Ported to JavaScript by bitaddress.org
|
||||
*/
|
||||
ec.FieldElementFp.prototype.sqrt = function () {
|
||||
if (!this.q.testBit(0)) throw new Error("even value of q");
|
||||
|
||||
// p mod 4 == 3
|
||||
if (this.q.testBit(1)) {
|
||||
// z = g^(u+1) + p, p = 4u + 3
|
||||
var z = new ec.FieldElementFp(this.q, this.x.modPow(this.q.shiftRight(2).add(BigInteger.ONE), this.q));
|
||||
return z.square().equals(this) ? z : null;
|
||||
}
|
||||
|
||||
// p mod 4 == 1
|
||||
var qMinusOne = this.q.subtract(BigInteger.ONE);
|
||||
var legendreExponent = qMinusOne.shiftRight(1);
|
||||
if (!(this.x.modPow(legendreExponent, this.q).equals(BigInteger.ONE))) return null;
|
||||
var u = qMinusOne.shiftRight(2);
|
||||
var k = u.shiftLeft(1).add(BigInteger.ONE);
|
||||
var Q = this.x;
|
||||
var fourQ = Q.shiftLeft(2).mod(this.q);
|
||||
var U, V;
|
||||
|
||||
do {
|
||||
var rand = new SecureRandom();
|
||||
var P;
|
||||
do {
|
||||
P = new BigInteger(this.q.bitLength(), rand);
|
||||
}
|
||||
while (P.compareTo(this.q) >= 0 || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, this.q).equals(qMinusOne)));
|
||||
|
||||
var result = ec.FieldElementFp.fastLucasSequence(this.q, P, Q, k);
|
||||
|
||||
U = result[0];
|
||||
V = result[1];
|
||||
if (V.multiply(V).mod(this.q).equals(fourQ)) {
|
||||
// Integer division by 2, mod q
|
||||
if (V.testBit(0)) {
|
||||
V = V.add(this.q);
|
||||
}
|
||||
V = V.shiftRight(1);
|
||||
return new ec.FieldElementFp(this.q, V);
|
||||
}
|
||||
}
|
||||
while (U.equals(BigInteger.ONE) || U.equals(qMinusOne));
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
||||
* Ported to JavaScript by bitaddress.org
|
||||
*/
|
||||
ec.FieldElementFp.fastLucasSequence = function (p, P, Q, k) {
|
||||
// TODO Research and apply "common-multiplicand multiplication here"
|
||||
|
||||
var n = k.bitLength();
|
||||
var s = k.getLowestSetBit();
|
||||
var Uh = BigInteger.ONE;
|
||||
var Vl = BigInteger.TWO;
|
||||
var Vh = P;
|
||||
var Ql = BigInteger.ONE;
|
||||
var Qh = BigInteger.ONE;
|
||||
|
||||
for (var j = n - 1; j >= s + 1; --j) {
|
||||
Ql = Ql.multiply(Qh).mod(p);
|
||||
if (k.testBit(j)) {
|
||||
Qh = Ql.multiply(Q).mod(p);
|
||||
Uh = Uh.multiply(Vh).mod(p);
|
||||
Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
|
||||
Vh = Vh.multiply(Vh).subtract(Qh.shiftLeft(1)).mod(p);
|
||||
}
|
||||
else {
|
||||
Qh = Ql;
|
||||
Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
|
||||
Vh = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
|
||||
Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
|
||||
}
|
||||
}
|
||||
|
||||
Ql = Ql.multiply(Qh).mod(p);
|
||||
Qh = Ql.multiply(Q).mod(p);
|
||||
Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
|
||||
Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
|
||||
Ql = Ql.multiply(Qh).mod(p);
|
||||
|
||||
for (var j = 1; j <= s; ++j) {
|
||||
Uh = Uh.multiply(Vl).mod(p);
|
||||
Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
|
||||
Ql = Ql.multiply(Ql).mod(p);
|
||||
}
|
||||
|
||||
return [Uh, Vl];
|
||||
};
|
||||
|
||||
// ----------------
|
||||
// ECPointFp constructor
|
||||
ec.PointFp = function (curve, x, y, z, compressed) {
|
||||
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;
|
||||
// compression flag
|
||||
this.compressed = !!compressed;
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.getX = function () {
|
||||
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));
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.getY = function () {
|
||||
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));
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.equals = function (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);
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.isInfinity = function () {
|
||||
if ((this.x == null) && (this.y == null)) return true;
|
||||
return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO);
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.negate = function () {
|
||||
return new ec.PointFp(this.curve, this.x, this.y.negate(), this.z);
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.add = function (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 ec.PointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3);
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.twice = function () {
|
||||
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 ec.PointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3);
|
||||
};
|
||||
|
||||
// Simple NAF (Non-Adjacent Form) multiplication algorithm
|
||||
// TODO: modularize the multiplication algorithm
|
||||
ec.PointFp.prototype.multiply = 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.add(hBit ? this : neg);
|
||||
}
|
||||
}
|
||||
|
||||
return R;
|
||||
};
|
||||
|
||||
// Compute this*j + x*k (simultaneous multiplication)
|
||||
ec.PointFp.prototype.multiplyTwo = function (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;
|
||||
};
|
||||
|
||||
// patched by bitaddress.org and Casascius for use with Bitcoin.ECKey
|
||||
// patched by coretechs to support compressed public keys
|
||||
ec.PointFp.prototype.getEncoded = function (compressed) {
|
||||
var x = this.getX().toBigInteger();
|
||||
var y = this.getY().toBigInteger();
|
||||
var len = 32; // integerToBytes will zero pad if integer is less than 32 bytes. 32 bytes length is required by the Bitcoin protocol.
|
||||
var enc = ec.integerToBytes(x, len);
|
||||
|
||||
// when compressed prepend byte depending if y point is even or odd
|
||||
if (compressed) {
|
||||
if (y.isEven()) {
|
||||
enc.unshift(0x02);
|
||||
}
|
||||
else {
|
||||
enc.unshift(0x03);
|
||||
}
|
||||
}
|
||||
else {
|
||||
enc.unshift(0x04);
|
||||
enc = enc.concat(ec.integerToBytes(y, len)); // uncompressed public key appends the bytes of the y point
|
||||
}
|
||||
return enc;
|
||||
};
|
||||
|
||||
ec.PointFp.decodeFrom = function (curve, enc) {
|
||||
var type = enc[0];
|
||||
var dataLen = enc.length - 1;
|
||||
|
||||
// Extract x and y as byte arrays
|
||||
var xBa = enc.slice(1, 1 + dataLen / 2);
|
||||
var yBa = enc.slice(1 + dataLen / 2, 1 + dataLen);
|
||||
|
||||
// Prepend zero byte to prevent interpretation as negative integer
|
||||
xBa.unshift(0);
|
||||
yBa.unshift(0);
|
||||
|
||||
// Convert to BigIntegers
|
||||
var x = new BigInteger(xBa);
|
||||
var y = new BigInteger(yBa);
|
||||
|
||||
// Return point
|
||||
return new ec.PointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y));
|
||||
};
|
||||
|
||||
ec.PointFp.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);
|
||||
|
||||
var x3 = gamma.square().subtract(this.x).subtract(b.x);
|
||||
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
|
||||
|
||||
return new ec.PointFp(this.curve, x3, y3);
|
||||
};
|
||||
|
||||
ec.PointFp.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 ec.PointFp(this.curve, x3, y3);
|
||||
};
|
||||
|
||||
ec.PointFp.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;
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.isOnCurve = function () {
|
||||
var x = this.getX().toBigInteger();
|
||||
var y = this.getY().toBigInteger();
|
||||
var a = this.curve.getA().toBigInteger();
|
||||
var b = this.curve.getB().toBigInteger();
|
||||
var n = this.curve.getQ();
|
||||
var lhs = y.multiply(y).mod(n);
|
||||
var rhs = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(n);
|
||||
return lhs.equals(rhs);
|
||||
};
|
||||
|
||||
ec.PointFp.prototype.toString = function () {
|
||||
return '(' + this.getX().toBigInteger().toString() + ',' + this.getY().toBigInteger().toString() + ')';
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate an elliptic curve point.
|
||||
*
|
||||
* See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive
|
||||
*/
|
||||
ec.PointFp.prototype.validate = function () {
|
||||
var n = this.curve.getQ();
|
||||
|
||||
// Check Q != O
|
||||
if (this.isInfinity()) {
|
||||
throw new Error("Point is at infinity.");
|
||||
}
|
||||
|
||||
// Check coordinate bounds
|
||||
var x = this.getX().toBigInteger();
|
||||
var y = this.getY().toBigInteger();
|
||||
if (x.compareTo(BigInteger.ONE) < 0 || x.compareTo(n.subtract(BigInteger.ONE)) > 0) {
|
||||
throw new Error('x coordinate out of bounds');
|
||||
}
|
||||
if (y.compareTo(BigInteger.ONE) < 0 || y.compareTo(n.subtract(BigInteger.ONE)) > 0) {
|
||||
throw new Error('y coordinate out of bounds');
|
||||
}
|
||||
|
||||
// Check y^2 = x^3 + ax + b (mod n)
|
||||
if (!this.isOnCurve()) {
|
||||
throw new Error("Point is not on the curve.");
|
||||
}
|
||||
|
||||
// Check nQ = 0 (Q is a scalar multiple of G)
|
||||
if (this.multiply(n).isInfinity()) {
|
||||
// TODO: This check doesn't work - fix.
|
||||
throw new Error("Point is not a scalar multiple of G.");
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// ----------------
|
||||
// ECCurveFp constructor
|
||||
ec.CurveFp = function (q, a, b) {
|
||||
this.q = q;
|
||||
this.a = this.fromBigInteger(a);
|
||||
this.b = this.fromBigInteger(b);
|
||||
this.infinity = new ec.PointFp(this, null, null);
|
||||
}
|
||||
|
||||
ec.CurveFp.prototype.getQ = function () {
|
||||
return this.q;
|
||||
};
|
||||
|
||||
ec.CurveFp.prototype.getA = function () {
|
||||
return this.a;
|
||||
};
|
||||
|
||||
ec.CurveFp.prototype.getB = function () {
|
||||
return this.b;
|
||||
};
|
||||
|
||||
ec.CurveFp.prototype.equals = function (other) {
|
||||
if (other == this) return true;
|
||||
return (this.q.equals(other.q) && this.a.equals(other.a) && this.b.equals(other.b));
|
||||
};
|
||||
|
||||
ec.CurveFp.prototype.getInfinity = function () {
|
||||
return this.infinity;
|
||||
};
|
||||
|
||||
ec.CurveFp.prototype.fromBigInteger = function (x) {
|
||||
return new ec.FieldElementFp(this.q, x);
|
||||
};
|
||||
|
||||
// for now, work with hex strings because they're easier in JS
|
||||
// compressed support added by bitaddress.org
|
||||
ec.CurveFp.prototype.decodePointHex = function (s) {
|
||||
var firstByte = parseInt(s.substr(0, 2), 16);
|
||||
switch (firstByte) { // first byte
|
||||
case 0:
|
||||
return this.infinity;
|
||||
case 2: // compressed
|
||||
case 3: // compressed
|
||||
var yTilde = firstByte & 1;
|
||||
var xHex = s.substr(2, s.length - 2);
|
||||
var X1 = new BigInteger(xHex, 16);
|
||||
return this.decompressPoint(yTilde, X1);
|
||||
case 4: // uncompressed
|
||||
case 6: // hybrid
|
||||
case 7: // hybrid
|
||||
var len = (s.length - 2) / 2;
|
||||
var xHex = s.substr(2, len);
|
||||
var yHex = s.substr(len + 2, len);
|
||||
|
||||
return new ec.PointFp(this,
|
||||
this.fromBigInteger(new BigInteger(xHex, 16)),
|
||||
this.fromBigInteger(new BigInteger(yHex, 16)));
|
||||
|
||||
default: // unsupported
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
||||
* Ported to JavaScript by bitaddress.org
|
||||
*
|
||||
* Number yTilde
|
||||
* BigInteger X1
|
||||
*/
|
||||
ec.CurveFp.prototype.decompressPoint = function (yTilde, X1) {
|
||||
var x = this.fromBigInteger(X1);
|
||||
var alpha = x.multiply(x.square().add(this.getA())).add(this.getB());
|
||||
var beta = alpha.sqrt();
|
||||
// if we can't find a sqrt we haven't got a point on the curve - run!
|
||||
if (beta == null) throw new Error("Invalid point compression");
|
||||
var betaValue = beta.toBigInteger();
|
||||
var bit0 = betaValue.testBit(0) ? 1 : 0;
|
||||
if (bit0 != yTilde) {
|
||||
// Use the other root
|
||||
beta = this.fromBigInteger(this.getQ().subtract(betaValue));
|
||||
}
|
||||
return new ec.PointFp(this, x, beta, null, true);
|
||||
};
|
||||
|
||||
|
||||
ec.fromHex = function (s) { return new BigInteger(s, 16); };
|
||||
|
||||
ec.integerToBytes = function (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;
|
||||
};
|
||||
|
||||
|
||||
// Named EC curves
|
||||
// ----------------
|
||||
// X9ECParameters constructor
|
||||
ec.X9Parameters = function (curve, g, n, h) {
|
||||
this.curve = curve;
|
||||
this.g = g;
|
||||
this.n = n;
|
||||
this.h = h;
|
||||
}
|
||||
ec.X9Parameters.prototype.getCurve = function () { return this.curve; };
|
||||
ec.X9Parameters.prototype.getG = function () { return this.g; };
|
||||
ec.X9Parameters.prototype.getN = function () { return this.n; };
|
||||
ec.X9Parameters.prototype.getH = function () { return this.h; };
|
||||
|
||||
// secp256k1 is the Curve used by Bitcoin
|
||||
ec.secNamedCurves = {
|
||||
// used by Bitcoin
|
||||
"secp256k1": function () {
|
||||
// p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
|
||||
var p = ec.fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
|
||||
var a = BigInteger.ZERO;
|
||||
var b = ec.fromHex("7");
|
||||
var n = ec.fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
|
||||
var h = BigInteger.ONE;
|
||||
var curve = new ec.CurveFp(p, a, b);
|
||||
var G = curve.decodePointHex("04"
|
||||
+ "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
|
||||
+ "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
|
||||
return new ec.X9Parameters(curve, G, n, h);
|
||||
}
|
||||
};
|
||||
|
||||
// secp256k1 called by Bitcoin's ECKEY
|
||||
ec.getSECCurveByName = function (name) {
|
||||
if (ec.secNamedCurves[name] == undefined) return null;
|
||||
return ec.secNamedCurves[name]();
|
||||
}
|
||||
})();
|
162
src/main.css
Normal file
162
src/main.css
Normal file
File diff suppressed because one or more lines are too long
61
src/ninja.brainwallet.js
Normal file
61
src/ninja.brainwallet.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
ninja.wallets.brainwallet = {
|
||||
open: function () {
|
||||
document.getElementById("brainarea").style.display = "block";
|
||||
document.getElementById("brainpassphrase").focus();
|
||||
},
|
||||
|
||||
close: function () {
|
||||
document.getElementById("brainarea").style.display = "none";
|
||||
},
|
||||
|
||||
minPassphraseLength: 15,
|
||||
|
||||
view: function () {
|
||||
var key = document.getElementById("brainpassphrase").value.toString().replace(/^\s+|\s+$/g, ""); // trim white space
|
||||
document.getElementById("brainpassphrase").value = key;
|
||||
var keyConfirm = document.getElementById("brainpassphraseconfirm").value.toString().replace(/^\s+|\s+$/g, ""); // trim white space
|
||||
document.getElementById("brainpassphraseconfirm").value = keyConfirm;
|
||||
|
||||
if (key == keyConfirm || document.getElementById("brainpassphraseshow").checked) {
|
||||
// enforce a minimum passphrase length
|
||||
if (key.length >= ninja.wallets.brainwallet.minPassphraseLength) {
|
||||
var bytes = Crypto.SHA256(key, { asBytes: true });
|
||||
var btcKey = new Bitcoin.ECKey(bytes);
|
||||
var bitcoinAddress = btcKey.getBitcoinAddress();
|
||||
var privWif = btcKey.getBitcoinWalletImportFormat();
|
||||
document.getElementById("brainbtcaddress").innerHTML = bitcoinAddress;
|
||||
document.getElementById("brainbtcprivwif").innerHTML = privWif;
|
||||
ninja.qrCode.showQrCode({
|
||||
"brainqrcodepublic": bitcoinAddress,
|
||||
"brainqrcodeprivate": privWif
|
||||
});
|
||||
document.getElementById("brainkeyarea").style.visibility = "visible";
|
||||
}
|
||||
else {
|
||||
alert(ninja.translator.get("brainalertpassphrasetooshort"));
|
||||
ninja.wallets.brainwallet.clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert(ninja.translator.get("brainalertpassphrasedoesnotmatch"));
|
||||
ninja.wallets.brainwallet.clear();
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
document.getElementById("brainkeyarea").style.visibility = "hidden";
|
||||
},
|
||||
|
||||
showToggle: function (element) {
|
||||
if (element.checked) {
|
||||
document.getElementById("brainpassphrase").setAttribute("type", "text");
|
||||
document.getElementById("brainpassphraseconfirm").style.visibility = "hidden";
|
||||
document.getElementById("brainlabelconfirm").style.visibility = "hidden";
|
||||
}
|
||||
else {
|
||||
document.getElementById("brainpassphrase").setAttribute("type", "password");
|
||||
document.getElementById("brainpassphraseconfirm").style.visibility = "visible";
|
||||
document.getElementById("brainlabelconfirm").style.visibility = "visible";
|
||||
}
|
||||
}
|
||||
};
|
73
src/ninja.bulkwallet.js
Normal file
73
src/ninja.bulkwallet.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
ninja.wallets.bulkwallet = {
|
||||
open: function () {
|
||||
document.getElementById("bulkarea").style.display = "block";
|
||||
// show a default CSV list if the text area is empty
|
||||
if (document.getElementById("bulktextarea").value == "") {
|
||||
// return control of the thread to the browser to render the tab switch UI then build a default CSV list
|
||||
setTimeout(function () { ninja.wallets.bulkwallet.buildCSV(3, 1, document.getElementById("bulkcompressed").checked); }, 200);
|
||||
}
|
||||
},
|
||||
|
||||
close: function () {
|
||||
document.getElementById("bulkarea").style.display = "none";
|
||||
},
|
||||
|
||||
// use this function to bulk generate addresses
|
||||
// rowLimit: number of Bitcoin Addresses to generate
|
||||
// startIndex: add this number to the row index for output purposes
|
||||
// returns:
|
||||
// index,bitcoinAddress,privateKeyWif
|
||||
buildCSV: function (rowLimit, startIndex, compressedAddrs) {
|
||||
var bulkWallet = ninja.wallets.bulkwallet;
|
||||
document.getElementById("bulktextarea").value = ninja.translator.get("bulkgeneratingaddresses") + rowLimit;
|
||||
bulkWallet.csv = [];
|
||||
bulkWallet.csvRowLimit = rowLimit;
|
||||
bulkWallet.csvRowsRemaining = rowLimit;
|
||||
bulkWallet.csvStartIndex = --startIndex;
|
||||
bulkWallet.compressedAddrs = !!compressedAddrs;
|
||||
setTimeout(bulkWallet.batchCSV, 0);
|
||||
},
|
||||
|
||||
csv: [],
|
||||
csvRowsRemaining: null, // use to keep track of how many rows are left to process when building a large CSV array
|
||||
csvRowLimit: 0,
|
||||
csvStartIndex: 0,
|
||||
|
||||
batchCSV: function () {
|
||||
var bulkWallet = ninja.wallets.bulkwallet;
|
||||
if (bulkWallet.csvRowsRemaining > 0) {
|
||||
bulkWallet.csvRowsRemaining--;
|
||||
var key = new Bitcoin.ECKey(false);
|
||||
key.setCompressed(bulkWallet.compressedAddrs);
|
||||
|
||||
bulkWallet.csv.push((bulkWallet.csvRowLimit - bulkWallet.csvRowsRemaining + bulkWallet.csvStartIndex)
|
||||
+ ",\"" + key.getBitcoinAddress() + "\",\"" + key.toString("wif")
|
||||
//+ "\",\"" + key.toString("wifcomp") // uncomment these lines to add different private key formats to the CSV
|
||||
//+ "\",\"" + key.getBitcoinHexFormat()
|
||||
//+ "\",\"" + key.toString("base64")
|
||||
+ "\"");
|
||||
|
||||
document.getElementById("bulktextarea").value = ninja.translator.get("bulkgeneratingaddresses") + bulkWallet.csvRowsRemaining;
|
||||
|
||||
// release thread to browser to render UI
|
||||
setTimeout(bulkWallet.batchCSV, 0);
|
||||
}
|
||||
// processing is finished so put CSV in text area
|
||||
else if (bulkWallet.csvRowsRemaining === 0) {
|
||||
document.getElementById("bulktextarea").value = bulkWallet.csv.join("\n");
|
||||
}
|
||||
},
|
||||
|
||||
openCloseFaq: function (faqNum) {
|
||||
// do close
|
||||
if (document.getElementById("bulka" + faqNum).style.display == "block") {
|
||||
document.getElementById("bulka" + faqNum).style.display = "none";
|
||||
document.getElementById("bulke" + faqNum).setAttribute("class", "more");
|
||||
}
|
||||
// do open
|
||||
else {
|
||||
document.getElementById("bulka" + faqNum).style.display = "block";
|
||||
document.getElementById("bulke" + faqNum).setAttribute("class", "less");
|
||||
}
|
||||
}
|
||||
};
|
124
src/ninja.detailwallet.js
Normal file
124
src/ninja.detailwallet.js
Normal file
|
@ -0,0 +1,124 @@
|
|||
ninja.wallets.detailwallet = {
|
||||
open: function () {
|
||||
document.getElementById("detailarea").style.display = "block";
|
||||
document.getElementById("detailprivkey").focus();
|
||||
},
|
||||
|
||||
close: function () {
|
||||
document.getElementById("detailarea").style.display = "none";
|
||||
},
|
||||
|
||||
viewDetails: function () {
|
||||
var bip38 = false;
|
||||
var key = document.getElementById("detailprivkey").value.toString().replace(/^\s+|\s+$/g, ""); // trim white space
|
||||
if (key == "") {
|
||||
ninja.wallets.detailwallet.clear();
|
||||
return;
|
||||
}
|
||||
document.getElementById("detailprivkey").value = key;
|
||||
if (Bitcoin.ECKey.isMiniFormat(key)) {
|
||||
// show Private Key Mini Format
|
||||
document.getElementById("detailprivmini").innerHTML = key;
|
||||
document.getElementById("detailmini").style.display = "block";
|
||||
document.getElementById("detailbip38commands").style.display = "none";
|
||||
}
|
||||
else if (ninja.privateKey.isBIP38Format(key)) {
|
||||
if (document.getElementById("detailbip38commands").style.display != "block") {
|
||||
document.getElementById("detailbip38commands").style.display = "block";
|
||||
document.getElementById("detailprivkeypassphrase").focus();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
bip38 = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// hide Private Key Mini Format
|
||||
document.getElementById("detailmini").style.display = "none";
|
||||
document.getElementById("detailbip38commands").style.display = "none";
|
||||
}
|
||||
|
||||
if (bip38) {
|
||||
var passphrase = document.getElementById("detailprivkeypassphrase").value.toString().replace(/^\s+|\s+$/g, ""); // trim white space
|
||||
if (passphrase == "") {
|
||||
alert(ninja.translator.get("bip38alertpassphraserequired"));
|
||||
return;
|
||||
}
|
||||
ninja.privateKey.BIP38EncryptedKeyToByteArrayAsync(key, passphrase, function (btcKeyOrError) {
|
||||
document.getElementById("busyblock").className = "";
|
||||
if (btcKeyOrError.message) {
|
||||
alert(btcKeyOrError.message);
|
||||
ninja.wallets.detailwallet.clear();
|
||||
} else {
|
||||
ninja.wallets.detailwallet.populateKeyDetails(new Bitcoin.ECKey(btcKeyOrError));
|
||||
}
|
||||
});
|
||||
document.getElementById("busyblock").className = "busy";
|
||||
}
|
||||
else {
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.priv == null) {
|
||||
// enforce a minimum passphrase length
|
||||
if (key.length >= ninja.wallets.brainwallet.minPassphraseLength) {
|
||||
// Deterministic Wallet confirm box to ask if user wants to SHA256 the input to get a private key
|
||||
var usePassphrase = confirm(ninja.translator.get("detailconfirmsha256"));
|
||||
if (usePassphrase) {
|
||||
var bytes = Crypto.SHA256(key, { asBytes: true });
|
||||
var btcKey = new Bitcoin.ECKey(bytes);
|
||||
}
|
||||
else {
|
||||
ninja.wallets.detailwallet.clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert(ninja.translator.get("detailalertnotvalidprivatekey"));
|
||||
ninja.wallets.detailwallet.clear();
|
||||
}
|
||||
}
|
||||
ninja.wallets.detailwallet.populateKeyDetails(btcKey);
|
||||
}
|
||||
},
|
||||
|
||||
populateKeyDetails: function (btcKey) {
|
||||
if (btcKey.priv != null) {
|
||||
btcKey.setCompressed(false);
|
||||
document.getElementById("detailprivhex").innerHTML = btcKey.toString().toUpperCase();
|
||||
document.getElementById("detailprivb64").innerHTML = btcKey.toString("base64");
|
||||
var bitcoinAddress = btcKey.getBitcoinAddress();
|
||||
var wif = btcKey.getBitcoinWalletImportFormat();
|
||||
document.getElementById("detailpubkey").innerHTML = btcKey.getPubKeyHex();
|
||||
document.getElementById("detailaddress").innerHTML = bitcoinAddress;
|
||||
document.getElementById("detailprivwif").innerHTML = wif;
|
||||
btcKey.setCompressed(true);
|
||||
var bitcoinAddressComp = btcKey.getBitcoinAddress();
|
||||
var wifComp = btcKey.getBitcoinWalletImportFormat();
|
||||
document.getElementById("detailpubkeycomp").innerHTML = btcKey.getPubKeyHex();
|
||||
document.getElementById("detailaddresscomp").innerHTML = bitcoinAddressComp;
|
||||
document.getElementById("detailprivwifcomp").innerHTML = wifComp;
|
||||
|
||||
ninja.qrCode.showQrCode({
|
||||
"detailqrcodepublic": bitcoinAddress,
|
||||
"detailqrcodepubliccomp": bitcoinAddressComp,
|
||||
"detailqrcodeprivate": wif,
|
||||
"detailqrcodeprivatecomp": wifComp
|
||||
}, 4);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
document.getElementById("detailpubkey").innerHTML = "";
|
||||
document.getElementById("detailpubkeycomp").innerHTML = "";
|
||||
document.getElementById("detailaddress").innerHTML = "";
|
||||
document.getElementById("detailaddresscomp").innerHTML = "";
|
||||
document.getElementById("detailprivwif").innerHTML = "";
|
||||
document.getElementById("detailprivwifcomp").innerHTML = "";
|
||||
document.getElementById("detailprivhex").innerHTML = "";
|
||||
document.getElementById("detailprivb64").innerHTML = "";
|
||||
document.getElementById("detailprivmini").innerHTML = "";
|
||||
document.getElementById("detailqrcodepublic").innerHTML = "";
|
||||
document.getElementById("detailqrcodepubliccomp").innerHTML = "";
|
||||
document.getElementById("detailqrcodeprivate").innerHTML = "";
|
||||
document.getElementById("detailqrcodeprivatecomp").innerHTML = "";
|
||||
document.getElementById("detailbip38commands").style.display = "none";
|
||||
}
|
||||
};
|
220
src/ninja.key.js
Normal file
220
src/ninja.key.js
Normal file
|
@ -0,0 +1,220 @@
|
|||
var ninja = { wallets: {} };
|
||||
|
||||
ninja.privateKey = {
|
||||
isPrivateKey: function (key) {
|
||||
return (
|
||||
Bitcoin.ECKey.isWalletImportFormat(key) ||
|
||||
Bitcoin.ECKey.isCompressedWalletImportFormat(key) ||
|
||||
Bitcoin.ECKey.isHexFormat(key) ||
|
||||
Bitcoin.ECKey.isBase64Format(key) ||
|
||||
Bitcoin.ECKey.isMiniFormat(key)
|
||||
);
|
||||
},
|
||||
getECKeyFromAdding: function (privKey1, privKey2) {
|
||||
var n = EllipticCurve.getSECCurveByName("secp256k1").getN();
|
||||
var ecKey1 = new Bitcoin.ECKey(privKey1);
|
||||
var ecKey2 = new Bitcoin.ECKey(privKey2);
|
||||
// if both keys are the same return null
|
||||
if (ecKey1.getBitcoinHexFormat() == ecKey2.getBitcoinHexFormat()) return null;
|
||||
if (ecKey1 == null || ecKey2 == null) return null;
|
||||
var combinedPrivateKey = new Bitcoin.ECKey(ecKey1.priv.add(ecKey2.priv).mod(n));
|
||||
// compressed when both keys are compressed
|
||||
if (ecKey1.compressed && ecKey2.compressed) combinedPrivateKey.setCompressed(true);
|
||||
return combinedPrivateKey;
|
||||
},
|
||||
getECKeyFromMultiplying: function (privKey1, privKey2) {
|
||||
var n = EllipticCurve.getSECCurveByName("secp256k1").getN();
|
||||
var ecKey1 = new Bitcoin.ECKey(privKey1);
|
||||
var ecKey2 = new Bitcoin.ECKey(privKey2);
|
||||
// if both keys are the same return null
|
||||
if (ecKey1.getBitcoinHexFormat() == ecKey2.getBitcoinHexFormat()) return null;
|
||||
if (ecKey1 == null || ecKey2 == null) return null;
|
||||
var combinedPrivateKey = new Bitcoin.ECKey(ecKey1.priv.multiply(ecKey2.priv).mod(n));
|
||||
// compressed when both keys are compressed
|
||||
if (ecKey1.compressed && ecKey2.compressed) combinedPrivateKey.setCompressed(true);
|
||||
return combinedPrivateKey;
|
||||
},
|
||||
// 58 base58 characters starting with 6P
|
||||
isBIP38Format: function (key) {
|
||||
return (/^6P[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{56}$/.test(key));
|
||||
},
|
||||
BIP38EncryptedKeyToByteArrayAsync: function (base58Encrypted, passphrase, callback) {
|
||||
var hex;
|
||||
try {
|
||||
hex = Bitcoin.Base58.decode(base58Encrypted);
|
||||
} catch (e) {
|
||||
callback(new Error(ninja.translator.get("detailalertnotvalidprivatekey")));
|
||||
return;
|
||||
}
|
||||
|
||||
// 43 bytes: 2 bytes prefix, 37 bytes payload, 4 bytes checksum
|
||||
if (hex.length != 43) {
|
||||
callback(new Error(ninja.translator.get("detailalertnotvalidprivatekey")));
|
||||
return;
|
||||
}
|
||||
// first byte is always 0x01
|
||||
else if (hex[0] != 0x01) {
|
||||
callback(new Error(ninja.translator.get("detailalertnotvalidprivatekey")));
|
||||
return;
|
||||
}
|
||||
|
||||
var expChecksum = hex.slice(-4);
|
||||
hex = hex.slice(0, -4);
|
||||
var checksum = Bitcoin.Util.dsha256(hex);
|
||||
if (checksum[0] != expChecksum[0] || checksum[1] != expChecksum[1] || checksum[2] != expChecksum[2] || checksum[3] != expChecksum[3]) {
|
||||
callback(new Error(ninja.translator.get("detailalertnotvalidprivatekey")));
|
||||
return;
|
||||
}
|
||||
|
||||
var isCompPoint = false;
|
||||
var isECMult = false;
|
||||
var hasLotSeq = false;
|
||||
// second byte for non-EC-multiplied key
|
||||
if (hex[1] == 0x42) {
|
||||
// key should use compression
|
||||
if (hex[2] == 0xe0) {
|
||||
isCompPoint = true;
|
||||
}
|
||||
// key should NOT use compression
|
||||
else if (hex[2] != 0xc0) {
|
||||
callback(new Error(ninja.translator.get("detailalertnotvalidprivatekey")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// second byte for EC-multiplied key
|
||||
else if (hex[1] == 0x43) {
|
||||
isECMult = true;
|
||||
isCompPoint = (hex[2] & 0x20) != 0;
|
||||
hasLotSeq = (hex[2] & 0x04) != 0;
|
||||
if ((hex[2] & 0x24) != hex[2]) {
|
||||
callback(new Error(ninja.translator.get("detailalertnotvalidprivatekey")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
callback(new Error(ninja.translator.get("detailalertnotvalidprivatekey")));
|
||||
return;
|
||||
}
|
||||
|
||||
var decrypted;
|
||||
var AES_opts = { mode: new Crypto.mode.ECB(Crypto.pad.NoPadding), asBytes: true };
|
||||
|
||||
var verifyHashAndReturn = function () {
|
||||
var tmpkey = new Bitcoin.ECKey(decrypted); // decrypted using closure
|
||||
var base58AddrText = tmpkey.setCompressed(isCompPoint).getBitcoinAddress(); // isCompPoint using closure
|
||||
checksum = Bitcoin.Util.dsha256(base58AddrText); // checksum using closure
|
||||
|
||||
if (checksum[0] != hex[3] || checksum[1] != hex[4] || checksum[2] != hex[5] || checksum[3] != hex[6]) {
|
||||
callback(new Error(ninja.translator.get("bip38alertincorrectpassphrase"))); // callback using closure
|
||||
return;
|
||||
}
|
||||
callback(tmpkey.getBitcoinPrivateKeyByteArray()); // callback using closure
|
||||
};
|
||||
|
||||
if (!isECMult) {
|
||||
var addresshash = hex.slice(3, 7);
|
||||
Crypto_scrypt(passphrase, addresshash, 16384, 8, 8, 64, function (derivedBytes) {
|
||||
var k = derivedBytes.slice(32, 32 + 32);
|
||||
decrypted = Crypto.AES.decrypt(hex.slice(7, 7 + 32), k, AES_opts);
|
||||
for (var x = 0; x < 32; x++) decrypted[x] ^= derivedBytes[x];
|
||||
verifyHashAndReturn(); //TODO: pass in 'decrypted' as a param
|
||||
});
|
||||
}
|
||||
else {
|
||||
var ownerentropy = hex.slice(7, 7 + 8);
|
||||
var ownersalt = !hasLotSeq ? ownerentropy : ownerentropy.slice(0, 4);
|
||||
Crypto_scrypt(passphrase, ownersalt, 16384, 8, 8, 32, function (prefactorA) {
|
||||
var passfactor;
|
||||
if (!hasLotSeq) { // hasLotSeq using closure
|
||||
passfactor = prefactorA;
|
||||
} else {
|
||||
var prefactorB = prefactorA.concat(ownerentropy); // ownerentropy using closure
|
||||
passfactor = Bitcoin.Util.dsha256(prefactorB);
|
||||
}
|
||||
var kp = new Bitcoin.ECKey(passfactor);
|
||||
var passpoint = kp.setCompressed(true).getPub();
|
||||
|
||||
var encryptedpart2 = hex.slice(23, 23 + 16);
|
||||
|
||||
var addresshashplusownerentropy = hex.slice(3, 3 + 12);
|
||||
Crypto_scrypt(passpoint, addresshashplusownerentropy, 1024, 1, 1, 64, function (derived) {
|
||||
var k = derived.slice(32);
|
||||
|
||||
var unencryptedpart2 = Crypto.AES.decrypt(encryptedpart2, k, AES_opts);
|
||||
for (var i = 0; i < 16; i++) { unencryptedpart2[i] ^= derived[i + 16]; }
|
||||
|
||||
var encryptedpart1 = hex.slice(15, 15 + 8).concat(unencryptedpart2.slice(0, 0 + 8));
|
||||
var unencryptedpart1 = Crypto.AES.decrypt(encryptedpart1, k, AES_opts);
|
||||
for (var i = 0; i < 16; i++) { unencryptedpart1[i] ^= derived[i]; }
|
||||
|
||||
var seedb = unencryptedpart1.slice(0, 0 + 16).concat(unencryptedpart2.slice(8, 8 + 8));
|
||||
|
||||
var factorb = Bitcoin.Util.dsha256(seedb);
|
||||
|
||||
var ps = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
var privateKey = BigInteger.fromByteArrayUnsigned(passfactor).multiply(BigInteger.fromByteArrayUnsigned(factorb)).remainder(ps.getN());
|
||||
|
||||
decrypted = privateKey.toByteArrayUnsigned();
|
||||
verifyHashAndReturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ninja.publicKey = {
|
||||
isPublicKeyHexFormat: function (key) {
|
||||
key = key.toString();
|
||||
return ninja.publicKey.isUncompressedPublicKeyHexFormat(key) || ninja.publicKey.isCompressedPublicKeyHexFormat(key);
|
||||
},
|
||||
// 130 characters [0-9A-F] starts with 04
|
||||
isUncompressedPublicKeyHexFormat: function (key) {
|
||||
key = key.toString();
|
||||
return /^04[A-Fa-f0-9]{128}$/.test(key);
|
||||
},
|
||||
// 66 characters [0-9A-F] starts with 02 or 03
|
||||
isCompressedPublicKeyHexFormat: function (key) {
|
||||
key = key.toString();
|
||||
return /^0[2-3][A-Fa-f0-9]{64}$/.test(key);
|
||||
},
|
||||
getBitcoinAddressFromByteArray: function (pubKeyByteArray) {
|
||||
var pubKeyHash = Bitcoin.Util.sha256ripe160(pubKeyByteArray);
|
||||
var addr = new Bitcoin.Address(pubKeyHash);
|
||||
return addr.toString();
|
||||
},
|
||||
getHexFromByteArray: function (pubKeyByteArray) {
|
||||
return Crypto.util.bytesToHex(pubKeyByteArray).toString().toUpperCase();
|
||||
},
|
||||
getByteArrayFromAdding: function (pubKeyHex1, pubKeyHex2) {
|
||||
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
var curve = ecparams.getCurve();
|
||||
var ecPoint1 = curve.decodePointHex(pubKeyHex1);
|
||||
var ecPoint2 = curve.decodePointHex(pubKeyHex2);
|
||||
// if both points are the same return null
|
||||
if (ecPoint1.equals(ecPoint2)) return null;
|
||||
var compressed = (ecPoint1.compressed && ecPoint2.compressed);
|
||||
var pubKey = ecPoint1.add(ecPoint2).getEncoded(compressed);
|
||||
return pubKey;
|
||||
},
|
||||
getByteArrayFromMultiplying: function (pubKeyHex, ecKey) {
|
||||
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
var ecPoint = ecparams.getCurve().decodePointHex(pubKeyHex);
|
||||
var compressed = (ecPoint.compressed && ecKey.compressed);
|
||||
// if both points are the same return null
|
||||
ecKey.setCompressed(false);
|
||||
if (ecPoint.equals(ecKey.getPubPoint())) {
|
||||
return null;
|
||||
}
|
||||
var bigInt = ecKey.priv;
|
||||
var pubKey = ecPoint.multiply(bigInt).getEncoded(compressed);
|
||||
return pubKey;
|
||||
},
|
||||
// used by unit test
|
||||
getDecompressedPubKeyHex: function (pubKeyHexComp) {
|
||||
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
var ecPoint = ecparams.getCurve().decodePointHex(pubKeyHexComp);
|
||||
var pubByteArray = ecPoint.getEncoded(0);
|
||||
var pubHexUncompressed = ninja.publicKey.getHexFromByteArray(pubByteArray);
|
||||
return pubHexUncompressed;
|
||||
}
|
||||
};
|
186
src/ninja.misc.js
Normal file
186
src/ninja.misc.js
Normal file
|
@ -0,0 +1,186 @@
|
|||
ninja.seeder = {
|
||||
// number of mouse movements to wait for
|
||||
seedLimit: (function () {
|
||||
var num = Crypto.util.randomBytes(12)[11];
|
||||
return 50 + Math.floor(num);
|
||||
})(),
|
||||
|
||||
seedCount: 0, // counter
|
||||
|
||||
// seed function exists to wait for mouse movement to add more entropy before generating an address
|
||||
seed: function (evt) {
|
||||
if (!evt) var evt = window.event;
|
||||
|
||||
// seed a bunch (minimum seedLimit) of times based on mouse moves
|
||||
SecureRandom.seedTime();
|
||||
// seed mouse position X and Y
|
||||
if (evt) SecureRandom.seedInt((evt.clientX * evt.clientY));
|
||||
|
||||
ninja.seeder.seedCount++;
|
||||
// seeding is over now we generate and display the address
|
||||
if (ninja.seeder.seedCount == ninja.seeder.seedLimit) {
|
||||
ninja.wallets.singlewallet.open();
|
||||
// UI
|
||||
document.getElementById("generate").style.display = "none";
|
||||
document.getElementById("menu").style.visibility = "visible";
|
||||
}
|
||||
},
|
||||
|
||||
// If user has not moved the mouse or if they are on a mobile device
|
||||
// we will force the generation after a random period of time.
|
||||
forceGenerate: function () {
|
||||
// if the mouse has not moved enough
|
||||
if (ninja.seeder.seedCount < ninja.seeder.seedLimit) {
|
||||
SecureRandom.seedTime();
|
||||
ninja.seeder.seedCount = ninja.seeder.seedLimit - 1;
|
||||
ninja.seeder.seed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ninja.qrCode = {
|
||||
// determine which type number is big enough for the input text length
|
||||
getTypeNumber: function (text) {
|
||||
var lengthCalculation = text.length * 8 + 12; // length as calculated by the QRCode
|
||||
if (lengthCalculation < 72) { return 1; }
|
||||
else if (lengthCalculation < 128) { return 2; }
|
||||
else if (lengthCalculation < 208) { return 3; }
|
||||
else if (lengthCalculation < 288) { return 4; }
|
||||
else if (lengthCalculation < 368) { return 5; }
|
||||
else if (lengthCalculation < 480) { return 6; }
|
||||
else if (lengthCalculation < 528) { return 7; }
|
||||
else if (lengthCalculation < 688) { return 8; }
|
||||
else if (lengthCalculation < 800) { return 9; }
|
||||
else if (lengthCalculation < 976) { return 10; }
|
||||
return null;
|
||||
},
|
||||
|
||||
createCanvas: function (text, sizeMultiplier) {
|
||||
sizeMultiplier = (sizeMultiplier == undefined) ? 2 : sizeMultiplier; // default 2
|
||||
// create the qrcode itself
|
||||
var typeNumber = ninja.qrCode.getTypeNumber(text);
|
||||
var qrcode = new QRCode(typeNumber, QRCode.ErrorCorrectLevel.H);
|
||||
qrcode.addData(text);
|
||||
qrcode.make();
|
||||
var width = qrcode.getModuleCount() * sizeMultiplier;
|
||||
var height = qrcode.getModuleCount() * sizeMultiplier;
|
||||
// create canvas element
|
||||
var canvas = document.createElement('canvas');
|
||||
var scale = 10.0;
|
||||
canvas.width = width * scale;
|
||||
canvas.height = height * scale;
|
||||
canvas.style.width = width + 'px';
|
||||
canvas.style.height = height + 'px';
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.scale(scale, scale);
|
||||
// compute tileW/tileH based on width/height
|
||||
var tileW = width / qrcode.getModuleCount();
|
||||
var tileH = height / qrcode.getModuleCount();
|
||||
// draw in the canvas
|
||||
for (var row = 0; row < qrcode.getModuleCount(); row++) {
|
||||
for (var col = 0; col < qrcode.getModuleCount(); col++) {
|
||||
ctx.fillStyle = qrcode.isDark(row, col) ? "#000000" : "#ffffff";
|
||||
ctx.fillRect(col * tileW, row * tileH, tileW, tileH);
|
||||
}
|
||||
}
|
||||
// return just built canvas
|
||||
return canvas;
|
||||
},
|
||||
|
||||
// generate a QRCode and return it's representation as an Html table
|
||||
createTableHtml: function (text) {
|
||||
var typeNumber = ninja.qrCode.getTypeNumber(text);
|
||||
var qr = new QRCode(typeNumber, QRCode.ErrorCorrectLevel.H);
|
||||
qr.addData(text);
|
||||
qr.make();
|
||||
var tableHtml = "<table class='qrcodetable'>";
|
||||
for (var r = 0; r < qr.getModuleCount(); r++) {
|
||||
tableHtml += "<tr>";
|
||||
for (var c = 0; c < qr.getModuleCount(); c++) {
|
||||
if (qr.isDark(r, c)) {
|
||||
tableHtml += "<td class='qrcodetddark'/>";
|
||||
} else {
|
||||
tableHtml += "<td class='qrcodetdlight'/>";
|
||||
}
|
||||
}
|
||||
tableHtml += "</tr>";
|
||||
}
|
||||
tableHtml += "</table>";
|
||||
return tableHtml;
|
||||
},
|
||||
|
||||
// show QRCodes with canvas OR table (IE8)
|
||||
// parameter: keyValuePair
|
||||
// example: { "id1": "string1", "id2": "string2"}
|
||||
// "id1" is the id of a div element where you want a QRCode inserted.
|
||||
// "string1" is the string you want encoded into the QRCode.
|
||||
showQrCode: function (keyValuePair, sizeMultiplier) {
|
||||
for (var key in keyValuePair) {
|
||||
var value = keyValuePair[key];
|
||||
try {
|
||||
if (document.getElementById(key)) {
|
||||
document.getElementById(key).innerHTML = "";
|
||||
document.getElementById(key).appendChild(ninja.qrCode.createCanvas(value, sizeMultiplier));
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// for browsers that do not support canvas (IE8)
|
||||
document.getElementById(key).innerHTML = ninja.qrCode.createTableHtml(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ninja.tabSwitch = function (walletTab) {
|
||||
if (walletTab.className.indexOf("selected") == -1) {
|
||||
// unselect all tabs
|
||||
for (var wType in ninja.wallets) {
|
||||
document.getElementById(wType).className = "tab";
|
||||
ninja.wallets[wType].close();
|
||||
}
|
||||
walletTab.className += " selected";
|
||||
ninja.wallets[walletTab.getAttribute("id")].open();
|
||||
}
|
||||
};
|
||||
|
||||
ninja.getQueryString = function () {
|
||||
var result = {}, queryString = location.search.substring(1), re = /([^&=]+)=([^&]*)/g, m;
|
||||
while (m = re.exec(queryString)) {
|
||||
result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// use when passing an Array of Functions
|
||||
ninja.runSerialized = function (functions, onComplete) {
|
||||
onComplete = onComplete || function () { };
|
||||
|
||||
if (functions.length === 0) onComplete();
|
||||
else {
|
||||
// run the first function, and make it call this
|
||||
// function when finished with the rest of the list
|
||||
var f = functions.shift();
|
||||
f(function () { ninja.runSerialized(functions, onComplete); });
|
||||
}
|
||||
};
|
||||
|
||||
ninja.forSerialized = function (initial, max, whatToDo, onComplete) {
|
||||
onComplete = onComplete || function () { };
|
||||
|
||||
if (initial === max) { onComplete(); }
|
||||
else {
|
||||
// same idea as runSerialized
|
||||
whatToDo(initial, function () { ninja.forSerialized(++initial, max, whatToDo, onComplete); });
|
||||
}
|
||||
};
|
||||
|
||||
// use when passing an Object (dictionary) of Functions
|
||||
ninja.foreachSerialized = function (collection, whatToDo, onComplete) {
|
||||
var keys = [];
|
||||
for (var name in collection) {
|
||||
keys.push(name);
|
||||
}
|
||||
ninja.forSerialized(0, keys.length, function (i, callback) {
|
||||
whatToDo(keys[i], callback);
|
||||
}, onComplete);
|
||||
};
|
28
src/ninja.onload.js
Normal file
28
src/ninja.onload.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
// run unit tests
|
||||
if (ninja.getQueryString()["unittests"] == "true" || ninja.getQueryString()["unittests"] == "1") {
|
||||
ninja.unitTests.runSynchronousTests();
|
||||
ninja.translator.showEnglishJson();
|
||||
}
|
||||
// run async unit tests
|
||||
if (ninja.getQueryString()["asyncunittests"] == "true" || ninja.getQueryString()["asyncunittests"] == "1") {
|
||||
ninja.unitTests.runAsynchronousTests();
|
||||
}
|
||||
|
||||
// change language
|
||||
if (ninja.getQueryString()["culture"] != undefined) {
|
||||
ninja.translator.translate(ninja.getQueryString()["culture"]);
|
||||
}
|
||||
|
||||
// testnet, check if testnet edition should be activated
|
||||
if (ninja.getQueryString()["testnet"] == "true" || ninja.getQueryString()["testnet"] == "1") {
|
||||
document.getElementById("testnet").innerHTML = ninja.translator.get("testneteditionactivated");
|
||||
document.getElementById("testnet").style.display = "block";
|
||||
document.getElementById("detailwifprefix").innerHTML = "'9'";
|
||||
document.getElementById("detailcompwifprefix").innerHTML = "'c'";
|
||||
Bitcoin.Address.networkVersion = 0x6F; // testnet
|
||||
Bitcoin.ECKey.privateKeyPrefix = 0xEF; // testnet
|
||||
ninja.testnetMode = true;
|
||||
}
|
||||
|
||||
// if users does not move mouse after random amount of time then generate the key anyway.
|
||||
setTimeout(ninja.seeder.forceGenerate, ninja.seeder.seedLimit * 20);
|
169
src/ninja.paperwallet.js
Normal file
169
src/ninja.paperwallet.js
Normal file
File diff suppressed because one or more lines are too long
36
src/ninja.singlewallet.js
Normal file
36
src/ninja.singlewallet.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
ninja.wallets.singlewallet = {
|
||||
open: function () {
|
||||
if (document.getElementById("btcaddress").innerHTML == "") {
|
||||
ninja.wallets.singlewallet.generateNewAddressAndKey();
|
||||
}
|
||||
document.getElementById("singlearea").style.display = "block";
|
||||
},
|
||||
|
||||
close: function () {
|
||||
document.getElementById("singlearea").style.display = "none";
|
||||
},
|
||||
|
||||
// generate bitcoin address and private key and update information in the HTML
|
||||
generateNewAddressAndKey: function () {
|
||||
try {
|
||||
var key = new Bitcoin.ECKey(false);
|
||||
var bitcoinAddress = key.getBitcoinAddress();
|
||||
var privateKeyWif = key.getBitcoinWalletImportFormat();
|
||||
document.getElementById("btcaddress").innerHTML = bitcoinAddress;
|
||||
document.getElementById("btcprivwif").innerHTML = privateKeyWif;
|
||||
var keyValuePair = {
|
||||
"qrcode_public": bitcoinAddress,
|
||||
"qrcode_private": privateKeyWif
|
||||
};
|
||||
ninja.qrCode.showQrCode(keyValuePair);
|
||||
}
|
||||
catch (e) {
|
||||
// browser does not have sufficient JavaScript support to generate a bitcoin address
|
||||
alert(e);
|
||||
document.getElementById("btcaddress").innerHTML = "error";
|
||||
document.getElementById("btcprivwif").innerHTML = "error";
|
||||
document.getElementById("qrcode_public").innerHTML = "";
|
||||
document.getElementById("qrcode_private").innerHTML = "";
|
||||
}
|
||||
}
|
||||
};
|
316
src/ninja.translator.js
Normal file
316
src/ninja.translator.js
Normal file
|
@ -0,0 +1,316 @@
|
|||
ninja.translator = {
|
||||
currentCulture: "en",
|
||||
|
||||
translate: function (culture) {
|
||||
var dict = ninja.translator.translations[culture];
|
||||
if (dict) {
|
||||
// set current culture
|
||||
ninja.translator.currentCulture = culture;
|
||||
// update menu UI
|
||||
for (var cult in ninja.translator.translations) {
|
||||
document.getElementById("culture" + cult).setAttribute("class", "");
|
||||
}
|
||||
document.getElementById("culture" + culture).setAttribute("class", "selected");
|
||||
// apply translations
|
||||
for (var id in dict) {
|
||||
if (document.getElementById(id) && document.getElementById(id).value) {
|
||||
document.getElementById(id).value = dict[id];
|
||||
}
|
||||
else if (document.getElementById(id)) {
|
||||
document.getElementById(id).innerHTML = dict[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get: function (id) {
|
||||
var translation = ninja.translator.translations[ninja.translator.currentCulture][id];
|
||||
return translation;
|
||||
},
|
||||
|
||||
translations: {
|
||||
"en": {
|
||||
// javascript alerts or messages
|
||||
"testneteditionactivated": "TESTNET EDITION ACTIVATED",
|
||||
"paperlabelbitcoinaddress": "Bitcoin Address:",
|
||||
"paperlabelprivatekey": "Private Key (Wallet Import Format):",
|
||||
"bulkgeneratingaddresses": "Generating addresses... ",
|
||||
"brainalertpassphrasetooshort": "The passphrase you entered is too short.\n\nWarning: Choosing a strong passphrase is important to avoid brute force attempts to guess your passphrase and steal your bitcoins.",
|
||||
"brainalertpassphrasedoesnotmatch": "The passphrase does not match the confirm passphrase.",
|
||||
"detailalertnotvalidprivatekey": "The text you entered is not a valid Private Key",
|
||||
"detailconfirmsha256": "The text you entered is not a valid Private Key!\n\nWould you like to use the entered text as a passphrase and create a Private Key using a SHA256 hash of the passphrase?\n\nWarning: Choosing a strong passphrase is important to avoid brute force attempts to guess your passphrase and steal your bitcoins.",
|
||||
"bip38alertincorrectpassphrase": "Incorrect passphrase for this encrypted private key.",
|
||||
"bip38alertpassphraserequired": "Passphrase required for BIP38 key",
|
||||
"vanityinvalidinputcouldnotcombinekeys": "Invalid input. Could not combine keys.",
|
||||
"vanityalertinvalidinputpublickeysmatch": "Invalid input. The Public Key of both entries match. You must input two different keys.",
|
||||
"vanityalertinvalidinputcannotmultiple": "Invalid input. Cannot multiply two public keys. Select 'Add' to add two public keys to get a bitcoin address.",
|
||||
"vanityprivatekeyonlyavailable": "Only available when combining two private keys",
|
||||
"vanityalertinvalidinputprivatekeysmatch": "Invalid input. The Private Key of both entries match. You must input two different keys."
|
||||
},
|
||||
|
||||
"es": {
|
||||
// javascript alerts or messages
|
||||
"testneteditionactivated": "Testnet se activa",
|
||||
"paperlabelbitcoinaddress": "Dirección Bitcoin:",
|
||||
"paperlabelprivatekey": "Clave privada (formato para importar):",
|
||||
"bulkgeneratingaddresses": "Generación de direcciones... ",
|
||||
"brainalertpassphrasetooshort": "La contraseña introducida es demasiado corta.\n\nAviso: Es importante escoger una contraseña fuerte para evitar ataques de fuerza bruta a fin de adivinarla y robar tus bitcoins.",
|
||||
"brainalertpassphrasedoesnotmatch": "Las contraseñas no coinciden.",
|
||||
"detailalertnotvalidprivatekey": "El texto que has introducido no es una clave privada válida",
|
||||
"detailconfirmsha256": "El texto que has introducido no es una clave privada válida\n\n¿Quieres usar ese texto como si fuera una contraseña y generar una clave privada usando un hash SHA256 de tal contraseña?\n\nAviso: Es importante escoger una contraseña fuerte para evitar ataques de fuerza bruta a fin de adivinarla y robar tus bitcoins.",
|
||||
"bip38alertincorrectpassphrase": "Incorrect passphrase for this encrypted private key.", //TODO: please translate
|
||||
"bip38alertpassphraserequired": "Passphrase required for BIP38 key", //TODO: please translate
|
||||
"vanityinvalidinputcouldnotcombinekeys": "Entrada no válida. No se puede combinar llaves.",
|
||||
"vanityalertinvalidinputpublickeysmatch": "Entrada no válida. La clave pública de ambos coincidan entradas. Debe introducir dos claves diferentes.",
|
||||
"vanityalertinvalidinputcannotmultiple": "Entrada no válida. No se puede multiplicar dos claves públicas. Seleccione 'Añadir' para agregar dos claves públicas para obtener una dirección bitcoin.",
|
||||
"vanityprivatekeyonlyavailable": "Sólo está disponible cuando se combinan dos claves privadas",
|
||||
"vanityalertinvalidinputprivatekeysmatch": "Entrada no válida. La clave privada de ambos coincidan entradas. Debe introducir dos claves diferentes.",
|
||||
|
||||
// header and menu html
|
||||
"tagline": "Generador de carteras Bitcoin de código abierto en lado de cliente con Javascript",
|
||||
"generatelabelbitcoinaddress": "Generando dirección Bitcoin...",
|
||||
"generatelabelmovemouse": "Mueve un poco el ratón para crear entropía...",
|
||||
"singlewallet": "Una sola cartera",
|
||||
"paperwallet": "Cartera en papel",
|
||||
"bulkwallet": "Direcciones en masa",
|
||||
"brainwallet": "Cartera mental",
|
||||
"vanitywallet": "Cartera personalizada",
|
||||
"detailwallet": "Detalles de la cartera",
|
||||
|
||||
// footer html
|
||||
"footerlabeldonations": "Donaciones:",
|
||||
"footerlabeltranslatedby": "Traducción: <b>12345</b>Vypv2QSmuRXcciT5oEB27mPbWGeva",
|
||||
"footerlabelpgp": "Clave pública PGP",
|
||||
"footerlabelversion": "Histórico de versiones",
|
||||
"footerlabelgithub": "Repositorio GitHub",
|
||||
"footerlabelcopyright1": "Copyright bitaddress.org.",
|
||||
"footerlabelcopyright2": "Copyright del código JavaScript: en el fuente.",
|
||||
"footerlabelnowarranty": "Sin garantía.",
|
||||
|
||||
// single wallet html
|
||||
"newaddress": "Generar dirección",
|
||||
"singleprint": "Imprimir",
|
||||
"singlelabelbitcoinaddress": "Dirección Bitcoin",
|
||||
"singlelabelprivatekey": "Clave privada (formato para importar):",
|
||||
|
||||
// paper wallet html
|
||||
"paperlabelhideart": "Ocultar diseño",
|
||||
"paperlabeladdressesperpage": "Direcciones por página:",
|
||||
"paperlabeladdressestogenerate": "Direcciones en total:",
|
||||
"papergenerate": "Generar",
|
||||
"paperprint": "Imprimir",
|
||||
|
||||
// bulk wallet html
|
||||
"bulklabelstartindex": "Empezar en:",
|
||||
"bulklabelrowstogenerate": "Filas a generar:",
|
||||
"bulklabelcompressed": "Compressed addresses?", //TODO: please translate
|
||||
"bulkgenerate": "Generar",
|
||||
"bulkprint": "Imprimir",
|
||||
"bulklabelcsv": "Valores separados por coma:",
|
||||
"bulklabelformat": "Índice,Dirección,Clave privada (formato para importar)",
|
||||
"bulklabelq1": "¿Por qué debo usar \"Direcciones en masa\" para aceptar Bitcoins en mi web?",
|
||||
"bulka1": "La forma tradicional de aceptar bitcoins en tu web requiere tener instalado el cliente oficial de bitcoin (\"bitcoind\"). Sin embargo muchos servicios de hosting no permiten instalar dicho cliente. Además, ejecutar el cliente en tu servidor supone que las claves privadas están también en el servidor y podrían ser comprometidas en caso de intrusión. Al usar este mecanismo, puedes subir al servidor sólo las dirección de bitcoin y no las claves privadas. De esta forma no te tienes que preocupar de que alguien robe la cartera si se cuelan en el servidor.",
|
||||
"bulklabelq2": "¿Cómo uso \"Direcciones en masa\" para aceptar bitcoins en mi web?",
|
||||
"bulklabela2li1": "Usa el tab \"Direcciones en masa\" para generar por anticipado muchas direcciones (más de 10000). Copia y pega la lista de valores separados por comas (CSV) a un archivo de texto seguro (cifrado) en tu ordenador. Guarda una copia de seguridad en algún lugar seguro.",
|
||||
"bulklabela2li2": "Importa las direcciones en la base de datos de tu servidor. No subas la cartera ni las claves públicas, o de lo contrario te lo pueden robar. Sube sólo las direcciones, ya que es lo que se va a mostrar a los clientes.",
|
||||
"bulklabela2li3": "Ofrece una alternativa en el carro de la compra de tu web para que los clientes paguen con Bitcoin. Cuando el cliente elija pagar con Bitcoin, les muestras una de las direcciones de la base de datos como su \"dirección de pago\" y guardas esto junto con el pedido.",
|
||||
"bulklabela2li4": "Ahora te hace falta recibir una notificación del pago. Busca en google \"notificación de pagos bitcoin\" (o \"bitcoin payment notification\" en inglés) y suscríbete a alguno de los servicios que aparezcan. Hay varios de ellos, que te pueden notificar vía Web services, API, SMS, email, etc. Una vez te llegue la notificación, lo cual puede ser automatizado, entonces ya puedes procesar el pedido. Para comprobar a mano si has recibido un pago, puedes usar Block Explorer: reemplaza DIRECCION a continuación por la dirección que estés comprobando. La transacción puede tardar entre 10 minutos y una hora en ser confirmada. <br />http://www.blockexplorer.com/address/DIRECCION<br /><br />Puedes ver las transacciones sin confirmar en: http://blockchain.info/ <br />Las transacciones sin confirmar suelen aparecer ahí en unos 30 segundos.",
|
||||
"bulklabela2li5": "Las bitcoins que recibas se almacenarán de forma segura en la cadena de bloques. Usa la cartera original que generaste en el paso 1 para usarlas.",
|
||||
|
||||
// brain wallet html
|
||||
"brainlabelenterpassphrase": "Contraseña:",
|
||||
"brainlabelshow": "Mostrar",
|
||||
"brainprint": "Imprimir",
|
||||
"brainlabelconfirm": "Confirmar contraseña:",
|
||||
"brainview": "Ver",
|
||||
"brainalgorithm": "Algoritmo: SHA256(contraseña)",
|
||||
"brainlabelbitcoinaddress": "Dirección Bitcoin:",
|
||||
"brainlabelprivatekey": "Clave privada (formato para importar):",
|
||||
|
||||
// vanity wallet html
|
||||
"vanitylabelstep1": "Paso 1 - Genera tu par de claves",
|
||||
"vanitynewkeypair": "Generar",
|
||||
"vanitylabelstep1publickey": "Clave pública:",
|
||||
"vanitylabelstep1pubnotes": "Copia y pega la línea de arriba en el campo \"Your-Part-Public-Key\" de la web de Vanity Pool.",
|
||||
"vanitylabelstep1privatekey": "Clave privada:",
|
||||
"vanitylabelstep1privnotes": "Copia y pega la clave pública de arriba en un archivo de texto. Es mejor que lo almacenes en un volumen cifrado. Lo necesitarás para recuperar la clave privada una vez Vanity Pool haya encontrado tu prefijo.",
|
||||
"vanitylabelstep2calculateyourvanitywallet": "Paso 2 - Calcula tu cartera personalizada",
|
||||
"vanitylabelenteryourpart": "Introduce la clave privada generada en el paso 1, y que has guardado:",
|
||||
"vanitylabelenteryourpoolpart": "Introduce la clave privada obtenida de la Vanity Pool:",
|
||||
"vanitylabelnote1": "[NOTA: esta casilla de entrada puede aceptar una clave pública o clave privada]",
|
||||
"vanitylabelnote2": "[NOTA: esta casilla de entrada puede aceptar una clave pública o clave privada]",
|
||||
"vanitylabelradioadd": "Añadir",
|
||||
"vanitylabelradiomultiply": "Multiplicar",
|
||||
"vanitycalc": "Calcular cartera personalizada",
|
||||
"vanitylabelbitcoinaddress": "Dirección Bitcoin personalizada:",
|
||||
"vanitylabelnotesbitcoinaddress": "Esta es tu nueva dirección, que debería tener el prefijo deseado.",
|
||||
"vanitylabelpublickeyhex": "Clave pública personalizada (HEX):",
|
||||
"vanitylabelnotespublickeyhex": "Lo anterior es la clave pública en formato hexadecimal.",
|
||||
"vanitylabelprivatekey": "Clave privada personalizada (formato para importar):",
|
||||
"vanitylabelnotesprivatekey": "Esto es la clave privada para introducir en tu cartera.",
|
||||
|
||||
// detail wallet html
|
||||
"detaillabelenterprivatekey": "Introduce la clave privada (en cualquier formato)",
|
||||
"detailview": "Ver detalles",
|
||||
"detailprint": "Imprimir",
|
||||
"detaillabelnote1": "Tu clave privada es un número secreto, único, que sólo tú conoces. Se puede expresar en varios formatos. Aquí abajo mostramos la dirección y la clave pública que se corresponden con tu clave privada, así como la clave privada en los formatos más conocidos (para importar, hex, base64 y mini).",
|
||||
"detaillabelnote2": "Bitcoin v0.6+ almacena las claves públicas comprimidas. El cliente también soporta importar/exportar claves privadas usando importprivkey/dumpprivkey. El formato de las claves privadas exportadas depende de si la dirección se generó en una cartera antigua o nueva.",
|
||||
"detaillabelbitcoinaddress": "Dirección Bitcoin:",
|
||||
"detaillabelbitcoinaddresscomp": "Dirección Bitcoin (comprimida):",
|
||||
"detaillabelpublickey": "Clave pública (130 caracteres [0-9A-F]):",
|
||||
"detaillabelpublickeycomp": "Clave pública (comprimida, 66 caracteres [0-9A-F]):",
|
||||
"detaillabelprivwif": "Clave privada para importar (51 caracteres en base58, empieza con un",
|
||||
"detaillabelprivwifcomp": "Clave privada para importar (comprimida, 52 caracteres en base58, empieza con",
|
||||
"detailcompwifprefix": "'K' o 'L'",
|
||||
"detaillabelprivhex": "Clave privada en formato hexadecimal (64 caracteres [0-9A-F]):",
|
||||
"detaillabelprivb64": "Clave privada en base64 (44 caracteres):",
|
||||
"detaillabelprivmini": "Clave privada en formato mini (22, 26 o 30 caracteres, empieza por 'S'):",
|
||||
"detaillabelpassphrase": "BIP38 Passphrase", //TODO: please translate
|
||||
"detaildecrypt": "Decrypt BIP38" //TODO: please translate
|
||||
},
|
||||
|
||||
"fr": {
|
||||
"testneteditionactivated": "ÉDITION TESTNET ACTIVÉE",
|
||||
"paperlabelbitcoinaddress": "Adresse Bitcoin:",
|
||||
"paperlabelprivatekey": "Clé Privée (Format d'importation de porte-monnaie):",
|
||||
"bulkgeneratingaddresses": "Création de l'adresse... ",
|
||||
"brainalertpassphrasetooshort": "Le mot de passe que vous avez entré est trop court.\n\nAttention: Choisir un mot de passe solide est important pour vous protéger des attaques bruteforce visant à trouver votre mot de passe et voler vos Bitcoins.",
|
||||
"brainalertpassphrasedoesnotmatch": "Le mot de passe ne correspond pas au mot de passe de vérification.",
|
||||
"detailalertnotvalidprivatekey": "Le texte que vous avez entré n'est pas une Clé Privée valide",
|
||||
"detailconfirmsha256": "Le texte que vous avez entré n'est pas une Clé Privée valide!\n\nVoulez-vous utiliser le texte comme un mot de passe et créer une Clé Privée à partir d'un hash SHA256 de ce mot de passe?\n\nAttention: Choisir un mot de passe solide est important pour vous protéger des attaques bruteforce visant à trouver votre mot de passe et voler vos Bitcoins.",
|
||||
"bip38alertincorrectpassphrase": "Incorrect passphrase for this encrypted private key.", //TODO: please translate
|
||||
"bip38alertpassphraserequired": "Passphrase required for BIP38 key", //TODO: please translate
|
||||
"vanityinvalidinputcouldnotcombinekeys": "Entrée non valide. Impossible de combiner les clés.",
|
||||
"vanityalertinvalidinputpublickeysmatch": "Entrée non valide. La clé publique des deux entrées est identique. Vous devez entrer deux clés différentes.",
|
||||
"vanityalertinvalidinputcannotmultiple": "Entrée non valide. Il n'est pas possible de multiplier deux clés publiques. Sélectionner 'Ajouter' pour ajouter deux clés publiques pour obtenir une adresse Bitcoin.",
|
||||
"vanityprivatekeyonlyavailable": "Seulement disponible si vos combinez deux clés privées",
|
||||
"vanityalertinvalidinputprivatekeysmatch": "Entrée non valide. La clé Privée des deux entrées est identique. Vous devez entrer deux clés différentes.",
|
||||
"tagline": "Générateur De Porte-Monnaie Bitcoin Javascript Hors-Ligne",
|
||||
"generatelabelbitcoinaddress": "Création de l'adresse Bitcoin...",
|
||||
"generatelabelmovemouse": "BOUGEZ votre souris pour ajouter de l'entropie...",
|
||||
"singlewallet": "Porte-Monnaie Simple",
|
||||
"paperwallet": "Porte-Monnaie Papier",
|
||||
"bulkwallet": "Porte-Monnaie En Vrac",
|
||||
"brainwallet": "Porte-Monnaie Cerveau",
|
||||
"vanitywallet": "Porte-Monnaie Vanité",
|
||||
"detailwallet": "Détails du Porte-Monnaie",
|
||||
"footerlabeldonations": "Dons:",
|
||||
"footerlabeltranslatedby": "Traduction: 1Gy7NYSJNUYqUdXTBow5d7bCUEJkUFDFSq",
|
||||
"footerlabelpgp": "Clé Publique PGP",
|
||||
"footerlabelversion": "Historique De Version Signé",
|
||||
"footerlabelgithub": "Dépôt GitHub",
|
||||
"footerlabelcopyright1": "Copyright bitaddress.org.",
|
||||
"footerlabelcopyright2": "Les droits d'auteurs JavaScript sont inclus dans le code source.",
|
||||
"footerlabelnowarranty": "Aucune garantie.",
|
||||
"newaddress": "Générer Une Nouvelle Adresse",
|
||||
"singleprint": "Imprimer",
|
||||
"singlelabelbitcoinaddress": "Adresse Bitcoin:",
|
||||
"singlelabelprivatekey": "Clé Privée (Format d'importation de porte-monnaie):",
|
||||
"paperlabelhideart": "Retirer Le Style?",
|
||||
"paperlabeladdressesperpage": "Adresses par page:",
|
||||
"paperlabeladdressestogenerate": "Nombre d'adresses à créer:",
|
||||
"papergenerate": "Générer",
|
||||
"paperprint": "Imprimer",
|
||||
"bulklabelstartindex": "Commencer à l'index:",
|
||||
"bulklabelrowstogenerate": "Colonnes à générer:",
|
||||
"bulklabelcompressed": "Compressed addresses?", //TODO: please translate
|
||||
"bulkgenerate": "Générer",
|
||||
"bulkprint": "Imprimer",
|
||||
"bulklabelcsv": "Valeurs Séparées Par Des Virgules (CSV):",
|
||||
"bulklabelformat": "Index,Adresse,Clé Privée (WIF)",
|
||||
"bulklabelq1": "Pourquoi utiliserais-je un Porte-monnaie en vrac pour accepter les Bitcoins sur mon site web?",
|
||||
"bulka1": "L'approche traditionnelle pour accepter des Bitcoins sur votre site web requière l'installation du logiciel Bitcoin officiel (\"bitcoind\"). Plusieurs hébergeurs ne supportent pas l'installation du logiciel Bitcoin. De plus, faire fonctionner le logiciel Bitcoin sur votre serveur web signifie que vos clés privées sont hébergées sur le serveur et pourraient donc être volées si votre serveur web était compromis. En utilisant un Porte-monnaie en vrac, vous pouvez publiquer seulement les adresses Bitcoin sur votre serveur et non les clés privées. Vous n'avez alors pas à vous inquiéter du risque de vous faire voler votre porte-monnaie si votre serveur était compromis.",
|
||||
"bulklabelq2": "Comment utiliser le Porte-monnaie en vrac pour utiliser le Bitcoin sur mon site web?",
|
||||
"bulklabela2li1": "Utilisez le Porte-monnaie en vrac pour pré-générer une large quantité d'adresses Bitcoin (10,000+). Copiez collez les données séparées par des virgules (CSV) dans un fichier texte sécurisé dans votre ordinateur. Sauvegardez ce fichier dans un endroit sécurisé.",
|
||||
"bulklabela2li2": "Importez les adresses Bitcoin dans une base de donnée sur votre serveur web. (N'ajoutez pas le porte-monnaie ou les clés privées sur votre serveur web, sinon vous courrez le risque de vous faire voler si votre serveur est compromis. Ajoutez seulement les adresses Bitcoin qui seront visibles à vos visiteurs.)",
|
||||
"bulklabela2li3": "Ajoutez une option dans votre panier en ligne pour que vos clients puissent vous payer en Bitcoin. Quand un client choisi de vous payer en Bitcoin, vous pouvez afficher une des adresses de votre base de donnée comme \"adresse de paiment\" pour votre client et sauvegarder cette adresse avec sa commande.",
|
||||
"bulklabela2li4": "Vous avez maintenant besoin d'être avisé quand le paiement est reçu. Cherchez \"bitcoin payment notification\" sur Google et inscrivez-vous à un service de notification de paiement Bitcoin. Il y a plusieurs services qui vous avertiront via des services Web, API, SMS, Email, etc. Une fois que vous avez reçu la notification, qui devrait être programmée automatiquement, vous pouvez traiter la commande de votre client. Pour vérifier manuellement si un paiement est arrivé, vous pouvez utiliser Block Explorer. Remplacez ADRESSE par l'adresse Bitcoin que vous souhaitez vérifier. La confirmation de la transaction pourrait prendre de 10 à 60 minutes pour être confirmée.<br />http://www.blockexplorer.com/address/ADRESSE<br /><br />Les transactions non confirmées peuvent être visualisées ici: http://blockchain.info/ <br />Vous devriez voir la transaction à l'intérieur de 30 secondes.",
|
||||
"bulklabela2li5": "Les Bitcoins vos s'accumuler de façon sécuritaire dans la chaîne de blocs. Utilisez le porte-monnaie original que vous avez généré à l'étape 1 pour les dépenser.",
|
||||
"brainlabelenterpassphrase": "Entrez votre mot de passe: ",
|
||||
"brainlabelshow": "Afficher?",
|
||||
"brainprint": "Imprimer",
|
||||
"brainlabelconfirm": "Confirmer le mot de passe: ",
|
||||
"brainview": "Visualiser",
|
||||
"brainalgorithm": "Algorithme: SHA256(mot de passe)",
|
||||
"brainlabelbitcoinaddress": "Adresse Bitcoin:",
|
||||
"brainlabelprivatekey": "Clé Privée (Format d'importation de porte-monnaie):",
|
||||
"vanitylabelstep1": "Étape 1 - Générer votre \"Étape 1 Paire De Clés\"",
|
||||
"vanitynewkeypair": "Générer",
|
||||
"vanitylabelstep1publickey": "Étape 1 Clé Publique:",
|
||||
"vanitylabelstep1pubnotes": "Copiez celle-ci dans la case Votre-Clé-Publique du site de Vanity Pool.",
|
||||
"vanitylabelstep1privatekey": "Step 1 Clé Privée:",
|
||||
"vanitylabelstep1privnotes": "Copiez la cette Clé Privée dans un fichier texte. Idéalement, sauvegardez la dans un fichier encrypté. Vous en aurez besoin pour récupérer la Clé Privée lors que Vanity Pool aura trouvé votre préfixe.",
|
||||
"vanitylabelstep2calculateyourvanitywallet": "Étape 2 - Calculer votre Porte-monnaie Vanité",
|
||||
"vanitylabelenteryourpart": "Entrez votre Clé Privée (générée à l'étape 1 plus haut et précédemment sauvegardée):",
|
||||
"vanitylabelenteryourpoolpart": "Entrez la Clé Privée (provenant de Vanity Pool):",
|
||||
"vanitylabelnote1": "[NOTE: cette case peut accepter une clé publique ou un clé privée]",
|
||||
"vanitylabelnote2": "[NOTE: cette case peut accepter une clé publique ou un clé privée]",
|
||||
"vanitylabelradioadd": "Ajouter",
|
||||
"vanitylabelradiomultiply": "Multiplier",
|
||||
"vanitycalc": "Calculer Le Porte-monnaie Vanité",
|
||||
"vanitylabelbitcoinaddress": "Adresse Bitcoin Vanité:",
|
||||
"vanitylabelnotesbitcoinaddress": "Ci-haut est votre nouvelle adresse qui devrait inclure le préfix requis.",
|
||||
"vanitylabelpublickeyhex": "Clé Public Vanité (HEX):",
|
||||
"vanitylabelnotespublickeyhex": "Celle-ci est la Clé Publique dans le format hexadécimal. ",
|
||||
"vanitylabelprivatekey": "Clé Privée Vanité (WIF):",
|
||||
"vanitylabelnotesprivatekey": "Celle-ci est la Clé Privée pour accéder à votre porte-monnaie. ",
|
||||
"detaillabelenterprivatekey": "Entrez la Clé Privée (quel que soit son format)",
|
||||
"detailview": "Voir les détails",
|
||||
"detailprint": "Imprimer",
|
||||
"detaillabelnote1": "Votre Clé Privée Bitcoin est un nombre secret que vous êtes le seul à connaître. Il peut être encodé sous la forme d'un nombre sous différents formats. Ci-bas, nous affichons l'adresse Bitcoin et la Clé Publique qui corresponds à la Clé Privée ainsi que la Clé Privée dans les formats d'encodage les plus populaires (WIF, HEX, B64, MINI).",
|
||||
"detaillabelnote2": "Bitcoin v0.6+ conserve les clés publiques dans un format compressé. Le logiciel supporte maintenant aussi l'importation et l'exportation de clés privées avec importprivkey/dumpprivkey. Le format de la clé privée exportée est déterminé selon la version du porte-monnaie Bitcoin.",
|
||||
"detaillabelbitcoinaddress": "Adresse Bitcoin:",
|
||||
"detaillabelbitcoinaddresscomp": "Adresse Bitcoin (compressée):",
|
||||
"detaillabelpublickey": "Clé Publique (130 caractères [0-9A-F]):",
|
||||
"detaillabelpublickeycomp": "Clé Publique (compressée, 66 caractères [0-9A-F]):",
|
||||
"detaillabelprivwif": "Clé Privée WIF (51 caractères base58, débute avec un a",
|
||||
"detaillabelprivwifcomp": "Clé Privée WIF (compressée, 52 caractères base58, débute avec un a",
|
||||
"detailcompwifprefix": "'K' ou 'L'",
|
||||
"detaillabelprivhex": "Clé Privée Format Hexadecimal (64 caractères [0-9A-F]):",
|
||||
"detaillabelprivb64": "Clé Privée Base64 (44 caractères):",
|
||||
"detaillabelprivmini": "Clé Privée Format Mini (22, 26 ou 30 caractères, débute avec un 'S'):",
|
||||
"detaillabelpassphrase": "BIP38 Passphrase", //TODO: please translate
|
||||
"detaildecrypt": "Decrypt BIP38" //TODO: please translate
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ninja.translator.showEnglishJson = function () {
|
||||
var english = ninja.translator.translations["en"];
|
||||
var spanish = ninja.translator.translations["es"];
|
||||
var spanishClone = {};
|
||||
for (var key in spanish) {
|
||||
spanishClone[key] = spanish[key];
|
||||
}
|
||||
var newLang = {};
|
||||
for (var key in english) {
|
||||
newLang[key] = english[key];
|
||||
delete spanishClone[key];
|
||||
}
|
||||
for (var key in spanishClone) {
|
||||
if (document.getElementById(key)) {
|
||||
if (document.getElementById(key).value) {
|
||||
newLang[key] = document.getElementById(key).value;
|
||||
}
|
||||
else {
|
||||
newLang[key] = document.getElementById(key).innerHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
var div = document.createElement("div");
|
||||
div.setAttribute("class", "englishjson");
|
||||
div.innerHTML = "<h3>English Json</h3>";
|
||||
var elem = document.createElement("textarea");
|
||||
elem.setAttribute("rows", "35");
|
||||
elem.setAttribute("cols", "110");
|
||||
elem.setAttribute("wrap", "off");
|
||||
var langJson = "{\n";
|
||||
for (var key in newLang) {
|
||||
langJson += "\t\"" + key + "\"" + ": " + "\"" + newLang[key].replace(/\"/g, "\\\"").replace(/\n/g, "\\n") + "\",\n";
|
||||
}
|
||||
langJson = langJson.substr(0, langJson.length - 2);
|
||||
langJson += "\n}\n";
|
||||
elem.innerHTML = langJson;
|
||||
div.appendChild(elem);
|
||||
document.body.appendChild(div);
|
||||
};
|
502
src/ninja.unittests.js
Normal file
502
src/ninja.unittests.js
Normal file
|
@ -0,0 +1,502 @@
|
|||
(function (ninja) {
|
||||
var ut = ninja.unitTests = {
|
||||
runSynchronousTests: function () {
|
||||
document.getElementById("busyblock").className = "busy";
|
||||
var div = document.createElement("div");
|
||||
div.setAttribute("class", "unittests");
|
||||
div.setAttribute("id", "unittests");
|
||||
var testResults = "";
|
||||
var passCount = 0;
|
||||
var testCount = 0;
|
||||
for (var test in ut.synchronousTests) {
|
||||
var exceptionMsg = "";
|
||||
var resultBool = false;
|
||||
try {
|
||||
resultBool = ut.synchronousTests[test]();
|
||||
} catch (ex) {
|
||||
exceptionMsg = ex.toString();
|
||||
resultBool = false;
|
||||
}
|
||||
if (resultBool == true) {
|
||||
var passFailStr = "pass";
|
||||
passCount++;
|
||||
}
|
||||
else {
|
||||
var passFailStr = "<b>FAIL " + exceptionMsg + "</b>";
|
||||
}
|
||||
testCount++;
|
||||
testResults += test + ": " + passFailStr + "<br/>";
|
||||
}
|
||||
testResults += passCount + " of " + testCount + " synchronous tests passed";
|
||||
if (passCount < testCount) {
|
||||
testResults += "<b>" + (testCount - passCount) + " unit test(s) failed</b>";
|
||||
}
|
||||
div.innerHTML = "<h3>Unit Tests</h3><div id=\"unittestresults\">" + testResults + "<br/><br/></div>";
|
||||
document.body.appendChild(div);
|
||||
document.getElementById("busyblock").className = "";
|
||||
|
||||
},
|
||||
|
||||
runAsynchronousTests: function () {
|
||||
document.getElementById("busyblock").className = "busy";
|
||||
// run the asynchronous tests one after another so we don't crash the browser
|
||||
ninja.foreachSerialized(ninja.unitTests.asynchronousTests, function (name, cb) {
|
||||
ninja.unitTests.asynchronousTests[name](cb);
|
||||
}, function () {
|
||||
document.getElementById("unittestresults").innerHTML += "running of asynchronous unit tests complete!<br/>";
|
||||
document.getElementById("busyblock").className = "";
|
||||
});
|
||||
},
|
||||
|
||||
synchronousTests: {
|
||||
//ninja.publicKey tests
|
||||
testIsPublicKeyHexFormat: function () {
|
||||
var key = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var bool = ninja.publicKey.isPublicKeyHexFormat(key);
|
||||
if (bool != true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetHexFromByteArray: function () {
|
||||
var bytes = [4, 120, 152, 47, 64, 250, 12, 11, 122, 85, 113, 117, 131, 175, 201, 154, 78, 223, 211, 1, 162, 114, 157, 197, 155, 11, 142, 185, 225, 134, 146, 188, 181, 33, 240, 84, 250, 217, 130, 175, 76, 193, 147, 58, 253, 31, 27, 86, 62, 167, 121, 166, 170, 108, 206, 54, 163, 11, 148, 125, 214, 83, 230, 62, 68];
|
||||
var key = ninja.publicKey.getHexFromByteArray(bytes);
|
||||
if (key != "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testHexToBytes: function () {
|
||||
var key = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var bytes = Crypto.util.hexToBytes(key);
|
||||
if (bytes.toString() != "4,120,152,47,64,250,12,11,122,85,113,117,131,175,201,154,78,223,211,1,162,114,157,197,155,11,142,185,225,134,146,188,181,33,240,84,250,217,130,175,76,193,147,58,253,31,27,86,62,167,121,166,170,108,206,54,163,11,148,125,214,83,230,62,68") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetBitcoinAddressFromByteArray: function () {
|
||||
var bytes = [4, 120, 152, 47, 64, 250, 12, 11, 122, 85, 113, 117, 131, 175, 201, 154, 78, 223, 211, 1, 162, 114, 157, 197, 155, 11, 142, 185, 225, 134, 146, 188, 181, 33, 240, 84, 250, 217, 130, 175, 76, 193, 147, 58, 253, 31, 27, 86, 62, 167, 121, 166, 170, 108, 206, 54, 163, 11, 148, 125, 214, 83, 230, 62, 68];
|
||||
var address = ninja.publicKey.getBitcoinAddressFromByteArray(bytes);
|
||||
if (address != "1Cnz9ULjzBPYhDw1J8bpczDWCEXnC9HuU1") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromAdding: function () {
|
||||
var key1 = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var key2 = "0419153E53FECAD7FF07FEC26F7DDEB1EDD66957711AA4554B8475F10AFBBCD81C0159DC0099AD54F733812892EB9A11A8C816A201B3BAF0D97117EBA2033C9AB2";
|
||||
var bytes = ninja.publicKey.getByteArrayFromAdding(key1, key2);
|
||||
if (bytes.toString() != "4,151,19,227,152,54,37,184,255,4,83,115,216,102,189,76,82,170,57,4,196,253,2,41,74,6,226,33,167,199,250,74,235,223,128,233,99,150,147,92,57,39,208,84,196,71,68,248,166,106,138,95,172,253,224,70,187,65,62,92,81,38,253,79,0") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromAddingCompressed: function () {
|
||||
var key1 = "0278982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB5";
|
||||
var key2 = "0219153E53FECAD7FF07FEC26F7DDEB1EDD66957711AA4554B8475F10AFBBCD81C";
|
||||
var bytes = ninja.publicKey.getByteArrayFromAdding(key1, key2);
|
||||
var hex = ninja.publicKey.getHexFromByteArray(bytes);
|
||||
if (hex != "029713E3983625B8FF045373D866BD4C52AA3904C4FD02294A06E221A7C7FA4AEB") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromAddingUncompressedAndCompressed: function () {
|
||||
var key1 = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var key2 = "0219153E53FECAD7FF07FEC26F7DDEB1EDD66957711AA4554B8475F10AFBBCD81C";
|
||||
var bytes = ninja.publicKey.getByteArrayFromAdding(key1, key2);
|
||||
if (bytes.toString() != "4,151,19,227,152,54,37,184,255,4,83,115,216,102,189,76,82,170,57,4,196,253,2,41,74,6,226,33,167,199,250,74,235,223,128,233,99,150,147,92,57,39,208,84,196,71,68,248,166,106,138,95,172,253,224,70,187,65,62,92,81,38,253,79,0") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromAddingShouldReturnNullWhenSameKey1: function () {
|
||||
var key1 = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var key2 = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var bytes = ninja.publicKey.getByteArrayFromAdding(key1, key2);
|
||||
if (bytes != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromAddingShouldReturnNullWhenSameKey2: function () {
|
||||
var key1 = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var key2 = "0278982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB5";
|
||||
var bytes = ninja.publicKey.getByteArrayFromAdding(key1, key2);
|
||||
if (bytes != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromMultiplying: function () {
|
||||
var key1 = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var key2 = "SQE6yipP5oW8RBaStWoB47xsRQ8pat";
|
||||
var bytes = ninja.publicKey.getByteArrayFromMultiplying(key1, new Bitcoin.ECKey(key2));
|
||||
if (bytes.toString() != "4,102,230,163,180,107,9,21,17,48,35,245,227,110,199,119,144,57,41,112,64,245,182,40,224,41,230,41,5,26,206,138,57,115,35,54,105,7,180,5,106,217,57,229,127,174,145,215,79,121,163,191,211,143,215,50,48,156,211,178,72,226,68,150,52") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromMultiplyingCompressedOutputsUncompressed: function () {
|
||||
var key1 = "0278982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB5";
|
||||
var key2 = "SQE6yipP5oW8RBaStWoB47xsRQ8pat";
|
||||
var bytes = ninja.publicKey.getByteArrayFromMultiplying(key1, new Bitcoin.ECKey(key2));
|
||||
if (bytes.toString() != "4,102,230,163,180,107,9,21,17,48,35,245,227,110,199,119,144,57,41,112,64,245,182,40,224,41,230,41,5,26,206,138,57,115,35,54,105,7,180,5,106,217,57,229,127,174,145,215,79,121,163,191,211,143,215,50,48,156,211,178,72,226,68,150,52") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromMultiplyingCompressedOutputsCompressed: function () {
|
||||
var key1 = "0278982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB5";
|
||||
var key2 = "L1n4cgNZAo2KwdUc15zzstvo1dcxpBw26NkrLqfDZtU9AEbPkLWu";
|
||||
var ecKey = new Bitcoin.ECKey(key2);
|
||||
var bytes = ninja.publicKey.getByteArrayFromMultiplying(key1, ecKey);
|
||||
if (bytes.toString() != "2,102,230,163,180,107,9,21,17,48,35,245,227,110,199,119,144,57,41,112,64,245,182,40,224,41,230,41,5,26,206,138,57") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromMultiplyingShouldReturnNullWhenSameKey1: function () {
|
||||
var key1 = "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44";
|
||||
var key2 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var bytes = ninja.publicKey.getByteArrayFromMultiplying(key1, new Bitcoin.ECKey(key2));
|
||||
if (bytes != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetByteArrayFromMultiplyingShouldReturnNullWhenSameKey2: function () {
|
||||
var key1 = "0278982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB5";
|
||||
var key2 = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
|
||||
var bytes = ninja.publicKey.getByteArrayFromMultiplying(key1, new Bitcoin.ECKey(key2));
|
||||
if (bytes != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// confirms multiplication is working and BigInteger was created correctly (Pub Key B vs Priv Key A)
|
||||
testGetPubHexFromMultiplyingPrivAPubB: function () {
|
||||
var keyPub = "04F04BF260DCCC46061B5868F60FE962C77B5379698658C98A93C3129F5F98938020F36EBBDE6F1BEAF98E5BD0E425747E68B0F2FB7A2A59EDE93F43C0D78156FF";
|
||||
var keyPriv = "B1202A137E917536B3B4C5010C3FF5DDD4784917B3EEF21D3A3BF21B2E03310C";
|
||||
var bytes = ninja.publicKey.getByteArrayFromMultiplying(keyPub, new Bitcoin.ECKey(keyPriv));
|
||||
var pubHex = ninja.publicKey.getHexFromByteArray(bytes);
|
||||
if (pubHex != "04C6732006AF4AE571C7758DF7A7FB9E3689DFCF8B53D8724D3A15517D8AB1B4DBBE0CB8BB1C4525F8A3001771FC7E801D3C5986A555E2E9441F1AD6D181356076") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// confirms multiplication is working and BigInteger was created correctly (Pub Key A vs Priv Key B)
|
||||
testGetPubHexFromMultiplyingPrivBPubA: function () {
|
||||
var keyPub = "0429BF26C0AF7D31D608474CEBD49DA6E7C541B8FAD95404B897643476CE621CFD05E24F7AE8DE8033AADE5857DB837E0B704A31FDDFE574F6ECA879643A0D3709";
|
||||
var keyPriv = "7DE52819F1553C2BFEDE6A2628B6FDDF03C2A07EB21CF77ACA6C2C3D252E1FD9";
|
||||
var bytes = ninja.publicKey.getByteArrayFromMultiplying(keyPub, new Bitcoin.ECKey(keyPriv));
|
||||
var pubHex = ninja.publicKey.getHexFromByteArray(bytes);
|
||||
if (pubHex != "04C6732006AF4AE571C7758DF7A7FB9E3689DFCF8B53D8724D3A15517D8AB1B4DBBE0CB8BB1C4525F8A3001771FC7E801D3C5986A555E2E9441F1AD6D181356076") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// Private Key tests
|
||||
testBadKeyIsNotWif: function () {
|
||||
return !(Bitcoin.ECKey.isWalletImportFormat("bad key"));
|
||||
},
|
||||
testBadKeyIsNotWifCompressed: function () {
|
||||
return !(Bitcoin.ECKey.isCompressedWalletImportFormat("bad key"));
|
||||
},
|
||||
testBadKeyIsNotHex: function () {
|
||||
return !(Bitcoin.ECKey.isHexFormat("bad key"));
|
||||
},
|
||||
testBadKeyIsNotBase64: function () {
|
||||
return !(Bitcoin.ECKey.isBase64Format("bad key"));
|
||||
},
|
||||
testBadKeyIsNotMini: function () {
|
||||
return !(Bitcoin.ECKey.isMiniFormat("bad key"));
|
||||
},
|
||||
testBadKeyReturnsNullPrivFromECKey: function () {
|
||||
var key = "bad key";
|
||||
var ecKey = new Bitcoin.ECKey(key);
|
||||
if (ecKey.priv != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetBitcoinPrivateKeyByteArray: function () {
|
||||
var key = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var bytes = [41, 38, 101, 195, 135, 36, 24, 173, 241, 218, 127, 250, 58, 100, 111, 47, 6, 2, 36, 109, 166, 9, 138, 145, 210, 41, 195, 33, 80, 242, 113, 139];
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinPrivateKeyByteArray().toString() != bytes.toString()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testECKeyDecodeWalletImportFormat: function () {
|
||||
var key = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var bytes1 = [41, 38, 101, 195, 135, 36, 24, 173, 241, 218, 127, 250, 58, 100, 111, 47, 6, 2, 36, 109, 166, 9, 138, 145, 210, 41, 195, 33, 80, 242, 113, 139];
|
||||
var bytes2 = Bitcoin.ECKey.decodeWalletImportFormat(key);
|
||||
if (bytes1.toString() != bytes2.toString()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testECKeyDecodeCompressedWalletImportFormat: function () {
|
||||
var key = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
|
||||
var bytes1 = [41, 38, 101, 195, 135, 36, 24, 173, 241, 218, 127, 250, 58, 100, 111, 47, 6, 2, 36, 109, 166, 9, 138, 145, 210, 41, 195, 33, 80, 242, 113, 139];
|
||||
var bytes2 = Bitcoin.ECKey.decodeCompressedWalletImportFormat(key);
|
||||
if (bytes1.toString() != bytes2.toString()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testWifToPubKeyHex: function () {
|
||||
var key = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getPubKeyHex() != "0478982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB521F054FAD982AF4CC1933AFD1F1B563EA779A6AA6CCE36A30B947DD653E63E44"
|
||||
|| btcKey.getPubPoint().compressed != false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testWifToPubKeyHexCompressed: function () {
|
||||
var key = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
btcKey.setCompressed(true);
|
||||
if (btcKey.getPubKeyHex() != "0278982F40FA0C0B7A55717583AFC99A4EDFD301A2729DC59B0B8EB9E18692BCB5"
|
||||
|| btcKey.getPubPoint().compressed != true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testBase64ToECKey: function () {
|
||||
var key = "KSZlw4ckGK3x2n/6OmRvLwYCJG2mCYqR0inDIVDycYs=";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinBase64Format() != "KSZlw4ckGK3x2n/6OmRvLwYCJG2mCYqR0inDIVDycYs=") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testHexToECKey: function () {
|
||||
var key = "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinHexFormat() != "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testCompressedWifToECKey: function () {
|
||||
var key = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinWalletImportFormat() != "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S"
|
||||
|| btcKey.getPubPoint().compressed != true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testWifToECKey: function () {
|
||||
var key = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinWalletImportFormat() != "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testBrainToECKey: function () {
|
||||
var key = "bitaddress.org unit test";
|
||||
var bytes = Crypto.SHA256(key, { asBytes: true });
|
||||
var btcKey = new Bitcoin.ECKey(bytes);
|
||||
if (btcKey.getBitcoinWalletImportFormat() != "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testMini30CharsToECKey: function () {
|
||||
var key = "SQE6yipP5oW8RBaStWoB47xsRQ8pat";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinWalletImportFormat() != "5JrBLQseeZdYw4jWEAHmNxGMr5fxh9NJU3fUwnv4khfKcg2rJVh") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromAdding: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "SQE6yipP5oW8RBaStWoB47xsRQ8pat";
|
||||
var ecKey = ninja.privateKey.getECKeyFromAdding(key1, key2);
|
||||
if (ecKey.getBitcoinWalletImportFormat() != "5KAJTSqSjpsZ11KyEE3qu5PrJVjR4ZCbNxK3Nb1F637oe41m1c2") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromAddingCompressed: function () {
|
||||
var key1 = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
|
||||
var key2 = "L1n4cgNZAo2KwdUc15zzstvo1dcxpBw26NkrLqfDZtU9AEbPkLWu";
|
||||
var ecKey = ninja.privateKey.getECKeyFromAdding(key1, key2);
|
||||
if (ecKey.getBitcoinWalletImportFormat() != "L3A43j2pc2J8F2SjBNbYprPrcDpDCh8Aju8dUH65BEM2r7RFSLv4") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromAddingUncompressedAndCompressed: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "L1n4cgNZAo2KwdUc15zzstvo1dcxpBw26NkrLqfDZtU9AEbPkLWu";
|
||||
var ecKey = ninja.privateKey.getECKeyFromAdding(key1, key2);
|
||||
if (ecKey.getBitcoinWalletImportFormat() != "5KAJTSqSjpsZ11KyEE3qu5PrJVjR4ZCbNxK3Nb1F637oe41m1c2") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromAddingShouldReturnNullWhenSameKey1: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var ecKey = ninja.privateKey.getECKeyFromAdding(key1, key2);
|
||||
if (ecKey != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromAddingShouldReturnNullWhenSameKey2: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
|
||||
var ecKey = ninja.privateKey.getECKeyFromAdding(key1, key2);
|
||||
if (ecKey != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromMultiplying: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "SQE6yipP5oW8RBaStWoB47xsRQ8pat";
|
||||
var ecKey = ninja.privateKey.getECKeyFromMultiplying(key1, key2);
|
||||
if (ecKey.getBitcoinWalletImportFormat() != "5KetpZ5mCGagCeJnMmvo18n4iVrtPSqrpnW5RP92Gv2BQy7GPCk") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromMultiplyingCompressed: function () {
|
||||
var key1 = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
|
||||
var key2 = "L1n4cgNZAo2KwdUc15zzstvo1dcxpBw26NkrLqfDZtU9AEbPkLWu";
|
||||
var ecKey = ninja.privateKey.getECKeyFromMultiplying(key1, key2);
|
||||
if (ecKey.getBitcoinWalletImportFormat() != "L5LFitc24jme2PfVChJS3bKuQAPBp54euuqLWciQdF2CxnaU3M8t") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromMultiplyingUncompressedAndCompressed: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "L1n4cgNZAo2KwdUc15zzstvo1dcxpBw26NkrLqfDZtU9AEbPkLWu";
|
||||
var ecKey = ninja.privateKey.getECKeyFromMultiplying(key1, key2);
|
||||
if (ecKey.getBitcoinWalletImportFormat() != "5KetpZ5mCGagCeJnMmvo18n4iVrtPSqrpnW5RP92Gv2BQy7GPCk") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromMultiplyingShouldReturnNullWhenSameKey1: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var ecKey = ninja.privateKey.getECKeyFromMultiplying(key1, key2);
|
||||
if (ecKey != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromMultiplyingShouldReturnNullWhenSameKey2: function () {
|
||||
var key1 = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
|
||||
var key2 = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
|
||||
var ecKey = ninja.privateKey.getECKeyFromMultiplying(key1, key2);
|
||||
if (ecKey != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testGetECKeyFromBase6Key: function () {
|
||||
var base = 6;
|
||||
var baseKey = "100531114202410255230521444145414341221420541210522412225005202300434134213212540304311321323051431";
|
||||
var hexKey = "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B";
|
||||
var bigInt = new BigInteger(baseKey, base);
|
||||
var ecKey = new Bitcoin.ECKey(bigInt);
|
||||
if (ecKey.getBitcoinHexFormat() != hexKey) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// EllipticCurve tests
|
||||
testDecodePointEqualsDecodeFrom: function () {
|
||||
var key = "04F04BF260DCCC46061B5868F60FE962C77B5379698658C98A93C3129F5F98938020F36EBBDE6F1BEAF98E5BD0E425747E68B0F2FB7A2A59EDE93F43C0D78156FF";
|
||||
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
var ecPoint1 = EllipticCurve.PointFp.decodeFrom(ecparams.getCurve(), Crypto.util.hexToBytes(key));
|
||||
var ecPoint2 = ecparams.getCurve().decodePointHex(key);
|
||||
if (!ecPoint1.equals(ecPoint2)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testDecodePointHexForCompressedPublicKey: function () {
|
||||
var key = "03F04BF260DCCC46061B5868F60FE962C77B5379698658C98A93C3129F5F989380";
|
||||
var pubHexUncompressed = ninja.publicKey.getDecompressedPubKeyHex(key);
|
||||
if (pubHexUncompressed != "04F04BF260DCCC46061B5868F60FE962C77B5379698658C98A93C3129F5F98938020F36EBBDE6F1BEAF98E5BD0E425747E68B0F2FB7A2A59EDE93F43C0D78156FF") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// old bugs
|
||||
testBugWithLeadingZeroBytePublicKey: function () {
|
||||
var key = "5Je7CkWTzgdo1RpwjYhwnVKxQXt8EPRq17WZFtWcq5umQdsDtTP";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinAddress() != "1M6dsMZUjFxjdwsyVk8nJytWcfr9tfUa9E") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
testBugWithLeadingZeroBytePrivateKey: function () {
|
||||
var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da";
|
||||
var btcKey = new Bitcoin.ECKey(key);
|
||||
if (btcKey.getBitcoinAddress() != "1NAjZjF81YGfiJ3rTKc7jf1nmZ26KN7Gkn") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
asynchronousTests: {
|
||||
testBip38: function (done) {
|
||||
var tests = [["6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg", "TestingOneTwoThree", "5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR"],
|
||||
["6PRNFFkZc2NZ6dJqFfhRoFNMR9Lnyj7dYGrzdgXXVMXcxoKTePPX1dWByq", "Satoshi", "5HtasZ6ofTHP6HCwTqTkLDuLQisYPah7aUnSKfC7h4hMUVw2gi5"],
|
||||
["6PYNKZ1EAgYgmQfmNVamxyXVWHzK5s6DGhwP4J5o44cvXdoY7sRzhtpUeo", "TestingOneTwoThree", "L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP"],
|
||||
["6PYLtMnXvfG3oJde97zRyLYFZCYizPU5T3LwgdYJz1fRhh16bU7u6PPmY7", "Satoshi", "KwYgW8gcxj1JWJXhPSu4Fqwzfhp5Yfi42mdYmMa4XqK7NJxXUSK7"],
|
||||
["6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX", "TestingOneTwoThree", "5K4caxezwjGCGfnoPTZ8tMcJBLB7Jvyjv4xxeacadhq8nLisLR2"],
|
||||
["6PfLGnQs6VZnrNpmVKfjotbnQuaJK4KZoPFrAjx1JMJUa1Ft8gnf5WxfKd", "Satoshi", "5KJ51SgxWaAYR13zd9ReMhJpwrcX47xTJh2D3fGPG9CM8vkv5sH"],
|
||||
["6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j", "MOLON LABE", "5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8"],
|
||||
["6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5mar2ngH", Crypto.charenc.UTF8.bytesToString([206, 156, 206, 159, 206, 155, 206, 169, 206, 157, 32, 206, 155, 206, 145, 206, 146, 206, 149])/*UTF-8 characters, encoded in source so they don't get corrupted*/, "5KMKKuUmAkiNbA3DazMQiLfDq47qs8MAEThm4yL8R2PhV1ov33D"]];
|
||||
// running each test uses a lot of memory, which isn't freed
|
||||
// immediately, so give the VM a little time to reclaim memory
|
||||
function waitThenCall(callback) {
|
||||
return function () { setTimeout(callback, 6000); }
|
||||
}
|
||||
|
||||
var decryptTest = function (test, i, onComplete) {
|
||||
ninja.privateKey.BIP38EncryptedKeyToByteArrayAsync(test[0], test[1], function (privBytes) {
|
||||
if (privBytes.constructor == Error) {
|
||||
document.getElementById("unittestresults").innerHTML += "fail testDecryptBip38 #" + i + ", error: " + privBytes.message + "<br/>";
|
||||
} else {
|
||||
var btcKey = new Bitcoin.ECKey(privBytes);
|
||||
var wif = !test[2].substr(0, 1).match(/[LK]/) ? btcKey.setCompressed(false).getBitcoinWalletImportFormat() : btcKey.setCompressed(true).getBitcoinWalletImportFormat();
|
||||
if (wif != test[2]) {
|
||||
document.getElementById("unittestresults").innerHTML += "fail testDecryptBip38 #" + i + "<br/>";
|
||||
} else {
|
||||
document.getElementById("unittestresults").innerHTML += "pass testDecryptBip38 #" + i + "<br/>";
|
||||
}
|
||||
}
|
||||
onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("unittestresults").innerHTML += "running " + tests.length + " tests named testDecryptBip38<br/>";
|
||||
ninja.runSerialized([function (cb) {
|
||||
ninja.forSerialized(0, tests.length, function (i, callback) {
|
||||
decryptTest(tests[i], i, waitThenCall(callback));
|
||||
}, waitThenCall(cb));
|
||||
} ], done);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(ninja);
|
114
src/ninja.vanitywallet.js
Normal file
114
src/ninja.vanitywallet.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
ninja.wallets.vanitywallet = {
|
||||
open: function () {
|
||||
document.getElementById("vanityarea").style.display = "block";
|
||||
},
|
||||
|
||||
close: function () {
|
||||
document.getElementById("vanityarea").style.display = "none";
|
||||
document.getElementById("vanitystep1area").style.display = "none";
|
||||
document.getElementById("vanitystep2area").style.display = "none";
|
||||
document.getElementById("vanitystep1icon").setAttribute("class", "more");
|
||||
document.getElementById("vanitystep2icon").setAttribute("class", "more");
|
||||
},
|
||||
|
||||
generateKeyPair: function () {
|
||||
var key = new Bitcoin.ECKey(false);
|
||||
var publicKey = key.getPubKeyHex();
|
||||
var privateKey = key.getBitcoinHexFormat();
|
||||
document.getElementById("vanitypubkey").innerHTML = publicKey;
|
||||
document.getElementById("vanityprivatekey").innerHTML = privateKey;
|
||||
document.getElementById("vanityarea").style.display = "block";
|
||||
document.getElementById("vanitystep1area").style.display = "none";
|
||||
},
|
||||
|
||||
addKeys: function () {
|
||||
var privateKeyWif = ninja.translator.get("vanityinvalidinputcouldnotcombinekeys");
|
||||
var bitcoinAddress = ninja.translator.get("vanityinvalidinputcouldnotcombinekeys");
|
||||
var publicKeyHex = ninja.translator.get("vanityinvalidinputcouldnotcombinekeys");
|
||||
try {
|
||||
var input1KeyString = document.getElementById("vanityinput1").value;
|
||||
var input2KeyString = document.getElementById("vanityinput2").value;
|
||||
|
||||
// both inputs are public keys
|
||||
if (ninja.publicKey.isPublicKeyHexFormat(input1KeyString) && ninja.publicKey.isPublicKeyHexFormat(input2KeyString)) {
|
||||
// add both public keys together
|
||||
if (document.getElementById("vanityradioadd").checked) {
|
||||
var pubKeyByteArray = ninja.publicKey.getByteArrayFromAdding(input1KeyString, input2KeyString);
|
||||
if (pubKeyByteArray == null) {
|
||||
alert(ninja.translator.get("vanityalertinvalidinputpublickeysmatch"));
|
||||
}
|
||||
else {
|
||||
privateKeyWif = ninja.translator.get("vanityprivatekeyonlyavailable");
|
||||
bitcoinAddress = ninja.publicKey.getBitcoinAddressFromByteArray(pubKeyByteArray);
|
||||
publicKeyHex = ninja.publicKey.getHexFromByteArray(pubKeyByteArray);
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert(ninja.translator.get("vanityalertinvalidinputcannotmultiple"));
|
||||
}
|
||||
}
|
||||
// one public key and one private key
|
||||
else if ((ninja.publicKey.isPublicKeyHexFormat(input1KeyString) && ninja.privateKey.isPrivateKey(input2KeyString))
|
||||
|| (ninja.publicKey.isPublicKeyHexFormat(input2KeyString) && ninja.privateKey.isPrivateKey(input1KeyString))
|
||||
) {
|
||||
privateKeyWif = ninja.translator.get("vanityprivatekeyonlyavailable");
|
||||
var pubKeyHex = (ninja.publicKey.isPublicKeyHexFormat(input1KeyString)) ? input1KeyString : input2KeyString;
|
||||
var ecKey = (ninja.privateKey.isPrivateKey(input1KeyString)) ? new Bitcoin.ECKey(input1KeyString) : new Bitcoin.ECKey(input2KeyString);
|
||||
// add
|
||||
if (document.getElementById("vanityradioadd").checked) {
|
||||
var pubKeyCombined = ninja.publicKey.getByteArrayFromAdding(pubKeyHex, ecKey.getPubKeyHex());
|
||||
}
|
||||
// multiply
|
||||
else {
|
||||
var pubKeyCombined = ninja.publicKey.getByteArrayFromMultiplying(pubKeyHex, ecKey);
|
||||
}
|
||||
if (pubKeyCombined == null) {
|
||||
alert(ninja.translator.get("vanityalertinvalidinputpublickeysmatch"));
|
||||
} else {
|
||||
bitcoinAddress = ninja.publicKey.getBitcoinAddressFromByteArray(pubKeyCombined);
|
||||
publicKeyHex = ninja.publicKey.getHexFromByteArray(pubKeyCombined);
|
||||
}
|
||||
}
|
||||
// both inputs are private keys
|
||||
else if (ninja.privateKey.isPrivateKey(input1KeyString) && ninja.privateKey.isPrivateKey(input2KeyString)) {
|
||||
var combinedPrivateKey;
|
||||
// add both private keys together
|
||||
if (document.getElementById("vanityradioadd").checked) {
|
||||
combinedPrivateKey = ninja.privateKey.getECKeyFromAdding(input1KeyString, input2KeyString);
|
||||
}
|
||||
// multiply both private keys together
|
||||
else {
|
||||
combinedPrivateKey = ninja.privateKey.getECKeyFromMultiplying(input1KeyString, input2KeyString);
|
||||
}
|
||||
if (combinedPrivateKey == null) {
|
||||
alert(ninja.translator.get("vanityalertinvalidinputprivatekeysmatch"));
|
||||
}
|
||||
else {
|
||||
bitcoinAddress = combinedPrivateKey.getBitcoinAddress();
|
||||
privateKeyWif = combinedPrivateKey.getBitcoinWalletImportFormat();
|
||||
publicKeyHex = combinedPrivateKey.getPubKeyHex();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
document.getElementById("vanityprivatekeywif").innerHTML = privateKeyWif;
|
||||
document.getElementById("vanityaddress").innerHTML = bitcoinAddress;
|
||||
document.getElementById("vanitypublickeyhex").innerHTML = publicKeyHex;
|
||||
document.getElementById("vanitystep2area").style.display = "block";
|
||||
document.getElementById("vanitystep2icon").setAttribute("class", "less");
|
||||
},
|
||||
|
||||
openCloseStep: function (num) {
|
||||
// do close
|
||||
if (document.getElementById("vanitystep" + num + "area").style.display == "block") {
|
||||
document.getElementById("vanitystep" + num + "area").style.display = "none";
|
||||
document.getElementById("vanitystep" + num + "icon").setAttribute("class", "more");
|
||||
}
|
||||
// do open
|
||||
else {
|
||||
document.getElementById("vanitystep" + num + "area").style.display = "block";
|
||||
document.getElementById("vanitystep" + num + "icon").setAttribute("class", "less");
|
||||
}
|
||||
}
|
||||
};
|
1034
src/qrcode.js
Normal file
1034
src/qrcode.js
Normal file
File diff suppressed because it is too large
Load diff
130
src/securerandom.js
Normal file
130
src/securerandom.js
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*!
|
||||
* Random number generator with ArcFour PRNG
|
||||
*
|
||||
* NOTE: For best results, put code like
|
||||
* <body onclick='SecureRandom.seedTime();' onkeypress='SecureRandom.seedTime();'>
|
||||
* in your main HTML document.
|
||||
*
|
||||
* Copyright Tom Wu, bitaddress.org BSD License.
|
||||
* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE
|
||||
*/
|
||||
(function () {
|
||||
|
||||
// Constructor function of Global SecureRandom object
|
||||
var sr = window.SecureRandom = function () { };
|
||||
|
||||
// Properties
|
||||
sr.state;
|
||||
sr.pool;
|
||||
sr.pptr;
|
||||
|
||||
// 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()
|
||||
sr.poolSize = 256;
|
||||
|
||||
|
||||
// --- object methods ---
|
||||
|
||||
// public method
|
||||
// ba: byte array
|
||||
sr.prototype.nextBytes = function (ba) {
|
||||
var i;
|
||||
for (i = 0; i < ba.length; ++i) ba[i] = sr.getByte();
|
||||
};
|
||||
|
||||
|
||||
// --- static methods ---
|
||||
|
||||
// Mix in the current time (w/milliseconds) into the pool
|
||||
// NOTE: this method should be called from body click/keypress event handlers to increase entropy
|
||||
sr.seedTime = function () {
|
||||
sr.seedInt(new Date().getTime());
|
||||
}
|
||||
|
||||
sr.getByte = function () {
|
||||
if (sr.state == null) {
|
||||
sr.seedTime();
|
||||
sr.state = sr.ArcFour(); // Plug in your RNG constructor here
|
||||
sr.state.init(sr.pool);
|
||||
for (sr.pptr = 0; sr.pptr < sr.pool.length; ++sr.pptr)
|
||||
sr.pool[sr.pptr] = 0;
|
||||
sr.pptr = 0;
|
||||
}
|
||||
// TODO: allow reseeding after first request
|
||||
return sr.state.next();
|
||||
}
|
||||
|
||||
// Mix in a 32-bit integer into the pool
|
||||
sr.seedInt = function (x) {
|
||||
sr.pool[sr.pptr++] ^= x & 255;
|
||||
sr.pool[sr.pptr++] ^= (x >> 8) & 255;
|
||||
sr.pool[sr.pptr++] ^= (x >> 16) & 255;
|
||||
sr.pool[sr.pptr++] ^= (x >> 24) & 255;
|
||||
if (sr.pptr >= sr.poolSize) sr.pptr -= sr.poolSize;
|
||||
}
|
||||
|
||||
|
||||
// Arcfour is a PRNG
|
||||
sr.ArcFour = function () {
|
||||
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;
|
||||
|
||||
return new Arcfour();
|
||||
};
|
||||
|
||||
|
||||
// Initialize the pool with junk if needed.
|
||||
if (sr.pool == null) {
|
||||
sr.pool = new Array();
|
||||
sr.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)
|
||||
sr.pool[sr.pptr++] = z.charCodeAt(t) & 255;
|
||||
}
|
||||
while (sr.pptr < sr.poolSize) { // extract some randomness from Math.random()
|
||||
t = Math.floor(65536 * Math.random());
|
||||
sr.pool[sr.pptr++] = t >>> 8;
|
||||
sr.pool[sr.pptr++] = t & 255;
|
||||
}
|
||||
sr.pptr = 0;
|
||||
sr.seedTime();
|
||||
// entropy
|
||||
sr.seedInt(window.screenX);
|
||||
sr.seedInt(window.screenY);
|
||||
}
|
||||
})();
|
Loading…
Reference in a new issue