diff --git a/bitcore.js b/bitcore.js
index c1f595a..66de69d 100644
--- a/bitcore.js
+++ b/bitcore.js
@@ -60,5 +60,6 @@ requireWhenAccessed('WalletKey', './lib/WalletKey');
requireWhenAccessed('PeerManager', './lib/PeerManager');
requireWhenAccessed('Message', './lib/Message');
requireWhenAccessed('Electrum', './lib/Electrum');
+requireWhenAccessed('Armory', './lib/Armory');
module.exports.Buffer = Buffer;
diff --git a/browser/build.js b/browser/build.js
index 46cbf07..e900bf8 100644
--- a/browser/build.js
+++ b/browser/build.js
@@ -24,6 +24,7 @@ var pack = function (params) {
var modules = [
'lib/Address',
+ 'lib/Armory',
'lib/Base58',
'lib/BIP32',
'lib/Block',
diff --git a/examples/Armory.js b/examples/Armory.js
new file mode 100644
index 0000000..713cd8b
--- /dev/null
+++ b/examples/Armory.js
@@ -0,0 +1,66 @@
+var Armory = require('../lib/Armory');
+var Address = require('../lib/Address');
+
+// Initial public key can be retrieved from paper backup
+
+var PublicX = '9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 b809 f7ab ce4e';
+var PublicY = '66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4';
+var pubkey = '04' + PublicX.split(' ').join('') + PublicY.split(' ').join('');
+
+// Chain code can be generated by entering paper backup
+// on brainwallet.org/#chains or by using Armory.fromSeed() below
+
+var chaincode = '84ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481e';
+
+var armory = new Armory(chaincode, pubkey);
+
+console.log('Deriving public keys for');
+console.log('------------------------');
+console.log('Chain code: %s', chaincode);
+console.log('Public key: %s', pubkey);
+console.log('');
+
+for (var i = 0; i < 5; i++) {
+ console.log(Address.fromPubKey(armory.pubkey).as('base58'));
+ armory = armory.next();
+}
+
+// Derive first public key and chain code from seed
+var seed = [
+ 'aagh hjfj sihk ietj giik wwai awtd uodh hnji',
+ 'soss uaku egod utai itos fijj ihgi jhau jtoo'
+];
+
+console.log('');
+console.log('');
+console.log('Deriving public keys for');
+console.log('------------------------');
+console.log('Seed: %s', seed.join(' '));
+console.log('');
+
+// skip first public key
+var a = Armory.fromSeed(seed.join('\n')).next();
+
+for (var i = 0; i < 5; i++) {
+ console.log(Address.fromPubKey(a.pubkey).as('base58'));
+ a = a.next();
+}
+
+
+var mpk = '045a09a3286873a72f164476bde9d1d8e5c2bc044e35aa47eb6e798e325a86417f7c35b61d9905053533e0b4f2a26eca0330aadf21c638969e45aaace50e4c0c8784ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481e';
+
+console.log('');
+console.log('');
+console.log('Deriving public keys for');
+console.log('------------------------');
+console.log('Master Public Key: %s', mpk);
+console.log('');
+
+// skip first public key
+var b = Armory.fromMasterPublicKey(mpk).next();
+
+for (var i = 0; i < 5; i++) {
+ console.log(Address.fromPubKey(b.pubkey).as('base58'));
+ b = b.next();
+}
+
diff --git a/examples/browser/armory.html b/examples/browser/armory.html
new file mode 100644
index 0000000..4de54d5
--- /dev/null
+++ b/examples/browser/armory.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+ Enter you paper wallet seed:
+
+
+
+
+
+
+
+
diff --git a/lib/Armory.js b/lib/Armory.js
new file mode 100644
index 0000000..94be0e5
--- /dev/null
+++ b/lib/Armory.js
@@ -0,0 +1,115 @@
+var Point = require('./Point'),
+ Key = require('./Key'),
+ sha256 = require('../util').sha256,
+ twoSha256 = require('../util').twoSha256;
+
+/**
+ * For now, this class can only supports derivation from public key
+ * It doesn't support private key derivation (TODO).
+ *
+ * @example examples/Armory.js
+ */
+function Armory (chaincode, pubkey) {
+ this.chaincode = new Buffer(chaincode, 'hex');
+ this.pubkey = new Buffer(pubkey, 'hex');
+}
+
+Armory.prototype.generatePubKey = function () {
+ var pubKey = this.pubkey;
+ var chainCode = this.chaincode;
+ var chainXor = twoSha256(pubKey);
+
+ for (var i = 0; i < 32; i++)
+ chainXor[i] ^= chainCode[i];
+
+ var pt = Point.fromUncompressedPubKey(pubKey);
+ pt = Point.multiply(pt, chainXor);
+
+ var new_pubkey = pt.toUncompressedPubKey();
+
+ return new_pubkey;
+};
+
+Armory.prototype.next = function () {
+ var next_pubkey = this.generatePubKey();
+ return new Armory(this.chaincode, next_pubkey);
+};
+
+/**
+ * PS: MPK here represents the pubkey concatenated
+ * with the chain code. It is an unofficial standard.
+ *
+ * Armory will soon release an officially supported
+ * format:
+ *
+ * https://github.com/etotheipi/BitcoinArmory/issues/204#issuecomment-42217801
+ */
+Armory.fromMasterPublicKey = function (mpk) {
+ var pubkey = mpk.substr(0, 130);
+ var chaincode = mpk.substr(130, mpk.length);
+ return new Armory(chaincode, pubkey);
+};
+
+function decode (str) {
+ var from = '0123456789abcdef';
+ var to = 'asdfghjkwertuion';
+ var res = '';
+ for (var i = 0; i < str.length; i++)
+ res += from.charAt(to.indexOf(str.charAt(i)));
+ return res;
+}
+
+Armory.decodeSeed = function (seed) {
+ var keys = seed.trim().split('\n');
+ var lines = [];
+
+ for (var i = 0; i < keys.length; i++) {
+ var k = keys[i].replace(' ','');
+ var raw = new Buffer(decode(k), 'hex');
+ var data = raw.slice(0, 16);
+ lines.push(data);
+ }
+
+ var privKey = Buffer.concat([ lines[0], lines[1] ]);
+ var chainCode = (lines.length==4) ?
+ Buffer.concat([ lines[2], lines[3] ]) : Armory.deriveChaincode(privKey);
+
+ return {
+ privKey: privKey,
+ chainCode: chainCode
+ };
+};
+
+// Derive chain code from root key
+Armory.fromSeed = function (seed) {
+ var res = Armory.decodeSeed(seed);
+ // generate first public key
+ var key = new Key();
+ key.private = res.privKey;
+ key.compressed = false;
+ key.regenerateSync();
+
+ return new Armory(res.chainCode, key.public);
+};
+
+Armory.deriveChaincode = function (root) {
+ var msg = 'Derive Chaincode from Root Key';
+ var hash = twoSha256(root);
+
+ var okey = [];
+ var ikey = [];
+ for (var i = 0; i < hash.length; i++) {
+ okey.push(0x5c ^ hash[i]);
+ ikey.push(0x36 ^ hash[i]);
+ }
+
+ okey = new Buffer(okey);
+ ikey = new Buffer(ikey);
+
+ var m = new Buffer(msg, 'utf8');
+ var a = sha256(Buffer.concat([ ikey, m ]));
+ var b = sha256(Buffer.concat([ okey, a ]));
+ return b;
+};
+
+module.exports = Armory;
diff --git a/lib/Curve.js b/lib/Curve.js
index f2d140d..dfdef52 100644
--- a/lib/Curve.js
+++ b/lib/Curve.js
@@ -4,14 +4,20 @@ var bignum = imports.bignum || require('bignum');
var Point = imports.Point || require('./Point');
var n = bignum.fromBuffer(new Buffer("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 'hex'), {size: 32});
-var G = new Point(bignum.fromBuffer(new Buffer("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 'hex'), {size: 32}),
- bignum.fromBuffer(new Buffer("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 'hex'), {size: 32}));
-/* secp256k1 curve */
+
var Curve = function() {
};
+/* secp256k1 curve */
+var G;
Curve.getG = function() {
+ // don't use Point in top scope, causes exception in browser
+ // when Point is not loaded yet
+
+ // use cached version if available
+ G = G || new Point(bignum.fromBuffer(new Buffer("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 'hex'), {size: 32}),
+ bignum.fromBuffer(new Buffer("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 'hex'), {size: 32}));
return G;
};
diff --git a/lib/Point.js b/lib/Point.js
index 64d4dd7..3864b80 100644
--- a/lib/Point.js
+++ b/lib/Point.js
@@ -19,6 +19,12 @@ Point.add = function(p1, p2) {
return Point.fromUncompressedPubKey(pubKey);
};
+Point.multiply = function(p1, x) {
+ var u1 = p1.toUncompressedPubKey();
+ var pubKey = CPPKey.multiplyUncompressed(u1, x);
+ return Point.fromUncompressedPubKey(pubKey);
+};
+
//convert the public key of a Key into a Point
Point.fromUncompressedPubKey = function(pubkey) {
var point = new Point();
diff --git a/lib/browser/Point.js b/lib/browser/Point.js
index 1e935e0..2579d12 100644
--- a/lib/browser/Point.js
+++ b/lib/browser/Point.js
@@ -48,6 +48,32 @@ Point.add = function(p1, p2) {
return point;
};
+Point.multiply = function(p1, x) {
+ var x = new BigInteger(x.toString('hex'), 16);
+
+ var ecparams = getSECCurveByName('secp256k1');
+
+ var p1xhex = p1.x.toBuffer({size: 32}).toString('hex');
+ var p1x = new BigInteger(p1xhex, 16);
+ var p1yhex = p1.y.toBuffer({size: 32}).toString('hex');
+ var p1y = new BigInteger(p1yhex, 16);
+ var p1px = new ECFieldElementFp(ecparams.getCurve().getQ(), p1x);
+ var p1py = new ECFieldElementFp(ecparams.getCurve().getQ(), p1y);
+ var p1p = new ECPointFp(ecparams.getCurve(), p1px, p1py);
+
+ var p = p1p.multiply(x);
+
+ var point = new Point();
+ var pointxbuf = new Buffer(p.getX().toBigInteger().toByteArrayUnsigned());
+ point.x = bignum.fromBuffer(pointxbuf, {size: pointxbuf.length});
+ assert(pointxbuf.length <= 32);
+ var pointybuf = new Buffer(p.getY().toBigInteger().toByteArrayUnsigned());
+ assert(pointybuf.length <= 32);
+ point.y = bignum.fromBuffer(pointybuf, {size: pointybuf.length});
+
+ return point;
+};
+
//convert the public key of a Key into a Point
Point.fromUncompressedPubKey = function(pubkey) {
var point = new Point();
diff --git a/src/eckey.cc b/src/eckey.cc
index 8f279a6..02329f9 100644
--- a/src/eckey.cc
+++ b/src/eckey.cc
@@ -124,6 +124,7 @@ void Key::Init(Handle