From 6ce01ab434a5e2645f3064eded6a48fc85a966e4 Mon Sep 17 00:00:00 2001 From: nginnever Date: Tue, 2 Feb 2016 15:50:45 -0800 Subject: [PATCH] keys now generated and derived match with go client --- README.md | 5 +- package.json | 4 +- src/crypto.proto | 15 ++++++ src/index.js | 128 +++++++++++++++++++++++++++++++++++++++++------ tests/id-test.js | 9 ++-- 5 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 src/crypto.proto diff --git a/README.md b/README.md index 34daa65..c6ca0de 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,10 @@ peer-id JavaScript implementation # Description -A IPFS Peer Id is based on a sha256 has of the peer public key, using [multihash](https://github.com/jbenet/multihash) +A IPFS Peer Id is based on a sha256 hash of the peer public key, using [multihash](https://github.com/jbenet/multihash) + +The public key is currently a base64 encoded string of a protobuf of an RSA DER buffer. This uses a node buffer to pass the base64 encoded public key protobuf to the multihash for ID generation. + # Usage diff --git a/package.json b/package.json index cb8054b..b489016 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ }, "dependencies": { "bs58": "^3.0.0", - "keypair": "^1.0.0", "multihashing": "^0.2.0", - "node-forge": "^0.6.38" + "node-forge": "^0.6.38", + "protocol-buffers": "^3.1.4" } } diff --git a/src/crypto.proto b/src/crypto.proto new file mode 100644 index 0000000..76bc53c --- /dev/null +++ b/src/crypto.proto @@ -0,0 +1,15 @@ +package crypto.pb; + +enum KeyType { + RSA = 0; +} + +message PublicKey { + required KeyType Type = 1; + required bytes Data = 2; +} + +message PrivateKey { + required KeyType Type = 1; + required bytes Data = 2; +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index f4e3cae..cf54ea9 100644 --- a/src/index.js +++ b/src/index.js @@ -2,10 +2,14 @@ * Id is an object representation of a peer Id. a peer Id is a multihash */ +var fs = require('fs') var multihashing = require('multihashing') var base58 = require('bs58') -var keypair = require('keypair') var forge = require('node-forge') +var protobuf = require('protocol-buffers') + +//protobuf read from file +var messages = protobuf(fs.readFileSync(__dirname+'/crypto.proto')) exports = module.exports = Id @@ -26,7 +30,7 @@ function Id (id, privKey, pubKey) { self.toPrint = function () { return { - id: self.toHexString(), + id: self.toB58String(), privKey: privKey.toString('hex'), pubKey: pubKey.toString('hex') } @@ -47,18 +51,78 @@ function Id (id, privKey, pubKey) { } } -function fix (str) { - return str.replace(/\r/g, '') + '\n' +//unwrap the private key protobuf stream +function unmarshal (key) { + var dpb = messages.PrivateKey.decode(key) + return dpb +} + +//create a public key protobuf to be base64 string stored in config +function marshal (data, type) { + if(type == 'Public'){ + var epb = messages.PublicKey.encode({ + Type: 0, + Data: data + }) + } + + if(type == 'Private'){ + var epb = messages.PrivateKey.encode({ + Type: 0, + Data: data + }) + } + + return epb } // generation - exports.create = function () { - var pair = keypair() + //generate keys + var pair = forge.rsa.generateKeyPair({bits:2048, e: 0x10001}) - var mhId = multihashing(pair.public, 'sha2-256') + //Create Public Key + //return the RSA public key to asn1 object and DER encode + var asnPub = forge.pki.publicKeyToAsn1(pair.publicKey) - return new Id(mhId, pair.private, pair.public) + //create der buffer of public key asn.1 object + var derPub = forge.asn1.toDer(asnPub) + + //create forge buffer of der public key buffer + var fDerBuf = forge.util.createBuffer(derPub.data, 'binary') + + //convert forge buffer to node buffer public key + var nDerBuf = new Buffer(fDerBuf.getBytes(), 'binary') + + //protobuf the new DER bytes to the PublicKey Data: field + var marPubKey = marshal(nDerBuf, 'Public') + + //encode the protobuf public key to base64 string + var pubKeyb64 = marPubKey.toString('base64') + + + //create Private Key + //return the RSA private key to asn1 object and DER encode + var asnPriv = forge.pki.privateKeyToAsn1(pair.privateKey) + + //create der buffer of private key asn.1 object + var derPriv = forge.asn1.toDer(asnPriv) + + //create forge buffer of der private key buffer + var fDerBufPriv = forge.util.createBuffer(derPriv.data, 'binary') + + //convert forge buffer to node buffer private key + var nDerBufPriv = new Buffer(fDerBufPriv.getBytes(), 'binary') + + //protobuf the new DER bytes to the PrivateKey Data: field + var marPrivKey = marshal(nDerBufPriv, 'Private') + + //encode the protobuf private key to base64 string + var privKeyb64 = marPrivKey.toString('base64') + + var mhId = multihashing(marPubKey, 'sha2-256') + + return new Id(mhId, privKeyb64, pubKeyb64) } exports.createFromHexString = function (str) { @@ -74,17 +138,49 @@ exports.createFromB58String = function (str) { } exports.createFromPubKey = function (pubKey) { + var buf = new Buffer(pubKey, 'base64') var mhId = multihashing(pubKey, 'sha2-256') return new Id(mhId, null, pubKey) } exports.createFromPrivKey = function (privKey) { - var privateKey = forge.pki.privateKeyFromPem(privKey) - var publicKey = { - n: privateKey.n, - e: privateKey.e - } - var pubKey = fix(forge.pki.publicKeyToRSAPublicKeyPem(publicKey, 72)) - var mhId = multihashing(pubKey, 'sha2-256') - return new Id(mhId, privKey, pubKey) + //create a buffer from the base64 encoded string + var buf = new Buffer(privKey, 'base64') + + //get the private key data from the protobuf + var mpk = unmarshal(buf) + + //create a forge buffer + var fbuf = forge.util.createBuffer(mpk.Data.toString('binary')) + + //create an asn1 object from the private key bytes saved in the protobuf Data: field + var asnPriv = forge.asn1.fromDer(fbuf) + + //get the RSA privatekey data from the asn1 object + var privateKey = forge.pki.privateKeyFromAsn1(asnPriv) + + //set the RSA public key to the modulus and exponent of the private key + var publicKey = forge.pki.rsa.setPublicKey(privateKey.n, privateKey.e) + + //return the RSA public to asn1 object and DER encode + var asnPub = forge.pki.publicKeyToAsn1(publicKey) + + //create der buffer of public key asn.1 object + var derPub = forge.asn1.toDer(asnPub) + + //create forge buffer of der buffer + var fDerBuf = forge.util.createBuffer(derPub.data, 'binary') + + //convert forge buffer to node buffer + var nDerBuf = new Buffer(fDerBuf.getBytes(), 'binary') + + //protobuf the new DER bytes to the PublicKey Data: field + var marPubKey = marshal(nDerBuf, 'Public') + + //encode the protobuf public key to base64 string + var pubKeyb64 = marPubKey.toString('base64') + + var mhId = multihashing(marPubKey, 'sha2-256') + + return new Id(mhId, privKey, pubKeyb64) } diff --git a/tests/id-test.js b/tests/id-test.js index 220e35e..d0b2042 100644 --- a/tests/id-test.js +++ b/tests/id-test.js @@ -3,8 +3,8 @@ var PeerId = require('../src') var testId = { id: '1220151ab1658d8294ab34b71d5582cfe20d06414212f440a69366f1bc31deb5c72d', - privKey: '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAgPEiGHOwFEUdo95/DaALH69umbFI4xD3Jmla0hiHbkcW535arBfFd8nJ\ns5VPt49sgdSgn1ZmiqmHLgMwMz6mKplu4GsmWj5mjdyxiNl5z6R2rF+ZziuiwRTeHVX/8zR8\nM7Cbh0QXmzpoq6LcNOFHbg495zsbmT9QAtjVsS1KyF5324mxbTZtjaD6hxJkAL8aVi0ikvhA\nL4HuZ1m3brjSSZ0+epFCFL7UIoJlFfOvap2sAyxdOrSvY2PXKTE00s51YTin5+CrvofRLCJP\nROls1oFkhXIDGfuTGTxMxe3hSUlNj0LjQi6RXPGat/5XH0nCuFTODmyhrnnnx51OdgT9vwID\nAQABAoIBAQAIAnKSwET0zWJM9po/12w5eKVPKMMVT816dlrs6Bcpk4LpuGCbhhJ/IWrFHAZK\nqb8cxX+AxlYyUNuT0SDiXgbmaIeJqz5DptKqB0aD8LZvXpD8nieote8zPT+a5Oe0TNNWRqcy\nnNk2jEdKOiChrEjKnlncDkDloRgwRRXpHp4hmh3XrZwygAekxC+LFhO5YS4fuc5tQAzGyl/O\nGKnEmOtRqz4bYQRTrrfhwtAWdMOC90AEtoIPapLnJPBUujHNn7KLktdrmlPSqxFilIIe3jJH\nH0oG9Nr9ueSNat54NQZr2BBrXliFqXu/SomiAfN0jmouB+8lzNYSoUK25JmbJQExAoGBANLE\nLqJS1DpLa3Mg/GMCeCbjWG6qwpLjJttFAG2F4Yst3elwr7EiSR4aEARFikFJXS2XX4Rf16FM\njI582Cfq3QuksJ3FHMXWv6qu+avROSYPTFrKkzLSD9qsALX5YlZRv/skwzpLeE7Vjy6g1y6E\ne7AwENVdJabWdRhlqKadCe/3AoGBAJydZUEGn7EKAG98XuXsrPrP1yVIdG7tdGEktXwjJ2Wi\nCCptWwNqH/cGU/Oxm60oDvE/z7DtsFMXKlLRisIV8UbRotQXNeEwe33bTXHAAnTaGXxJ8DxX\nddvPjnoeg7SqyaKxAZW4hP8BfKZJXEQtxcnPgXXpLpbEMH4giWhJwX55AoGBAKqqiUiP4aJC\nqANV1okl2r1Cor0aMOxYW4J6YVpOatAUl/kLcnjw1lw1pnqPBODQ006zoHjEUwsdvUMz/KR2\nHf/rn8hhcGcS+ajwfuOOS8Rx5tYt6vvf9U6QsRKpmeNj1x06K4vsyMKtU3/iZdwZEz8b7MWY\n44AxcCgNSX+A8icJAoGAI3VnVV+gjD7NdnBcNAZv66Fe/qv24J6WeOAMzvxOkS4sVx7HOnCu\nqAkgvM37hyrIp0phRZerEkTuai3TErpRFE2mZgqTQlbtvsMGN7jXVYmDt6Yt5BuRLaFCiteZ\nzi/U0ybsSu+p/OpjRGrbnvwWCekXUJDo4W2t5QCM27XHP1ECgYBbKYbcswxeBha2au4Cj3cf\nz7oW5khGNziJELJSG8ulUNtR0LkfS429JXWjo3qYVYPBeEPePoYE1qcrDnSQesYt2yq1y5Uh\nJjwAxK1wVGw9UcQl0w/utuDxcGCArlszFcRNPX1g/5e0F07OXM8bF9gQcom1HZBKgLaoLStI\nz94OOA==\n-----END RSA PRIVATE KEY-----\n', - pubKey: '-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAgPEiGHOwFEUdo95/DaALH69umbFI4xD3Jmla0hiHbkcW535arBfFd8nJs5VP\nt49sgdSgn1ZmiqmHLgMwMz6mKplu4GsmWj5mjdyxiNl5z6R2rF+ZziuiwRTeHVX/8zR8M7Cb\nh0QXmzpoq6LcNOFHbg495zsbmT9QAtjVsS1KyF5324mxbTZtjaD6hxJkAL8aVi0ikvhAL4Hu\nZ1m3brjSSZ0+epFCFL7UIoJlFfOvap2sAyxdOrSvY2PXKTE00s51YTin5+CrvofRLCJPROls\n1oFkhXIDGfuTGTxMxe3hSUlNj0LjQi6RXPGat/5XH0nCuFTODmyhrnnnx51OdgT9vwIDAQAB\n-----END RSA PUBLIC KEY-----\n' + privKey: 'CAASqQkwggSlAgEAAoIBAQC1/m95i4waplpSl43/uefQClPfuguPsX6qa6pWE0Df+krs5p+RnhX+8j2aWBI9QZ0pliGryet5qM3in6J1ihb2LzjLUWIsWrMLxtqi/mQDjj1f0HfV11Q/k2v6yVgSgYJ+VoW51hCpAEdEnhthsbyKWMD6xVIvtkqOmK/FaxUMa2boWCVKsycf39U6GVaiuTk32btw6LqGMo8P1cehWDy80SOzv0qk2mD7ZJbKnVfVm8xwU+gzogasOFYs/LmKb4IEV9Otq0MHmWGUN0VyC82htoGBidheRs1Ssh7TDVtWlNTJSG1vJJV7cnIhpGKut2b0pMxhY1Sg0kn/VrfriXINAgMBAAECggEBAJrJuI4r/hF8gz3T4NYri9oJrqSOW97vG8heohVrcrYM70TmMblsN1ELPxHS7lBjSgRgyGqP5lMnG1UwaMCHnlfseeWTZmhLDBVsH/CZZP8RL2oaqJGb/u/Dtwcp0FqNBCvn8vzH8IuMzRCzWJ6SyMTyD9A5m1kxNeBqRLUoClLwZsLKuL8ld9sXfwmn1D1yY9sWLWCcrlPZnm4v49otc6Q9GYpurhOLBlQOqkEVqSyiN9N70bZ3QNPxWpAjKZS08X5PK0teI0b3uMZB9KwCdPkjMxHyymulyQ/bOkm1CCogFpGlG8abwyusIfrrjQOCeX+sUSei6/K/eN2kPLUUcoECgYEA0EKwQPq6mXmXiWqVMk08wz1JPqiNE9zreqaSOx9aOO/DbeR0xkfWjKy4RoCY9zo8FW0Y7jvwuEojm7LNZSrLrur3b2myfzHsdsjuo2t/fVilVrjtzxDEQSeBNOJasVdS+ZhB+/GHWfeZEcMjhXHkW9WRgMwIC/jTanmqLYy4nzkCgYEA37ZZJ+QH8+saafhztaSK/IC8pjJLTkHsMBjhSpVGm9TptpVdxHx/cHJdCDiHPfeP+TIPSOD//hGbHoTZuy2owZ0FJ2QFxyLpagEUch+Sy0f7fQZ+LoeUa1UJ9RsCbCCeKtcZvqZ0fuu9iWoR8LvJ7UI19SEyQ2T7Fymvbq3hFXUCgYEAsHHf7J3BHKjE97rifwSrV2sENF8Pb+W7aGXZ/NdaVGTm+aMWQKu6neL0GV94ufWP1ENjXOxRzYGa255IoM76VM9kJfOyNEuy4QzqCnDYSfWh13DEoqu86sqykIC6gAfRGACk3vVKTLIW8NKYtMXCyP+P0ESNCL+fN1WvFfpkrRkCgYEA3nriP6GvpwxwwGKt8D8rWeJNuprZ+YHl+g9EPoAmMGOV6laxYe7Obm3Nx5cwKJhDPnhiawAYlfu8YKWOQ3AtHB+kOIBonppBt4JLaxOrUS7NFJGYe32qRPPVa0TpK89kfQZePBQeVvrrC/XI0bhwINxv/NB+xDdw3qA+L7wM1OECgYA+3YgYJ6nbvT7s4DylRmyZ4AfOt3TcIjS3D0PDj4S4agZrvlPCKgn1LwBIFutjOe6V9lPSWZJiw6flfImhpWvQOap3zdT9gsU5PhbUmD3TndAk0n10+lmMYpGkU2UM/+zV5HsiZA5X+DPcVbSnIuNT/7pZdbD5uFF9Ibzoh022Fg==', + pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1/m95i4waplpSl43/uefQClPfuguPsX6qa6pWE0Df+krs5p+RnhX+8j2aWBI9QZ0pliGryet5qM3in6J1ihb2LzjLUWIsWrMLxtqi/mQDjj1f0HfV11Q/k2v6yVgSgYJ+VoW51hCpAEdEnhthsbyKWMD6xVIvtkqOmK/FaxUMa2boWCVKsycf39U6GVaiuTk32btw6LqGMo8P1cehWDy80SOzv0qk2mD7ZJbKnVfVm8xwU+gzogasOFYs/LmKb4IEV9Otq0MHmWGUN0VyC82htoGBidheRs1Ssh7TDVtWlNTJSG1vJJV7cnIhpGKut2b0pMxhY1Sg0kn/VrfriXINAgMBAAE=' } var testIdHex = '1220151ab1658d8294ab34b71d5582cfe20d06414212f440a69366f1bc31deb5c72d' @@ -15,6 +15,7 @@ var testIdB58String = 'QmPm2sunRFpswBAByqunK5Yk8PLj7mxL5HpCS4Qg6p7LdS' test('create a new Id', function (t) { var id = PeerId.create() + console.log(id.toPrint()) t.ok(id) t.end() }) @@ -39,12 +40,12 @@ test('Recreate an B58 String', function (t) { test('Recreate from a Public Key', function (t) { var id = PeerId.createFromPubKey(testId.pubKey) - t.ok(id) + t.ok(id.pubKey == testId.pubKey) t.end() }) test('Recreate from a Private Key', function (t) { var id = PeerId.createFromPrivKey(testId.privKey) - t.ok(id) + t.ok(id.pubKey == testId.pubKey) t.end() })