Merge branch 'dev' of https://github.com/DarkFlippers/unleashed-firmware into xfw-dev --nobuild
@@ -327,6 +327,9 @@ distenv.PhonyTarget(
|
||||
"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
|
||||
distenv.PhonyTarget(
|
||||
|
||||
602
applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c
Normal 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;
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
int run_minunit_test_furi();
|
||||
int run_minunit_test_furi_hal();
|
||||
int run_minunit_test_furi_hal_crypto();
|
||||
int run_minunit_test_furi_string();
|
||||
int run_minunit_test_infrared();
|
||||
int run_minunit_test_rpc();
|
||||
@@ -39,6 +40,7 @@ typedef struct {
|
||||
const UnitTest unit_tests[] = {
|
||||
{.name = "furi", .entry = run_minunit_test_furi},
|
||||
{.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 = "storage", .entry = run_minunit_test_storage},
|
||||
{.name = "stream", .entry = run_minunit_test_stream},
|
||||
|
||||
@@ -7,6 +7,5 @@ App(
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=60,
|
||||
fap_icon="mouse_10px.png",
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -8,7 +8,7 @@
|
||||
#define TAG "U2F"
|
||||
|
||||
#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_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
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) {
|
||||
FURI_LOG_E(TAG, "Encryption failed");
|
||||
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);
|
||||
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;
|
||||
uint32_t version = 0;
|
||||
|
||||
// Check if unique key exists in secure eclave(typo?) and generate it if missing
|
||||
if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false;
|
||||
// Check if unique key exists in secure eclave and generate it if missing
|
||||
if(!furi_hal_crypto_enclave_ensure_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false;
|
||||
|
||||
FuriString* filetype;
|
||||
filetype = furi_string_alloc();
|
||||
@@ -220,7 +220,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) {
|
||||
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");
|
||||
break;
|
||||
}
|
||||
@@ -231,7 +231,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) {
|
||||
FURI_LOG_E(TAG, "Decryption failed");
|
||||
break;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(key_slot);
|
||||
furi_hal_crypto_enclave_unload_key(key_slot);
|
||||
} else {
|
||||
if(!flipper_format_read_hex(flipper_format, "Data", cert_key, 32)) {
|
||||
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");
|
||||
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");
|
||||
break;
|
||||
}
|
||||
@@ -296,7 +296,7 @@ bool u2f_data_key_load(uint8_t* device_key) {
|
||||
FURI_LOG_E(TAG, "Decryption failed");
|
||||
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;
|
||||
} 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(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");
|
||||
return false;
|
||||
}
|
||||
@@ -327,7 +327,7 @@ bool u2f_data_key_generate(uint8_t* device_key) {
|
||||
FURI_LOG_E(TAG, "Encryption failed");
|
||||
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);
|
||||
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");
|
||||
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");
|
||||
break;
|
||||
}
|
||||
@@ -402,7 +402,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) {
|
||||
FURI_LOG_E(TAG, "Decryption failed");
|
||||
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) {
|
||||
*cnt_val = cnt.counter;
|
||||
state = true;
|
||||
@@ -434,7 +434,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) {
|
||||
cnt.control = U2F_COUNTER_CONTROL_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");
|
||||
return false;
|
||||
}
|
||||
@@ -443,7 +443,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) {
|
||||
FURI_LOG_E(TAG, "Encryption failed");
|
||||
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);
|
||||
FlipperFormat* flipper_format = flipper_format_file_alloc(storage);
|
||||
|
||||
@@ -33,7 +33,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
|
||||
} while(0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@@ -160,7 +160,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
|
||||
} while(0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -251,25 +251,25 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) {
|
||||
if(key_slot > 0) {
|
||||
uint8_t iv[16] = {0};
|
||||
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(
|
||||
"Slot %d before %d is empty, which is not allowed",
|
||||
key_slot - 1,
|
||||
key_slot);
|
||||
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)) {
|
||||
furi_hal_crypto_store_unload_key(key_slot);
|
||||
if(furi_hal_crypto_enclave_load_key(key_slot, iv)) {
|
||||
furi_hal_crypto_enclave_unload_key(key_slot);
|
||||
printf("Key slot %d is already used", key_slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
printf("Failure");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <storage/storage.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/gui_i.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <notification/notification.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);
|
||||
}
|
||||
}
|
||||
|
||||
static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {
|
||||
UNUSED(context);
|
||||
furi_assert(canvas);
|
||||
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);
|
||||
|
||||
FuriHalRtcDateTime curr_dt;
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h;
|
||||
|
||||
if(forced) {
|
||||
desktop->clock_type = (locale_get_time_format() == LocaleTimeFormat24h);
|
||||
}
|
||||
|
||||
if(forced || (desktop->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;
|
||||
if(desktop->time_hour != curr_dt.hour || desktop->time_minute != curr_dt.minute ||
|
||||
desktop->time_format_12 != time_format_12) {
|
||||
desktop->time_format_12 = time_format_12;
|
||||
desktop->time_hour = curr_dt.hour;
|
||||
desktop->time_minute = curr_dt.minute;
|
||||
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);
|
||||
|
||||
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));
|
||||
} else if(!is_enabled) { //&& furi_timer_is_running(desktop->update_clock_timer)) {
|
||||
} else {
|
||||
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) {
|
||||
furi_assert(context);
|
||||
furi_assert(canvas);
|
||||
|
||||
Desktop* desktop = context;
|
||||
|
||||
uint8_t d[4] = {
|
||||
desktop->minute % 10,
|
||||
desktop->minute / 10,
|
||||
desktop->hour % 10,
|
||||
desktop->hour / 10,
|
||||
};
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
uint8_t new_w = desktop_clock_get_num_w(d[0]) + //c1
|
||||
desktop_clock_get_num_w(d[1]) + //c2
|
||||
desktop_clock_get_num_w(d[2]) + //c3
|
||||
desktop_clock_get_num_w(d[3]) + //c4
|
||||
2 + 4; // ":" + 4 separators
|
||||
uint8_t hour = desktop->time_hour;
|
||||
if(desktop->time_format_12) {
|
||||
if(hour > 12) {
|
||||
hour -= 12;
|
||||
}
|
||||
if(hour == 0) {
|
||||
hour = 12;
|
||||
}
|
||||
}
|
||||
|
||||
// further away from the battery charge indicator, if the smallest minute is 1
|
||||
view_port_set_width(desktop->clock_viewport, new_w - !(d[0] == 1));
|
||||
char buffer[20];
|
||||
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;
|
||||
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]]);
|
||||
canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer);
|
||||
}
|
||||
|
||||
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;
|
||||
case DesktopGlobalAfterAppFinished:
|
||||
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);
|
||||
return true;
|
||||
case DesktopGlobalAutoLock:
|
||||
@@ -224,8 +191,8 @@ static void desktop_clock_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
|
||||
if(gui_get_count_of_enabled_view_port_in_layer(desktop->gui, GuiLayerStatusBarLeft) < 6) {
|
||||
desktop_clock_upd_time(desktop, false);
|
||||
if(gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6) {
|
||||
desktop_clock_update(desktop);
|
||||
|
||||
view_port_enabled_set(desktop->clock_viewport, true);
|
||||
} else {
|
||||
@@ -420,11 +387,6 @@ Desktop* desktop_alloc() {
|
||||
desktop->update_clock_timer =
|
||||
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);
|
||||
|
||||
return desktop;
|
||||
@@ -524,7 +486,7 @@ int32_t desktop_srv(void* p) {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -74,9 +74,10 @@ struct Desktop {
|
||||
FuriTimer* update_clock_timer;
|
||||
|
||||
FuriPubSub* status_pubsub;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
bool clock_type : 1; // true - 24h false - 12h
|
||||
|
||||
uint8_t time_hour;
|
||||
uint8_t time_minute;
|
||||
bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H
|
||||
|
||||
bool in_transition : 1;
|
||||
};
|
||||
|
||||
@@ -20,11 +20,12 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
|
||||
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_check(layer < GuiLayerMAX);
|
||||
uint8_t ret = 0;
|
||||
size_t ret = 0;
|
||||
|
||||
gui_lock(gui);
|
||||
ViewPortArray_it_t it;
|
||||
ViewPortArray_it_last(it, gui->layers[layer]);
|
||||
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);
|
||||
}
|
||||
gui_unlock(gui);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -141,8 +141,6 @@ Canvas* gui_direct_draw_acquire(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
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -76,6 +76,12 @@ struct Gui {
|
||||
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);
|
||||
|
||||
/** Update GUI, request redraw
|
||||
@@ -84,8 +90,30 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array);
|
||||
*/
|
||||
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);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Unlock GUI
|
||||
*
|
||||
* @param gui The Gui instance
|
||||
*/
|
||||
void gui_unlock(Gui* gui);
|
||||
|
||||
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_0.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_1.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_10.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_11.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_12.png
vendored
Normal file
|
After Width: | Height: | Size: 980 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_13.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_14.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_15.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_16.png
vendored
Normal file
|
After Width: | Height: | Size: 979 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_17.png
vendored
Normal file
|
After Width: | Height: | Size: 988 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_18.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_19.png
vendored
Normal file
|
After Width: | Height: | Size: 259 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_2.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_20.png
vendored
Normal file
|
After Width: | Height: | Size: 922 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_21.png
vendored
Normal file
|
After Width: | Height: | Size: 895 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_22.png
vendored
Normal file
|
After Width: | Height: | Size: 831 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_23.png
vendored
Normal file
|
After Width: | Height: | Size: 803 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_24.png
vendored
Normal file
|
After Width: | Height: | Size: 859 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_25.png
vendored
Normal file
|
After Width: | Height: | Size: 854 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_26.png
vendored
Normal file
|
After Width: | Height: | Size: 821 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_27.png
vendored
Normal file
|
After Width: | Height: | Size: 791 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_28.png
vendored
Normal file
|
After Width: | Height: | Size: 871 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_29.png
vendored
Normal file
|
After Width: | Height: | Size: 954 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_3.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_30.png
vendored
Normal file
|
After Width: | Height: | Size: 871 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_31.png
vendored
Normal file
|
After Width: | Height: | Size: 926 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_32.png
vendored
Normal file
|
After Width: | Height: | Size: 889 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_33.png
vendored
Normal file
|
After Width: | Height: | Size: 933 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_34.png
vendored
Normal file
|
After Width: | Height: | Size: 873 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_35.png
vendored
Normal file
|
After Width: | Height: | Size: 907 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_36.png
vendored
Normal file
|
After Width: | Height: | Size: 803 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_37.png
vendored
Normal file
|
After Width: | Height: | Size: 769 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_38.png
vendored
Normal file
|
After Width: | Height: | Size: 909 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_39.png
vendored
Normal file
|
After Width: | Height: | Size: 915 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_4.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_40.png
vendored
Normal file
|
After Width: | Height: | Size: 918 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_41.png
vendored
Normal file
|
After Width: | Height: | Size: 883 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_42.png
vendored
Normal file
|
After Width: | Height: | Size: 1020 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_43.png
vendored
Normal file
|
After Width: | Height: | Size: 788 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_44.png
vendored
Normal file
|
After Width: | Height: | Size: 997 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_45.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_46.png
vendored
Normal file
|
After Width: | Height: | Size: 949 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_47.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_48.png
vendored
Normal file
|
After Width: | Height: | Size: 1016 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_49.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_5.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_50.png
vendored
Normal file
|
After Width: | Height: | Size: 988 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_51.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_52.png
vendored
Normal file
|
After Width: | Height: | Size: 991 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_53.png
vendored
Normal file
|
After Width: | Height: | Size: 778 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_54.png
vendored
Normal file
|
After Width: | Height: | Size: 830 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_55.png
vendored
Normal file
|
After Width: | Height: | Size: 720 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_56.png
vendored
Normal file
|
After Width: | Height: | Size: 576 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_57.png
vendored
Normal file
|
After Width: | Height: | Size: 398 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_58.png
vendored
Normal file
|
After Width: | Height: | Size: 474 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_59.png
vendored
Normal file
|
After Width: | Height: | Size: 970 B |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_6.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_60.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_61.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_62.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_7.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_8.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Sad_song_128x64/frame_9.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
284
assets/dolphin/external/L1_Sad_song_128x64/meta.txt
vendored
Normal 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
|
||||
7
assets/dolphin/external/manifest.txt
vendored
@@ -161,3 +161,10 @@ Max butthurt: 14
|
||||
Min level: 27
|
||||
Max level: 30
|
||||
Weight: 3
|
||||
|
||||
Name: L1_Sad_song_128x64
|
||||
Min butthurt: 11
|
||||
Max butthurt: 14
|
||||
Min level: 14
|
||||
Max level: 30
|
||||
Weight: 4
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,34.4,,
|
||||
Version,+,35.0,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.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_is_expired,_Bool,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_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_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t
|
||||
Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t
|
||||
Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_unload_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_enable,void,
|
||||
Function,+,furi_hal_debug_is_gdb_session_active,_Bool,
|
||||
|
||||
|
@@ -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_is_expired,_Bool,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_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_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t
|
||||
Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t
|
||||
Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*"
|
||||
Function,+,furi_hal_crypto_enclave_unload_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_enable,void,
|
||||
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_direct_draw_acquire,Canvas*,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_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*"
|
||||
Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include <furi_hal_crypto.h>
|
||||
#include <furi_hal_cortex.h>
|
||||
#include <furi_hal_bt.h>
|
||||
#include <furi_hal_random.h>
|
||||
#include <furi_hal_bus.h>
|
||||
@@ -13,7 +14,7 @@
|
||||
#define ENCLAVE_SIGNATURE_SIZE 16
|
||||
|
||||
#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_INIT (AES_CR_MODE_0)
|
||||
@@ -24,6 +25,19 @@
|
||||
#define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE)
|
||||
#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 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.data = key_data;
|
||||
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));
|
||||
FURI_LOG_E(TAG, "Error writing key to slot %u", slot);
|
||||
return false;
|
||||
@@ -90,21 +104,21 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end
|
||||
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 valid_keys_nb = 0;
|
||||
uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS;
|
||||
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 > keys_nb) return false;
|
||||
} else { // Unique key
|
||||
if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing
|
||||
return false;
|
||||
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;
|
||||
furi_hal_crypto_store_unload_key(i);
|
||||
furi_hal_crypto_enclave_unload_key(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -116,14 +130,14 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) {
|
||||
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(valid_keys_nb);
|
||||
uint8_t keys = 0;
|
||||
uint8_t keys_valid = 0;
|
||||
uint8_t buffer[ENCLAVE_SIGNATURE_SIZE];
|
||||
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++;
|
||||
if(furi_hal_crypto_encrypt(
|
||||
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) ==
|
||||
0;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(key_slot + 1);
|
||||
furi_hal_crypto_enclave_unload_key(key_slot + 1);
|
||||
}
|
||||
}
|
||||
*keys_nb = keys;
|
||||
@@ -142,7 +156,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) {
|
||||
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(slot);
|
||||
|
||||
@@ -208,6 +222,16 @@ static void crypto_key_init(uint32_t* key, uint32_t* iv) {
|
||||
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) {
|
||||
furi_check((blk_len <= 4) && (blk_len > 0));
|
||||
|
||||
@@ -219,14 +243,8 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t countdown = CRYPTO_TIMEOUT;
|
||||
while(!READ_BIT(AES1->SR, AES_SR_CCF)) {
|
||||
if(LL_SYSTICK_IsActiveCounterFlag()) {
|
||||
countdown--;
|
||||
}
|
||||
if(countdown == 0) {
|
||||
return false;
|
||||
}
|
||||
if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(furi_hal_crypto_mutex);
|
||||
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()) {
|
||||
return false;
|
||||
}
|
||||
@@ -279,6 +297,27 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) {
|
||||
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 state = false;
|
||||
|
||||
@@ -310,14 +349,8 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size)
|
||||
|
||||
SET_BIT(AES1->CR, AES_CR_EN);
|
||||
|
||||
uint32_t countdown = CRYPTO_TIMEOUT;
|
||||
while(!READ_BIT(AES1->SR, AES_SR_CCF)) {
|
||||
if(LL_SYSTICK_IsActiveCounterFlag()) {
|
||||
countdown--;
|
||||
}
|
||||
if(countdown == 0) {
|
||||
return false;
|
||||
}
|
||||
if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) {
|
||||
// Signature verification
|
||||
uint8_t enclave_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 == '.') {
|
||||
property_value_out(
|
||||
&property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys);
|
||||
|
||||
@@ -1,6 +1,40 @@
|
||||
/**
|
||||
* @file furi_hal_crypto.h
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -12,10 +46,27 @@
|
||||
extern "C" {
|
||||
#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 */
|
||||
typedef enum {
|
||||
FuriHalCryptoKeyTypeMaster, /**< Master key */
|
||||
FuriHalCryptoKeyTypeSimple, /**< Simple enencrypted key */
|
||||
FuriHalCryptoKeyTypeSimple, /**< Simple unencrypted key */
|
||||
FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */
|
||||
} FuriHalCryptoKeyType;
|
||||
|
||||
@@ -32,40 +83,89 @@ typedef struct {
|
||||
uint8_t* data;
|
||||
} 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();
|
||||
|
||||
bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb);
|
||||
|
||||
bool furi_hal_crypto_verify_key(uint8_t key_slot);
|
||||
|
||||
/** Store key in crypto storage
|
||||
/** Verify factory provisioned keys
|
||||
*
|
||||
* @param key FuriHalCryptoKey to store. Only Master, Simple or
|
||||
* Encrypted
|
||||
* @param slot pinter to int where store slot number will be saved
|
||||
* @param keys_nb The keys number of
|
||||
* @param valid_keys_nb The valid keys number of
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -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);
|
||||
|
||||
/** 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
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -122,7 +122,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream,
|
||||
|
||||
do {
|
||||
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");
|
||||
break;
|
||||
}
|
||||
@@ -181,7 +181,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream,
|
||||
}
|
||||
} 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);
|
||||
|
||||
free(encrypted_line);
|
||||
@@ -280,7 +280,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
@@ -326,7 +326,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8
|
||||
stream_write_char(stream, '\n');
|
||||
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);
|
||||
result = encrypted_line_count == total_keys;
|
||||
if(result) {
|
||||
@@ -421,7 +421,7 @@ bool subghz_keystore_raw_encrypted_save(
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
@@ -474,7 +474,7 @@ bool subghz_keystore_raw_encrypted_save(
|
||||
|
||||
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;
|
||||
|
||||
@@ -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");
|
||||
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);
|
||||
|
||||
} 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;
|
||||
} while(0);
|
||||
flipper_format_free(flipper_format);
|
||||
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from fbt.util import resolve_real_dir_node
|
||||
from typing import Callable, ClassVar, List, Optional, Tuple, Union
|
||||
|
||||
|
||||
@@ -147,7 +148,7 @@ class AppManager:
|
||||
FlipperApplication(
|
||||
*args,
|
||||
**kw,
|
||||
_appdir=app_dir_node,
|
||||
_appdir=resolve_real_dir_node(app_dir_node),
|
||||
_apppath=os.path.dirname(app_manifest_path),
|
||||
_appmanager=self,
|
||||
),
|
||||
|
||||
@@ -45,7 +45,7 @@ def single_quote(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):
|
||||
node = node.get()
|
||||
|
||||
@@ -53,15 +53,7 @@ def extract_abs_dir(node):
|
||||
if os.path.exists(repo_dir.abspath):
|
||||
return repo_dir
|
||||
|
||||
|
||||
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
|
||||
raise StopError(f"Can't find absolute path for {node.name} ({node})")
|
||||
|
||||
|
||||
def path_as_posix(path):
|
||||
|
||||
@@ -8,11 +8,14 @@ from SCons.Errors import StopError
|
||||
|
||||
|
||||
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[0].File(env.subst("${ICON_FILE_NAME}.c")),
|
||||
target[0].File(env.subst("${ICON_FILE_NAME}.h")),
|
||||
]
|
||||
return target, source
|
||||
return target, icons_src
|
||||
|
||||
|
||||
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"):
|
||||
# Gathering icons sources
|
||||
try:
|
||||
os.mkdir(str(source_dir))
|
||||
except FileExistsError:
|
||||
pass
|
||||
icons_src = env.GlobRecursive("*.png", source_dir)
|
||||
icons_src += env.GlobRecursive("frame_rate", source_dir)
|
||||
|
||||
icons = env.IconBuilder(
|
||||
return env.IconBuilder(
|
||||
target_dir,
|
||||
source_dir,
|
||||
None,
|
||||
ICON_SRC_DIR=source_dir,
|
||||
ICON_FILE_NAME=icon_bundle_name,
|
||||
)
|
||||
env.Depends(icons, icons_src)
|
||||
return icons
|
||||
|
||||
|
||||
def generate(env):
|
||||
@@ -141,7 +139,7 @@ def generate(env):
|
||||
BUILDERS={
|
||||
"IconBuilder": Builder(
|
||||
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}",
|
||||
),
|
||||
emitter=icons_emitter,
|
||||
|
||||
@@ -11,7 +11,7 @@ from fbt.appmanifest import FlipperApplication, FlipperAppType, FlipperManifestE
|
||||
from fbt.elfmanifest import assemble_manifest_data
|
||||
from fbt.fapassets import FileBundler
|
||||
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.Builder import Builder
|
||||
from SCons.Errors import UserError
|
||||
@@ -50,7 +50,8 @@ class AppBuilder:
|
||||
|
||||
def _setup_app_env(self):
|
||||
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)
|
||||
|
||||
@@ -119,7 +120,7 @@ class AppBuilder:
|
||||
CPPDEFINES=lib_def.cdefines,
|
||||
CPPPATH=list(
|
||||
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,
|
||||
)
|
||||
),
|
||||
@@ -133,7 +134,7 @@ class AppBuilder:
|
||||
def _build_app(self):
|
||||
self.app_env.Append(
|
||||
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(
|
||||
|
||||
@@ -26,6 +26,10 @@ class Programmer(ABC):
|
||||
def option_bytes_set(self, file_path: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def option_bytes_recover(self) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def otp_write(self, address: int, file_path: str) -> bool:
|
||||
pass
|
||||
|
||||
@@ -44,11 +44,11 @@ class OpenOCDProgrammer(Programmer):
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool:
|
||||
stm32 = STM32WB55()
|
||||
stm32 = STM32WB55(self.openocd)
|
||||
if mode == Programmer.RunMode.Run:
|
||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
||||
stm32.reset(stm32.RunMode.Run)
|
||||
elif mode == Programmer.RunMode.Stop:
|
||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
||||
stm32.reset(stm32.RunMode.Init)
|
||||
else:
|
||||
raise Exception("Unknown mode")
|
||||
|
||||
@@ -96,11 +96,11 @@ class OpenOCDProgrammer(Programmer):
|
||||
|
||||
def option_bytes_validate(self, file_path: str) -> bool:
|
||||
# Registers
|
||||
stm32 = STM32WB55()
|
||||
stm32 = STM32WB55(self.openocd)
|
||||
|
||||
# OpenOCD
|
||||
self.openocd.start()
|
||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
||||
stm32.reset(stm32.RunMode.Init)
|
||||
|
||||
# Generate Option Bytes data
|
||||
ob_data = OptionBytesData(file_path)
|
||||
@@ -133,7 +133,7 @@ class OpenOCDProgrammer(Programmer):
|
||||
self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error)
|
||||
|
||||
# Stop OpenOCD
|
||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
||||
stm32.reset(stm32.RunMode.Run)
|
||||
self.openocd.stop()
|
||||
|
||||
return return_code
|
||||
@@ -143,11 +143,11 @@ class OpenOCDProgrammer(Programmer):
|
||||
|
||||
def option_bytes_set(self, file_path: str) -> bool:
|
||||
# Registers
|
||||
stm32 = STM32WB55()
|
||||
stm32 = STM32WB55(self.openocd)
|
||||
|
||||
# OpenOCD
|
||||
self.openocd.start()
|
||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
||||
stm32.reset(stm32.RunMode.Init)
|
||||
|
||||
# Generate Option Bytes data
|
||||
ob_data = OptionBytesData(file_path)
|
||||
@@ -159,11 +159,11 @@ class OpenOCDProgrammer(Programmer):
|
||||
ob_dwords = int(ob_length / 8)
|
||||
|
||||
# Clear flash errors
|
||||
stm32.clear_flash_errors(self.openocd)
|
||||
stm32.clear_flash_errors()
|
||||
|
||||
# Unlock Flash and Option Bytes
|
||||
stm32.flash_unlock(self.openocd)
|
||||
stm32.option_bytes_unlock(self.openocd)
|
||||
stm32.flash_unlock()
|
||||
stm32.option_bytes_unlock()
|
||||
|
||||
ob_need_to_apply = False
|
||||
|
||||
@@ -194,16 +194,16 @@ class OpenOCDProgrammer(Programmer):
|
||||
self.openocd.write_32(device_reg_addr, ob_value)
|
||||
|
||||
if ob_need_to_apply:
|
||||
stm32.option_bytes_apply(self.openocd)
|
||||
stm32.option_bytes_apply()
|
||||
else:
|
||||
self.logger.info("Option Bytes are already correct")
|
||||
|
||||
# Load Option Bytes
|
||||
# That will reset and also lock the Option Bytes and the Flash
|
||||
stm32.option_bytes_load(self.openocd)
|
||||
stm32.option_bytes_load()
|
||||
|
||||
# Stop OpenOCD
|
||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
||||
stm32.reset(stm32.RunMode.Run)
|
||||
self.openocd.stop()
|
||||
|
||||
return True
|
||||
@@ -233,11 +233,10 @@ class OpenOCDProgrammer(Programmer):
|
||||
self.logger.debug(f"Data: {data.hex().upper()}")
|
||||
|
||||
# Start OpenOCD
|
||||
oocd = self.openocd
|
||||
oocd.start()
|
||||
self.openocd.start()
|
||||
|
||||
# Registers
|
||||
stm32 = STM32WB55()
|
||||
stm32 = STM32WB55(self.openocd)
|
||||
|
||||
try:
|
||||
# Check that OTP is empty for the given address
|
||||
@@ -245,7 +244,7 @@ class OpenOCDProgrammer(Programmer):
|
||||
already_written = True
|
||||
for i in range(0, data_size, 4):
|
||||
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:
|
||||
self.logger.error(
|
||||
f"OTP memory at {address + i:08X} is not empty: {device_word:08X}"
|
||||
@@ -260,7 +259,7 @@ class OpenOCDProgrammer(Programmer):
|
||||
return OpenOCDProgrammerResult.Success
|
||||
|
||||
self.reset(self.RunMode.Stop)
|
||||
stm32.clear_flash_errors(oocd)
|
||||
stm32.clear_flash_errors()
|
||||
|
||||
# Write OTP memory by 8 bytes
|
||||
for i in range(0, data_size, 8):
|
||||
@@ -269,14 +268,14 @@ class OpenOCDProgrammer(Programmer):
|
||||
self.logger.debug(
|
||||
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
|
||||
validation_result = True
|
||||
|
||||
for i in range(0, data_size, 4):
|
||||
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:
|
||||
self.logger.error(
|
||||
f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}"
|
||||
@@ -284,11 +283,21 @@ class OpenOCDProgrammer(Programmer):
|
||||
validation_result = False
|
||||
finally:
|
||||
# Stop OpenOCD
|
||||
stm32.reset(oocd, stm32.RunMode.Run)
|
||||
oocd.stop()
|
||||
stm32.reset(stm32.RunMode.Run)
|
||||
self.openocd.stop()
|
||||
|
||||
return (
|
||||
OpenOCDProgrammerResult.Success
|
||||
if validation_result
|
||||
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()
|
||||
|
||||
@@ -16,6 +16,7 @@ class Register32:
|
||||
self.names = [definition.name for definition in definition_list] # typecheck
|
||||
self.address = address
|
||||
self.definition_list = definition_list
|
||||
self.openocd = None
|
||||
|
||||
# Validate that the definitions are not overlapping
|
||||
for i in range(len(definition_list)):
|
||||
@@ -76,6 +77,14 @@ class Register32:
|
||||
def __dir__(self):
|
||||
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):
|
||||
for definition in self.definition_list:
|
||||
definition.value = (value >> definition.offset) & (
|
||||
@@ -88,8 +97,8 @@ class Register32:
|
||||
value |= definition.value << definition.offset
|
||||
return value
|
||||
|
||||
def load(self, openocd: OpenOCD):
|
||||
self.set(openocd.read_32(self.address))
|
||||
def load(self):
|
||||
self.set(self.get_openocd().read_32(self.address))
|
||||
|
||||
def store(self, openocd: OpenOCD):
|
||||
openocd.write_32(self.address, self.get())
|
||||
def store(self):
|
||||
self.get_openocd().write_32(self.address, self.get())
|
||||
|
||||
@@ -108,23 +108,27 @@ class STM32WB55:
|
||||
15: None, # Core 2 Options
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, openocd: OpenOCD):
|
||||
self.openocd = openocd
|
||||
self.logger = logging.getLogger("STM32WB55")
|
||||
|
||||
self.FLASH_CR.set_openocd(self.openocd)
|
||||
self.FLASH_SR.set_openocd(self.openocd)
|
||||
|
||||
class RunMode(Enum):
|
||||
Init = "init"
|
||||
Run = "run"
|
||||
Halt = "halt"
|
||||
|
||||
def reset(self, oocd: OpenOCD, mode: RunMode):
|
||||
def reset(self, mode: RunMode):
|
||||
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
|
||||
# And also clear all other flash error flags
|
||||
self.logger.debug("Resetting flash errors")
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
self.FLASH_SR.OP_ERR = 1
|
||||
self.FLASH_SR.PROG_ERR = 1
|
||||
self.FLASH_SR.WRP_ERR = 1
|
||||
@@ -135,51 +139,51 @@ class STM32WB55:
|
||||
self.FLASH_SR.FAST_ERR = 1
|
||||
self.FLASH_SR.RD_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
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 0:
|
||||
self.logger.debug("Flash is already unlocked")
|
||||
return
|
||||
|
||||
# Unlock flash
|
||||
self.logger.debug("Unlocking Flash")
|
||||
oocd.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_KEY1)
|
||||
self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
|
||||
|
||||
# Check if flash is unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 0:
|
||||
self.logger.debug("Flash unlocked")
|
||||
else:
|
||||
self.logger.error("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
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 0:
|
||||
self.logger.debug("Options is already unlocked")
|
||||
return
|
||||
|
||||
# Unlock options
|
||||
self.logger.debug("Unlocking Options")
|
||||
oocd.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_OPTKEY1)
|
||||
self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
|
||||
|
||||
# Check if options is unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 0:
|
||||
self.logger.debug("Options unlocked")
|
||||
else:
|
||||
self.logger.error("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
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 1:
|
||||
self.logger.debug("Options is already locked")
|
||||
return
|
||||
@@ -187,19 +191,19 @@ class STM32WB55:
|
||||
# Lock options
|
||||
self.logger.debug("Locking Options")
|
||||
self.FLASH_CR.OPT_LOCK = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Check if options is locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 1:
|
||||
self.logger.debug("Options locked")
|
||||
else:
|
||||
self.logger.error("Options lock failed")
|
||||
raise Exception("Options lock failed")
|
||||
|
||||
def flash_lock(self, oocd: OpenOCD):
|
||||
def flash_lock(self):
|
||||
# Check if flash is already locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 1:
|
||||
self.logger.debug("Flash is already locked")
|
||||
return
|
||||
@@ -207,31 +211,31 @@ class STM32WB55:
|
||||
# Lock flash
|
||||
self.logger.debug("Locking Flash")
|
||||
self.FLASH_CR.LOCK = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Check if flash is locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 1:
|
||||
self.logger.debug("Flash locked")
|
||||
else:
|
||||
self.logger.error("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.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
self.FLASH_CR.OPT_STRT = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# 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.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
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:
|
||||
# Check if this option byte (dword) is mapped to a register
|
||||
@@ -241,16 +245,16 @@ class STM32WB55:
|
||||
|
||||
return device_reg_addr
|
||||
|
||||
def flash_wait_for_operation(self, oocd: OpenOCD):
|
||||
def flash_wait_for_operation(self):
|
||||
# Wait for flash operation to complete
|
||||
# TODO: timeout
|
||||
while True:
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
if self.FLASH_SR.BSY == 0:
|
||||
break
|
||||
|
||||
def flash_dump_status_register(self, oocd: OpenOCD):
|
||||
self.FLASH_SR.load(oocd)
|
||||
def flash_dump_status_register(self):
|
||||
self.FLASH_SR.load()
|
||||
self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}")
|
||||
if self.FLASH_SR.EOP:
|
||||
self.logger.info(" End of operation")
|
||||
@@ -283,70 +287,87 @@ class STM32WB55:
|
||||
if self.FLASH_SR.PESD:
|
||||
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}")
|
||||
|
||||
if address % 8 != 0:
|
||||
self.logger.error("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")
|
||||
return
|
||||
|
||||
self.flash_unlock(oocd)
|
||||
self.flash_unlock()
|
||||
|
||||
# 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:
|
||||
self.logger.error("Flash is busy")
|
||||
self.flash_dump_status_register(oocd)
|
||||
self.flash_dump_status_register()
|
||||
raise Exception("Flash is busy")
|
||||
|
||||
# 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.ERRIE = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Check that flash memory program and erase operations are allowed
|
||||
if self.FLASH_SR.PESD:
|
||||
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")
|
||||
|
||||
# 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)
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
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.
|
||||
# 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
|
||||
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
|
||||
self.flash_wait_for_operation(oocd)
|
||||
self.flash_wait_for_operation()
|
||||
|
||||
# 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:
|
||||
self.logger.error("Flash operation failed")
|
||||
self.flash_dump_status_register(oocd)
|
||||
self.flash_dump_status_register()
|
||||
raise Exception("Flash operation failed")
|
||||
|
||||
# Clear the EOP flag
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
self.FLASH_SR.EOP = 1
|
||||
self.FLASH_SR.store(oocd)
|
||||
self.FLASH_SR.store()
|
||||
|
||||
# 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.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
|
||||
|
||||
@@ -22,6 +22,12 @@ class Main(App):
|
||||
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
|
||||
self._add_args(self.parser_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):
|
||||
parser.add_argument(
|
||||
@@ -75,6 +81,20 @@ class Main(App):
|
||||
|
||||
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__":
|
||||
Main()()
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from SCons.Platform import TempFileMunge
|
||||
from SCons.Node import FS
|
||||
from SCons.Errors import UserError
|
||||
|
||||
|
||||
import os
|
||||
import multiprocessing
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
from SCons.Errors import UserError
|
||||
from SCons.Node import FS
|
||||
from SCons.Platform import TempFileMunge
|
||||
|
||||
SetOption("num_jobs", multiprocessing.cpu_count())
|
||||
SetOption("max_drift", 1)
|
||||
# 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
|
||||
|
||||
from fbt.util import (
|
||||
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.appmanifest import FlipperApplication, FlipperAppType
|
||||
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
|
||||
env = core_env.Clone(
|
||||
@@ -107,7 +105,7 @@ env = core_env.Clone(
|
||||
PROGSUFFIX=".elf",
|
||||
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
|
||||
SINGLEQUOTEFUNC=single_quote,
|
||||
ABSPATHGETTERFUNC=extract_abs_dir_path,
|
||||
ABSPATHGETTERFUNC=resolve_real_dir_node,
|
||||
APPS=[],
|
||||
UFBT_API_VERSION=SdkCache(
|
||||
core_env.subst("$SDK_DEFINITION"), load_version_only=True
|
||||
@@ -277,7 +275,7 @@ for app in known_extapps:
|
||||
continue
|
||||
|
||||
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 = [
|
||||
appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact),
|
||||
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}',
|
||||
)
|
||||
|
||||
# Update WiFi devboard firmware
|
||||
dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
|
||||
|
||||
# Linter
|
||||
|
||||
dist_env.PhonyTarget(
|
||||
|
||||
@@ -26,6 +26,8 @@ Flashing & debugging:
|
||||
Install firmware using self-update package
|
||||
debug, debug_other, blackmagic:
|
||||
Start GDB
|
||||
devboard_flash:
|
||||
Update WiFi dev board with the latest firmware
|
||||
|
||||
Other:
|
||||
cli:
|
||||
|
||||
@@ -3,7 +3,7 @@ from fbt.util import (
|
||||
tempfile_arg_esc_func,
|
||||
single_quote,
|
||||
wrap_tempfile,
|
||||
extract_abs_dir_path,
|
||||
resolve_real_dir_node,
|
||||
)
|
||||
|
||||
import os
|
||||
@@ -59,7 +59,7 @@ coreenv = VAR_ENV.Clone(
|
||||
PROGSUFFIX=".elf",
|
||||
ENV=forward_os_env,
|
||||
SINGLEQUOTEFUNC=single_quote,
|
||||
ABSPATHGETTERFUNC=extract_abs_dir_path,
|
||||
ABSPATHGETTERFUNC=resolve_real_dir_node,
|
||||
# Setting up temp file parameters - to overcome command line length limits
|
||||
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
|
||||
ROOT_DIR=Dir("#"),
|
||||
|
||||