Skip to content

Commit

Permalink
perf(INJI-458): cache fixed result of custom keystore presence
Browse files Browse the repository at this point in the history
Each call over the RN bridge can take significant user-visible time, and since
the result is used multiple times and cannot change for a device in runtime,
it can be computed once stored for later use.

Signed-off-by: Harsh Vardhan <[email protected]>
  • Loading branch information
vharsh committed Oct 18, 2023
1 parent ed76f21 commit b6fe6e9
Show file tree
Hide file tree
Showing 14 changed files with 64 additions and 37 deletions.
14 changes: 13 additions & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,23 @@ fileignoreconfig:
checksum: 18af825821bc95e1056050623b804a5a8e7435b9e3383916a5d63024eeba9553
- filename: screens/WelcomeScreenController.ts
checksum: d8fe74404c80bf435459f4d20427a661fb622f0ee9f754345616abd346b98d14
- filename: machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.ts
checksum: b02bd62a768257e835d035cab0b01480f35035a0dfa0ffd18bcadf282f1fab84
- filename: machines/store.ts
checksum: 71a3e3972b176c7c3f3a6b4c59b92f0d1856241ac884b9e27ff5ab81b0a71bcd
- filename: shared/cryptoutil/cryptoUtil.ts
checksum: 439e238178fbd6a8c113127f408a5fae3b6075a84ba13c3c37a72a653d5733f5
- filename: shared/telemetry/TelemetryUtils.js
checksum: 9a61cd59a3718adf1f14faf3024fec66a3295ef373878a878a28e5cb1287afaa
- filename: machines/store.ts
checksum: bbee269bd9703644c2a345819291caa42479b5ad0d5288713da780495b2ffb49
- filename: shared/fileStorage.ts
checksum: 07cb337dc1d5b0f0eef56270ac4f4f589260ee5e490183c024cf98a2aeafb139
- filename: shared/storage.ts
checksum: c8d874aa373bdf526bf59192139822f56915e702ef673bac4e0d7549b0fea3d0
checksum: c8d874aa373bdf526bf59192139822f56915e702ef673bac4e0d7549b0fea3d0
checksum: f9711b617b986af9bb733a31373e49494667ef07b74988fbf09688cb50ca73bd
- filename: machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.ts
checksum: 3ac59db154914fec7770e14b0451f21f98a0fe8bb0b0243e800ae3aac8a2941d
- filename: machines/issuersMachine.ts
checksum: ae05c4d0d2a65e480156014b324a454269e00dba47414c00c757f81b4fc27cef
version: ""
4 changes: 2 additions & 2 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {Alert} from 'react-native';
import {configureTelemetry} from './shared/telemetry/TelemetryUtils';
import {MessageOverlay} from './components/MessageOverlay';
import SecureKeystore from 'react-native-secure-keystore';
import {isCustomSecureKeystore} from './shared/cryptoutil/cryptoUtil';
import {isCustomKeystore} from './shared/cryptoutil/cryptoUtil';
import i18n from './i18n';
import './shared/flipperConfig';

Expand Down Expand Up @@ -91,7 +91,7 @@ const AppInitialization: React.FC = () => {
const {t} = useTranslation('common');

useEffect(() => {
if (isCustomSecureKeystore()) {
if (isCustomKeystore) {
SecureKeystore.updatePopup(
t('biometricPopup.title'),
t('biometricPopup.description'),
Expand Down
6 changes: 3 additions & 3 deletions machines/QrLoginMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {MY_VCS_STORE_KEY, ESIGNET_BASE_URL} from '../shared/constants';
import {StoreEvents} from './store';
import {linkTransactionResponse, VC} from '../types/VC/ExistingMosipVC/vc';
import {request} from '../shared/request';
import {getJwt, isCustomSecureKeystore} from '../shared/cryptoutil/cryptoUtil';
import {getJwt, isCustomKeystore} from '../shared/cryptoutil/cryptoUtil';
import {
getBindingCertificateConstant,
getPrivateKey,
Expand Down Expand Up @@ -363,7 +363,7 @@ export const qrLoginMachine =
sendAuthenticate: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);
Expand Down Expand Up @@ -397,7 +397,7 @@ export const qrLoginMachine =
sendConsent: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {VCMetadata} from '../../../shared/VCMetadata';
import {VC} from '../../../types/VC/ExistingMosipVC/vc';
import {
generateKeys,
isCustomSecureKeystore,
isCustomKeystore,
WalletBindingResponse,
} from '../../../shared/cryptoutil/cryptoUtil';
import {log} from 'xstate/lib/actions';
Expand Down Expand Up @@ -632,7 +632,7 @@ export const EsignetMosipVCItemMachine = model.createMachine(
),
setPublicKey: assign({
publicKey: (context, event) => {
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return (event.data as KeyPair).public;
}
return event.data as string;
Expand Down Expand Up @@ -788,7 +788,7 @@ export const EsignetMosipVCItemMachine = model.createMachine(
return walletResponse;
},
generateKeyPair: async context => {
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return await generateKeys();
}
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
Expand Down Expand Up @@ -825,7 +825,7 @@ export const EsignetMosipVCItemMachine = model.createMachine(
return vc != null;
},

isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isCustomKeystore,
},
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export interface Typegen0 {
sendWalletBindingSuccess:
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]';
setContext: 'STORE_RESPONSE';
setContext: 'GET_VC_RESPONSE' | 'STORE_RESPONSE';
setGeneratedOn: 'GET_VC_RESPONSE';
setOtp: 'INPUT_OTP';
setPinCard: 'PIN_CARD';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {verifyCredential} from '../../../shared/vcjs/verifyCredential';
import {log} from 'xstate/lib/actions';
import {
generateKeys,
isCustomSecureKeystore,
isCustomKeystore,
WalletBindingResponse,
} from '../../../shared/cryptoutil/cryptoUtil';
import {KeyPair} from 'react-native-rsa-native';
Expand Down Expand Up @@ -839,7 +839,7 @@ export const ExistingMosipVCItemMachine =
),
setPublicKey: assign({
publicKey: (context, event) => {
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return (event.data as KeyPair).public;
}
return event.data as string;
Expand Down Expand Up @@ -1235,7 +1235,7 @@ export const ExistingMosipVCItemMachine =
},

generateKeyPair: async context => {
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return await generateKeys();
}
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
Expand Down Expand Up @@ -1410,7 +1410,7 @@ export const ExistingMosipVCItemMachine =
return context.isVerified;
},

isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isCustomKeystore,
},
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export interface Typegen0 {
resetWalletBindingSuccess: 'DISMISS';
revokeVID: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
sendTamperedVc: 'TAMPERED_VC';
sendTelemetryEvents: 'STORE_RESPONSE';
sendVcUpdated: 'PIN_CARD';
sendWalletBindingSuccess:
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]'
Expand Down
11 changes: 4 additions & 7 deletions machines/issuersMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import {MY_VCS_STORE_KEY} from '../shared/constants';
import {StoreEvents} from './store';
import {AppServices} from '../shared/GlobalContext';
import NetInfo from '@react-native-community/netinfo';
import {
generateKeys,
isCustomSecureKeystore,
} from '../shared/cryptoutil/cryptoUtil';
import {generateKeys, isCustomKeystore} from '../shared/cryptoutil/cryptoUtil';
import SecureKeystore from 'react-native-secure-keystore';
import {KeyPair} from 'react-native-rsa-native';
import {ActivityLogEvents} from './activityLog';
Expand Down Expand Up @@ -449,7 +446,7 @@ export const IssuersMachine = model.createMachine(
}),
setPublicKey: assign({
publicKey: (_, event) => {
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return (event.data as KeyPair).public;
}
return event.data as string;
Expand Down Expand Up @@ -524,7 +521,7 @@ export const IssuersMachine = model.createMachine(
return await authorize(context.selectedIssuer);
},
generateKeyPair: async context => {
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return await generateKeys();
}
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
Expand Down Expand Up @@ -569,7 +566,7 @@ export const IssuersMachine = model.createMachine(
);
},
shouldFetchIssuersAgain: context => context.issuers.length === 0,
isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isCustomKeystore,
},
},
);
Expand Down
6 changes: 6 additions & 0 deletions machines/issuersMachine.typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export interface Typegen0 {
type: 'error.platform.issuersMachine.performAuthorization:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.verifyingCredential:invocation[0]': {
type: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
Expand Down Expand Up @@ -89,6 +93,8 @@ export interface Typegen0 {
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]'
| 'error.platform.issuersMachine.downloadIssuerConfig:invocation[0]'
| 'error.platform.issuersMachine.performAuthorization:invocation[0]';
sendErrorEndEvent: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
sendSuccessEndEvent: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
setCredentialWrapper: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
setError:
| 'error.platform.issuersMachine.displayIssuers:invocation[0]'
Expand Down
4 changes: 2 additions & 2 deletions machines/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import getAllConfigurations, {
import Storage from '../shared/storage';
import ShortUniqueId from 'short-unique-id';
import {__AppId} from '../shared/GlobalVariables';
import {isCustomSecureKeystore} from '../shared/cryptoutil/cryptoUtil';
import {isCustomKeystore} from '../shared/cryptoutil/cryptoUtil';

const model = createModel(
{
Expand Down Expand Up @@ -274,7 +274,7 @@ function generateAppId() {
}

function deviceSupportsHardwareKeystore() {
return isIOS() ? true : isCustomSecureKeystore();
return isIOS() ? true : isCustomKeystore;
}

type State = StateFrom<typeof settingsMachine>;
Expand Down
8 changes: 4 additions & 4 deletions machines/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
ENCRYPTION_ID,
encryptJson,
HMAC_ALIAS,
isCustomSecureKeystore,
isCustomKeystore,
} from '../shared/cryptoutil/cryptoUtil';
import {VCMetadata} from '../shared/VCMetadata';
import FileStorage, {getFilePath} from '../shared/fileStorage';
Expand Down Expand Up @@ -82,7 +82,7 @@ export const storeMachine =
events: {} as EventFrom<typeof model>,
},
id: 'store',
initial: !isCustomSecureKeystore()
initial: !isCustomKeystore
? 'gettingEncryptionKey'
: 'checkEncryptionKey',
states: {
Expand Down Expand Up @@ -441,7 +441,7 @@ export const storeMachine =
generateEncryptionKey: () => async callback => {
const randomBytes = await generateSecureRandom(32);
const randomBytesString = binaryToBase64(randomBytes);
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
const hasSetCredentials = await Keychain.setGenericPassword(
ENCRYPTION_ID,
randomBytesString,
Expand Down Expand Up @@ -475,7 +475,7 @@ export const storeMachine =
},

guards: {
isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isCustomKeystore,
},
},
);
Expand Down
21 changes: 16 additions & 5 deletions shared/cryptoutil/cryptoUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export function generateKeys(): Promise<KeyPair> {
return Promise.resolve(RSA.generateKeys(4096));
}

/**
* isCustomKeystore is a cached check of existence of a hardware keystore.
*/
export const isCustomKeystore = isCustomSecureKeystore();

export async function getJwt(
privateKey: string,
individualId: string,
Expand Down Expand Up @@ -70,7 +75,7 @@ export async function createSignature(
) {
let signature64;

if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
const key = forge.pki.privateKeyFromPem(privateKey);
const md = forge.md.sha256.create();
md.update(preHash, 'utf8');
Expand Down Expand Up @@ -98,7 +103,13 @@ export function encodeB64(str: string) {
return replaceCharactersInB64(encodedB64);
}

export function isCustomSecureKeystore() {
/**
* DO NOT USE DIRECTLY and/or REPEATEDLY in application lifeycle.
*
* This can make a call to the Android native layer hence taking up more time,
* use the isCustomKeystore constant in the app lifeycle instead.
*/
function isCustomSecureKeystore() {
return !isIOS() ? SecureKeystore.deviceSupportsHardware() : false;
}

Expand All @@ -112,7 +123,7 @@ export interface WalletBindingResponse {
export async function clear() {
try {
console.log('clearing entire storage');
if (isCustomSecureKeystore()) {
if (isCustomKeystore) {
SecureKeystore.clearKeys();
}
await Storage.clear();
Expand All @@ -131,7 +142,7 @@ export async function encryptJson(
return JSON.stringify(data);
}

if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return CryptoJS.AES.encrypt(data, encryptionKey).toString();
}
return await SecureKeystore.encryptData(ENCRYPTION_ID, data);
Expand All @@ -147,7 +158,7 @@ export async function decryptJson(
return JSON.parse(encryptedData);
}

if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return CryptoJS.AES.decrypt(encryptedData, encryptionKey).toString(
CryptoJS.enc.Utf8,
);
Expand Down
4 changes: 2 additions & 2 deletions shared/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
decryptJson,
encryptJson,
HMAC_ALIAS,
isCustomSecureKeystore,
isCustomKeystore,
} from './cryptoutil/cryptoUtil';
import {VCMetadata} from './VCMetadata';
import {ENOENT, getItem} from '../machines/store';
Expand All @@ -30,7 +30,7 @@ async function generateHmac(
encryptionKey: string,
data: string,
): Promise<string> {
if (!isCustomSecureKeystore()) {
if (!isCustomKeystore) {
return CryptoJS.HmacSHA256(encryptionKey, data).toString();
}
return await SecureKeystore.generateHmacSha(HMAC_ALIAS, data);
Expand Down
4 changes: 2 additions & 2 deletions shared/telemetry/TelemetryUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../GlobalVariables';
import {OBSRV_HOST} from 'react-native-dotenv';
import DeviceInfo from 'react-native-device-info';
import {isCustomSecureKeystore} from '../cryptoutil/cryptoUtil';
import {isCustomKeystore} from '../cryptoutil/cryptoUtil';
import * as RNLocalize from 'react-native-localize';

export function sendStartEvent(data) {
Expand Down Expand Up @@ -124,7 +124,7 @@ export function getAppInfoEventData() {
osName: DeviceInfo.getSystemName(),
osVersion: DeviceInfo.getSystemVersion(),
osApiLevel: Platform.Version.toString(),
isHardwareKeystoreSupported: isCustomSecureKeystore(),
isHardwareKeystoreSupported: isCustomKeystore,
dateTime: new Date().getTime(),
zone: RNLocalize.getTimeZone(),
offset: new Date().getTimezoneOffset() * 60 * 1000,
Expand Down

0 comments on commit b6fe6e9

Please sign in to comment.