feat(protocol): trezor-push-notification decode

This commit is contained in:
karliatto
2025-09-26 11:05:19 +02:00
parent 5218f5d74b
commit 70f0b76f2e
6 changed files with 123 additions and 0 deletions

View File

@@ -1 +1,2 @@
export const PROTOCOL_MALFORMED = 'Malformed protocol format' as const;
export const PROTOCOL_MISSMATCH_VERSION = 'Protocol missmatch version' as const;

View File

@@ -3,5 +3,6 @@ export * as v2 from './protocol-v2';
export * as bridge from './protocol-bridge';
export * as thp from './protocol-thp';
export * as trzd from './protocol-trzd';
export * as tpn from './protocol-tpn';
export * from './errors';
export * from './types';

View File

@@ -0,0 +1,2 @@
export const MESSAGE_LENGTH = 3;
export const TPN_VERSION = 1;

View File

@@ -0,0 +1,51 @@
import { PROTOCOL_MALFORMED, PROTOCOL_MISSMATCH_VERSION } from '../errors';
import { MESSAGE_LENGTH, TPN_VERSION } from './constants';
export enum Version {
v1 = 1,
}
// https://github.com/trezor/trezor-firmware/blob/a779f46b0eb5bf19e7d0594cd618f981a2791022/core/embed/sys/notify/inc/sys/notify.h#L41
export enum TrezorPushNotificationType {
BOOT = 0 /**< Device boot/startup notification */,
UNLOCK = 1 /**< Device unlocked and ready to accept messages */,
LOCK = 2 /**< Device hard-locked and won't accept messages */,
DISCONNECT = 3 /**< User-initiated disconnect from host */,
SETTING_CHANGE = 4 /**< Change of settings */,
SOFTLOCK = 5 /**< Device soft-locked (e.g., after clicking power button) */,
SOFTUNLOCK = 6 /**< Device soft-unlocked (e.g., after successful pin entry) */,
PIN_CHANGE = 7 /**< Pin changed on the device */,
WIPE = 8 /**< Factory reset (wipe) invoked */,
UNPAIR = 9 /**< BLE bonding for current connection deleted */,
}
export enum TrezorPushNotificationMode {
NormalMode = 0,
BootloaderMode = 1,
}
export interface DecodedTrezorPushNotification {
type: TrezorPushNotificationType;
mode: TrezorPushNotificationMode;
}
export const decode = (message: number[]): DecodedTrezorPushNotification => {
const [version, type, mode] = message;
if (!version || version !== TPN_VERSION) {
throw new Error(PROTOCOL_MISSMATCH_VERSION);
}
if (
message.length !== MESSAGE_LENGTH ||
!Object.values(Version).includes(version) ||
!Object.values(TrezorPushNotificationType).includes(type) ||
!Object.values(TrezorPushNotificationMode).includes(mode)
) {
throw new Error(PROTOCOL_MALFORMED);
}
return {
type,
mode,
};
};

View File

@@ -0,0 +1,3 @@
// Protocol "tpn" trezor-push-notification
export * from './decode';

View File

@@ -0,0 +1,65 @@
import { PROTOCOL_MALFORMED, PROTOCOL_MISSMATCH_VERSION, tpn } from '../src';
import {
TrezorPushNotificationMode,
TrezorPushNotificationType,
Version,
} from '../src/protocol-tpn/decode';
describe('protocol-tpn', () => {
describe('decode', () => {
it('should correctly decode a valid Boot message in Normal Mode', () => {
const message = [
Version.v1,
TrezorPushNotificationType.BOOT,
TrezorPushNotificationMode.NormalMode,
];
const result = tpn.decode(message);
expect(result).toEqual({
type: TrezorPushNotificationType.BOOT,
mode: TrezorPushNotificationMode.NormalMode,
});
});
it('should correctly decode a valid Lock message in Bootloader Mode', () => {
const message = [
Version.v1,
TrezorPushNotificationType.LOCK,
TrezorPushNotificationMode.BootloaderMode,
];
const result = tpn.decode(message);
expect(result).toEqual({
type: TrezorPushNotificationType.LOCK,
mode: TrezorPushNotificationMode.BootloaderMode,
});
});
it('should throw a protocol error for messages with incorrect version', () => {
const invalidVersionMessage = [
99,
TrezorPushNotificationType.BOOT,
TrezorPushNotificationMode.BootloaderMode,
];
expect(() => tpn.decode(invalidVersionMessage)).toThrow(PROTOCOL_MISSMATCH_VERSION);
});
it('should throw a protocol error for messages with incorrect length', () => {
const shortMessage = [1, 0];
const longMessage = [1, 0, 1, 99];
expect(() => tpn.decode(shortMessage)).toThrow(PROTOCOL_MALFORMED);
expect(() => tpn.decode(longMessage)).toThrow(PROTOCOL_MALFORMED);
});
it('should throw a protocol error for an unknown notification type', () => {
const messageWithInvalidType = [Version.v1, 99, TrezorPushNotificationMode.NormalMode];
expect(() => tpn.decode(messageWithInvalidType)).toThrow(PROTOCOL_MALFORMED);
});
it('should throw a protocol error for an unknown mode', () => {
const messageWithInvalidMode = [Version.v1, TrezorPushNotificationType.BOOT, 99];
expect(() => tpn.decode(messageWithInvalidMode)).toThrow(PROTOCOL_MALFORMED);
});
});
});