Skip to content
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

feat: added encryption and decryption operations in inheritance app #120

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading