diff --git a/packages/sdk/src/StreamrClientError.ts b/packages/sdk/src/StreamrClientError.ts index d4efbf9215..2f02226b55 100644 --- a/packages/sdk/src/StreamrClientError.ts +++ b/packages/sdk/src/StreamrClientError.ts @@ -1,3 +1,5 @@ +import { MessageID } from './protocol/MessageID' + export type StreamrClientErrorCode = 'STREAM_NOT_FOUND' | 'NODE_NOT_FOUND' | @@ -8,6 +10,7 @@ export type StreamrClientErrorCode = 'PIPELINE_ERROR' | 'UNSUPPORTED_OPERATION' | 'INVALID_STREAM_METADATA' | + 'DECRYPT_ERROR' | 'STORAGE_NODE_ERROR' | 'UNKNOWN_ERROR' @@ -21,3 +24,7 @@ export class StreamrClientError extends Error { this.name = this.constructor.name } } + +export const formMessageIdDescription = (messageId: MessageID): string => { + return JSON.stringify(messageId) +} diff --git a/packages/sdk/src/encryption/EncryptionUtil.ts b/packages/sdk/src/encryption/EncryptionUtil.ts index 0028c486fe..ae71b0f976 100644 --- a/packages/sdk/src/encryption/EncryptionUtil.ts +++ b/packages/sdk/src/encryption/EncryptionUtil.ts @@ -1,12 +1,10 @@ import crypto, { CipherKey } from 'crypto' import { StreamMessage, StreamMessageAESEncrypted } from '../protocol/StreamMessage' -import { StreamMessageError } from '../protocol/StreamMessageError' import { GroupKey } from './GroupKey' +import { formMessageIdDescription, StreamrClientError } from '../StreamrClientError' -export class DecryptError extends StreamMessageError { - constructor(streamMessage: StreamMessage, message = '') { - super(`Decrypt error: ${message}`, streamMessage) - } +export const createDecryptError = (message: string, streamMessage: StreamMessage): StreamrClientError => { + return new StreamrClientError(`${message} (messageId=${formMessageIdDescription(streamMessage.messageId)})`, 'DECRYPT_ERROR') } export const INITIALIZATION_VECTOR_LENGTH = 16 @@ -53,17 +51,16 @@ export class EncryptionUtil { let content: Uint8Array try { content = this.decryptWithAES(streamMessage.content, groupKey.data) - } catch (err) { - throw new DecryptError(streamMessage, err.stack) + } catch { + throw createDecryptError('AES decryption failed', streamMessage) } let newGroupKey: GroupKey | undefined = undefined if (streamMessage.newGroupKey) { try { newGroupKey = groupKey.decryptNextGroupKey(streamMessage.newGroupKey) - } catch (err) { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new DecryptError(streamMessage, `Could not decrypt new group key: ${err.stack}`) + } catch { + throw createDecryptError('Could not decrypt new encryption key', streamMessage) } } diff --git a/packages/sdk/src/encryption/decrypt.ts b/packages/sdk/src/encryption/decrypt.ts index 4fe447dd18..2221c5364b 100644 --- a/packages/sdk/src/encryption/decrypt.ts +++ b/packages/sdk/src/encryption/decrypt.ts @@ -1,5 +1,5 @@ import { DestroySignal } from '../DestroySignal' -import { DecryptError, EncryptionUtil } from '../encryption/EncryptionUtil' +import { createDecryptError, EncryptionUtil } from '../encryption/EncryptionUtil' import { GroupKey } from '../encryption/GroupKey' import { GroupKeyManager } from '../encryption/GroupKeyManager' import { EncryptionType, StreamMessage, StreamMessageAESEncrypted } from '../protocol/StreamMessage' @@ -22,12 +22,11 @@ export const decrypt = async ( streamMessage.groupKeyId, streamMessage.getPublisherId() ) - } catch (e: any) { + } catch { if (destroySignal.isDestroyed()) { return streamMessage } - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new DecryptError(streamMessage, `Could not get GroupKey ${streamMessage.groupKeyId}: ${e.message}`) + throw createDecryptError(`Could not get encryption key ${streamMessage.groupKeyId}`, streamMessage) } if (destroySignal.isDestroyed()) { return streamMessage diff --git a/packages/sdk/test/integration/resend-with-existing-key.test.ts b/packages/sdk/test/integration/resend-with-existing-key.test.ts index e773378e27..dfb3b0dcb2 100644 --- a/packages/sdk/test/integration/resend-with-existing-key.test.ts +++ b/packages/sdk/test/integration/resend-with-existing-key.test.ts @@ -4,12 +4,12 @@ import { fastWallet } from '@streamr/test-utils' import { collect, toEthereumAddress, toStreamID, toUserId } from '@streamr/utils' import { Stream } from '../../src/Stream' import { StreamrClient } from '../../src/StreamrClient' -import { DecryptError } from '../../src/encryption/EncryptionUtil' import { GroupKey } from '../../src/encryption/GroupKey' import { StreamPermission } from '../../src/permission' import { FakeEnvironment } from '../test-utils/fake/FakeEnvironment' import { FakeStorageNode } from '../test-utils/fake/FakeStorageNode' import { createMockMessage, createRelativeTestStreamId, getLocalGroupKeyStore } from '../test-utils/utils' +import { StreamrClientError } from '../../src/StreamrClientError' /* * A subscriber has some GroupKeys in the local store and reads historical data @@ -68,7 +68,8 @@ describe('resend with existing key', () => { await collect(messageStream) expect(onError).toBeCalled() const error = onError.mock.calls[0][0] - expect(error).toBeInstanceOf(DecryptError) + expect(error).toBeInstanceOf(StreamrClientError) + expect(error.code).toBe('DECRYPT_ERROR') } beforeEach(async () => { diff --git a/packages/sdk/test/integration/revoke-permissions.test.ts b/packages/sdk/test/integration/revoke-permissions.test.ts index a751774d60..ceeb32a26c 100644 --- a/packages/sdk/test/integration/revoke-permissions.test.ts +++ b/packages/sdk/test/integration/revoke-permissions.test.ts @@ -159,7 +159,7 @@ describe('revoke permissions', () => { break } } - }).rejects.toThrow(/not a subscriber|Could not get GroupKey/) + }).rejects.toThrow(/not a subscriber|Could not get encryption key/) } finally { clearTimeout(t) // run in finally to ensure publish promise finishes before diff --git a/packages/sdk/test/integration/update-encryption-key.test.ts b/packages/sdk/test/integration/update-encryption-key.test.ts index c7e3c89609..e1be54495a 100644 --- a/packages/sdk/test/integration/update-encryption-key.test.ts +++ b/packages/sdk/test/integration/update-encryption-key.test.ts @@ -2,12 +2,12 @@ import 'reflect-metadata' import { StreamPartID, StreamPartIDUtils, until } from '@streamr/utils' import { Message } from '../../src/Message' -import { DecryptError } from '../../src/encryption/EncryptionUtil' import { GroupKey } from '../../src/encryption/GroupKey' import { StreamPermission } from '../../src/permission' import { nextValue } from '../../src/utils/iterators' import { StreamrClient } from './../../src/StreamrClient' import { FakeEnvironment } from './../test-utils/fake/FakeEnvironment' +import { StreamrClientError } from '../../src/StreamrClientError' /* * Subscriber has subscribed to a stream, and the publisher updates the encryption key for that stream. @@ -161,7 +161,8 @@ describe('update encryption key', () => { mockId: 2 }) await until(() => onError.mock.calls.length > 0, 10 * 1000) - expect(onError.mock.calls[0][0]).toBeInstanceOf(DecryptError) + expect(onError.mock.calls[0][0]).toBeInstanceOf(StreamrClientError) + expect(onError.mock.calls[0][0].code).toBe('DECRYPT_ERROR') }, 10 * 1000) }) }) diff --git a/packages/sdk/test/unit/Decrypt.test.ts b/packages/sdk/test/unit/Decrypt.test.ts index b516041d4e..21001b9ed1 100644 --- a/packages/sdk/test/unit/Decrypt.test.ts +++ b/packages/sdk/test/unit/Decrypt.test.ts @@ -10,6 +10,7 @@ import { GroupKeyManager } from '../../src/encryption/GroupKeyManager' import { decrypt } from '../../src/encryption/decrypt' import { createGroupKeyManager, createMockMessage } from '../test-utils/utils' import { EncryptionType, StreamMessage, StreamMessageAESEncrypted } from './../../src/protocol/StreamMessage' +import { formMessageIdDescription } from '../../src/StreamrClientError' describe('Decrypt', () => { @@ -54,6 +55,9 @@ describe('Decrypt', () => { msg as StreamMessageAESEncrypted, groupKeyManager, destroySignal) - }).rejects.toThrow(`Decrypt error: Could not get GroupKey ${groupKey.id}`) + }).rejects.toThrowStreamrError({ + code: 'DECRYPT_ERROR', + message: `Could not get encryption key ${groupKey.id} (messageId=${formMessageIdDescription(msg.messageId)})` + }) }) }) diff --git a/packages/sdk/test/unit/EncryptionUtil.test.ts b/packages/sdk/test/unit/EncryptionUtil.test.ts index c1b3838bb6..4c50209575 100644 --- a/packages/sdk/test/unit/EncryptionUtil.test.ts +++ b/packages/sdk/test/unit/EncryptionUtil.test.ts @@ -5,6 +5,7 @@ import { GroupKey } from '../../src/encryption/GroupKey' import { createMockMessage } from '../test-utils/utils' import { EncryptedGroupKey } from './../../src/protocol/EncryptedGroupKey' import { StreamMessage, StreamMessageAESEncrypted } from './../../src/protocol/StreamMessage' +import { formMessageIdDescription } from '../../src/StreamrClientError' const STREAM_ID = toStreamID('streamId') @@ -61,6 +62,9 @@ describe('EncryptionUtil', () => { ...msg, newGroupKey: new EncryptedGroupKey('mockId', hexToBinary('0x1234')) }) as StreamMessageAESEncrypted - expect(() => EncryptionUtil.decryptStreamMessage(msg2, key)).toThrow('Could not decrypt new group key') + expect(() => EncryptionUtil.decryptStreamMessage(msg2, key)).toThrowStreamrError({ + code: 'DECRYPT_ERROR', + message: `Could not decrypt new encryption key (messageId=${formMessageIdDescription(msg2.messageId)})` + }) }) }) diff --git a/packages/sdk/test/unit/messagePipeline.test.ts b/packages/sdk/test/unit/messagePipeline.test.ts index f3cd1bcbea..6aa5b24d3a 100644 --- a/packages/sdk/test/unit/messagePipeline.test.ts +++ b/packages/sdk/test/unit/messagePipeline.test.ts @@ -9,7 +9,7 @@ import { StrictStreamrClientConfig } from '../../src/Config' import { DestroySignal } from '../../src/DestroySignal' import { ERC1271ContractFacade } from '../../src/contracts/ERC1271ContractFacade' import { StreamRegistry } from '../../src/contracts/StreamRegistry' -import { DecryptError, EncryptionUtil } from '../../src/encryption/EncryptionUtil' +import { EncryptionUtil } from '../../src/encryption/EncryptionUtil' import { GroupKey } from '../../src/encryption/GroupKey' import { GroupKeyManager } from '../../src/encryption/GroupKeyManager' import { LitProtocolFacade } from '../../src/encryption/LitProtocolFacade' @@ -22,6 +22,7 @@ import { PushPipeline } from '../../src/utils/PushPipeline' import { mockLoggerFactory } from '../test-utils/utils' import { MessageID } from './../../src/protocol/MessageID' import { ContentType, EncryptionType, SignatureType, StreamMessage, StreamMessageType } from './../../src/protocol/StreamMessage' +import { StreamrClientError } from '../../src/StreamrClientError' const CONTENT = { foo: 'bar' @@ -168,8 +169,9 @@ describe('messagePipeline', () => { const output = await collect(pipeline) expect(onError).toBeCalledTimes(1) const error = onError.mock.calls[0][0] - expect(error).toBeInstanceOf(DecryptError) - expect(error.message).toMatch(/timed out/) + expect(error).toBeInstanceOf(StreamrClientError) + expect(error.code).toBe('DECRYPT_ERROR') + expect(error.message).toMatch(/Could not get encryption key/) expect(output).toEqual([]) expect(streamRegistry.invalidatePermissionCaches).toBeCalledTimes(1) expect(streamRegistry.invalidatePermissionCaches).toBeCalledWith(StreamPartIDUtils.getStreamID(streamPartId))