Skip to content

Commit 57c1778

Browse files
feat(root): add new audit key baseCoin method
Ticket: WP-4242 TICKET: WP-4242
1 parent 9365736 commit 57c1778

File tree

30 files changed

+781
-10
lines changed

30 files changed

+781
-10
lines changed

modules/abstract-cosmos/src/cosmosCoin.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
AuditDecryptedKeyParams,
3+
AuditKeyResponse,
24
BaseCoin,
35
BaseTransaction,
46
BitGoBase,
@@ -43,6 +45,7 @@ import {
4345
} from './lib';
4446
import { ROOT_PATH } from './lib/constants';
4547
import utils from './lib/utils';
48+
import { auditEcdsaPrivateKey } from '@bitgo/sdk-lib-mpc';
4649

4750
/**
4851
* Cosmos accounts support memo Id based addresses
@@ -665,4 +668,23 @@ export class CosmosCoin<CustomMessage = never> extends BaseCoin {
665668
getKeyPair(publicKey: string): CosmosKeyPair {
666669
throw new Error('Method not implemented');
667670
}
671+
672+
/** @inheritDoc **/
673+
auditDecryptedKey({ multiSigType, publicKey, prv }: AuditDecryptedKeyParams): AuditKeyResponse {
674+
if (multiSigType !== 'tss') {
675+
throw new Error('Unsupported multiSigType');
676+
} else {
677+
const result = auditEcdsaPrivateKey(prv as string, publicKey as string);
678+
if (result.isValid) {
679+
return { isValid: true };
680+
} else {
681+
if (!result.isCommonKeychainValid) {
682+
return { isValid: false, message: 'Invalid common keychain' };
683+
} else if (!result.isPrivateKeyValid) {
684+
return { isValid: false, message: 'Invalid private key' };
685+
}
686+
return { isValid: false };
687+
}
688+
}
689+
}
668690
}

modules/abstract-eth/src/abstractEthLikeCoin.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ import { CoinFamily, BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
66
import { bip32 } from '@bitgo/secp256k1';
77
import { randomBytes } from 'crypto';
88
import {
9+
AuditDecryptedKeyParams,
10+
AuditKeyResponse,
911
BaseCoin,
12+
bitcoin,
1013
BitGoBase,
1114
FullySignedTransaction,
1215
HalfSignedAccountTransaction,
16+
isValidPrv,
17+
isValidXprv,
1318
KeyPair,
1419
MethodNotImplementedError,
1520
ParsedTransaction,
@@ -25,6 +30,7 @@ import BigNumber from 'bignumber.js';
2530

2631
import { isValidEthAddress, KeyPair as EthKeyPair, TransactionBuilder } from './lib';
2732
import { VerifyEthAddressOptions } from './abstractEthLikeNewCoins';
33+
import { auditEcdsaPrivateKey } from '@bitgo/sdk-lib-mpc';
2834

2935
export interface EthSignTransactionOptions extends SignTransactionOptions {
3036
txPrebuild: TransactionPrebuild;
@@ -226,4 +232,32 @@ export abstract class AbstractEthLikeCoin extends BaseCoin {
226232
* @return a new transaction builder
227233
*/
228234
protected abstract getTransactionBuilder(common?: EthLikeCommon.default): TransactionBuilder;
235+
236+
auditDecryptedKey({ multiSigType, publicKey, prv }: AuditDecryptedKeyParams): AuditKeyResponse {
237+
if (multiSigType === 'tss') {
238+
const result = auditEcdsaPrivateKey(prv as string, publicKey as string);
239+
if (result.isValid) {
240+
return { isValid: true };
241+
} else {
242+
if (!result.isCommonKeychainValid) {
243+
return { isValid: false, message: 'Invalid common keychain' };
244+
} else if (!result.isPrivateKeyValid) {
245+
return { isValid: false, message: 'Invalid private key' };
246+
}
247+
return { isValid: false };
248+
}
249+
} else {
250+
if (!isValidPrv(prv) && !isValidXprv(prv)) {
251+
return { isValid: false, message: 'Invalid private key' };
252+
}
253+
if (publicKey) {
254+
const genPubKey = bitcoin.HDNode.fromBase58(prv).neutered().toBase58();
255+
if (genPubKey !== publicKey) {
256+
return { isValid: false, message: 'Incorrect xpub' };
257+
}
258+
}
259+
260+
return { isValid: true };
261+
}
262+
}
229263
}

modules/abstract-substrate/src/abstractSubstrateCoin.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
AuditDecryptedKeyParams,
3+
AuditKeyResponse,
24
BaseCoin,
35
BitGoBase,
46
EDDSAMethods,
@@ -27,7 +29,7 @@ import { KeyPair as SubstrateKeyPair, Transaction } from './lib';
2729
import { DEFAULT_SUBSTRATE_PREFIX } from './lib/constants';
2830
import { SignTransactionOptions, VerifiedTransactionParameters, Material } from './lib/iface';
2931
import utils from './lib/utils';
30-
import { getDerivationPath } from '@bitgo/sdk-lib-mpc';
32+
import { auditEddsaPrivateKey, getDerivationPath } from '@bitgo/sdk-lib-mpc';
3133
import BigNumber from 'bignumber.js';
3234
import { ApiPromise } from '@polkadot/api';
3335

@@ -519,4 +521,22 @@ export class SubstrateCoin extends BaseCoin {
519521
}
520522
return { transactions: broadcastableTransactions, lastScanIndex };
521523
}
524+
525+
/** inherited doc */
526+
auditDecryptedKey({ publicKey, prv, multiSigType }: AuditDecryptedKeyParams): AuditKeyResponse {
527+
if (multiSigType !== 'tss') {
528+
throw new Error('Unsupported multiSigType');
529+
}
530+
const result = auditEddsaPrivateKey(prv, publicKey ?? '');
531+
if (result.isValid) {
532+
return { isValid: true };
533+
} else {
534+
if (!result.isCommonKeychainValid) {
535+
return { isValid: false, message: 'Invalid common keychain' };
536+
} else if (!result.isPrivateKeyValid) {
537+
return { isValid: false, message: 'Invalid private key' };
538+
}
539+
return { isValid: false };
540+
}
541+
}
522542
}

modules/abstract-utxo/src/abstractUtxoCoin.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ExtraPrebuildParamsOptions,
1414
HalfSignedUtxoTransaction,
1515
IBaseCoin,
16+
AuditKeyResponse,
1617
InvalidAddressDerivationPropertyError,
1718
InvalidAddressError,
1819
IRequestTracer,
@@ -44,6 +45,9 @@ import {
4445
VerifyAddressOptions as BaseVerifyAddressOptions,
4546
VerifyTransactionOptions as BaseVerifyTransactionOptions,
4647
Wallet,
48+
isValidPrv,
49+
isValidXprv,
50+
bitcoin,
4751
} from '@bitgo/sdk-core';
4852

4953
import {
@@ -1136,4 +1140,21 @@ export abstract class AbstractUtxoCoin extends BaseCoin {
11361140
getRecoveryProvider(apiToken?: string): RecoveryProvider {
11371141
return forCoin(this.getChain(), apiToken);
11381142
}
1143+
1144+
auditDecryptedKey({ multiSigType, publicKey, prv }): AuditKeyResponse {
1145+
if (multiSigType === 'tss') {
1146+
throw new Error('tss auditing is not supported for this coin');
1147+
}
1148+
if (!isValidPrv(prv) && !isValidXprv(prv)) {
1149+
return { isValid: false, message: 'Invalid private key' };
1150+
}
1151+
if (publicKey) {
1152+
const genPubKey = bitcoin.HDNode.fromBase58(prv).neutered().toBase58();
1153+
if (genPubKey !== publicKey) {
1154+
return { isValid: false, message: 'Incorrect xpub' };
1155+
}
1156+
}
1157+
1158+
return { isValid: true };
1159+
}
11391160
}

modules/sdk-coin-ada/src/ada.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ import {
3131
PrebuildTransactionWithIntentOptions,
3232
MultisigType,
3333
multisigTypes,
34+
AuditDecryptedKeyParams,
35+
AuditKeyResponse,
3436
} from '@bitgo/sdk-core';
3537
import { KeyPair as AdaKeyPair, Transaction, TransactionBuilderFactory, Utils } from './lib';
3638
import { BaseCoin as StaticsBaseCoin, CoinFamily, coins } from '@bitgo/statics';
3739
import adaUtils from './lib/utils';
3840
import * as request from 'superagent';
3941
import BigNumber from 'bignumber.js';
40-
import { getDerivationPath } from '@bitgo/sdk-lib-mpc';
42+
import { auditEddsaPrivateKey, getDerivationPath } from '@bitgo/sdk-lib-mpc';
4143

4244
export const DEFAULT_SCAN_FACTOR = 20; // default number of receive addresses to scan for funds
4345

@@ -628,4 +630,24 @@ export class Ada extends BaseCoin {
628630
intent.unspents = params.unspents;
629631
intent.senderAddress = params.senderAddress;
630632
}
633+
634+
/** inherited doc */
635+
auditDecryptedKey({ publicKey, prv, multiSigType }: AuditDecryptedKeyParams): AuditKeyResponse {
636+
if (multiSigType !== 'tss') {
637+
throw new Error('Unsupported multiSigType');
638+
}
639+
640+
const result = auditEddsaPrivateKey(prv, publicKey ?? '');
641+
642+
if (result.isValid) {
643+
return { isValid: true };
644+
} else {
645+
if (!result.isCommonKeychainValid) {
646+
return { isValid: false, message: 'Invalid common keychain' };
647+
} else if (!result.isPrivateKeyValid) {
648+
return { isValid: false, message: 'Invalid private key' };
649+
}
650+
return { isValid: false };
651+
}
652+
}
631653
}

modules/sdk-coin-algo/src/algo.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
NotSupported,
3030
MultisigType,
3131
multisigTypes,
32+
AuditDecryptedKeyParams,
33+
AuditKeyResponse,
3234
} from '@bitgo/sdk-core';
3335
import stellar from 'stellar-sdk';
3436
import BigNumber from 'bignumber.js';
@@ -846,4 +848,22 @@ export class Algo extends BaseCoin {
846848
private getBuilder(): AlgoLib.TransactionBuilderFactory {
847849
return new AlgoLib.TransactionBuilderFactory(coins.get(this.getBaseChain()));
848850
}
851+
852+
/** @inheritDoc */
853+
protected auditDecryptedKey({ publicKey, prv, multiSigType }: AuditDecryptedKeyParams): AuditKeyResponse {
854+
if (multiSigType === 'tss') {
855+
throw new Error('Unsupported multiSigType');
856+
}
857+
858+
try {
859+
const algoKey = new AlgoLib.KeyPair({ prv });
860+
if (publicKey && publicKey !== algoKey.getKeys().pub) {
861+
return { isValid: false, message: 'Incorrect ALGO public key' };
862+
}
863+
} catch (e) {
864+
return { isValid: false, message: 'Invalid private key' };
865+
}
866+
867+
return { isValid: true };
868+
}
849869
}

modules/sdk-coin-algo/test/unit/algo.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AlgoLib, Talgo } from '../../src';
22
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
3-
import { BitGoAPI } from '@bitgo/sdk-api';
3+
import { BitGoAPI, encrypt } from '@bitgo/sdk-api';
44
import * as AlgoResources from '../fixtures/algo';
55
import { randomBytes } from 'crypto';
66
import { coins } from '@bitgo/statics';
@@ -10,6 +10,7 @@ import { Algo } from '../../src/algo';
1010
import BigNumber from 'bignumber.js';
1111
import { TransactionBuilderFactory } from '../../src/lib';
1212
import { KeyPair } from '@bitgo/sdk-core';
13+
import { algoBackupKey } from './fixtures/algoBackupKey';
1314

1415
describe('ALGO:', function () {
1516
let bitgo: TestBitGoAPI;
@@ -1128,4 +1129,43 @@ describe('ALGO:', function () {
11281129
});
11291130
});
11301131
});
1132+
1133+
describe('AuditKey', () => {
1134+
const { key } = algoBackupKey;
1135+
const walletPassphrase = 'ZQ8MhxT84m4P';
1136+
1137+
it('should return { isValid: true) } for valid inputs', async () => {
1138+
const result = await basecoin.auditKey({
1139+
encryptedPrv: key,
1140+
walletPassphrase,
1141+
});
1142+
result.should.deepEqual({ isValid: true });
1143+
});
1144+
1145+
it('should return { isValid: false } if the walletPassphrase is incorrect', async () => {
1146+
const result = await basecoin.auditKey({
1147+
encryptedPrv: key,
1148+
walletPassphrase: 'foo',
1149+
});
1150+
result.should.deepEqual({ isValid: false, message: "failed to decrypt prv: ccm: tag doesn't match" });
1151+
});
1152+
it('should return { isValid: false } if the key is altered', async () => {
1153+
const alteredKey = key.replace(/[0-9]/g, '0');
1154+
const result = await basecoin.auditKey({
1155+
encryptedPrv: alteredKey,
1156+
walletPassphrase,
1157+
});
1158+
result.isValid.should.equal(false);
1159+
});
1160+
1161+
it('should return { isValid: false } if the key is not a valid key', async () => {
1162+
const invalidKey = '#@)$#($*@)#($*';
1163+
const encryptedPrv = encrypt(walletPassphrase, invalidKey);
1164+
const result = await basecoin.auditKey({
1165+
encryptedPrv,
1166+
walletPassphrase,
1167+
});
1168+
result.should.deepEqual({ isValid: false, message: 'Invalid private key' });
1169+
});
1170+
});
11311171
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const algoBackupKey = {
2+
key:
3+
'{"iv":"ZdWrTtponn9Q4wK9VOkJdw==","v":1,"iter":10000,"ks":256,"ts":64,"mode"' +
4+
':"ccm","adata":"","cipher":"aes","salt":"dQ6rGI+qnmk=","ct":"Y4oLG6d+w3Apo+' +
5+
'nQdH2YfNcXZrA9RCrMw5M8r7GHgxsUoZ62ZHzhjk7CpOLi4YCFIwSf40pqShGrELQkA+VkGnFD"' +
6+
'}',
7+
};

modules/sdk-coin-btc/test/unit/btc.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'should';
2+
import { btcBackupKey } from './fixtures';
23

34
import { TestBitGoAPI, TestBitGo } from '@bitgo/sdk-test';
45

@@ -66,4 +67,40 @@ describe('BTC:', function () {
6667
}
6768
});
6869
});
70+
71+
describe('Audit Key', () => {
72+
const { key } = btcBackupKey;
73+
let coin: Tbtc;
74+
before(() => {
75+
coin = bitgo.coin('tbtc') as Tbtc;
76+
});
77+
78+
it('should return { isValid: true } for valid inputs', async () => {
79+
const result = await coin.auditKey({
80+
encryptedPrv: key,
81+
walletPassphrase: 'kAm[EFQ6o=SxlcLFDw%,',
82+
});
83+
result.should.deepEqual({ isValid: true });
84+
});
85+
86+
it('should return { isValid: false } if the walletPassphrase is incorrect', async () => {
87+
const result = await coin.auditKey({
88+
encryptedPrv: key,
89+
walletPassphrase: 'foo',
90+
});
91+
result.should.deepEqual({
92+
isValid: false,
93+
message: "failed to decrypt prv: ccm: tag doesn't match",
94+
});
95+
});
96+
97+
it('should return { isValid: false } if the key is altered', async () => {
98+
const alteredKey = key.replace(/[0-9]/g, '0');
99+
const result = await coin.auditKey({
100+
encryptedPrv: alteredKey,
101+
walletPassphrase: 'kAm[EFQ6o=SxlcLFDw%,',
102+
});
103+
result.isValid.should.equal(false);
104+
});
105+
});
69106
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const btcBackupKey = {
2+
key:
3+
'{"iv":"JgqqE4W45/tKBSMSYqD+qg==","v":1,"iter":10000,"ks":256,"ts":64,"mode"' +
4+
':"ccm","adata":"","cipher":"aes","salt":"kiLPf8VSdI0=","ct":"zUh4Oko/06g02E' +
5+
'wnqOfzJbTwtE2p3b19jDk8Tum07Jv3N/RP7Bo0w/ObLBO1uIJFossO3nJ1JS+7t/vPQhdCtN8oD' +
6+
'6YrZnEZYrRwN6JQkL1uYPnZ1PoWbYI9navK5CLU1KQwDTO9YEN46++OrzFH+CjpQVLblaw="}',
7+
};

0 commit comments

Comments
 (0)