import { mockRandomForEach } from "jest-mock-random"; import sinon from "sinon"; import DigitalBitsBase from "@digitalbits-blockchain/xdb-digitalbits-base"; import { KeyType } from "./constants/keys"; import { KeyManager } from "./KeyManager"; import { IdentityEncrypter } from "./plugins/IdentityEncrypter"; import { MemoryKeyStore } from "./plugins/MemoryKeyStore"; import { ScryptEncrypter } from "./plugins/ScryptEncrypter"; // tslint:disable-next-line describe("KeyManager", function() { let clock: sinon.SinonFakeTimers; beforeEach(() => { clock = sinon.useFakeTimers(666); mockRandomForEach(0.5); }); afterEach(() => { clock.restore(); }); test("Save an ID of one's own", async () => { const testStore = new MemoryKeyStore(); const testKeyManager = new KeyManager({ keyStore: testStore }); const id = "this is a very good id, and I like it"; testKeyManager.registerEncrypter(IdentityEncrypter); const password = "test"; const metadata = await testKeyManager.storeKey({ key: { id, type: KeyType.plaintextKey, publicKey: "AVACYN", privateKey: "ARCHANGEL" }, password, encrypterName: "IdentityEncrypter" }); expect(metadata).toEqual({ id }); expect(await testKeyManager.loadAllKeyIds()).toEqual([id]); expect(await testKeyManager.loadKey(id, password)).toEqual({ id, type: KeyType.plaintextKey, publicKey: "AVACYN", privateKey: "ARCHANGEL" }); }); test("Save and remove an ID of one's own", async () => { const testStore = new MemoryKeyStore(); const testKeyManager = new KeyManager({ keyStore: testStore }); const id = "this is a very good id, and I like it"; testKeyManager.registerEncrypter(IdentityEncrypter); const password = "test"; const metadata = await testKeyManager.storeKey({ key: { id, type: KeyType.plaintextKey, publicKey: "AVACYN", privateKey: "ARCHANGEL" }, password, encrypterName: "IdentityEncrypter" }); expect(metadata).toEqual({ id }); expect(await testKeyManager.loadAllKeyIds()).toEqual([id]); expect(await testKeyManager.loadKey(id, password)).toEqual({ id, type: KeyType.plaintextKey, publicKey: "AVACYN", privateKey: "ARCHANGEL" }); await testKeyManager.removeKey(metadata.id); try { await testKeyManager.loadKey(id, password); expect( "The function should have thrown but didn't, the test failed!" ).toBe(null); } catch (e: any) { expect(e.toString()).toContain("Key not found"); } }); test("Save keys", async () => { const testStore = new MemoryKeyStore(); const testKeyManager = new KeyManager({ keyStore: testStore }); testKeyManager.registerEncrypter(IdentityEncrypter); const password = "test"; const metadata = await testKeyManager.storeKey({ key: { type: KeyType.plaintextKey, publicKey: "AVACYN", privateKey: "ARCHANGEL" }, password, encrypterName: "IdentityEncrypter" }); expect(metadata).toEqual({ id: "0.5" }); expect(await testKeyManager.loadKey("0.5", password)).toEqual({ id: "0.5", privateKey: "ARCHANGEL", publicKey: "AVACYN", type: "plaintextKey" }); }); test("Save / remove keys", async () => { const testStore = new MemoryKeyStore(); const testKeyManager = new KeyManager({ keyStore: testStore }); testKeyManager.registerEncrypter(IdentityEncrypter); const password = "test"; const metadata = await testKeyManager.storeKey({ key: { type: KeyType.plaintextKey, publicKey: "AVACYN", privateKey: "ARCHANGEL" }, password, encrypterName: "IdentityEncrypter" }); expect(metadata).toEqual({ id: "0.5" }); expect(await testKeyManager.loadKey("0.5", password)).toEqual({ id: "0.5", privateKey: "ARCHANGEL", publicKey: "AVACYN", type: "plaintextKey" }); await testKeyManager.removeKey(metadata.id); try { await testKeyManager.loadKey("0.5", password); expect( "The function should have thrown but didn't, the test failed!" ).toBe(null); } catch (e: any) { expect(e.toString()).toContain("Key not found"); } }); test("Sign transactions", async () => { // set up the manager const testStore = new MemoryKeyStore(); const testKeyManager = new KeyManager({ keyStore: testStore }); const network = DigitalBitsBase.Networks.TESTNET; testKeyManager.registerEncrypter(IdentityEncrypter); const keypair = DigitalBitsBase.Keypair.master(network); // save this key const keyMetadata = await testKeyManager.storeKey({ key: { type: KeyType.plaintextKey, publicKey: keypair.publicKey(), privateKey: keypair.secret(), network }, password: "test", encrypterName: "IdentityEncrypter" }); const source = new DigitalBitsBase.Account( "GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB", "0" ); const transaction = new DigitalBitsBase.TransactionBuilder(source, { fee: "100", networkPassphrase: network }) .addOperation(DigitalBitsBase.Operation.inflation({})) .setTimeout(DigitalBitsBase.TimeoutInfinite) .build(); const signedTransaction = await testKeyManager.signTransaction({ transaction, id: keyMetadata.id, password: "test" }); expect( keypair.verify( transaction.hash(), signedTransaction.signatures[0].signature() ) ).toEqual(true); }); }); describe("KeyManager Scrypt", () => { let clock: sinon.SinonFakeTimers; beforeEach(() => { clock = sinon.useFakeTimers(666); mockRandomForEach(0.5); }); afterEach(() => { clock.restore(); }); test("Save / remove keys", async () => { const testStore = new MemoryKeyStore(); const testKeyManager = new KeyManager({ keyStore: testStore }); testKeyManager.registerEncrypter(ScryptEncrypter); const password = "test"; const metadata = await testKeyManager.storeKey({ key: { type: KeyType.plaintextKey, publicKey: "AVACYN", privateKey: "ARCHANGEL" }, password, encrypterName: ScryptEncrypter.name }); expect(metadata).toEqual({ id: "0.5" }); expect(await testKeyManager.loadKey("0.5", password)).toEqual({ id: "0.5", privateKey: "ARCHANGEL", publicKey: "AVACYN", type: "plaintextKey" }); try { await testKeyManager.loadKey( "0.5", "i don't know the password but I'm hoping the decrypter works anyway" ); expect( "The function should have thrown but didn't, the test failed!" ).toBe(null); } catch (e: any) { expect(e.toString()).toContain("Couldn’t decrypt key"); } await testKeyManager.removeKey(metadata.id); try { await testKeyManager.loadKey("0.5", password); expect( "The function should have thrown but didn't, the test failed!" ).toBe(null); } catch (e: any) { expect(e.toString()).toContain("Key not found"); } }); }); describe("KeyManager Scrypt, multiple keys with different passwords", () => { let clock: sinon.SinonFakeTimers; beforeEach(() => { clock = sinon.useFakeTimers(666); }); afterEach(() => { clock.restore(); }); test("Save multiple keys", async () => { const testStore = new MemoryKeyStore(); const testKeyManager = new KeyManager({ keyStore: testStore }); testKeyManager.registerEncrypter(ScryptEncrypter); const password1 = "test1"; const metadata1 = await testKeyManager.storeKey({ key: { id: "key1", type: KeyType.plaintextKey, publicKey: "AVACYN1", privateKey: "ARCHANGEL1" }, password: password1, encrypterName: ScryptEncrypter.name }); expect(metadata1).toEqual({ id: "key1" }); expect(await testKeyManager.loadKey("key1", password1)).toEqual({ id: "key1", privateKey: "ARCHANGEL1", publicKey: "AVACYN1", type: "plaintextKey" }); const password2 = "test1"; const metadata2 = await testKeyManager.storeKey({ key: { id: "key2", type: KeyType.plaintextKey, publicKey: "AVACYN2", privateKey: "ARCHANGEL2" }, password: password2, encrypterName: ScryptEncrypter.name }); expect(metadata2).toEqual({ id: "key2" }); expect(await testKeyManager.loadKey("key2", password2)).toEqual({ id: "key2", privateKey: "ARCHANGEL2", publicKey: "AVACYN2", type: "plaintextKey" }); expect(await testKeyManager.loadKey("key1", password1)).toEqual({ id: "key1", privateKey: "ARCHANGEL1", publicKey: "AVACYN1", type: "plaintextKey" }); }); });