Compare commits

...

34 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
David Dias
21f96a4eb7 chore: release version v0.8.3 2017-02-10 16:16:49 -08:00
David Dias
39b5124526 chore: update contributors 2017-02-10 16:16:49 -08:00
David Dias
93e50dde10 fix: browserify-optional needs to be dep 2017-02-10 16:16:11 -08:00
David Dias
e383da5d44 chore: release version v0.8.2 2017-02-10 10:28:44 -08:00
David Dias
9b687f380c chore: update contributors 2017-02-10 10:28:44 -08:00
Friedel Ziegelmayer
82abede64a fix: use browserify-optional (#72)
This works around the issue that browserify throws on missing modules.
2017-02-10 10:21:56 -08:00
David Dias
a56ab406e4 chore: release version v0.8.1 2017-02-09 08:32:12 -08:00
David Dias
d753941c6e chore: update contributors 2017-02-09 08:32:12 -08:00
David Dias
6022eb0838 chore: ^ to ~ 2017-02-09 08:29:55 -08:00
Dmitriy Ryajov
1c6d0912cd chore: removing --webworker from build steps 2017-02-09 12:14:26 +01:00
Friedel Ziegelmayer
e18804c31e chore: release version v0.8.0 2017-02-07 21:03:56 +01:00
Friedel Ziegelmayer
133a2c8833 chore: update contributors 2017-02-07 21:03:55 +01:00
Friedel Ziegelmayer
fad0865b2b chore: drop webworker flag 2017-02-07 20:46:58 +01:00
Friedel Ziegelmayer
4edef07182 Merge pull request #67 from libp2p/greenkeeper-aegir-10.0.0
Update aegir to version 10.0.0 🚀
2017-02-07 20:45:13 +01:00
Friedel Ziegelmayer
19c6ce7c06 Merge pull request #65 from dryajov/master
feat: change window to self for webworker support
2017-02-07 20:45:01 +01:00
Friedel Ziegelmayer
3fa76a874f Merge pull request #66 from libp2p/expose-proto
feat: expose protobuf
2017-02-07 20:44:36 +01:00
greenkeeperio-bot
6e0fd6f257 chore(package): update aegir to version 10.0.0
https://greenkeeper.io/
2017-02-07 18:45:52 +01:00
Friedel Ziegelmayer
c91d9b61c8 feat: expose protobuf
this allows other modules to reuse the protobuf definition
2017-02-07 17:51:43 +01:00
Yusef Napora
76eeb5aa18 feat: add support for secp256k1 keys through the libp2p-crypto-secp256k1 module 2017-02-04 10:23:38 +01:00
dryajov
843b5e33d6 WIP: use self instead of window for WebWorker compatibility 2017-01-31 02:09:46 -08:00
19 changed files with 384 additions and 64 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

@@ -107,9 +107,21 @@ Depending on the environment this is either an instance of [node-webcrypto-ossl]
### `keys` ### `keys`
#### Supported Key Types
The [`generateKeyPair`](#generatekeypairtype-bits-callback), [`marshalPublicKey`](#marshalpublickeykey-type-callback), and
[`marshalPrivateKey`](#marshalprivatekeykey-type) functions accept a string `type` argument.
Currently the `'RSA'` and `'ed25519'` types are supported, although ed25519 keys support only signing and
verification of messages. For encryption / decryption support, RSA keys should be used.
Installing the [libp2p-crypto-secp256k1](https://github.com/libp2p/js-libp2p-crypto-secp256k1) module adds support for the
`'secp256k1'` type, which supports ECDSA signatures using the secp256k1 elliptic curve popularized by Bitcoin. This module
is not installed by default, and should be explicitly depended on if your project requires secp256k1 support.
### `generateKeyPair(type, bits, callback)` ### `generateKeyPair(type, bits, callback)`
- `type: String`, only `'RSA'` is currently supported - `type: String`, see [Supported Key Types](#supported-key-types) above.
- `bits: Number` Minimum of 1024 - `bits: Number` Minimum of 1024
- `callback: Function` - `callback: Function`
@@ -160,8 +172,8 @@ Calls back with an object of the form
### `marshalPublicKey(key[, type], callback)` ### `marshalPublicKey(key[, type], callback)`
- `key: crypto.rsa.RsaPublicKey` - `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey`
- `type: String`, only `'RSA'` is currently supported - `type: String`, see [Supported Key Types](#supported-key-types) above.
Converts a public key object into a protobuf serialized public key. Converts a public key object into a protobuf serialized public key.
@@ -173,8 +185,8 @@ Converts a protobuf serialized public key into its representative object.
### `marshalPrivateKey(key[, type])` ### `marshalPrivateKey(key[, type])`
- `key: crypto.rsa.RsaPrivateKey` - `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
- `type: String`, only `'RSA'` is currently supported - `type: String`, see [Supported Key Types](#supported-key-types) above.
Converts a private key object into a protobuf serialized private key. Converts a private key object into a protobuf serialized private key.

View File

@@ -1,6 +1,6 @@
{ {
"name": "libp2p-crypto", "name": "libp2p-crypto",
"version": "0.7.7", "version": "0.8.7",
"description": "Crypto primitives for libp2p", "description": "Crypto primitives for libp2p",
"main": "src/index.js", "main": "src/index.js",
"browser": { "browser": {
@@ -17,10 +17,10 @@
"test": "npm run test:node && npm run test:no-webcrypto && npm run test:browser", "test": "npm run test:node && npm run test:no-webcrypto && npm run test:browser",
"test:node": "aegir-test --env node", "test:node": "aegir-test --env node",
"test:no-webcrypto": "NO_WEBCRYPTO=true aegir-test --env node", "test:no-webcrypto": "NO_WEBCRYPTO=true aegir-test --env node",
"test:browser": "aegir-test --env browser --webworker", "test:browser": "aegir-test --env browser",
"release": "aegir-release --webworker", "release": "aegir-release",
"release-minor": "aegir-release --type minor --webworker", "release-minor": "aegir-release --type minor",
"release-major": "aegir-release --type major --webworker", "release-major": "aegir-release --type major",
"coverage": "aegir-coverage", "coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish" "coverage-publish": "aegir-coverage publish"
}, },
@@ -34,50 +34,55 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"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",
"multihashing-async": "^0.4.0", "libp2p-crypto-secp256k1": "^0.1.4",
"nodeify": "^1.0.0", "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",
"webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master" "webcrypto-shim": "github:dignifiedquire/webcrypto-shim#master"
}, },
"devDependencies": { "devDependencies": {
"aegir": "^9.4.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.16" "node-webcrypto-ossl": "^1.0.21"
}, },
"pre-commit": [ "pre-commit": [
"lint", "lint",
"test" "test"
], ],
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=4.0.0",
"npm": ">=3.0.0"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/ipfs/js-libp2p-crypto.git" "url": "https://github.com/libp2p/js-libp2p-crypto.git"
}, },
"bugs": { "bugs": {
"url": "https://github.com/ipfs/js-libp2p-crypto/issues" "url": "https://github.com/libp2p/js-libp2p-crypto/issues"
}, },
"homepage": "https://github.com/ipfs/js-libp2p-crypto", "homepage": "https://github.com/libp2p/js-libp2p-crypto",
"contributors": [ "contributors": [
"David Dias <daviddias.p@gmail.com>", "David Dias <daviddias.p@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>", "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Greenkeeper <support@greenkeeper.io>", "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>",
"dryajov <dryajov@gmail.com>", "greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
"greenkeeperio-bot <support@greenkeeper.io>",
"nikuda <nikuda@gmail.com>" "nikuda <nikuda@gmail.com>"
] ]
} }

View File

@@ -3,6 +3,7 @@
module.exports = `enum KeyType { module.exports = `enum KeyType {
RSA = 0; RSA = 0;
Ed25519 = 1; Ed25519 = 1;
Secp256k1 = 2;
} }
message PublicKey { message PublicKey {

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

@@ -3,12 +3,12 @@
'use strict' 'use strict'
module.exports = function getWebCrypto () { module.exports = function getWebCrypto () {
if (typeof window !== 'undefined') { if (typeof self !== 'undefined') {
// This is only a shim for interfaces, not for functionality // This is only a shim for interfaces, not for functionality
require('webcrypto-shim')(window) require('webcrypto-shim')(self)
if (window.crypto) { if (self.crypto) {
return window.crypto return self.crypto
} }
} }

View File

@@ -5,12 +5,17 @@ const protobuf = require('protocol-buffers')
const pbm = protobuf(require('./crypto.proto')) const pbm = protobuf(require('./crypto.proto'))
const c = require('./crypto') const c = require('./crypto')
exports.protobuf = pbm
exports.hmac = c.hmac exports.hmac = c.hmac
exports.aes = c.aes exports.aes = c.aes
exports.webcrypto = c.webcrypto exports.webcrypto = c.webcrypto
const keys = exports.keys = require('./keys') const keys = exports.keys = require('./keys')
const KEY_TYPES = ['rsa', 'ed25519'] function isValidKeyType (keyType) {
const key = keys[keyType.toLowerCase()]
return key !== undefined
}
exports.keyStretcher = require('./key-stretcher') exports.keyStretcher = require('./key-stretcher')
exports.generateEphemeralKeyPair = require('./ephemeral-keys') exports.generateEphemeralKeyPair = require('./ephemeral-keys')
@@ -25,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) => {
@@ -35,6 +53,12 @@ exports.unmarshalPublicKey = (buf) => {
return keys.rsa.unmarshalRsaPublicKey(decoded.Data) return keys.rsa.unmarshalRsaPublicKey(decoded.Data)
case pbm.KeyType.Ed25519: case pbm.KeyType.Ed25519:
return keys.ed25519.unmarshalEd25519PublicKey(decoded.Data) return keys.ed25519.unmarshalEd25519PublicKey(decoded.Data)
case pbm.KeyType.Secp256k1:
if (keys.secp256k1) {
return keys.secp256k1.unmarshalSecp256k1PublicKey(decoded.Data)
} else {
throw new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')
}
default: default:
throw new Error('invalid or unsupported key type') throw new Error('invalid or unsupported key type')
} }
@@ -43,7 +67,7 @@ exports.unmarshalPublicKey = (buf) => {
// Converts a public key object into a protobuf serialized public key // Converts a public key object into a protobuf serialized public key
exports.marshalPublicKey = (key, type) => { exports.marshalPublicKey = (key, type) => {
type = (type || 'rsa').toLowerCase() type = (type || 'rsa').toLowerCase()
if (KEY_TYPES.indexOf(type) < 0) { if (!isValidKeyType(type)) {
throw new Error('invalid or unsupported key type') throw new Error('invalid or unsupported key type')
} }
@@ -60,6 +84,12 @@ exports.unmarshalPrivateKey = (buf, callback) => {
return keys.rsa.unmarshalRsaPrivateKey(decoded.Data, callback) return keys.rsa.unmarshalRsaPrivateKey(decoded.Data, callback)
case pbm.KeyType.Ed25519: case pbm.KeyType.Ed25519:
return keys.ed25519.unmarshalEd25519PrivateKey(decoded.Data, callback) return keys.ed25519.unmarshalEd25519PrivateKey(decoded.Data, callback)
case pbm.KeyType.Secp256k1:
if (keys.secp256k1) {
return keys.secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data, callback)
} else {
return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package'))
}
default: default:
callback(new Error('invalid or unsupported key type')) callback(new Error('invalid or unsupported key type'))
} }
@@ -68,7 +98,7 @@ exports.unmarshalPrivateKey = (buf, callback) => {
// Converts a private key object into a protobuf serialized private key // Converts a private key object into a protobuf serialized private key
exports.marshalPrivateKey = (key, type) => { exports.marshalPrivateKey = (key, type) => {
type = (type || 'rsa').toLowerCase() type = (type || 'rsa').toLowerCase()
if (KEY_TYPES.indexOf(type) < 0) { if (!isValidKeyType(type)) {
throw new Error('invalid or unsupported key type') throw new Error('invalid or unsupported key type')
} }

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,5 +2,6 @@
module.exports = { module.exports = {
rsa: require('./rsa'), rsa: require('./rsa'),
ed25519: require('./ed25519') ed25519: require('./ed25519'),
secp256k1: require('libp2p-crypto-secp256k1')
} }

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]

11
test/fixtures/secp256k1.js vendored Normal file
View File

@@ -0,0 +1,11 @@
'use strict'
const Buffer = require('safe-buffer').Buffer
module.exports = {
// protobuf marshaled key pair generated with libp2p-crypto-secp256k1
// and marshaled with libp2p-crypto.marshalPublicKey / marshalPrivateKey
pbmPrivateKey: Buffer.from('08021220e0600103010000000100000000000000be1dc82c2e000000e8d6030301000000', 'hex'),
pbmPublicKey: Buffer.from('0802122103a9a7272a726fa083abf31ba44037f8347fbc5e5d3113d62a7c6bc26752fd8ee1', 'hex')
}

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')
@@ -23,9 +25,17 @@ describe('libp2p-crypto', () => {
const key2 = crypto.unmarshalPublicKey(crypto.marshalPublicKey(key.public)) const key2 = crypto.unmarshalPublicKey(crypto.marshalPublicKey(key.public))
expect(key2.equals(key.public)).to.be.eql(true) expect(key2.equals(key.public)).to.be.eql(true)
expect(() => {
crypto.marshalPublicKey(key.public, 'invalid-key-type')
}).to.throw()
}) })
it('marshalPrivateKey and unmarshalPrivateKey', (done) => { it('marshalPrivateKey and unmarshalPrivateKey', (done) => {
expect(() => {
crypto.marshalPrivateKey(key, 'invalid-key-type')
}).to.throw()
crypto.unmarshalPrivateKey(crypto.marshalPrivateKey(key), (err, key2) => { crypto.unmarshalPrivateKey(crypto.marshalPrivateKey(key), (err, key2) => {
if (err) { if (err) {
return done(err) return done(err)
@@ -103,7 +113,7 @@ describe('libp2p-crypto', () => {
it('throws with no number passed', () => { it('throws with no number passed', () => {
expect(() => { expect(() => {
crypto.randomBytes() crypto.randomBytes()
}).to.throw }).to.throw()
}) })
it('generates different random things', () => { it('generates different random things', () => {

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()
}) })
}) })

107
test/secp256k1.spec.js Normal file
View File

@@ -0,0 +1,107 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const fixtures = require('./fixtures/secp256k1')
const crypto = require('../src')
const mockPublicKey = {
bytes: fixtures.pbmPublicKey
}
const mockPrivateKey = {
bytes: fixtures.pbmPrivateKey,
public: mockPublicKey
}
const mockSecp256k1Module = {
generateKeyPair (bits, callback) {
callback(null, mockPrivateKey)
},
unmarshalSecp256k1PrivateKey (buf, callback) {
callback(null, mockPrivateKey)
},
unmarshalSecp256k1PublicKey (buf) {
return mockPublicKey
}
}
describe('with libp2p-crypto-secp256k1 module present', () => {
let key
before((done) => {
crypto.keys['secp256k1'] = mockSecp256k1Module
crypto.generateKeyPair('secp256k1', 256, (err, _key) => {
if (err) return done(err)
key = _key
done()
})
})
after((done) => {
delete crypto.keys['secp256k1']
done()
})
it('generates a valid key', (done) => {
expect(
key
).to.exist()
done()
})
it('protobuf encoding', (done) => {
const keyMarshal = crypto.marshalPrivateKey(key)
crypto.unmarshalPrivateKey(keyMarshal, (err, key2) => {
if (err) return done(err)
const keyMarshal2 = crypto.marshalPrivateKey(key2)
expect(
keyMarshal
).to.be.eql(
keyMarshal2
)
const pk = key.public
const pkMarshal = crypto.marshalPublicKey(pk)
const pk2 = crypto.unmarshalPublicKey(pkMarshal)
const pkMarshal2 = crypto.marshalPublicKey(pk2)
expect(
pkMarshal
).to.be.eql(
pkMarshal2
)
done()
})
})
})
describe('without libp2p-crypto-secp256k1 module present', () => {
it('fails to generate a secp256k1 key', (done) => {
crypto.generateKeyPair('secp256k1', 256, (err, key) => {
expect(err).to.exist()
expect(key).to.not.exist()
done()
})
})
it('fails to unmarshal a secp256k1 private key', (done) => {
crypto.unmarshalPrivateKey(fixtures.pbmPrivateKey, (err, key) => {
expect(err).to.exist()
expect(key).to.not.exist()
done()
})
})
it('fails to unmarshal a secp256k1 public key', () => {
expect(() => {
crypto.unmarshalPublicKey(fixtures.pbmPublicKey)
}).to.throw(Error)
})
})

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')