Merge branch 'dev' of https://github.com/DarkFlippers/unleashed-firmware into xfw-dev --nobuild

This commit is contained in:
Willy-JL
2023-08-13 02:26:24 +02:00
95 changed files with 1850 additions and 299 deletions

View File

@@ -327,6 +327,9 @@ distenv.PhonyTarget(
"cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}"
) )
# Update WiFi devboard firmware
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
# Find blackmagic probe # Find blackmagic probe
distenv.PhonyTarget( distenv.PhonyTarget(

View File

@@ -0,0 +1,602 @@
#include <stdio.h>
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
static const uint8_t key_ctr_1[32] = {
0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,
0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04,
};
static const uint8_t iv_ctr_1[16] = {
0x00,
0x00,
0x00,
0x60,
0xDB,
0x56,
0x72,
0xC9,
0x7A,
0xA8,
0xF0,
0xB2,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_1[16] = {
0x53,
0x69,
0x6E,
0x67,
0x6C,
0x65,
0x20,
0x62,
0x6C,
0x6F,
0x63,
0x6B,
0x20,
0x6D,
0x73,
0x67,
};
static const uint8_t tv_ctr_ct_1[16] = {
0x14,
0x5A,
0xD0,
0x1D,
0xBF,
0x82,
0x4E,
0xC7,
0x56,
0x08,
0x63,
0xDC,
0x71,
0xE3,
0xE0,
0xC0,
};
static const uint8_t key_ctr_2[32] = {
0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,
0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04,
};
static const uint8_t iv_ctr_2[16] = {
0x00,
0x00,
0x00,
0x60,
0xDB,
0x56,
0x72,
0xC9,
0x7A,
0xA8,
0xF0,
0xB2,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_2[0] = {};
//static const uint8_t tv_ctr_ct_2[0] = {};
static const uint8_t key_ctr_3[32] = {
0xF6, 0xD6, 0x6D, 0x6B, 0xD5, 0x2D, 0x59, 0xBB, 0x07, 0x96, 0x36, 0x58, 0x79, 0xEF, 0xF8, 0x86,
0xC6, 0x6D, 0xD5, 0x1A, 0x5B, 0x6A, 0x99, 0x74, 0x4B, 0x50, 0x59, 0x0C, 0x87, 0xA2, 0x38, 0x84,
};
static const uint8_t iv_ctr_3[16] = {
0x00,
0xFA,
0xAC,
0x24,
0xC1,
0x58,
0x5E,
0xF1,
0x5A,
0x43,
0xD8,
0x75,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_3[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
};
static const uint8_t tv_ctr_ct_3[32] = {
0xF0, 0x5E, 0x23, 0x1B, 0x38, 0x94, 0x61, 0x2C, 0x49, 0xEE, 0x00, 0x0B, 0x80, 0x4E, 0xB2, 0xA9,
0xB8, 0x30, 0x6B, 0x50, 0x8F, 0x83, 0x9D, 0x6A, 0x55, 0x30, 0x83, 0x1D, 0x93, 0x44, 0xAF, 0x1C,
};
static const uint8_t key_ctr_4[32] = {
0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2,
0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D,
};
static const uint8_t iv_ctr_4[16] = {
0x00,
0x1C,
0xC5,
0xB7,
0x51,
0xA5,
0x1D,
0x70,
0xA1,
0xC1,
0x11,
0x48,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_4[36] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,
};
static const uint8_t tv_ctr_ct_4[36] = {
0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7, 0xCE, 0x75, 0x94, 0x46,
0x2A, 0xCA, 0x4F, 0xAA, 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07,
0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F, 0x1E, 0xC0, 0xE6, 0xB8,
};
static const uint8_t key_ctr_5[32] = {
0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2,
0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D,
};
static const uint8_t iv_ctr_5[16] = {
0x00,
0x1C,
0xC5,
0xB7,
0x51,
0xA5,
0x1D,
0x70,
0xA1,
0xC1,
0x11,
0x48,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_5[0] = {};
//static const uint8_t tv_ctr_ct_5[0] = {};
static const uint8_t key_gcm_1[32] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t iv_gcm_1[16] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_1[0] = {};
//static const uint8_t tv_gcm_ct_1[0] = {};
static const uint8_t aad_gcm_1[0] = {};
static const uint8_t tv_gcm_tag_1[16] = {
0x53,
0x0F,
0x8A,
0xFB,
0xC7,
0x45,
0x36,
0xB9,
0xA9,
0x63,
0xB4,
0xF1,
0xC4,
0xCB,
0x73,
0x8B,
};
static const uint8_t key_gcm_2[32] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t iv_gcm_2[16] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_2[16] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t tv_gcm_ct_2[16] = {
0xCE,
0xA7,
0x40,
0x3D,
0x4D,
0x60,
0x6B,
0x6E,
0x07,
0x4E,
0xC5,
0xD3,
0xBA,
0xF3,
0x9D,
0x18,
};
static const uint8_t aad_gcm_2[0] = {};
static const uint8_t tv_gcm_tag_2[16] = {
0xD0,
0xD1,
0xC8,
0xA7,
0x99,
0x99,
0x6B,
0xF0,
0x26,
0x5B,
0x98,
0xB5,
0xD4,
0x8A,
0xB9,
0x19,
};
static const uint8_t key_gcm_3[32] = {
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
};
static const uint8_t iv_gcm_3[16] = {
0xCA,
0xFE,
0xBA,
0xBE,
0xFA,
0xCE,
0xDB,
0xAD,
0xDE,
0xCA,
0xF8,
0x88,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_3[64] = {
0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A,
0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72,
0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25,
0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55,
};
static const uint8_t tv_gcm_ct_3[64] = {
0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, 0x7D,
0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, 0xD1, 0xAA,
0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, 0x82, 0x88, 0x38,
0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, 0x89, 0x80, 0x15, 0xAD,
};
static const uint8_t aad_gcm_3[0] = {};
static const uint8_t tv_gcm_tag_3[16] = {
0xB0,
0x94,
0xDA,
0xC5,
0xD9,
0x34,
0x71,
0xBD,
0xEC,
0x1A,
0x50,
0x22,
0x70,
0xE3,
0xCC,
0x6C,
};
static const uint8_t key_gcm_4[32] = {
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
};
static const uint8_t iv_gcm_4[16] = {
0xCA,
0xFE,
0xBA,
0xBE,
0xFA,
0xCE,
0xDB,
0xAD,
0xDE,
0xCA,
0xF8,
0x88,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_4[60] = {
0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26,
0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31,
0x8A, 0x72, 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49,
0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39,
};
static const uint8_t tv_gcm_ct_4[60] = {
0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42,
0x7D, 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55,
0xD1, 0xAA, 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56,
0x82, 0x88, 0x38, 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62,
};
static const uint8_t aad_gcm_4[20] = {
0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED,
0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2,
};
static const uint8_t tv_gcm_tag_4[16] = {
0x76,
0xFC,
0x6E,
0xCE,
0x0F,
0x4E,
0x17,
0x68,
0xCD,
0xDF,
0x88,
0x53,
0xBB,
0x2D,
0x55,
0x1B,
};
static void furi_hal_crypto_ctr_setup() {
}
static void furi_hal_crypto_ctr_teardown() {
}
static void furi_hal_crypto_gcm_setup() {
}
static void furi_hal_crypto_gcm_teardown() {
}
MU_TEST(furi_hal_crypto_ctr_1) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_1)];
ret = furi_hal_crypto_ctr(key_ctr_1, iv_ctr_1, pt_ctr_1, ct, sizeof(pt_ctr_1));
mu_assert(ret, "CTR 1 failed");
mu_assert_mem_eq(tv_ctr_ct_1, ct, sizeof(pt_ctr_1));
}
MU_TEST(furi_hal_crypto_ctr_2) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_2)];
ret = furi_hal_crypto_ctr(key_ctr_2, iv_ctr_2, pt_ctr_2, ct, sizeof(pt_ctr_2));
mu_assert(ret, "CTR 2 failed");
//mu_assert_mem_eq(tv_ctr_ct_2, ct, sizeof(pt_ctr_2));
}
MU_TEST(furi_hal_crypto_ctr_3) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_3)];
ret = furi_hal_crypto_ctr(key_ctr_3, iv_ctr_3, pt_ctr_3, ct, sizeof(pt_ctr_3));
mu_assert(ret, "CTR 3 failed");
mu_assert_mem_eq(tv_ctr_ct_3, ct, sizeof(pt_ctr_3));
}
MU_TEST(furi_hal_crypto_ctr_4) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_4)];
ret = furi_hal_crypto_ctr(key_ctr_4, iv_ctr_4, pt_ctr_4, ct, sizeof(pt_ctr_4));
mu_assert(ret, "CTR 4 failed");
mu_assert_mem_eq(tv_ctr_ct_4, ct, sizeof(pt_ctr_4));
}
MU_TEST(furi_hal_crypto_ctr_5) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_5)];
ret = furi_hal_crypto_ctr(key_ctr_5, iv_ctr_5, pt_ctr_5, ct, sizeof(pt_ctr_5));
mu_assert(ret, "CTR 5 failed");
//mu_assert_mem_eq(tv_ctr_ct_5, ct, sizeof(pt_ctr_5));
}
MU_TEST(furi_hal_crypto_gcm_1) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_1)];
uint8_t ct[sizeof(pt_gcm_1)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_1,
iv_gcm_1,
aad_gcm_1,
sizeof(aad_gcm_1),
pt_gcm_1,
ct,
sizeof(pt_gcm_1),
tag_enc,
false);
mu_assert(ret, "GCM 1 encryption failed");
//mu_assert_mem_eq(tv_gcm_ct_1, ct, sizeof(pt_gcm_1));
mu_assert_mem_eq(tv_gcm_tag_1, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_1, iv_gcm_1, aad_gcm_1, sizeof(aad_gcm_1), ct, pt, sizeof(pt_gcm_1), tag_dec, true);
mu_assert(ret, "GCM 1 decryption failed");
//mu_assert_mem_eq(pt_gcm_1, pt, sizeof(pt_gcm_1));
mu_assert_mem_eq(tv_gcm_tag_1, tag_dec, 16);
}
MU_TEST(furi_hal_crypto_gcm_2) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_2)];
uint8_t ct[sizeof(pt_gcm_2)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_2,
iv_gcm_2,
aad_gcm_2,
sizeof(aad_gcm_2),
pt_gcm_2,
ct,
sizeof(pt_gcm_2),
tag_enc,
false);
mu_assert(ret, "GCM 2 encryption failed");
mu_assert_mem_eq(tv_gcm_ct_2, ct, sizeof(pt_gcm_2));
mu_assert_mem_eq(tv_gcm_tag_2, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_2, iv_gcm_2, aad_gcm_2, sizeof(aad_gcm_2), ct, pt, sizeof(pt_gcm_2), tag_dec, true);
mu_assert(ret, "GCM 2 decryption failed");
mu_assert_mem_eq(pt_gcm_2, pt, sizeof(pt_gcm_2));
mu_assert_mem_eq(tv_gcm_tag_2, tag_dec, 16);
}
MU_TEST(furi_hal_crypto_gcm_3) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_3)];
uint8_t ct[sizeof(pt_gcm_3)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_3,
iv_gcm_3,
aad_gcm_3,
sizeof(aad_gcm_3),
pt_gcm_3,
ct,
sizeof(pt_gcm_3),
tag_enc,
false);
mu_assert(ret, "GCM 3 encryption failed");
mu_assert_mem_eq(tv_gcm_ct_3, ct, sizeof(pt_gcm_3));
mu_assert_mem_eq(tv_gcm_tag_3, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_3, iv_gcm_3, aad_gcm_3, sizeof(aad_gcm_3), ct, pt, sizeof(pt_gcm_3), tag_dec, true);
mu_assert(ret, "GCM 3 decryption failed");
mu_assert_mem_eq(pt_gcm_3, pt, sizeof(pt_gcm_3));
mu_assert_mem_eq(tv_gcm_tag_3, tag_dec, 16);
}
MU_TEST(furi_hal_crypto_gcm_4) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_4)];
uint8_t ct[sizeof(pt_gcm_4)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_4,
iv_gcm_4,
aad_gcm_4,
sizeof(aad_gcm_4),
pt_gcm_4,
ct,
sizeof(pt_gcm_4),
tag_enc,
false);
mu_assert(ret, "GCM 4 encryption failed");
mu_assert_mem_eq(tv_gcm_ct_4, ct, sizeof(pt_gcm_4));
mu_assert_mem_eq(tv_gcm_tag_4, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_4, iv_gcm_4, aad_gcm_4, sizeof(aad_gcm_4), ct, pt, sizeof(pt_gcm_4), tag_dec, true);
mu_assert(ret, "GCM 4 decryption failed");
mu_assert_mem_eq(pt_gcm_4, pt, sizeof(pt_gcm_4));
mu_assert_mem_eq(tv_gcm_tag_4, tag_dec, 16);
}
MU_TEST_SUITE(furi_hal_crypto_ctr_test) {
MU_SUITE_CONFIGURE(&furi_hal_crypto_ctr_setup, &furi_hal_crypto_ctr_teardown);
MU_RUN_TEST(furi_hal_crypto_ctr_1);
MU_RUN_TEST(furi_hal_crypto_ctr_2);
MU_RUN_TEST(furi_hal_crypto_ctr_3);
MU_RUN_TEST(furi_hal_crypto_ctr_4);
MU_RUN_TEST(furi_hal_crypto_ctr_5);
}
MU_TEST_SUITE(furi_hal_crypto_gcm_test) {
MU_SUITE_CONFIGURE(&furi_hal_crypto_gcm_setup, &furi_hal_crypto_gcm_teardown);
MU_RUN_TEST(furi_hal_crypto_gcm_1);
MU_RUN_TEST(furi_hal_crypto_gcm_2);
MU_RUN_TEST(furi_hal_crypto_gcm_3);
MU_RUN_TEST(furi_hal_crypto_gcm_4);
}
int run_minunit_test_furi_hal_crypto() {
MU_RUN_SUITE(furi_hal_crypto_ctr_test);
MU_RUN_SUITE(furi_hal_crypto_gcm_test);
return MU_EXIT_CODE;
}

View File

@@ -10,6 +10,7 @@
int run_minunit_test_furi(); int run_minunit_test_furi();
int run_minunit_test_furi_hal(); int run_minunit_test_furi_hal();
int run_minunit_test_furi_hal_crypto();
int run_minunit_test_furi_string(); int run_minunit_test_furi_string();
int run_minunit_test_infrared(); int run_minunit_test_infrared();
int run_minunit_test_rpc(); int run_minunit_test_rpc();
@@ -39,6 +40,7 @@ typedef struct {
const UnitTest unit_tests[] = { const UnitTest unit_tests[] = {
{.name = "furi", .entry = run_minunit_test_furi}, {.name = "furi", .entry = run_minunit_test_furi},
{.name = "furi_hal", .entry = run_minunit_test_furi_hal}, {.name = "furi_hal", .entry = run_minunit_test_furi_hal},
{.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto},
{.name = "furi_string", .entry = run_minunit_test_furi_string}, {.name = "furi_string", .entry = run_minunit_test_furi_string},
{.name = "storage", .entry = run_minunit_test_storage}, {.name = "storage", .entry = run_minunit_test_storage},
{.name = "stream", .entry = run_minunit_test_stream}, {.name = "stream", .entry = run_minunit_test_stream},

View File

@@ -7,6 +7,5 @@ App(
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=60, order=60,
fap_icon="mouse_10px.png",
fap_category="Debug", fap_category="Debug",
) )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -8,7 +8,7 @@
#define TAG "U2F" #define TAG "U2F"
#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2 #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2
#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11 #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT
#define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key #define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key
#define U2F_CERT_USER 1 // User certificate, private key is encrypted with unique key #define U2F_CERT_USER 1 // User certificate, private key is encrypted with unique key
@@ -130,7 +130,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) {
// Generate random IV // Generate random IV
furi_hal_random_fill_buf(iv, 16); furi_hal_random_fill_buf(iv, 16);
if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
return false; return false;
} }
@@ -139,7 +139,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) {
FURI_LOG_E(TAG, "Encryption failed"); FURI_LOG_E(TAG, "Encryption failed");
return false; return false;
} }
furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* flipper_format = flipper_format_file_alloc(storage); FlipperFormat* flipper_format = flipper_format_file_alloc(storage);
@@ -172,8 +172,8 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) {
uint8_t key_slot = 0; uint8_t key_slot = 0;
uint32_t version = 0; uint32_t version = 0;
// Check if unique key exists in secure eclave(typo?) and generate it if missing // Check if unique key exists in secure eclave and generate it if missing
if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; if(!furi_hal_crypto_enclave_ensure_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false;
FuriString* filetype; FuriString* filetype;
filetype = furi_string_alloc(); filetype = furi_string_alloc();
@@ -220,7 +220,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) {
break; break;
} }
if(!furi_hal_crypto_store_load_key(key_slot, iv)) { if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
break; break;
} }
@@ -231,7 +231,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) {
FURI_LOG_E(TAG, "Decryption failed"); FURI_LOG_E(TAG, "Decryption failed");
break; break;
} }
furi_hal_crypto_store_unload_key(key_slot); furi_hal_crypto_enclave_unload_key(key_slot);
} else { } else {
if(!flipper_format_read_hex(flipper_format, "Data", cert_key, 32)) { if(!flipper_format_read_hex(flipper_format, "Data", cert_key, 32)) {
FURI_LOG_E(TAG, "Missing data"); FURI_LOG_E(TAG, "Missing data");
@@ -286,7 +286,7 @@ bool u2f_data_key_load(uint8_t* device_key) {
FURI_LOG_E(TAG, "Missing data"); FURI_LOG_E(TAG, "Missing data");
break; break;
} }
if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
break; break;
} }
@@ -296,7 +296,7 @@ bool u2f_data_key_load(uint8_t* device_key) {
FURI_LOG_E(TAG, "Decryption failed"); FURI_LOG_E(TAG, "Decryption failed");
break; break;
} }
furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);
state = true; state = true;
} while(0); } while(0);
} }
@@ -318,7 +318,7 @@ bool u2f_data_key_generate(uint8_t* device_key) {
furi_hal_random_fill_buf(iv, 16); furi_hal_random_fill_buf(iv, 16);
furi_hal_random_fill_buf(key, 32); furi_hal_random_fill_buf(key, 32);
if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
return false; return false;
} }
@@ -327,7 +327,7 @@ bool u2f_data_key_generate(uint8_t* device_key) {
FURI_LOG_E(TAG, "Encryption failed"); FURI_LOG_E(TAG, "Encryption failed");
return false; return false;
} }
furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* flipper_format = flipper_format_file_alloc(storage); FlipperFormat* flipper_format = flipper_format_file_alloc(storage);
@@ -392,7 +392,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) {
FURI_LOG_E(TAG, "Missing data"); FURI_LOG_E(TAG, "Missing data");
break; break;
} }
if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
break; break;
} }
@@ -402,7 +402,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) {
FURI_LOG_E(TAG, "Decryption failed"); FURI_LOG_E(TAG, "Decryption failed");
break; break;
} }
furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);
if(cnt.control == U2F_COUNTER_CONTROL_VAL) { if(cnt.control == U2F_COUNTER_CONTROL_VAL) {
*cnt_val = cnt.counter; *cnt_val = cnt.counter;
state = true; state = true;
@@ -434,7 +434,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) {
cnt.control = U2F_COUNTER_CONTROL_VAL; cnt.control = U2F_COUNTER_CONTROL_VAL;
cnt.counter = cnt_val; cnt.counter = cnt_val;
if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
return false; return false;
} }
@@ -443,7 +443,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) {
FURI_LOG_E(TAG, "Encryption failed"); FURI_LOG_E(TAG, "Encryption failed");
return false; return false;
} }
furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* flipper_format = flipper_format_file_alloc(storage); FlipperFormat* flipper_format = flipper_format_file_alloc(storage);

View File

@@ -33,7 +33,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
break; break;
} }
if(!furi_hal_crypto_store_load_key(key_slot, iv)) { if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {
printf("Unable to load key from slot %d", key_slot); printf("Unable to load key from slot %d", key_slot);
break; break;
} }
@@ -88,7 +88,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
} while(0); } while(0);
if(key_loaded) { if(key_loaded) {
furi_hal_crypto_store_unload_key(key_slot); furi_hal_crypto_enclave_unload_key(key_slot);
} }
} }
@@ -108,7 +108,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
break; break;
} }
if(!furi_hal_crypto_store_load_key(key_slot, iv)) { if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {
printf("Unable to load key from slot %d", key_slot); printf("Unable to load key from slot %d", key_slot);
break; break;
} }
@@ -160,7 +160,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
} while(0); } while(0);
if(key_loaded) { if(key_loaded) {
furi_hal_crypto_store_unload_key(key_slot); furi_hal_crypto_enclave_unload_key(key_slot);
} }
} }
@@ -175,14 +175,14 @@ void crypto_cli_has_key(Cli* cli, FuriString* args) {
break; break;
} }
if(!furi_hal_crypto_store_load_key(key_slot, iv)) { if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) {
printf("Unable to load key from slot %d", key_slot); printf("Unable to load key from slot %d", key_slot);
break; break;
} }
printf("Successfully loaded key from slot %d", key_slot); printf("Successfully loaded key from slot %d", key_slot);
furi_hal_crypto_store_unload_key(key_slot); furi_hal_crypto_enclave_unload_key(key_slot);
} while(0); } while(0);
} }
@@ -251,25 +251,25 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) {
if(key_slot > 0) { if(key_slot > 0) {
uint8_t iv[16] = {0}; uint8_t iv[16] = {0};
if(key_slot > 1) { if(key_slot > 1) {
if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { if(!furi_hal_crypto_enclave_load_key(key_slot - 1, iv)) {
printf( printf(
"Slot %d before %d is empty, which is not allowed", "Slot %d before %d is empty, which is not allowed",
key_slot - 1, key_slot - 1,
key_slot); key_slot);
break; break;
} }
furi_hal_crypto_store_unload_key(key_slot - 1); furi_hal_crypto_enclave_unload_key(key_slot - 1);
} }
if(furi_hal_crypto_store_load_key(key_slot, iv)) { if(furi_hal_crypto_enclave_load_key(key_slot, iv)) {
furi_hal_crypto_store_unload_key(key_slot); furi_hal_crypto_enclave_unload_key(key_slot);
printf("Key slot %d is already used", key_slot); printf("Key slot %d is already used", key_slot);
break; break;
} }
} }
uint8_t slot; uint8_t slot;
if(furi_hal_crypto_store_add_key(&key, &slot)) { if(furi_hal_crypto_enclave_store_key(&key, &slot)) {
printf("Success. Stored to slot: %d", slot); printf("Success. Stored to slot: %d", slot);
} else { } else {
printf("Failure"); printf("Failure");

View File

@@ -1,6 +1,7 @@
#include <storage/storage.h> #include <storage/storage.h>
#include <assets_icons.h> #include <assets_icons.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <gui/gui_i.h>
#include <gui/view_stack.h> #include <gui/view_stack.h>
#include <notification/notification.h> #include <notification/notification.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
@@ -38,103 +39,69 @@ static void desktop_loader_callback(const void* message, void* context) {
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished);
} }
} }
static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {
UNUSED(context); UNUSED(context);
furi_assert(canvas); furi_assert(canvas);
canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8); canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8);
} }
static void desktop_clock_upd_time(Desktop* desktop, bool forced) { static void desktop_clock_update(Desktop* desktop) {
furi_assert(desktop); furi_assert(desktop);
FuriHalRtcDateTime curr_dt; FuriHalRtcDateTime curr_dt;
furi_hal_rtc_get_datetime(&curr_dt); furi_hal_rtc_get_datetime(&curr_dt);
bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h;
if(forced) { if(desktop->time_hour != curr_dt.hour || desktop->time_minute != curr_dt.minute ||
desktop->clock_type = (locale_get_time_format() == LocaleTimeFormat24h); desktop->time_format_12 != time_format_12) {
} desktop->time_format_12 = time_format_12;
desktop->time_hour = curr_dt.hour;
if(forced || (desktop->minute != curr_dt.minute)) { desktop->time_minute = curr_dt.minute;
if(desktop->clock_type) {
desktop->hour = curr_dt.hour;
} else {
desktop->hour = (curr_dt.hour > 12) ? curr_dt.hour - 12 :
((curr_dt.hour == 0) ? 12 : curr_dt.hour);
}
desktop->minute = curr_dt.minute;
view_port_update(desktop->clock_viewport); view_port_update(desktop->clock_viewport);
} }
} }
static void desktop_clock_toggle_view(Desktop* desktop, bool is_enabled) { static void desktop_clock_reconfigure(Desktop* desktop) {
furi_assert(desktop); furi_assert(desktop);
desktop_clock_upd_time(desktop, true); desktop_clock_update(desktop);
if(is_enabled) { // && !furi_timer_is_running(desktop->update_clock_timer)) { if(XTREME_SETTINGS()->statusbar_clock) {
furi_timer_start(desktop->update_clock_timer, furi_ms_to_ticks(1000)); furi_timer_start(desktop->update_clock_timer, furi_ms_to_ticks(1000));
} else if(!is_enabled) { //&& furi_timer_is_running(desktop->update_clock_timer)) { } else {
furi_timer_stop(desktop->update_clock_timer); furi_timer_stop(desktop->update_clock_timer);
} }
view_port_enabled_set(desktop->clock_viewport, is_enabled); view_port_enabled_set(desktop->clock_viewport, XTREME_SETTINGS()->statusbar_clock);
} }
static uint8_t desktop_clock_get_num_w(uint8_t num) {
if(num == 1) {
return 3;
} else if(num == 4) {
return 6;
} else {
return 5;
}
}
static const char* digit[10] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
static void desktop_clock_draw_callback(Canvas* canvas, void* context) { static void desktop_clock_draw_callback(Canvas* canvas, void* context) {
furi_assert(context); furi_assert(context);
furi_assert(canvas); furi_assert(canvas);
Desktop* desktop = context; Desktop* desktop = context;
uint8_t d[4] = {
desktop->minute % 10,
desktop->minute / 10,
desktop->hour % 10,
desktop->hour / 10,
};
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontPrimary);
uint8_t new_w = desktop_clock_get_num_w(d[0]) + //c1 uint8_t hour = desktop->time_hour;
desktop_clock_get_num_w(d[1]) + //c2 if(desktop->time_format_12) {
desktop_clock_get_num_w(d[2]) + //c3 if(hour > 12) {
desktop_clock_get_num_w(d[3]) + //c4 hour -= 12;
2 + 4; // ":" + 4 separators }
if(hour == 0) {
hour = 12;
}
}
// further away from the battery charge indicator, if the smallest minute is 1 char buffer[20];
view_port_set_width(desktop->clock_viewport, new_w - !(d[0] == 1)); snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute);
uint8_t x = new_w; // ToDo: never do that, may cause visual glitches
view_port_set_width(
desktop->clock_viewport,
canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1));
uint8_t y = 8; canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer);
uint8_t offset_r;
canvas_draw_str_aligned(canvas, x, y, AlignRight, AlignBottom, digit[d[0]]);
offset_r = desktop_clock_get_num_w(d[0]);
canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y, AlignRight, AlignBottom, digit[d[1]]);
offset_r = desktop_clock_get_num_w(d[1]);
canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y - 1, AlignRight, AlignBottom, ":");
offset_r = 2;
canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y, AlignRight, AlignBottom, digit[d[2]]);
offset_r = desktop_clock_get_num_w(d[2]);
canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y, AlignRight, AlignBottom, digit[d[3]]);
} }
static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) { static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) {
@@ -154,7 +121,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
return true; return true;
case DesktopGlobalAfterAppFinished: case DesktopGlobalAfterAppFinished:
animation_manager_load_and_continue_animation(desktop->animation_manager); animation_manager_load_and_continue_animation(desktop->animation_manager);
desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); desktop_clock_reconfigure(desktop);
desktop_auto_lock_arm(desktop); desktop_auto_lock_arm(desktop);
return true; return true;
case DesktopGlobalAutoLock: case DesktopGlobalAutoLock:
@@ -224,8 +191,8 @@ static void desktop_clock_timer_callback(void* context) {
furi_assert(context); furi_assert(context);
Desktop* desktop = context; Desktop* desktop = context;
if(gui_get_count_of_enabled_view_port_in_layer(desktop->gui, GuiLayerStatusBarLeft) < 6) { if(gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6) {
desktop_clock_upd_time(desktop, false); desktop_clock_update(desktop);
view_port_enabled_set(desktop->clock_viewport, true); view_port_enabled_set(desktop->clock_viewport, true);
} else { } else {
@@ -420,11 +387,6 @@ Desktop* desktop_alloc() {
desktop->update_clock_timer = desktop->update_clock_timer =
furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop); furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop);
FuriHalRtcDateTime curr_dt;
furi_hal_rtc_get_datetime(&curr_dt);
desktop_clock_upd_time(desktop, true);
furi_record_create(RECORD_DESKTOP, desktop); furi_record_create(RECORD_DESKTOP, desktop);
return desktop; return desktop;
@@ -524,7 +486,7 @@ int32_t desktop_srv(void* p) {
DESKTOP_KEYBINDS_LOAD(&desktop->keybinds, sizeof(desktop->keybinds)); DESKTOP_KEYBINDS_LOAD(&desktop->keybinds, sizeof(desktop->keybinds));
desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); desktop_clock_reconfigure(desktop);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);

View File

@@ -74,9 +74,10 @@ struct Desktop {
FuriTimer* update_clock_timer; FuriTimer* update_clock_timer;
FuriPubSub* status_pubsub; FuriPubSub* status_pubsub;
uint8_t hour;
uint8_t minute; uint8_t time_hour;
bool clock_type : 1; // true - 24h false - 12h uint8_t time_minute;
bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H
bool in_transition : 1; bool in_transition : 1;
}; };

View File

@@ -20,11 +20,12 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
return NULL; return NULL;
} }
uint8_t gui_get_count_of_enabled_view_port_in_layer(Gui* gui, GuiLayer layer) { size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) {
furi_assert(gui); furi_assert(gui);
furi_check(layer < GuiLayerMAX); furi_check(layer < GuiLayerMAX);
uint8_t ret = 0; size_t ret = 0;
gui_lock(gui);
ViewPortArray_it_t it; ViewPortArray_it_t it;
ViewPortArray_it_last(it, gui->layers[layer]); ViewPortArray_it_last(it, gui->layers[layer]);
while(!ViewPortArray_end_p(it)) { while(!ViewPortArray_end_p(it)) {
@@ -34,6 +35,8 @@ uint8_t gui_get_count_of_enabled_view_port_in_layer(Gui* gui, GuiLayer layer) {
} }
ViewPortArray_previous(it); ViewPortArray_previous(it);
} }
gui_unlock(gui);
return ret; return ret;
} }

View File

@@ -141,8 +141,6 @@ Canvas* gui_direct_draw_acquire(Gui* gui);
*/ */
void gui_direct_draw_release(Gui* gui); void gui_direct_draw_release(Gui* gui);
uint8_t gui_get_count_of_enabled_view_port_in_layer(Gui* gui, GuiLayer layer);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -76,6 +76,12 @@ struct Gui {
ViewPort* ongoing_input_view_port; ViewPort* ongoing_input_view_port;
}; };
/** Find enabled ViewPort in ViewPortArray
*
* @param[in] array The ViewPortArray instance
*
* @return ViewPort instance or NULL
*/
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); ViewPort* gui_view_port_find_enabled(ViewPortArray_t array);
/** Update GUI, request redraw /** Update GUI, request redraw
@@ -84,8 +90,30 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array);
*/ */
void gui_update(Gui* gui); void gui_update(Gui* gui);
/** Input event callback
*
* Used to receive input from input service or to inject new input events
*
* @param[in] value The value pointer (InputEvent*)
* @param ctx The context (Gui instance)
*/
void gui_input_events_callback(const void* value, void* ctx); void gui_input_events_callback(const void* value, void* ctx);
/** Get count of view ports in layer
*
* @param gui The Gui instance
* @param[in] layer GuiLayer that we want to get count of view ports
*/
size_t gui_active_view_port_count(Gui* gui, GuiLayer layer);
/** Lock GUI
*
* @param gui The Gui instance
*/
void gui_lock(Gui* gui); void gui_lock(Gui* gui);
/** Unlock GUI
*
* @param gui The Gui instance
*/
void gui_unlock(Gui* gui); void gui_unlock(Gui* gui);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,284 @@
Filetype: Flipper Animation
Version: 1
Width: 128
Height: 64
Passive frames: 31
Active frames: 55
Frames order: 0 1 2 3 4 5 0 1 6 7 8 5 9 10 11 12 13 14 15 0 1 2 3 4 5 0 1 6 7 8 5 9 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 53 54 53 55 56 57 58 59 60 61 62 4 5
Active cycles: 1
Frame rate: 2
Duration: 3600
Active cooldown: 6
Bubble slots: 4
Slot: 0
X: 65
Y: 14
Text: All by myself
AlignH: Left
AlignV: Bottom
StartFrame: 45
EndFrame: 51
Slot: 0
X: 5
Y: 16
Text: Don't want
AlignH: Right
AlignV: Bottom
StartFrame: 56
EndFrame: 58
Slot: 0
X: 15
Y: 15
Text: to be
AlignH: Right
AlignV: Bottom
StartFrame: 59
EndFrame: 60
Slot: 0
X: 14
Y: 14
Text: All by myself
AlignH: Right
AlignV: Bottom
StartFrame: 63
EndFrame: 69
Slot: 0
X: 81
Y: 25
Text: anymore
AlignH: Left
AlignV: Bottom
StartFrame: 72
EndFrame: 74
Slot: 1
X: 65
Y: 14
Text: Nevermind
AlignH: Left
AlignV: Bottom
StartFrame: 45
EndFrame: 48
Slot: 1
X: 65
Y: 14
Text: I'll find
AlignH: Left
AlignV: Bottom
StartFrame: 49
EndFrame: 52
Slot: 1
X: 2
Y: 16
Text: Someone like
AlignH: Right
AlignV: Bottom
StartFrame: 56
EndFrame: 58
Slot: 1
X: 11
Y: 16
Text: youuuuu
AlignH: Right
AlignV: Bottom
StartFrame: 59
EndFrame: 60
Slot: 1
X: 3
Y: 14
Text: I wish nothing
AlignH: Right
AlignV: Bottom
StartFrame: 64
EndFrame: 66
Slot: 1
X: 6
Y: 14
Text: but the best
AlignH: Right
AlignV: Bottom
StartFrame: 67
EndFrame: 70
Slot: 1
X: 81
Y: 25
Text: for you
AlignH: Left
AlignV: Bottom
StartFrame: 72
EndFrame: 74
Slot: 2
X: 65
Y: 14
Text: What have I
AlignH: Left
AlignV: Bottom
StartFrame: 45
EndFrame: 48
Slot: 2
X: 65
Y: 14
Text: become
AlignH: Left
AlignV: Bottom
StartFrame: 47
EndFrame: 51
Slot: 2
X: 6
Y: 16
Text: My dearest
AlignH: Right
AlignV: Bottom
StartFrame: 56
EndFrame: 58
Slot: 2
X: 14
Y: 16
Text: friend
AlignH: Right
AlignV: Bottom
StartFrame: 59
EndFrame: 60
Slot: 2
X: 17
Y: 14
Text: Everyone
AlignH: Right
AlignV: Bottom
StartFrame: 63
EndFrame: 64
Slot: 2
X: 17
Y: 14
Text: I know
AlignH: Right
AlignV: Bottom
StartFrame: 65
EndFrame: 67
Slot: 2
X: 17
Y: 14
Text: goes away
AlignH: Right
AlignV: Bottom
StartFrame: 68
EndFrame: 70
Slot: 2
X: 81
Y: 25
Text: in the\n end
AlignH: Left
AlignV: Bottom
StartFrame: 72
EndFrame: 74
Slot: 3
X: 73
Y: 14
Text: We could\n have been
AlignH: Left
AlignV: Bottom
StartFrame: 45
EndFrame: 48
Slot: 3
X: 73
Y: 14
Text: so good\n together
AlignH: Left
AlignV: Bottom
StartFrame: 49
EndFrame: 51
Slot: 3
X: 7
Y: 17
Text: We could\n have lived
AlignH: Right
AlignV: Bottom
StartFrame: 55
EndFrame: 57
Slot: 3
X: 7
Y: 17
Text: this dance\n forever
AlignH: Right
AlignV: Bottom
StartFrame: 58
EndFrame: 60
Slot: 3
X: 12
Y: 14
Text: But now
AlignH: Right
AlignV: Bottom
StartFrame: 64
EndFrame: 65
Slot: 3
X: 5
Y: 14
Text: who's gonna
AlignH: Right
AlignV: Bottom
StartFrame: 66
EndFrame: 67
Slot: 3
X: 7
Y: 14
Text: dance with
AlignH: Right
AlignV: Bottom
StartFrame: 68
EndFrame: 69
Slot: 3
X: 26
Y: 14
Text: me?
AlignH: Right
AlignV: Bottom
StartFrame: 70
EndFrame: 70
Slot: 3
X: 81
Y: 25
Text: Please
AlignH: Left
AlignV: Bottom
StartFrame: 72
EndFrame: 74
Slot: 3
X: 81
Y: 25
Text: stay
AlignH: Left
AlignV: Bottom
StartFrame: 74
EndFrame: 75

View File

@@ -161,3 +161,10 @@ Max butthurt: 14
Min level: 27 Min level: 27
Max level: 30 Max level: 30
Weight: 3 Weight: 3
Name: L1_Sad_song_128x64
Min butthurt: 11
Max butthurt: 14
Min level: 14
Max level: 30
Weight: 4

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,34.4,, Version,+,35.0,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
@@ -1035,14 +1035,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t,
Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t
Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer
Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer
Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t"
Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t"
Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t"
Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool"
Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*"
Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*"
Function,-,furi_hal_crypto_init,void, Function,-,furi_hal_crypto_init,void,
Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*"
Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*"
Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t
Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_crypto_unload_key,_Bool,
Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*"
Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t
Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_disable,void,
Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_enable,void,
Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,+,furi_hal_debug_is_gdb_session_active,_Bool,
1 entry status name type params
2 Version + 34.4 35.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1035 Function + furi_hal_cortex_timer_get FuriHalCortexTimer uint32_t
1036 Function + furi_hal_cortex_timer_is_expired _Bool FuriHalCortexTimer
1037 Function + furi_hal_cortex_timer_wait void FuriHalCortexTimer
1038 Function + furi_hal_crypto_ctr _Bool const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t
1039 Function + furi_hal_crypto_decrypt _Bool const uint8_t*, uint8_t*, size_t
1040 Function + furi_hal_crypto_encrypt _Bool const uint8_t*, uint8_t*, size_t
1041 Function + furi_hal_crypto_gcm _Bool const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool
1042 Function + furi_hal_crypto_gcm_decrypt_and_verify FuriHalCryptoGCMState const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*
1043 Function + furi_hal_crypto_gcm_encrypt_and_tag FuriHalCryptoGCMState const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*
1044 Function - furi_hal_crypto_init void
1045 Function + furi_hal_crypto_store_add_key furi_hal_crypto_load_key _Bool FuriHalCryptoKey*, uint8_t* const uint8_t*, const uint8_t*
1046 Function + furi_hal_crypto_store_load_key furi_hal_crypto_enclave_store_key _Bool uint8_t, const uint8_t* FuriHalCryptoKey*, uint8_t*
1047 Function + furi_hal_crypto_store_unload_key furi_hal_crypto_enclave_load_key _Bool uint8_t uint8_t, const uint8_t*
1048 Function + furi_hal_crypto_verify_enclave furi_hal_crypto_enclave_unload_key _Bool uint8_t*, uint8_t* uint8_t
1049 Function + furi_hal_crypto_verify_key furi_hal_crypto_unload_key _Bool uint8_t
1050 Function + furi_hal_crypto_enclave_verify _Bool uint8_t*, uint8_t*
1051 Function + furi_hal_crypto_enclave_ensure_key _Bool uint8_t
1052 Function + furi_hal_debug_disable void
1053 Function + furi_hal_debug_enable void
1054 Function + furi_hal_debug_is_gdb_session_active _Bool

View File

@@ -1169,14 +1169,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t,
Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t
Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer
Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer
Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t"
Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t"
Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t"
Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool"
Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*"
Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*"
Function,-,furi_hal_crypto_init,void, Function,-,furi_hal_crypto_init,void,
Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*"
Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*"
Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t
Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_crypto_unload_key,_Bool,
Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*"
Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t
Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_disable,void,
Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_enable,void,
Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,+,furi_hal_debug_is_gdb_session_active,_Bool,
@@ -1713,7 +1719,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi
Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer"
Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_acquire,Canvas*,Gui*
Function,+,gui_direct_draw_release,void,Gui* Function,+,gui_direct_draw_release,void,Gui*
Function,-,gui_get_count_of_enabled_view_port_in_layer,uint8_t,"Gui*, GuiLayer"
Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_get_framebuffer_size,size_t,const Gui*
Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*"
Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
1 entry status name type params
1169 Function + furi_hal_cortex_timer_get FuriHalCortexTimer uint32_t
1170 Function + furi_hal_cortex_timer_is_expired _Bool FuriHalCortexTimer
1171 Function + furi_hal_cortex_timer_wait void FuriHalCortexTimer
1172 Function + furi_hal_crypto_ctr _Bool const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t
1173 Function + furi_hal_crypto_decrypt _Bool const uint8_t*, uint8_t*, size_t
1174 Function + furi_hal_crypto_encrypt _Bool const uint8_t*, uint8_t*, size_t
1175 Function + furi_hal_crypto_gcm _Bool const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool
1176 Function + furi_hal_crypto_gcm_decrypt_and_verify FuriHalCryptoGCMState const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*
1177 Function + furi_hal_crypto_gcm_encrypt_and_tag FuriHalCryptoGCMState const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*
1178 Function - furi_hal_crypto_init void
1179 Function + furi_hal_crypto_store_add_key furi_hal_crypto_load_key _Bool FuriHalCryptoKey*, uint8_t* const uint8_t*, const uint8_t*
1180 Function + furi_hal_crypto_store_load_key furi_hal_crypto_enclave_store_key _Bool uint8_t, const uint8_t* FuriHalCryptoKey*, uint8_t*
1181 Function + furi_hal_crypto_store_unload_key furi_hal_crypto_enclave_load_key _Bool uint8_t uint8_t, const uint8_t*
1182 Function + furi_hal_crypto_verify_enclave furi_hal_crypto_enclave_unload_key _Bool uint8_t*, uint8_t* uint8_t
1183 Function + furi_hal_crypto_verify_key furi_hal_crypto_unload_key _Bool uint8_t
1184 Function + furi_hal_crypto_enclave_verify _Bool uint8_t*, uint8_t*
1185 Function + furi_hal_crypto_enclave_ensure_key _Bool uint8_t
1186 Function + furi_hal_debug_disable void
1187 Function + furi_hal_debug_enable void
1188 Function + furi_hal_debug_is_gdb_session_active _Bool
1719 Function + gui_add_view_port void Gui*, ViewPort*, GuiLayer
1720 Function + gui_direct_draw_acquire Canvas* Gui*
1721 Function + gui_direct_draw_release void Gui*
Function - gui_get_count_of_enabled_view_port_in_layer uint8_t Gui*, GuiLayer
1722 Function + gui_get_framebuffer_size size_t const Gui*
1723 Function + gui_remove_framebuffer_callback void Gui*, GuiCanvasCommitCallback, void*
1724 Function + gui_remove_view_port void Gui*, ViewPort*

View File

@@ -1,4 +1,5 @@
#include <furi_hal_crypto.h> #include <furi_hal_crypto.h>
#include <furi_hal_cortex.h>
#include <furi_hal_bt.h> #include <furi_hal_bt.h>
#include <furi_hal_random.h> #include <furi_hal_random.h>
#include <furi_hal_bus.h> #include <furi_hal_bus.h>
@@ -13,7 +14,7 @@
#define ENCLAVE_SIGNATURE_SIZE 16 #define ENCLAVE_SIGNATURE_SIZE 16
#define CRYPTO_BLK_LEN (4 * sizeof(uint32_t)) #define CRYPTO_BLK_LEN (4 * sizeof(uint32_t))
#define CRYPTO_TIMEOUT (1000) #define CRYPTO_TIMEOUT_US (1000000)
#define CRYPTO_MODE_ENCRYPT 0U #define CRYPTO_MODE_ENCRYPT 0U
#define CRYPTO_MODE_INIT (AES_CR_MODE_0) #define CRYPTO_MODE_INIT (AES_CR_MODE_0)
@@ -24,6 +25,19 @@
#define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) #define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE)
#define CRYPTO_AES_CBC (AES_CR_CHMOD_0) #define CRYPTO_AES_CBC (AES_CR_CHMOD_0)
#define CRYPTO_AES_CTR (AES_CR_CHMOD_1)
#define CRYPTO_CTR_IV_LEN (12U)
#define CRYPTO_CTR_CTR_LEN (4U)
#define CRYPTO_AES_GCM (AES_CR_CHMOD_1 | AES_CR_CHMOD_0)
#define CRYPTO_GCM_IV_LEN (12U)
#define CRYPTO_GCM_CTR_LEN (4U)
#define CRYPTO_GCM_TAG_LEN (16U)
#define CRYPTO_GCM_PH_INIT (0x0U << AES_CR_GCMPH_Pos)
#define CRYPTO_GCM_PH_HEADER (AES_CR_GCMPH_0)
#define CRYPTO_GCM_PH_PAYLOAD (AES_CR_GCMPH_1)
#define CRYPTO_GCM_PH_FINAL (AES_CR_GCMPH_1 | AES_CR_GCMPH_0)
static FuriMutex* furi_hal_crypto_mutex = NULL; static FuriMutex* furi_hal_crypto_mutex = NULL;
static bool furi_hal_crypto_mode_init_done = false; static bool furi_hal_crypto_mode_init_done = false;
@@ -80,7 +94,7 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end
key.size = FuriHalCryptoKeySize256; key.size = FuriHalCryptoKeySize256;
key.data = key_data; key.data = key_data;
furi_hal_random_fill_buf(key_data, 32); furi_hal_random_fill_buf(key_data, 32);
if(!furi_hal_crypto_store_add_key(&key, &slot)) { if(!furi_hal_crypto_enclave_store_key(&key, &slot)) {
explicit_bzero(key_data, sizeof(key_data)); explicit_bzero(key_data, sizeof(key_data));
FURI_LOG_E(TAG, "Error writing key to slot %u", slot); FURI_LOG_E(TAG, "Error writing key to slot %u", slot);
return false; return false;
@@ -90,21 +104,21 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end
return true; return true;
} }
bool furi_hal_crypto_verify_key(uint8_t key_slot) { bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot) {
uint8_t keys_nb = 0; uint8_t keys_nb = 0;
uint8_t valid_keys_nb = 0; uint8_t valid_keys_nb = 0;
uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS;
uint8_t empty_iv[16] = {0}; uint8_t empty_iv[16] = {0};
furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); furi_hal_crypto_enclave_verify(&keys_nb, &valid_keys_nb);
if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key
if(key_slot > keys_nb) return false; if(key_slot > keys_nb) return false;
} else { // Unique key } else { // Unique key
if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing
return false; return false;
for(uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) { for(uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) {
if(furi_hal_crypto_store_load_key(i, empty_iv)) { if(furi_hal_crypto_enclave_load_key(i, empty_iv)) {
last_valid_slot = i; last_valid_slot = i;
furi_hal_crypto_store_unload_key(i); furi_hal_crypto_enclave_unload_key(i);
break; break;
} }
} }
@@ -116,14 +130,14 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) {
return true; return true;
} }
bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb) {
furi_assert(keys_nb); furi_assert(keys_nb);
furi_assert(valid_keys_nb); furi_assert(valid_keys_nb);
uint8_t keys = 0; uint8_t keys = 0;
uint8_t keys_valid = 0; uint8_t keys_valid = 0;
uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; uint8_t buffer[ENCLAVE_SIGNATURE_SIZE];
for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) { for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) {
if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { if(furi_hal_crypto_enclave_load_key(key_slot + 1, enclave_signature_iv[key_slot])) {
keys++; keys++;
if(furi_hal_crypto_encrypt( if(furi_hal_crypto_encrypt(
enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) { enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) {
@@ -131,7 +145,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) {
memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) ==
0; 0;
} }
furi_hal_crypto_store_unload_key(key_slot + 1); furi_hal_crypto_enclave_unload_key(key_slot + 1);
} }
} }
*keys_nb = keys; *keys_nb = keys;
@@ -142,7 +156,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) {
return false; return false;
} }
bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) { bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot) {
furi_assert(key); furi_assert(key);
furi_assert(slot); furi_assert(slot);
@@ -208,6 +222,16 @@ static void crypto_key_init(uint32_t* key, uint32_t* iv) {
AES1->IVR0 = iv[3]; AES1->IVR0 = iv[3];
} }
static bool furi_hal_crypto_wait_flag(uint32_t flag) {
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CRYPTO_TIMEOUT_US);
while(!READ_BIT(AES1->SR, flag)) {
if(furi_hal_cortex_timer_is_expired(timer)) {
return false;
}
}
return true;
}
static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) {
furi_check((blk_len <= 4) && (blk_len > 0)); furi_check((blk_len <= 4) && (blk_len > 0));
@@ -219,15 +243,9 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) {
} }
} }
uint32_t countdown = CRYPTO_TIMEOUT; if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) {
while(!READ_BIT(AES1->SR, AES_SR_CCF)) {
if(LL_SYSTICK_IsActiveCounterFlag()) {
countdown--;
}
if(countdown == 0) {
return false; return false;
} }
}
SET_BIT(AES1->CR, AES_CR_CCFC); SET_BIT(AES1->CR, AES_CR_CCFC);
@@ -240,7 +258,7 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) {
return true; return true;
} }
bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv) {
furi_assert(slot > 0 && slot <= 100); furi_assert(slot > 0 && slot <= 100);
furi_assert(furi_hal_crypto_mutex); furi_assert(furi_hal_crypto_mutex);
furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk);
@@ -263,7 +281,7 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) {
} }
} }
bool furi_hal_crypto_store_unload_key(uint8_t slot) { bool furi_hal_crypto_enclave_unload_key(uint8_t slot) {
if(!furi_hal_bt_is_alive()) { if(!furi_hal_bt_is_alive()) {
return false; return false;
} }
@@ -279,6 +297,27 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) {
return (shci_state == SHCI_Success); return (shci_state == SHCI_Success);
} }
bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv) {
furi_assert(furi_hal_crypto_mutex);
furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk);
furi_hal_bus_enable(FuriHalBusAES1);
furi_hal_crypto_mode_init_done = false;
crypto_key_init((uint32_t*)key, (uint32_t*)iv);
return true;
}
bool furi_hal_crypto_unload_key(void) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
furi_hal_bus_disable(FuriHalBusAES1);
furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk);
return true;
}
bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) {
bool state = false; bool state = false;
@@ -310,15 +349,9 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size)
SET_BIT(AES1->CR, AES_CR_EN); SET_BIT(AES1->CR, AES_CR_EN);
uint32_t countdown = CRYPTO_TIMEOUT; if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) {
while(!READ_BIT(AES1->SR, AES_SR_CCF)) {
if(LL_SYSTICK_IsActiveCounterFlag()) {
countdown--;
}
if(countdown == 0) {
return false; return false;
} }
}
SET_BIT(AES1->CR, AES_CR_CCFC); SET_BIT(AES1->CR, AES_CR_CCFC);
@@ -343,3 +376,360 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size)
return state; return state;
} }
static void crypto_key_init_bswap(uint32_t* key, uint32_t* iv, uint32_t chaining_mode) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
MODIFY_REG(
AES1->CR,
AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD,
CRYPTO_DATATYPE_32B | CRYPTO_KEYSIZE_256B | chaining_mode);
if(key != NULL) {
AES1->KEYR7 = __builtin_bswap32(key[0]);
AES1->KEYR6 = __builtin_bswap32(key[1]);
AES1->KEYR5 = __builtin_bswap32(key[2]);
AES1->KEYR4 = __builtin_bswap32(key[3]);
AES1->KEYR3 = __builtin_bswap32(key[4]);
AES1->KEYR2 = __builtin_bswap32(key[5]);
AES1->KEYR1 = __builtin_bswap32(key[6]);
AES1->KEYR0 = __builtin_bswap32(key[7]);
}
AES1->IVR3 = __builtin_bswap32(iv[0]);
AES1->IVR2 = __builtin_bswap32(iv[1]);
AES1->IVR1 = __builtin_bswap32(iv[2]);
AES1->IVR0 = __builtin_bswap32(iv[3]);
}
static bool
furi_hal_crypto_load_key_bswap(const uint8_t* key, const uint8_t* iv, uint32_t chaining_mode) {
furi_assert(furi_hal_crypto_mutex);
furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk);
furi_hal_bus_enable(FuriHalBusAES1);
crypto_key_init_bswap((uint32_t*)key, (uint32_t*)iv, chaining_mode);
return true;
}
static bool wait_for_crypto(void) {
if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) {
return false;
}
SET_BIT(AES1->CR, AES_CR_CCFC);
return true;
}
static bool furi_hal_crypto_process_block_bswap(const uint8_t* in, uint8_t* out, size_t bytes) {
uint32_t block[CRYPTO_BLK_LEN / 4];
memset(block, 0, sizeof(block));
memcpy(block, in, bytes);
block[0] = __builtin_bswap32(block[0]);
block[1] = __builtin_bswap32(block[1]);
block[2] = __builtin_bswap32(block[2]);
block[3] = __builtin_bswap32(block[3]);
if(!crypto_process_block(block, block, CRYPTO_BLK_LEN / 4)) {
return false;
}
block[0] = __builtin_bswap32(block[0]);
block[1] = __builtin_bswap32(block[1]);
block[2] = __builtin_bswap32(block[2]);
block[3] = __builtin_bswap32(block[3]);
memcpy(out, block, bytes);
return true;
}
static bool furi_hal_crypto_process_block_no_read_bswap(const uint8_t* in, size_t bytes) {
uint32_t block[CRYPTO_BLK_LEN / 4];
memset(block, 0, sizeof(block));
memcpy(block, in, bytes);
AES1->DINR = __builtin_bswap32(block[0]);
AES1->DINR = __builtin_bswap32(block[1]);
AES1->DINR = __builtin_bswap32(block[2]);
AES1->DINR = __builtin_bswap32(block[3]);
return wait_for_crypto();
}
static void furi_hal_crypto_ctr_prep_iv(uint8_t* iv) {
/* append counter to IV */
iv[CRYPTO_CTR_IV_LEN] = 0;
iv[CRYPTO_CTR_IV_LEN + 1] = 0;
iv[CRYPTO_CTR_IV_LEN + 2] = 0;
iv[CRYPTO_CTR_IV_LEN + 3] = 1;
}
static bool furi_hal_crypto_ctr_payload(const uint8_t* input, uint8_t* output, size_t length) {
SET_BIT(AES1->CR, AES_CR_EN);
MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT);
size_t last_block_bytes = length % CRYPTO_BLK_LEN;
size_t i;
for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) {
if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
}
if(last_block_bytes > 0) {
if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
}
CLEAR_BIT(AES1->CR, AES_CR_EN);
return true;
}
bool furi_hal_crypto_ctr(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* input,
uint8_t* output,
size_t length) {
/* prepare IV and counter */
uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN];
memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); //-V1086
furi_hal_crypto_ctr_prep_iv(iv_and_counter);
/* load key and IV and set the mode to CTR */
if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_CTR)) {
furi_hal_crypto_unload_key();
return false;
}
/* process the input and write to output */
bool state = furi_hal_crypto_ctr_payload(input, output, length);
furi_hal_crypto_unload_key();
return state;
}
static void furi_hal_crypto_gcm_prep_iv(uint8_t* iv) {
/* append counter to IV */
iv[CRYPTO_GCM_IV_LEN] = 0;
iv[CRYPTO_GCM_IV_LEN + 1] = 0;
iv[CRYPTO_GCM_IV_LEN + 2] = 0;
iv[CRYPTO_GCM_IV_LEN + 3] = 2;
}
static bool furi_hal_crypto_gcm_init(bool decrypt) {
/* GCM init phase */
MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_INIT);
if(decrypt) {
MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT);
} else {
MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT);
}
SET_BIT(AES1->CR, AES_CR_EN);
if(!wait_for_crypto()) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
return true;
}
static bool furi_hal_crypto_gcm_header(const uint8_t* aad, size_t aad_length) {
/* GCM header phase */
MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_HEADER);
SET_BIT(AES1->CR, AES_CR_EN);
size_t last_block_bytes = aad_length % CRYPTO_BLK_LEN;
size_t i;
for(i = 0; i < aad_length - last_block_bytes; i += CRYPTO_BLK_LEN) {
if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], CRYPTO_BLK_LEN)) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
}
if(last_block_bytes > 0) {
if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], last_block_bytes)) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
}
return true;
}
static bool furi_hal_crypto_gcm_payload(
const uint8_t* input,
uint8_t* output,
size_t length,
bool decrypt) {
/* GCM payload phase */
MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_PAYLOAD);
SET_BIT(AES1->CR, AES_CR_EN);
size_t last_block_bytes = length % CRYPTO_BLK_LEN;
size_t i;
for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) {
if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
}
if(last_block_bytes > 0) {
if(!decrypt) {
MODIFY_REG(
AES1->CR, AES_CR_NPBLB, (CRYPTO_BLK_LEN - last_block_bytes) << AES_CR_NPBLB_Pos);
}
if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
}
return true;
}
static bool furi_hal_crypto_gcm_finish(size_t aad_length, size_t payload_length, uint8_t* tag) {
/* GCM final phase */
MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_FINAL);
uint32_t last_block[CRYPTO_BLK_LEN / 4];
memset(last_block, 0, sizeof(last_block));
last_block[1] = __builtin_bswap32((uint32_t)(aad_length * 8));
last_block[3] = __builtin_bswap32((uint32_t)(payload_length * 8));
if(!furi_hal_crypto_process_block_bswap((uint8_t*)&last_block[0], tag, CRYPTO_BLK_LEN)) {
CLEAR_BIT(AES1->CR, AES_CR_EN);
return false;
}
return true;
}
static bool furi_hal_crypto_gcm_compare_tag(const uint8_t* tag1, const uint8_t* tag2) {
uint8_t diff = 0;
size_t i;
for(i = 0; i < CRYPTO_GCM_TAG_LEN; i++) {
diff |= tag1[i] ^ tag2[i];
}
return (diff == 0);
}
bool furi_hal_crypto_gcm(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* aad,
size_t aad_length,
const uint8_t* input,
uint8_t* output,
size_t length,
uint8_t* tag,
bool decrypt) {
/* GCM init phase */
/* prepare IV and counter */
uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN];
memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); //-V1086
furi_hal_crypto_gcm_prep_iv(iv_and_counter);
/* load key and IV and set the mode to CTR */
if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_GCM)) {
furi_hal_crypto_unload_key();
return false;
}
if(!furi_hal_crypto_gcm_init(decrypt)) {
furi_hal_crypto_unload_key();
return false;
}
/* GCM header phase */
if(aad_length > 0) {
if(!furi_hal_crypto_gcm_header(aad, aad_length)) {
furi_hal_crypto_unload_key();
return false;
}
}
/* GCM payload phase */
if(!furi_hal_crypto_gcm_payload(input, output, length, decrypt)) {
furi_hal_crypto_unload_key();
return false;
}
/* GCM final phase */
if(!furi_hal_crypto_gcm_finish(aad_length, length, tag)) {
furi_hal_crypto_unload_key();
return false;
}
furi_hal_crypto_unload_key();
return true;
}
FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* aad,
size_t aad_length,
const uint8_t* input,
uint8_t* output,
size_t length,
uint8_t* tag) {
if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, tag, false)) {
memset(output, 0, length);
memset(tag, 0, CRYPTO_GCM_TAG_LEN);
return FuriHalCryptoGCMStateError;
}
return FuriHalCryptoGCMStateOk;
}
FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* aad,
size_t aad_length,
const uint8_t* input,
uint8_t* output,
size_t length,
const uint8_t* tag) {
uint8_t dtag[CRYPTO_GCM_TAG_LEN];
if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, dtag, true)) {
memset(output, 0, length);
return FuriHalCryptoGCMStateError;
}
if(!furi_hal_crypto_gcm_compare_tag(dtag, tag)) {
memset(output, 0, length);
return FuriHalCryptoGCMStateAuthFailure;
}
return FuriHalCryptoGCMStateOk;
}

View File

@@ -283,7 +283,7 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) {
// Signature verification // Signature verification
uint8_t enclave_keys = 0; uint8_t enclave_keys = 0;
uint8_t enclave_valid_keys = 0; uint8_t enclave_valid_keys = 0;
bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys); bool enclave_valid = furi_hal_crypto_enclave_verify(&enclave_keys, &enclave_valid_keys);
if(sep == '.') { if(sep == '.') {
property_value_out( property_value_out(
&property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys); &property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys);

View File

@@ -1,6 +1,40 @@
/** /**
* @file furi_hal_crypto.h * @file furi_hal_crypto.h
*
* Cryptography HAL API * Cryptography HAL API
*
* !!! READ THIS FIRST !!!
*
* Flipper was never designed to be secure, nor it passed cryptography audit.
* Despite of the fact that keys are stored in secure enclave there are some
* types of attack that can be performed against AES engine to recover
* keys(theoretical). Also there is no way to securely deliver user keys to
* device and never will be. In addition device is fully open and there is no
* way to guarantee safety of your data, it can be easily dumped with debugger
* or modified code.
*
* Secure enclave on WB series is implemented on core2 FUS side and can be used
* only if core2 alive. Enclave is responsible for storing, loading and
* unloading keys to and from enclave/AES in secure manner(AES engine key
* registers will be locked when key from enclave loaded)
*
* There are 11 keys that we provision at factory:
* - 0 - Master key for secure key delivery. Impossible to use for anything but
* key provisioning. We don't plan to use it too.
* - 1 - 10 - Keys used by firmware. All devices got the same set of keys. You
* also can use them in your applications.
*
* Also there is a slot 11 that we use for device unique key. This slot is
* intentionally left blank till the moment of first use, so you can ensure that
* we don't know your unique key. Also you can provision this key by your self
* with crypto cli or API.
*
* Other slots can be used for your needs. But since enclave is sequential
* append only, we can not guarantee you that slots you want are free. NEVER USE
* THEM FOR PUBLIC APPLICATIONS.
*
* Also you can directly load raw keys into AES engine and use it for your
* needs.
*/ */
#pragma once #pragma once
@@ -12,10 +46,27 @@
extern "C" { extern "C" {
#endif #endif
/** Factory provisioned master key slot. Should never be used. */
#define FURI_HAL_CRYPTO_ENCLAVE_MASTER_KEY_SLOT (0u)
/** Factory provisioned keys slot range. All of them are exactly same on all flippers. */
#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_START (1u)
#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END (10u)
/** Device unique key slot. This key generated on first use or provisioned by user. Use furi_hal_crypto_enclave_ensure_key before using this slot. */
#define FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT (11u)
/** User key slot range. This slots can be used for your needs, but never use them in public apps. */
#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u)
#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u)
/** [Deprecated] Indicates availability of advanced crypto functions, will be dropped before v1.0 */
#define FURI_HAL_CRYPTO_ADVANCED_AVAIL 1
/** FuriHalCryptoKey Type */ /** FuriHalCryptoKey Type */
typedef enum { typedef enum {
FuriHalCryptoKeyTypeMaster, /**< Master key */ FuriHalCryptoKeyTypeMaster, /**< Master key */
FuriHalCryptoKeyTypeSimple, /**< Simple enencrypted key */ FuriHalCryptoKeyTypeSimple, /**< Simple unencrypted key */
FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */ FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */
} FuriHalCryptoKeyType; } FuriHalCryptoKeyType;
@@ -32,40 +83,89 @@ typedef struct {
uint8_t* data; uint8_t* data;
} FuriHalCryptoKey; } FuriHalCryptoKey;
/** Initialize cryptography layer This includes AES engines, PKA and RNG /** FuriHalCryptoGCMState Result of a GCM operation */
*/ typedef enum {
FuriHalCryptoGCMStateOk, /**< operation successful */
FuriHalCryptoGCMStateError, /**< error during encryption/decryption */
FuriHalCryptoGCMStateAuthFailure, /**< tags do not match, auth failed */
} FuriHalCryptoGCMState;
/** Initialize cryptography layer(includes AES engines, PKA and RNG) */
void furi_hal_crypto_init(); void furi_hal_crypto_init();
bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb); /** Verify factory provisioned keys
bool furi_hal_crypto_verify_key(uint8_t key_slot);
/** Store key in crypto storage
* *
* @param key FuriHalCryptoKey to store. Only Master, Simple or * @param keys_nb The keys number of
* Encrypted * @param valid_keys_nb The valid keys number of
* @param slot pinter to int where store slot number will be saved *
* @return true if all enclave keys are intact, false otherwise
*/
bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb);
/** Ensure that requested slot and slots before this slot contains keys.
*
* This function is used to provision FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT. Also you
* may want to use it to generate some unique keys in user key slot range.
*
* @warning Because of the sequential nature of the secure enclave this
* method will generate key for all slots from
* FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END to the slot your requested.
* Keys are generated using on-chip RNG.
*
* @param[in] key_slot The key slot to enclave
*
* @return true if key exists or created, false if enclave corrupted
*/
bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot);
/** Store key in crypto enclave
*
* @param key FuriHalCryptoKey to be stored
* @param slot pointer to int where enclave slot will be stored
* *
* @return true on success * @return true on success
*/ */
bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot); bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot);
/** Init AES engine and load key from crypto store /** Init AES engine and load key from crypto enclave
* *
* @param slot store slot number * @warning Use only with furi_hal_crypto_enclave_unload_key()
*
* @param slot enclave slot
* @param[in] iv pointer to 16 bytes Initialization Vector data * @param[in] iv pointer to 16 bytes Initialization Vector data
* *
* @return true on success * @return true on success
*/ */
bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv); bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv);
/** Unload key engine and deinit AES engine /** Unload key and deinit AES engine
* *
* @param slot store slot number * @warning Use only with furi_hal_crypto_enclave_load_key()
*
* @param slot enclave slot
* *
* @return true on success * @return true on success
*/ */
bool furi_hal_crypto_store_unload_key(uint8_t slot); bool furi_hal_crypto_enclave_unload_key(uint8_t slot);
/** Init AES engine and load supplied key
*
* @warning Use only with furi_hal_crypto_unload_key()
*
* @param[in] key pointer to 32 bytes key data
* @param[in] iv pointer to 16 bytes Initialization Vector data
*
* @return true on success
*/
bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv);
/** Unload key and de-init AES engine
*
* @warning Use this function only with furi_hal_crypto_load_key()
*
* @return true on success
*/
bool furi_hal_crypto_unload_key(void);
/** Encrypt data /** Encrypt data
* *
@@ -87,6 +187,109 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size)
*/ */
bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size); bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size);
/** Encrypt the input using AES-CTR
*
* Decryption can be performed by supplying the ciphertext as input. Inits and
* deinits the AES engine internally.
*
* @param[in] key pointer to 32 bytes key data
* @param[in] iv pointer to 12 bytes Initialization Vector data
* @param[in] input pointer to input data
* @param[out] output pointer to output data
* @param length length of the input and output in bytes
*
* @return true on success
*/
bool furi_hal_crypto_ctr(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* input,
uint8_t* output,
size_t length);
/** Encrypt/decrypt the input using AES-GCM
*
* When decrypting the tag generated needs to be compared to the tag attached to
* the ciphertext in a constant-time fashion. If the tags are not equal, the
* decryption failed and the plaintext returned needs to be discarded. Inits and
* deinits the AES engine internally.
*
* @param[in] key pointer to 32 bytes key data
* @param[in] iv pointer to 12 bytes Initialization Vector data
* @param[in] aad pointer to additional authentication data
* @param aad_length length of the additional authentication data in bytes
* @param[in] input pointer to input data
* @param[out] output pointer to output data
* @param length length of the input and output in bytes
* @param[out] tag pointer to 16 bytes space for the tag
* @param decrypt true for decryption, false otherwise
*
* @return true on success
*/
bool furi_hal_crypto_gcm(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* aad,
size_t aad_length,
const uint8_t* input,
uint8_t* output,
size_t length,
uint8_t* tag,
bool decrypt);
/** Encrypt the input using AES-GCM and generate a tag
*
* Inits and deinits the AES engine internally.
*
* @param[in] key pointer to 32 bytes key data
* @param[in] iv pointer to 12 bytes Initialization Vector data
* @param[in] aad pointer to additional authentication data
* @param aad_length length of the additional authentication data in bytes
* @param[in] input pointer to input data
* @param[out] output pointer to output data
* @param length length of the input and output in bytes
* @param[out] tag pointer to 16 bytes space for the tag
*
* @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on
* failure
*/
FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* aad,
size_t aad_length,
const uint8_t* input,
uint8_t* output,
size_t length,
uint8_t* tag);
/** Decrypt the input using AES-GCM and verify the provided tag
*
* Inits and deinits the AES engine internally.
*
* @param[in] key pointer to 32 bytes key data
* @param[in] iv pointer to 12 bytes Initialization Vector data
* @param[in] aad pointer to additional authentication data
* @param aad_length length of the additional authentication data in bytes
* @param[in] input pointer to input data
* @param[out] output pointer to output data
* @param length length of the input and output in bytes
* @param[out] tag pointer to 16 bytes tag
*
* @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on
* failure, FuriHalCryptoGCMStateAuthFailure if the tag does not
* match
*/
FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify(
const uint8_t* key,
const uint8_t* iv,
const uint8_t* aad,
size_t aad_length,
const uint8_t* input,
uint8_t* output,
size_t length,
const uint8_t* tag);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -122,7 +122,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream,
do { do {
if(iv) { if(iv) {
if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {
FURI_LOG_E(TAG, "Unable to load decryption key"); FURI_LOG_E(TAG, "Unable to load decryption key");
break; break;
} }
@@ -181,7 +181,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream,
} }
} while(ret > 0 && result); } while(ret > 0 && result);
if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(iv) furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);
} while(false); } while(false);
free(encrypted_line); free(encrypted_line);
@@ -280,7 +280,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8
subghz_keystore_mess_with_iv(iv); subghz_keystore_mess_with_iv(iv);
if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
break; break;
} }
@@ -326,7 +326,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8
stream_write_char(stream, '\n'); stream_write_char(stream, '\n');
encrypted_line_count++; encrypted_line_count++;
} }
furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);
size_t total_keys = SubGhzKeyArray_size(instance->data); size_t total_keys = SubGhzKeyArray_size(instance->data);
result = encrypted_line_count == total_keys; result = encrypted_line_count == total_keys;
if(result) { if(result) {
@@ -421,7 +421,7 @@ bool subghz_keystore_raw_encrypted_save(
subghz_keystore_mess_with_iv(iv); subghz_keystore_mess_with_iv(iv);
if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
break; break;
} }
@@ -474,7 +474,7 @@ bool subghz_keystore_raw_encrypted_save(
flipper_format_free(output_flipper_format); flipper_format_free(output_flipper_format);
furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);
if(!result) break; if(!result) break;
@@ -576,7 +576,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t*
} }
} }
if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) {
FURI_LOG_E(TAG, "Unable to load encryption key"); FURI_LOG_E(TAG, "Unable to load encryption key");
break; break;
} }
@@ -604,7 +604,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t*
memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len);
} while(0); } while(0);
furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT);
if(decrypted) result = true; if(decrypted) result = true;
} while(0); } while(0);
flipper_format_free(flipper_format); flipper_format_free(flipper_format);

View File

@@ -2,6 +2,7 @@ import os
import re import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from fbt.util import resolve_real_dir_node
from typing import Callable, ClassVar, List, Optional, Tuple, Union from typing import Callable, ClassVar, List, Optional, Tuple, Union
@@ -147,7 +148,7 @@ class AppManager:
FlipperApplication( FlipperApplication(
*args, *args,
**kw, **kw,
_appdir=app_dir_node, _appdir=resolve_real_dir_node(app_dir_node),
_apppath=os.path.dirname(app_manifest_path), _apppath=os.path.dirname(app_manifest_path),
_appmanager=self, _appmanager=self,
), ),

View File

@@ -45,7 +45,7 @@ def single_quote(arg_list):
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
def extract_abs_dir(node): def resolve_real_dir_node(node):
if isinstance(node, SCons.Node.FS.EntryProxy): if isinstance(node, SCons.Node.FS.EntryProxy):
node = node.get() node = node.get()
@@ -53,15 +53,7 @@ def extract_abs_dir(node):
if os.path.exists(repo_dir.abspath): if os.path.exists(repo_dir.abspath):
return repo_dir return repo_dir
raise StopError(f"Can't find absolute path for {node.name} ({node})")
def extract_abs_dir_path(node):
abs_dir_node = extract_abs_dir(node)
if abs_dir_node is None:
raise StopError(f"Can't find absolute path for {node.name}")
# Don't return abspath attribute (type is str), it will break in
# OverrideEnvironment.subst_list() by splitting path on spaces
return abs_dir_node
def path_as_posix(path): def path_as_posix(path):

View File

@@ -8,11 +8,14 @@ from SCons.Errors import StopError
def icons_emitter(target, source, env): def icons_emitter(target, source, env):
icons_src = env.GlobRecursive("*.png", env["ICON_SRC_DIR"])
icons_src += env.GlobRecursive("frame_rate", env["ICON_SRC_DIR"])
target = [ target = [
target[0].File(env.subst("${ICON_FILE_NAME}.c")), target[0].File(env.subst("${ICON_FILE_NAME}.c")),
target[0].File(env.subst("${ICON_FILE_NAME}.h")), target[0].File(env.subst("${ICON_FILE_NAME}.h")),
] ]
return target, source return target, icons_src
def proto_emitter(target, source, env): def proto_emitter(target, source, env):
@@ -104,21 +107,16 @@ def proto_ver_generator(target, source, env):
def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"): def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"):
# Gathering icons sources
try: try:
os.mkdir(str(source_dir)) os.mkdir(str(source_dir))
except FileExistsError: except FileExistsError:
pass pass
icons_src = env.GlobRecursive("*.png", source_dir) return env.IconBuilder(
icons_src += env.GlobRecursive("frame_rate", source_dir)
icons = env.IconBuilder(
target_dir, target_dir,
source_dir, None,
ICON_SRC_DIR=source_dir,
ICON_FILE_NAME=icon_bundle_name, ICON_FILE_NAME=icon_bundle_name,
) )
env.Depends(icons, icons_src)
return icons
def generate(env): def generate(env):
@@ -141,7 +139,7 @@ def generate(env):
BUILDERS={ BUILDERS={
"IconBuilder": Builder( "IconBuilder": Builder(
action=Action( action=Action(
'${PYTHON3} ${ASSETS_COMPILER} icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename "${ICON_FILE_NAME}"', '${PYTHON3} ${ASSETS_COMPILER} icons ${ICON_SRC_DIR} ${TARGET.dir} --filename "${ICON_FILE_NAME}"',
"${ICONSCOMSTR}", "${ICONSCOMSTR}",
), ),
emitter=icons_emitter, emitter=icons_emitter,

View File

@@ -11,7 +11,7 @@ from fbt.appmanifest import FlipperApplication, FlipperAppType, FlipperManifestE
from fbt.elfmanifest import assemble_manifest_data from fbt.elfmanifest import assemble_manifest_data
from fbt.fapassets import FileBundler from fbt.fapassets import FileBundler
from fbt.sdk.cache import SdkCache from fbt.sdk.cache import SdkCache
from fbt.util import extract_abs_dir_path from fbt.util import resolve_real_dir_node
from SCons.Action import Action from SCons.Action import Action
from SCons.Builder import Builder from SCons.Builder import Builder
from SCons.Errors import UserError from SCons.Errors import UserError
@@ -50,7 +50,8 @@ class AppBuilder:
def _setup_app_env(self): def _setup_app_env(self):
self.app_env = self.fw_env.Clone( self.app_env = self.fw_env.Clone(
FAP_SRC_DIR=self.app._appdir, FAP_WORK_DIR=self.app_work_dir FAP_SRC_DIR=self.app._appdir,
FAP_WORK_DIR=self.app_work_dir,
) )
self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False) self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False)
@@ -119,7 +120,7 @@ class AppBuilder:
CPPDEFINES=lib_def.cdefines, CPPDEFINES=lib_def.cdefines,
CPPPATH=list( CPPPATH=list(
map( map(
lambda cpath: extract_abs_dir_path(self.app._appdir.Dir(cpath)), lambda cpath: resolve_real_dir_node(self.app._appdir.Dir(cpath)),
lib_def.cincludes, lib_def.cincludes,
) )
), ),
@@ -133,7 +134,7 @@ class AppBuilder:
def _build_app(self): def _build_app(self):
self.app_env.Append( self.app_env.Append(
LIBS=[*self.app.fap_libs, *self.private_libs], LIBS=[*self.app.fap_libs, *self.private_libs],
CPPPATH=self.app_env.Dir(self.app_work_dir), CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir],
) )
app_sources = list( app_sources = list(

View File

@@ -26,6 +26,10 @@ class Programmer(ABC):
def option_bytes_set(self, file_path: str) -> bool: def option_bytes_set(self, file_path: str) -> bool:
pass pass
@abstractmethod
def option_bytes_recover(self) -> bool:
pass
@abstractmethod @abstractmethod
def otp_write(self, address: int, file_path: str) -> bool: def otp_write(self, address: int, file_path: str) -> bool:
pass pass

View File

@@ -44,11 +44,11 @@ class OpenOCDProgrammer(Programmer):
self.logger = logging.getLogger() self.logger = logging.getLogger()
def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool: def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool:
stm32 = STM32WB55() stm32 = STM32WB55(self.openocd)
if mode == Programmer.RunMode.Run: if mode == Programmer.RunMode.Run:
stm32.reset(self.openocd, stm32.RunMode.Run) stm32.reset(stm32.RunMode.Run)
elif mode == Programmer.RunMode.Stop: elif mode == Programmer.RunMode.Stop:
stm32.reset(self.openocd, stm32.RunMode.Init) stm32.reset(stm32.RunMode.Init)
else: else:
raise Exception("Unknown mode") raise Exception("Unknown mode")
@@ -96,11 +96,11 @@ class OpenOCDProgrammer(Programmer):
def option_bytes_validate(self, file_path: str) -> bool: def option_bytes_validate(self, file_path: str) -> bool:
# Registers # Registers
stm32 = STM32WB55() stm32 = STM32WB55(self.openocd)
# OpenOCD # OpenOCD
self.openocd.start() self.openocd.start()
stm32.reset(self.openocd, stm32.RunMode.Init) stm32.reset(stm32.RunMode.Init)
# Generate Option Bytes data # Generate Option Bytes data
ob_data = OptionBytesData(file_path) ob_data = OptionBytesData(file_path)
@@ -133,7 +133,7 @@ class OpenOCDProgrammer(Programmer):
self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error) self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error)
# Stop OpenOCD # Stop OpenOCD
stm32.reset(self.openocd, stm32.RunMode.Run) stm32.reset(stm32.RunMode.Run)
self.openocd.stop() self.openocd.stop()
return return_code return return_code
@@ -143,11 +143,11 @@ class OpenOCDProgrammer(Programmer):
def option_bytes_set(self, file_path: str) -> bool: def option_bytes_set(self, file_path: str) -> bool:
# Registers # Registers
stm32 = STM32WB55() stm32 = STM32WB55(self.openocd)
# OpenOCD # OpenOCD
self.openocd.start() self.openocd.start()
stm32.reset(self.openocd, stm32.RunMode.Init) stm32.reset(stm32.RunMode.Init)
# Generate Option Bytes data # Generate Option Bytes data
ob_data = OptionBytesData(file_path) ob_data = OptionBytesData(file_path)
@@ -159,11 +159,11 @@ class OpenOCDProgrammer(Programmer):
ob_dwords = int(ob_length / 8) ob_dwords = int(ob_length / 8)
# Clear flash errors # Clear flash errors
stm32.clear_flash_errors(self.openocd) stm32.clear_flash_errors()
# Unlock Flash and Option Bytes # Unlock Flash and Option Bytes
stm32.flash_unlock(self.openocd) stm32.flash_unlock()
stm32.option_bytes_unlock(self.openocd) stm32.option_bytes_unlock()
ob_need_to_apply = False ob_need_to_apply = False
@@ -194,16 +194,16 @@ class OpenOCDProgrammer(Programmer):
self.openocd.write_32(device_reg_addr, ob_value) self.openocd.write_32(device_reg_addr, ob_value)
if ob_need_to_apply: if ob_need_to_apply:
stm32.option_bytes_apply(self.openocd) stm32.option_bytes_apply()
else: else:
self.logger.info("Option Bytes are already correct") self.logger.info("Option Bytes are already correct")
# Load Option Bytes # Load Option Bytes
# That will reset and also lock the Option Bytes and the Flash # That will reset and also lock the Option Bytes and the Flash
stm32.option_bytes_load(self.openocd) stm32.option_bytes_load()
# Stop OpenOCD # Stop OpenOCD
stm32.reset(self.openocd, stm32.RunMode.Run) stm32.reset(stm32.RunMode.Run)
self.openocd.stop() self.openocd.stop()
return True return True
@@ -233,11 +233,10 @@ class OpenOCDProgrammer(Programmer):
self.logger.debug(f"Data: {data.hex().upper()}") self.logger.debug(f"Data: {data.hex().upper()}")
# Start OpenOCD # Start OpenOCD
oocd = self.openocd self.openocd.start()
oocd.start()
# Registers # Registers
stm32 = STM32WB55() stm32 = STM32WB55(self.openocd)
try: try:
# Check that OTP is empty for the given address # Check that OTP is empty for the given address
@@ -245,7 +244,7 @@ class OpenOCDProgrammer(Programmer):
already_written = True already_written = True
for i in range(0, data_size, 4): for i in range(0, data_size, 4):
file_word = int.from_bytes(data[i : i + 4], "little") file_word = int.from_bytes(data[i : i + 4], "little")
device_word = oocd.read_32(address + i) device_word = self.openocd.read_32(address + i)
if device_word != 0xFFFFFFFF and device_word != file_word: if device_word != 0xFFFFFFFF and device_word != file_word:
self.logger.error( self.logger.error(
f"OTP memory at {address + i:08X} is not empty: {device_word:08X}" f"OTP memory at {address + i:08X} is not empty: {device_word:08X}"
@@ -260,7 +259,7 @@ class OpenOCDProgrammer(Programmer):
return OpenOCDProgrammerResult.Success return OpenOCDProgrammerResult.Success
self.reset(self.RunMode.Stop) self.reset(self.RunMode.Stop)
stm32.clear_flash_errors(oocd) stm32.clear_flash_errors()
# Write OTP memory by 8 bytes # Write OTP memory by 8 bytes
for i in range(0, data_size, 8): for i in range(0, data_size, 8):
@@ -269,14 +268,14 @@ class OpenOCDProgrammer(Programmer):
self.logger.debug( self.logger.debug(
f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}" f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}"
) )
stm32.write_flash_64(oocd, address + i, word_1, word_2) stm32.write_flash_64(address + i, word_1, word_2)
# Validate OTP memory # Validate OTP memory
validation_result = True validation_result = True
for i in range(0, data_size, 4): for i in range(0, data_size, 4):
file_word = int.from_bytes(data[i : i + 4], "little") file_word = int.from_bytes(data[i : i + 4], "little")
device_word = oocd.read_32(address + i) device_word = self.openocd.read_32(address + i)
if file_word != device_word: if file_word != device_word:
self.logger.error( self.logger.error(
f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}" f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}"
@@ -284,11 +283,21 @@ class OpenOCDProgrammer(Programmer):
validation_result = False validation_result = False
finally: finally:
# Stop OpenOCD # Stop OpenOCD
stm32.reset(oocd, stm32.RunMode.Run) stm32.reset(stm32.RunMode.Run)
oocd.stop() self.openocd.stop()
return ( return (
OpenOCDProgrammerResult.Success OpenOCDProgrammerResult.Success
if validation_result if validation_result
else OpenOCDProgrammerResult.ErrorValidation else OpenOCDProgrammerResult.ErrorValidation
) )
def option_bytes_recover(self) -> bool:
try:
self.openocd.start()
stm32 = STM32WB55(self.openocd)
stm32.reset(stm32.RunMode.Halt)
stm32.option_bytes_recover()
return True
finally:
self.openocd.stop()

View File

@@ -16,6 +16,7 @@ class Register32:
self.names = [definition.name for definition in definition_list] # typecheck self.names = [definition.name for definition in definition_list] # typecheck
self.address = address self.address = address
self.definition_list = definition_list self.definition_list = definition_list
self.openocd = None
# Validate that the definitions are not overlapping # Validate that the definitions are not overlapping
for i in range(len(definition_list)): for i in range(len(definition_list)):
@@ -76,6 +77,14 @@ class Register32:
def __dir__(self): def __dir__(self):
return self.names return self.names
def set_openocd(self, openocd: OpenOCD):
self.openocd = openocd
def get_openocd(self) -> OpenOCD:
if self.openocd is None:
raise RuntimeError("OpenOCD is not installed")
return self.openocd
def set(self, value: int): def set(self, value: int):
for definition in self.definition_list: for definition in self.definition_list:
definition.value = (value >> definition.offset) & ( definition.value = (value >> definition.offset) & (
@@ -88,8 +97,8 @@ class Register32:
value |= definition.value << definition.offset value |= definition.value << definition.offset
return value return value
def load(self, openocd: OpenOCD): def load(self):
self.set(openocd.read_32(self.address)) self.set(self.get_openocd().read_32(self.address))
def store(self, openocd: OpenOCD): def store(self):
openocd.write_32(self.address, self.get()) self.get_openocd().write_32(self.address, self.get())

View File

@@ -108,23 +108,27 @@ class STM32WB55:
15: None, # Core 2 Options 15: None, # Core 2 Options
} }
def __init__(self): def __init__(self, openocd: OpenOCD):
self.openocd = openocd
self.logger = logging.getLogger("STM32WB55") self.logger = logging.getLogger("STM32WB55")
self.FLASH_CR.set_openocd(self.openocd)
self.FLASH_SR.set_openocd(self.openocd)
class RunMode(Enum): class RunMode(Enum):
Init = "init" Init = "init"
Run = "run" Run = "run"
Halt = "halt" Halt = "halt"
def reset(self, oocd: OpenOCD, mode: RunMode): def reset(self, mode: RunMode):
self.logger.debug("Resetting device") self.logger.debug("Resetting device")
oocd.send_tcl(f"reset {mode.value}") self.openocd.send_tcl(f"reset {mode.value}")
def clear_flash_errors(self, oocd: OpenOCD): def clear_flash_errors(self):
# Errata 2.2.9: Flash OPTVERR flag is always set after system reset # Errata 2.2.9: Flash OPTVERR flag is always set after system reset
# And also clear all other flash error flags # And also clear all other flash error flags
self.logger.debug("Resetting flash errors") self.logger.debug("Resetting flash errors")
self.FLASH_SR.load(oocd) self.FLASH_SR.load()
self.FLASH_SR.OP_ERR = 1 self.FLASH_SR.OP_ERR = 1
self.FLASH_SR.PROG_ERR = 1 self.FLASH_SR.PROG_ERR = 1
self.FLASH_SR.WRP_ERR = 1 self.FLASH_SR.WRP_ERR = 1
@@ -135,51 +139,51 @@ class STM32WB55:
self.FLASH_SR.FAST_ERR = 1 self.FLASH_SR.FAST_ERR = 1
self.FLASH_SR.RD_ERR = 1 self.FLASH_SR.RD_ERR = 1
self.FLASH_SR.OPTV_ERR = 1 self.FLASH_SR.OPTV_ERR = 1
self.FLASH_SR.store(oocd) self.FLASH_SR.store()
def flash_unlock(self, oocd: OpenOCD): def flash_unlock(self):
# Check if flash is already unlocked # Check if flash is already unlocked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 0: if self.FLASH_CR.LOCK == 0:
self.logger.debug("Flash is already unlocked") self.logger.debug("Flash is already unlocked")
return return
# Unlock flash # Unlock flash
self.logger.debug("Unlocking Flash") self.logger.debug("Unlocking Flash")
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
# Check if flash is unlocked # Check if flash is unlocked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 0: if self.FLASH_CR.LOCK == 0:
self.logger.debug("Flash unlocked") self.logger.debug("Flash unlocked")
else: else:
self.logger.error("Flash unlock failed") self.logger.error("Flash unlock failed")
raise Exception("Flash unlock failed") raise Exception("Flash unlock failed")
def option_bytes_unlock(self, oocd: OpenOCD): def option_bytes_unlock(self):
# Check if options is already unlocked # Check if options is already unlocked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 0: if self.FLASH_CR.OPT_LOCK == 0:
self.logger.debug("Options is already unlocked") self.logger.debug("Options is already unlocked")
return return
# Unlock options # Unlock options
self.logger.debug("Unlocking Options") self.logger.debug("Unlocking Options")
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
# Check if options is unlocked # Check if options is unlocked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 0: if self.FLASH_CR.OPT_LOCK == 0:
self.logger.debug("Options unlocked") self.logger.debug("Options unlocked")
else: else:
self.logger.error("Options unlock failed") self.logger.error("Options unlock failed")
raise Exception("Options unlock failed") raise Exception("Options unlock failed")
def option_bytes_lock(self, oocd: OpenOCD): def option_bytes_lock(self):
# Check if options is already locked # Check if options is already locked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 1: if self.FLASH_CR.OPT_LOCK == 1:
self.logger.debug("Options is already locked") self.logger.debug("Options is already locked")
return return
@@ -187,19 +191,19 @@ class STM32WB55:
# Lock options # Lock options
self.logger.debug("Locking Options") self.logger.debug("Locking Options")
self.FLASH_CR.OPT_LOCK = 1 self.FLASH_CR.OPT_LOCK = 1
self.FLASH_CR.store(oocd) self.FLASH_CR.store()
# Check if options is locked # Check if options is locked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 1: if self.FLASH_CR.OPT_LOCK == 1:
self.logger.debug("Options locked") self.logger.debug("Options locked")
else: else:
self.logger.error("Options lock failed") self.logger.error("Options lock failed")
raise Exception("Options lock failed") raise Exception("Options lock failed")
def flash_lock(self, oocd: OpenOCD): def flash_lock(self):
# Check if flash is already locked # Check if flash is already locked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 1: if self.FLASH_CR.LOCK == 1:
self.logger.debug("Flash is already locked") self.logger.debug("Flash is already locked")
return return
@@ -207,31 +211,31 @@ class STM32WB55:
# Lock flash # Lock flash
self.logger.debug("Locking Flash") self.logger.debug("Locking Flash")
self.FLASH_CR.LOCK = 1 self.FLASH_CR.LOCK = 1
self.FLASH_CR.store(oocd) self.FLASH_CR.store()
# Check if flash is locked # Check if flash is locked
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 1: if self.FLASH_CR.LOCK == 1:
self.logger.debug("Flash locked") self.logger.debug("Flash locked")
else: else:
self.logger.error("Flash lock failed") self.logger.error("Flash lock failed")
raise Exception("Flash lock failed") raise Exception("Flash lock failed")
def option_bytes_apply(self, oocd: OpenOCD): def option_bytes_apply(self):
self.logger.debug("Applying Option Bytes") self.logger.debug("Applying Option Bytes")
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
self.FLASH_CR.OPT_STRT = 1 self.FLASH_CR.OPT_STRT = 1
self.FLASH_CR.store(oocd) self.FLASH_CR.store()
# Wait for Option Bytes to be applied # Wait for Option Bytes to be applied
self.flash_wait_for_operation(oocd) self.flash_wait_for_operation()
def option_bytes_load(self, oocd: OpenOCD): def option_bytes_load(self):
self.logger.debug("Loading Option Bytes") self.logger.debug("Loading Option Bytes")
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
self.FLASH_CR.OBL_LAUNCH = 1 self.FLASH_CR.OBL_LAUNCH = 1
self.FLASH_CR.store(oocd) self.FLASH_CR.store()
def option_bytes_id_to_address(self, id: int) -> int: def option_bytes_id_to_address(self, id: int) -> int:
# Check if this option byte (dword) is mapped to a register # Check if this option byte (dword) is mapped to a register
@@ -241,16 +245,16 @@ class STM32WB55:
return device_reg_addr return device_reg_addr
def flash_wait_for_operation(self, oocd: OpenOCD): def flash_wait_for_operation(self):
# Wait for flash operation to complete # Wait for flash operation to complete
# TODO: timeout # TODO: timeout
while True: while True:
self.FLASH_SR.load(oocd) self.FLASH_SR.load()
if self.FLASH_SR.BSY == 0: if self.FLASH_SR.BSY == 0:
break break
def flash_dump_status_register(self, oocd: OpenOCD): def flash_dump_status_register(self):
self.FLASH_SR.load(oocd) self.FLASH_SR.load()
self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}") self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}")
if self.FLASH_SR.EOP: if self.FLASH_SR.EOP:
self.logger.info(" End of operation") self.logger.info(" End of operation")
@@ -283,70 +287,87 @@ class STM32WB55:
if self.FLASH_SR.PESD: if self.FLASH_SR.PESD:
self.logger.info(" Programming / erase operation suspended.") self.logger.info(" Programming / erase operation suspended.")
def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int): def write_flash_64(self, address: int, word_1: int, word_2: int):
self.logger.debug(f"Writing flash at address {address:08x}") self.logger.debug(f"Writing flash at address {address:08x}")
if address % 8 != 0: if address % 8 != 0:
self.logger.error("Address must be aligned to 8 bytes") self.logger.error("Address must be aligned to 8 bytes")
raise Exception("Address must be aligned to 8 bytes") raise Exception("Address must be aligned to 8 bytes")
if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4): if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32(
address + 4
):
self.logger.debug("Data is already programmed") self.logger.debug("Data is already programmed")
return return
self.flash_unlock(oocd) self.flash_unlock()
# Check that no flash main memory operation is ongoing by checking the BSY bit # Check that no flash main memory operation is ongoing by checking the BSY bit
self.FLASH_SR.load(oocd) self.FLASH_SR.load()
if self.FLASH_SR.BSY: if self.FLASH_SR.BSY:
self.logger.error("Flash is busy") self.logger.error("Flash is busy")
self.flash_dump_status_register(oocd) self.flash_dump_status_register()
raise Exception("Flash is busy") raise Exception("Flash is busy")
# Enable end of operation interrupts and error interrupts # Enable end of operation interrupts and error interrupts
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
self.FLASH_CR.EOPIE = 1 self.FLASH_CR.EOPIE = 1
self.FLASH_CR.ERRIE = 1 self.FLASH_CR.ERRIE = 1
self.FLASH_CR.store(oocd) self.FLASH_CR.store()
# Check that flash memory program and erase operations are allowed # Check that flash memory program and erase operations are allowed
if self.FLASH_SR.PESD: if self.FLASH_SR.PESD:
self.logger.error("Flash operations are not allowed") self.logger.error("Flash operations are not allowed")
self.flash_dump_status_register(oocd) self.flash_dump_status_register()
raise Exception("Flash operations are not allowed") raise Exception("Flash operations are not allowed")
# Check and clear all error programming flags due to a previous programming. # Check and clear all error programming flags due to a previous programming.
self.clear_flash_errors(oocd) self.clear_flash_errors()
# Set the PG bit in the Flash memory control register (FLASH_CR) # Set the PG bit in the Flash memory control register (FLASH_CR)
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
self.FLASH_CR.PG = 1 self.FLASH_CR.PG = 1
self.FLASH_CR.store(oocd) self.FLASH_CR.store()
# Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed. # Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed.
# Write the first word # Write the first word
oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
# Write the second word # Write the second word
oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
# Wait for the BSY bit to be cleared # Wait for the BSY bit to be cleared
self.flash_wait_for_operation(oocd) self.flash_wait_for_operation()
# Check that EOP flag is set in the FLASH_SR register # Check that EOP flag is set in the FLASH_SR register
self.FLASH_SR.load(oocd) self.FLASH_SR.load()
if not self.FLASH_SR.EOP: if not self.FLASH_SR.EOP:
self.logger.error("Flash operation failed") self.logger.error("Flash operation failed")
self.flash_dump_status_register(oocd) self.flash_dump_status_register()
raise Exception("Flash operation failed") raise Exception("Flash operation failed")
# Clear the EOP flag # Clear the EOP flag
self.FLASH_SR.load(oocd) self.FLASH_SR.load()
self.FLASH_SR.EOP = 1 self.FLASH_SR.EOP = 1
self.FLASH_SR.store(oocd) self.FLASH_SR.store()
# Clear the PG bit in the FLASH_CR register # Clear the PG bit in the FLASH_CR register
self.FLASH_CR.load(oocd) self.FLASH_CR.load()
self.FLASH_CR.PG = 0 self.FLASH_CR.PG = 0
self.FLASH_CR.store(oocd) self.FLASH_CR.store()
self.flash_lock(oocd) self.flash_lock()
def option_bytes_recover(self):
self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset
# Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work
# self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH
# self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB")
# self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB
# self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F")
self.flash_unlock()
self.option_bytes_unlock()
self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB
self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR
self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR
self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT
self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH

View File

@@ -22,6 +22,12 @@ class Main(App):
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
self._add_args(self.parser_set) self._add_args(self.parser_set)
self.parser_set.set_defaults(func=self.set) self.parser_set.set_defaults(func=self.set)
# Set command
self.parser_recover = self.subparsers.add_parser(
"recover", help="Recover Option Bytes"
)
self._add_args(self.parser_recover)
self.parser_recover.set_defaults(func=self.recover)
def _add_args(self, parser): def _add_args(self, parser):
parser.add_argument( parser.add_argument(
@@ -75,6 +81,20 @@ class Main(App):
return return_code return return_code
def recover(self):
self.logger.info("Setting Option Bytes")
# OpenOCD
openocd = OpenOCDProgrammer(
self.args.interface,
self.args.port_base,
self.args.serial,
)
openocd.option_bytes_recover()
return 0
if __name__ == "__main__": if __name__ == "__main__":
Main()() Main()()

View File

@@ -1,12 +1,11 @@
from SCons.Platform import TempFileMunge
from SCons.Node import FS
from SCons.Errors import UserError
import os
import multiprocessing import multiprocessing
import os
import pathlib import pathlib
from SCons.Errors import UserError
from SCons.Node import FS
from SCons.Platform import TempFileMunge
SetOption("num_jobs", multiprocessing.cpu_count()) SetOption("num_jobs", multiprocessing.cpu_count())
SetOption("max_drift", 1) SetOption("max_drift", 1)
# SetOption("silent", False) # SetOption("silent", False)
@@ -67,16 +66,15 @@ core_env.Append(CPPDEFINES=GetOption("extra_defines"))
# Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state # Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state
from fbt.util import ( from fbt.appmanifest import FlipperApplication, FlipperAppType
tempfile_arg_esc_func,
single_quote,
extract_abs_dir,
extract_abs_dir_path,
wrap_tempfile,
path_as_posix,
)
from fbt.appmanifest import FlipperAppType, FlipperApplication
from fbt.sdk.cache import SdkCache from fbt.sdk.cache import SdkCache
from fbt.util import (
path_as_posix,
resolve_real_dir_node,
single_quote,
tempfile_arg_esc_func,
wrap_tempfile,
)
# Base environment with all tools loaded from SDK # Base environment with all tools loaded from SDK
env = core_env.Clone( env = core_env.Clone(
@@ -107,7 +105,7 @@ env = core_env.Clone(
PROGSUFFIX=".elf", PROGSUFFIX=".elf",
TEMPFILEARGESCFUNC=tempfile_arg_esc_func, TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
SINGLEQUOTEFUNC=single_quote, SINGLEQUOTEFUNC=single_quote,
ABSPATHGETTERFUNC=extract_abs_dir_path, ABSPATHGETTERFUNC=resolve_real_dir_node,
APPS=[], APPS=[],
UFBT_API_VERSION=SdkCache( UFBT_API_VERSION=SdkCache(
core_env.subst("$SDK_DEFINITION"), load_version_only=True core_env.subst("$SDK_DEFINITION"), load_version_only=True
@@ -277,7 +275,7 @@ for app in known_extapps:
continue continue
app_artifacts = appenv.BuildAppElf(app) app_artifacts = appenv.BuildAppElf(app)
app_src_dir = extract_abs_dir(app_artifacts.app._appdir) app_src_dir = resolve_real_dir_node(app_artifacts.app._appdir)
app_artifacts.installer = [ app_artifacts.installer = [
appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact), appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact),
appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug), appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug),
@@ -348,6 +346,9 @@ appenv.PhonyTarget(
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}',
) )
# Update WiFi devboard firmware
dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
# Linter # Linter
dist_env.PhonyTarget( dist_env.PhonyTarget(

View File

@@ -26,6 +26,8 @@ Flashing & debugging:
Install firmware using self-update package Install firmware using self-update package
debug, debug_other, blackmagic: debug, debug_other, blackmagic:
Start GDB Start GDB
devboard_flash:
Update WiFi dev board with the latest firmware
Other: Other:
cli: cli:

View File

@@ -3,7 +3,7 @@ from fbt.util import (
tempfile_arg_esc_func, tempfile_arg_esc_func,
single_quote, single_quote,
wrap_tempfile, wrap_tempfile,
extract_abs_dir_path, resolve_real_dir_node,
) )
import os import os
@@ -59,7 +59,7 @@ coreenv = VAR_ENV.Clone(
PROGSUFFIX=".elf", PROGSUFFIX=".elf",
ENV=forward_os_env, ENV=forward_os_env,
SINGLEQUOTEFUNC=single_quote, SINGLEQUOTEFUNC=single_quote,
ABSPATHGETTERFUNC=extract_abs_dir_path, ABSPATHGETTERFUNC=resolve_real_dir_node,
# Setting up temp file parameters - to overcome command line length limits # Setting up temp file parameters - to overcome command line length limits
TEMPFILEARGESCFUNC=tempfile_arg_esc_func, TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
ROOT_DIR=Dir("#"), ROOT_DIR=Dir("#"),