Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions @types/miscreant.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
declare module 'miscreant' {
interface ICryptoProvider {
importBlockCipherKey(keyData: Uint8Array): Promise<IBlockCipher>;
}

interface IBlockCipher {
clear(): this;
encryptBlock(block: Uint8Array): Promise<Uint8Array>;
}

export class PolyfillCryptoProvider implements ICryptoProvider {
importBlockCipherKey(keyData: Uint8Array): Promise<IBlockCipher>;
}

export class WebCryptoProvider implements ICryptoProvider {
importBlockCipherKey(keyData: Uint8Array): Promise<IBlockCipher>;
}

export class SIV {
static importKey(
keyData: Uint8Array,
algorithm: string,
provider: ICryptoProvider,
): Promise<SIV>;

seal(
plaintext: Uint8Array,
associatedData: Uint8Array[],
): Promise<Uint8Array>;

open(
ciphertext: Uint8Array,
associatedData: Uint8Array[],
): Promise<Uint8Array>;

clear(): this;
}

export class AEAD {
static importKey(
keyData: Uint8Array,
algorithm: string,
provider: ICryptoProvider,
): Promise<AEAD>;

seal(
plaintext: Uint8Array,
nonce: Uint8Array,
associatedData?: Uint8Array,
): Promise<Uint8Array>;

open(
ciphertext: Uint8Array,
nonce: Uint8Array,
associatedData?: Uint8Array,
): Promise<Uint8Array>;

clear(): this;
}
}
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,19 @@
"@mysten/sui": "1.28.0",
"@ngraveio/bc-ur": "^1.1.13",
"@nktkas/hyperliquid": "0.29.1",
"@onekeyfe/cross-inpage-provider-core": "2.2.62",
"@onekeyfe/cross-inpage-provider-errors": "2.2.62",
"@onekeyfe/cross-inpage-provider-injected": "2.2.62",
"@onekeyfe/cross-inpage-provider-types": "2.2.62",
"@onekeyfe/extension-bridge-hosted": "2.2.62",
"@onekeyfe/cross-inpage-provider-core": "2.2.63",
"@onekeyfe/cross-inpage-provider-errors": "2.2.63",
"@onekeyfe/cross-inpage-provider-injected": "2.2.63",
"@onekeyfe/cross-inpage-provider-types": "2.2.63",
"@onekeyfe/extension-bridge-hosted": "2.2.63",
"@onekeyfe/hd-ble-sdk": "1.1.24-alpha.2",
"@onekeyfe/hd-common-connect-sdk": "1.1.24-alpha.2",
"@onekeyfe/hd-core": "1.1.24-alpha.2",
"@onekeyfe/hd-shared": "1.1.24-alpha.2",
"@onekeyfe/hd-transport": "1.1.24-alpha.2",
"@onekeyfe/hd-transport-electron": "1.1.24-alpha.2",
"@onekeyfe/hd-web-sdk": "1.1.24-alpha.2",
"@onekeyfe/onekey-cross-webview": "2.2.62",
"@onekeyfe/onekey-cross-webview": "2.2.63",
"@polkadot/extension-inject": "0.54.1",
"@polkadot/types": "14.3.1",
"@polkadot/util-crypto": "13.2.3",
Expand Down
3 changes: 2 additions & 1 deletion packages/kit-bg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"main": "src/index.tsx",
"dependencies": {
"eth-url-parser": "^1.0.4",
"idb": "^7.1.1"
"idb": "^7.1.1",
"miscreant": "^0.3.2"
},
"scripts": {
"_folderslint": "yarn folderslint",
Expand Down
52 changes: 52 additions & 0 deletions packages/kit-bg/src/providers/ProviderApiBtc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,58 @@ class ProviderApiBtc extends ProviderApiBase {
};
}

@providerApiMethod()
public async getBalanceV2(request: IJsBridgeMessagePayload) {
const { accountInfo: { networkId, accountId } = {} } = (
await this.getAccountsInfo(request)
)[0];

const { balance } =
await this.backgroundApi.serviceAccountProfile.fetchAccountDetails({
networkId: networkId ?? '',
accountId: accountId ?? '',
});
return {
available: balance,
unavailable: 0,
total: balance,
};
}

@providerApiMethod()
public async getBitcoinUtxos(
request: IJsBridgeMessagePayload,
params?: { cursor?: number; size?: number },
) {
const { accountInfo: { networkId, accountId } = {} } = (
await this.getAccountsInfo(request)
)[0];

const { utxoList } =
await this.backgroundApi.serviceAccountProfile.fetchAccountDetails({
networkId: networkId ?? '',
accountId: accountId ?? '',
withUTXOList: true,
});

const mappedUtxos =
utxoList?.map((it) => {
const bn = new BigNumber(it.value ?? 0);
const satoshis = bn.isNaN() ? 0 : bn.toNumber();

return {
txid: it.txid,
vout: it.vout,
pubkey: it.txPubkey,
satoshis,
scriptPk: it.scriptPublicKey,
};
}) ?? [];

const { cursor = 0, size = mappedUtxos.length } = params ?? {};
return mappedUtxos.slice(cursor, cursor + size);
}

@providerApiMethod()
public async getInscriptions() {
throw web3Errors.rpc.methodNotSupported();
Expand Down
93 changes: 92 additions & 1 deletion packages/kit-bg/src/providers/ProviderApiCosmos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import {
IMPL_COSMOS,
} from '@onekeyhq/shared/src/engine/engineConsts';
import { OneKeyLocalError } from '@onekeyhq/shared/src/errors';
import {
EDAppConnectionModal,
EModalRoutes,
} from '@onekeyhq/shared/src/routes';
import { defaultLogger } from '@onekeyhq/shared/src/logger/logger';
import accountUtils from '@onekeyhq/shared/src/utils/accountUtils';
import hexUtils from '@onekeyhq/shared/src/utils/hexUtils';
Expand All @@ -33,8 +37,11 @@ import type { INetworkAccount } from '@onekeyhq/shared/types/account';
import type { IConnectionAccountInfo } from '@onekeyhq/shared/types/dappConnection';
import { EMessageTypesCommon } from '@onekeyhq/shared/types/message';

import type { SecretNetworkEncryption } from '../vaults/impls/cosmos/sdkCosmos/SecretNetworkEncryption';
import { vaultFactory } from '../vaults/factory';
import ProviderApiBase from './ProviderApiBase';

import type VaultCosmos from '../vaults/impls/cosmos/Vault';
import type { IProviderBaseBackgroundNotifyInfo } from './ProviderApiBase';
import type { IJsBridgeMessagePayload } from '@onekeyfe/cross-inpage-provider-types';

Expand Down Expand Up @@ -165,7 +172,7 @@ class ProviderApiCosmos extends ProviderApiBase {
networkId,
});
if (!network) {
throw new OneKeyLocalError('Invalid chainId');
return undefined;
}

try {
Expand Down Expand Up @@ -722,6 +729,90 @@ class ProviderApiCosmos extends ProviderApiBase {
});
}

// Enigma (Secret Network) support
private async _getOrCreateEnigmaUtils(
request: IJsBridgeMessagePayload,
chainId: string,
): Promise<SecretNetworkEncryption> {
const networkId = this.convertCosmosChainId(chainId);
if (!networkId) throw new OneKeyLocalError('Invalid chainId');

const account = await this._getAccount(request, networkId);

const { accountInfo } = account;
const walletId = accountInfo?.walletId ?? '';
const accountId = accountInfo?.accountId ?? account.account.id;

let password = await this.backgroundApi.servicePassword.getCachedPassword();

if (!password) {
const result = (await this.backgroundApi.serviceDApp.openModal({
request,
screens: [
EModalRoutes.DAppConnectionModal,
Comment on lines +748 to +752

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail fast on unsupported wallets before password prompt

_getOrCreateEnigmaUtils asks for a password before checking whether the current keyring can derive an Enigma seed, but VaultCosmos.getEnigmaSeed throws for unsupported wallet types (e.g., hardware/watching). In that scenario, users are forced through an unlock modal and then still get a hard failure, which is a dead-end flow for those wallets; the support check should happen before opening the unlock modal.

Useful? React with 👍 / 👎.

EDAppConnectionModal.CosmosEnigmaUnlockModal,
],
params: {
walletId,
accountId,
networkId: accountInfo?.networkId ?? networkId,
},
fullScreen: true,
})) as { password: string };
password = result.password;
}

const vault = (await vaultFactory.getVault({
networkId: accountInfo?.networkId ?? networkId,
accountId,
})) as VaultCosmos;

return vault.getOrCreateEnigmaUtils({ password });
}

@providerApiMethod()
public async getEnigmaPubKey(
request: IJsBridgeMessagePayload,
params: { chainId: string },
): Promise<string> {
const utils = await this._getOrCreateEnigmaUtils(request, params.chainId);
const pubkey = await utils.getPubkey();
return bytesToHex(pubkey);
}
Comment on lines +773 to +781
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Missing @permissionRequired() decorator on Enigma provider methods breaks security pattern

All existing Cosmos provider methods that perform sensitive cryptographic operations (signAmino, signDirect, sendTx, signArbitrary, verifyArbitrary) are decorated with @permissionRequired() to ensure the dApp has an approved connection before proceeding. The four new Enigma methods (getEnigmaPubKey, enigmaEncrypt, enigmaDecrypt, enigmaGetTxEncryptionKey) perform cryptographic operations using the user's private key-derived seed but lack this decorator. While _getOrCreateEnigmaUtils internally calls _getAccountgetAccountsInfo which may fail for unconnected dApps, the missing decorator breaks the defense-in-depth pattern established throughout the codebase. The CLAUDE.md rule states: "Crypto operations MUST follow established patterns" and "Transaction verification and risk detection MUST NOT be bypassed".

Prompt for agents
Add the @permissionRequired() decorator to all four Enigma provider methods in packages/kit-bg/src/providers/ProviderApiCosmos.ts to match the existing pattern used by signAmino, signDirect, sendTx, signArbitrary, and verifyArbitrary. Specifically, add @permissionRequired() before @providerApiMethod() on the following methods:
1. getEnigmaPubKey (line 773)
2. enigmaEncrypt (line 783)
3. enigmaDecrypt (line 793)
4. enigmaGetTxEncryptionKey (line 806)
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


@providerApiMethod()
public async enigmaEncrypt(
request: IJsBridgeMessagePayload,
params: { chainId: string; contractCodeHash: string; msg: object },
): Promise<string> {
const utils = await this._getOrCreateEnigmaUtils(request, params.chainId);
const encrypted = await utils.encrypt(params.contractCodeHash, params.msg);
return bytesToHex(encrypted);
}

@providerApiMethod()
public async enigmaDecrypt(
request: IJsBridgeMessagePayload,
params: { chainId: string; ciphertext: string; nonce: string },
): Promise<string> {
const utils = await this._getOrCreateEnigmaUtils(request, params.chainId);
const decrypted = await utils.decrypt(
hexToBytes(params.ciphertext),
hexToBytes(params.nonce),
);
return bytesToHex(decrypted);
}

@providerApiMethod()
public async enigmaGetTxEncryptionKey(
request: IJsBridgeMessagePayload,
params: { chainId: string; nonce: string },
): Promise<string> {
const utils = await this._getOrCreateEnigmaUtils(request, params.chainId);
const key = await utils.getTxEncryptionKey(hexToBytes(params.nonce));
return bytesToHex(key);
}

@providerApiMethod()
public async cosmos_signDirect(
request: IJsBridgeMessagePayload,
Expand Down
12 changes: 12 additions & 0 deletions packages/kit-bg/src/vaults/impls/cosmos/KeyringHd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import coreChainApi from '@onekeyhq/core/src/instance/coreChainApi';
import type { ISignedTxPro } from '@onekeyhq/core/src/types';

import { KeyringHdBase } from '../../base/KeyringHdBase';
import { SecretNetworkEncryption } from './sdkCosmos/SecretNetworkEncryption';

import type { IDBAccount } from '../../../dbs/local/types';
import type {
Expand Down Expand Up @@ -44,4 +45,15 @@ export class KeyringHd extends KeyringHdBase {
override async signMessage(params: ISignMessageParams): Promise<string[]> {
return this.baseSignMessage(params);
}

async getEnigmaSeed(params: { password: string }): Promise<Uint8Array> {
const { password } = params;
const privateKeys = await this.baseGetPrivateKeys({ password });
const account = await this.vault.getAccount();
const encryptedPrivateKeyHex = privateKeys[account.path];
return SecretNetworkEncryption.deriveEnigmaSeed({
encryptedPrivateKeyHex,
password,
});
}
}
13 changes: 13 additions & 0 deletions packages/kit-bg/src/vaults/impls/cosmos/KeyringImported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import coreChainApi from '@onekeyhq/core/src/instance/coreChainApi';
import type { ISignedMessagePro, ISignedTxPro } from '@onekeyhq/core/src/types';

import { KeyringImportedBase } from '../../base/KeyringImportedBase';
import { SecretNetworkEncryption } from './sdkCosmos/SecretNetworkEncryption';

import type { IDBAccount } from '../../../dbs/local/types';
import type {
Expand Down Expand Up @@ -48,4 +49,16 @@ export class KeyringImported extends KeyringImportedBase {
): Promise<ISignedMessagePro> {
return this.baseSignMessage(params);
}

async getEnigmaSeed(params: { password: string }): Promise<Uint8Array> {
const { password } = params;
const privateKeys = await this.baseGetPrivateKeys({ password });
const account = await this.vault.getAccount();
const encryptedPrivateKeyHex =
privateKeys[account.path] || Object.values(privateKeys)[0];
return SecretNetworkEncryption.deriveEnigmaSeed({
encryptedPrivateKeyHex,
password,
});
}
}
Loading