diff --git a/packages/app-inheritance/src/operations/decryptMessagesWithPin/index.ts b/packages/app-inheritance/src/operations/decryptMessagesWithPin/index.ts index aa6d924e..91680b5e 100644 --- a/packages/app-inheritance/src/operations/decryptMessagesWithPin/index.ts +++ b/packages/app-inheritance/src/operations/decryptMessagesWithPin/index.ts @@ -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, @@ -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'); diff --git a/packages/app-inheritance/src/operations/decryptMessagesWithPin/types.ts b/packages/app-inheritance/src/operations/decryptMessagesWithPin/types.ts index 0b0b49c2..4d88ca2c 100644 --- a/packages/app-inheritance/src/operations/decryptMessagesWithPin/types.ts +++ b/packages/app-inheritance/src/operations/decryptMessagesWithPin/types.ts @@ -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; diff --git a/packages/app-inheritance/src/operations/encryptMessagesWithPin/index.ts b/packages/app-inheritance/src/operations/encryptMessagesWithPin/index.ts index d1c7bb65..85b28c89 100644 --- a/packages/app-inheritance/src/operations/encryptMessagesWithPin/index.ts +++ b/packages/app-inheritance/src/operations/encryptMessagesWithPin/index.ts @@ -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, @@ -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'), ); @@ -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 }; }; diff --git a/packages/app-inheritance/src/operations/encryptMessagesWithPin/types.ts b/packages/app-inheritance/src/operations/encryptMessagesWithPin/types.ts index 0835fc9b..d7b4a8b8 100644 --- a/packages/app-inheritance/src/operations/encryptMessagesWithPin/types.ts +++ b/packages/app-inheritance/src/operations/encryptMessagesWithPin/types.ts @@ -17,7 +17,7 @@ export interface InheritanceMessage { export interface IEncryptMessagesWithPinParams { walletId: Uint8Array; - messages: InheritanceMessage[]; + messages: Record; onEvent?: EncryptMessagesWithPinEventHandler; } diff --git a/packages/app-inheritance/src/utils/operationHelper.ts b/packages/app-inheritance/src/utils/operationHelper.ts index 0da8dd71..aa0bf954 100644 --- a/packages/app-inheritance/src/utils/operationHelper.ts +++ b/packages/app-inheritance/src/utils/operationHelper.ts @@ -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, @@ -38,6 +39,8 @@ export class OperationHelper { private readonly onStatus?: OnStatus; + private static readonly CHUNK_SIZE = 2048; + constructor(params: { sdk: ISDK; queryKey: Q; @@ -69,4 +72,95 @@ export class OperationHelper { 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, + QK extends keyof Exclude, + >(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, + QK extends keyof Exclude, + >(queryKey: QK, resultKey: RK): Promise { + 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)); + } } diff --git a/packages/app-inheritance/tests/02.authWallet/__fixtures__/valid.ts b/packages/app-inheritance/tests/02.authWallet/__fixtures__/valid.ts index 2466a695..849cf033 100644 --- a/packages/app-inheritance/tests/02.authWallet/__fixtures__/valid.ts +++ b/packages/app-inheritance/tests/02.authWallet/__fixtures__/valid.ts @@ -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( diff --git a/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/error.ts b/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/error.ts index 36832483..231db9af 100644 --- a/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/error.ts +++ b/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/error.ts @@ -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, - }, - ], }, }, }), diff --git a/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/valid.ts b/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/valid.ts index 76d216a1..85e3d101 100644 --- a/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/valid.ts +++ b/packages/app-inheritance/tests/03.encryptMessagesWithPin/__fixtures__/valid.ts @@ -1,15 +1,15 @@ -import { createFlowStatus } from '@cypherock/sdk-utils'; +import { createFlowStatus, hexToUint8Array } from '@cypherock/sdk-utils'; import { IEncryptMessagesTestCase } from './types'; -import { Query, Result } from '../../../src/proto/generated/inheritance/core'; +import { Query } from '../../../src/proto/generated/inheritance/core'; -const encryptSingeMessage: IEncryptMessagesTestCase = { - name: 'Encrypt single message', +const encryptEmptyMessage: IEncryptMessagesTestCase = { + name: 'Encrypt empty message', params: { walletId: new Uint8Array([ 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, 103, 233, 62, 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53, 86, 128, 26, 3, 187, ]), - messages: [{ value: 'test' }], + messages: {}, }, queries: [ { @@ -24,30 +24,25 @@ const encryptSingeMessage: IEncryptMessagesTestCase = { 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 }, - ], }, }, }), ).finish(), ), }, + { + name: 'send chunk', + data: hexToUint8Array('120612040a022001'), + }, + { + name: 'request chunk', + data: hexToUint8Array('12041a020a00'), + }, ], results: [ { - name: 'result', - data: Uint8Array.from( - Result.encode( - Result.create({ - encrypt: { - result: { - encryptedData: new Uint8Array([0]), // dummy data, encrypted data actually depends on session - }, - }, - }), - ).finish(), - ), + name: 'confirmation', + data: hexToUint8Array('12020a00'), statuses: [ { flowStatus: createFlowStatus(0, 0), @@ -71,103 +66,26 @@ const encryptSingeMessage: IEncryptMessagesTestCase = { }, ], }, - ], - mocks: { eventCalls: [[0], [1], [2], [3], [4]] }, - output: { encryptedPacket: new Uint8Array([0]) }, -}; - -const encryptMultipleMessages: IEncryptMessagesTestCase = { - name: 'Encrypt multiple messages', - params: { - walletId: new Uint8Array([ - 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, 103, 233, 62, - 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53, 86, 128, 26, 3, 187, - ]), - messages: [ - { value: 'test' }, - { value: 'something else' }, - { - value: 'something other than something else', - verifyOnDevice: true, - }, - ], - }, - queries: [ { - name: 'initiate query', - data: Uint8Array.from( - Query.encode( - Query.create({ - encrypt: { - initiate: { - walletId: new Uint8Array([ - 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, - 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, - }, - ], - }, - }, - }), - ).finish(), - ), + name: 'chunk ack', + data: hexToUint8Array('120412020a00'), }, - ], - results: [ { name: 'result', - data: Uint8Array.from( - Result.encode( - Result.create({ - encrypt: { - result: { - encryptedData: new Uint8Array([0]), // dummy data, encrypted data actually depends on session - }, - }, - }), - ).finish(), + data: hexToUint8Array( + '122a1a280a260a220a20cb5bb0f4b2434bae6f6f49700a55cd4f14b1fd682e0506df66da8a2d3d18094d2001', ), - statuses: [ - { - flowStatus: createFlowStatus(0, 0), - expectEventCalls: [0], - }, - { - flowStatus: createFlowStatus(1, 0), - expectEventCalls: [1], - }, - { - flowStatus: createFlowStatus(2, 0), - expectEventCalls: [2], - }, - { - flowStatus: createFlowStatus(3, 0), - expectEventCalls: [3], - }, - { - flowStatus: createFlowStatus(4, 0), - expectEventCalls: [4], - }, - ], }, ], mocks: { eventCalls: [[0], [1], [2], [3], [4]] }, - output: { encryptedPacket: new Uint8Array([0]) }, + output: { + encryptedPacket: hexToUint8Array( + 'cb5bb0f4b2434bae6f6f49700a55cd4f14b1fd682e0506df66da8a2d3d18094d', + ), + }, }; -const valid: IEncryptMessagesTestCase[] = [ - encryptSingeMessage, - encryptMultipleMessages, -]; +// TODO: Add more cases +const valid: IEncryptMessagesTestCase[] = [encryptEmptyMessage]; export default valid; diff --git a/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/error.ts b/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/error.ts index 1b65b373..65b47998 100644 --- a/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/error.ts +++ b/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/error.ts @@ -22,8 +22,6 @@ const commonParams = { Query.create({ decrypt: { initiate: { - encryptedData: new Uint8Array([0]), - walletId: new Uint8Array([ 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, 103, 233, 62, 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53, diff --git a/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/valid.ts b/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/valid.ts index 2160af41..b6a80956 100644 --- a/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/valid.ts +++ b/packages/app-inheritance/tests/04.decryptMessagesWithPin/__fixtures__/valid.ts @@ -1,11 +1,13 @@ -import { createFlowStatus } from '@cypherock/sdk-utils'; +import { createFlowStatus, hexToUint8Array } from '@cypherock/sdk-utils'; import { IDecryptMessagesTestCase } from './types'; -import { Query, Result } from '../../../src/proto/generated/inheritance/core'; +import { Query } from '../../../src/proto/generated/inheritance/core'; -const decryptSingeMessage: IDecryptMessagesTestCase = { - name: 'Decrypt single message', +const decryptEmptyMessage: IDecryptMessagesTestCase = { + name: 'Decrypt empty message', params: { - encryptedData: new Uint8Array([0]), // TODO: update data + encryptedData: hexToUint8Array( + 'cb5bb0f4b2434bae6f6f49700a55cd4f14b1fd682e0506df66da8a2d3d18094d', + ), walletId: new Uint8Array([ 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, 103, 233, 62, 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53, 86, 128, 26, 3, 187, @@ -19,7 +21,6 @@ const decryptSingeMessage: IDecryptMessagesTestCase = { Query.create({ decrypt: { initiate: { - encryptedData: new Uint8Array([0]), walletId: new Uint8Array([ 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, 103, 233, 62, 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53, @@ -31,101 +32,21 @@ const decryptSingeMessage: IDecryptMessagesTestCase = { ).finish(), ), }, - ], - results: [ { - name: 'messages', - data: Uint8Array.from( - Result.encode( - Result.create({ - decrypt: { - messages: { - plainData: [ - { - message: Buffer.from('test'), - }, - ], - }, - }, - }), - ).finish(), + name: 'send chunk', + data: hexToUint8Array( + '1a2a12280a260a220a20cb5bb0f4b2434bae6f6f49700a55cd4f14b1fd682e0506df66da8a2d3d18094d2001', ), - statuses: [ - { - flowStatus: createFlowStatus(0, 0), - expectEventCalls: [0], - }, - { - flowStatus: createFlowStatus(1, 0), - expectEventCalls: [1], - }, - { - flowStatus: createFlowStatus(2, 0), - expectEventCalls: [2], - }, - { - flowStatus: createFlowStatus(3, 0), - expectEventCalls: [3], - }, - ], }, - ], - mocks: { eventCalls: [[0], [1], [2], [3]] }, - output: { - decryptedData: [new Uint8Array(Buffer.from('test'))], - decryptedDataAsStrings: ['test'], - }, -}; - -const decryptMultipleMessages: IDecryptMessagesTestCase = { - name: 'Decrypt multiple messages', - params: { - encryptedData: new Uint8Array([0]), - walletId: new Uint8Array([ - 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, 103, 233, 62, - 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53, 86, 128, 26, 3, 187, - ]), - }, - queries: [ { - name: 'initiate query', - data: Uint8Array.from( - Query.encode( - Query.create({ - decrypt: { - initiate: { - encryptedData: new Uint8Array([0]), - - walletId: new Uint8Array([ - 199, 89, 252, 26, 32, 135, 183, 211, 90, 220, 38, 17, 160, - 103, 233, 62, 110, 172, 92, 20, 35, 250, 190, 146, 62, 8, 53, - 86, 128, 26, 3, 187, - ]), - }, - }, - }), - ).finish(), - ), + name: 'request chunk', + data: hexToUint8Array('1a041a020a00'), }, ], results: [ { - name: 'result', - data: Uint8Array.from( - Result.encode( - Result.create({ - decrypt: { - messages: { - plainData: [ - { message: Buffer.from('test') }, - { message: Buffer.from('new message') }, - { message: Buffer.from('another message') }, - ], - }, - }, - }), - ).finish(), - ), + name: 'send confirmation', + data: hexToUint8Array('1a020a00'), statuses: [ { flowStatus: createFlowStatus(0, 0), @@ -145,19 +66,20 @@ const decryptMultipleMessages: IDecryptMessagesTestCase = { }, ], }, + { + name: 'send chunk', + data: hexToUint8Array('1a0412020a00'), + }, + { + name: 'request chunk', + data: hexToUint8Array('1a041a020a00'), + }, ], mocks: { eventCalls: [[0], [1], [2], [3]] }, - output: { - decryptedData: ['test', 'new message', 'another message'].map( - x => new Uint8Array(Buffer.from(x)), - ), - decryptedDataAsStrings: ['test', 'new message', 'another message'], - }, + output: {}, }; -const valid: IDecryptMessagesTestCase[] = [ - decryptSingeMessage, - decryptMultipleMessages, -]; +// TODO: Add more cases +const valid: IDecryptMessagesTestCase[] = [decryptEmptyMessage]; export default valid; diff --git a/submodules/common b/submodules/common index 1b74ba33..4f25bf7c 160000 --- a/submodules/common +++ b/submodules/common @@ -1 +1 @@ -Subproject commit 1b74ba33c162a9f3a91bb6a33b7edf0a5d08eba2 +Subproject commit 4f25bf7c59b47fcf243029e5222bf31db2c80546