Skip to content

Commit

Permalink
feat: added chunking for encryption flow (#123)
Browse files Browse the repository at this point in the history
fix: update chunking methods to send at least a single chunk (#126)
  • Loading branch information
TejasvOnly authored Oct 4, 2024
1 parent facce42 commit c5d6b2f
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
} 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,
Expand Down Expand Up @@ -59,21 +63,36 @@ export const decryptMessagesWithPin = async (
await helper.sendQuery({
initiate: {
walletId: params.walletId,
encryptedData: params.encryptedData,
},
});

const result = await helper.waitForResult();
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.messages?.plainData);
assertOrThrowInvalidResult(result.decryptedData);

const output: IDecryptMessagesWithPinResult = {};

const output = {
decryptedData: result.messages.plainData.map(data => data.message),
decryptedDataAsStrings: result.messages.plainData.map(data =>
Buffer.from(data.message).toString(),
),
};
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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ export enum DecryptMessagesWithPinEvent {
export type DecryptMessagesWithPinEventHandler = (
event: DecryptMessagesWithPinEvent,
) => void;
export interface IDecryptMessagesWithPinResult {
decryptedData: Uint8Array[];
decryptedDataAsStrings: string[];

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

export type IDecryptMessagesWithPinResult = Record<
number,
IDecryptMessagesWithPinResultValue
>;

export interface IDecryptMessagesWithPinParams {
walletId: Uint8Array;
encryptedData: Uint8Array;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
} 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,
Expand Down Expand Up @@ -33,7 +37,7 @@ export const encryptMessageWithPin = async (
params.walletId.length === WALLET_ID_LENGTH,
`Wallet Id should be exactly ${WALLET_ID_LENGTH} bytes`,
);
params.messages.forEach(message =>
Object.values(params.messages).forEach(message =>
assert(message.value, 'Every message should have a valid value'),
);

Expand All @@ -58,20 +62,34 @@ export const encryptMessageWithPin = async (
await helper.sendQuery({
initiate: {
walletId: params.walletId,
plainData: params.messages.map(message => ({
},
});

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 result = await helper.waitForResult();
const encryptedData = await helper.receiveInChunks(
'encryptedDataRequest',
'encryptedData',
);
const result = EncryptDataWithPinEncryptedDataStructure.decode(encryptedData);
logger.verbose('encryptMessages response', result);

assertOrThrowInvalidResult(result.result?.encryptedData);
assertOrThrowInvalidResult(result.encryptedData);

forceStatusUpdate(EncryptMessagesWithPinEvent.MESSAGE_ENCRYPTED_CARD_TAP);

logger.info('Completed');
return { encryptedPacket: result.result.encryptedData };
return { encryptedPacket: result.encryptedData };
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface InheritanceMessage {

export interface IEncryptMessagesWithPinParams {
walletId: Uint8Array;
messages: InheritanceMessage[];
messages: Record<number, InheritanceMessage>;
onEvent?: EncryptMessagesWithPinEventHandler;
}

Expand Down
94 changes: 94 additions & 0 deletions packages/app-inheritance/src/utils/operationHelper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ISDK } from '@cypherock/sdk-core';
import { DeviceAppError, DeviceAppErrorType } from '@cypherock/sdk-interfaces';
import { OnStatus } from '@cypherock/sdk-utils';
import { ChunkAck, ChunkPayload } from '../proto/generated/common';
import {
DeepPartial,
Exact,
Expand Down Expand Up @@ -38,6 +39,8 @@ export class OperationHelper<Q extends QueryKey, R extends ResultKey> {

private readonly onStatus?: OnStatus;

private static readonly CHUNK_SIZE = 2048;

constructor(params: {
sdk: ISDK;
queryKey: Q;
Expand Down Expand Up @@ -69,4 +72,95 @@ export class OperationHelper<Q extends QueryKey, R extends ResultKey> {

return resultData;
}

private static splitIntoChunks(rawData: Uint8Array): Uint8Array[] {
const chunks: Uint8Array[] = [];
const totalChunks = Math.ceil(rawData.length / OperationHelper.CHUNK_SIZE);

for (let i = 0; i < totalChunks; i += 1) {
const chunk = rawData.slice(
i * OperationHelper.CHUNK_SIZE,
i * OperationHelper.CHUNK_SIZE + OperationHelper.CHUNK_SIZE,
);
chunks.push(chunk);
}

return chunks;
}

public async sendInChunks<
RK extends keyof Exclude<Result[R], null | undefined>,
QK extends keyof Exclude<Query[Q], null | undefined>,
>(data: Uint8Array, queryKey: QK, resultKey: RK) {
const chunks = OperationHelper.splitIntoChunks(data);
let remainingSize = data.length;
let i = 0;

do {
const chunk = chunks[i] ?? [];
remainingSize -= chunk.length;

const chunkPayload: ChunkPayload = {
chunk,
chunkIndex: i,
totalChunks: Math.max(chunks.length, 1),
remainingSize,
};

await this.sendQuery({
[queryKey]: {
chunkPayload,
},
});

const result = await this.waitForResult();
assertOrThrowInvalidResult(result[resultKey]);

const { chunkAck } = result[resultKey] as {
chunkAck: ChunkAck;
};

assertOrThrowInvalidResult(chunkAck);
assertOrThrowInvalidResult(chunkAck.chunkIndex === i);
i += 1;
} while (i < chunks.length);
}

public async receiveInChunks<
RK extends keyof Exclude<Result[R], null | undefined>,
QK extends keyof Exclude<Query[Q], null | undefined>,
>(queryKey: QK, resultKey: RK): Promise<Uint8Array> {
const chunks: Uint8Array[] = [];

let index = 0;

while (1) {
const chunkAck: ChunkAck = {
chunkIndex: index,
};
await this.sendQuery({ [queryKey]: { chunkAck } });

const chunk = await this.waitForResult();
assertOrThrowInvalidResult(chunk[resultKey]);

const { chunkPayload } = chunk[resultKey] as {
chunkPayload: ChunkPayload;
};
assertOrThrowInvalidResult(
chunkPayload.chunkIndex === chunkAck.chunkIndex,
);

chunks.push(chunkPayload.chunk);

if (
chunkPayload.remainingSize === 0 &&
chunkPayload.chunkIndex + 1 >= chunkPayload.totalChunks
) {
break;
}

index += 1;
}
return new Uint8Array(Buffer.concat(chunks));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,14 @@ const authenticateWalletWithPublicKey: IAuthWalletTestCase = {
flowStatus: createFlowStatus(3, 0),
expectEventCalls: [3],
},
{
flowStatus: createFlowStatus(4, 0),
expectEventCalls: [4],
},
],
},
],
mocks: { eventCalls: [[0], [1], [2], [3]] },
mocks: { eventCalls: [[0], [1], [2], [3], [4]] },
output: {
walletBased: {
signature: hexToUint8Array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,6 @@ const commonParams = {
103, 233, 62, 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53,
86, 128, 26, 3, 187,
]),
plainData: [
{ message: Buffer.from('test'), isVerifiedOnDevice: false },
{
message: Buffer.from('something else'),
isVerifiedOnDevice: false,
},
{
message: Buffer.from('something other than something else'),
isVerifiedOnDevice: true,
},
],
},
},
}),
Expand Down
Loading

0 comments on commit c5d6b2f

Please sign in to comment.