Skip to content

Commit

Permalink
feat: added --private-key option to CommandStart.ts and `CommandB…
Browse files Browse the repository at this point in the history
…ootstrap.ts`

This should allow us to override the keypair generation with the provided private key. this will speed up agent starting.

Still need to test this for `agent start` and `bootstrap`.

Related #404
  • Loading branch information
tegefaulkes committed Jul 15, 2022
1 parent 4e210ca commit 82f7b21
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/bin/agent/CommandStart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class CommandStart extends CommandPolykey {
this.addOption(binOptions.backgroundOutFile);
this.addOption(binOptions.backgroundErrFile);
this.addOption(binOptions.fresh);
this.addOption(binOptions.privateKey);
this.action(async (options) => {
options.clientHost =
options.clientHost ?? config.defaults.networkConfig.clientHost;
Expand Down Expand Up @@ -94,6 +95,7 @@ class CommandStart extends CommandPolykey {
keysConfig: {
rootKeyPairBits: options.rootKeyPairBits,
recoveryCode: recoveryCodeIn,
privateKeyOverride: options.privateKey,
},
proxyConfig: {
connConnectTime: options.connectionTimeout,
Expand Down
2 changes: 2 additions & 0 deletions src/bin/bootstrap/CommandBootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class CommandBootstrap extends CommandPolykey {
this.addOption(binOptions.recoveryCodeFile);
this.addOption(binOptions.rootKeyPairBits);
this.addOption(binOptions.fresh);
this.addOption(binOptions.privateKey);
this.action(async (options) => {
const bootstrapUtils = await import('../../bootstrap/utils');
const password = await binProcessors.processNewPassword(
Expand All @@ -27,6 +28,7 @@ class CommandBootstrap extends CommandPolykey {
keysConfig: {
rootKeyPairBits: options.rootKeyPairBits,
recoveryCode: recoveryCodeIn,
privateKeyOverride: options.privateKey,
},
fresh: options.fresh,
fs: this.fs,
Expand Down
8 changes: 8 additions & 0 deletions src/bin/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ const noPing = new commander.Option('--no-ping', 'Skip ping step').default(
true,
);

const privateKey = new commander.Option(
'--private-key <privateKey>',
'Override key generation with a private key Pem',
)
.argParser(binParsers.parsePrivateKeyPem)
.default(undefined);

export {
nodePath,
format,
Expand All @@ -187,4 +194,5 @@ export {
pullVault,
forceNodeAdd,
noPing,
privateKey,
};
5 changes: 5 additions & 0 deletions src/bin/utils/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ function parseSecretPath(secretPath: string): [string, string, string?] {
return [vaultName, directoryPath, undefined];
}

const parsePrivateKeyPem = validateParserToArgListParser(
validationUtils.parsePrivateKeyPem,
);

export {
parseInteger,
parseNumber,
Expand All @@ -109,4 +113,5 @@ export {
parseProviderIdList,
parseCoreCount,
parseSecretPath,
parsePrivateKeyPem,
};
3 changes: 2 additions & 1 deletion src/bootstrap/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FileSystem } from '../types';
import type { RecoveryCode } from '../keys/types';
import type { RecoveryCode, PrivateKey } from '../keys/types';
import path from 'path';
import Logger from '@matrixai/logger';
import { DB } from '@matrixai/db';
Expand Down Expand Up @@ -40,6 +40,7 @@ async function bootstrapState({
rootCertDuration?: number;
dbKeyBits?: number;
recoveryCode?: RecoveryCode;
privateKeyOverride?: PrivateKey;
};
fresh?: boolean;
fs?: FileSystem;
Expand Down
18 changes: 18 additions & 0 deletions src/validation/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import type { GestaltAction, GestaltId } from '../gestalts/types';
import type { VaultAction, VaultId } from '../vaults/types';
import type { Host, Hostname, Port } from '../network/types';
import type { ClaimId } from '../claims/types';
import type { PrivateKey } from '../keys/types';
import * as validationErrors from './errors';
import * as nodesUtils from '../nodes/utils';
import * as gestaltsUtils from '../gestalts/utils';
import * as vaultsUtils from '../vaults/utils';
import * as networkUtils from '../network/utils';
import * as claimsUtils from '../claims/utils';
import * as keysUtils from '../keys/utils';
import config from '../config';

function parseInteger(data: any): number {
Expand Down Expand Up @@ -259,6 +261,21 @@ function parseSeedNodes(data: any): [SeedNodes, boolean] {
return [seedNodes, defaults];
}

function parsePrivateKeyPem(data: any): PrivateKey {
if (typeof data !== 'string') {
throw new validationErrors.ErrorParse('Private key Pem must be a string');
}
let privateKey: PrivateKey;
try {
privateKey = keysUtils.privateKeyFromPem(data);
} catch (e) {
throw new validationErrors.ErrorParse(
'Must provide a valid private key Pem',
);
}
return privateKey;
}

export {
parseInteger,
parseNumber,
Expand All @@ -276,4 +293,5 @@ export {
parsePort,
parseNetwork,
parseSeedNodes,
parsePrivateKeyPem,
};
13 changes: 9 additions & 4 deletions tests/keys/KeyManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,20 +164,25 @@ describe('KeyManager', () => {
test('override key generation with privateKeyOverride', async () => {
const keysPath = `${dataDir}/keys`;
const keyPair = await keysUtils.generateKeyPair(4096);
const mockedGenerateKeyPair = jest.spyOn(keysUtils, 'generateDeterministicKeyPair');
const mockedGenerateKeyPair = jest.spyOn(
keysUtils,
'generateDeterministicKeyPair',
);
const keyManager = await KeyManager.createKeyManager({
keysPath,
password,
privateKeyOverride: keyPair.privateKey,
logger,
});
expect(mockedGenerateKeyPair).not.toHaveBeenCalled()
expect(mockedGenerateKeyPair).not.toHaveBeenCalled();
const keysPathContents = await fs.promises.readdir(keysPath);
expect(keysPathContents).toContain('root.pub');
expect(keysPathContents).toContain('root.key');
expect(keysUtils.publicKeyToPem(keyManager.getRootKeyPair().publicKey)).toEqual(keysUtils.publicKeyToPem(keyPair.publicKey));
expect(
keysUtils.publicKeyToPem(keyManager.getRootKeyPair().publicKey),
).toEqual(keysUtils.publicKeyToPem(keyPair.publicKey));
await keyManager.stop();
})
});
test('uses WorkerManager for generating root key pair', async () => {
const keysPath = `${dataDir}/keys`;
const keyManager = await KeyManager.createKeyManager({
Expand Down

0 comments on commit 82f7b21

Please sign in to comment.