orbitdb/test/key-store.test.js
2023-03-27 11:48:30 +13:00

357 lines
10 KiB
JavaScript

import { strictEqual, deepStrictEqual, deepEqual } from 'assert'
import * as crypto from '@libp2p/crypto'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import rmrf from 'rimraf'
import { copy } from 'fs-extra'
import KeyStore, { signMessage, verifyMessage } from '../src/key-store.js'
import LevelStorage from '../src/storage/level.js'
import testKeysPath from './fixtures/test-keys-path.js'
const defaultPath = './keystore'
const keysPath = './testkeys'
describe('KeyStore', () => {
let keystore
describe('Creating and retrieving keys', () => {
let id
beforeEach(async () => {
keystore = await KeyStore()
id = 'key1'
await keystore.createKey(id)
})
afterEach(async () => {
if (keystore) {
await keystore.close()
await rmrf(defaultPath)
}
})
it('creates a key', async () => {
const hasKey = await keystore.hasKey(id)
strictEqual(hasKey, true)
})
it('throws an error when creating a key without an id', async () => {
let err
try {
await keystore.createKey()
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: id needed to create a key')
})
it('throws an error when creating a key with a null id', async () => {
let err
try {
await keystore.createKey(null)
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: id needed to create a key')
})
it('returns true if key exists', async () => {
const id = 'key1'
await keystore.createKey(id)
const hasKey = await keystore.hasKey(id)
strictEqual(hasKey, true)
})
it('returns false if key does not exist', async () => {
const id = 'key1234567890'
const hasKey = await keystore.hasKey(id)
strictEqual(hasKey, false)
})
it('throws an error when checking if key exists when no id is specified', async () => {
let err
try {
await keystore.hasKey()
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: id needed to check a key')
})
it('gets a key', async () => {
const id = 'key1'
const keys = await keystore.createKey(id)
const result = await keystore.getKey(id)
deepEqual(result, keys)
})
it('throws an error when getting a key without an id', async () => {
const id = 'key1'
let err
await keystore.createKey(id)
try {
await keystore.getKey()
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: id needed to get a key')
})
it('throws an error when getting a key with a null id', async () => {
const id = 'key1'
let err
await keystore.createKey(id)
try {
await keystore.getKey(null)
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: id needed to get a key')
})
it('gets a non-existent key', async () => {
const expected = undefined
const id = 'key111111111'
const actual = await keystore.getKey(id)
strictEqual(actual, expected)
})
it('doesn\'t create a key when keystore is closed', async () => {
let err
await keystore.close()
try {
await keystore.createKey(id)
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: Database is not open')
})
})
describe('Options', () => {
const unmarshal = crypto.keys.supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey
const privateKey = '198594a8de39fd97017d11996d619b3746211605a9d290964badf58bc79bdb33'
const publicKey = '0260baeaffa1de1e4135e5b395e0380563a622b9599d1b8e012a0f7603f516bdaa'
let privateKeyBuffer, publicKeyBuffer, unmarshalledPrivateKey
before(async () => {
privateKeyBuffer = uint8ArrayFromString(privateKey, 'base16')
publicKeyBuffer = uint8ArrayFromString(publicKey, 'base16')
unmarshalledPrivateKey = await unmarshal(privateKeyBuffer)
})
describe('Using default options', () => {
beforeEach(async () => {
const storage = await LevelStorage({ path: defaultPath })
await storage.put('private_key1', privateKeyBuffer)
await storage.put('public_key1', publicKeyBuffer)
await storage.close()
keystore = await KeyStore()
})
afterEach(async () => {
if (keystore) {
await keystore.close()
await rmrf(defaultPath)
}
})
it('uses default storage and default path to retrieve a key', async () => {
deepEqual(await keystore.getKey('key1'), unmarshalledPrivateKey)
})
})
describe('Setting options.storage', () => {
const path = './custom-level-key-store'
beforeEach(async () => {
const storage = await LevelStorage({ path })
await storage.put('private_key2', privateKeyBuffer)
await storage.put('public_key2', publicKeyBuffer)
keystore = await KeyStore({ storage })
})
afterEach(async () => {
if (keystore) {
await keystore.close()
await rmrf(path)
}
})
it('uses the given storage to retrieve a key', async () => {
deepEqual(await keystore.getKey('key2'), unmarshalledPrivateKey)
})
})
describe('Setting options.path', () => {
beforeEach(async () => {
await copy(testKeysPath, keysPath)
const storage = await LevelStorage({ path: keysPath })
await storage.put('private_key3', privateKeyBuffer)
await storage.put('public_key3', publicKeyBuffer)
await storage.close()
keystore = await KeyStore({ path: keysPath })
})
afterEach(async () => {
if (keystore) {
await keystore.close()
}
await rmrf(keysPath)
})
it('uses default storage using given path to retrieve a key', async () => {
deepEqual(await keystore.getKey('key3'), unmarshalledPrivateKey)
})
})
})
describe('Using keys for signing and verifying', () => {
beforeEach(async () => {
await copy(testKeysPath, keysPath)
keystore = await KeyStore({ path: keysPath })
// For creating test keys fixtures (level) database
// const identities = await Identities({ keystore })
// const a = await identities.createIdentity({ id: 'userA' })
// const b = await identities.createIdentity({ id: 'userB' })
// const c = await identities.createIdentity({ id: 'userC' })
// const d = await identities.createIdentity({ id: 'userD' })
// const x = await identities.createIdentity({ id: 'userX' })
})
afterEach(async () => {
if (keystore) {
await keystore.close()
}
await rmrf(keysPath)
})
describe('Signing', () => {
it('signs data', async () => {
const expected = '3045022100df961fa46bb8a3cb92594a24205e6008a84daa563ac3530f583bb9f9cef5af3b02207b84c5d63387d0a710e42e05785fbccdaf2534c8ed16adb8afd57c3eba930529'
const key = await keystore.getKey('userA')
const actual = await signMessage(key, 'data data data')
strictEqual(actual, expected)
})
it('throws an error if no key is passed', async () => {
let err
try {
await signMessage(null, 'data data data')
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: No signing key given')
})
it('throws an error if no data is passed', async () => {
const key = 'key_1'
let err
try {
await signMessage(key)
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: Given input data was undefined')
})
})
describe('Getting the public key', async () => {
let key
beforeEach(async () => {
key = await keystore.getKey('userA')
})
it('gets the public key', async () => {
const expected = '02e7247a4c155b63d182a23c70cb6fe8ba2e44bc9e9d62dc45d4c4167ccde95944'
const publicKey = await keystore.getPublic(key)
strictEqual(publicKey, expected)
})
it('gets the public key buffer', async () => {
const expected = '02e7247a4c155b63d182a23c70cb6fe8ba2e44bc9e9d62dc45d4c4167ccde95944'
const publicKey = await keystore.getPublic(key, { format: 'buffer' })
deepStrictEqual(uint8ArrayToString(publicKey, 'base16'), expected)
})
it('throws an error if no keys are passed', async () => {
try {
await keystore.getPublic()
} catch (e) {
strictEqual(true, true)
}
})
it('throws an error if a bad format is passed', async () => {
try {
await keystore.getPublic(key, { format: 'foo' })
} catch (e) {
strictEqual(true, true)
}
})
})
describe('Verifying', async function () {
let key, publicKey
beforeEach(async () => {
key = await keystore.getKey('userA')
publicKey = await keystore.getPublic(key)
})
it('verifies content', async () => {
const signature = await signMessage(key, 'data data data')
const expectedSignature = '3045022100df961fa46bb8a3cb92594a24205e6008a84daa563ac3530f583bb9f9cef5af3b02207b84c5d63387d0a710e42e05785fbccdaf2534c8ed16adb8afd57c3eba930529'
strictEqual(expectedSignature, signature)
const verified = await verifyMessage(expectedSignature, publicKey, 'data data data')
strictEqual(verified, true)
})
it('verifies content with cache', async () => {
const data = 'data'.repeat(1024 * 1024)
const signature = await signMessage(key, data)
const startTime = new Date().getTime()
await verifyMessage(signature, publicKey, data)
const first = new Date().getTime()
await verifyMessage(signature, publicKey, data)
const after = new Date().getTime()
console.log('First pass:', first - startTime, 'ms', 'Cached:', after - first, 'ms')
strictEqual(first - startTime > after - first, true)
})
it('does not verify content with bad signature', async () => {
const signature = 'xxxxxx'
const verified = await verifyMessage(signature, publicKey, 'data data data')
strictEqual(verified, false)
})
})
})
})