Skip to content
This repository has been archived by the owner on Oct 7, 2024. It is now read-only.

Commit

Permalink
Export key for encrypted key login
Browse files Browse the repository at this point in the history
  • Loading branch information
darkwing committed Sep 30, 2022
1 parent 462cd15 commit 189f85b
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 14 deletions.
47 changes: 34 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const KEYRINGS_TYPE_MAP = {
HD_KEYRING: 'HD Key Tree',
SIMPLE_KEYRING: 'Simple Key Pair',
};

/**
* Strip the hex prefix from an address, if present
* @param {string} address - The address that might be hex prefixed.
Expand Down Expand Up @@ -145,7 +146,7 @@ class KeyringController extends EventEmitter {
*/
async setLocked() {
// set locked
this.password = null;
delete this.encryptionKey;
this.memStore.updateState({ isUnlocked: false });
// remove keyrings
this.keyrings = [];
Expand All @@ -168,7 +169,15 @@ class KeyringController extends EventEmitter {
* @returns {Promise<Object>} A Promise that resolves to the state.
*/
async submitPassword(password) {
await this.verifyPassword(password);
this.keyrings = await this.unlockKeyrings(password);
await this.persistAllKeyrings(password);
this.setUnlocked();
this.fullUpdate();
}

async submitEncryptionKey(encryptionKey) {
this.keyrings = await this.unlockKeyrings(undefined, encryptionKey);
this.setUnlocked();
this.fullUpdate();
}
Expand Down Expand Up @@ -202,7 +211,7 @@ class KeyringController extends EventEmitter {
* @param {Object} opts - The constructor options for the keyring.
* @returns {Promise<Keyring>} The new keyring.
*/
async addNewKeyring(type, opts) {
async addNewKeyring(type, opts, password) {
const Keyring = this.getKeyringClassForType(type);
const keyring = new Keyring(opts);
if ((!opts || !opts.mnemonic) && type === KEYRINGS_TYPE_MAP.HD_KEYRING) {
Expand All @@ -214,7 +223,7 @@ class KeyringController extends EventEmitter {
await this.checkForDuplicate(type, accounts);

this.keyrings.push(keyring);
await this.persistAllKeyrings();
await this.persistAllKeyrings(password);

await this._updateMemStoreKeyrings();
this.fullUpdate();
Expand Down Expand Up @@ -491,10 +500,13 @@ class KeyringController extends EventEmitter {
* @returns {Promise<void>} - A promise that resolves if the operation was successful.
*/
async createFirstKeyTree(password) {
this.password = password;
this.clearKeyrings();

const keyring = await this.addNewKeyring(KEYRINGS_TYPE_MAP.HD_KEYRING);
const keyring = await this.addNewKeyring(
KEYRINGS_TYPE_MAP.HD_KEYRING,
{},
password,
);
const [firstAccount] = await keyring.getAccounts();
if (!firstAccount) {
throw new Error('KeyringController - No account found on keychain.');
Expand All @@ -516,12 +528,11 @@ class KeyringController extends EventEmitter {
* @param {string} password - The keyring controller password.
* @returns {Promise<boolean>} Resolves to true once keyrings are persisted.
*/
async persistAllKeyrings(password = this.password) {
async persistAllKeyrings(password) {
if (typeof password !== 'string') {
throw new Error('KeyringController - password is not a string');
}

this.password = password;
const serializedKeyrings = await Promise.all(
this.keyrings.map(async (keyring) => {
const [type, data] = await Promise.all([
Expand All @@ -531,11 +542,13 @@ class KeyringController extends EventEmitter {
return { type, data };
}),
);
const encryptedString = await this.encryptor.encrypt(
this.password,

const vault = await this.encryptor.encrypt(
password,
serializedKeyrings,
);
this.store.updateState({ vault: encryptedString });

this.store.updateState({ vault });
return true;
}

Expand All @@ -548,15 +561,23 @@ class KeyringController extends EventEmitter {
* @param {string} password - The keyring controller password.
* @returns {Promise<Array<Keyring>>} The keyrings.
*/
async unlockKeyrings(password) {
async unlockKeyrings(password, encryptionKey) {
const encryptedVault = this.store.getState().vault;
if (!encryptedVault) {
throw new Error('Cannot unlock without a previous vault.');
}

await this.clearKeyrings();
const vault = await this.encryptor.decrypt(password, encryptedVault);
this.password = password;

let vault;
if (password) {
const result = await this.encryptor.decrypt(password, encryptedVault);
vault = result.vault;
this.encryptionKey = result.extractedKeyString;
} else {
vault = (await this.encryptor.decryptWithEncryptedKeyString(encryptionKey)).vault;
}

await Promise.all(vault.map(this._restoreKeyring.bind(this)));
await this._updateMemStoreKeyrings();
return this.keyrings;
Expand Down
3 changes: 2 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('KeyringController', function () {
});

await keyringController.createNewVaultAndKeychain(password);
await keyringController.submitPassword(password);
});

afterEach(function () {
Expand Down Expand Up @@ -63,7 +64,7 @@ describe('KeyringController', function () {
describe('submitPassword', function () {
it('should not create new keyrings when called in series', async function () {
await keyringController.createNewVaultAndKeychain(password);
await keyringController.persistAllKeyrings();
await keyringController.persistAllKeyrings(password);
expect(keyringController.keyrings).toHaveLength(1);

await keyringController.submitPassword(`${password}a`);
Expand Down
4 changes: 4 additions & 0 deletions test/lib/mock-encryptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ module.exports = {
return Promise.resolve(mockHex);
}),

decryptWithEncryptedKeyString(_keyStr) {
return this.decrypt();
},

decrypt(_password, _text) {
return Promise.resolve(cacheVal || {});
},
Expand Down

0 comments on commit 189f85b

Please sign in to comment.