-
Notifications
You must be signed in to change notification settings - Fork 293
feat(root): add key validation function #6064
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
Open
mohammadalfaiyazbitgo
wants to merge
1
commit into
master
Choose a base branch
from
WP-4242/audit-key-method
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+470
−0
Open
Changes from all commits
Commits
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import * as base32 from 'hi-base32'; | ||
import { decrypt, BitGoAPI } from '@bitgo/sdk-api'; | ||
import { bitcoin } from '@bitgo/sdk-core'; | ||
import * as stellar from 'stellar-sdk'; | ||
import { coins, KeyCurve } from '@bitgo/statics'; | ||
|
||
export type AuditKeyParams = { | ||
coinName: string; | ||
encryptedPrv: string; | ||
walletPassphrase: string; | ||
} & ( | ||
| { | ||
multiSigType: 'tss'; | ||
publicKey: string; | ||
} | ||
| { | ||
multiSigType?: 'onchain'; | ||
publicKey?: string; | ||
} | ||
); | ||
|
||
/** | ||
* Audit a key by decrypting the encrypted private key and verifying it matches the provided public key | ||
* | ||
* @param coinName - The coin type | ||
* @param multiSigType - Optional type of multi-signature wallet | ||
* @param encryptedPrv - The encrypted private key to audit | ||
* @param publicKey - The public key to verify against | ||
* @param walletPassphrase - The passphrase to decrypt the encrypted private key | ||
* @returns Object containing audit results | ||
*/ | ||
export async function auditKey({ | ||
coinName, | ||
publicKey, | ||
encryptedPrv, | ||
walletPassphrase, | ||
multiSigType, | ||
}: AuditKeyParams): Promise<{ isValid: boolean; message?: string }> { | ||
const coin = coins.get(coinName); | ||
if (!coin) { | ||
return { isValid: false, message: `Unsupported coin ${coinName}` }; | ||
} | ||
try { | ||
// 1. Decrypt the encrypted private key | ||
const secret = decrypt(walletPassphrase, encryptedPrv); | ||
|
||
// 2. Determine curve type based on coin and multiSigType | ||
const curveType = coin.primaryKeyCurve; // default | ||
|
||
// 3. Validate the key | ||
|
||
switch (curveType) { | ||
case KeyCurve.Secp256k1: | ||
if (multiSigType === 'tss') { | ||
if (publicKey.length !== 130 && publicKey.length !== 0) { | ||
return { isValid: false, message: 'Incorrect TSS common keychain' }; | ||
} | ||
|
||
// DKLs key chains do not have a fixed length but we know for sure they are greater than 192 in length | ||
if (secret.length !== 128 && secret.length !== 192 && secret.length <= 192) { | ||
return { isValid: false, message: 'Incorrect TSS keychain' }; | ||
} | ||
} else if (multiSigType === 'onchain' && publicKey) { | ||
const genPubKey = bitcoin.HDNode.fromBase58(secret).neutered().toBase58(); | ||
if (genPubKey !== publicKey) { | ||
return { isValid: false, message: 'Incorrect xpub' }; | ||
} | ||
} | ||
break; | ||
case KeyCurve.Ed25519: | ||
if (multiSigType === 'tss') { | ||
// For TSS validation, we would need GPG private key for full implementation | ||
// This is a simplified validation of key format | ||
if (publicKey.length !== 128) { | ||
return { isValid: false, message: 'Incorrect TSS common keychain' }; | ||
} | ||
|
||
try { | ||
const parsedKey = JSON.parse(secret); | ||
if ('uShare' in parsedKey) { | ||
// If the key is in JSON format, we need to check the private key length | ||
const privateKeyLength = parsedKey.uShare.seed.length + parsedKey.uShare.chaincode.length; | ||
if (privateKeyLength !== 128) { | ||
return { isValid: false, message: 'Incorrect TSS keychain' }; | ||
} | ||
} else { | ||
// If the key is not in JSON format, we need to check the length directly | ||
if (secret.length !== 128) { | ||
return { isValid: false, message: 'Incorrect TSS keychain' }; | ||
} | ||
} | ||
} catch (e) { | ||
return { isValid: false, message: 'Invalid TSS keychain format' }; | ||
} | ||
} else { | ||
// Validate Non-TSS Ed25519 keys (e.g., XLM, ALGO, HBAR) | ||
const sdk = new BitGoAPI(); | ||
|
||
switch (coinName) { | ||
case 'xlm': | ||
if (secret.substr(0, 1) !== 'S') { | ||
return { isValid: false, message: 'Incorrect XLM key' }; | ||
} | ||
if (publicKey) { | ||
const { register: registerXLM } = await import('@bitgo/sdk-coin-xlm'); | ||
registerXLM(sdk); | ||
const xlmCoin = sdk.coin('xlm'); | ||
const genPubKey = xlmCoin.generateKeyPair((stellar.Keypair.fromSecret(secret) as any)._secretSeed).pub; | ||
if (genPubKey !== publicKey) { | ||
return { isValid: false, message: 'Incorrect XLM public key' }; | ||
} | ||
} | ||
break; | ||
case 'algo': | ||
const { register: registerAlgo } = await import('@bitgo/sdk-coin-algo'); | ||
registerAlgo(sdk); | ||
|
||
// Validate Secret length | ||
if (secret.length !== 58) { | ||
return { isValid: false, message: 'Incorrect ALGO key' }; | ||
} | ||
// Validate Public key length if provided | ||
if (publicKey) { | ||
const algoCoin = sdk.coin('algo'); | ||
const decoded = base32.decode.asBytes(secret); | ||
const ALGORAND_SEED_BYTE_LENGTH = 36; | ||
const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; | ||
const seed = Buffer.from(decoded.slice(0, ALGORAND_SEED_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH)); | ||
const genPubKey = algoCoin.generateKeyPair(seed).pub; | ||
if (genPubKey !== publicKey) { | ||
return { isValid: false, message: 'Incorrect ALGO public key' }; | ||
} | ||
} | ||
break; | ||
case 'hbar': | ||
if (secret.length !== 96) { | ||
return { isValid: false, message: 'Incorrect HBAR key' }; | ||
} | ||
|
||
if (publicKey) { | ||
const { register: registerHbar, KeyPair: hbarKeypair } = await import('@bitgo/sdk-coin-hbar'); | ||
registerHbar(sdk); | ||
const genPubKey = new hbarKeypair({ prv: secret }).getKeys().pub; | ||
if (genPubKey !== publicKey) return { isValid: false, message: 'Incorrect HBAR public key' }; | ||
} | ||
break; | ||
default: | ||
return { isValid: false, message: `Unsupported coin ${coinName}` }; | ||
} | ||
} | ||
break; | ||
default: { | ||
return { isValid: false, message: `Unsupported curve ${curveType}` }; | ||
} | ||
} | ||
|
||
return { isValid: true }; | ||
} catch (error) { | ||
return { isValid: false, message: error.message }; | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
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.
if publicKey isn't provided we should be returning isValid:false for multisig no?
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.
actually we should throw an error, saying public key needs to be provided to validate multisig key
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.
Actually the problem is that the encrypted prv on the key card doesn't have a public key for user/backup on multiSig Wallets. It just has BitGo's public key.
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.
ok if that's the case, we need to at least validate the private key somehow, i.e. length, format, etc... as it stands the code wont validate anything if there isnt a public key provided for multisig
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.
i understand though length could be tricky since private keys are probably stored encoded based on how the coin likes to encode its prv's . If we can instantiate a coin instance and that coin instance has some validate key function that would be nice