-
Notifications
You must be signed in to change notification settings - Fork 307
feat: add external signing functionality for v2 wallet #1944
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
ee26c72
feat: add signing functionality to external signer mode
3c0e9a1
feat: check config when running in external signer mode
fe78332
feat: check that signerFileSystemPath path contains a private key
7b00932
feat: only allow external signing feature to run in test mode
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,7 +17,7 @@ import { Config, config } from './config'; | |||||||||||||
| const debug = debugLib('bitgo:express'); | ||||||||||||||
|
|
||||||||||||||
| import { SSL_OP_NO_TLSv1 } from 'constants'; | ||||||||||||||
| import { IpcError, NodeEnvironmentError, TlsConfigurationError } from './errors'; | ||||||||||||||
| import { IpcError, NodeEnvironmentError, TlsConfigurationError, ExternalSignerConfigError } from './errors'; | ||||||||||||||
|
|
||||||||||||||
| import { Environments } from 'bitgo'; | ||||||||||||||
| import * as clientRoutes from './clientRoutes'; | ||||||||||||||
|
|
@@ -155,12 +155,41 @@ export function createBaseUri(config: Config): string { | |||||||||||||
| return `http${tls ? 's' : ''}://${bind}${!isStandardPort ? ':' + port : ''}`; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Check the that the json file containing the external signer private key exists | ||||||||||||||
| * @param path | ||||||||||||||
| */ | ||||||||||||||
| function checkSignerPrvPath(path: string) { | ||||||||||||||
| try { | ||||||||||||||
| const privKeyFile = fs.readFileSync(path, { encoding: 'utf8' }); | ||||||||||||||
| const privKey = JSON.parse(privKeyFile); | ||||||||||||||
| if (privKey.prv === undefined) { | ||||||||||||||
| throw new Error(`required field "prv" is missing`); | ||||||||||||||
| } | ||||||||||||||
| } catch (e) { | ||||||||||||||
| throw new Error(`Failed to parse ${path} - ${e.message}`); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Check environment and other preconditions to ensure bitgo-express can start safely | ||||||||||||||
| * @param config | ||||||||||||||
| */ | ||||||||||||||
| function checkPreconditions(config: Config) { | ||||||||||||||
| const { env, disableEnvCheck, bind, ipc, disableSSL, keyPath, crtPath, customRootUri, customBitcoinNetwork } = config; | ||||||||||||||
| const { | ||||||||||||||
| env, | ||||||||||||||
| disableEnvCheck, | ||||||||||||||
| bind, | ||||||||||||||
| ipc, | ||||||||||||||
| disableSSL, | ||||||||||||||
| keyPath, | ||||||||||||||
| crtPath, | ||||||||||||||
| customRootUri, | ||||||||||||||
| customBitcoinNetwork, | ||||||||||||||
| externalSignerUrl, | ||||||||||||||
| signerMode, | ||||||||||||||
| signerFileSystemPath, | ||||||||||||||
| } = config; | ||||||||||||||
|
|
||||||||||||||
| // warn or throw if the NODE_ENV is not production when BITGO_ENV is production - this can leak system info from express | ||||||||||||||
| if (env === 'prod' && process.env.NODE_ENV !== 'production') { | ||||||||||||||
|
|
@@ -190,6 +219,30 @@ function checkPreconditions(config: Config) { | |||||||||||||
| console.warn(`customRootUri or customBitcoinNetwork is set, but env is '${env}'. Setting env to 'custom'.`); | ||||||||||||||
| config.env = 'custom'; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if (env !== 'test' && (externalSignerUrl !== undefined || signerMode !== undefined)) { | ||||||||||||||
| throw new ExternalSignerConfigError('external signer feature is only enabled for test mode.'); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if (externalSignerUrl !== undefined && (signerMode !== undefined || signerFileSystemPath !== undefined)) { | ||||||||||||||
| throw new ExternalSignerConfigError( | ||||||||||||||
| 'signerMode or signerFileSystemPath is set, but externalSignerUrl is also set.' | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if ((signerMode !== undefined || signerFileSystemPath !== undefined) && !(signerMode && signerFileSystemPath)) { | ||||||||||||||
| throw new ExternalSignerConfigError( | ||||||||||||||
| 'signerMode and signerFileSystemPath must both be set in order to run in external signing mode.' | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if (signerFileSystemPath !== undefined) { | ||||||||||||||
| try { | ||||||||||||||
| checkSignerPrvPath(signerFileSystemPath); | ||||||||||||||
| } catch (e) { | ||||||||||||||
| throw e; | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
+240
to
+244
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to catch if you're just going to immediately rethrow - feel free to handle in a follow up PR to clean up some other nits as well.
Suggested change
|
||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| export function setupRoutes(app: express.Application, config: Config): void { | ||||||||||||||
|
|
||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /** | ||
| * @prettier | ||
| */ | ||
| import * as sinon from 'sinon'; | ||
|
|
||
| import 'should-http'; | ||
| import 'should-sinon'; | ||
| import '../../lib/asserts'; | ||
|
|
||
| import * as express from 'express'; | ||
| import { handleV2Sign } from '../../../src/clientRoutes'; | ||
| import * as fs from 'fs'; | ||
| import { Btc } from 'bitgo/dist/src/v2/coins/btc'; | ||
| import { BitGo } from 'bitgo'; | ||
|
|
||
| describe('External signer', () => { | ||
| it('should read prv from signerFileSystemPath and pass it to coin.signTransaction', async () => { | ||
| const validPrv = | ||
| '{"prv":"xprv9s21ZrQH143K3EuPWCBuqnWxydaQV6et9htQige4EswvcHKEzNmkVmwTwKoadyHzJYppuADB7Us7AbaNLToNvoFoSxuWqndQRYtnNy5DUY2"}'; | ||
| const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(validPrv); | ||
| const signTransactionStub = sinon.stub(Btc.prototype, 'signTransaction').resolves('signedTx'); | ||
|
|
||
| const req = { | ||
| bitgo: new BitGo({ env: 'test' }), | ||
| params: { | ||
| coin: 'tbtc', | ||
| }, | ||
| config: { | ||
| signerFileSystemPath: 'signerFileSystemPath', | ||
| }, | ||
| } as unknown as express.Request; | ||
|
|
||
| await handleV2Sign(req); | ||
|
|
||
| readFileStub.should.be.calledOnceWith('signerFileSystemPath'); | ||
| signTransactionStub.should.be.calledOnceWith( | ||
| sinon.match({ | ||
| prv: 'xprv9s21ZrQH143K3EuPWCBuqnWxydaQV6et9htQige4EswvcHKEzNmkVmwTwKoadyHzJYppuADB7Us7AbaNLToNvoFoSxuWqndQRYtnNy5DUY2', | ||
| }) | ||
| ); | ||
| readFileStub.restore(); | ||
| signTransactionStub.restore(); | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this is no longer necessary and can be removed from all other errors defined here. Please remove in a follow up PR though, no need to do this clean up item here