mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-03-06 23:39:38 +01:00
refactor(protocol): use protocol-v2 constants in protocol-thp
This commit is contained in:
committed by
Szymon Lesisz
parent
c149719480
commit
27668ed095
@@ -1,15 +1,3 @@
|
||||
export const THP_CREATE_CHANNEL_REQUEST = 0x40;
|
||||
export const THP_CREATE_CHANNEL_RESPONSE = 0x41;
|
||||
export const THP_HANDSHAKE_INIT_REQUEST = 0x00;
|
||||
export const THP_HANDSHAKE_INIT_RESPONSE = 0x01;
|
||||
export const THP_HANDSHAKE_COMPLETION_REQUEST = 0x02;
|
||||
export const THP_HANDSHAKE_COMPLETION_RESPONSE = 0x03;
|
||||
export const THP_ERROR_HEADER_BYTE = 0x42;
|
||||
export const THP_READ_ACK_HEADER_BYTE = 0x20; // [0x20, 0x30];
|
||||
export const THP_CONTROL_BYTE_ENCRYPTED = 0x04; // [0x04, 0x14];
|
||||
export const THP_CONTROL_BYTE_DECRYPTED = 0x05; // [0x05, 0x15];
|
||||
export const THP_CONTINUATION_PACKET = 0x80;
|
||||
|
||||
export const THP_DEFAULT_CHANNEL = Buffer.from([0xff, 0xff]);
|
||||
|
||||
export const CRC_LENGTH = 4;
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import { ThpState } from './ThpState';
|
||||
import {
|
||||
CRC_LENGTH,
|
||||
TAG_LENGTH,
|
||||
THP_CONTROL_BYTE_DECRYPTED,
|
||||
THP_CONTROL_BYTE_ENCRYPTED,
|
||||
THP_CREATE_CHANNEL_RESPONSE,
|
||||
THP_ERROR_HEADER_BYTE,
|
||||
THP_HANDSHAKE_COMPLETION_RESPONSE,
|
||||
THP_HANDSHAKE_INIT_RESPONSE,
|
||||
THP_READ_ACK_HEADER_BYTE,
|
||||
} from './constants';
|
||||
import { CRC_LENGTH, TAG_LENGTH } from './constants';
|
||||
import { aesgcm } from './crypto';
|
||||
import { THP_CONTROL_BYTE } from '../protocol-v2/constants';
|
||||
import { TransportProtocolDecode } from '../types';
|
||||
import { crc32 } from './crypto/crc32';
|
||||
import { getHandshakeHash, getTrezorState } from './crypto/pairing';
|
||||
@@ -189,10 +180,10 @@ export const decodeSendAck = (decodedMessage: MessageV2) => {
|
||||
validateCrc(decodedMessage);
|
||||
|
||||
const { magic } = readThpHeader(decodedMessage.header);
|
||||
if (magic === THP_ERROR_HEADER_BYTE) {
|
||||
if (magic === THP_CONTROL_BYTE.ERROR) {
|
||||
return decodeThpError(decodedMessage.payload);
|
||||
}
|
||||
if (magic === THP_READ_ACK_HEADER_BYTE) {
|
||||
if (magic === THP_CONTROL_BYTE.ACK_MESSAGE) {
|
||||
return decodeReadAck();
|
||||
}
|
||||
};
|
||||
@@ -217,32 +208,27 @@ export const decode = (
|
||||
};
|
||||
|
||||
const { magic } = header;
|
||||
if (magic === THP_ERROR_HEADER_BYTE) {
|
||||
if (magic === THP_CONTROL_BYTE.ERROR) {
|
||||
return decodeThpError(message.payload);
|
||||
}
|
||||
|
||||
if (magic === THP_READ_ACK_HEADER_BYTE) {
|
||||
if (magic === THP_CONTROL_BYTE.ACK_MESSAGE) {
|
||||
return decodeReadAck();
|
||||
}
|
||||
|
||||
if (magic === THP_CREATE_CHANNEL_RESPONSE) {
|
||||
if (magic === THP_CONTROL_BYTE.CHANNEL_ALLOCATION_RES) {
|
||||
return createChannelResponse(message, protobufDecoder);
|
||||
}
|
||||
|
||||
if (magic === THP_HANDSHAKE_INIT_RESPONSE) {
|
||||
if (magic === THP_CONTROL_BYTE.HANDSHAKE_INIT_RES) {
|
||||
return readHandshakeInitResponse(message);
|
||||
}
|
||||
|
||||
if (magic === THP_HANDSHAKE_COMPLETION_RESPONSE) {
|
||||
if (magic === THP_CONTROL_BYTE.HANDSHAKE_COMP_RES) {
|
||||
return readHandshakeCompletionResponse(message);
|
||||
}
|
||||
|
||||
if (magic === THP_CONTROL_BYTE_ENCRYPTED) {
|
||||
return readProtobufMessage(message, protobufDecoder);
|
||||
}
|
||||
|
||||
// TODO: decrypted message decoding (not implemented in FW)
|
||||
if (magic === THP_CONTROL_BYTE_DECRYPTED) {
|
||||
if (magic === THP_CONTROL_BYTE.ENCRYPTED) {
|
||||
return readProtobufMessage(message, protobufDecoder);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { ThpState } from './ThpState';
|
||||
import {
|
||||
CRC_LENGTH,
|
||||
TAG_LENGTH,
|
||||
THP_CONTROL_BYTE_ENCRYPTED,
|
||||
THP_CREATE_CHANNEL_REQUEST,
|
||||
THP_DEFAULT_CHANNEL,
|
||||
THP_HANDSHAKE_COMPLETION_REQUEST,
|
||||
THP_HANDSHAKE_INIT_REQUEST,
|
||||
THP_READ_ACK_HEADER_BYTE,
|
||||
} from './constants';
|
||||
import { CRC_LENGTH, TAG_LENGTH, THP_DEFAULT_CHANNEL } from './constants';
|
||||
import { aesgcm, crc32 } from './crypto';
|
||||
import { THP_CONTROL_BYTE } from '../protocol-v2/constants';
|
||||
import { getIvFromNonce } from './crypto/tools';
|
||||
import { addAckBit, addSequenceBit, isThpMessageName } from './utils';
|
||||
|
||||
@@ -103,7 +95,7 @@ const createChannelRequest = (data: Buffer, channel: Buffer) => {
|
||||
const length = Buffer.alloc(2);
|
||||
length.writeUInt16BE(data.length + CRC_LENGTH); // 8 nonce + 4 crc
|
||||
|
||||
const magic = Buffer.from([THP_CREATE_CHANNEL_REQUEST]);
|
||||
const magic = Buffer.from([THP_CONTROL_BYTE.CHANNEL_ALLOCATION_REQ]);
|
||||
const message = Buffer.concat([magic, channel, length, data]);
|
||||
const crc = crc32(message);
|
||||
|
||||
@@ -114,7 +106,7 @@ const handshakeInitRequest = (data: Buffer, channel: Buffer) => {
|
||||
const length = Buffer.alloc(2);
|
||||
length.writeUInt16BE(data.length + CRC_LENGTH);
|
||||
|
||||
const magic = Buffer.from([THP_HANDSHAKE_INIT_REQUEST]);
|
||||
const magic = Buffer.from([THP_CONTROL_BYTE.HANDSHAKE_INIT_REQ]);
|
||||
const message = Buffer.concat([magic, channel, length, data]);
|
||||
const crc = crc32(message);
|
||||
|
||||
@@ -125,7 +117,7 @@ const handshakeCompletionRequest = (data: Buffer, channel: Buffer, sendBit: numb
|
||||
const length = Buffer.alloc(2);
|
||||
length.writeUInt16BE(data.length + CRC_LENGTH);
|
||||
|
||||
const magic = addSequenceBit(THP_HANDSHAKE_COMPLETION_REQUEST, sendBit);
|
||||
const magic = addSequenceBit(THP_CONTROL_BYTE.HANDSHAKE_COMP_REQ, sendBit);
|
||||
const message = Buffer.concat([magic, channel, length, data]);
|
||||
const crc = crc32(message);
|
||||
|
||||
@@ -169,7 +161,7 @@ export const encodeProtobufMessage = (
|
||||
length.writeUInt16BE(1 + 2 + data.length + TAG_LENGTH + CRC_LENGTH); // 1 session_id + 2 messageType + protobuf len + 16 tag + 4 crc
|
||||
|
||||
// TODO: distinguish encrypted and decrypted messages (not implemented in FW)
|
||||
const magic = addSequenceBit(THP_CONTROL_BYTE_ENCRYPTED, thpState.sendBit);
|
||||
const magic = addSequenceBit(THP_CONTROL_BYTE.ENCRYPTED, thpState.sendBit);
|
||||
const header = Buffer.concat([magic, channel]);
|
||||
|
||||
const messageTypeBytes = Buffer.alloc(2);
|
||||
@@ -195,7 +187,7 @@ export const encodeAck = (state: ThpState) => {
|
||||
const length = Buffer.alloc(2);
|
||||
length.writeUInt16BE(CRC_LENGTH);
|
||||
|
||||
const magic = addAckBit(THP_READ_ACK_HEADER_BYTE, state.recvAckBit);
|
||||
const magic = addAckBit(THP_CONTROL_BYTE.ACK_MESSAGE, state.recvAckBit);
|
||||
const message = Buffer.concat([magic, state.channel, length]);
|
||||
const crc = crc32(message);
|
||||
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import type { ThpState } from './ThpState';
|
||||
import {
|
||||
THP_CONTINUATION_PACKET,
|
||||
THP_CONTROL_BYTE_DECRYPTED,
|
||||
THP_CONTROL_BYTE_ENCRYPTED,
|
||||
THP_CREATE_CHANNEL_REQUEST,
|
||||
THP_CREATE_CHANNEL_RESPONSE,
|
||||
THP_ERROR_HEADER_BYTE,
|
||||
THP_HANDSHAKE_COMPLETION_REQUEST,
|
||||
THP_HANDSHAKE_COMPLETION_RESPONSE,
|
||||
THP_HANDSHAKE_INIT_REQUEST,
|
||||
THP_HANDSHAKE_INIT_RESPONSE,
|
||||
THP_READ_ACK_HEADER_BYTE,
|
||||
} from './constants';
|
||||
import { ThpMessageSyncBit, ThpPairingMethod } from './messages';
|
||||
import { THP_CONTROL_BYTE } from '../protocol-v2/constants';
|
||||
|
||||
export const addAckBit = (magic: number, ackBit: number) => {
|
||||
const result = Buffer.alloc(1);
|
||||
@@ -60,7 +48,9 @@ export const readThpHeader = (bytes: Buffer) => {
|
||||
// Trezor doesn't expect ThpAck ThpCreateChannelResponse
|
||||
export const isAckExpected = (bytesOrMagic: Buffer | number[]) => {
|
||||
const isCreateChannelMessage = (magic: number) =>
|
||||
[THP_CREATE_CHANNEL_REQUEST, THP_CREATE_CHANNEL_RESPONSE].includes(magic);
|
||||
[THP_CONTROL_BYTE.CHANNEL_ALLOCATION_REQ, THP_CONTROL_BYTE.CHANNEL_ALLOCATION_RES].includes(
|
||||
magic,
|
||||
);
|
||||
|
||||
if (Array.isArray(bytesOrMagic)) {
|
||||
return !bytesOrMagic.find(n => isCreateChannelMessage(n));
|
||||
@@ -73,20 +63,17 @@ export const isAckExpected = (bytesOrMagic: Buffer | number[]) => {
|
||||
export const getExpectedResponses = (bytes: Buffer) => {
|
||||
const { magic } = readThpHeader(bytes);
|
||||
|
||||
if (magic === THP_CREATE_CHANNEL_REQUEST) {
|
||||
return [THP_CREATE_CHANNEL_RESPONSE];
|
||||
if (magic === THP_CONTROL_BYTE.CHANNEL_ALLOCATION_REQ) {
|
||||
return [THP_CONTROL_BYTE.CHANNEL_ALLOCATION_RES];
|
||||
}
|
||||
if (magic === THP_HANDSHAKE_INIT_REQUEST) {
|
||||
return [THP_HANDSHAKE_INIT_RESPONSE, THP_CONTINUATION_PACKET];
|
||||
if (magic === THP_CONTROL_BYTE.HANDSHAKE_INIT_REQ) {
|
||||
return [THP_CONTROL_BYTE.HANDSHAKE_INIT_RES, THP_CONTROL_BYTE.CONTINUATION_PACKET];
|
||||
}
|
||||
if (magic === THP_HANDSHAKE_COMPLETION_REQUEST) {
|
||||
return [THP_HANDSHAKE_COMPLETION_RESPONSE, THP_CONTINUATION_PACKET];
|
||||
if (magic === THP_CONTROL_BYTE.HANDSHAKE_COMP_REQ) {
|
||||
return [THP_CONTROL_BYTE.HANDSHAKE_COMP_RES, THP_CONTROL_BYTE.CONTINUATION_PACKET];
|
||||
}
|
||||
if (magic === THP_CONTROL_BYTE_ENCRYPTED) {
|
||||
return [THP_CONTROL_BYTE_ENCRYPTED, THP_CONTINUATION_PACKET];
|
||||
}
|
||||
if (magic === THP_CONTROL_BYTE_DECRYPTED) {
|
||||
return [THP_CONTROL_BYTE_DECRYPTED, THP_CONTINUATION_PACKET];
|
||||
if (magic === THP_CONTROL_BYTE.ENCRYPTED) {
|
||||
return [THP_CONTROL_BYTE.ENCRYPTED, THP_CONTROL_BYTE.CONTINUATION_PACKET];
|
||||
}
|
||||
|
||||
return [];
|
||||
@@ -95,12 +82,12 @@ export const getExpectedResponses = (bytes: Buffer) => {
|
||||
// get expected responses from ThpState (stored as numbers)
|
||||
// and join them with the channel to receive 3 bytes header
|
||||
export const getExpectedHeaders = (state: ThpState): Buffer[] =>
|
||||
[...state.expectedResponses, THP_ERROR_HEADER_BYTE] // error could be sent any time
|
||||
[...state.expectedResponses, THP_CONTROL_BYTE.ERROR] // error could be sent any time
|
||||
.map(resp => {
|
||||
switch (resp) {
|
||||
case THP_CONTINUATION_PACKET:
|
||||
return Buffer.from([resp]); // THP_CONTINUATION_PACKET is not masked with sequence bit
|
||||
case THP_READ_ACK_HEADER_BYTE:
|
||||
case THP_CONTROL_BYTE.CONTINUATION_PACKET:
|
||||
return Buffer.from([resp]); // THP_CONTROL_BYTE.CONTINUATION_PACKET is not masked with sequence bit
|
||||
case THP_CONTROL_BYTE.ACK_MESSAGE:
|
||||
return addAckBit(resp, state.sendAckBit);
|
||||
default:
|
||||
return addSequenceBit(resp, state.recvBit);
|
||||
@@ -121,7 +108,7 @@ export const isExpectedResponse = (bytes: Buffer, state: ThpState) => {
|
||||
}
|
||||
|
||||
const { magic } = header;
|
||||
if (magic === THP_ERROR_HEADER_BYTE) {
|
||||
if (magic === THP_CONTROL_BYTE.ERROR) {
|
||||
// ThpError is always expected
|
||||
return true;
|
||||
}
|
||||
@@ -131,7 +118,7 @@ export const isExpectedResponse = (bytes: Buffer, state: ThpState) => {
|
||||
if (magic === expectedResponses[i]) {
|
||||
// continuation packet is not masked by controlBit
|
||||
if (
|
||||
magic !== THP_CONTINUATION_PACKET &&
|
||||
magic !== THP_CONTROL_BYTE.CONTINUATION_PACKET &&
|
||||
(header.sequenceBit !== state?.recvBit || header.ackBit !== state?.recvAckBit)
|
||||
) {
|
||||
console.warn('Unexpected control bit');
|
||||
|
||||
Reference in New Issue
Block a user