2017-02-04 04:23:38 -05:00
|
|
|
/* eslint-env mocha */
|
|
|
|
'use strict'
|
|
|
|
|
2017-03-21 15:05:22 +00:00
|
|
|
const chai = require('chai')
|
|
|
|
const dirtyChai = require('dirty-chai')
|
|
|
|
const expect = chai.expect
|
|
|
|
chai.use(dirtyChai)
|
2017-07-22 10:57:27 -07:00
|
|
|
const crypto = require('../../src')
|
2020-04-06 12:46:39 -05:00
|
|
|
const secp256k1 = crypto.keys.supportedKeys.secp256k1
|
|
|
|
const keysPBM = crypto.keys.keysPBM
|
|
|
|
const randomBytes = crypto.randomBytes
|
|
|
|
const secp256k1Crypto = require('../../src/keys/secp256k1')(randomBytes)
|
2020-08-07 15:23:02 +01:00
|
|
|
const uint8ArrayFromString = require('uint8arrays/from-string')
|
2021-03-15 18:07:13 +01:00
|
|
|
const fixtures = require('../fixtures/go-key-secp256k1')
|
2020-04-06 12:46:39 -05:00
|
|
|
|
|
|
|
describe('secp256k1 keys', () => {
|
|
|
|
let key
|
|
|
|
|
|
|
|
before(async () => {
|
|
|
|
key = await secp256k1.generateKeyPair()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('generates a valid key', async () => {
|
|
|
|
expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
|
|
|
|
expect(key.public).to.be.an.instanceof(secp256k1.Secp256k1PublicKey)
|
|
|
|
|
|
|
|
const digest = await key.hash()
|
|
|
|
expect(digest).to.have.length(34)
|
|
|
|
|
|
|
|
const publicDigest = await key.public.hash()
|
|
|
|
expect(publicDigest).to.have.length(34)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('optionally accepts a `bits` argument when generating a key', async () => {
|
2020-07-18 12:44:23 +02:00
|
|
|
const _key = await secp256k1.generateKeyPair()
|
2020-04-06 12:46:39 -05:00
|
|
|
expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('signs', async () => {
|
|
|
|
const text = randomBytes(512)
|
|
|
|
const sig = await key.sign(text)
|
|
|
|
const res = await key.public.verify(text, sig)
|
|
|
|
expect(res).to.equal(true)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('encoding', async () => {
|
|
|
|
const keyMarshal = key.marshal()
|
|
|
|
const key2 = await secp256k1.unmarshalSecp256k1PrivateKey(keyMarshal)
|
|
|
|
const keyMarshal2 = key2.marshal()
|
|
|
|
|
|
|
|
expect(keyMarshal).to.eql(keyMarshal2)
|
|
|
|
|
|
|
|
const pk = key.public
|
|
|
|
const pkMarshal = pk.marshal()
|
|
|
|
const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal)
|
|
|
|
const pkMarshal2 = pk2.marshal()
|
|
|
|
|
|
|
|
expect(pkMarshal).to.eql(pkMarshal2)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('key id', async () => {
|
2021-03-15 18:07:13 +01:00
|
|
|
const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey)
|
|
|
|
const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data)
|
2020-04-06 12:46:39 -05:00
|
|
|
const id = await key.id()
|
2021-03-15 18:07:13 +01:00
|
|
|
expect(id).to.eql('QmPCyMBGEyifPtx5aa6k6wkY9N1eBf9vHK1eKfNc35q9uq')
|
2020-04-06 12:46:39 -05:00
|
|
|
})
|
|
|
|
|
2020-08-05 17:14:12 +02:00
|
|
|
it('should export a password encrypted libp2p-key', async () => {
|
|
|
|
const key = await crypto.keys.generateKeyPair('secp256k1')
|
|
|
|
const encryptedKey = await key.export('my secret')
|
|
|
|
// Import the key
|
|
|
|
const importedKey = await crypto.keys.import(encryptedKey, 'my secret')
|
|
|
|
expect(key.equals(importedKey)).to.equal(true)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fail to import libp2p-key with wrong password', async () => {
|
|
|
|
const key = await crypto.keys.generateKeyPair('secp256k1')
|
|
|
|
const encryptedKey = await key.export('my secret', 'libp2p-key')
|
|
|
|
try {
|
|
|
|
await crypto.keys.import(encryptedKey, 'not my secret')
|
|
|
|
} catch (err) {
|
|
|
|
expect(err).to.exist()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
expect.fail('should have thrown')
|
|
|
|
})
|
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
describe('key equals', () => {
|
|
|
|
it('equals itself', () => {
|
|
|
|
expect(key.equals(key)).to.eql(true)
|
|
|
|
|
|
|
|
expect(key.public.equals(key.public)).to.eql(true)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('not equals other key', async () => {
|
2020-07-18 12:44:23 +02:00
|
|
|
const key2 = await secp256k1.generateKeyPair()
|
2020-04-06 12:46:39 -05:00
|
|
|
expect(key.equals(key2)).to.eql(false)
|
|
|
|
expect(key2.equals(key)).to.eql(false)
|
|
|
|
expect(key.public.equals(key2.public)).to.eql(false)
|
|
|
|
expect(key2.public.equals(key.public)).to.eql(false)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
it('sign and verify', async () => {
|
2020-08-07 15:23:02 +01:00
|
|
|
const data = uint8ArrayFromString('hello world')
|
2020-04-06 12:46:39 -05:00
|
|
|
const sig = await key.sign(data)
|
|
|
|
const valid = await key.public.verify(data, sig)
|
|
|
|
expect(valid).to.eql(true)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('fails to verify for different data', async () => {
|
2020-08-07 15:23:02 +01:00
|
|
|
const data = uint8ArrayFromString('hello world')
|
2020-04-06 12:46:39 -05:00
|
|
|
const sig = await key.sign(data)
|
2020-08-07 15:23:02 +01:00
|
|
|
const valid = await key.public.verify(uint8ArrayFromString('hello'), sig)
|
2020-04-06 12:46:39 -05:00
|
|
|
expect(valid).to.eql(false)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('key generation error', () => {
|
|
|
|
let generateKey
|
|
|
|
let secp256k1
|
|
|
|
|
|
|
|
before(() => {
|
|
|
|
generateKey = secp256k1Crypto.generateKey
|
|
|
|
secp256k1 = require('../../src/keys/secp256k1-class')(keysPBM, randomBytes, secp256k1Crypto)
|
|
|
|
secp256k1Crypto.generateKey = () => { throw new Error('Error generating key') }
|
|
|
|
})
|
|
|
|
|
|
|
|
after(() => {
|
|
|
|
secp256k1Crypto.generateKey = generateKey
|
|
|
|
})
|
|
|
|
|
|
|
|
it('returns an error if key generation fails', async () => {
|
|
|
|
try {
|
|
|
|
await secp256k1.generateKeyPair()
|
|
|
|
} catch (err) {
|
|
|
|
return expect(err.message).to.equal('Error generating key')
|
|
|
|
}
|
|
|
|
throw new Error('Expected error to be thrown')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('handles generation of invalid key', () => {
|
|
|
|
let generateKey
|
|
|
|
let secp256k1
|
2017-02-04 04:23:38 -05:00
|
|
|
|
2019-07-11 00:32:51 +02:00
|
|
|
before(() => {
|
2020-04-06 12:46:39 -05:00
|
|
|
generateKey = secp256k1Crypto.generateKey
|
|
|
|
secp256k1 = require('../../src/keys/secp256k1-class')(keysPBM, randomBytes, secp256k1Crypto)
|
2020-08-07 15:23:02 +01:00
|
|
|
secp256k1Crypto.generateKey = () => uint8ArrayFromString('not a real key')
|
2019-07-11 00:32:51 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
after(() => {
|
2020-04-06 12:46:39 -05:00
|
|
|
secp256k1Crypto.generateKey = generateKey
|
2019-07-11 00:32:51 +02:00
|
|
|
})
|
2017-07-22 10:57:27 -07:00
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('returns an error if key generator returns an invalid key', async () => {
|
2019-07-10 17:15:26 +01:00
|
|
|
try {
|
2020-04-06 12:46:39 -05:00
|
|
|
await secp256k1.generateKeyPair()
|
|
|
|
} catch (err) {
|
|
|
|
return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32')
|
|
|
|
}
|
|
|
|
throw new Error('Expected error to be thrown')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('crypto functions', () => {
|
|
|
|
let privKey
|
|
|
|
let pubKey
|
|
|
|
|
|
|
|
before(async () => {
|
|
|
|
privKey = await secp256k1Crypto.generateKey()
|
|
|
|
pubKey = secp256k1Crypto.computePublicKey(privKey)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('generates valid keys', () => {
|
|
|
|
expect(() => {
|
|
|
|
secp256k1Crypto.validatePrivateKey(privKey)
|
|
|
|
secp256k1Crypto.validatePublicKey(pubKey)
|
|
|
|
}).to.not.throw()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('does not validate an invalid key', () => {
|
2020-08-07 15:23:02 +01:00
|
|
|
expect(() => secp256k1Crypto.validatePublicKey(uint8ArrayFromString('42'))).to.throw()
|
|
|
|
expect(() => secp256k1Crypto.validatePrivateKey(uint8ArrayFromString('42'))).to.throw()
|
2020-04-06 12:46:39 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
it('validates a correct signature', async () => {
|
2020-08-07 15:23:02 +01:00
|
|
|
const sig = await secp256k1Crypto.hashAndSign(privKey, uint8ArrayFromString('hello'))
|
|
|
|
const valid = await secp256k1Crypto.hashAndVerify(pubKey, sig, uint8ArrayFromString('hello'))
|
2020-04-06 12:46:39 -05:00
|
|
|
expect(valid).to.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-08-07 15:23:02 +01:00
|
|
|
it('errors if given a null Uint8Array to sign', async () => {
|
2020-04-06 12:46:39 -05:00
|
|
|
try {
|
|
|
|
await secp256k1Crypto.hashAndSign(privKey, null)
|
2019-07-10 17:15:26 +01:00
|
|
|
} catch (err) {
|
|
|
|
return // expected
|
|
|
|
}
|
|
|
|
throw new Error('Expected error to be thrown')
|
2017-07-22 10:57:27 -07:00
|
|
|
})
|
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('errors when signing with an invalid key', async () => {
|
|
|
|
try {
|
2020-08-07 15:23:02 +01:00
|
|
|
await secp256k1Crypto.hashAndSign(uint8ArrayFromString('42'), uint8ArrayFromString('Hello'))
|
2020-04-06 12:46:39 -05:00
|
|
|
} catch (err) {
|
|
|
|
return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32')
|
|
|
|
}
|
|
|
|
throw new Error('Expected error to be thrown')
|
|
|
|
})
|
|
|
|
|
2020-08-07 15:23:02 +01:00
|
|
|
it('errors if given a null Uint8Array to validate', async () => {
|
|
|
|
const sig = await secp256k1Crypto.hashAndSign(privKey, uint8ArrayFromString('hello'))
|
2020-04-06 12:46:39 -05:00
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
try {
|
2020-04-06 12:46:39 -05:00
|
|
|
await secp256k1Crypto.hashAndVerify(privKey, sig, null)
|
2019-07-10 17:15:26 +01:00
|
|
|
} catch (err) {
|
|
|
|
return // expected
|
|
|
|
}
|
|
|
|
throw new Error('Expected error to be thrown')
|
2017-07-22 10:57:27 -07:00
|
|
|
})
|
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('errors when validating a message with an invalid signature', async () => {
|
|
|
|
try {
|
2020-08-07 15:23:02 +01:00
|
|
|
await secp256k1Crypto.hashAndVerify(pubKey, uint8ArrayFromString('invalid-sig'), uint8ArrayFromString('hello'))
|
2020-04-06 12:46:39 -05:00
|
|
|
} catch (err) {
|
|
|
|
return expect(err.message).to.equal('Signature could not be parsed')
|
|
|
|
}
|
|
|
|
throw new Error('Expected error to be thrown')
|
2017-07-22 10:57:27 -07:00
|
|
|
})
|
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('errors when signing with an invalid key', async () => {
|
|
|
|
try {
|
2020-08-07 15:23:02 +01:00
|
|
|
await secp256k1Crypto.hashAndSign(uint8ArrayFromString('42'), uint8ArrayFromString('Hello'))
|
2020-04-06 12:46:39 -05:00
|
|
|
} catch (err) {
|
|
|
|
return expect(err.message).to.equal('Expected private key to be an Uint8Array with length 32')
|
|
|
|
}
|
|
|
|
throw new Error('Expected error to be thrown')
|
2017-02-04 04:23:38 -05:00
|
|
|
})
|
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('throws when compressing an invalid public key', () => {
|
2020-08-07 15:23:02 +01:00
|
|
|
expect(() => secp256k1Crypto.compressPublicKey(uint8ArrayFromString('42'))).to.throw()
|
2020-04-06 12:46:39 -05:00
|
|
|
})
|
2019-07-11 00:32:51 +02:00
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('throws when decompressing an invalid public key', () => {
|
2020-08-07 15:23:02 +01:00
|
|
|
expect(() => secp256k1Crypto.decompressPublicKey(uint8ArrayFromString('42'))).to.throw()
|
2020-04-06 12:46:39 -05:00
|
|
|
})
|
2017-02-04 04:23:38 -05:00
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('compresses/decompresses a valid public key', () => {
|
|
|
|
const decompressed = secp256k1Crypto.decompressPublicKey(pubKey)
|
|
|
|
expect(decompressed).to.exist()
|
|
|
|
expect(decompressed.length).to.be.eql(65)
|
|
|
|
const recompressed = secp256k1Crypto.compressPublicKey(decompressed)
|
|
|
|
expect(recompressed).to.eql(pubKey)
|
|
|
|
})
|
|
|
|
})
|
2017-02-04 04:23:38 -05:00
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
describe('go interop', () => {
|
|
|
|
it('loads a private key marshaled by go-libp2p-crypto', async () => {
|
|
|
|
// we need to first extract the key data from the protobuf, which is
|
|
|
|
// normally handled by js-libp2p-crypto
|
|
|
|
const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey)
|
|
|
|
expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1)
|
|
|
|
|
|
|
|
const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data)
|
|
|
|
expect(key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
|
|
|
|
expect(key.bytes).to.eql(fixtures.privateKey)
|
2017-02-04 04:23:38 -05:00
|
|
|
})
|
2019-07-11 00:32:51 +02:00
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('loads a public key marshaled by go-libp2p-crypto', () => {
|
|
|
|
const decoded = keysPBM.PublicKey.decode(fixtures.publicKey)
|
|
|
|
expect(decoded.Type).to.be.eql(keysPBM.KeyType.Secp256k1)
|
|
|
|
|
|
|
|
const key = secp256k1.unmarshalSecp256k1PublicKey(decoded.Data)
|
|
|
|
expect(key).to.be.an.instanceof(secp256k1.Secp256k1PublicKey)
|
|
|
|
expect(key.bytes).to.eql(fixtures.publicKey)
|
2019-07-11 00:32:51 +02:00
|
|
|
})
|
|
|
|
|
2020-04-06 12:46:39 -05:00
|
|
|
it('generates the same signature as go-libp2p-crypto', async () => {
|
|
|
|
const decoded = keysPBM.PrivateKey.decode(fixtures.privateKey)
|
|
|
|
expect(decoded.Type).to.eql(keysPBM.KeyType.Secp256k1)
|
|
|
|
|
|
|
|
const key = await secp256k1.unmarshalSecp256k1PrivateKey(decoded.Data)
|
|
|
|
const sig = await key.sign(fixtures.message)
|
|
|
|
expect(sig).to.eql(fixtures.signature)
|
2019-07-11 00:32:51 +02:00
|
|
|
})
|
2017-02-04 04:23:38 -05:00
|
|
|
})
|