Skip to content

Commit

Permalink
Added support for encrypted 'HID++ long' as seen on LIGHTSPEED device
Browse files Browse the repository at this point in the history
  • Loading branch information
mame82 committed Jul 19, 2019
1 parent 3eb6b03 commit 66c663f
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 3 deletions.
8 changes: 7 additions & 1 deletion esb_illegalmod/nrf_esb_illegalmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -2599,43 +2599,49 @@ uint32_t nrf_esb_update_channel_frequency_table(uint8_t * values, uint8_t length
// reset frequency to first channel
m_esb_addr.rf_channel = 0;

//NRF_LOG_INFO("New channel table with length %d", length);
NRF_LOG_INFO("New channel table with length %d", length);
return NRF_SUCCESS;
}

uint32_t nrf_esb_update_channel_frequency_table_unifying() {
uint8_t unifying_frequencies[25] = { 5,8,11,14,17,20,23,26,29,32,35,38,41,44,47,50,53,56,59,62,65,68,71,74,77 };
uint8_t unifying_frequencies_len = 25;
NRF_LOG_INFO("Using channel table 'Unifying'");
return nrf_esb_update_channel_frequency_table(unifying_frequencies, unifying_frequencies_len);
}

uint32_t nrf_esb_update_channel_frequency_table_lightspeed() {
uint8_t unifying_frequencies[12] = { 1,49,56,41,79,80,81,24,89,78,26,3 };
uint8_t unifying_frequencies_len = 12;
NRF_LOG_INFO("Using channel table 'Lightspeed'");
return nrf_esb_update_channel_frequency_table(unifying_frequencies, unifying_frequencies_len);
}

uint32_t nrf_esb_update_channel_frequency_table_unifying_reduced() {
uint8_t unifying_frequencies[12] = { 5,8,14,17,32,35,41,44,62,65,71,74 };
uint8_t unifying_frequencies_len = 12;
NRF_LOG_INFO("Using channel table 'Unifying reduced'");
return nrf_esb_update_channel_frequency_table(unifying_frequencies, unifying_frequencies_len);
}

uint32_t nrf_esb_update_channel_frequency_table_unifying_pairing() {
uint8_t unifying_frequencies[11] = { 62,8,35,65,14,41,71,17,44,74,5 };
uint8_t unifying_frequencies_len = 11;
NRF_LOG_INFO("Using channel table 'Unifying pairing'");
return nrf_esb_update_channel_frequency_table(unifying_frequencies, unifying_frequencies_len);
}

uint32_t nrf_esb_update_channel_frequency_table_lightspeed_pairing() {
uint8_t unifying_frequencies[12] = { 26,41,79,3,1,80,81,56,49,89,25,82 };
uint8_t unifying_frequencies_len = 12;
NRF_LOG_INFO("Using channel table 'Lightspeed pairing'");
return nrf_esb_update_channel_frequency_table(unifying_frequencies, unifying_frequencies_len);
}

uint32_t nrf_esb_update_channel_frequency_table_all() {
uint8_t all_frequencies[100] = { 0 };
for (uint8_t i=0; i<sizeof(all_frequencies); i++) all_frequencies[i] = i;
NRF_LOG_INFO("Using channel table 'ALL'");
return nrf_esb_update_channel_frequency_table(all_frequencies, sizeof(all_frequencies));
}

Expand Down
5 changes: 5 additions & 0 deletions logitacker/logitacker_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@ uint32_t logitacker_devices_device_update_classification(logitacker_devices_unif
logitacker_unifying_extract_counter_from_encrypted_keyboard_frame(frame, &p_device->last_used_aes_ctr);
break;

case UNIFYING_RF_REPORT_ENCRYPTED_HIDPP_LONG:
if (len != 30) return NRF_ERROR_INVALID_DATA;
p_device->caps |= LOGITACKER_DEVICE_CAPS_UNIFYING_COMPATIBLE;
p_device->report_types |= LOGITACKER_DEVICE_REPORT_TYPES_LONG_HIDPP;
break;
case UNIFYING_RF_REPORT_HIDPP_LONG:
//ToDo: check if HID++ reports provide additional information (f.e. long version of device name is exchanged)
if (len != 22) return NRF_ERROR_INVALID_DATA;
Expand Down
26 changes: 26 additions & 0 deletions logitacker/logitacker_processor_passive_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ static logitacker_processor_t m_processor = {0};
static logitacker_processor_passive_enum_ctx_t m_static_passive_enum_ctx; //we reuse the same context, alternatively an malloc'ed ctx would allow separate instances
static char addr_str_buff[LOGITACKER_DEVICE_ADDR_STR_LEN] = {0};
static uint8_t m_keyboard_report_decryption_buffer[8] = { 0 };
static uint8_t m_hidpp_long_report_decryption_buffer[23] = { 0 };

void processor_passive_enum_init_func(logitacker_processor_t *p_processor);
void processor_passive_enum_init_func_(logitacker_processor_passive_enum_ctx_t *self);
Expand Down Expand Up @@ -315,6 +316,31 @@ void passive_enum_process_rx(logitacker_processor_passive_enum_ctx_t *self) {

}

if (unifying_report_type == UNIFYING_RF_REPORT_ENCRYPTED_HIDPP_LONG) {
if (p_device != NULL && p_device->key_known) {
if (logitacker_unifying_crypto_decrypt_encrypted_hidpp_frame(m_hidpp_long_report_decryption_buffer, p_device->key, &self->tmp_rx_payload) == NRF_SUCCESS) {
if (g_logitacker_global_config.passive_enum_pass_through_hidraw) {
// generate decrypted pseudo frame for hidraw pass trough
// 07 C1 00 4E 00 00 00 00 00 EA
nrf_esb_payload_t pseudo_frame;
pseudo_frame.rssi = self->tmp_rx_payload.rssi;
pseudo_frame.pid = self->tmp_rx_payload.pid;
pseudo_frame.rx_channel = self->tmp_rx_payload.rx_channel;
pseudo_frame.data[0] = self->tmp_rx_payload.data[0];
pseudo_frame.data[1] = 0x51; //HID++ long with keep alive bit
memcpy(&pseudo_frame.data[2], m_hidpp_long_report_decryption_buffer, 19);
logitacker_unifying_payload_update_checksum(pseudo_frame.data, 22);
pseudo_frame.length = 22;
pseudo_frame.validated_promiscuous_frame = false;

if (logitacker_usb_write_hidraw_input_report_rf_frame(LOGITACKER_MODE_PASSIVE_ENUMERATION, addr, &pseudo_frame) != NRF_SUCCESS) {
NRF_LOG_ERROR("Failed wirting decrypted frame to hidraw");
}
}
}
}
}

if (unifying_report_type == UNIFYING_RF_REPORT_ENCRYPTED_KEYBOARD) {
// check if device key is known
if (p_device != NULL && p_device->key_known) {
Expand Down
3 changes: 3 additions & 0 deletions logitacker/logitacker_unifying.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ void logitacker_unifying_frame_classify_log(nrf_esb_payload_t frame) {
case UNIFYING_RF_REPORT_HIDPP_LONG:
NRF_LOG_INFO("%sHID++ long", UNIFYING_CLASSIFY_LOG_PREFIX);
return;
case UNIFYING_RF_REPORT_ENCRYPTED_HIDPP_LONG:
NRF_LOG_INFO("%sENCRYPTED HID++ long", UNIFYING_CLASSIFY_LOG_PREFIX);
return;
case UNIFYING_RF_REPORT_ENCRYPTED_KEYBOARD:
//counter = frame.data[10] << 24 | frame.data[11] << 16 | frame.data[12] << 8 | frame.data[13];
counter = 0;
Expand Down
1 change: 1 addition & 0 deletions logitacker/logitacker_unifying.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define UNIFYING_RF_REPORT_HIDPP_SHORT 0x10
#define UNIFYING_RF_REPORT_HIDPP_LONG 0x11
#define UNIFYING_RF_REPORT_ENCRYPTED_KEYBOARD 0x13
#define UNIFYING_RF_REPORT_ENCRYPTED_HIDPP_LONG 0x1b
#define UNIFYING_RF_REPORT_PAIRING 0x1f

#define UNIFYING_RF_REPORT_BIT_KEEP_ALIVE 0x40
Expand Down
72 changes: 72 additions & 0 deletions logitacker/logitacker_unifying_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,78 @@ uint32_t logitacker_unifying_crypto_decrypt_encrypted_keyboard_frame(uint8_t * r
return NRF_SUCCESS;
}

uint32_t logitacker_unifying_crypto_decrypt_encrypted_hidpp_frame(uint8_t * result, uint8_t * device_key, nrf_esb_payload_t * rf_frame) {
/*
* <info> app: Unifying RF frame: ENCRYPTED HID++ long
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: 00 5B 5F BF 8C 1A 86 A6|.[_.....
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: 4F 0A 94 51 74 16 EE 20|O..Qt..
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: 9F C0 21 37 9A B6 85 B1|..!7....
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: 29 12 17 45 0C FF |)..E..
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: frame RX in passive enumeration mode (addr C6:1B:34:4A:26, len: 30, ch idx 17, raw ch 56)
<info> app: Unifying RF frame: ENCRYPTED HID++ long
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: 00 5B F2 B2 8B 04 60 BA|.[....`.
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: E8 EF 06 61 75 D2 D7 BA|...au...
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: E2 FA 29 A7 84 C5 32 AF|..)...2.
<info> LOGITACKER_PROCESSOR_PASIVE_ENUM: 9D 14 17 45 0C 53 |...E.S
*/


// validate frame
VERIFY_PARAM_NOT_NULL(result);
VERIFY_PARAM_NOT_NULL(device_key);
VERIFY_PARAM_NOT_NULL(rf_frame);
VERIFY_TRUE(rf_frame->length == 30, NRF_ERROR_INVALID_DATA);
VERIFY_TRUE((rf_frame->data[1] & 0x1f) == UNIFYING_RF_REPORT_ENCRYPTED_HIDPP_LONG, NRF_ERROR_INVALID_DATA); //encrypted keyboard frame

ret_code_t ret_val;

//two successive counters are used to calculate two keys

// extract counter
uint8_t counter[4] = { 0 };
uint8_t counter2[4] = { 0 };
memcpy(counter, &rf_frame->data[0x19], 4);

uint32_t tmp_counter = * ((uint32_t *) counter);
uint32_t tmp_counter2 = tmp_counter;
tmp_counter2++;
memcpy(counter2, &tmp_counter2, 4);

NRF_LOG_INFO("COUNTER1: %08x", tmp_counter)
NRF_LOG_INFO("COUNTER1: %08x", tmp_counter2)

//generate frame key1
uint8_t frame_key1[16] = { 0 };
ret_val = logitacker_unifying_crypto_calculate_frame_key(frame_key1, device_key, counter);
VERIFY_SUCCESS(ret_val);
//generate frame key2
uint8_t frame_key2[16] = { 0 };
ret_val = logitacker_unifying_crypto_calculate_frame_key(frame_key2, device_key, counter2);
VERIFY_SUCCESS(ret_val);

NRF_LOG_INFO("Frame key1:");
NRF_LOG_HEXDUMP_INFO(frame_key1,16);
NRF_LOG_INFO("Frame key2:");
NRF_LOG_HEXDUMP_INFO(frame_key2,16);

//copy cipher part to result
memcpy(result, &rf_frame->data[2], 23);

// xor decrypt with relevant part of AES key (yes, only half of the key - generated from high-entropy input data - is used)
for (int i=0; i<16; i++) {
result[i] ^= frame_key1[i];
}
for (int i=0; i<7; i++) {
result[16+i] ^= frame_key2[i];
}

NRF_LOG_INFO("Decrypt:");
NRF_LOG_HEXDUMP_INFO(result,23);

return NRF_SUCCESS;
}

uint32_t logitacker_unifying_crypto_encrypt_keyboard_frame(nrf_esb_payload_t * result_rf_frame, uint8_t * plain_payload, logitacker_device_unifying_device_key_t device_key, uint32_t counter) {
// validate frame
VERIFY_PARAM_NOT_NULL(plain_payload);
Expand Down
2 changes: 1 addition & 1 deletion logitacker/logitacker_unifying_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ uint32_t logitacker_unifying_crypto_decrypt_encrypted_keyboard_frame(uint8_t * r
uint32_t logitacker_unifying_crypto_encrypt_keyboard_frame(nrf_esb_payload_t *result_rf_frame, uint8_t *plain_payload,
logitacker_device_unifying_device_key_t device_key,
uint32_t counter);

uint32_t logitacker_unifying_crypto_decrypt_encrypted_hidpp_frame(uint8_t * result, uint8_t * device_key, nrf_esb_payload_t * rf_frame);
#endif //LOGITACKER_UNIFYING_CRYPTO_H
2 changes: 1 addition & 1 deletion logitacker/logitacker_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ static void usbd_hid_generic_event_handler(app_usbd_class_inst_t const *p_inst,
}
case APP_USBD_HID_USER_EVT_IN_REPORT_DONE:
{
NRF_LOG_DEBUG("report sent successfully")
NRF_LOG_INFO("HID raw report sent successfully")
m_current_hidraw_in_report_buf_read++;
m_current_hidraw_in_report_buf_read &= (HIDRAW_IN_REPORT_BUF_COUNT-1);
m_current_hidraw_in_report_buf_enqueued--;
Expand Down

0 comments on commit 66c663f

Please sign in to comment.