Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

Catch error when unmarshaling instead of crashing #113

Merged
merged 7 commits into from
Dec 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 25 additions & 20 deletions src/keys/ed25519.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,28 @@ exports.publicKeyLength = nacl.sign.publicKeyLength
exports.privateKeyLength = nacl.sign.secretKeyLength

exports.generateKey = function (callback) {
const done = (err, res) => setImmediate(() => {
callback(err, res)
setImmediate(() => {
let result
try {
result = nacl.sign.keyPair()
} catch (err) {
return callback(err)
}
callback(null, result)
})

let keys
try {
keys = nacl.sign.keyPair()
} catch (err) {
return done(err)
}
done(null, keys)
}

// seed should be a 32 byte uint8array
exports.generateKeyFromSeed = function (seed, callback) {
const done = (err, res) => setImmediate(() => callback(err, res))

let keys
try {
keys = nacl.sign.keyPair.fromSeed(seed)
} catch (err) {
return done(err)
}
done(null, keys)
setImmediate(() => {
let result
try {
result = nacl.sign.keyPair.fromSeed(seed)
} catch (err) {
return callback(err)
}
callback(null, result)
})
}

exports.hashAndSign = function (key, msg, callback) {
Expand All @@ -41,6 +39,13 @@ exports.hashAndSign = function (key, msg, callback) {

exports.hashAndVerify = function (key, sig, msg, callback) {
setImmediate(() => {
callback(null, nacl.sign.detached.verify(msg, sig, key))
let result
try {
result = nacl.sign.detached.verify(msg, sig, key)
} catch (err) {
return callback(err)
}

callback(null, result)
})
}
8 changes: 7 additions & 1 deletion src/keys/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ exports.marshalPublicKey = (key, type) => {
// Converts a protobuf serialized private key into its
// representative object
exports.unmarshalPrivateKey = (buf, callback) => {
const decoded = keysPBM.PrivateKey.decode(buf)
let decoded
try {
decoded = keysPBM.PrivateKey.decode(buf)
} catch (err) {
return callback(err)
}

const data = decoded.Data

switch (decoded.Type) {
Expand Down
73 changes: 48 additions & 25 deletions src/keys/rsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,36 @@ const jwkToPem = require('pem-jwk').jwk2pem
exports.utils = require('./rsa-utils')

exports.generateKey = function (bits, callback) {
const done = (err, res) => setImmediate(() => callback(err, res))

let key
try {
key = keypair({ bits: bits })
} catch (err) {
return done(err)
}

done(null, {
privateKey: pemToJwk(key.private),
publicKey: pemToJwk(key.public)
setImmediate(() => {
let result
try {
const key = keypair({ bits: bits })
result = {
privateKey: pemToJwk(key.private),
publicKey: pemToJwk(key.public)
}
} catch (err) {
return callback(err)
}

callback(null, result)
})
}

// Takes a jwk key
exports.unmarshalPrivateKey = function (key, callback) {
callback(null, {
privateKey: key,
publicKey: {
kty: key.kty,
n: key.n,
e: key.e
setImmediate(() => {
if (!key) {
return callback(new Error('Key is invalid'))
}
callback(null, {
privateKey: key,
publicKey: {
kty: key.kty,
n: key.n,
e: key.e
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where would an exception be thrown in this operation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never underestimate the power of undefined: TypeError: Cannot read property 'e' of undefined

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but in this case, it would never through that error, only if you did something like key.e.something. As long as if key exists and it is an object, key.e will never throw

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted this, but now inside a setImmediate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what about unmarshalPrivateKey(undefined, (err, res) => {}) ??

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mkg20001 good point, fixing.

})
})
}

Expand All @@ -41,16 +47,33 @@ exports.getRandomValues = function (arr) {
}

exports.hashAndSign = function (key, msg, callback) {
const sign = crypto.createSign('RSA-SHA256')
setImmediate(() => {
let result
try {
const sign = crypto.createSign('RSA-SHA256')
sign.update(msg)
const pem = jwkToPem(key)
result = sign.sign(pem)
} catch (err) {
return callback(new Error('Key or message is invalid!: ' + err.message))
}

sign.update(msg)
setImmediate(() => callback(null, sign.sign(jwkToPem(key))))
callback(null, result)
})
}

exports.hashAndVerify = function (key, sig, msg, callback) {
const verify = crypto.createVerify('RSA-SHA256')

verify.update(msg)
setImmediate(() => {
let result
try {
const verify = crypto.createVerify('RSA-SHA256')
verify.update(msg)
const pem = jwkToPem(key)
result = verify.verify(pem, sig)
} catch (err) {
return callback(new Error('Key or message is invalid!:' + err.message))
}

setImmediate(() => callback(null, verify.verify(jwkToPem(key), sig)))
callback(null, result)
})
}
3 changes: 2 additions & 1 deletion test/crypto.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ chai.use(dirtyChai)
const crypto = require('../src')
const fixtures = require('./fixtures/go-key-rsa')

describe('libp2p-crypto', () => {
describe('libp2p-crypto', function () {
this.timeout(20 * 1000)
let key
before((done) => {
crypto.keys.generateKeyPair('RSA', 2048, (err, _key) => {
Expand Down
46 changes: 46 additions & 0 deletions test/helpers/test-garbage-error-handling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* eslint-env mocha */
'use strict'

const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)

const util = require('util')
const garbage = [Buffer.from('00010203040506070809', 'hex'), {}, null, false, undefined, true, 1, 0, Buffer.from(''), 'aGVsbG93b3JsZA==', 'helloworld', '']

function doTests (fncName, fnc, num, skipBuffersAndStrings) {
if (!num) {
num = 1
}

garbage.forEach((garbage) => {
if (skipBuffersAndStrings && (Buffer.isBuffer(garbage) || (typeof garbage) === 'string')) {
// skip this garbage because it's a buffer or a string and we were told do do that
return
}
let args = []
for (let i = 0; i < num; i++) {
args.push(garbage)
}
it(fncName + '(' + args.map(garbage => util.inspect(garbage)).join(', ') + ')', cb => {
args.push((err, res) => {
expect(err).to.exist()
expect(res).to.not.exist()
cb()
})

fnc.apply(null, args)
})
})
}

module.exports = (obj, fncs, num) => {
describe('returns error via cb instead of crashing', () => {
fncs.forEach(fnc => {
doTests(fnc, obj[fnc], num)
})
})
}

module.exports.doTests = doTests
11 changes: 10 additions & 1 deletion test/keys/ed25519.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ const crypto = require('../../src')
const ed25519 = crypto.keys.supportedKeys.ed25519
const fixtures = require('../fixtures/go-key-ed25519')

describe('ed25519', () => {
const testGarbage = require('../helpers/test-garbage-error-handling')

describe('ed25519', function () {
this.timeout(20 * 1000)
let key
before((done) => {
crypto.keys.generateKeyPair('Ed25519', 512, (err, _key) => {
Expand Down Expand Up @@ -176,6 +179,12 @@ describe('ed25519', () => {
})
})

describe('returns error via cb instead of crashing', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
testGarbage.doTests('key.verify', key.verify.bind(key), 2)
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
})

describe('go interop', () => {
let privateKey

Expand Down
11 changes: 10 additions & 1 deletion test/keys/rsa.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ const crypto = require('../../src')
const rsa = crypto.keys.supportedKeys.rsa
const fixtures = require('../fixtures/go-key-rsa')

describe('RSA', () => {
const testGarbage = require('../helpers/test-garbage-error-handling')

describe('RSA', function () {
this.timeout(20 * 1000)
let key

before((done) => {
Expand Down Expand Up @@ -131,6 +134,12 @@ describe('RSA', () => {
})
})

describe('returns error via cb instead of crashing', () => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
testGarbage.doTests('key.verify', key.verify.bind(key), 2, true)
testGarbage.doTests('crypto.keys.unmarshalPrivateKey', crypto.keys.unmarshalPrivateKey.bind(crypto.keys))
})

describe('go interop', () => {
it('verifies with data from go', (done) => {
const key = crypto.keys.unmarshalPublicKey(fixtures.verify.publicKey)
Expand Down
6 changes: 3 additions & 3 deletions test/keys/secp256k1.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const mockSecp256k1Module = {
}

describe('without libp2p-crypto-secp256k1 module present', () => {
crypto.keys.supportedKeys['secp256k1'] = undefined
crypto.keys.supportedKeys.secp256k1 = undefined

it('fails to generate a secp256k1 key', (done) => {
crypto.keys.generateKeyPair('secp256k1', 256, (err, key) => {
Expand Down Expand Up @@ -61,7 +61,7 @@ describe('with libp2p-crypto-secp256k1 module present', () => {
let key

before((done) => {
crypto.keys.supportedKeys['secp256k1'] = mockSecp256k1Module
crypto.keys.supportedKeys.secp256k1 = mockSecp256k1Module
crypto.keys.generateKeyPair('secp256k1', 256, (err, _key) => {
if (err) return done(err)
key = _key
Expand All @@ -70,7 +70,7 @@ describe('with libp2p-crypto-secp256k1 module present', () => {
})

after((done) => {
delete crypto.keys['secp256k1']
delete crypto.keys.secp256k1
done()
})

Expand Down