mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-03-15 03:31:03 +00:00
Merge pull request #173 from ChainSafe/cayman/secp-migration
Integrate libp2p-crypto-secp256k1
This commit is contained in:
commit
ccda21fe91
@ -171,9 +171,7 @@ main()
|
||||
|
||||
The [`generateKeyPair`](#generatekeypairtype-bits), [`marshalPublicKey`](#marshalpublickeykey-type), 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.
|
||||
Currently the `'RSA'`, `'ed25519'`, and `secp256k1` types are supported, although ed25519 and secp256k1 keys support only signing and verification of messages. For encryption / decryption support, RSA keys should be used.
|
||||
|
||||
### `crypto.keys.generateKeyPair(type, bits)`
|
||||
|
||||
@ -232,7 +230,7 @@ Resolves to an object of the form:
|
||||
|
||||
### `crypto.keys.marshalPublicKey(key, [type])`
|
||||
|
||||
- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | require('libp2p-crypto-secp256k1').Secp256k1PublicKey`
|
||||
- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | keys.secp256k1.Secp256k1PublicKey`
|
||||
- `type: String`, see [Supported Key Types](#supported-key-types) above. Defaults to 'rsa'.
|
||||
|
||||
Returns `Buffer`
|
||||
@ -249,7 +247,7 @@ Converts a protobuf serialized public key into its representative object.
|
||||
|
||||
### `crypto.keys.marshalPrivateKey(key, [type])`
|
||||
|
||||
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | require('libp2p-crypto-secp256k1').Secp256k1PrivateKey`
|
||||
- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | keys.secp256k1.Secp256k1PrivateKey`
|
||||
- `type: String`, see [Supported Key Types](#supported-key-types) above.
|
||||
|
||||
Returns `Buffer`
|
||||
|
@ -33,20 +33,22 @@
|
||||
"IPFS",
|
||||
"libp2p",
|
||||
"crypto",
|
||||
"rsa"
|
||||
"rsa",
|
||||
"secp256k1"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"err-code": "^2.0.0",
|
||||
"is-typedarray": "^1.0.0",
|
||||
"iso-random-stream": "^1.1.0",
|
||||
"keypair": "^1.0.1",
|
||||
"libp2p-crypto-secp256k1": "^0.4.2",
|
||||
"multibase": "^0.7.0",
|
||||
"multihashing-async": "^0.8.1",
|
||||
"node-forge": "~0.9.1",
|
||||
"pem-jwk": "^2.0.0",
|
||||
"protons": "^1.0.1",
|
||||
"secp256k1": "^4.0.0",
|
||||
"ursa-optional": "~0.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -13,7 +13,7 @@ exports = module.exports
|
||||
const supportedKeys = {
|
||||
rsa: require('./rsa-class'),
|
||||
ed25519: require('./ed25519-class'),
|
||||
secp256k1: require('libp2p-crypto-secp256k1')(keysPBM, require('../random-bytes'))
|
||||
secp256k1: require('./secp256k1-class')(keysPBM, require('../random-bytes'))
|
||||
}
|
||||
|
||||
exports.supportedKeys = supportedKeys
|
||||
|
110
src/keys/secp256k1-class.js
Normal file
110
src/keys/secp256k1-class.js
Normal file
@ -0,0 +1,110 @@
|
||||
'use strict'
|
||||
|
||||
const multibase = require('multibase')
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
|
||||
module.exports = (keysProtobuf, randomBytes, crypto) => {
|
||||
crypto = crypto || require('./secp256k1')(randomBytes)
|
||||
|
||||
class Secp256k1PublicKey {
|
||||
constructor (key) {
|
||||
crypto.validatePublicKey(key)
|
||||
this._key = key
|
||||
}
|
||||
|
||||
verify (data, sig) {
|
||||
return crypto.hashAndVerify(this._key, sig, data)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return crypto.compressPublicKey(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return keysProtobuf.PublicKey.encode({
|
||||
Type: keysProtobuf.KeyType.Secp256k1,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash () {
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
}
|
||||
|
||||
class Secp256k1PrivateKey {
|
||||
constructor (key, publicKey) {
|
||||
this._key = key
|
||||
this._publicKey = publicKey || crypto.computePublicKey(key)
|
||||
crypto.validatePrivateKey(this._key)
|
||||
crypto.validatePublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
sign (message) {
|
||||
return crypto.hashAndSign(this._key, message)
|
||||
}
|
||||
|
||||
get public () {
|
||||
return new Secp256k1PublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return this._key
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return keysProtobuf.PrivateKey.encode({
|
||||
Type: keysProtobuf.KeyType.Secp256k1,
|
||||
Data: this.marshal()
|
||||
})
|
||||
}
|
||||
|
||||
equals (key) {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash () {
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the key.
|
||||
*
|
||||
* The key id is the base58 encoding of the SHA-256 multihash of its public key.
|
||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||
* of the PKCS SubjectPublicKeyInfo.
|
||||
*
|
||||
* @param {function(Error, id)} callback
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async id () {
|
||||
const hash = await this.public.hash()
|
||||
return multibase.encode('base58btc', hash).toString().slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalSecp256k1PrivateKey (bytes) {
|
||||
return new Secp256k1PrivateKey(bytes)
|
||||
}
|
||||
|
||||
function unmarshalSecp256k1PublicKey (bytes) {
|
||||
return new Secp256k1PublicKey(bytes)
|
||||
}
|
||||
|
||||
async function generateKeyPair () {
|
||||
const privateKeyBytes = await crypto.generateKey()
|
||||
return new Secp256k1PrivateKey(privateKeyBytes)
|
||||
}
|
||||
|
||||
return {
|
||||
Secp256k1PublicKey,
|
||||
Secp256k1PrivateKey,
|
||||
unmarshalSecp256k1PrivateKey,
|
||||
unmarshalSecp256k1PublicKey,
|
||||
generateKeyPair
|
||||
}
|
||||
}
|
86
src/keys/secp256k1.js
Normal file
86
src/keys/secp256k1.js
Normal file
@ -0,0 +1,86 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
var isTypedArray = require('is-typedarray').strict
|
||||
const secp256k1 = require('secp256k1')
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
const HASH_ALGORITHM = 'sha2-256'
|
||||
|
||||
function typedArrayTobuffer (arr) {
|
||||
if (isTypedArray(arr)) {
|
||||
// To avoid a copy, use the typed array's underlying ArrayBuffer to back new Buffer
|
||||
var buf = Buffer.from(arr.buffer)
|
||||
if (arr.byteLength !== arr.buffer.byteLength) {
|
||||
// Respect the "view", i.e. byteOffset and byteLength, without doing a copy
|
||||
buf = buf.slice(arr.byteOffset, arr.byteOffset + arr.byteLength)
|
||||
}
|
||||
return buf
|
||||
} else {
|
||||
// Pass through all other types to `Buffer.from`
|
||||
return Buffer.from(arr)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (randomBytes) => {
|
||||
const privateKeyLength = 32
|
||||
|
||||
function generateKey () {
|
||||
let privateKey
|
||||
do {
|
||||
privateKey = randomBytes(32)
|
||||
} while (!secp256k1.privateKeyVerify(privateKey))
|
||||
return privateKey
|
||||
}
|
||||
|
||||
async function hashAndSign (key, msg) {
|
||||
const digest = await sha.digest(msg, HASH_ALGORITHM)
|
||||
const sig = secp256k1.ecdsaSign(digest, key)
|
||||
return typedArrayTobuffer(secp256k1.signatureExport(sig.signature))
|
||||
}
|
||||
|
||||
async function hashAndVerify (key, sig, msg) {
|
||||
const digest = await sha.digest(msg, HASH_ALGORITHM)
|
||||
sig = typedArrayTobuffer(secp256k1.signatureImport(sig))
|
||||
return secp256k1.ecdsaVerify(sig, digest, key)
|
||||
}
|
||||
|
||||
function compressPublicKey (key) {
|
||||
if (!secp256k1.publicKeyVerify(key)) {
|
||||
throw new Error('Invalid public key')
|
||||
}
|
||||
return typedArrayTobuffer(secp256k1.publicKeyConvert(key, true))
|
||||
}
|
||||
|
||||
function decompressPublicKey (key) {
|
||||
return typedArrayTobuffer(secp256k1.publicKeyConvert(key, false))
|
||||
}
|
||||
|
||||
function validatePrivateKey (key) {
|
||||
if (!secp256k1.privateKeyVerify(key)) {
|
||||
throw new Error('Invalid private key')
|
||||
}
|
||||
}
|
||||
|
||||
function validatePublicKey (key) {
|
||||
if (!secp256k1.publicKeyVerify(key)) {
|
||||
throw new Error('Invalid public key')
|
||||
}
|
||||
}
|
||||
|
||||
function computePublicKey (privateKey) {
|
||||
validatePrivateKey(privateKey)
|
||||
return typedArrayTobuffer(secp256k1.publicKeyCreate(privateKey))
|
||||
}
|
||||
|
||||
return {
|
||||
generateKey,
|
||||
privateKeyLength,
|
||||
hashAndSign,
|
||||
hashAndVerify,
|
||||
compressPublicKey,
|
||||
decompressPublicKey,
|
||||
validatePrivateKey,
|
||||
validatePublicKey,
|
||||
computePublicKey
|
||||
}
|
||||
}
|
31
test/fixtures/go-key-secp256k1.js
vendored
Normal file
31
test/fixtures/go-key-secp256k1.js
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
|
||||
// The keypair and signature below were generated in a gore repl session (https://github.com/motemen/gore)
|
||||
// using the secp256k1 fork of go-libp2p-crypto by github user @vyzo
|
||||
//
|
||||
// gore> :import github.com/vyzo/go-libp2p-crypto
|
||||
// gore> :import crypto/rand
|
||||
// gore> :import io/ioutil
|
||||
// gore> priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.Secp256k1, 256, rand.Reader)
|
||||
// gore> privBytes, err := priv.Bytes()
|
||||
// gore> pubBytes, err := pub.Bytes()
|
||||
// gore> msg := []byte("hello! and welcome to some awesome crypto primitives")
|
||||
// gore> sig, err := priv.Sign(msg)
|
||||
// gore> ioutil.WriteFile("/tmp/secp-go-priv.bin", privBytes, 0644)
|
||||
// gore> ioutil.WriteFile("/tmp/secp-go-pub.bin", pubBytes, 0644)
|
||||
// gore> ioutil.WriteFile("/tmp/secp-go-sig.bin", sig, 0644)
|
||||
//
|
||||
// The generated files were then read in a node repl with e.g.:
|
||||
// > fs.readFileSync('/tmp/secp-go-pub.bin').toString('hex')
|
||||
// '08021221029c0ce5d53646ed47112560297a3e59b78b8cbd4bae37c7a0c236eeb91d0fbeaf'
|
||||
//
|
||||
// and the results copy/pasted in here
|
||||
|
||||
module.exports = {
|
||||
privateKey: Buffer.from('08021220358f15db8c2014d570e8e3a622454e2273975a3cca443ec0c45375b13d381d18', 'hex'),
|
||||
publicKey: Buffer.from('08021221029c0ce5d53646ed47112560297a3e59b78b8cbd4bae37c7a0c236eeb91d0fbeaf', 'hex'),
|
||||
message: Buffer.from('hello! and welcome to some awesome crypto primitives', 'utf-8'),
|
||||
signature: Buffer.from('304402200e4c629e9f5d99439115e60989cd40087f6978c36078b0b50cf3d30af5c38d4102204110342c8e7f0809897c1c7a66e49e1c6b7cb0a6ed6993640ec2fe742c1899a9', 'hex')
|
||||
}
|
@ -1,81 +1,267 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const sinon = require('sinon')
|
||||
|
||||
const fixtures = require('../fixtures/secp256k1')
|
||||
const crypto = require('../../src')
|
||||
const secp256k1 = crypto.keys.supportedKeys.secp256k1
|
||||
const keysPBM = crypto.keys.keysPBM
|
||||
const randomBytes = crypto.randomBytes
|
||||
const secp256k1Crypto = require('../../src/keys/secp256k1')(randomBytes)
|
||||
|
||||
describe('without libp2p-crypto-secp256k1 module present', () => {
|
||||
before(() => {
|
||||
const empty = null
|
||||
sinon.replace(crypto.keys.supportedKeys, 'secp256k1', empty)
|
||||
describe('secp256k1 keys', () => {
|
||||
let key
|
||||
|
||||
before(async () => {
|
||||
key = await secp256k1.generateKeyPair()
|
||||
})
|
||||
|
||||
after(() => {
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('fails to generate a secp256k1 key', async () => {
|
||||
try {
|
||||
await crypto.keys.generateKeyPair('secp256k1', 256)
|
||||
} catch (err) {
|
||||
return // expected
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 private key', async () => {
|
||||
try {
|
||||
await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey)
|
||||
} catch (err) {
|
||||
return // expected
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('fails to unmarshal a secp256k1 public key', () => {
|
||||
expect(() => {
|
||||
crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with libp2p-crypto-secp256k1 module present', () => {
|
||||
it('generates a valid key', async () => {
|
||||
const key = await crypto.keys.generateKeyPair('secp256k1', 256)
|
||||
expect(key).to.exist()
|
||||
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('protobuf encoding', async () => {
|
||||
const key = await crypto.keys.generateKeyPair('secp256k1', 256)
|
||||
expect(key).to.exist()
|
||||
it('optionally accepts a `bits` argument when generating a key', async () => {
|
||||
const _key = await secp256k1.generateKeyPair(256)
|
||||
expect(_key).to.be.an.instanceof(secp256k1.Secp256k1PrivateKey)
|
||||
})
|
||||
|
||||
const keyMarshal = crypto.keys.marshalPrivateKey(key)
|
||||
const key2 = await crypto.keys.unmarshalPrivateKey(keyMarshal)
|
||||
const keyMarshal2 = crypto.keys.marshalPrivateKey(key2)
|
||||
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 = crypto.keys.marshalPublicKey(pk)
|
||||
const pk2 = crypto.keys.unmarshalPublicKey(pkMarshal)
|
||||
const pkMarshal2 = crypto.keys.marshalPublicKey(pk2)
|
||||
const pkMarshal = pk.marshal()
|
||||
const pk2 = secp256k1.unmarshalSecp256k1PublicKey(pkMarshal)
|
||||
const pkMarshal2 = pk2.marshal()
|
||||
|
||||
expect(pkMarshal).to.eql(pkMarshal2)
|
||||
})
|
||||
|
||||
it('unmarshals a secp256k1 private key', async () => {
|
||||
const key = await crypto.keys.unmarshalPrivateKey(fixtures.pbmPrivateKey)
|
||||
expect(key).to.exist()
|
||||
it('key id', async () => {
|
||||
const id = await key.id()
|
||||
expect(id).to.exist()
|
||||
expect(id).to.be.a('string')
|
||||
})
|
||||
|
||||
it('unmarshals a secp256k1 public key', () => {
|
||||
const key = crypto.keys.unmarshalPublicKey(fixtures.pbmPublicKey)
|
||||
expect(key).to.exist()
|
||||
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 () => {
|
||||
const key2 = await secp256k1.generateKeyPair(256)
|
||||
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 () => {
|
||||
const data = Buffer.from('hello world')
|
||||
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 () => {
|
||||
const data = Buffer.from('hello world')
|
||||
const sig = await key.sign(data)
|
||||
const valid = await key.public.verify(Buffer.from('hello'), sig)
|
||||
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
|
||||
|
||||
before(() => {
|
||||
generateKey = secp256k1Crypto.generateKey
|
||||
secp256k1 = require('../../src/keys/secp256k1-class')(keysPBM, randomBytes, secp256k1Crypto)
|
||||
secp256k1Crypto.generateKey = () => Buffer.from('not a real key')
|
||||
})
|
||||
|
||||
after(() => {
|
||||
secp256k1Crypto.generateKey = generateKey
|
||||
})
|
||||
|
||||
it('returns an error if key generator returns an invalid key', async () => {
|
||||
try {
|
||||
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', () => {
|
||||
expect(() => secp256k1Crypto.validatePublicKey(Buffer.from('42'))).to.throw()
|
||||
expect(() => secp256k1Crypto.validatePrivateKey(Buffer.from('42'))).to.throw()
|
||||
})
|
||||
|
||||
it('validates a correct signature', async () => {
|
||||
const sig = await secp256k1Crypto.hashAndSign(privKey, Buffer.from('hello'))
|
||||
const valid = await secp256k1Crypto.hashAndVerify(pubKey, sig, Buffer.from('hello'))
|
||||
expect(valid).to.equal(true)
|
||||
})
|
||||
|
||||
it('errors if given a null buffer to sign', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndSign(privKey, null)
|
||||
} catch (err) {
|
||||
return // expected
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('errors when signing with an invalid key', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'))
|
||||
} 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')
|
||||
})
|
||||
|
||||
it('errors if given a null buffer to validate', async () => {
|
||||
const sig = await secp256k1Crypto.hashAndSign(privKey, Buffer.from('hello'))
|
||||
|
||||
try {
|
||||
await secp256k1Crypto.hashAndVerify(privKey, sig, null)
|
||||
} catch (err) {
|
||||
return // expected
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('errors when validating a message with an invalid signature', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndVerify(pubKey, Buffer.from('invalid-sig'), Buffer.from('hello'))
|
||||
} catch (err) {
|
||||
return expect(err.message).to.equal('Signature could not be parsed')
|
||||
}
|
||||
throw new Error('Expected error to be thrown')
|
||||
})
|
||||
|
||||
it('errors when signing with an invalid key', async () => {
|
||||
try {
|
||||
await secp256k1Crypto.hashAndSign(Buffer.from('42'), Buffer.from('Hello'))
|
||||
} 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')
|
||||
})
|
||||
|
||||
it('throws when compressing an invalid public key', () => {
|
||||
expect(() => secp256k1Crypto.compressPublicKey(Buffer.from('42'))).to.throw()
|
||||
})
|
||||
|
||||
it('throws when decompressing an invalid public key', () => {
|
||||
expect(() => secp256k1Crypto.decompressPublicKey(Buffer.from('42'))).to.throw()
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
describe('go interop', () => {
|
||||
const fixtures = require('../fixtures/go-key-secp256k1')
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user