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

Commit

Permalink
Add --add-legacy flag to keys create (#9032)
Browse files Browse the repository at this point in the history
🌱 Add --add-legacy flag to keys create
  • Loading branch information
shuse2 authored Sep 24, 2023
1 parent aa95c24 commit 3bf3b35
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 29 deletions.
126 changes: 97 additions & 29 deletions commander/src/bootstrapping/commands/keys/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/

import { codec } from '@liskhq/lisk-codec';
import { bls, address as addressUtil, ed, encrypt } from '@liskhq/lisk-cryptography';
import { bls, address as addressUtil, ed, encrypt, legacy } from '@liskhq/lisk-cryptography';
import { Command, Flags as flagParser } from '@oclif/core';
import * as fs from 'fs-extra';
import * as path from 'path';
Expand Down Expand Up @@ -60,6 +60,9 @@ export class CreateCommand extends Command {
description: 'Chain id',
default: 0,
}),
'add-legacy': flagParser.boolean({
description: 'Add legacy key derivation path to the result',
}),
};

async run(): Promise<void> {
Expand All @@ -72,6 +75,7 @@ export class CreateCommand extends Command {
count,
offset,
chainid,
'add-legacy': addLegacy,
},
} = await this.parse(CreateCommand);

Expand All @@ -86,7 +90,37 @@ export class CreateCommand extends Command {
}

const keys = [];
for (let i = 0; i < count; i += 1) {
let i = 0;
if (addLegacy) {
const legacyKeyPath = 'legacy';
const { privateKey: accountPrivateKey, publicKey: accountPublicKey } =
legacy.getPrivateAndPublicKeyFromPassphrase(passphrase);
const address = addressUtil.getAddressFromPublicKey(accountPublicKey);
const generatorPrivateKey = accountPrivateKey;
const generatorPublicKey = ed.getPublicKeyFromPrivateKey(generatorPrivateKey);
const blsKeyPath = `m/12381/134/${chainid}/99999`;
const blsPrivateKey = await bls.getPrivateKeyFromPhraseAndPath(passphrase, blsKeyPath);
const blsPublicKey = bls.getPublicKeyFromPrivateKey(blsPrivateKey);
const result = await this._createEncryptedObject(
{
address,
keyPath: legacyKeyPath,
accountPrivateKey,
accountPublicKey,
generatorKeyPath: legacyKeyPath,
generatorPrivateKey,
generatorPublicKey,
blsKeyPath,
blsPrivateKey,
blsPublicKey,
password,
},
noEncrypt,
);
keys.push(result);
i += 1;
}
for (; i < count; i += 1) {
const accountKeyPath = `m/44'/134'/${offset + i}'`;
const generatorKeyPath = `m/25519'/134'/${chainid}'/${offset + i}'`;
const blsKeyPath = `m/12381/134/${chainid}/${offset + i}`;
Expand All @@ -102,37 +136,23 @@ export class CreateCommand extends Command {
const blsPrivateKey = await bls.getPrivateKeyFromPhraseAndPath(passphrase, blsKeyPath);
const blsPublicKey = bls.getPublicKeyFromPrivateKey(blsPrivateKey);

let encryptedMessageObject = {};
if (!noEncrypt) {
const plainGeneratorKeyData = {
generatorKey: generatorPublicKey,
const result = await this._createEncryptedObject(
{
address,
keyPath: accountKeyPath,
accountPrivateKey,
accountPublicKey,
generatorKeyPath,
generatorPrivateKey,
blsKey: blsPublicKey,
generatorPublicKey,
blsKeyPath,
blsPrivateKey,
};
const encodedGeneratorKeys = codec.encode(plainGeneratorKeysSchema, plainGeneratorKeyData);
encryptedMessageObject = await encrypt.encryptMessageWithPassword(
encodedGeneratorKeys,
blsPublicKey,
password,
);
}

keys.push({
address: addressUtil.getLisk32AddressFromAddress(address),
keyPath: accountKeyPath,
publicKey: accountPublicKey.toString('hex'),
privateKey: accountPrivateKey.toString('hex'),
plain: {
generatorKeyPath,
generatorKey: generatorPublicKey.toString('hex'),
generatorPrivateKey: generatorPrivateKey.toString('hex'),
blsKeyPath,
blsKey: blsPublicKey.toString('hex'),
blsProofOfPossession: bls.popProve(blsPrivateKey).toString('hex'),
blsPrivateKey: blsPrivateKey.toString('hex'),
},
encrypted: encryptedMessageObject,
});
noEncrypt,
);
keys.push(result);
}

if (output) {
Expand All @@ -141,4 +161,52 @@ export class CreateCommand extends Command {
this.log(JSON.stringify({ keys }, undefined, ' '));
}
}
private async _createEncryptedObject(
input: {
address: Buffer;
keyPath: string;
accountPublicKey: Buffer;
accountPrivateKey: Buffer;
generatorKeyPath: string;
generatorPublicKey: Buffer;
generatorPrivateKey: Buffer;
blsKeyPath: string;
blsPublicKey: Buffer;
blsPrivateKey: Buffer;
password: string;
},
noEncrypt: boolean,
) {
let encryptedMessageObject = {};
if (!noEncrypt) {
const plainGeneratorKeyData = {
generatorKey: input.generatorPublicKey,
generatorPrivateKey: input.generatorPrivateKey,
blsKey: input.blsPublicKey,
blsPrivateKey: input.blsPrivateKey,
};
const encodedGeneratorKeys = codec.encode(plainGeneratorKeysSchema, plainGeneratorKeyData);
encryptedMessageObject = await encrypt.encryptMessageWithPassword(
encodedGeneratorKeys,
input.password,
);
}

return {
address: addressUtil.getLisk32AddressFromAddress(input.address),
keyPath: input.keyPath,
publicKey: input.accountPublicKey.toString('hex'),
privateKey: input.accountPrivateKey.toString('hex'),
plain: {
generatorKeyPath: input.generatorKeyPath,
generatorKey: input.generatorPublicKey.toString('hex'),
generatorPrivateKey: input.generatorPrivateKey.toString('hex'),
blsKeyPath: input.blsKeyPath,
blsKey: input.blsPublicKey.toString('hex'),
blsProofOfPossession: bls.popProve(input.blsPrivateKey).toString('hex'),
blsPrivateKey: input.blsPrivateKey.toString('hex'),
},
encrypted: encryptedMessageObject,
};
}
}
66 changes: 66 additions & 0 deletions commander/test/bootstrapping/commands/keys/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ describe('keys:create command', () => {
jest.spyOn(process.stdout, 'write').mockImplementation(val => stdout.push(val as string) > -1);
jest.spyOn(process.stderr, 'write').mockImplementation(val => stderr.push(val as string) > -1);
jest.spyOn(cryptography.ed, 'getPrivateKeyFromPhraseAndPath');
jest.spyOn(cryptography.legacy, 'getPrivateAndPublicKeyFromPassphrase');
jest.spyOn(cryptography.ed, 'getPublicKeyFromPrivateKey');
jest.spyOn(cryptography.address, 'getAddressFromPublicKey');
jest.spyOn(cryptography.bls, 'getPrivateKeyFromPhraseAndPath');
Expand Down Expand Up @@ -176,6 +177,71 @@ describe('keys:create command', () => {
});
});

describe('keys:create --add-legacy --passphrase', () => {
it('should create valid keys', async () => {
const legacyKeys =
cryptography.legacy.getPrivateAndPublicKeyFromPassphrase(defaultPassphrase);
const legacyBLSKeyPath = 'm/12381/134/0/99999';
const legacyBLSPrivateKey = await cryptography.bls.getPrivateKeyFromPhraseAndPath(
defaultPassphrase,
legacyBLSKeyPath,
);
await CreateCommand.run(['--add-legacy', '--passphrase', defaultPassphrase], config);
const loggedData = JSON.parse(stdout[0]);

expect(cryptography.legacy.getPrivateAndPublicKeyFromPassphrase).toHaveBeenCalledWith(
defaultPassphrase,
);
expect(cryptography.address.getAddressFromPublicKey).toHaveBeenCalledWith(
legacyKeys.publicKey,
);
expect(cryptography.ed.getPublicKeyFromPrivateKey).not.toHaveBeenCalledWith(
defaultAccountPrivateKey,
);
expect(cryptography.ed.getPrivateKeyFromPhraseAndPath).not.toHaveBeenCalledWith(
defaultPassphrase,
defaultGeneratorKeyPath,
);
expect(cryptography.ed.getPublicKeyFromPrivateKey).not.toHaveBeenCalledWith(
defaultGeneratorPrivateKey,
);
expect(cryptography.bls.getPrivateKeyFromPhraseAndPath).toHaveBeenCalledWith(
defaultPassphrase,
legacyBLSKeyPath,
);
expect(cryptography.bls.getPublicKeyFromPrivateKey).toHaveBeenCalledWith(legacyBLSPrivateKey);
expect(readerUtils.getPassphraseFromPrompt).not.toHaveBeenCalledWith('passphrase', true);
expect(readerUtils.getPasswordFromPrompt).toHaveBeenCalledWith('password', true);

expect(loggedData).toMatchObject({
keys: [
{
address: cryptography.address.getLisk32AddressFromPublicKey(legacyKeys.publicKey),
keyPath: 'legacy',
publicKey: legacyKeys.publicKey.toString('hex'),
privateKey: legacyKeys.privateKey.toString('hex'),
plain: {
generatorKeyPath: 'legacy',
generatorKey: legacyKeys.publicKey.toString('hex'),
generatorPrivateKey: legacyKeys.privateKey.toString('hex'),
blsKeyPath: legacyBLSKeyPath,
blsPrivateKey: legacyBLSPrivateKey.toString('hex'),
},
},
],
});
expect(loggedData.keys[0].encrypted).toBeDefined();
expect(loggedData.keys[0].encrypted).toHaveProperty('ciphertext');
expect(loggedData.keys[0].encrypted).toHaveProperty('mac');
expect(loggedData.keys[0].encrypted).toHaveProperty('cipherparams');
expect(loggedData.keys[0].encrypted).toHaveProperty('kdfparams');
expect(loggedData.keys[0].encrypted.cipher).toBe('aes-128-gcm');
expect(loggedData.keys[0].encrypted.kdf).toBe('argon2id');
expect(loggedData.keys[0].encrypted.version).toBe('1');
expect(consoleWarnSpy).toHaveBeenCalledTimes(0);
});
});

describe('keys:create --no-encrypt true', () => {
it('should create valid keys', async () => {
await CreateCommand.run(['--no-encrypt'], config);
Expand Down

0 comments on commit 3bf3b35

Please sign in to comment.