diff --git a/README.md b/README.md index 7d0455b..9bb618e 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,31 @@ needed for libp2p. This is based on this [go implementation](https://github.com/ - `type: String`, only `'RSA'` is currently supported - `bits: Number` - `cb: Function`, with the signature `function (err, privateKey)` + +Generates a keypair of the given type and bitsize. + +### `marshalPublicKey(key[, type])` + +- `key: crypto.rsa.RsaPublicKey` +- `type: String`, only `'RSA'` is currently supported + +Converts a public key object into a protobuf serialized public key. + +### `unmarshalPublicKey(buf)` + +- `buf: Buffer` + +Converts a protobuf serialized public key into its representative object. + +### `marshalPrivateKey(key[, type])` + +- `key: crypto.rsa.RsaPrivateKey` +- `type: String`, only `'RSA'` is currently supported + +Converts a private key object into a protobuf serialized private key. + +### `unmarshalPrivateKey(buf)` + +- `buf: Buffer` + +Converts a protobuf serialized private key into its representative object. diff --git a/src/index.js b/src/index.js index 141aec6..cda4e04 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,10 @@ 'use strict' +const protobuf = require('protocol-buffers') +const fs = require('fs') +const path = require('path') +const pbm = protobuf(fs.readFileSync(path.join(__dirname, './crypto.proto'))) + exports.utils = require('./utils') const keys = exports.keys = require('./keys') @@ -26,3 +31,59 @@ exports.generateEphemeralKeyPair = (curveName, cb) => { exports.keyStretcher = (cipherType, hashType, secret) => { throw new Error('Not implemented') } + +// Converts a protobuf serialized public key into its +// representative object +exports.unmarshalPublicKey = (buf) => { + const decoded = pbm.PublicKey.decode(buf) + + switch (decoded.Type) { + case pbm.KeyType.RSA: + return keys.rsa.unmarshalRsaPublicKey(decoded.Data) + default: + throw new Error('invalid or unsupported key type') + } +} + +// Converts a public key object into a protobuf serialized public key +exports.marshalPublicKey = (key, type) => { + type = (type || 'rsa').toLowerCase() + + // for now only rsa is supported + if (type !== 'rsa') { + throw new Error('invalid or unsupported key type') + } + + return pbm.PublicKey.encode({ + Type: pbm.KeyType.RSA, + Data: key.marshal() + }) +} + +// Converts a protobuf serialized private key into its +// representative object +exports.unmarshalPrivateKey = (buf) => { + const decoded = pbm.PrivateKey.decode(buf) + + switch (decoded.Type) { + case pbm.KeyType.RSA: + return keys.rsa.unmarshalRsaPrivateKey(decoded.Data) + default: + throw new Error('invalid or unsupported key type') + } +} + +// Converts a private key object into a protobuf serialized private key +exports.marshalPrivateKey = (key, type) => { + type = (type || 'rsa').toLowerCase() + + // for now only rsa is supported + if (type !== 'rsa') { + throw new Error('invalid or unsupported key type') + } + + return pbm.PrivateKey.encode({ + Type: pbm.KeyType.RSA, + Data: key.marshal() + }) +} diff --git a/src/keys/rsa.js b/src/keys/rsa.js index ec89590..ef1782c 100644 --- a/src/keys/rsa.js +++ b/src/keys/rsa.js @@ -25,7 +25,7 @@ class RsaPublicKey { } marshal () { - return forge.asn1.toDer(pki.publicKeyToAsn1(this._key)).bytes() + return new Buffer(forge.asn1.toDer(pki.publicKeyToAsn1(this._key)).bytes(), 'binary') } get bytes () { @@ -74,7 +74,7 @@ class RsaPrivateKey { } marshal () { - return forge.asn1.toDer(pki.privateKeyToAsn1(this._privateKey)).bytes() + return new Buffer(forge.asn1.toDer(pki.privateKeyToAsn1(this._privateKey)).bytes(), 'binary') } get bytes () { @@ -94,12 +94,18 @@ class RsaPrivateKey { } function unmarshalRsaPrivateKey (bytes) { + if (Buffer.isBuffer(bytes)) { + bytes = forge.util.createBuffer(bytes.toString('binary')) + } const key = pki.privateKeyFromAsn1(forge.asn1.fromDer(bytes)) return new RsaPrivateKey(key) } function unmarshalRsaPublicKey (bytes) { + if (Buffer.isBuffer(bytes)) { + bytes = forge.util.createBuffer(bytes.toString('binary')) + } const key = pki.publicKeyFromAsn1(forge.asn1.fromDer(bytes)) return new RsaPublicKey(key) diff --git a/test/index.spec.js b/test/index.spec.js index 3dc0834..281c6d8 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -4,116 +4,26 @@ const expect = require('chai').expect const crypto = require('../src') -const rsa = crypto.keys.rsa describe('libp2p-crypto', () => { - describe('generateKeyPair', () => { - describe('RSA', () => { - let key - before((done) => { - crypto.generateKeyPair('RSA', 2048, (err, _key) => { - if (err) return done(err) - key = _key - done() - }) - }) - - it('generates a valid key', () => { - expect( - key - ).to.be.an.instanceof( - rsa.RsaPrivateKey - ) - - expect( - key.hash() - ).to.have.length( - 34 - ) - }) - - it('signs', () => { - const pk = key.public - const text = key.genSecret() - const sig = key.sign(text) - - expect( - pk.verify(text, sig) - ).to.be.eql( - true - ) - }) - - it('encoding', () => { - const keyMarshal = key.marshal() - const key2 = rsa.unmarshalRsaPrivateKey(keyMarshal) - const keyMarshal2 = key2.marshal() - - expect( - keyMarshal - ).to.be.eql( - keyMarshal2 - ) - - const pk = key.public - const pkMarshal = pk.marshal() - const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal) - const pkMarshal2 = pk2.marshal() - - expect( - pkMarshal - ).to.be.eql( - pkMarshal2 - ) - }) - - describe('key equals', () => { - it('equals itself', () => { - expect( - key.equals(key) - ).to.be.eql( - true - ) - - expect( - key.public.equals(key.public) - ).to.be.eql( - true - ) - }) - - it('not equals other key', (done) => { - crypto.generateKeyPair('RSA', 2048, (err, key2) => { - if (err) return done(err) - - expect( - key.equals(key2) - ).to.be.eql( - false - ) - - expect( - key2.equals(key) - ).to.be.eql( - false - ) - - expect( - key.public.equals(key2.public) - ).to.be.eql( - false - ) - - expect( - key2.public.equals(key.public) - ).to.be.eql( - false - ) - - done() - }) - }) - }) + let key + before((done) => { + crypto.generateKeyPair('RSA', 2048, (err, _key) => { + if (err) return done(err) + key = _key + done() }) }) + + it('marshalPublicKey and unmarshalPublicKey', () => { + const key2 = crypto.unmarshalPublicKey(crypto.marshalPublicKey(key.public)) + + expect(key2.equals(key.public)).to.be.eql(true) + }) + + it('marshalPrivateKey and unmarshalPrivateKey', () => { + const key2 = crypto.unmarshalPrivateKey(crypto.marshalPrivateKey(key)) + + expect(key2.equals(key)).to.be.eql(true) + }) }) diff --git a/test/rsa.spec.js b/test/rsa.spec.js new file mode 100644 index 0000000..52fe924 --- /dev/null +++ b/test/rsa.spec.js @@ -0,0 +1,115 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect + +const crypto = require('../src') +const rsa = crypto.keys.rsa + +describe('RSA', () => { + let key + before((done) => { + crypto.generateKeyPair('RSA', 2048, (err, _key) => { + if (err) return done(err) + key = _key + done() + }) + }) + + it('generates a valid key', () => { + expect( + key + ).to.be.an.instanceof( + rsa.RsaPrivateKey + ) + + expect( + key.hash() + ).to.have.length( + 34 + ) + }) + + it('signs', () => { + const pk = key.public + const text = key.genSecret() + const sig = key.sign(text) + + expect( + pk.verify(text, sig) + ).to.be.eql( + true + ) + }) + + it('encoding', () => { + const keyMarshal = key.marshal() + const key2 = rsa.unmarshalRsaPrivateKey(keyMarshal) + const keyMarshal2 = key2.marshal() + + expect( + keyMarshal + ).to.be.eql( + keyMarshal2 + ) + + const pk = key.public + const pkMarshal = pk.marshal() + const pk2 = rsa.unmarshalRsaPublicKey(pkMarshal) + const pkMarshal2 = pk2.marshal() + + expect( + pkMarshal + ).to.be.eql( + pkMarshal2 + ) + }) + + describe('key equals', () => { + it('equals itself', () => { + expect( + key.equals(key) + ).to.be.eql( + true + ) + + expect( + key.public.equals(key.public) + ).to.be.eql( + true + ) + }) + + it('not equals other key', (done) => { + crypto.generateKeyPair('RSA', 2048, (err, key2) => { + if (err) return done(err) + + expect( + key.equals(key2) + ).to.be.eql( + false + ) + + expect( + key2.equals(key) + ).to.be.eql( + false + ) + + expect( + key.public.equals(key2.public) + ).to.be.eql( + false + ) + + expect( + key2.public.equals(key.public) + ).to.be.eql( + false + ) + + done() + }) + }) + }) +})