Merge remote-tracking branch 'ul/dev' into mntm-dev --nobuild

This commit is contained in:
Willy-JL
2025-04-30 04:46:18 +01:00
9 changed files with 174 additions and 54 deletions

View File

@@ -105,6 +105,7 @@
- Enable Asset Packs in NFC app again due to reduced RAM usage (#396 by @Willy-JL)
- Improve loading of parser plugins (by @Willy-JL)
- UL: Use default UL/UL-C pwd/key as default value for key input (by @mishamyte)
- UL: Attempt Ultralight C authentication with default key (by @mishamyte)
- OFW: Added naming for DESFire cards + fix MF3ICD40 cards unable to be read (by @Demae)
- OFW: FeliCa Protocol Expose Read Block API and Allow Specifying Service (by @zinongli)
- OFW: Enable MFUL sync poller to be provided with passwords (by @GMMan)
@@ -140,6 +141,7 @@
- UL: Fix Hollarm protocol with more verification (by @xMasterX)
- UL: Fix GangQi protocol (by @DoberBit and @mishamyte)
- UL: Came Atomo button hold simulation with full cycle to allow proper pairing with receiver (by @xMasterX)
- OFW: Fix sample durations when using external CC1101 (by @Aerosnail)
- OFW: NFC: ST25TB poller mode check (by @RebornedBrain)
- OFW: RFID: Fix Detection Conflict Between Securakey and Noralsy Format (by @zinongli)
- Furi:

View File

@@ -644,11 +644,13 @@ void subghz_device_cc1101_ext_start_async_rx(
furi_hal_bus_enable(FuriHalBusTIM17);
// Configure TIM
LL_TIM_InitTypeDef TIM_InitStruct = {0};
//Set the timer resolution to 2 us
LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);
LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetAutoReload(TIM17, 0xFFFF);
LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1);
TIM_InitStruct.Prescaler = (64 << 1) - 1;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0xFFFF;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
LL_TIM_Init(TIM17, &TIM_InitStruct);
// Timer: advanced
LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);

View File

@@ -23,6 +23,7 @@ typedef struct {
} JsGpioPinInst;
ARRAY_DEF(ManagedPinsArray, JsGpioPinInst*, M_PTR_OPLIST); //-V575
#define M_OPL_ManagedPinsArray_t() ARRAY_OPLIST(ManagedPinsArray)
/**
* Per-module instance control structure
@@ -444,20 +445,26 @@ static void js_gpio_destroy(void* inst) {
JsGpioInst* module = (JsGpioInst*)inst;
// reset pins
ManagedPinsArray_it_t iterator;
for(ManagedPinsArray_it(iterator, module->managed_pins); !ManagedPinsArray_end_p(iterator);
ManagedPinsArray_next(iterator)) {
JsGpioPinInst* manager_data = *ManagedPinsArray_cref(iterator);
if(manager_data->had_interrupt) {
furi_hal_gpio_disable_int_callback(manager_data->pin);
furi_hal_gpio_remove_int_callback(manager_data->pin);
for
M_EACH(item, module->managed_pins, ManagedPinsArray_t) {
JsGpioPinInst* manager_data = *item;
if(manager_data->had_interrupt) {
furi_hal_gpio_disable_int_callback(manager_data->pin);
furi_hal_gpio_remove_int_callback(manager_data->pin);
}
if(manager_data->pwm_output != FuriHalPwmOutputIdNone) {
if(furi_hal_pwm_is_running(manager_data->pwm_output))
furi_hal_pwm_stop(manager_data->pwm_output);
}
furi_hal_gpio_init(manager_data->pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_event_loop_maybe_unsubscribe(module->loop, manager_data->interrupt_semaphore);
furi_semaphore_free(manager_data->interrupt_semaphore);
free(manager_data->interrupt_contract);
free(manager_data);
}
furi_hal_gpio_init(manager_data->pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_event_loop_maybe_unsubscribe(module->loop, manager_data->interrupt_semaphore);
furi_semaphore_free(manager_data->interrupt_semaphore);
free(manager_data->interrupt_contract);
free(manager_data);
}
// free buffers
furi_hal_adc_release(module->adc_handle);

View File

@@ -75,6 +75,7 @@ Watch this video to learn more and see how different boards can be programmed (v
## Doorhan
With access to the receiver box:
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote)
2. Open your new remote file
3. Push `P` button for ~2 sec, led will start flashing
@@ -82,7 +83,29 @@ Watch this video to learn more and see how different boards can be programmed (v
5. Led on the receiver board will flash and turn off
6. Done!
Also you can program new remote using old remote on newer boards! See first video below:
With existing remote:
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote)
2. Open your new remote file
3. For next steps be close to the receiver board, around 1-2 meters
4. Press second button (lowest one) on the old remote, do not release second button and press 1st (upper) button, hold buttons for 1 sec and release them
5. Press working button on the old remote (the button you use for operating the receiver, aka opening the gate, etc) hold for 1 sec and release
6. Actions with old remote must be done in 5 seconds time, do not hold buttons for too long, and do not make it very fast
7. Receiver will beep, you will have 10 seconds to add new remote, now press Send on new remote on flipper two times holding for at least 1 sec
8. Receiver will beep again telling that new remote is added sucessfuly!
9. Done!
With copy of existing remote on flipper:
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote)
2. Open your existing remote (original) file
3. For next steps be close to the receiver board, around 1-2 meters
4. Press left button (0x8) on the flipper, hold for 1 sec and release the button and press right (0xA) button, hold button for 1 sec and release
5. Press working button on the flipper, should be center one aka Send (the button you use for operating the receiver, aka opening the gate, etc) hold for 1 sec and release
6. Actions with original remote copy must be done in 5 seconds time, do not hold buttons for too long, and do not make it very fast
7. Receiver will beep, now hold back and open new remote file, you will have 10 seconds to add new remote, press Send on new remote on flipper two times holding for at least 1 sec
8. Receiver will beep again telling that new remote is added sucessfuly!
9. Done!
Watch this videos to learn more (videos in Russian language): https://www.youtube.com/watch?v=wZ5121HYv50 / https://www.youtube.com/watch?v=1ucrDKF3vWc
## Somfy Telis
@@ -124,14 +147,24 @@ How to get seed to make full clone of your remote (**will conflict with original
1. Open `Read` in SubGHz on your flipper
2. (ONLY FOR ORIGINAL REMOTES) Hold all buttons on your remote at same time, example -> for 2 button remote - press them both at same time and hold OR press hidden button on back of remote with a pin or paper clip
For 4 buttons remote press & hold two buttons at upper row
3. You will receive signal on your flipper, open that signal and see `Fix:` value, it should start from `F` like `F00F1C9B`
4. If `Fix:` is showing first `F` see `Hop:` value -> This is your remote Seed
5. Write down Hop value
4. If `Fix:` is showing first `F` see `Hop:` value -> This is your remote Seed (except first digit `F` (this is the button code, aka programming button pressed means `F`))
5. Write down Hop value and replace first digit - `F` with `0`
6. Press button on your remote that you want to clone and receive its signal on your flipper
7. Open and write down `Fix:` value where first digit will be same as your button ID `Btn:`
8. Create new remote using BFT Mitto [Manual] - Enter FIX from step 7, enter counter `FF F9`, enter seed from step 5
9. Using counter values like `FF F9` can help bypassing current original remote counter value, and in result it also can fully desync original remote, only one remote can work at same time using this method
10. Throw away your original remote since now it needs to be re-added into receiver board :C
10. Also you can do this: Save your signal of the original remote (will say KL: Unknown),
then copy file to the PC and edit it and insert/replace those values after the `Key: 01 23 45 67 89 AB CD EF` (your key will have different value)
```
Seed: 0X XX XX XX
Manufacture: BFT
```
Replace `X`'s with digits from your Seed that you obtained by reading two button hold at the first steps,
Save and copy that file back to the flipper
Now you will have exact clone of your remote that will have same counter, by making couple presses you will make it higher than original and receiver will work with it, but original remote will reguire same amount of presses to work again, and vice versa.
11. Also your original remote may become non working since it needs to be re-added into receiver board if you made counter much higher than original :C
## CAME Atomo

View File

@@ -445,43 +445,35 @@ static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance
static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) {
NfcCommand command = NfcCommandContinue;
FURI_LOG_D(TAG, "MfulC auth");
if(mf_ultralight_support_feature(
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfu_event.data->auth_context.skip_auth) {
FURI_LOG_D(TAG, "Trying to authenticate with 3des key");
instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;
do {
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
furi_hal_random_fill_buf(RndA, sizeof(RndA));
instance->error = mf_ultralight_poller_authenticate_start(instance, RndA, output);
if(instance->error != MfUltralightErrorNone) break;
do {
if(mf_ultralight_support_feature(
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
instance->error = mf_ultralight_poller_authenticate_end(
instance, RndB, output, decoded_shifted_RndA);
if(instance->error != MfUltralightErrorNone) break;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfu_event.data->auth_context.skip_auth) {
FURI_LOG_D(TAG, "Trying to authenticate with 3des key");
instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;
instance->error =
mf_ultralight_poller_auth_tdes(instance, &instance->auth_context);
mf_ultralight_3des_shift_data(RndA);
instance->auth_context.auth_success =
(memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
if(instance->auth_context.auth_success) {
if(instance->error == MfUltralightErrorNone &&
instance->auth_context.auth_success) {
FURI_LOG_D(TAG, "Auth success");
} else {
FURI_LOG_D(TAG, "Auth failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
}
} while(false);
if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) {
FURI_LOG_D(TAG, "Auth failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
} else {
// We assume here that it is card read without explicitly provided key
// So we try to auth with default one
instance->state = MfUltralightPollerStateTryDefaultMfulCKey;
break;
}
}
}
instance->state = MfUltralightPollerStateReadPages;
instance->state = MfUltralightPollerStateReadPages;
} while(false);
return command;
}
@@ -578,6 +570,40 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll
return NfcCommandContinue;
}
static NfcCommand
mf_ultralight_poller_handler_try_default_ultralight_c_key(MfUltralightPoller* instance) {
do {
if(!mf_ultralight_support_feature(
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
break;
}
if(instance->auth_context.auth_success) {
break;
}
FURI_LOG_D(TAG, "Trying authentication with default 3DES key");
memcpy(
&instance->auth_context.tdes_key.data,
MF_ULTRALIGHT_C_DEFAULT_KEY,
MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);
instance->error = mf_ultralight_poller_auth_tdes(instance, &instance->auth_context);
if(instance->error == MfUltralightErrorNone && instance->auth_context.auth_success) {
FURI_LOG_D(TAG, "Default 3DES key detected");
} else {
FURI_LOG_D(TAG, "Authentication attempt with default 3DES key failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
}
} while(false);
instance->state = MfUltralightPollerStateReadPages;
return NfcCommandContinue;
}
static NfcCommand
mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) {
instance->state = MfUltralightPollerStateReadSuccess;
@@ -742,6 +768,8 @@ static const MfUltralightPollerReadHandler
mf_ultralight_poller_handler_read_tearing_flags,
[MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth,
[MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass,
[MfUltralightPollerStateTryDefaultMfulCKey] =
mf_ultralight_poller_handler_try_default_ultralight_c_key,
[MfUltralightPollerStateCheckMfulCAuthStatus] =
mf_ultralight_poller_handler_check_mfuc_auth_status,
[MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c,

View File

@@ -81,6 +81,19 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
MfUltralightPoller* instance,
MfUltralightPollerAuthContext* data);
/**
* @brief Perform 3DES authentication with key.
*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in, out] data pointer to the authentication context.
* @return MfUltralightErrorNone on success, an error code on failure.
*/
MfUltralightError mf_ultralight_poller_auth_tdes(
MfUltralightPoller* instance,
MfUltralightPollerAuthContext* data);
/**
* @brief Start authentication procedure.
*

View File

@@ -1,6 +1,7 @@
#include "mf_ultralight_poller_i.h"
#include <furi.h>
#include <furi_hal.h>
#define TAG "MfUltralightPoller"
@@ -62,6 +63,38 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
return ret;
}
MfUltralightError mf_ultralight_poller_auth_tdes(
MfUltralightPoller* instance,
MfUltralightPollerAuthContext* data) {
furi_check(instance);
furi_check(data);
MfUltralightError ret = MfUltralightErrorNone;
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
furi_hal_random_fill_buf(RndA, sizeof(RndA));
ret = mf_ultralight_poller_authenticate_start(instance, RndA, output);
if(ret != MfUltralightErrorNone) {
return ret;
}
uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
ret = mf_ultralight_poller_authenticate_end(instance, RndB, output, decoded_shifted_RndA);
if(ret != MfUltralightErrorNone) {
return ret;
}
mf_ultralight_3des_shift_data(RndA);
data->auth_success = (memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
return ret;
}
static MfUltralightError mf_ultralight_poller_send_authenticate_cmd(
MfUltralightPoller* instance,
const uint8_t* cmd,
@@ -134,7 +167,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
mf_ultralight_3des_decrypt(
&instance->des_context,
instance->mfu_event.data->auth_context.tdes_key.data,
instance->auth_context.tdes_key.data,
iv,
encRndB,
sizeof(encRndB),
@@ -145,7 +178,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
mf_ultralight_3des_encrypt(
&instance->des_context,
instance->mfu_event.data->auth_context.tdes_key.data,
instance->auth_context.tdes_key.data,
encRndB,
output,
MF_ULTRALIGHT_C_AUTH_DATA_SIZE,
@@ -179,7 +212,7 @@ MfUltralightError mf_ultralight_poller_authenticate_end(
mf_ultralight_3des_decrypt(
&instance->des_context,
instance->mfu_event.data->auth_context.tdes_key.data,
instance->auth_context.tdes_key.data,
RndB,
bit_buffer_get_data(instance->rx_buffer) + 1,
MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE,

View File

@@ -59,6 +59,7 @@ typedef enum {
MfUltralightPollerStateAuthMfulC,
MfUltralightPollerStateReadPages,
MfUltralightPollerStateTryDefaultPass,
MfUltralightPollerStateTryDefaultMfulCKey,
MfUltralightPollerStateCheckMfulCAuthStatus,
MfUltralightPollerStateReadFailed,
MfUltralightPollerStateReadSuccess,

View File

@@ -2767,6 +2767,7 @@ Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltral
Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t"
Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t"
Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
Function,+,mf_ultralight_poller_auth_tdes,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
Function,+,mf_ultralight_poller_authenticate_end,MfUltralightError,"MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*"
Function,+,mf_ultralight_poller_authenticate_start,MfUltralightError,"MfUltralightPoller*, const uint8_t*, uint8_t*"
Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*"
1 entry status name type params
2767 Function + mf_ultralight_is_page_pwd_or_pack _Bool MfUltralightType, uint16_t
2768 Function + mf_ultralight_load _Bool MfUltralightData*, FlipperFormat*, uint32_t
2769 Function + mf_ultralight_poller_auth_pwd MfUltralightError MfUltralightPoller*, MfUltralightPollerAuthContext*
2770 Function + mf_ultralight_poller_auth_tdes MfUltralightError MfUltralightPoller*, MfUltralightPollerAuthContext*
2771 Function + mf_ultralight_poller_authenticate_end MfUltralightError MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*
2772 Function + mf_ultralight_poller_authenticate_start MfUltralightError MfUltralightPoller*, const uint8_t*, uint8_t*
2773 Function + mf_ultralight_poller_read_counter MfUltralightError MfUltralightPoller*, uint8_t, MfUltralightCounter*