Compare commits

...

14 Commits

Author SHA1 Message Date
David Dias
2b0b7abd78 chore: release version v0.8.7 2017-03-21 15:07:57 +00:00
David Dias
17f2065d21 chore: update contributors 2017-03-21 15:07:57 +00:00
David Dias
2f065167fa chore: update aegir 2017-03-21 15:05:22 +00:00
Friedel Ziegelmayer
e0ecce18ce chore: release version v0.8.6 2017-03-03 21:44:03 +01:00
Friedel Ziegelmayer
cea1140ec6 chore: update contributors 2017-03-03 21:44:03 +01:00
Jack Kleeman
e5b7c1f622 feat(keys): implement generateKeyPairFromSeed for ed25519
Implement generateKeyPairFromSeed for ed25519 - this will produce the same keypair for the same seed (or first 32 bytes of reader) as given to GenerateKeyPairWithReader in go-libp2p.
2017-03-03 21:38:51 +01:00
Friedel Ziegelmayer
20326199d9 Merge pull request #80 from libp2p/greenkeeper/tweetnacl-1.0.0-rc.1
Update tweetnacl to the latest version 🚀
2017-02-24 17:56:59 +01:00
greenkeeper[bot]
4e56e1724f fix(package): update tweetnacl to version 1.0.0-rc.1
https://greenkeeper.io/
2017-02-22 11:39:32 +00:00
David Dias
7d63f698c0 chore: release version v0.8.5 2017-02-10 18:54:16 -08:00
David Dias
a49df7786c chore: update contributors 2017-02-10 18:54:16 -08:00
David Dias
c73adb00cc fix: add libp2p-crypto-secp256k1 2017-02-10 18:53:36 -08:00
David Dias
8a95de4700 chore: release version v0.8.4 2017-02-10 17:54:47 -08:00
David Dias
c35a65133f chore: update contributors 2017-02-10 17:54:47 -08:00
David Dias
4eb4aa05de fix: rm browserify-optional and transforms 2017-02-10 17:54:11 -08:00
15 changed files with 213 additions and 58 deletions

20
CHANGELOG.md Normal file
View File

@@ -0,0 +1,20 @@
<a name="0.8.7"></a>
## [0.8.7](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.6...v0.8.7) (2017-03-21)
<a name="0.8.6"></a>
## [0.8.6](https://github.com/libp2p/js-libp2p-crypto/compare/v0.8.5...v0.8.6) (2017-03-03)
### Bug Fixes
* **package:** update tweetnacl to version 1.0.0-rc.1 ([4e56e17](https://github.com/libp2p/js-libp2p-crypto/commit/4e56e17))
### Features
* **keys:** implement generateKeyPairFromSeed for ed25519 ([e5b7c1f](https://github.com/libp2p/js-libp2p-crypto/commit/e5b7c1f))

View File

@@ -1,6 +1,6 @@
{ {
"name": "libp2p-crypto", "name": "libp2p-crypto",
"version": "0.8.3", "version": "0.8.7",
"description": "Crypto primitives for libp2p", "description": "Crypto primitives for libp2p",
"main": "src/index.js", "main": "src/index.js",
"browser": { "browser": {
@@ -33,28 +33,29 @@
"author": "Friedel Ziegelmayer <dignifiedqurie@gmail.com>", "author": "Friedel Ziegelmayer <dignifiedqurie@gmail.com>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"multihashing-async": "~0.4.2",
"asn1.js": "^4.9.1", "asn1.js": "^4.9.1",
"async": "^2.1.4", "async": "^2.1.5",
"browserify-aes": "^1.0.6", "browserify-aes": "^1.0.6",
"keypair": "^1.0.1", "keypair": "^1.0.1",
"nodeify": "^1.0.0", "libp2p-crypto-secp256k1": "^0.1.4",
"multihashing-async": "~0.4.4",
"nodeify": "^1.0.1",
"pem-jwk": "^1.5.1", "pem-jwk": "^1.5.1",
"protocol-buffers": "^3.2.1", "protocol-buffers": "^3.2.1",
"rsa-pem-to-jwk": "^1.1.3", "rsa-pem-to-jwk": "^1.1.3",
"safe-buffer": "^5.0.1", "safe-buffer": "^5.0.1",
"tweetnacl": "^0.14.5", "tweetnacl": "^1.0.0-rc.1",
"browserify-optional": "^1.0.0",
"webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master" "webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master"
}, },
"devDependencies": { "devDependencies": {
"aegir": "^10.0.0", "aegir": "^11.0.0",
"benchmark": "^2.1.3", "benchmark": "^2.1.3",
"chai": "^3.5.0", "chai": "^3.5.0",
"dirty-chai": "^1.2.2",
"pre-commit": "^1.2.2" "pre-commit": "^1.2.2"
}, },
"optionalDependencies": { "optionalDependencies": {
"node-webcrypto-ossl": "^1.0.17" "node-webcrypto-ossl": "^1.0.21"
}, },
"pre-commit": [ "pre-commit": [
"lint", "lint",
@@ -64,11 +65,6 @@
"node": ">=4.0.0", "node": ">=4.0.0",
"npm": ">=3.0.0" "npm": ">=3.0.0"
}, },
"browserify": {
"transform": [
"browserify-optional"
]
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/libp2p/js-libp2p-crypto.git" "url": "https://github.com/libp2p/js-libp2p-crypto.git"
@@ -81,9 +77,11 @@
"David Dias <daviddias.p@gmail.com>", "David Dias <daviddias.p@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>", "Dmitriy Ryajov <dryajov@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>", "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Jack Kleeman <jackkleeman@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>", "Richard Littauer <richard.littauer@gmail.com>",
"Tom Swindell <t.swindell@rubyx.co.uk>", "Tom Swindell <t.swindell@rubyx.co.uk>",
"Yusef Napora <yusef@napora.org>", "Yusef Napora <yusef@napora.org>",
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
"greenkeeperio-bot <support@greenkeeper.io>", "greenkeeperio-bot <support@greenkeeper.io>",
"nikuda <nikuda@gmail.com>" "nikuda <nikuda@gmail.com>"
] ]

View File

@@ -22,6 +22,22 @@ exports.generateKey = function (callback) {
done(null, keys) 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) {
done(err)
return
}
done(null, keys)
}
exports.hashAndSign = function (key, msg, callback) { exports.hashAndSign = function (key, msg, callback) {
setImmediate(() => { setImmediate(() => {
callback(null, Buffer.from(nacl.sign.detached(msg, key))) callback(null, Buffer.from(nacl.sign.detached(msg, key)))

View File

@@ -30,6 +30,19 @@ exports.generateKeyPair = (type, bits, cb) => {
key.generateKeyPair(bits, cb) key.generateKeyPair(bits, cb)
} }
// Generates a keypair of the given type and bitsize
// seed is a 32 byte uint8array
exports.generateKeyPairFromSeed = (type, seed, bits, cb) => {
let key = keys[type.toLowerCase()]
if (!key) {
return cb(new Error('invalid or unsupported key type'))
}
if (type.toLowerCase() !== 'ed25519') {
return cb(new Error('Seed key derivation is unimplemented for RSA or secp256k1'))
}
key.generateKeyPairFromSeed(seed, bits, cb)
}
// Converts a protobuf serialized public key into its // Converts a protobuf serialized public key into its
// representative object // representative object
exports.unmarshalPublicKey = (buf) => { exports.unmarshalPublicKey = (buf) => {

View File

@@ -117,6 +117,27 @@ function generateKeyPair (_bits, cb) {
}) })
} }
function generateKeyPairFromSeed (seed, _bits, cb) {
if (cb === undefined && typeof _bits === 'function') {
cb = _bits
}
crypto.generateKeyFromSeed(seed, (err, keys) => {
if (err) {
return cb(err)
}
let privkey
try {
privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey)
} catch (err) {
cb(err)
return
}
cb(null, privkey)
})
}
function ensure (cb) { function ensure (cb) {
if (typeof cb !== 'function') { if (typeof cb !== 'function') {
throw new Error('callback is required') throw new Error('callback is required')
@@ -138,5 +159,6 @@ module.exports = {
Ed25519PrivateKey, Ed25519PrivateKey,
unmarshalEd25519PrivateKey, unmarshalEd25519PrivateKey,
unmarshalEd25519PublicKey, unmarshalEd25519PublicKey,
generateKeyPair generateKeyPair,
generateKeyPairFromSeed
} }

View File

@@ -2,10 +2,6 @@
module.exports = { module.exports = {
rsa: require('./rsa'), rsa: require('./rsa'),
ed25519: require('./ed25519') ed25519: require('./ed25519'),
} secp256k1: require('libp2p-crypto-secp256k1')
try {
module.exports.secp256k1 = require('libp2p-crypto-secp256k1')
} catch (err) {
} }

View File

@@ -2,7 +2,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const series = require('async/series') const series = require('async/series')
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
@@ -25,7 +28,7 @@ describe('AES-CTR', () => {
iv.fill(1) iv.fill(1)
crypto.aes.create(key, iv, (err, cipher) => { crypto.aes.create(key, iv, (err, cipher) => {
expect(err).to.not.exist expect(err).to.not.exist()
series([ series([
encryptAndDecrypt(cipher), encryptAndDecrypt(cipher),
@@ -46,17 +49,17 @@ describe('AES-CTR', () => {
iv.fill(1) iv.fill(1)
crypto.aes.create(key, iv, (err, cipher) => { crypto.aes.create(key, iv, (err, cipher) => {
expect(err).to.not.exist expect(err).to.not.exist()
series(fixtures[byte].inputs.map((rawIn, i) => (cb) => { series(fixtures[byte].inputs.map((rawIn, i) => (cb) => {
const input = Buffer.from(rawIn) const input = Buffer.from(rawIn)
const output = Buffer.from(fixtures[byte].outputs[i]) const output = Buffer.from(fixtures[byte].outputs[i])
cipher.encrypt(input, (err, res) => { cipher.encrypt(input, (err, res) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(res).to.have.length(output.length) expect(res).to.have.length(output.length)
expect(res).to.be.eql(output) expect(res).to.be.eql(output)
cipher.decrypt(res, (err, res) => { cipher.decrypt(res, (err, res) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(res).to.be.eql(input) expect(res).to.be.eql(input)
cb() cb()
}) })
@@ -79,17 +82,17 @@ describe('AES-CTR', () => {
iv.fill(1) iv.fill(1)
crypto.aes.create(key, iv, (err, cipher) => { crypto.aes.create(key, iv, (err, cipher) => {
expect(err).to.not.exist expect(err).to.not.exist()
series(goFixtures[byte].inputs.map((rawIn, i) => (cb) => { series(goFixtures[byte].inputs.map((rawIn, i) => (cb) => {
const input = Buffer.from(rawIn) const input = Buffer.from(rawIn)
const output = Buffer.from(goFixtures[byte].outputs[i]) const output = Buffer.from(goFixtures[byte].outputs[i])
cipher.encrypt(input, (err, res) => { cipher.encrypt(input, (err, res) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(res).to.have.length(output.length) expect(res).to.have.length(output.length)
expect(res).to.be.eql(output) expect(res).to.be.eql(output)
cipher.decrypt(res, (err, res) => { cipher.decrypt(res, (err, res) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(res).to.be.eql(input) expect(res).to.be.eql(input)
cb() cb()
}) })
@@ -105,9 +108,9 @@ function encryptAndDecrypt (cipher) {
data.fill(Math.ceil(Math.random() * 100)) data.fill(Math.ceil(Math.random() * 100))
return (cb) => { return (cb) => {
cipher.encrypt(data, (err, res) => { cipher.encrypt(data, (err, res) => {
expect(err).to.not.exist expect(err).to.not.exist()
cipher.decrypt(res, (err, res) => { cipher.decrypt(res, (err, res) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(res).to.be.eql(data) expect(res).to.be.eql(data)
cb() cb()
}) })

View File

@@ -1,7 +1,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('../src') const crypto = require('../src')
@@ -35,6 +38,70 @@ describe('ed25519', () => {
}) })
}) })
it('generates a valid key from seed', (done) => {
var seed = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => {
if (err) return done(err)
expect(
seededkey
).to.be.an.instanceof(
ed25519.Ed25519PrivateKey
)
seededkey.hash((err, digest) => {
if (err) {
return done(err)
}
expect(digest).to.have.length(34)
done()
})
})
})
it('generates the same key from the same seed', (done) => {
var seed = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => {
if (err) return done(err)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => {
if (err) return done(err)
expect(
seededkey1.equals(seededkey2)
).to.be.eql(
true
)
expect(
seededkey1.public.equals(seededkey2.public)
).to.be.eql(
true
)
done()
})
})
})
it('generates different keys for different seeds', (done) => {
var seed1 = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => {
if (err) return done(err)
var seed2 = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => {
if (err) return done(err)
expect(
seededkey1.equals(seededkey2)
).to.be.eql(
false
)
expect(
seededkey1.public.equals(seededkey2.public)
).to.be.eql(
false
)
done()
})
})
})
it('signs', (done) => { it('signs', (done) => {
const text = crypto.randomBytes(512) const text = crypto.randomBytes(512)
@@ -167,7 +234,7 @@ describe('ed25519', () => {
let privateKey let privateKey
before((done) => { before((done) => {
crypto.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => { crypto.unmarshalPrivateKey(fixtures.verify.privateKey, (err, key) => {
expect(err).to.not.exist expect(err).to.not.exist()
privateKey = key privateKey = key
done() done()
}) })
@@ -178,7 +245,7 @@ describe('ed25519', () => {
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => { key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
if (err) throw err if (err) throw err
expect(err).to.not.exist expect(err).to.not.exist()
expect(ok).to.be.eql(true) expect(ok).to.be.eql(true)
done() done()
}) })
@@ -186,7 +253,7 @@ describe('ed25519', () => {
it('generates the same signature as go', (done) => { it('generates the same signature as go', (done) => {
privateKey.sign(fixtures.verify.data, (err, sig) => { privateKey.sign(fixtures.verify.data, (err, sig) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(sig).to.deep.equal(fixtures.verify.signature) expect(sig).to.deep.equal(fixtures.verify.signature)
done() done()
}) })

View File

@@ -2,7 +2,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const parallel = require('async/parallel') const parallel = require('async/parallel')
const fixtures = require('./fixtures/go-elliptic-key') const fixtures = require('./fixtures/go-elliptic-key')
@@ -27,12 +30,12 @@ describe('generateEphemeralKeyPair', () => {
(cb) => crypto.generateEphemeralKeyPair(curve, cb), (cb) => crypto.generateEphemeralKeyPair(curve, cb),
(cb) => crypto.generateEphemeralKeyPair(curve, cb) (cb) => crypto.generateEphemeralKeyPair(curve, cb)
], (err, keys) => { ], (err, keys) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(keys[0].key).to.have.length(lengths[curve]) expect(keys[0].key).to.have.length(lengths[curve])
expect(keys[1].key).to.have.length(lengths[curve]) expect(keys[1].key).to.have.length(lengths[curve])
keys[0].genSharedKey(keys[1].key, (err, shared) => { keys[0].genSharedKey(keys[1].key, (err, shared) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(shared).to.have.length(secretLengths[curve]) expect(shared).to.have.length(secretLengths[curve])
done() done()
}) })
@@ -48,7 +51,7 @@ describe('generateEphemeralKeyPair', () => {
(cb) => crypto.generateEphemeralKeyPair(curve, cb), (cb) => crypto.generateEphemeralKeyPair(curve, cb),
(cb) => crypto.generateEphemeralKeyPair(curve, cb) (cb) => crypto.generateEphemeralKeyPair(curve, cb)
], (err, res) => { ], (err, res) => {
expect(err).to.not.exist expect(err).to.not.exist()
const alice = res[0] const alice = res[0]
const bob = res[1] const bob = res[1]
bob.key = fixtures.bob.public bob.key = fixtures.bob.public
@@ -57,7 +60,7 @@ describe('generateEphemeralKeyPair', () => {
(cb) => alice.genSharedKey(bob.key, cb), (cb) => alice.genSharedKey(bob.key, cb),
(cb) => bob.genSharedKey(alice.key, fixtures.bob, cb) (cb) => bob.genSharedKey(alice.key, fixtures.bob, cb)
], (err, secrets) => { ], (err, secrets) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect( expect(
secrets[0] secrets[0]

View File

@@ -3,7 +3,10 @@
'use strict' 'use strict'
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const crypto = require('../src') const crypto = require('../src')
@@ -13,10 +16,10 @@ describe('HMAC', () => {
hashes.forEach((hash) => { hashes.forEach((hash) => {
it(`${hash} - sign and verify`, (done) => { it(`${hash} - sign and verify`, (done) => {
crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => { crypto.hmac.create(hash, Buffer.from('secret'), (err, hmac) => {
expect(err).to.not.exist expect(err).to.not.exist()
hmac.digest(Buffer.from('hello world'), (err, sig) => { hmac.digest(Buffer.from('hello world'), (err, sig) => {
expect(err).to.not.exist expect(err).to.not.exist()
expect(sig).to.have.length(hmac.length) expect(sig).to.have.length(hmac.length)
done() done()
}) })

View File

@@ -2,8 +2,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const crypto = require('../src') const crypto = require('../src')
const fixtures = require('./fixtures/go-key-rsa') const fixtures = require('./fixtures/go-key-rsa')

View File

@@ -2,8 +2,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const crypto = require('../src') const crypto = require('../src')
const fixtures = require('./fixtures/go-stretch-key') const fixtures = require('./fixtures/go-stretch-key')
@@ -39,8 +41,8 @@ describe('keyStretcher', () => {
return done(err) return done(err)
} }
expect(keys.k1).to.exist expect(keys.k1).to.exist()
expect(keys.k2).to.exist expect(keys.k2).to.exist()
done() done()
}) })
}) })

View File

@@ -1,7 +1,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const Buffer = require('safe-buffer').Buffer const Buffer = require('safe-buffer').Buffer
const crypto = require('../src') const crypto = require('../src')
@@ -169,8 +172,8 @@ describe('RSA', () => {
key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => { key.verify(fixtures.verify.data, fixtures.verify.signature, (err, ok) => {
if (err) throw err if (err) throw err
expect(err).to.not.exist expect(err).to.not.exist()
expect(ok).to.be.eql(true) expect(ok).to.equal(true)
done() done()
}) })
}) })

View File

@@ -1,7 +1,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const fixtures = require('./fixtures/secp256k1') const fixtures = require('./fixtures/secp256k1')
const crypto = require('../src') const crypto = require('../src')
@@ -48,7 +51,7 @@ describe('with libp2p-crypto-secp256k1 module present', () => {
it('generates a valid key', (done) => { it('generates a valid key', (done) => {
expect( expect(
key key
).to.exist ).to.exist()
done() done()
}) })
@@ -82,16 +85,16 @@ describe('with libp2p-crypto-secp256k1 module present', () => {
describe('without libp2p-crypto-secp256k1 module present', () => { describe('without libp2p-crypto-secp256k1 module present', () => {
it('fails to generate a secp256k1 key', (done) => { it('fails to generate a secp256k1 key', (done) => {
crypto.generateKeyPair('secp256k1', 256, (err, key) => { crypto.generateKeyPair('secp256k1', 256, (err, key) => {
expect(err).to.exist expect(err).to.exist()
expect(key).to.not.exist expect(key).to.not.exist()
done() done()
}) })
}) })
it('fails to unmarshal a secp256k1 private key', (done) => { it('fails to unmarshal a secp256k1 private key', (done) => {
crypto.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => { crypto.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
expect(err).to.exist expect(err).to.exist()
expect(key).to.not.exist expect(key).to.not.exist()
done() done()
}) })
}) })

View File

@@ -2,7 +2,11 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const expect = require('chai').expect const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const util = require('../src/crypto/util') const util = require('../src/crypto/util')
const BN = require('bn.js') const BN = require('bn.js')