Skip to content

Commit

Permalink
feat: added encryption and decryption operations in inheritance app (#…
Browse files Browse the repository at this point in the history
…120)

Co-authored-by: TejasvOnly <[email protected]>
Co-authored-by: Md Irshad Ansari <[email protected]>
Co-authored-by: Vaibhav Sethia <[email protected]>
fix: remove inbetween acks from decryption flow (#122)
fix: update chunking methods to send at least a single chunk (#126)
  • Loading branch information
4 people authored Nov 12, 2024
1 parent d090cc9 commit 9b2748a
Show file tree
Hide file tree
Showing 31 changed files with 8,270 additions and 2,493 deletions.
16 changes: 16 additions & 0 deletions packages/app-inheritance/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ export class InheritanceApp {
return new InheritanceApp(sdk);
}

public async encryptMessagesWithPin(
params: operations.IEncryptMessagesWithPinParams,
) {
return this.sdk.runOperation(() =>
operations.encryptMessageWithPin(this.sdk, params),
);
}

public async decryptMessagesWithPin(
params: operations.IDecryptMessagesWithPinParams,
) {
return this.sdk.runOperation(() =>
operations.decryptMessagesWithPin(this.sdk, params),
);
}

public async authWallet(params: operations.IAuthWalletParams) {
return this.sdk.runOperation(() => operations.authWallet(this.sdk, params));
}
Expand Down
2 changes: 2 additions & 0 deletions packages/app-inheritance/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './appId';
export * from './walletId';
1 change: 1 addition & 0 deletions packages/app-inheritance/src/constants/walletId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const WALLET_ID_LENGTH = 32;
44 changes: 32 additions & 12 deletions packages/app-inheritance/src/operations/authWallet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,49 @@ import {
createStatusListener,
} from '@cypherock/sdk-utils';
import { APP_VERSION } from '../../constants/appId';
import { IWalletAuthResultResponse } from '../../proto/generated/types';
import {
assertOrThrowInvalidResult,
OperationHelper,
logger as rootLogger,
} from '../../utils';
import { IAuthWalletParams, WALLET_ID_LENGTH, WalletAuthEvent } from './types';
import { WalletAuthStatus } from '../../proto/generated/inheritance/wallet_auth';
import { IAuthWalletParams, AuthWalletEvent, AuthWalletType } from './types';
import { WALLET_ID_LENGTH } from '../../constants';
import { AuthWalletStatus, IAuthWalletResultResponse } from '../../types';

export * from './types';

const logger = createLoggerWithPrefix(rootLogger, 'authWallet');

const typeMap: Record<
AuthWalletType,
{ doSeedBased: boolean; doWalletBased: boolean }
> = {
'seed-based': {
doSeedBased: true,
doWalletBased: false,
},
'wallet-based': {
doSeedBased: false,
doWalletBased: true,
},
'seed-and-wallet-based': {
doSeedBased: true,
doWalletBased: true,
},
};

export const authWallet = async (
sdk: ISDK,
params: IAuthWalletParams,
): Promise<IWalletAuthResultResponse> => {
): Promise<IAuthWalletResultResponse> => {
assert(params, 'Params should be defined');
assert(params.walletId, 'walletId should be defined');
assert(params.challenge, 'challenge should be defined');
assert(
typeof params.isPublicKey === 'boolean',
typeof params.withPublicKey === 'boolean',
'isPublicKey should be defined',
);
assert(typeMap[params.type] !== undefined, 'invaild type provided');
assert(
params.walletId.length === WALLET_ID_LENGTH,
`Wallet Id should be exactly ${WALLET_ID_LENGTH} bytes`,
Expand All @@ -38,33 +57,34 @@ export const authWallet = async (

logger.info('Started', { ...params, onEvent: undefined });
const { forceStatusUpdate, onStatus } = createStatusListener({
enums: WalletAuthEvent,
operationEnums: WalletAuthStatus,
enums: AuthWalletEvent,
operationEnums: AuthWalletStatus,
onEvent: params.onEvent,
logger,
});

const helper = new OperationHelper({
sdk,
queryKey: 'walletAuth',
resultKey: 'walletAuth',
queryKey: 'authWallet',
resultKey: 'authWallet',
onStatus,
});

await helper.sendQuery({
initiate: {
challenge: params.challenge,
walletId: params.walletId,
isPublickey: params.isPublicKey,
withPublicKey: params.withPublicKey,
...typeMap[params.type],
},
});

const result = await helper.waitForResult();
logger.verbose('WalletAuthResponse', result);
logger.verbose('AuthWalletResponse', result);

assertOrThrowInvalidResult(result.result);

forceStatusUpdate(WalletAuthEvent.CARD_TAP);
forceStatusUpdate(AuthWalletEvent.WALLET_BASED_CARD_TAP);

logger.info('Completed');
return result.result;
Expand Down
18 changes: 13 additions & 5 deletions packages/app-inheritance/src/operations/authWallet/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
export enum WalletAuthEvent {
export enum AuthWalletEvent {
INIT = 0,
CARD_TAP = 1,
CONFIRMED = 1,
SEED_BASED_CARD_TAP = 2,
CARD_PAIRING_CARD_TAP = 3,
WALLET_BASED_CARD_TAP = 4,
}

export type AuthWalletEventHandler = (event: WalletAuthEvent) => void;
export const WALLET_ID_LENGTH = 32;
export type AuthWalletType =
| 'seed-based'
| 'wallet-based'
| 'seed-and-wallet-based';

export type AuthWalletEventHandler = (event: AuthWalletEvent) => void;

export interface IAuthWalletParams {
challenge: Uint8Array;
walletId: Uint8Array;
isPublicKey: boolean;
withPublicKey: boolean;
type: AuthWalletType;
onEvent?: AuthWalletEventHandler;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { ISDK } from '@cypherock/sdk-core';
import {
assert,
createLoggerWithPrefix,
createStatusListener,
} from '@cypherock/sdk-utils';
import { WALLET_ID_LENGTH } from '../../constants';
import { APP_VERSION } from '../../constants/appId';
import {
DecryptDataWithPinDecryptedDataStructure,
DecryptDataWithPinEncryptedDataStructure,
} from '../../proto/generated/inheritance/decrypt_data_with_pin';
import { DecryptDataStatus } from '../../types';
import {
assertOrThrowInvalidResult,
OperationHelper,
logger as rootLogger,
} from '../../utils';
import {
DecryptMessagesWithPinEvent,
IDecryptMessagesWithPinParams,
IDecryptMessagesWithPinResult,
} from './types';

export * from './types';

const logger = createLoggerWithPrefix(rootLogger, 'decryptMessages');

export const decryptMessagesWithPin = async (
sdk: ISDK,
params: IDecryptMessagesWithPinParams,
): Promise<IDecryptMessagesWithPinResult> => {
assert(params, 'Params should be defined');
assert(params.walletId, 'walletId should be defined');
assert(
params.walletId.length === WALLET_ID_LENGTH,
`Wallet Id should be exactly ${WALLET_ID_LENGTH} bytes`,
);
assert(params.encryptedData, 'data should be defined');
assert(
params.encryptedData.length > 0,
'At least one message required to decrypt',
);

await sdk.checkAppCompatibility(APP_VERSION);

logger.info('Started', { ...params, onEvent: undefined });

const { forceStatusUpdate, onStatus } = createStatusListener({
enums: DecryptMessagesWithPinEvent,
operationEnums: DecryptDataStatus,
onEvent: params.onEvent,
logger,
});

const helper = new OperationHelper({
sdk,
queryKey: 'decrypt',
resultKey: 'decrypt',
onStatus,
});

await helper.sendQuery({
initiate: {
walletId: params.walletId,
},
});

await helper.waitForResult();
logger.verbose('decryptMessages confirmed');

const rawData = DecryptDataWithPinEncryptedDataStructure.encode(
DecryptDataWithPinEncryptedDataStructure.create({
data: params.encryptedData,
}),
).finish();
await helper.sendInChunks(rawData, 'encryptedData', 'dataAccepted');

const decryptedData = await helper.receiveInChunks(
'decryptedDataRequest',
'decryptedData',
);
const result = DecryptDataWithPinDecryptedDataStructure.decode(decryptedData);
logger.verbose('decryptMessages response', result);

assertOrThrowInvalidResult(result.decryptedData);

const output: IDecryptMessagesWithPinResult = {};

for (const data of result.decryptedData) {
output[data.tag] = {
data: data.message,
dataAsString: Buffer.from(data.message).toString(),
};
}

forceStatusUpdate(DecryptMessagesWithPinEvent.PIN_VERIFIED);
logger.info('Completed');
return output;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export enum DecryptMessagesWithPinEvent {
INIT = 0,
CONFIRMED = 1,
MESSAGE_DECRYPTED_CARD_TAP = 2,
PIN_VERIFIED = 3,
}

export type DecryptMessagesWithPinEventHandler = (
event: DecryptMessagesWithPinEvent,
) => void;

export interface IDecryptMessagesWithPinResultValue {
data: Uint8Array;
dataAsString: string;
}

export type IDecryptMessagesWithPinResult = Record<
number,
IDecryptMessagesWithPinResultValue
>;

export interface IDecryptMessagesWithPinParams {
walletId: Uint8Array;
encryptedData: Uint8Array;
onEvent?: DecryptMessagesWithPinEventHandler;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { ISDK } from '@cypherock/sdk-core';
import {
assert,
createLoggerWithPrefix,
createStatusListener,
} from '@cypherock/sdk-utils';
import { WALLET_ID_LENGTH } from '../../constants';
import { APP_VERSION } from '../../constants/appId';
import {
EncryptDataWithPinEncryptedDataStructure,
EncryptDataWithPinPlainDataStructure,
} from '../../proto/generated/inheritance/encrypt_data_with_pin';
import { EncryptDataStatus } from '../../types';
import {
assertOrThrowInvalidResult,
OperationHelper,
logger as rootLogger,
} from '../../utils';
import {
EncryptMessagesWithPinEvent,
IEncryptMessagesWithPinParams,
IEncryptMessagesWithPinResult,
} from './types';

export * from './types';

const logger = createLoggerWithPrefix(rootLogger, 'encryptMessages');

export const encryptMessageWithPin = async (
sdk: ISDK,
params: IEncryptMessagesWithPinParams,
): Promise<IEncryptMessagesWithPinResult> => {
assert(params, 'Params should be defined');
assert(params.walletId, 'walletId should be defined');
assert(params.messages, 'messages should be defined');
assert(
params.walletId.length === WALLET_ID_LENGTH,
`Wallet Id should be exactly ${WALLET_ID_LENGTH} bytes`,
);
Object.values(params.messages).forEach(message =>
assert(message.value, 'Every message should have a valid value'),
);

await sdk.checkAppCompatibility(APP_VERSION);

logger.info('Started', { ...params, onEvent: undefined });

const { forceStatusUpdate, onStatus } = createStatusListener({
enums: EncryptMessagesWithPinEvent,
operationEnums: EncryptDataStatus,
onEvent: params.onEvent,
logger,
});

const helper = new OperationHelper({
sdk,
queryKey: 'encrypt',
resultKey: 'encrypt',
onStatus,
});

await helper.sendQuery({
initiate: {
walletId: params.walletId,
},
});

await helper.waitForResult();
logger.verbose('encryptMessages confirmed');

const rawData = EncryptDataWithPinPlainDataStructure.encode(
EncryptDataWithPinPlainDataStructure.create({
data: Object.entries(params.messages).map(([key, message]) => ({
message: Buffer.from(message.value),
isVerifiedOnDevice: message.verifyOnDevice ?? false,
tag: parseInt(key, 10),
})),
}),
).finish();
await helper.sendInChunks(rawData, 'plainData', 'dataAccepted');

const encryptedData = await helper.receiveInChunks(
'encryptedDataRequest',
'encryptedData',
);
const result = EncryptDataWithPinEncryptedDataStructure.decode(encryptedData);
logger.verbose('encryptMessages response', result);

assertOrThrowInvalidResult(result.encryptedData);

forceStatusUpdate(EncryptMessagesWithPinEvent.MESSAGE_ENCRYPTED_CARD_TAP);

logger.info('Completed');
return { encryptedPacket: result.encryptedData };
};
Loading

0 comments on commit 9b2748a

Please sign in to comment.